Repository navigation
R1: the résumé editor: sections in your order, every printed field, a PDF preview - #436
Merged
Merged
Conversation
added 5 commits
October 15, 2026 14:42
… PDF preview - Settings → Résumé → Sections lists the PDF's sections in print order. Each one can move up or down (buttons, with the move announced and focus kept on it), take its own heading (Enter or leaving the field saves, Escape cancels) and be hidden without losing its content. Page size (Letter or A4) and "Reset to default" sit below. Every change saves at once; a failed one is put back, with a toast. - PATCH /api/profile/base-resume/layout saves the layout to both copies of the base résumé, on the résumé as it is now; only the fields sent change, and a malformed layout is a 400. A résumé save, and a replacement read from a file, keep the stored layout whatever the body carries. - The editor now covers everything the PDF prints: location, website and profile links; a role's summary; bullets one per field for roles, schools and projects; a school's start, GPA and bullets; project keywords; a certificate's link. Roles, schools, projects, certificates, links, skill groups, skills and bullets all move up and down. A test checks the fields the renderer reads are ones the editor writes. - Preview PDF and Download PDF for the base résumé use GET /api/profile/base-resume/pdf, which renders the saved résumé and stores nothing (no version row, no file). The preview shows the page count. - A tailored draft carries the base layout it was tailored from, prints with the base résumé's current layout, and keeps the one it was downloaded with. Marking its job applied freezes the layout on its drafts too.
…en't split, stale previews
- Moving a role, school, project or skill group keeps focus on its own Move
buttons; it used to land on a bullet's, which carry the same markers
- Opening a section and changing nothing is not a change, whatever the stored
lines look like ("AWS (EC2, S3)" stays one skill, even after a save)
- Lines split only where the user asks: Enter in a bullet, or a comma in a skill
or keyword, starts the next one after it with the focus there, and a pasted
list becomes one line each. Nothing is split when a field is left
- A new line gets the focus; removing one leaves it on the next line, or on Add
- Project keywords are a list like skills (the comma text box kept a trailing
", " that couldn't be deleted)
- A layout save that fails puts the editor back to what was saved once every
change made is answered, even when a page-size change followed it
- Preview and Download wait for layout changes still being saved, and a preview
made before the latest save is dropped and rendered again
- A hidden section's field keeps its name ("Skills heading") and is described as
hidden; while a section saves, the sections say they're busy, not missing
- The reported CSP allows the preview's blob: frame
…es drafts - PATCH /layout saves both copies through changeBaseResume's one transaction, under the profile row lock; the separate base-version write is gone - A résumé save reads the stored layout inside saveBaseResume's transaction, under the same lock, so a layout change made at the same time is never undone (it was, before: the new pgtest fails on the old read) - pgtests: a layout change whose second write fails changes neither copy, and résumé saves and layout changes at once keep the layout - A job moved into applied, follow-up due, interview, offer or rejected, from a stage before them, freezes the layout on its drafts. Outreach, referrals and archiving don't - The editor test that fills in every field gets 30 s: it timed out at 5 s with the other test files running alongside
The preview was a blob: frame, which needs the browser's PDF viewer: the CSP's object-src 'none' can stop it, and phones don't show a PDF inside a page. The pages are now drawn into canvases with pdf.js (on the main thread, its worker module loaded alongside), one under the other, each named "Page 1 of 2". If they can't be drawn, it says so and points to Download PDF. pdfjs-dist 6.4.299, the version the API's tests already use, is now a web dependency. The CSP change for blob: frames is dropped. Checked in Chromium on a page sent with the app's enforced CSP: the sample résumé draws in full, with no CSP errors.
…retryable preview - The preview loads pdf.js's legacy build: the modern one calls Uint8Array.prototype.toHex, Map.prototype.getOrInsertComputed and Math.sumPrecise, missing in Chrome before ~140 and older Safari. A test reads a résumé the API drew with pdf.js for real (it failed with "toHex is not a function" on the modern build); a page that can't be drawn is logged - A failed preview can be tried again: each preview draws afresh - The layout's headings don't count toward the résumé's 60,000-character cap (they're bounded on their own): a résumé near the cap rendered 413 after a rename - File mode: a résumé save reads the stored layout and writes both copies in one step of the file queue, so a layout change at the same time is never undone - A draft tailored after the job was applied stays unfrozen when the job moves on to an interview; the test now fails without the guard - The PDF preview has its own per-user limit (30 a minute in production), not the AI routes' - A body that isn't JSON, or is JSON null, is a 400, not a 500 - Sections copy: tailored résumés follow it until downloaded or the job moves to Applied
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2b77e814bf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #415. Refs #352, #408, #425, #333.
Why
I want the base résumé to look the way I lay it out, as Jobright does: rename the headings, put the sections in my order, hide one, and have tailored résumés keep that layout. None of it existed. Headings and order were fixed in code, the base résumé couldn't be previewed or downloaded, and Settings couldn't edit fields the PDF prints:
What changed
Sections in Settings → Résumé: every section in print order, with Move up / Move down (each move announced, focus kept), an inline heading (Enter or leaving the field saves, Escape cancels), Hide/Show, the page size (Letter or A4) and Reset to default. Every change saves at once; a failed one is put back, with a toast. No dragging, so nothing needs a mouse.
PATCH /api/profile/base-resume/layout saves both copies of the base résumé in Save the base résumé in one transaction (profile and base version row) #408's one transaction, under the profile row lock. Only the fields sent change, and a malformed layout is a 400 (so is a body that isn't JSON). A résumé save, or a replacement read from a file, keeps the stored layout: it's read inside the save's transaction (one step of the file queue in file mode), so a layout change made at the same time is never undone. The layout's headings don't count toward the résumé's size cap.
The editor covers everything the PDF prints:
Every list moves up and down. Enter in a bullet, or a comma in a skill or keyword, starts the next one; stored lines are never split. A test checks that every field the renderer reads is one the editor writes.
Preview PDF and Download PDF use GET /api/profile/base-resume/pdf, which renders the saved résumé and stores nothing: no version row, no file. It has its own per-user limit (30 a minute), not the AI routes'. The preview draws the pages with pdf.js's legacy build (older Chrome and Safari included) and shows the page count. It's not a frame, because the CSP's object-src 'none' can stop the browser's PDF viewer, and phones don't show a PDF inside a page. A failed preview can be tried again. render-pdf is unchanged and has no caller; render-pdf can create a second base résumé version that disagrees with Settings #425 fixes it.
Tailored drafts carry the base layout they were tailored from and print with the base résumé's current layout. Downloading one, or moving its job into Applied or a later stage (follow-up due, interview, offer, rejected), freezes the layout on it, so what was sent doesn't change. Outreach, referrals and archiving don't freeze it. A draft tailored after applying stays unfrozen until it's downloaded.
Evidence
npm run checkclean.npm test: 699 API, 391 web.npm run test:pg: 59/59 on a fresh database, including:Deploy
API and web together; no migration. The web adds pdfjs-dist 6.4.299. After deploy, on live: