Commit Graph
6178 Commits
Author SHA1 Message Date
Amruth Pillai bc28441b66 feat(pdf): render resumes with the forme engine
Templates keep their react-pdf-style primitives. A small React renderer
records what they draw, and the result is converted to a Forme document:
styles, fonts (with ligatures disabled for text extraction), Phosphor
icons as SVG paths, pictures fitted by object-fit, and the page map from
source locations. Workarounds for Forme 0.25 layout defects live in the
converter: opaque translucent colours, border insets, row splitting,
column gaps across pages, absolute positioning and repeated backgrounds.

Consumers, the Semantic CSS adapter and the old react-pdf dependencies
follow in later commits.
2026-09-29 06:22:38 +02:00
Amruth Pillai 195fbaaaf4 docs: log M12 in the redesign plan 2026-09-29 01:29:36 +02:00
Amruth Pillai 82e2c92a51 test(e2e): wait for the assistant to settle and for focus to return
Enter in the composer is ignored until the previous reply has finished, so the helper presses again until the message goes; the palette's focus return is polled rather than read once.
2026-09-29 01:29:12 +02:00
Amruth Pillai 9062114379 chore(i18n): extract catalogs for the m12 fixes 2026-09-29 01:24:47 +02:00
Amruth Pillai 92dc11bd2f test(e2e): axe on the main screens, and focus return
Adds @axe-core/playwright and audits the editor's modes, the assistant, both Share tabs, Documents, New, the letter editor, the command palette, Applications, Settings, the shared resume, the ATS checker and sign-in against WCAG 2.1 AA, in light and dark and at phone width. A keyboard spec checks that the Share sheet, the assistant, New and the command palette return focus to what opened them.
2026-09-29 01:22:21 +02:00
Amruth Pillai 62572a20ca fix(web): accessibility and tablet fixes from the M12 audit
- The resume and letter canvases are focusable, labelled regions, so the page
  scrolls from the keyboard even before its lines load.
- Closing the assistant returns focus to the ✦ button.
- The ✦ button shows on tablets and phones too, where the assistant opens as
  a drawer or full screen.
- On tablets, Download PDF drops its label so the bar fits beside the mode
  switch.
- The phone reflow darkens a template colour that's too faint to read on
  white, and the checked download format's extension steps up to ink-2.
