Commit Graph
58 Commits
Author SHA1 Message Date
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 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 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 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 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 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 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 e3d72ab0e7 fix(web): make motion animations instant when reduced motion is on
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.
2026-09-29 13:26:54 +02:00
Amruth Pillai 8a8de96a96 chore(web): keep single-file motion constants private 2026-09-29 13:14:57 +02:00
Amruth Pillai c652a288ba feat(web): swap content in place without a one-frame jump
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).
2026-09-29 13:08:14 +02:00
Amruth Pillai 28eea458d9 feat(web): let content arrive instead of popping in
Loaded content now settles in: library cards and rows, checker issue rows,
assistant suggestions and past conversations rise in with a short stagger on
first appearance only; new assistant messages and "Thinking…" rise in while
history stays put; a newly made document fades up from 96% with its accent
ring fading in.

Resume thumbnails keep the last drawn page of the same resume up while a new
one renders, so edits and return visits no longer drop to a placeholder. The
builder's first preview render paints hidden under the placeholder pages and
fades in; the PDF viewer reserves an A4 slot and holds its overlay until a
page has painted. The ATS checker ring fills on first reveal, ring colour
blends across the threshold, and report and insights bars grow from their
baseline.

Hand-rolled pulsing placeholders become the static Skeleton, shaped like the
view that is loading (documents grid or list, applications views, insights,
API keys). ENTER_CLASS and stagger() live in libs/motion.ts.
2026-09-29 13:02:24 +02:00
Amruth Pillai 607c561792 feat(ui): make overlays and controls move from where they come from
Menus, popovers and combobox lists now slide in from their trigger on
whichever side they open (data-side aware, RTL-correct), and submenus
open in 120ms and close in 84ms. The combobox list exits at 0.7x like
every other popup, and tooltips scale from their trigger.

Toasts keep travelling in the swipe direction when dismissed, ease back
on a cancelled swipe, and a replaced toast drops out before the new one
has finished rising.

The tabs indicator uses the movement curve and now also draws the line
variant's underline, so it slides instead of teleporting. Hotkey mode
switches in the resume and letter builders land instantly. Radio dots
and checkbox ticks grow in instead of popping or only fading.
2026-09-29 12:56:19 +02:00
Amruth Pillai 30819e2fc7 feat(web): land dropped kanban cards where they were dropped
The board now shows a dropped card in its target column in the same
render the drag ends, via a one-card override that clears once the list
query catches up or rolls back, so the card no longer blinks back to its
old column. The overlay then settles into the card's new slot (or back
home) with a 200ms on-screen-movement drop animation, skipped under
reduced motion. The floating card lifts in on pickup, flattens while it
lands, and shows a grabbing cursor.

Adds a shared DRAG_SETTLE dnd-kit timing to libs/motion.
2026-09-29 12:49:02 +02:00
Amruth Pillai 500cabaf4a perf(web): stop every navigation from waiting on the network
Root beforeLoad now reads the session and flags through the query cache
instead of refetching them on every navigation and preload. A signed-in
session is reused for a minute, a signed-out one is always re-checked,
and every sign-in, profile and 2FA change invalidates the cached session
before re-running route guards. The active Lingui locale is no longer
re-activated per navigation.

Links now preload on intent with TanStack Query deciding freshness, the
pending loader shows after 300ms and holds for 300ms, and it fades in on
in-app navigations while still taking over the HTML loader without a
blink on first load.
2026-09-29 12:35:38 +02:00
Amruth Pillai 982e688e65 refactor(web): put app transitions on the house motion tokens
Set Tailwind's default transition duration and curve to --d1/--ease so
bare transition-* utilities run quick on the house curve. Mirror the
motion tokens in libs/motion.ts (EASE, EASE_MOVE, D1-D3, EXIT) and move
app UI off ease-out-strong and hand-typed 150/300ms durations; the
landing page keeps --ease-out-strong.

