# 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](../PHASE5_PLAN.md#p5-11--live-preview-presentation), [P5-09 frame](../p5-09/README.md), [P5-10 editing boundary](../p5-10/README.md), [revision readiness](../p5-09-readiness/README.md) and [pagination oracle](../p5-09-pagination-readiness/README.md) 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](evidence/browser-first-summary.json) retains 107 passes / one locator failure; the [final summary](evidence/browser-summary.json) 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](evidence/fidelity-results.json) / [measurements](evidence/measurements.json) 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](evidence/renderer-source-hashes.json) are identical to the accepted pagination checkpoint. This measures local output fidelity, not production font/photo qualification. ## Visual, accessibility and performance evidence [Visual manifest](evidence/visual-manifest.json) 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](evidence/light-en-one-page-code-default.png), [keyboard focus](evidence/light-en-one-page-code-focus.png) | | Narrow phone header, fit and focus | [Light/English 320px](evidence/light-en-one-page-code-fit-320.png), [dark/Bokmål 320px](evidence/dark-nb-one-page-code-fit-320.png) | | Intermediate focused pane and unchanged paper | [768px](evidence/light-en-one-page-code-fit-768.png) | | Multi-page Minimal, start/end | [Start](evidence/dark-nb-multiple-pages-start.png), [end](evidence/light-en-multiple-pages-end.png) | | Ten-page document, phone | [Last page](evidence/dark-nb-very-long-phone-last-page.png) | | Visible loading/error chrome in both locales/themes | [Loading light/English](evidence/inherited-loading-light-en.png), [error light/Bokmål](evidence/inherited-error-light-nb.png), [loading dark/English](evidence/inherited-loading-dark-en.png), [error dark/Bokmål](evidence/inherited-error-dark-nb.png) | 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](evidence/axe-summary.json): **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](evidence/scroll-retention.json) 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](evidence/preview-viewports.json) retains the actual focus/capture bounds, including the above qualification. [Local layout timing](evidence/layout-performance.json) 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](evidence/preview-measurements.json) 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: ```text 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`: ```text 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.