Commit Graph
184 Commits
Author SHA1 Message Date
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
bad431b2fc fix(import): auto-detect JSON format and show readable errors (#1) (#3296)
* fix(import): auto-detect JSON format and show readable errors (#1)

Readable import errors, a fail-soft v4 parser, and auto-detect of the JSON format so uploads just work. The format dropdown becomes an optional override. PDF and DOCX (AI) paths are untouched.

* fix(import): address review feedback on the v4 guard and error message

- reactive-resume-v4-json.tsx: reject arrays in isRecord so array-valued
  basics, sections, or metadata no longer pass the v4 shape guard.
- reactive-resume-v4-json.tsx: reuse the guard's error instance in the
  catch arm instead of allocating a duplicate NOT_V4_MESSAGE.
- error.ts: use a singular "Problem" label for root-level Zod issues so
  the message stays grammatical.
- add a regression test for array-valued v4 branches.

* fix(import): preserve selected JSON format

* test(import): cover selected JSON parser

---------

Co-authored-by: MrTig-afk <MrTig-afk@users.noreply.github.com>
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-08-13 23:01:06 +02:00
Syed Ali Abbas ZaidiandAmruth Pillai 9f13638eab fix(web): drop focus when panning the builder canvas (#3303)
react-zoom-pan-pinch calls preventDefault() on its window-level mousedown
listener so that dragging the canvas does not select text. That also cancels the
browser's native focus shift, so focus stays wherever it was before the pan --
typically the sidebar button that opened the last dialog, since closing a dialog
restores focus to its trigger. A focused button activates on Space keyup, so
holding space to pan and then releasing it reopened the most recent dialog.

Blur the focused element from onPanningStart, which reinstates exactly the focus
change the browser would have made on its own. onPanningStart only fires when the
mousedown target is inside the transform wrapper, so sidebar and dialog clicks are
unaffected, and keyboard-only users never trigger a pointer pan.

Closes #3300

Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-08-13 22:54:23 +02:00
13e584d522 fix(icon-picker): allow selecting the empty/no-icon option (#3298)
The icon picker grid starts with an empty string entry that renders the
"no icon" (prohibit) symbol, but the onClick guard "if (icon)" treated
the empty string as falsy and ignored the click. Change the guard to
check for a defined string value so the empty/no-icon option can be
selected.

Closes #3252
Closes #3261

Co-authored-by: Devin <devin@example.com>
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-08-13 22:54:16 +02:00
github-actions[bot]andCrowdin Bot e4b28e9825 Sync Translations from Crowdin (#3315)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-08-13 09:36:15 +02:00
Amruth Pillai 2d6ea9ce8d fix(auth): allow implicit social signup 2026-08-13 09:23:04 +02:00
Syed Ali Abbas Zaidi 3a5b12e2a4 fix(web): confirm before the import dialog's provider link navigates away (#3308) 2026-08-11 10:26:04 +02:00
Amruth Pillai 035d94183b fix(web): show static template previews in gallery (#3302) 2026-08-10 12:18:47 +02:00
Amruth Pillai c292968314 chore: update translations 2026-08-09 15:10:09 +02:00
Amruth Pillai ba1f469950 fix(a11y): label 2FA copy button, restore template focus ring, guard API-key double-submit
- Add an sr-only "Copy secret" label to the icon-only copy button in the 2FA
  enable dialog; it was previously announced as an unlabeled button.
- Add a focus-visible ring to template gallery cards. The only ring was gated
  on the selected state, so keyboard focus was invisible while tabbing.
- Disable the API-key create submit button while the request is in flight to
  prevent duplicate keys from a double-click.

Surfaced by a shadscan UI audit. The remaining ~95 findings were false
positives from the auditor not understanding the pnpm monorepo and the
TanStack Start root-route shell, and were waived.

Claude-Session: https://claude.ai/code/session_01JYTniVDeA56o1kGhdoCUoD
2026-08-09 14:48:46 +02:00
Amruth Pillai 88a19619da chore: update dependencies 2026-08-09 12:17:09 +02:00
autofix-ci[bot] 36232b631d [autofix.ci] apply automated fixes 2026-07-31 15:25:43 +00:00
Amruth Pillai 9eec1520a1 Merge branch 'main' of github.com:amruthpillai/reactive-resume 2026-07-31 17:24:40 +02:00
Amruth Pillai 131c1492cd chore: update dependencies 2026-07-31 17:24:32 +02:00
Amruth PillaiandCursor Agent ba8e1be2ab fix(stylesheet): harden PDF preflight and surface worker failures (#3284)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-31 07:57:54 +02:00
Amruth PillaiandCursor Agent 4a8f87ab8f fix(web): stop custom styles from sticking on Checking (#3283)
Concurrent compile requests from editor intelligence were rejecting
in-flight edit compiles as stale, and the store swallowed that rejection
without leaving compiling. Resolve all compile results and surface
compile failures as an error status so styles can apply again.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-31 01:00:45 +02:00
github-actions[bot]andCrowdin Bot 186c400ab7 Sync Translations from Crowdin (#3281)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-30 18:59:23 +02:00
Amruth Pillai 3589b534f5 feat: enable semantic CSS by default 2026-07-30 16:28:44 +02:00
Amruth Pillai ae8d48bcee chore: update translations 2026-07-30 12:59:19 +02:00
Amruth Pillai 164a279306 chore: update dependencies 2026-07-30 12:45:49 +02:00
github-actions[bot]andCrowdin Bot 79e4a3ddc8 Sync Translations from Crowdin (#3279)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-30 12:41:45 +02:00
Amruth PillaiandCursor Agent d2ffbf9618 feat: add semantic CSS stylesheets (#3274)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-30 12:39:15 +02:00
4ac19f81b3 fix: clamp page margin values to [0, 100] to prevent crash on paste (#3277)
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-29 18:13:42 +02:00
Amruth Pillai b303b89758 fix(web): restore Tiptap Enter and list editing 2026-07-28 15:10:24 +02:00
Amruth Pillai 048eab3b49 fix(web): preserve bootstrap attribute order 2026-07-28 08:48:14 +02:00
Amruth Pillai 93c06934bd fix(web): preserve Rocket Loader exclusion in build 2026-07-28 08:38:05 +02:00
Amruth Pillai 1e665fbe7e perf(web): remove homepage video from the LCP path 2026-07-28 08:35:07 +02:00
Amruth Pillai 994093b981 chore: update translations 2026-07-27 20:57:54 +02:00
Amruth Pillai 9110e86997 refactor: ponytail audit 2026-07-27 20:26:16 +02:00
EMRANandAmruth Pillai 0eb9ce012e fix: profile picture delete icon should reset image correctly (#3176) (#3258)
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-07-27 13:31:57 +02:00
Emanuele TonelloandAmruth Pillai 47349e7ab3 feat: support editing AI provider models in the UI and auto-fill LinkedIn job postings (#3259)
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-07-27 13:21:57 +02:00
Amruth Pillai 3266066826 chore: update dependencies 2026-07-27 13:19:54 +02:00
autofix-ci[bot] 6503da7e49 [autofix.ci] apply automated fixes 2026-07-27 11:14:46 +00:00
落尘 2a0782517c fix: clamp custom style numeric inputs (#3262) 2026-07-27 13:13:48 +02:00
github-actions[bot]andCrowdin Bot 9085a199cf Sync Translations from Crowdin (#3243)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-09 15:35:39 +02:00
Amruth Pillai 2b0aac820c chore(i18n): sync translations from crowdin 2026-07-09 01:24:39 +02:00
Amruth Pillai d50948ddee chore(i18n): update application timeline translations 2026-07-09 01:17:56 +02:00
Amruth Pillai 18d0c14aa1 feat: add application timeline history (#3237)
* feat: add application timeline history

* fix: address application timeline review

* fix: keep application tracker e2e stable

* fix: use stable timeline e2e selector

* fix: target timeline note input in e2e
2026-07-09 00:36:45 +02:00
Amruth Pillai 90105cb148 chore: integrate improve-integration 2026-07-08 19:08:31 +02:00
github-actions[bot]andCrowdin Bot 25021507a0 [skip ci] chore(i18n): sync translations from crowdin (#3233)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-07 18:21:35 +02:00
github-actions[bot]andCrowdin Bot b87a9d8282 Sync Translations from Crowdin (#3231)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-07 17:06:14 +02:00
Amruth Pillai 46afc65cc6 Add application tracker REST and MCP parity
Add comprehensive Application Tracker REST and MCP coverage, document the MCP workflow, add Markdown/ActionLint checks, bump the release version, and fill all extracted translations.
2026-07-07 17:02:39 +02:00
github-actions[bot]andCrowdin Bot d37ac57cc5 [skip ci] chore(i18n): sync translations from crowdin (#3224)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-06 00:30:34 +02:00
Amruth Pillai fb9c217af2 feat: add command palette entity search 2026-07-06 00:29:06 +02:00
Amruth Pillai b404dbd42a Add application tracker (#3220)
* feat(applications): job application tracker with AI copilot

Add an Applications module at /dashboard/applications: pipeline board
(dnd-kit), table view with bulk actions, Insights (fit tiles, funnel,
sources, shareable funnel-flow SVG), campaigns, tags, CSV import, and
Add/Edit/Detail slide-overs. Each application links a live Reactive
Resume.

AI "Application Copilot" (applications.ai.*): job-posting autofill,
resume↔job match score (fit ring), resume tailoring, and cover-letter /
follow-up drafting — via the user's configured provider.

Board cards + table rows get context menus (edit / move / archive /
delete). Charts are CSS/SVG (no new chart dep); adds a UI Checkbox.

Also includes local TanStack devtools setup and toolchain bumps.

Claude-Session: https://claude.ai/code/session_01TEeRHnEayw2MFCShFRyL5f

* feat(applications): close follow-up gaps + squash migrations

Finish the deferred/open items on the applications tracker:

- Cover-letter upload re-enabled. Fix the storage blocker by deriving the
  key extension from content type (buildFileKey/EXTENSION_BY_CONTENT_TYPE)
  instead of hardcoding .jpeg, so PDFs serve correctly and non-JPEG image
  avatars keep working under FLAG_DISABLE_IMAGE_PROCESSING. Add
  coverLetterUrl/coverLetterName columns + Documents-section upload/remove.
- Contacts editor in the detail sheet (add/edit/remove, keyed per app).
- Board caps rendered cards per column (COLUMN_PAGE_SIZE=50 + "Show more").
- Extract new Lingui messages across locales.
- Guard coverLetterUrl to http(s)/relative at the API boundary.

Squash the five branch-only application-table migrations (create -> +tags
-> +cover-letter -> drop -> re-add) into a single clean CREATE TABLE via
drizzle-kit generate.

Claude-Session: https://claude.ai/code/session_01TEeRHnEayw2MFCShFRyL5f

* chore: update dependencies

* fix(web): address React Doctor findings — compiler, purity, query, component structure

prefer-module-scope-pure-function: hoist buildSubtitle, getDecimalPlaces,
handleLocaleChange, onLocaleChange, stop, listContent/groupedListContent to
module scope so they aren't rebuilt on every render.

react-compiler-todo (??=): rewrite draft.metadata.styleRules ??= [] to the
non-assignment form to unblock auto-memoization.

set-state-in-effect: derive updatedAtLabel at render time instead of syncing
it through useState + useEffect.

query-destructure-result: destructure useQuery results at call site in
resume-analysis and resume-thumbnail to follow TanStack Query v5 convention.

only-export-components: extract non-component exports to sibling .ts files so
Fast Refresh can preserve component state:
  - getNextWeights → typography/get-next-weights.ts
  - detectJsonImportType + ImportType → dialogs/resume/import.utils.ts
  - getLocaleOptions → features/locale/locale-options.tsx
  - preview helpers + DEFAULT_PDF_PAGE_SIZE → preview.shared.utils.ts
  - resolveHighlightToolbarState + defaultHighlightColor → rich-input.utils.ts
  - computeDelta + getSparklinePoints → statistics.utils.ts

no-multi-comp: split multi-component files into focused companions:
  - ResumePane + ToolbarButton → routes/agent/-components/resume-pane.tsx
  - DesktopBuilderShell → builder/$resumeId/-components/desktop-builder-shell.tsx
  - MobileBuilderShell + helpers → builder/$resumeId/-components/mobile-builder-shell.tsx
  - setBuilderLayout/getBuilderLayout moved to -store/sidebar.ts

fix(tests): add Resume type import to section-builder mocks and cast partial
mock data as unknown as Resume to satisfy stricter type checking; fix
noExplicitAny Biome errors in the same mocks.

* feat(applications): improve performance

* chore: fix knip issues

* perf(builder): halve per-keystroke render cost

Section-form fields called `form.handleSubmit()` on every keystroke, which
re-validated the whole form and toggled submit state — firing the render
cascade twice per character (~6809 renders/keystroke, FPS dropping to 9).

Persist via a form-level `listeners.onChange` instead and drop the per-field
`handleSubmit()` (basics, custom-fields, design). Narrow header/dock resume
subscriptions to metadata slices so they no longer re-render on content edits.

Cuts renders 6809 -> 3403 per keystroke (50%), 0 frame drops. Save, preview,
and design controls verified working; 449/449 web tests pass.

* perf(home): eliminate hero CLS from unreserved video box

The hero <section> is `flex items-center` (shrink-to-fit), so the video
wrapper's width depended on the video's intrinsic size, which only resolves
after the media loads. aspect-ratio couldn't reserve height without a definite
width, so the video grew from ~190px to ~563px after first paint and shoved the
centered hero text down ~373px (CLS ~0.095).

Give the wrapper a definite width (w-full + mx-auto on the CometCard) and set an
explicit aspect ratio + width/height on the video so its box is reserved before
load. CLS 0.095 -> 0; hero stays visually centered at max-w-4xl.

* docs: add application tracker guides

* chore(db): squash application migrations

* fix(email): import React in auth template for server-side rendering compatibility

* chore(release): v5.2.1

* Refactor resume rendering and builder workflows

* fix: address application tracker review findings
2026-07-05 23:44:04 +02:00
github-actions[bot]andCrowdin Bot 0d1bfd4e6b Sync Translations from Crowdin (#3218)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-05 14:42:41 +02:00
Amruth Pillai 20c803e934 feat(export): separate resume/cover-letter downloads, redesign dialog, add Markdown export (#3217)
* feat(export): separate resume/cover-letter downloads, redesign dialog, add Markdown

Let people export the resume and cover letter as distinct documents, and add a
Markdown format alongside PDF / DOCX / JSON (handy for AI agents).

- Server/API: scope PDF generation and download URLs to a resume/cover-letter target.
- Export domain: getResumeExportData + resumeHasCoverLetter in @reactive-resume/resume.
- Redesign the download dialog: one global "What to export" scope toggle (Tabs) plus
  flattened per-format rows, reusing existing UI components and design language.
- Add Markdown export (@reactive-resume/resume/markdown) with a small tiptap-HTML converter.
- Fix blank section headings in DOCX and Markdown by injecting the locale-aware
  section-title resolver (titles are stored empty and resolved at render time).
- Locale catalogs updated for the new strings.

* test(e2e): open the download dialog before exporting JSON

The JSON export moved into the redesigned download dialog, so the spec now opens
the dialog from the Export sidebar section before clicking "Download JSON".
2026-07-05 14:40:49 +02:00
Amruth Pillai 8416a92153 fix: improve mobile responsive layouts 2026-07-04 23:54:02 +02:00
Amruth Pillai 3f6e22addb chore(locales): update PO revision dates and add attachment translations 2026-07-04 22:52:09 +02:00
autofix-ci[bot] d5b177aa89 [autofix.ci] apply automated fixes 2026-07-04 20:29:16 +00:00