Give snapping hover and focus states a transition, slide progress fills
with translate instead of animating width, keep selection outlines'
width and style constant so they fade from transparent instead of
flashing dark, and drop the unused agent-prompt-marquee keyframes.
2026-09-29 12:32:45 +02:00
Amruth Pillai eae7de3fb0 refactor(web): drop server-rendering guards from the client-only app 2026-09-29 10:48:08 +02:00
Amruth Pillai 36163a52b4 chore(web): keep an internal sidebar type private 2026-09-29 10:28:49 +02:00
Amruth Pillai cb3c594655 refactor(web): drop combobox groups, SSR hydration options and a hand-typed form store 2026-09-29 10:27:07 +02:00
Amruth Pillai c302faa70b refactor(web): delete code only tests used and reuse shared locale and colour helpers 2026-09-29 10:25:13 +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 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 daa7d331f3 feat(web): documents home, trash and a new-document dialog
The dashboard becomes Documents: resumes and saved letters in one
library, in a new app shell (a 240px sidebar, an icon rail on tablets and
bottom tabs on phones) with Documents, Applications, Trash (when it has
items), New (N) and the account row.

- Documents: All / Resumes / Letters with counts, search (/) across
  titles, tags and linked applications, sort, grid or list (remembered on
  the device), tag chips once tags exist, cards with the real first page,
  "Resume · Edited 2h ago", the linked application, lock and "New" badges,
  and the first-run screen. A file dropped anywhere on the page imports.
- One menu on cards, rows and right-click/long-press: Open, Rename
  (inline), Duplicate, Copy for a job… (resumes) or Link to application…
  (letters), Tags…, Lock editing, and Move to Trash with Undo.
