From 074c78a7ef92cea8e4dfdae8cd6aaa7a9ec965f0 Mon Sep 17 00:00:00 2001 From: cesnimda Date: Sat, 18 Jul 2026 14:48:33 +0200 Subject: [PATCH] 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 --- docs/architecture/cv-builder.md | 39 +++++++++++++++++++--------- docs/architecture/cv-theme-engine.md | 15 ++++++++--- docs/implementation-roadmap.md | 12 +++++++++ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/docs/architecture/cv-builder.md b/docs/architecture/cv-builder.md index 9e0df43..0b7c8bb 100644 --- a/docs/architecture/cv-builder.md +++ b/docs/architecture/cv-builder.md @@ -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. diff --git a/docs/architecture/cv-theme-engine.md b/docs/architecture/cv-theme-engine.md index 57803ee..cc32442 100644 --- a/docs/architecture/cv-theme-engine.md +++ b/docs/architecture/cv-theme-engine.md @@ -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. diff --git a/docs/implementation-roadmap.md b/docs/implementation-roadmap.md index 345d67b..b4184b2 100644 --- a/docs/implementation-roadmap.md +++ b/docs/implementation-roadmap.md @@ -114,6 +114,18 @@ Goal: `Content Tab → Customise Tab → Preview → Export` (guide `:312`). > per-item override UI in the Content tab (the API + resolver support it; the editor exposes section- > level controls). **Known limitation:** external direct loads of `/cv/{slug}` bounce to root (SPA > deep-link issue, app-wide) — fix in the frontend static-export/routing config. +> +> **Phase 4.5 — Builder Polish SHIPPED 2026-07-18** (commits `585047d`, `e3b255f`, `582c4e0`). +> Public-CV deep links fixed (optional catch-all `app/[[...slug]]`; direct nav/refresh/shared links +> work, no infra change). Native drag-and-drop reorder for sections + entries (keyboard arrows kept). +> Per-item editing via `GET /api/cv/outline`: hide, title/subtitle override, rich-text bullets +> (`**bold** *italic* __underline__ [link]`, escaped-then-whitelisted server-side). `ItemOrder` +> reorders entries without touching the master profile. Preview: zoom presets, measured page count + +> page navigation + page-break indicators, "updating" state. Unsaved/Saving/Saved indicator, loading +> skeletons, better empty states, ATS-friendly theme badge, a11y (ARIA labels, keyboard theme cards, +> focus rings), print-quality page-break CSS, AA contrast fix. Docs: `cv-builder.md`, +> `cv-theme-engine.md`. **Still open:** PDF page-number footer (Playwright `footerTemplate`); +> client-side preview (kept server-rendered for fidelity); `ProfileCvController` split. | # | Task | Priority | Difficulty | Dependencies | Expected value | |---|---|---|---|---|---|