Keep manual preparation available without provider credentials.
Share bounded search and reading across Applications and the assistant
with one selected Firecrawl, Tavily, or Exa connection.
Backfill encrypted Firecrawl credentials without decrypting or removing
legacy rows. Add nullable posting-source metadata to applications.
Fix authentication recovery, account imports, application tracking, resume
editing and exports, sharing, API contracts, provider selection, and private
local attachments. Preserve authored content during PDF pagination.
Update guides, generated OpenAPI output, and translation catalogs to match
verified behavior and documented constraints.
Validation: 1,019 tests passed; 12 database/OAuth integration tests skipped.
Ten affected package typechecks, production build, Biome, and package
boundaries passed.
Every scrolled frame repainted the whole sticky stage: the fading title, contact sheet, labels and caption painted into it, and the template wipe animated clip-path over full pages. Android Chromium could not raster that during a fling and showed blank tiles.
Give each scroll-driven element its own layer, fade the contact sheet once instead of per thumbnail, and wipe by sliding clipped layers and moving the divider with a transform. Raster work across the scene drops about sevenfold in an emulated Pixel 7 trace.
The unanchored screenshots pattern in .vercelignore also matched
apps/web/public/screenshots, so the manifest's screenshots 404ed on Vercel.
Anchor it to the root and replace the screenshots with the redesigned app.
Replace the Type presets with five traditional resume faces: EB Garamond,
Tinos, Source Serif 4 with Source Sans 3, Carlito and Lato. Presets now set
body text in Regular and Bold (400/700), which every one of them ships.
Add a Custom row under the presets. It shows the resume's own fonts when they
match no preset, and opens Advanced (even when collapsed) at the Typography
editor, focusing the body font picker. On phones it switches the Design sheet
to the Page tab first.
The mobile editor body now clips instead of hiding overflow, so scrolling
inside the lowered Design sheet can no longer shift the sheet itself.
Vercel now deploys Reactive Resume as two services in one project:
`frontend` serves the static Vite build from apps/web/dist, and
`backend` runs the Hono server Function from apps/server. Top-level
rewrites send server-owned paths (/api, /uploads, /mcp, /.well-known,
robots.txt, sitemap.xml, llms.txt, schema.json, index.html) and every
path without a file extension to `backend`, so HTML shells keep their
injected SEO metadata. Paths with a file extension go to `frontend`.
The service builder needs a few accommodations:
- apps/server/vercel.mjs replaces api/index.mjs as the entrypoint,
because a service entrypoint must exist before the build runs.
- `outputDirectory: "."` stops the builder from using the Docker
entrypoint in dist/ as the Function handler.
- The builder loads external CommonJS dependencies through pnpm links
that it leaves out of the Function. ioredis and react-reconciler are
now bundled with their dependencies, and bcrypt is replaced with
bcryptjs, which reads and writes the same $2b$ hashes.
The Vercel compatibility workflow now builds with the services
framework and loads a copy of the backend Function outside the
checkout, so a dependency missing from the Function fails CI. The stale
PDFKit trace checks are removed.
Existing Vercel installations must set Framework Preset to Services
before redeploying; the self-hosting guide documents this.
- Add a Questions section to the homepage: eight answers as folded paper
notes that unfold from their crease, with a pencil circle and underline
drawn on open, a new doodle, and FAQPage structured data rendered from
the same localized answers.
- Prerender /ats-checker per locale alongside the homepage
(dist-prerender/<page>/<locale>.html) and add a "What it checks"
section; PDF.js and the importers now load only once a file is chosen.
- Make the server the single owner of SEO head tags: canonical, hreflang,
social cards with og:locale and the page's localized title, and JSON-LD
with an Organization entity. The router now sets only the title,
description and robots tags, so nothing is duplicated after startup and
the ATS checker keeps its structured data.
- Add hreflang alternates to the sitemap, expand llms.txt, and delete the
stale v5 robots.txt and sitemap.xml from public/.
- Draw decorative heading and typed-text layers as generated content, so
page text holds each heading once.
Undo leaves the desktop editor bar (it stays on ⌘Z), leaving history,
the assistant, Share and Download. A public resume now shows a "Public"
badge on the Share button instead of an unlabelled dot, and the button's
accessible name says so. The first page caption drops "· click any line to
edit it" and its reserved height.
The document menu's Information item opened a donation appeal and a row of
project links, which isn't what anyone expects from a resume's menu. It's now
Details: when the resume was created and last edited, its template, language
and page count, and whether it's private or shared (with its link). The
resume API returns `createdAt` for this.
The donation ask moves to the moment someone has what they came for: after a
successful download in Share & export (resumes and cover letters), one quiet
line wishes them luck and links to Open Collective. The old dialog's links
(docs, source, translations, bug reports, donate) live in the Settings
sidebar footer.
"Everything you've done, on one page." A single resume page is assembled,
written, restyled, checked, tailored and shared as the visitor scrolls,
followed by live community numbers, languages, a support receipt, a
closing call to action and the footer. It replaces the previous homepage
and its playgrounds.
Motion: a small scroll engine (features/homepage/scroll.ts) writes each
section's progress as --p on every animation frame, and the scenes derive
their motion from it with CSS calc(). React only re-renders on coarse
steps held in a zustand store. Reduced motion collapses every pinned
scene to one screen at its end state and stops all ambient motion.
Server-rendered copy: the web build now prerenders the homepage once per
locale (Vite app builder, features/homepage/prerender.tsx) into
apps/web/dist-prerender, which the server sends for "/" by `?locale=`,
then the saved locale cookie. main.tsx waits for the route to load before
React replaces the prerendered page, so it never flashes a loading screen.
dist-prerender is added to turbo outputs, the Docker image and the Vercel
function files.
SEO: localized title and description, a canonical per locale, hreflang
alternates for every locale over `/?locale=` addresses (the app now reads
that parameter), and SoftwareApplication JSON-LD. The FAQ structured data
is dropped because the page shows no FAQ.
Also: self-hosted Anybody and Martian Mono (landing only) and Newsreader
italic, graphite doodles as WebP, new Material Symbols in the icon subset,
the pull-cord theme switch on the app's theme cookie, and new catalog
strings extracted for translation.
The cover-letter and MCP OAuth flow suites were gated on environment variables
CI never set, so they never ran. Point them at the job's PostgreSQL. The
cover-letter suite works in its own schema; the OAuth suite gets a database of
its own because it writes signing keys under its own secret, which the e2e
server can't decrypt.
Nothing reads the unit step's coverage report, so test:ci no longer collects it.
Prune the unit and e2e suites to what protects security, user data, public
contracts and past regressions. Drop what slowed development without catching
bugs: markup and label assertions, wrappers that only proved Base UI works,
copied inventories and snapshots, mock call-shape checks, permutation matrices
across templates, fonts and locales, and env-gated suites that never ran in CI.
- Unit: 4749 tests in 421 files become 1212 in 227. PDF tests that rendered or
rasterized every template, font and locale combination go; one render per
template stays and now checks that every visible section reaches a page,
which a blank page used to pass.
- E2E: 55 tests in 35 specs become 11 in 9, one journey per severe area: sign-up
and sign-in, autosave, a failed save during navigation, JSON export and import,
public and password-protected sharing, slug redirects, OAuth consent for MCP
clients, and the assistant.
- Tests a coverage review found to be the only guard of a contract were kept or
restored, each checked by breaking the code it guards.
- Remove exports, tooling and dev dependencies that only the pruned tests used.
- The Semantic CSS guide check now reads indented code fences, so the guide
example it skipped is compiled too.
The sheet bodies scroll with no top padding, so the 4px focus ring on the
first control (the job description accordion, a field or a button) was cut
off. Pull each body up 4px and pad it back so the ring fits and the layout
doesn't move.
Resume and letter ids are UUIDv7, so their first eight characters are a
timestamp. Two documents with the same name created within about a minute
got the same path in the "Export everything" zip, and one silently
replaced the other. Name each file with the full id instead.
Remove the revert and unset keywords, vw/vh units, the |= and $= attribute
matchers, :nth-child(... of ...), the stylesheet analyzer and the
engine-unsupported warnings. inherit and initial remain the CSS-wide keywords.
One PasswordInput replaces seven copies of the show/hide password field. The OTP code fields get a label, prompts and keyword and name inputs ignore Enter during IME composition, and a failed profile save shows an error instead of hanging.
The color picker trigger receives the id and label FormControl gives it, and the address field no longer overwrites what you are typing when its own auto-save lands. Picture settings, the share tabs and the public page's copy button are split into shared parts and compile with the React Compiler.
usePageScale, CanvasStatusPill and DocumentMenuTrigger replace code duplicated across both builders, the letter bar and share sheet compile with the React Compiler, and a viewed letter version is fitted with its own page format.
Also guards the composer's Enter for Safari IME, gives the undo-detection effect real dependencies, lets the composer grow with its text, names the API keys table and moves acceptResumeProposals next to the other proposal logic.
The retry counter was missing from the subscription effect's dependencies, so after any stream error the builder stopped receiving AI, MCP and other-tab edits until a reload. Entry and section rows, the preview, the PDF canvas, check, export and the custom CSS editor now compile with the React Compiler, and the CSS editor no longer blanks its color swatches on every keystroke.
The ATS sample fetch checks the response and reports failures, the ats-pdf chunk loads alongside extraction, and the feature explorer and command palette pages are split into one component per view.
- Every field in the add/edit application sheet gets an accessible name.
- Enter no longer commits half-converted IME text (new isImeComposing helper).
- Relative-time formatters are cached per locale instead of built per row.
- Import, font fetch and interview/list state handle errors and prop changes without a stale frame.
- The remaining React Compiler bailouts in these features are gone, and the application form and new-document dialog are split into smaller parts.
Under @babel/core 8, babel-plugin-react-compiler 1.0 skipped every function with a destructuring default without saying so, which left about 70 components unmemoized, including every packages/ui primitive. A test compiles a component with a default prop through the same preset and fails if that comes back.
Stored resumes, resume versions, letters and letter versions still in
the old editor's legacy mode, or carrying legacy style rules with no
stylesheet, are converted to Semantic CSS once, right after the SQL
migrations, and the result is recorded in a new data_migration table so
later starts skip it. Only metadata.stylesheet is rewritten (the rules
stay for rollback), a row edited meanwhile is retried on the next start,
and a failure leaves the data as it was without stopping the server.
The API no longer converts on every read and save. Imports of old
Reactive Resume JSON exports convert their legacy rules on the way in.
Now that the API hands out every resume with its legacy style rules
converted, the renderer runs one styling system: the stylesheet mode,
the per-slot legacy rule hooks in the template primitives and the
section style provider are gone, and the Custom Styles editor loses its
legacy draft banner and Activate step. The converter stays (the API
uses it), with its fixtures now checked by rendering the converted
stylesheets.
With Custom Styles open, clicking a section or entry on the page adds a
rule for it under a comment naming it (or moves the cursor into its
existing rule). While the cursor is in a rule, everything it matches is
outlined on the page. Autocomplete offers sections and entries by name
and inserts their selector, instead of escaped UUIDs. The guide gains a
Style one element section, including the :nth-of-type form.
The phone tab bars (resume builder, letter builder, dashboard) drop the
pill behind the active icon for a short accent bar on the tab's top
edge. Switching tabs springs it across with a little overshoot
(spring 0.4s, bounce 0.3); reduced motion moves it at once. Dashboard
tab taps swap the page without the cross-fade, like a native tab bar,
so the bar's movement isn't hidden behind the page transition.
Custom styles no longer need @version 1; at the top: the language
version already lives beside the text, so new and converted stylesheets
leave it out, the editor hides it in older ones, and the compiler
ignores it. The validity status and inline diagnostics are gone too:
whatever applies shows in the preview and anything else is left out.
Scrolling the Design panel over the template cards jumped it back:
browser scroll anchoring misfired in the editor's panel scroller, so it
is off there. The panel was also one scroller shared by every mode, so
Design opened at Write's scroll position; each mode now gets its own.
In-page URL updates (mode, filters, open sheet) no longer ask the router
to reset or restore scroll.
The write panel is narrow, so an entry's fields now take the whole
column. Start and end dates still share a line and now span it, with
Present below. A new entry's badge just says Draft.
Motion's reducedMotion="user" only drops transforms, so opacity fades
still ran and could be caught half-faded (axe flagged the assistant).
The design makes every duration instant under reduced motion, as the
CSS tokens already do.
The phone entry screen pushes in from the end edge and slides back out on
Back, reversing mid-push. The Design sheet keeps its full height and moves
by translate instead of animating height, and rises from the tab bar when
Design opens. The selection bar rises and fades in.
Sheet is now backed by Base UI Drawer: bottom sheets follow a downward
swipe, dismiss on a flick or a drag past half their height, and settle back
otherwise. Side and top sheets are unchanged.
On phones, Settings rows push the section in from the end and the back link
returns the list from the start, layered on the page view transition.
Views that replace each other (new-document steps, auth post-submit
screens, ATS checker states, consent, social sign-in, new API key) now
fade up 4px as they arrive. The ATS lenses both stay mounted so switching
back never re-reads the PDF, and the 1024-1279 assistant swap crossfades
in the panel's cell (instant on Cmd+J).
Things that used to push layout no longer move what the user is reading:
the bulk-selection bar floats at the bottom of the list, the rich text
toolbar sits under the text, and the letter draft bar takes the Stop
button's place under the draft. The drop-to-import frame, follow-up
nudge, job-match panel, public-link options, and rare save states fade in.
Adds a Swap primitive so copy buttons crossfade Copy -> Copied at a fixed
width, and POP_CLASS for status icons (progress checks, tool status,
proposal Applied, the public download icon).