2026-09-29 01:22:20 +02:00
Amruth Pillai dcddc5639a fix(ui): 44px touch targets and readable descriptions on tinted rows
On coarse pointers, buttons, switches, checkboxes and tabs get an invisible hit area of at least 44x44 centred on them, without changing how they look. A checked switch row's description steps up to ink-2, since ink-3 falls short of 4.5:1 on the accent tint. Adds contrastOnWhite to the colour utilities.
2026-09-29 01:22:19 +02:00
Amruth Pillai 55a3284360 refactor(web): move the app chrome from phosphor to material symbols
Auth pages, dialogs, the command palette, the user menu, Applications, the rich-text toolbar, the stylesheet editor and the error screens use the Material Symbols set instead of Phosphor; the icon subset gains the 35 glyphs they need. Phosphor stays for brand logos, the landing page and the icons printed on resumes. Also removes the form field and test mock that only wrapped the old rich input.
2026-09-29 01:01:16 +02:00
Amruth Pillai f8767c32f1 refactor(ui): retire the shadcn colour aliases
Every class that used the old names (muted, primary, card, border, input and the rest) now uses the Desk & Paper token it resolved to, with text colours on their text tokens (accent-text, danger-text, ink-3). The compatibility block in the theme is gone.
2026-09-29 00:56:59 +02:00
Amruth Pillai 3a69dfc4d5 refactor(ui): remove the agent chat primitives and sidebar
Attachment, bubble, empty, marker, message, message scroller, questionnaire, resizable and sidebar had no users after the assistant moved into the editor, and neither did react-resizable-panels or @shadcn/react.
2026-09-29 00:55:50 +02:00
Amruth Pillai f2eb230f69 docs: log M11 in the redesign plan 2026-09-29 00:54:00 +02:00
Amruth Pillai d2ef001be9 test(server): give the first app route test time for the cold import
It imports the whole app and timed out under parallel runs.
2026-09-29 00:53:31 +02:00
Amruth Pillai 3604d6feb2 chore(i18n): extract catalogs for settings and public pages 2026-09-29 00:50:33 +02:00
Amruth Pillai 991d7e0c32 docs: describe the new ATS checker and its fix flow 2026-09-29 00:50:21 +02:00
Amruth Pillai 2ca57fab80 test(e2e): check a file on the public checker and fix it after signing up
Checks the sample file, reads it as software does, then signs up from Fix these in the editor and lands in Check on the imported resume.
2026-09-29 00:50:21 +02:00
Amruth Pillai d9ed63f720 feat(web): rebuild the public ATS checker around fixing the file
Idle is a drop zone with "Check a sample file" and an optional posting; busy
shows three labelled steps; the result is a 440px column with the score ring,
the categories that could cost a match (and the posting's missing terms), and
"Fix these in the editor", beside the file shown two ways: the original page
and the text as software reads it. Phones get one column with the fix pinned.

"Fix these in the editor" imports the same file, read in the browser as the
check was, and opens it in Check. Visitors sign up first; the file waits in
IndexedDB and is imported when they're back.

The public checker's AI review is dropped (Check -> Writing covers it), with
its locked card, the marketing parse preview and the old uploader. The PDF
viewer also renders a given file, for the original page.
2026-09-29 00:50:20 +02:00
Amruth Pillai 6282bf77db test(e2e): the shared page on phones, and links that aren't shared
Adds a phone visit that checks the reflow, tap-to-mail contacts and the pinned Download and Share, and an unknown link's message. The download preference spec expects the page's single Download button, and the root spec the new footer credit.
2026-09-29 00:41:54 +02:00
Amruth Pillai cc28f78537 feat(web): the shared resume page, reflowed on phones
Desktop and tablets get a 64px bar with the owner's name, headline and city,
Copy link and Download PDF, the page on the sunken canvas, and the footer
credit. Phones get the resume as readable text in the template's colour and
body font, in the order the PDF prints it (read from the semantic tree), with
contact details as tap targets and Download and Share pinned.

The owner's rich text is parsed into an allowlist of formatting elements and
plain links, never injected. With downloads off, Download is hidden and
printing shows a note instead of the page. Off, unknown and trashed links all
read "This resume isn't shared right now." with nothing about the owner.
2026-09-29 00:41:54 +02:00
Amruth Pillai a043b28867 docs: point the guides at the new settings pages
Guides now name Account, Preferences and AI & developer, the new key flow, and what the account export contains.
2026-09-29 00:33:44 +02:00
Amruth Pillai abca49120c test(e2e): cover the settings pages
Old addresses redirect, the name saves on blur, Export everything downloads a zip, the theme applies at once, and a new key is shown once and revoked with Undo.
2026-09-29 00:33:43 +02:00
Amruth Pillai 9f809890e4 feat(web): settings in three pages that save as you go
Six settings pages become Account, Preferences and AI & developer, with an
in-page nav beside a 680px column (tabs on tablets, a three-row root on
phones, which the Account tab opens).

- Account: photo upload, name, username with the instance host, and email,
  each saving on blur; password, a two-step verification switch, passkeys and
  connected sign-in; Export everything as one zip of documents and
  applications; Delete account with the counts of what goes and typing
  "delete"; Sign out.
- Preferences: Light, Dark and System tiles, the interface language and the
  motion note.
- AI & developer: provider rows with Test ("Connected · 420 ms" or the exact
  error) and an Edit dialog holding the switch and delete; Add provider with
  all sixteen; API keys in a table with a New key dialog (30 days, 90 days,
  Never), the key shown once, and Revoke with Undo; the MCP address with Copy.

The old settings addresses redirect, and links across the app, the command
palette and the user menu point at the new pages.
2026-09-29 00:33:37 +02:00
Amruth Pillai 55db11aea8 feat(api): include applications in the account export
Export everything downloads documents and applications together, so the account export now returns the user's applications, without the owner id.
2026-09-29 00:33:29 +02:00
Amruth Pillai cbd47ec1fb docs: log M10 in the redesign plan 2026-09-29 00:11:23 +02:00
Amruth Pillai 4acc5f19d4 chore(i18n): extract catalogs for the assistant 2026-09-29 00:11:22 +02:00
Amruth Pillai 3ef8eaeb26 test(e2e): drive the assistant and Improve against a scripted provider
A local OpenAI-compatible stub plays a short conversation: read the document, propose an edit, ask a question when asked, and stream slowly enough to stop. The spec connects it in place, accepts an edit and checks it was saved, answers a question, stops a reply, improves a line, asks from the command palette and follows the old /agent links. The E2E workflow allows the loopback base URL the stub needs.
2026-09-29 00:11:21 +02:00
Amruth Pillai e1d5b9ba9b feat(web): the assistant beside the document, and Improve in every text field
The assistant opens beside the resume or letter it works on: a third column
at 1280px and wider, in place of the left panel from 1024px, a drawer below
that, and full screen on phones. The bar's button and Mod+J toggle it.

The panel sets up a provider in place, suggests what to ask, streams replies
with Stop and Continue, asks clarifying questions, and shows proposed edits as
change sets with page marks, the outline's "n proposed" pill and the page
caption. Context chips decide whether the next message shares the document and
the posting. Errors keep the message with Retry and Switch model, past
conversations are grouped by document with their outcomes, and the
conversation can be copied as a transcript.

Improve in the rich-text toolbar suggests a stronger verb, a result, a shorter
line or the user's own request for the line holding the caret, and replaces it
only on Replace.

The Agents pages, the builder's assistant sheet and the application copilot
panel are removed; /agent links redirect to the document with the assistant
open. Command palette Ask, Job match's missing terms, Applications' Prepare for
next step and Copy for a job all open the assistant on the right document.
Removes the unused react-resizable-panels and @shadcn/helpers dependencies.
2026-09-29 00:11:16 +02:00
Amruth Pillai 26044b5346 docs: regenerate the OpenAPI spec and JSON schema guide
The published spec had fallen behind the runtime routes, including the cover-letter draft and version routes, and the schema guide was missing Check metadata. The OpenAPI test now lists the new letter routes and the Trash wording.
2026-09-29 00:11:06 +02:00
Amruth Pillai 6463a9e9c1 feat(api): an assistant bound to a resume or letter that proposes edits
Threads belong to a resume or a cover letter (agent_threads.cover_letter_id)
and count the edits they proposed and the user accepted. The propose_edits
tool rewrites or adds passages by id, resolved against the document as it is
now, and the edit statuses are stored with the message. read_letter joins
read_resume; apply_resume_patch, approvals, revert and archiving are removed.

Message context lets the user leave the document or the linked posting out of
a message; the posting now includes the application's notes. Redis is
optional: without it replies stream directly.

The proposal core (passages, additions, states) moves to
@reactive-resume/resume/proposals so the server and the web app share it.

Adds ai.improve, which suggests a rewrite of one line (stronger verb, a
result, shorter, or the user's own request) and says when it states
something new.
2026-09-29 00:11:00 +02:00
Amruth Pillai e86179187e docs: log M9 in the redesign plan 2026-09-28 23:08:13 +02:00
Amruth Pillai 12c7869ea7 chore(i18n): extract catalogs for letters 2026-09-28 23:08:12 +02:00
Amruth Pillai 2bf88584e8 test(e2e): drive the letter editor 2026-09-28 23:08:11 +02:00
Amruth Pillai 82fa327900 feat(web): letters on the editor shell, with drafting, History and both files to download 2026-09-28 23:08:10 +02:00
Amruth Pillai aa9a5113c0 fix(pdf): keep the letter's header when the page tree is built 2026-09-28 23:08:10 +02:00
Amruth Pillai fdad39c632 feat(api): structured letters with live links, versions and a streaming draft 2026-09-28 23:08:04 +02:00
Amruth Pillai c061367a27 docs: log M8 in the redesign plan 2026-09-28 22:15:25 +02:00
Amruth Pillai 2a41d45efc chore(i18n): extract catalogs for applications 2026-09-28 22:14:22 +02:00
Amruth Pillai 8dc45ee7e7 test(e2e): drive the rebuilt applications page 2026-09-28 22:14:21 +02:00
Amruth Pillai 462db6011a feat(web): applications as a grouped list, a rebuilt detail sheet and an add dialog
- The page: CSV import/export behind one icon, Add application, a dismissible
  nudge for the application waiting longest without a reply (10+ days),
  List · Board · Insights · Calendar, search across role, company, contacts
  and tags, and Show closed.
- List (the default): grouped by stage in the order that needs you first,
  with collapsible groups, the next step (warn when overdue), what was sent,
  sorting from the headers and row checkboxes for bulk moves, tags, closing
  and deleting. Phones get two-line rows and no board.
- Board: a column per stage; drops and Move to… show the same toast.
- Detail sheet (480 px, full screen on phones): the stage stepper with Move to
  next, the next step (the next interview or follow-up) with Edit and Add to
  calendar (.ics), what was sent (opening the version sent, read-only, in
  History), Tailor a resume and Write a letter, editable salary and source,
  contacts, tags, autosaved notes and the activity timeline. Close
  application… takes a reason; Delete is in ⋯ and asks first.
- Add dialog: paste a link or posting; its role, company and requirements are
  read into editable fields and the posting is saved with the application.
  Add, or Add and tailor a resume.
- Insights: how far applications get (from their stage history), how many
  heard back and how fast, and tailored against base resumes, above the
  existing charts.
- CSV import shows how columns were matched before saving and lets you
  download the rows it skips; export adds the closed reason.
- The builder opens History on a version from ?version=.
2026-09-28 22:14:20 +02:00
Amruth Pillai ce0b4c606a feat(api): a closed stage with reasons, what was sent, posting requirements and a posting reader
- Applications end in a `closed` stage with a reason (not selected, withdrew,
  accepted another offer, no response). The migration moves `rejected` to
  closed + not selected and archived applications to closed, rewrites
  `rejected` in their stage history, and ships rollback.sql for older
  versions. `archived` stays, deprecated; `rejected` is still accepted as
  input and means closed.
- Once an application with a linked resume reaches Applied, the resume is
  saved as a "sent" version named after the company, and the application keeps
  its id and the resume's Check score then.
- New columns: closed_reason, cover_letter_id (backfilled where exactly one
  letter was written for the application), sent_resume_version_id,
  sent_check_score and requirements.
- applications.ai.parsePosting reads a pasted link or posting. Links are
  fetched on the server: https only, public addresses checked at connect time,
  three redirects, 2 MB and 10 s at most. A page's JobPosting data fills the
  fields without AI; with a provider, the model reads role, company,
  location, salary and requirements.
- MCP application tools take closedReason and coverLetterId.
2026-09-28 21:44:55 +02:00
Amruth Pillai f5a3fa35eb docs: note the DOCX page alignment in the M7 log 2026-09-28 21:35:51 +02:00
Amruth Pillai 25306630e7 fix(docx): print pages the way the PDF does for one-column templates and full-width pages 2026-09-28 21:35:39 +02:00
Amruth Pillai 4fbc0d7b1d docs: log M7 in the redesign plan 2026-09-28 21:34:16 +02:00
Amruth Pillai 1253ef08a6 chore(i18n): extract catalogs for check mode 2026-09-28 21:33:43 +02:00
Amruth Pillai 3397c77917 test(e2e): drive check mode's issues, job match and parser view 2026-09-28 21:33:42 +02:00
Amruth Pillai 565424631a feat(web): check mode with pinned issues, job match, a writing review and proposals
- The score ring (live checks only, easing to each new score), the verdict,
  and Issues · Job match · Writing tabs.
- Issues: numbered cards with category, explanation and fix, pinned to their
  lines on the page with warn pins and wavy underlines. One-step fixes apply
  with undo; the rest open the field in Write. Show on page, and Ignore (Keep
  for the two-column issue), stored with the resume. Category rows below, open
  when they need attention. "Also check the exported PDF" reports in a toast.
- Job match reads the linked application's posting, or a pasted one that can
  be saved as an application. Missing terms ask where they belong (Add to
  Skills) or can be hidden; covered terms light up their entries on the page.
- Writing: an opt-in AI review that says what it sends. Rewrites of bullets
  and paragraphs arrive as proposals: struck-through old text and highlighted
  new text on the page, numbered markers, Accept, Reject, Accept all, A/R and
  arrow keys, one undo step. Out-of-date proposals can't be applied.
- "What a person sees / What a parser reads": the parser view extracts the
  text of the PDF on the page, in reading order, flagging issue lines.
- Phones step through issues on the page; tablet pins open the drawer.
- The builder's ATS section and its deep-check UI are gone.
2026-09-28 21:33:41 +02:00
Amruth Pillai 1608d56903 feat(api): passage rewrites in the writing review, and a resume's linked application
- ai.atsReview takes optional passages (id, where, text). A suggestion that
  rewrites one names it in passageId with the whole new passage, which the
  editor offers as a proposal to accept or reject.
- The review prompt fills its placeholders in one pass, so resume text that
  looks like a placeholder is sent as it is.
- resume.getById returns applicationId, the job application a resume was made
  for, so Check's job match can read its posting.
2026-09-28 21:33:18 +02:00
Amruth Pillai e26ce08fc5 feat(resume): check categories, stable issue keys, ignores and a two-column rule
- Every live check has a category (contact details, dates, layout, section
  headings, writing). Reports score the applicable rules, per category too.
- Findings carry a key that uses entry ids instead of array indexes, so it
  survives reordering. Keys in the new metadata.check.ignored set findings
  aside without counting them against the score.
- TWO_COLUMN_LAYOUT flags a two-column template that prints a sidebar.
- Full-width pages print no sidebar, so sections placed only there are now
  reported as never printing instead of passing as main-column content.
- metadata.check also holds job-posting terms hidden as not true; public
  viewers don't receive it.
- ats-pdf exports the job-description matcher, spelling variants and the
  semantics reader for the editor's job match and parser view.
2026-09-28 21:33:11 +02:00
Amruth Pillai 0e36729b80 docs: log M6 in the redesign plan 2026-09-28 20:46:23 +02:00
Amruth Pillai 7f162bf230 fix(web): satisfy Biome in the first-paint theme script and loader styles 2026-09-28 20:45:01 +02:00
Amruth Pillai 7831cc04a7 chore(i18n): extract catalogs for documents, trash and the new-document dialog 2026-09-28 20:39:05 +02:00