Import the server image reader with its explicit TypeScript extension so tsx does not resolve it as a native addon. Use the workspace automatic JSX configuration for the dev server so shared PDF components do not fall back to React.createElement.
Porygon lays the resume out as a ruled form after the Japanese résumé
form (rirekisho). The header is a block of bordered field cells framed
in the accent color with the photo in a cell of its own, and every
section is a table with a solid title bar over one ruled cell per
entry. Touching cells share one tinted rule.
Smeargle sets the resume like a magazine feature: the headline as an
uppercase kicker over a display name, the summary as an italic
standfirst, and italic section headings at the user's heading size.
Both are one-column and ATS-safe. Register them in the schema, renderer,
semantic manifests and gallery, add gallery and docs previews, extract
the new descriptions, regenerate the API spec, and list them wherever
the docs enumerate templates.
Resource-oriented REST aliases cover file imports (PDF text layer, JSON
Resume, Reactive Resume v4/current, LinkedIn), PDF/DOCX/Markdown/JSON
exports for resumes and cover letters, editor content checks, job-term
matching and a public PDF readability checker. Collection endpoints accept
limit/offset with X-Total-Count headers, and PATCH routes cover partial
updates. Existing routes and response shapes stay unchanged.
The OpenAPI spec marks public operations with `security: []`, the REST
surface gets a 40 MiB body limit, and Better Auth publishes its own
OpenAPI schema. Cookie sessions are rejected for cross-origin requests.
DOCX export parses HTML with node-html-parser so it runs on the server,
and PDF line extraction moves to @reactive-resume/import for reuse.
The Lingui Vite plugin transforms macros itself, so the Babel preset and
its plugin dependency go. TanStack Router's split-route query hides the
`.tsx` extension, so the parser is told to read TSX explicitly.
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.
S3 and Vercel Blob hand back the content type the client declared at
upload time, and the upload proxy served it inline on the app origin, so
an uploaded text/html or image/svg+xml file could run script as the app.
Only raster images (gif, jpeg, png, webp) now render inline; everything
else, PDFs included, is served as an application/octet-stream
attachment. The check runs at serve time, so it also covers objects
stored before this change.
Self-hosted Docker installs served the SPA's CSS, JS and HTML shells without any Content-Encoding. Hono's compress() now wraps only the web routes: it is registered after every API, MCP and upload route, so their streams are never buffered or re-encoded. The Vercel app keeps relying on its CDN, which already compresses.
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.