docs(architecture): record Phase 4.5 builder polish
cv-builder.md (outline-driven Content tab, DnD, rich text, preview page nav, public-CV routing fix + deployment note), cv-theme-engine.md (rich text/ATS/ print), roadmap Phase 4.5 shipped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -60,9 +60,19 @@ tenant filter — there is no current user). Unknown/private slug → 404
|
||||
|
||||
`/career/builder` lists variants (`CvBuilderPage`); the editor (`CvBuilderEditor`) is three tabs —
|
||||
**Content**, **Customize**, **AI Tools** (plus **History**) — beside an always-on live preview that
|
||||
re-renders through `POST /api/cv/preview` on a 350 ms debounce. Edits autosave on an 800 ms debounce
|
||||
(`source: autosave`), appending a version each save. `/cv/:slug` (`PublicCvPage`) renders a public CV
|
||||
in a sandboxed iframe.
|
||||
re-renders through `POST /api/cv/preview` on a 300 ms debounce. Edits autosave on an 800 ms debounce
|
||||
(`source: autosave`), appending a version each save; the header shows Unsaved / Saving / Saved.
|
||||
`/cv/:slug` (`PublicCvPage`) renders a public CV in a sandboxed iframe.
|
||||
|
||||
**Content tab** reads `GET /api/cv/outline` (the master profile as sections+entries with ItemKeys).
|
||||
Sections and entries reorder by native drag-and-drop (`useDragReorder`, no dependency) or arrow
|
||||
buttons (the keyboard-accessible path); entry order is stored per section as `ItemOrder` on the
|
||||
variant, never on the profile. Each entry exposes hide, title/subtitle override, and rich-text bullet
|
||||
editing (`RichTextField` — a markdown toolbar over a textarea; storage stays plain text, the server
|
||||
renderer converts the `**bold** *italic* __underline__ [text](url)` whitelist to safe HTML).
|
||||
|
||||
**Preview** has zoom presets (±, slider, Fit), a measured page count with prev/next page navigation
|
||||
and dashed page-break indicators, and an "updating…" chip. **Customize** badges ATS-friendly themes.
|
||||
|
||||
## AI
|
||||
|
||||
@@ -75,14 +85,19 @@ carries "preserve every factual claim — never invent" (MASTER_IMPLEMENTATION_G
|
||||
- **New theme**: append a `CvTheme` to the catalog (`cv-theme-engine.md`). No renderer change.
|
||||
- **New section kind**: add a builder in `CvVariantResolver` + a `Kind` branch in `ThemedCvRenderer`.
|
||||
- **DOCX export**: add an `IDocxExporter` consuming the same `CvRenderModel`; the model is format-neutral.
|
||||
- **Rich text / page numbers**: see `cv-theme-engine.md` "Deliberately not here (yet)".
|
||||
- **PDF page numbers**: `ShowPageNumbers` is carried but the footer is a Playwright `footerTemplate`
|
||||
concern, not CSS — see `cv-theme-engine.md`.
|
||||
|
||||
## Known limitation
|
||||
## Deployment — public CV routing (fixed 2026-07-18)
|
||||
|
||||
Direct external loads of any deep link (including `/cv/{slug}`) currently bounce to the app root — the
|
||||
CSR lift-and-shift Next.js static-export setup only prerenders `/`, so a hard navigation to a deep path
|
||||
doesn't reach the react-router route (in-app client navigation works). This affects the whole SPA, not
|
||||
just public CVs, but public CVs are the first feature that depends on external direct links working.
|
||||
Fix path: serve `index.html` for all non-`/api` paths so react-router owns routing (nginx already has
|
||||
`try_files $uri /index.html`; the redirect is client-side in the Next shell). Tracked as a follow-up —
|
||||
see `docs/architecture/frontend.md` / the Next.js migration note.
|
||||
`/cv/{slug}` (and every deep link) must work on direct navigation, refresh, and shared links. The app
|
||||
is a React Router SPA behind Next.js `output: export`, which previously prerendered only `/`, so a hard
|
||||
load of any deep path hit Next's client not-found before React Router could route it.
|
||||
|
||||
**Fix:** the single `app/page.tsx` is now an optional catch-all `app/[[...slug]]/` (a server
|
||||
`page.tsx` exporting `generateStaticParams` + a `"use client"` `ClientShell`, because
|
||||
`generateStaticParams` can't live in a client file). The catch-all matches every client path, so React
|
||||
Router owns routing on any hard load. Build still emits just `index.html`; nginx serves it for all
|
||||
unknown paths via `try_files $uri /index.html` (unchanged). **No infra/nginx change was required** —
|
||||
the fix is entirely in the frontend build. Verified live: `/login` and `/cv/{slug}` both resolve on
|
||||
direct navigation.
|
||||
|
||||
@@ -43,11 +43,20 @@ theme is smoke-tested automatically.
|
||||
dense), `ats-classic` (single, no graphics), `nordic` (sidebar-right), `elegant` (single, editorial),
|
||||
`creative` (sidebar-left, bold).
|
||||
|
||||
## Rich text, ATS, and print (2026-07-18)
|
||||
|
||||
- **Rich-text bullets** are shipped: bullets/summary support `**bold**`, `*italic*`, `__underline__`,
|
||||
`[text](url)` via `ThemedCvRenderer.Inline` — everything is HTML-escaped first, then a whitelist is
|
||||
re-applied, so no user tag survives. Storage stays plain text (no HTML, no sanitiser dependency).
|
||||
- **ATS-friendliness** is a data flag: `CvTheme.AtsFriendly` (set on the single-column themes),
|
||||
surfaced in `GET /api/cv/themes` and badged in the Customize tab. Two-column (sidebar) themes are not
|
||||
flagged, as sidebar layouts can trip naive resume parsers.
|
||||
- **Print quality**: the renderer emits `break-inside: avoid` on entries, `break-after: avoid` on
|
||||
section headings, and widow/orphan control, so entries don't split across a page in the Playwright
|
||||
PDF pass.
|
||||
|
||||
## Deliberately not here (yet)
|
||||
|
||||
- **Rich-text inside bullets.** The render model treats bullets as plain strings, so the builder's text
|
||||
areas are plain text. Adding bold/italic/links means the render model carries inline runs and the
|
||||
renderer emits them — a self-contained extension, no theme changes.
|
||||
- **PDF page numbers.** `ShowPageNumbers` is carried through settings but the actual footer is a
|
||||
Playwright `footerTemplate` concern (the PDF exporter), not CSS paged-media (Chromium's margin boxes
|
||||
are unreliable). Wire it in `PlaywrightCvPdfExporter` when needed.
|
||||
|
||||
Reference in New Issue
Block a user