- Trash lists days left, with Restore and Delete now (asks once).
- New: import a file in three labelled steps with the result and flagged
  fields (resumes, and saved letters' JSON), copy a resume for a job,
  start blank (named after its headline until renamed), a new letter, or
  a sample resume.

Also:
- /dashboard/resumes and /dashboard/cover-letters redirect to Documents.
- Settings pages sit behind a tab strip until they're regrouped, and the
  account menu gains Settings.
- A letter written inside a resume can be copied to Documents from its
  entry menu (Q3k), replacing the library's copy action.
- The builder's document menu and the letter editor move documents to
  Trash instead of deleting them.
- Dialogs reopened right after closing start fresh.
- Removed: the resume and letter libraries and the old create and import
  dialogs.
2026-09-28 20:38:54 +02:00
Amruth Pillai a82741f42a feat(web): edit resumes inline in the Write panel
Write becomes the Basics card, the outline of sections in print order and
Add section. Entries open in place (one at a time, the editor selection),
save as you type and show drafts as "Draft · not printed". Dates use a
structured field with a Present switch and the review note for text that
wasn't read exactly; descriptions use a restricted rich-text toolbar shown
on focus. Sections and entries reorder by drag or Alt+Up/Down, rows carry
the eye, count and every section option, and deleting shows Undo.

Clicking the page opens the entry and scrolls it into view; focusing a field
outlines its block. Phones push an open entry full screen. While a field
has focus the preview waits for a pause in typing.

The left section sidebar, the entry dialogs and the hidden-sections list are
removed.
2026-09-28 18:20:26 +02:00
Amruth Pillai 0f934bd849 feat(ui): adopt Desk & Paper tokens, fonts, System theme and restyled primitives
Replace the achromatic shadcn palette with the Desk & Paper tokens (warm
neutrals, one moss accent, danger, warn and info), exposed to Tailwind
under their spec names; the previous names resolve to the new tokens
until every screen is rebuilt. Hard-coded palette classes become semantic
tokens and stage colors follow the spec.

Fonts move to Newsreader, Hanken Grotesk and JetBrains Mono. The theme
gains a System option that follows the operating system live, applied
before first paint, and Base UI now receives the locale's direction.

Primitives follow the component spec: button variants primary,
secondary, ghost, danger and link with a loading state, accent focus
rings on inputs, SwitchRow, semantic badges, segmented and underline
tabs, restyled menus, dialogs, sheets, tooltips and command bar, and a
single bottom-center toast with an Undo action. New: IconButton,
SegmentedControl, RadioGroup and NativeSelect.
2026-09-28 16:29:30 +02:00
Amruth Pillai b48a9c2142 feat(web): refine motion system and simplify animated UI (#3546)
Audit every animation in the app and shared UI primitives against a
frequency-first motion bar: keyboard and high-frequency actions no longer
animate, remaining motion uses interruptible CSS transitions with shared
easing tokens, and redundant animation code is removed.

UI primitives (@reactive-resume/ui)
- Dialog, alert dialog, popover and tooltip move from tw-animate keyframes
  to Base UI data-starting/ending-style transitions; menus, popovers and
  tooltips skip motion when opened from the keyboard (data-instant).
- Dialog gains an `instant` prop; the command palette uses it.
- Accordion animates its real panel height; caret rotates instead of
  swapping icons.
- Menu backdrop blur moves onto the popup so it no longer snaps in after
  the fade; context menus and comboboxes fade only.
- Sidebar collapse uses the strong ease-out curve and snaps on Cmd+B.
- Toast, sheet, checkbox, tabs, toggle, inputs and message scroller get
  tokenised easing, correct transition properties and press feedback.
- Tabs no longer squeeze a trigger narrower than its label.
- Spinners keep spinning under prefers-reduced-motion.

Web app
- Remove the default route view transition and page-entrance slides.
- Add EASE_OUT_STRONG for Motion; replace built-in "easeOut" everywhere.
- Switch LazyMotion to domMax so layout and Reorder animations run.
- Builder: consolidate 12 section list files into one ItemsSection,
  opacity-only list rows with popLayout, instant Cmd+0, faster dock zoom,
  crossfade that no longer dips, transform-based progress bars.
- Dashboard: keep previous results while sorting/filtering, no empty-state
  flash, uncontrolled sidebar (no network round trip on collapse), calmer
  resume card tilt, no stacked hover wrappers.
- Settings: drop entrance/stagger wrappers and ActionButton.
- Agent: CSS marquee paused on hover; thread switches keep the layout.
- Homepage: fix invalid transition declarations, CSS spotlight drift,
  scroll-hiding header without a JS spring, tokenised curves.
- Theme switches change every color at once.
- Remove SSR-only useIsClient guards from the SPA.

Docs: rewrite the DESIGN.md animation section around the new tokens.
2026-09-27 17:10:42 +02:00
Amruth Pillai 8c40313980 feat(deploy): support Vercel Hobby alongside Docker (#3541)
* feat(deploy): support Vercel Hobby alongside Docker

* fix(deploy): include PDFKit runtime font assets

* docs(deploy): document Vercel and Docker setup

* docs(deploy): record storage persistence checks

* refactor(deploy): drop scheduled staging cleanup

Staging uploads are deleted after finalization and expired ones are swept
on each new upload, so the Vercel cron job, its route, and CRON_SECRET are
no longer needed. The Deploy with Vercel wizard now asks for two secrets.

* docs(deploy): restructure Vercel guides

Split the Vercel page into a how-to with its environment reference, move the
large RPC staging protocol to an API reference page, and move CI deployment
checks to the contributing section. Point Deploy with Vercel buttons at main.

* chore: remove agent planning records and fix web app description

Delete superpowers plans/specs, ADRs, issue plans, execution briefs, domain
context maps, and Europass research. Describe apps/web as a TanStack Router
SPA served by apps/server.

* refactor(deploy): simplify Vercel support code

- Share one Redis client and key namespace through @reactive-resume/db/redis
  for API and auth instead of a second auth-only client.
- Drop the auth seeding retry; the provider already treats concurrent inserts
  as no-ops and deployment preparation seeds before runtime.
- Detect staging support from POST /api/storage/stage (404 on Docker) instead
  of a separate GET probe.
- Read staged bodies directly; the signed upload already caps their size.
- Close per-subscription Redis connections with disconnect() alone.
- Check Blob health with one list call instead of write/read/delete.
- Remove redundant tsdown onlyBundle list, dead namespace fallbacks, and the
  conditional spread in the health status.

* fix(deploy): heal stopped runs with dead owners and keep auth up without Redis

- Run owners refresh a Redis heartbeat until they release their claim. Stop
  requests reap the run immediately when the owner has stopped heartbeating,
  instead of leaving the thread blocked until the 15-minute TTL reaper.
- Auth and oRPC rate limiters fall back to per-instance memory limits when
  Redis errors, instead of rejecting every login or failing requests.

* ci: allow esbuild build for Vercel CLI and register deployment deps with knip

pnpm 12 fails dlx installs with ignored build scripts, so allow esbuild
explicitly. The server bundle keeps @vercel/blob, ioredis, and jose external,
and api/index.mjs is the Vercel Function entry.

* fix(web): send buffered RPC bodies instead of teed streams

Reading a request clone turned the original body into a stream, which
browsers send without inspectable request data and which needs duplex
mode. Send the already buffered Blob for direct requests.

* fix(web): send direct RPC bodies as bytes

Blob request bodies are sent as data pipes, so browser tooling cannot
inspect them. Buffer the original request as an ArrayBuffer and send those
bytes; this restores the e2e save assertions that match on request data.
2026-09-26 02:37:22 +02:00
Amruth Pillai b953435f2c fix: audit code for reduction 2026-09-17 22:16:44 +02:00
Amruth Pillai f89acb4368 chore: migrate repository links to reactive-resume/reactive-resume 2026-09-12 11:18:32 +02:00
Amruth Pillai d77cb93494 fix: complete repository links and container publishing migration 2026-09-11 11:09:55 +02:00
Amruth Pillai 9550910f17 revert: remove repository migration changes from main 2026-09-11 03:23:27 +02:00
Amruth Pillai 31d6ee6251 chore: prepare repository migration and Docker Build Cloud publishing 2026-09-11 02:55:52 +02:00
Amruth Pillai de9a6dcfad fix: add missing twitter:url meta tag across social meta sites
Also fill in twitter:title/twitter:description on the ATS checker page,
matching its existing og: tags.
2026-09-06 21:27:40 +02:00
Amruth Pillai 368858a56f feat(resume): add per-section heading visibility (#3477) 2026-09-05 20:34:21 -07:00
Amruth Pillai fe9b59e111 fix: restore MCP OAuth registration and authorization (#3421)
* fix: align MCP OAuth provider schema and authorization flow

* test: isolate OpenAPI generation from OAuth initialization

* fix: accept auth routes without a callback query

* fix: require explicit OAuth consent and preserve signed requests

* test: verify OAuth audiences through real MCP initialization

* test(e2e): isolate OAuth token audience validation
2026-09-05 09:33:15 -07:00
Amruth Pillai fa41150723 fix: preserve photo compression during cropping and show upload limits (#3420)
* fix: preserve photo compression during cropping and show upload limits

* fix: bound cropped image size before upload
2026-09-05 08:59:13 -07:00
Amruth Pillai 5a6f5d4d68 fix: label remaining website and picture inputs (#3424)
* fix: connect remaining website and picture labels to inputs

* test(builder): use realistic website input events
2026-09-05 08:51:22 -07:00
Amruth Pillai 97ccb4ba06 fix(builder): remove sections emptied by item moves (#3417) 2026-09-05 07:29:32 -07:00
Amruth Pillai f29b92e2fb chore(copy): rewrite marketing, app, and docs copy to read less AI-generated
Rewrites the landing page, in-app microcopy, and public docs, then fixes what the rewrite exposed: stale template counts, a broken quickstart anchor, out-of-sync FAQ structured data, dead error-hint branches in the MCP tools, and wrong-sense translations across all 53 locales. Adds GLOSSARY.md so translators get the right sense of the ambiguous UI terms.
2026-08-28 22:18:29 +02:00
Amruth Pillai c288675b16 Release v5.2.9 (#3382)
* feat(ats): add ATS checker and replace resume analysis

Adds a public, browser-only ATS checker at /ats-checker and an ATS Check
section in the builder's right sidebar. PDFs are parsed locally: text
extraction, reading order, contact and date recovery, section detection,
and file-level readability are scored deterministically, with evidence
cited per finding and skipped checks reported rather than counted as
passes.

Removes the AI-scored resume analysis it supersedes: the resume_analysis
table (dropped via migration), the get_resume_analysis MCP tool, and
POST /ai/analyze-resume. The replacement, POST /ai/ats-review, reviews
extracted resume text and returns qualitative feedback with no score.

Also bumps the version to 5.2.9 and adds the changelog entry.

* chore(deps): bump workspace dependencies

* fix(ats-checker): keep negation inside each 'what this does not do' bullet

The three bullets were bare fragments whose negation came from the
section heading, which translators never see. A dozen locales rendered
them as affirmative assertions or imperatives, so the page claimed the
checker enforces a one-page rule and predicts rejection -- the opposite
of the source, and directly contradicted by the sentence beside it.

Each bullet now carries its own negation, so the polarity cannot be
lost in translation. Re-extracted and refilled across all 53 target
locales.
2026-08-27 03:37:01 +02:00
Amruth Pillai 3c195dc3f8 Release v5.2.8 (#3375)
Upgrades to Better Auth 1.7, expands Custom Styles coverage of item headers, and adds a human-approval step to the AI agent's resume edits.

Breaking for self-hosters using a custom OAuth provider: the callback path changes from /api/auth/oauth2/callback/custom to /api/auth/callback/custom, and installs using OAUTH_DISCOVERY_URL need one additional UPDATE after upgrading. Both are documented in docs/self-hosting/sso.mdx.

- Better Auth 1.7, with the account issuer migration and the jwks alg/crv columns the 1.7 jwt plugin requires
- Agent edits gated behind an approval step, with crash-safe runs and context pruning
- item-header now covers every section header row on every template; adds the item-header-row part
- Fixes provider unlinking, auth error messages, and version conflicts on freshly created resumes
- New /auth/error page, translated across all 53 target locales
- DeepSeek Harness plugin moved into packages/dsh-plugin
- Dependency bumps across the workspace
2026-08-24 21:44:16 +02:00
Amruth Pillai 39590eaff6 fix(auth): allow unlinking providers after the session ages past a day (#3364)
Better Auth guards `/unlink-account` with `freshSessionMiddleware`, which
rejects any session whose `createdAt` is older than `freshAge` (one day by
default). Sessions here last a week and there is no re-authentication flow to
refresh that timestamp, so disconnecting a provider failed with
`SESSION_NOT_FRESH` for every user who signed in more than a day ago.

Disable the freshness gate, and teach `getReadableErrorMessage` to read plain
error objects: Better Auth client errors are `{ code, message, status }`
objects rather than `Error` instances, so every auth toast was collapsing to
its generic fallback instead of showing the real reason.
2026-08-20 08:20:18 +02:00
Amruth Pillai 1e23a453a0 fix(seo): declare Twitter card tags with name attributes
X reads twitter:* meta tags from the name attribute, not property, so the card
validator reported twitter:title and twitter:description as missing. Also adds
the og:type tag the root head was never emitting.
2026-08-17 22:19:52 +02:00
Syed Ali Abbas ZaidiandAmruth Pillai 45303fb465 feat(resume): add a deterministic ATS parseability check (#3314)
* feat(resume): add a deterministic ATS parseability check

Adds an offline ATS linter that reports whether a parser can read a
resume, surfaced as an always-on panel in the builder.

The existing Resume Analysis panel needs a configured AI provider, so
users who never set one up get no feedback at all. These 22 rules run
as a pure function over ResumeData with no provider, no network and no
rendered PDF, so they work for everyone on every edit.

Rules cover contact details, date parseability, sections that hold
content but never render, column and sidebar placement, and typography
thresholds. The catalog mirrors the Semantic CSS diagnostic catalog:
stable codes carrying a severity, meaning and action, with no i18n
dependency so the web layer translates by code. Each finding carries a
JSON Pointer, which is what makes jump-to-field work.

Deliberately no second score. Resume Analysis owns overallScore, so
this reports "N of M checks passed" and counts by severity instead.

* fix(resume): accept localized ongoing periods and reject bare ones

Two period-parsing bugs found in review.

The ongoing-token set was English-only, so a German resume reading
"2020 - heute" was reported as unparseable and the panel told the user
to rewrite a perfectly valid range. Rather than guess translations for
55 locales, a range ending that carries no digits and is not a month
name in the resume's locale is now read as ongoing. That keeps a
genuinely incomplete ending such as "Jan 2020 - Feb" reported, since
"Feb" resolves as a month.

A bare "Present" also parsed as a valid period, so an experience entry
with no start date passed the check. A standalone ongoing token is now
rejected; ongoing tokens remain valid as the end of a range.

* feat(web): scroll ATS findings to the item they belong to

Findings for different items in one section all landed on the section
header, so a date problem on the third role gave no more help than
naming the section.

getAtsFindingTarget now resolves the offending item from the finding's
JSON Pointer against the resume, and SectionItem carries a matching DOM
id. The panel scrolls to that item and falls back to the section header
when the item is not mounted, which is what happens while its section
is collapsed.

* fix(resume): recognize ongoing periods by token, not by shape

The previous heuristic read any short, digit-free range ending as an
ongoing marker, so "2020 - unknown", "2020 - later" and "2020 - tbd"
parsed cleanly and suppressed the finding they should have raised.

Replaced with an explicit table of ongoing words keyed by language,
covering the locales the app ships. Matching is exact, so unrecognized
endings are reported again. A locale missing from the table falls back
to the earlier behaviour of reporting its ongoing periods, which is a
visible gap someone can close by adding a word rather than a silent
hole in detection.

Tests assert every listed token parses and that the table stays
lowercase, since lookups normalize that way.

* fix(ats): parse punctuated ongoing tokens

* fix(ats): parse Unicode punctuated ongoing tokens

---------

Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-08-13 23:07:55 +02:00
Amruth Pillai 9110e86997 refactor: ponytail audit 2026-07-27 20:26:16 +02:00
Amruth Pillai 90105cb148 chore: integrate improve-integration 2026-07-08 19:08:31 +02:00
Amruth Pillai 27efeab796 refactor(web): finding 7 - extract makeCustomSection, derive isStandard
Extract makeCustomSection factory to eliminate duplicate CustomSection
object literal. Replace hand-maintained SectionType array in
isStandardSectionId with membership check via `id in sections`.

Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
2026-07-04 21:29:20 +02:00
Amruth Pillai 57e9c8c487 v5.2.0: undo/redo, version history, embedded AI assistant, mobile builder & more (#3205) 2026-07-04 14:57:25 +02:00
JamesGoslingscoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>Amruth Pillai
b932711f08 feat: add section heading icons to PDF templates (#3127)
* feat: add section heading icons to PDF templates

Add customizable Phosphor icons before section titles in PDF output.
Users can toggle visibility globally via a new "Hide section heading icons"
switch (independent of item-level icons) and customize individual section
icons through the builder sidebar icon picker.

- Add `icon` field to `baseSectionSchema` and `summarySchema`
- Add `hideSectionIcons` to `pageSchema` (defaults to true for backward compat)
- Implement `SectionHeadingIcon` component with heading font-size scaling
- Support "none" sentinel for per-section icon hiding
- Fallback to sensible defaults (briefcase, graduation-cap, etc.) for legacy data
- Add icon picker to builder sidebar sections and custom section dialogs

Closes #2632

* test: add unit tests for section heading icons

- Add tests for getResumeSectionIcon() covering built-in sections,
  summary, custom sections, "none" sentinel, and default fallbacks
- Add schema tests for baseSectionSchema icon field, summarySchema icon,
  and pageSchema hideSectionIcons default behavior

* refactor: minor updates to icon display

* Update apps/web/locales/es-ES.po

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-06-01 14:02:42 +02:00
Amruth Pillai b04eef1479 feat: implement style rules 2026-05-27 10:57:33 +02:00
Amruth PillaiandCursor c66560ee12 refactor(web): dedupe isRTL via utils locale module
Re-export isRTL from @reactive-resume/utils/locale in the web locale
helper and consolidate RTL detection tests in the utils package.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 16:32:58 +02:00