Commit Graph
981 Commits
Author SHA1 Message Date
Amruth Pillai bc92b8cf7f fix: finalize v6 migrations and document workflows 2026-10-01 07:06:15 +02:00
Amruth Pillai 8105889b0c style(web): align AI provider and web access headers 2026-10-01 04:25:17 +02:00
Amruth Pillai 5d540f75d6 feat(web): refine integrations and suggest AI models 2026-10-01 04:11:49 +02:00
Amruth Pillai 3e14ecaab8 chore: update dependencies 2026-10-01 03:07:07 +02:00
Amruth Pillai fcfb18fe5a chore: migrate linting and formatting to Oxc 2026-09-30 22:53:16 +02:00
Amruth Pillai 3e7849e51f feat: save jobs and prepare applications
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.
2026-09-30 22:43:45 +02:00
Amruth Pillai 0bdf8066cd fix: make monorepo checks and caching consistent 2026-09-30 22:32:05 +02:00
Amruth Pillai bec152b7e9 feat: add shared AI and Firecrawl job search integrations
Add managed credentials, job posting search and scraping, database migration, translations, and self-hosting documentation. Fix empty resume-copy recovery and ensure every sheet popup renders inside a drawer viewport.
2026-09-30 21:00:43 +02:00
Amruth Pillai ec0932c5d2 fix(ui): unify fonts and prevent footer text clipping 2026-09-30 15:11:56 +02:00
Amruth Pillai 50af92e2fc fix: drain server requests and update deployment smoke 2026-09-30 06:21:46 +02:00
Amruth Pillai 9a803305c8 fix: address verified v6 app findings
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.
2026-09-30 06:15:09 +02:00
Amruth Pillai d6f03a3e67 fix(web): stop the landing design scene flickering on android chromium
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.
2026-09-30 06:07:16 +02:00
Amruth Pillai 31c58baed4 feat(applications): enhance application stage tracking and improve form handling 2026-09-30 01:27:21 +02:00
Amruth Pillai 36ba314e2a chore(deps): update @dotenvx/dotenvx to version 2.32.1 and @babel/core to version 7.29.7 2026-09-30 01:14:44 +02:00
Amruth Pillai 49fcc630d1 fix(web): refresh the PWA screenshots and ship them to Vercel
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.
2026-09-30 01:12:40 +02:00
Amruth Pillai d3ed651d65 feat(web): suggest traditional resume fonts and link to custom fonts
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.
2026-09-30 01:09:31 +02:00
Amruth Pillai 725be158c0 feat(deploy): deploy Vercel as frontend and backend services
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.
2026-09-30 00:17:24 +02:00
Amruth Pillai f8981502f1 feat(web): add an FAQ to the landing page and tighten SEO for answer engines
- 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.
2026-09-30 00:16:40 +02:00
Amruth Pillai 8ded9de46e chore: update versioning and dependencies, remove unused configuration files 2026-09-29 23:34:31 +02:00
Amruth Pillai f82e9f33f3 fix(web): use the brand icon in the app sidebar and rail
The sidebar and the collapsed rail drew a placeholder "Rr" tile; they now
show the real logomark.
2026-09-29 23:25:15 +02:00
Amruth Pillai 5b84bf800a feat(web): trim the editor bar and mark a public resume on Share
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.
2026-09-29 23:25:13 +02:00
Amruth Pillai 01e87a6f1b feat(web): show resume details in the document menu and ask for donations after a download
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.
2026-09-29 23:24:15 +02:00
Amruth Pillai 48b27802fd test: remove redundant cases and repeated setup 2026-09-29 23:17:30 +02:00
Amruth Pillai b399e289d6 feat(web): redesign the landing page as one scroll-driven story
"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.
2026-09-29 22:34:16 +02:00
Amruth Pillai 8a2fa26451 ci: run the database-backed suites and drop coverage from the unit step
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.
2026-09-29 22:31:56 +02:00
Amruth Pillai c0c7984712 test: keep only the tests that guard real breakage
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.
2026-09-29 22:31:45 +02:00
Amruth Pillai c6c51e5b23 fix(web): stop application sheets clipping the top focus ring
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.
2026-09-29 22:08:11 +02:00
Amruth Pillai 61f481055a fix(server): serve untrusted uploads as downloads
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.
2026-09-29 22:07:00 +02:00
Amruth Pillai e74403e12f perf(server): compress the web app's static files and HTML shells
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.
2026-09-29 22:04:03 +02:00
Amruth Pillai a9c4b84d38 fix(web): keep every document in the account export zip
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.
2026-09-29 22:03:19 +02:00
Amruth Pillai 16008a95d6 refactor(web): leave moving sections between columns to the write panel 2026-09-29 18:50:01 +02:00
Amruth Pillai 394e59e1af refactor(resume): drop semantic css features without a clear pdf meaning
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.
2026-09-29 18:50:00 +02:00
Amruth Pillai a6ef340c09 feat(web): add a password input and label the two-factor code fields
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.
2026-09-29 18:13:12 +02:00
Amruth Pillai 39f30ac9a1 fix(web): name the color picker and keep a typed address while it saves
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.
2026-09-29 18:13:12 +02:00
Amruth Pillai 7304c38303 refactor(web): share page canvas chrome between the resume and letter builders
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.
2026-09-29 18:13:11 +02:00
Amruth Pillai 59337fcd51 refactor(web): let the assistant, theme and ai settings compile with the react compiler
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.
2026-09-29 18:13:10 +02:00
Amruth Pillai 18597a6de0 fix(web): reconnect live resume updates after a stream error and let the builder compile
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.
2026-09-29 18:13:09 +02:00
Amruth Pillai c0e9d3fc62 refactor(web): let the homepage, command palette and ats checker compile with the react compiler
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.
2026-09-29 18:13:08 +02:00
Amruth Pillai 38447bd041 fix(web): label the application form and fix react doctor findings in applications and documents
- 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.
2026-09-29 18:13:07 +02:00
Amruth Pillai 861feb22eb fix(server): keep replacement patterns in resume text literal in page metadata
A name or summary containing $&, $' or $$ was expanded by String.replace into chunks of index.html, garbling the public resume's head. The inserts now use function replacers.
2026-09-29 18:13:07 +02:00
Amruth Pillai 73a3dfc423 fix(web): keep babel 7 so the react compiler compiles every component
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.
2026-09-29 18:13:06 +02:00
Amruth Pillai 92459122c5 feat(server): convert legacy style rules with a manual script instead of at startup
The conversion of stored legacy style rules to Semantic CSS no longer runs
when the server starts. The image now ships
apps/server/dist/migrate-legacy-styles.mjs, run by hand against
DATABASE_URL:

- without flags it's a dry run that converts in memory and reports counts
- --apply --backup <file> converts, appending every replaced stylesheet to
  the backup file before its row is written
- --restore <file> puts those stylesheets back, except on rows edited since

Each table is scanned once for the rows that need converting, then they're
converted in batches with progress logged. Only metadata.stylesheet is
rewritten, a row whose stylesheet changed after it was read is left alone,
and running it again skips what's converted. The data_migration table that
recorded the startup run is gone. The self-hosting guide explains the
one-time run.
2026-09-29 16:57:21 +02:00
Amruth Pillai c8aead4ff0 feat(server): convert stored legacy style rules once at startup instead of on every read
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.
2026-09-29 16:25:50 +02:00
Amruth Pillai a325232a09 refactor(pdf): render every resume with semantic css and drop the legacy styler
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.
2026-09-29 15:25:44 +02:00
Amruth Pillai cfb272229b feat(web): pick an element on the page to style it in custom styles
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.
2026-09-29 14:53:23 +02:00
Amruth Pillai 9acf289b11 feat(web): mark the active phone tab with a bar that springs between tabs
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.
2026-09-29 14:44:16 +02:00
Amruth Pillai 25131b3a82 chore(web): refresh translation catalogs
Adds the new Draft label and drops messages whose source is gone (the
stylesheet status, the removed notes editor).
2026-09-29 14:37:17 +02:00
Amruth Pillai 31fb576099 feat(web): write custom styles as plain CSS without a version line or checker
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.
2026-09-29 14:31:59 +02:00
Amruth Pillai a6676b3268 fix(web): keep the design panel where it's scrolled
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.
2026-09-29 14:26:00 +02:00
Amruth Pillai 0a5323520f feat(web): stack entry fields in one column and let dates fill the row
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.
2026-09-29 14:25:53 +02:00