docforge UX slice S3 (PRD §2.1 / §3 S3). A shared, body-attached modal lets
the lawyer browse template bases, flip output language + data-mode, and — in
the editor — commit a base with 'Diese Basis verwenden', replacing the blind
<select> as the chooser. Opened from the editor 👁 button AND both catalog row
kebabs (the S1/S2 stubs are now live).
Body renders the EXISTING structural preview on cheap rails; the body region is
swappable so S4 drops a truthful .docx→image render behind the same modal +
endpoint shape. Spinner + pager are the shell S4 expects.
Backend (minimal, no truthful render — that's S4):
- GET /api/submission-preview?base&code&lang&data&draft?&project? → {preview_html}.
Draft+mine uses the draft's resolved bag (previews a base the draft has NOT
committed to — no persistence); else a fresh context bag (project-less fill,
t-paliad-364). data=sample swaps a sample missing-marker so unresolved keys
render readable sample text, not [KEIN WERT].
- SubmissionDraftService.RenderPreviewWithMarker + RenderContextPreviewHTML
(thin: reuse BuildRenderBag / vars.Build + RenderHTML).
- resolvePreviewTemplateBytes: tpl:→carrier, base_id→bytes IFF it carries merge
placeholders, else fall back to the code's merge template (anchors-only
Composer bases can't be shown as structural HTML — their letterhead/styling
surfaces in S4's truthful render). sampleMissingMarker by key suffix.
- Unit tests: sampleMissingMarker + normalizePreviewLang.
Frontend:
- New shared client/base-preview-modal.ts (built once, body-attached; serves
editor + project tab + global picker). Base-switcher, DE/EN + meine/Beispiel
toggles, swappable body, spinner, pager stub, conditional 'Diese Basis
verwenden' (editor commits via onBaseChange; catalog = look-only).
- Editor 👁 button un-stubbed + wired; both catalog kebab 'Vorschau' items
un-stubbed + wired. global.css: .base-preview-* modal styles.
bun build (i18n scan clean) + go vet ./... + go test ./... (15 ok, 0 fail).