21 KiB
P5-11 — Live preview presentation
Starts from fc9598be8e1de0192c53270d7a331dd6bbfbe093 on codex/ui-redesign-phase4. This slice migrates the preview pane's presentation only. The approved plan, P5-09 frame, P5-10 editing boundary, revision readiness and pagination oracle remain authoritative. No P5-12+, B07, backend, schema, dependency, template or PDF-renderer change is included.
Preview chrome versus document truth
The existing preview owner remains in CvBuilderEditor.tsx. Its surrounding pane now uses an outlined MUI Paper, a compact level-two heading, the automatic Fit width percentage, a settled page count and a named Go to editor / Gå til redigering action. The neutral canvas uses background.default; headings/help use the shared typography/text hierarchy. A visible thin scrollbar makes the existing keyboard-scrollable region easier to discover. Pages have restrained theme shadow/divider outlines and existing localized Page X of N labels outside the physical document frame.
The actual document stays white in both application themes. Its iframe dimensions, borderless frame, transform scale and origin, previewDocumentHtml, hidden measuring iframe, renderer HTML, print CSS, fonts, page padding and native fragmentation algorithm are unchanged. The new page outline is outside its box and does not consume document space. Canvas padding is 8px on each side, matching the existing fit function's 16px gutter; clientWidth excludes the scrollbar. The page geometry remains renderer-owned A4/Letter metrics, rather than a second UI pagination model. PDF remains final output truth.
There was no manual zoom, fit-page, thumbnail or fullscreen workflow in the existing editor. This slice exposes the existing automatic fit, without adding those optional capabilities or controls that pretend they exist. Individual pages remain non-interactive containers; the existing named page iframes retain their native keyboard traversal.
Lifecycle, fit and error handling
The 300ms debounced preview request and newest-response guard remain unchanged. Save/history/conflict and preview states remain independent. HTTP completion does not establish pagination readiness: the existing hidden iframe waits for its native cv-preview-ready event after fonts, images and layout settle. A local measuredHtml marker records which returned HTML was measured. Only the measured current preview displays its final count/overflow/long-document messages. The scroll region is aria-busy during fetching or current-layout measurement; one polite status reports updating or the settled count. No arbitrary delay is added.
The initial fit effect previously ran while variant settings were null and the pane ref did not exist. For the default A4 document, loading did not change its width dependency, so the observer could remain unattached until a resize or pane transition. The existing effect now also depends on the boolean settings-ready transition, attaching once the asynchronously loaded pane exists. It retains the same fit helper/minimum scale and does not reattach on every keystroke.
Existing last-good HTML is retained during updates/failures. A failed preview displays a genuine error with the existing Retry operation, explicitly stating that editing can continue and PDF export is separate. An initial failure does not show a permanent loading skeleton or a fake completed blank page. Retry returns to updating and then settled current content. No PDF failure, save success or source mutation is inferred from preview state.
Page labels reserve the same space while their unsettled values are visually hidden and excluded from accessibility. Removing ten labels during an update shortened the long preview and clamped its bottom scroll position by 200px. Two native cases reproduced that exact loss against the earlier compiled candidate before rebuilding the fix. The final presentation keeps the label slots; held and failed request checks require the retained document's scroll height/top, iframe identity and HTML to remain unchanged. This repairs chrome-induced scrolling, without changing page geometry or assigning a separate page count.
Keyboard and responsive boundary
The named scroll region retains its existing tabindex, Page Up/Down and Home/End behaviour, iframe entry/exit, focus-visible treatment and focused-pane retention during reflow. The new return action switches to the existing edit pane and focuses its existing named region on the next animation frame. That region has tabIndex=-1 for programmatic focus, not a second ordinary tab stop. No modal, Escape trap, fullscreen state or independent editor is introduced.
The P5-09 desktop split/stacked intermediate layout and phone Edit/Preview modes remain. Returning to edit preserves the current tab, section expansion, draft, save state, undo and scroll ownership. EN/Bokmål help describes keyboard scrolling without changing user-authored document language. The bounded slice does not complete P5-14's final responsive or native-touch acceptance.
Final visual review at 320px found the title/scale group shrinking to accommodate every header item in one row, wrapping the Bokmål scale into four short lines. Giving that group a 140px flex basis lets the existing wrap move the return action to a separate row while keeping the fit label readable. This is preview-chrome reflow only; fit geometry and the editor/workspace breakpoints remain unchanged. The earlier partial browser run was stopped and is not reported as a complete baseline; the production build and full baseline were rerun after this final application change.
Preservation mapping and changed owners
IDs below use the PRES- prefix from the 57-contract ledger. This is scoped evidence, not exhaustive certification of all contracts or rendering combinations.
| PRES IDs | Existing behaviour | P5-11 change | Automated / visual proof |
|---|---|---|---|
| C06 | Debounced preview, automatic fit, page count and mobile panes | Compact header/canvas, settled count, initial observer attachment and explicit return action | Four focused owner tests; native preview chrome cases; inherited four theme/locale keyboard journeys |
| C08/C09/C12 | Templates, renderer-owned document settings, private/public PDF and authoritative export | Theme-aware surrounding chrome only; white output unchanged | Original 16-fixture oracle and renderer-source hashes; one/long/multi-page template captures; inherited PDF/private/public cases |
| C10 | One editing/save/undo/history/conflict owner | Return focuses the existing editor; pending draft remains intact | Focus/draft unit proof, native readback/undo/retry, five revision-readiness cases and inherited conflict/history cases |
| E08 | Shared themes, localisation, focus and loaded/error semantics | MUI semantic surfaces, EN/NB labels, polite busy/count and independent preview error | Four theme/locale keyboard cases plus new light/EN and dark/NB native captures/axe |
| E09 | Account-scoped private tree and stale-response rejection | No new storage or identity owner; newest preview guard retained | Overlapping success/failure fixture; inherited B02/account-disposal suite |
| C01/C02/C07/C11, W05/W07 | Canonical source, CV overrides/AI and immutable submitted capture | Regression boundaries; no presentation or mutation owner changed here | Complete inherited source/reset/AI/package tests and native canonical/sibling readback |
Routes /career/builder/:id, /career/builder, /career/profile, public /cv/:slug and private/published PDF authorization remain unchanged.
Rollback is the bounded preview-owner/label change. Adapt chrome-specific test expectations if that presentation is reverted, while retaining regression protection for newest responses, fit/focus/draft and long-document scroll preservation. There is no stored preference, data migration, backend deployment or second renderer to unwind.
| File | Responsibility |
|---|---|
job-tracker-ui/src/views/CvBuilderEditor.tsx |
Existing preview pane chrome, settled-HTML presentation marker, initial fit attachment and named editor return |
job-tracker-ui/src/i18n/translations.ts |
Five paired English/Bokmål preview scale/return/help/error descriptions; existing page labels reused |
job-tracker-ui/src/cv-builder-deep-link.test.tsx |
Four behavioural cases and stronger existing first-error assertions |
job-tracker-ui/e2e/cv-preview-presentation.spec.ts |
Native one/multi/very-long preview presentation, fit/reflow, keyboard return, loading/error/retry and source isolation |
job-tracker-ui/e2e/smoke.spec.ts |
Existing mobile Edit-mode button now matched by exact accessible name, with explicit selected-mode/visible-editor assertions |
Regression-first proof
Before implementation, four new focused cases failed on missing initial-fit presentation, settled busy/count semantics and the editor-return action. The complete owner file subsequently passed 34/34. The overlap case also proves that older successful and failed requests cannot replace newer HTML/count/state. Its in-process measurement event is complemented by actual Chromium native readiness below; it is not a PDF mock claimed as native fidelity proof.
Review caught and removed a terminal-error skeleton: loading chrome is gated on actual preview busy state, including initial failure/retry. No renderer, persistence, template, source, provider or route code was changed to obtain a pass.
The first native presentation attempts sent Ctrl+End as soon as Page Down moved more than 100px. Native keyboard smooth scrolling was still in progress: the English attempt did not reach the end, while the identical Bokmål path could pass. The fixture now attaches a native scrollend listener before Page Down and awaits that event before Ctrl+End. It retains the exact end/start assertions and never scrolls via test JavaScript or sleeps. The two native journeys then passed without an application key handler. Element screenshots also scrolled tall preview panes and captured sticky-shell overlap; final evidence uses actual viewport screenshots, without moving the page to manufacture a component image. The complete run validates the final stronger Shift+Tab → return action → named editor focus contract.
The first complete candidate run reported 107 passes / one failure. Its inherited Career Workspace smoke case used a partial accessible-name match for Edit, which now also matched the distinct Go to editor return action. Playwright rejected the ambiguous locator before activating either button. The readiness and new native mode/return checks remained green. This is why one additional test file outside the preview owners changes: an exact Edit match retains the intended mobile mode button, and selected-state/visible-editor assertions strengthen the original behaviour. No production control was removed/renamed to satisfy a selector, no case was skipped, and no assertion was weakened. The corrected case and complete suite are rerun; the failed attempt remains recorded separately from final acceptance.
Final validation and evidence
Final validation on 2026-10-07, after the last application change and the corrected smoke fixture:
| Gate | Inherited | Final result |
|---|---|---|
| Backend Release tests | 941 | 941 passed, zero failures/skips |
| Frontend Jest | 430, 80 suites | 434 passed, 80 suites; four new owner cases |
| Chromium browser | 106 actual passes | 108 actual passes, zero failures/skips/expected failures/flaky results; two new native cases |
| Original rendering oracle | 16/16, 36 = 36 pages | 16/16, 36 preview = 36 PDF pages |
| Revision-readiness subset | 5/5 | 5/5; included in the unchanged backend total |
| Rate-limit / ingress / shared PDF admission | 9 / 6 / 1 | 9 / 6 / 1 passed |
| Configuration/Compose | 3 | 3 passed |
| TypeScript / edited e2e TypeScript / lint | Passing | Passing, zero lint warnings |
| Frontend production / API Release builds | Passing | Passing; API zero warnings/errors |
| Supply-chain / Ollama evaluation / extension checks | Existing ancillary checks | 3 / 5 / 3 passed |
| Secret / whitespace / current local Markdown targets | Passing | Passing; final staged review includes the retained evidence |
No existing case was removed or quarantined. Strengthened retry, mobile mode and scroll-retention assertions do not increase case counts. P2-D01 is an ordinary required passing native package case. The first browser summary retains 107 passes / one locator failure; the final summary records the complete clean 108-case run (17.2 minutes). Earlier interrupted runs are not accepted baselines. Raw outputs stay ignored under .artifacts/p5-11/; retained summaries contain no credentials, cookies or raw traces.
The fidelity results / measurements pass the unchanged oracle: exact page count/content/page membership, dimensions within 0.5mm, anchors within 2mm, font size within 0.5pt and RGB within 1/255. Maximum observed anchor variance is 1.493055mm vertically and 0.000004mm horizontally; font variance is 0.005026pt and colour variance 0.00004510. No tolerance was widened. All 16 renderer HTML hashes are identical to the accepted pagination checkpoint. This measures local output fidelity, not production font/photo qualification.
Visual, accessibility and performance evidence
Visual manifest records 44 actual viewport captures from the final run, with state, locale/theme and SHA-256. Representative manually reviewed evidence:
| State / scope | Evidence |
|---|---|
| Code one-page, light/English desktop | Default, keyboard focus |
| Narrow phone header, fit and focus | Light/English 320px, dark/Bokmål 320px |
| Intermediate focused pane and unchanged paper | 768px |
| Multi-page Minimal, start/end | Start, end |
| Ten-page document, phone | Last page |
| Visible loading/error chrome in both locales/themes | Loading light/English, error light/Bokmål, loading dark/English, error dark/Bokmål |
Two reviewers inspected representative final images, including 24 independently reviewed loading/error, one/multi/long, fit and focus captures. No preview-chrome visual blocker was found: readable secondary labels, white paper in dark mode, visible violet/lavender focus, stable page boundaries and no horizontal overflow at 1440/768/320px. The 320px Bokmål fit label occupies one line and the return action its own row. The longer Bokmål Retry label wraps inside its alert without overlap. Inherited keyboard fixtures additionally cover light/Bokmål and dark/English with the final chrome.
Focused desktop/intermediate/long captures can place the preview header above the viewport or partly behind the existing sticky global bar. Those images prove paper/focus/retention, not visible loading/error controls; the separate inherited visible-state captures provide that evidence. The existing entry/exit and focus-scroll checks pass, but broader pointer/reflow/manual sticky-boundary qualification remains open. At 34% phone auto-fit the document text is small; no manual zoom capability is implied.
Axe summary: 211 scans, zero reported violations, including six new scoped preview scans. New preview scans retain incomplete colour-contrast checks, which are not passes. Native keyboard regression covers entry, Page Down, Home/End, iframe traversal and Shift+Tab → Go to editor → existing editing-region focus, including pending draft retention. Screenshots were manually reviewed; this is not human screen-reader/spoken-announcement acceptance.
Scroll retention records all four held/failed checks with 0px change: scroll top remains 7189px and height 7963px. The earlier candidate lost 200px because its label slots disappeared. Native DOM identity and HTML assertions also pass through updates/failures and viewport changes. Viewport geometry retains the actual focus/capture bounds, including the above qualification.
Local layout timing records the unchanged 16-fixture corpus: median native measurement 34.8ms → 20.1ms, range 19.5–43.8ms → 12.8–27.4ms. This is one local observation per fixture, not a statistical improvement or production benchmark. Additional native measurements show one/three-page initial layout at 11.8–25.9ms, ten-page initial layout at 19.2–21.6ms and held/retried ten-page layout at 126.0–128.3ms. No equivalent previous ten-page update benchmark exists, so an update-speed improvement is not claimed. No material regression was detected locally; the existing debounce/renderer boundary is retained, and zoom/reflow does not recreate page iframes or change renderer HTML. No continuous per-keystroke measuring loop is added.
P5-11 accepted locally within this scoped evidence. No new P5-12 blocker was found. Its setting → control → normalized value → renderer matrix remains its own required prerequisite; it has not begun. This is not full Phase 5, human accessibility or production acceptance.
Reproducible commands use the installed .NET SDK and the existing bundled Python/Chromium, with rtk as the local execution wrapper. Root commands:
dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-build --logger "trx;LogFileName=p5-11.trx" --results-directory .artifacts/p5-11/backend
dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-build --filter FullyQualifiedName~CvRevisionReadinessTests
dotnet build JobTrackerApi/JobTrackerApi.csproj --configuration Release --no-restore -warnaserror
python -X utf8 scripts/verify-cv-fidelity.py .artifacts/p5-11/fidelity
python scripts/test-ingress.py
python scripts/test-supply-chain.py
python scripts/test-ollama-evaluation.py
node browser-extension/tests/capture-url.test.mjs
python scripts/supply-chain.py scan-secrets
python .artifacts/p4-04-check-doc-links.py
git diff --check
git diff --cached --check
UI commands run from job-tracker-ui:
npm test -- --runInBand --silent --json --outputFile=../.artifacts/p5-11/jest.json
npm run typecheck
npm exec tsc -- --project ../.artifacts/p5-09-readiness-e2e-tsconfig.json --noEmit --incremental false
npm run lint
npm run build
npm run test:e2e -- --output=../.artifacts/p5-11/browser --reporter=line,json
npm run test:rate-limits -- --output=../.artifacts/p5-11/rate-limits --reporter=line
npm exec playwright test -- --config=playwright.ingress.config.ts --output=../.artifacts/p5-11/ingress --reporter=line
npm run test:pdf-admission -- --output=../.artifacts/p5-11/pdf-admission --reporter=line
The build uses NEXT_PUBLIC_API_BASE_URL=http://localhost:5302/api. Browser validation uses DOTNET_ROOT, the installed DOTNET_HOST_PATH, PLAYWRIGHT_STATIC_EXPORT=true, PLAYWRIGHT_PREBUILT_EXPORT=true, PLAYWRIGHT_REUSE_SERVERS=false, CV_FIDELITY_OUTPUT=../.artifacts/p5-11/fidelity and PLAYWRIGHT_JSON_OUTPUT_NAME=../.artifacts/p5-11/browser.json. Each run owns a fresh disposable database and local API/static export. The specialized suites run sequentially because they share port 5302. The ignored e2e TypeScript configuration includes every e2e/**/*.ts; the ignored link checker verifies current/root local Markdown targets and excludes historical archives. No application configuration, CI or test-runner configuration changes are included.
Remaining P5-12 boundary and open qualifications
P5-12 owns template/document controls, P5-13 AI review presentation and P5-14 final responsive CV acceptance. None begins here. No manual zoom/new preview mode, template capability, document geometry or PDF semantics is introduced.
Human screen-reader acceptance for P5-15 remains open: automated names, roles, keyboard and axe results do not establish spoken announcements. Retain incomplete axe/manual checks, broader pointer/reflow and production font/photo/browser qualification, F04 production ingress evidence, Dashboard preference-save 500, Board full-result scaling/native touch, deferred followMode compose UI, P5-10-O01 nested drag ownership and existing separately recorded timing/focus observations. This local preview checkpoint cannot close them. No B07 or production deployment occurs.