Merge: t-paliad-369 docforge UX PRD — truthful base preview + coherent drafting flow
This commit is contained in:
344
docs/plans/prd-docforge-ux-2026-06-01.md
Normal file
344
docs/plans/prd-docforge-ux-2026-06-01.md
Normal file
@@ -0,0 +1,344 @@
|
||||
# PRD — docforge UX: truthful base preview + a coherent submission-drafting flow
|
||||
|
||||
**Task:** t-paliad-369 (m 2026-06-01 17:16) · **Author:** leibniz (inventor) · **Date:** 2026-06-01
|
||||
**Status:** DESIGN — awaiting head's go/no-go on the coder shift. **No code in this task.**
|
||||
**Scope:** the **UX/flow around** the generation engine. The engine itself (Rubrum styling,
|
||||
caption, letterhead from the HLC `.dotm`) was made correct today — t-paliad-364 / -365 / -367 —
|
||||
and is **explicitly out of scope to redesign.**
|
||||
|
||||
**Reading order for the reviewer:** §0 (m's decisions) → §1 (journey map + hacky inventory) →
|
||||
§2 (the base-preview feature, with wireframes) → §3 (prioritized plan) → §4 (out of scope).
|
||||
|
||||
---
|
||||
|
||||
## §0 m's decisions (2026-06-01)
|
||||
|
||||
m was grilled in prose on the UX *vision*, then answered four concrete option-pickers.
|
||||
Both rounds are folded in here; the open-question record is §5.
|
||||
|
||||
### 0.1 Prose-grill answers (UX vision)
|
||||
|
||||
| # | Question | m's answer |
|
||||
|---|---|---|
|
||||
| G1 | What reads as "hacky"? | **(a) blind base dropdown, (b) invisible Bearbeiten-vs-Generieren, (c) preview ≠ real output, (d) dense panels — ALL bad.** NOT (e) `[KEIN WERT]` walls, NOT (f) duplicate catalog. |
|
||||
| G1′ | Panel count | **m sees TWO panels, not three.** (Resolved — see §1.4: the section panel is conditional and silently absent for his drafts.) |
|
||||
| G2 | Akte-first vs free-start | **Keep free-start first-class.** No Akte gating. Teach project-less drafts to fill what they can from the proceeding (already done, t-paliad-364). |
|
||||
| G3 | Base-preview fidelity | **Truthful.** "truthful would be awesome" — a real image of the actual Word page (letterhead logo, fonts, Rubrum table). |
|
||||
| G4 | Editor live-preview fidelity | **Structural suffices** ("a, I think") — show where vars/prose land. The live editing preview stays approximate; pixel-fidelity is **only** required for the base preview. |
|
||||
| G5 | Entry points | **Keep distinct, make consistent.** "in a project it is a bit different than the free approach." Don't converge the project tab and the global picker. |
|
||||
|
||||
**The load-bearing split (G3 vs G4):** *base preview = truthful (pixel-true .docx render)*;
|
||||
*live editing preview = structural (approximate HTML)*. The truthful infra built for the base
|
||||
preview *could* later upgrade the editing preview, but m does not require that now.
|
||||
|
||||
### 0.2 Concrete option-picker answers
|
||||
|
||||
| # | Decision | m's pick | Note |
|
||||
|---|---|---|---|
|
||||
| Q1 | Base-preview render infra | **LibreOffice on-demand + cached** | Renders the *actual* base with the draft's real data (`.docx`→PDF→image), cached. Truest "what you preview is what you generate". Cost: headless LibreOffice in the container + cold-render latency — scoped in §2.3. |
|
||||
| Q2 | Where the preview surfaces | **"Vorschau" button → modal** | Not an always-on inline pane. Pairs cleanly with on-demand rendering — we only render when the lawyer asks. |
|
||||
| Q3 | Bearbeiten vs Generieren | **One primary + ⋯ menu** | Primary "Entwurf öffnen"; a kebab offers the fast "Direkt exportieren" path. Kills the two-equal-buttons ambiguity, keeps the capability. |
|
||||
| Q4 | Editor sidebar density | **Meta → header toolbar** | Draft-meta (name, keyword, base+Vorschau, language) moves to a top header strip; the sidebar keeps only the fill-in work (parties + variables). |
|
||||
|
||||
---
|
||||
|
||||
## §1 Current journey map + the hacky inventory
|
||||
|
||||
Three entry points, one editor, one authoring page. All gated behind auth; knowledge-platform
|
||||
pages are separate. Grounded in the live tree (verified 2026-06-01).
|
||||
|
||||
### 1.1 Entry A — the project "Schriftsätze" tab
|
||||
`frontend/src/client/submissions.ts` · per-project, opened from a project detail page.
|
||||
|
||||
- Shows the **full cross-proceeding catalog** grouped by proceeding, with the project's own
|
||||
proceeding pinned at the top (lime border, " (dieses Projekt)" suffix) — m's 2026-05-23 decree.
|
||||
- **Each row carries two buttons:** `Bearbeiten` (→ editor at `/projects/{id}/submissions/{code}/draft`)
|
||||
and `Generieren` (POST `…/generate` → immediate `.docx` download, **skipping the editor**).
|
||||
- A `universell` badge marks rows without a dedicated per-code template.
|
||||
|
||||
**Hacky (G1-b):** the two buttons read as equals, but their behaviour is wildly different — one
|
||||
opens an editor, the other silently downloads a file. And the one-click `Generieren` runs the
|
||||
**merge path** (`onGenerateClick`, `submissions.ts:212`), which historically produced an *unfilled*
|
||||
doc (the t-paliad-363 P3 finding); even after today's fixes, "download a finished doc without ever
|
||||
seeing it" is a foot-gun next to "open the editor".
|
||||
|
||||
### 1.2 Entry B — the global `/submissions/new` picker
|
||||
`frontend/src/submissions-new.tsx` + `client/submissions-new.ts` · cross-proceeding catalog.
|
||||
|
||||
- Search box + proceeding chips + a grouped, read-only catalog table (just got the filter +
|
||||
group-header contrast fixes, t-paliad-365).
|
||||
- Each row: **"Entwurf starten"** → choose **"Ohne Projekt"** (jump straight to the draft) or
|
||||
**"Mit Projekt verknüpfen"** (a project-picker modal → project-scoped draft).
|
||||
|
||||
**Consistent-but-distinct (G5):** this serves a different moment than Entry A — *browsing the whole
|
||||
catalog and starting fresh* vs *working inside a known case*. m wants these kept distinct. The job
|
||||
is **consistency of affordances** (same row buttons, same naming, same kebab), not convergence.
|
||||
The duplicate-catalog concern (G1-f) m explicitly waved off.
|
||||
|
||||
### 1.3 Entry C — the draft editor
|
||||
`frontend/src/submission-draft.tsx` (shell) + `client/submission-draft.ts` (2873-line bundle).
|
||||
|
||||
Layout today (`.submission-draft-grid`):
|
||||
- **Sidebar** (`aside.submission-draft-sidebar`): draft switcher + "+ Neuer Entwurf" → name → keyword
|
||||
(filename) → **base picker** → language toggle → save status → "Aus Projekt importieren" → party
|
||||
picker → **~20 variable fields**. Everything stacked vertically.
|
||||
- **Section list** (`section.submission-draft-sections-wrap`, **`display:none` by default**) — the
|
||||
Composer prose-section editor. Painted *only* when the draft has seeded section rows.
|
||||
- **Preview pane** (`section.submission-draft-preview-wrap`): a read-only, lossy HTML render
|
||||
(`paintPreview`, `submission-draft.ts:1209`; `preview_html` from the server).
|
||||
|
||||
**Hacky (G1-a):** the base picker is a bare `<select>` (`#submission-draft-base`,
|
||||
`paintBasePicker`, `submission-draft.ts:1257`). It mixes legacy Gitea bases and uploaded templates
|
||||
in one dropdown (an optgroup "Hochgeladene Vorlagen"), and the lawyer picks **blind** — no idea
|
||||
what letterhead/structure each base yields. *This is m's headline ask.*
|
||||
|
||||
**Hacky (G1-c):** the preview pane is plain HTML — no letterhead logo, no HLpat fonts, no Rubrum
|
||||
table layout. It looks nothing like the exported Word document. "What you preview" ≠ "what you
|
||||
generate."
|
||||
|
||||
**Hacky (G1-d):** the sidebar is a long vertical stack of unlike things — draft management, template
|
||||
choice, language, parties, and every variable field — all competing for the same column.
|
||||
|
||||
### 1.4 The 2-vs-3-panel discrepancy — RESOLVED
|
||||
m reported seeing **two** panels; the brief described three. **m is right.** The middle
|
||||
"Abschnitte" panel is `wrap.style.display = "none"` whenever `state.view.sections` is empty
|
||||
(`paintSectionList`, `submission-draft.ts:1362-1364`). Section rows are seeded only for **Composer
|
||||
drafts** (a `base_id` whose `submission_bases.section_spec` seeds them) or when the lawyer manually
|
||||
adds a section. m's drafts (project-less / pre-Composer, `base_id IS NULL`) have **zero** sections,
|
||||
so the panel never appears and he sees `sidebar ‖ preview`.
|
||||
|
||||
This is itself a **silent UX inconsistency**: the editor is 2 panels for some drafts and 3 for
|
||||
others, with no signpost that a section editor exists. The layout design below is grounded in the
|
||||
**2-panel reality** and makes the third panel's presence/absence *explicit* (§3 Slice 5).
|
||||
|
||||
### 1.5 Entry D — `/admin/templates` authoring
|
||||
`frontend/src/templates-authoring.tsx` + `client/templates-authoring.ts` (docforge slice 6).
|
||||
|
||||
Admin-only. Upload `.docx` → render run-addressable text → select a span + pick a variable from the
|
||||
palette → drop a `{{slot}}` → save as a reusable template. Three columns: palette ‖ preview ‖ slots.
|
||||
|
||||
**Touch only for consistency:** uploaded templates surface in the *same* editor base picker as the
|
||||
Gitea bases, so the **base-preview feature (§2) must cover uploaded templates too** — an authored
|
||||
template should be previewable exactly like a Gitea base. No authoring-page redesign in this PRD.
|
||||
|
||||
### 1.6 Inventory summary (what we fix vs what m waved off)
|
||||
|
||||
| Smell | m's verdict | Addressed in |
|
||||
|---|---|---|
|
||||
| (a) blind base dropdown | **fix** | §2 base preview + §3 S2/S3/S4 |
|
||||
| (b) invisible Bearbeiten/Generieren | **fix** | §3 S1 |
|
||||
| (c) preview ≠ real output | **fix** (truthful base preview; live preview stays structural) | §2 + §3 S3/S4 |
|
||||
| (d) dense panels | **fix** | §3 S2 |
|
||||
| (e) `[KEIN WERT]` walls | not a priority (engine fix t-paliad-364 already softens) | §3 S5 (light polish only) |
|
||||
| (f) duplicate catalog | keep distinct | §3 S1 (consistency, not convergence) |
|
||||
|
||||
---
|
||||
|
||||
## §2 The template-base preview (m's headline ask)
|
||||
|
||||
> "Can we have a preview of the template base we are using?"
|
||||
|
||||
**Shape:** a **"Vorschau" button** (Q2) opens a **modal** that renders the selected base as a
|
||||
**truthful image of the actual Word page** (Q3) — same engine, same bytes that an export would
|
||||
produce — via **headless LibreOffice on-demand, cached** (Q1). The modal carries a base-switcher so
|
||||
the lawyer can flip bases and compare them truthfully → *"what you preview is what you generate."*
|
||||
|
||||
### 2.1 Wireframe — the Vorschau modal
|
||||
|
||||
```
|
||||
┌─ Vorschau — Vorlagenbasis ───────────────────────────────────────────────┐
|
||||
│ Basis: [ HLC Briefkopf ▾ ] Sprache: (•DE) ( EN ) [ ✕ ] │
|
||||
│ ├ HLC Briefkopf │
|
||||
│ ├ Universelles Skelett │
|
||||
│ ├ LG Düsseldorf │
|
||||
│ ├ UPC Formblatt │
|
||||
│ └ ── Hochgeladene Vorlagen ── │
|
||||
│ └ HLC Patents Style v0.26… │
|
||||
├───────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────┐ │
|
||||
│ │ [HLC logo] Hogan Lovells … │ ← real page │
|
||||
│ │ ───────────────────────────────────────────────── │ image, page │
|
||||
│ │ Landgericht Düsseldorf │ 1 of N │
|
||||
│ │ │ (LibreOffice │
|
||||
│ │ In dem Rechtsstreit │ .docx→PDF→PNG │
|
||||
│ │ Mustermandant GmbH – Klägerin – │ of the SAME │
|
||||
│ │ ./. │ bytes export │
|
||||
│ │ Musterbeklagte AG – Beklagte – │ would emit) │
|
||||
│ │ wegen Patentverletzung │ │
|
||||
│ │ Az. 4c O 12/23 │ │
|
||||
│ │ … │ │
|
||||
│ └──────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ‹ Seite 1 / 3 › Daten: (• meine Daten) ( Beispiel ) │
|
||||
│ [ Diese Basis verwenden ] │
|
||||
└───────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **Base-switcher** (top): flips the previewed base. "Diese Basis verwenden" commits it to the draft
|
||||
(the existing `base_id` / `template_version_id` PATCH). So the modal is *both* a preview and the
|
||||
base-chooser — replacing the blind `<select>` as the primary base-selection surface.
|
||||
- **Daten toggle:** *meine Daten* renders the draft's resolved bag (truthful to the export);
|
||||
*Beispiel* substitutes canned sample data (Mustermandant ./. Musterbeklagte, Az. 4c O 12/23) so a
|
||||
fresh/project-less draft previews a *full* page instead of `[KEIN WERT]` markers. Default: *meine
|
||||
Daten* when the draft has resolved values, *Beispiel* when it is essentially empty.
|
||||
- **Paging:** multi-page docs render page-by-page (`‹ Seite n / N ›`).
|
||||
|
||||
### 2.2 Where the button lives
|
||||
|
||||
Per Q4, the base picker moves into the **editor header toolbar**, and the "Vorschau" button sits
|
||||
right next to it (§3 S2 wireframe). The same modal is also reachable from a **catalog-row kebab**
|
||||
("Vorschau Vorlagenbasis", §3 S1) so a lawyer can eyeball a base *before* even opening the editor.
|
||||
|
||||
### 2.3 The truthful-render infra (Q1 = LibreOffice on-demand + cached)
|
||||
|
||||
**Pipeline (server-side, new `GET` preview endpoint):**
|
||||
|
||||
1. Resolve the request: `(draft_id | base identity, lang, data-mode)`.
|
||||
2. Run the **existing export pipeline** to produce `.docx` bytes — *the same compose/merge code an
|
||||
export uses*, so the preview is byte-faithful by construction. (Project-less / sample-data mode
|
||||
feeds a canned bag; "meine Daten" feeds the draft's resolved bag.)
|
||||
3. `soffice --headless --convert-to pdf` (headless LibreOffice) → PDF.
|
||||
4. PDF → PNG per page (poppler `pdftoppm`, or equivalent) at a sensible DPI.
|
||||
5. Return the PNG(s); the modal renders them.
|
||||
|
||||
**Caching** (this is what makes "on-demand" affordable):
|
||||
- Key on `(template identity, lang, data-mode, hash(resolved-bag))`. Identical inputs → cached PNG,
|
||||
no re-render. The bag-hash means editing a variable invalidates only that draft's preview.
|
||||
- Sample-data mode caches per `(base, lang)` only (data is constant) — so base *browsing/compare* is
|
||||
effectively free after the first render of each base.
|
||||
- Cache store: on-disk under the response/temp dir, or a small table — coder's call. No binary is
|
||||
retained as a *document* (the §0.5.7 no-retention invariant is about exported documents; preview
|
||||
PNGs are a regenerable cache, not a stored artifact — flag for the coder to confirm framing).
|
||||
|
||||
**Cost / risk to flag for m + coder:**
|
||||
- **Container dependency:** headless LibreOffice (~hundreds of MB) must be added to the Dokploy
|
||||
image (or a sidecar). This is the single biggest cost of the truthful path. *Recommend a sidecar/
|
||||
separate stage so the main Go image stays lean — coder evaluates.*
|
||||
- **Cold-render latency:** first render of a given `(base, lang, data)` is seconds (LibreOffice
|
||||
spin-up + convert). The modal shows a spinner ("⟳ wird gerendert…"); the cache makes repeats
|
||||
instant. A warm-cache pass over the 4–5 known bases in *sample-data* mode can pre-render the common
|
||||
cases at deploy time.
|
||||
- **Concurrency:** LibreOffice headless is single-instance-touchy; serialise conversions through a
|
||||
small worker/queue (one `soffice` at a time, or a pool). Flag for the coder.
|
||||
|
||||
**Tracer-bullet sequencing (important):** the modal **UX ships before the LibreOffice infra** — see
|
||||
§3 S3 (modal scaffold rendering the *existing structural HTML* first) → S4 (swap the modal body to
|
||||
the real PNG once LibreOffice lands). This de-risks the heavy infra: the base-compare UX is usable
|
||||
and reviewable immediately, and the truthful render drops in behind the same modal.
|
||||
|
||||
---
|
||||
|
||||
## §3 Prioritized UX-improvement plan (tracer-bullet first)
|
||||
|
||||
Cheap → meaty. Each slice independently shippable and independently reviewable. **S1/S2 are the
|
||||
quick wins that fix three of m's four "hacky" complaints with no new infra.**
|
||||
|
||||
### Slice 1 — Catalog row: one primary + ⋯ menu, consistent across both entry points *(quick win — kills G1-b, delivers G5-consistency)*
|
||||
TS/CSS only, no backend.
|
||||
- Replace the two equal buttons with a **primary `Entwurf öffnen`** + a **`⋯` kebab**:
|
||||
`Direkt exportieren (.docx)` and `Vorschau Vorlagenbasis`.
|
||||
- Apply the **same row component / vocabulary** to the project Schriftsätze tab
|
||||
(`client/submissions.ts`) *and* the global picker (`client/submissions-new.ts`) so they read
|
||||
consistently — while keeping each surface's distinct context (own-proceeding pin on the tab;
|
||||
search + chips + project-link modal on the global picker).
|
||||
- Drop the bare `universell` jargon badge for a clearer tooltip/label.
|
||||
|
||||
```
|
||||
Klageerwiderung Beklagte § 277 ZPO [ Entwurf öffnen ] [ ⋯ ]
|
||||
de.inf.lg.erwidg └─┐
|
||||
• Direkt exportieren (.docx)
|
||||
• Vorschau Vorlagenbasis
|
||||
```
|
||||
|
||||
### Slice 2 — Editor header toolbar: meta out of the sidebar *(quick win — kills G1-d)*
|
||||
TS/CSS layout, no backend.
|
||||
- Lift draft-meta (switcher, name, keyword, **base picker + 👁 Vorschau button**, language) into a
|
||||
**header strip** above the working area. Sidebar keeps only **parties + variables** (the fill-in
|
||||
work). Export button stays top-right.
|
||||
|
||||
```
|
||||
┌─ Klageerwiderung · de.inf.lg.erwidg ───────────────────────────────────────────┐
|
||||
│ Entwurf:[ Entwurf v2 ▾ ][+ Neu] Name:[__________] Stichwort:[__________] │
|
||||
│ Vorlagenbasis:[ HLC Briefkopf ▾ ][👁 Vorschau] Sprache:(•DE)(EN) [Als .docx ⤓]│
|
||||
├──────────────────────────────────┬──────────────────────────────────────────────┤
|
||||
│ PARTEIEN │ VORSCHAU (Struktur — wo Daten/Text landen) │
|
||||
│ ☑ Mustermandant (Klägerin) │ [letterhead] │
|
||||
│ ☑ Musterbeklagte (Beklagte) │ In dem Rechtsstreit … │
|
||||
│ VARIABLEN │ Az. «project.case_number» │
|
||||
│ project.case_number [________] │ … │
|
||||
│ … │ │
|
||||
└──────────────────────────────────┴──────────────────────────────────────────────┘
|
||||
```
|
||||
(When the draft has sections, the Composer "Abschnitte" panel sits between sidebar and preview —
|
||||
see S5 for making that presence explicit.)
|
||||
|
||||
### Slice 3 — Vorschau modal scaffold (structural render first) *(tracer bullet for the base preview)*
|
||||
- Build the modal shell (§2.1): base-switcher, Sprache toggle, Daten toggle, paging frame,
|
||||
"Diese Basis verwenden", spinner state.
|
||||
- Wire the `👁 Vorschau` button (S2 header) and the row kebab (S1) to open it.
|
||||
- **Body initially renders the existing structural `preview_html`** for the selected base — so the
|
||||
whole base-compare + choose UX is live and reviewable *before* any LibreOffice work.
|
||||
|
||||
### Slice 4 — Truthful render: LibreOffice on-demand + cached *(the meaty infra — delivers G3)*
|
||||
- Add the preview endpoint + the `.docx`→PDF→PNG pipeline + cache (§2.3).
|
||||
- Add headless LibreOffice to the deploy (sidecar recommended) + a single-flight conversion worker.
|
||||
- **Swap the modal body** from structural HTML (S3) to the real page PNG(s). Same modal, same UX.
|
||||
- Warm-cache the known bases in sample-data mode at deploy.
|
||||
- *This is the slice that carries the container-dependency + latency cost — gate it on m's explicit
|
||||
OK for the LibreOffice dependency.*
|
||||
|
||||
### Slice 5 — Section-panel discoverability + small honesty polish *(polish)*
|
||||
- Make the conditional "Abschnitte" panel **explicit**: when a draft *could* have sections but has
|
||||
none, show a slim empty-state ("Dieser Entwurf hat keine Abschnitte — [+ Abschnitt hinzufügen]")
|
||||
instead of silently rendering nothing — so the section editor is discoverable (fixes the §1.4
|
||||
inconsistency). When sections genuinely don't apply (pure merge-path draft), say so once.
|
||||
- Light `[KEIN WERT]` softening in the *live* preview (e.g. a muted "‹noch leer: …›" treatment)
|
||||
so honest gaps read as gentle prompts, not errors. (G1-e is low priority; keep it light.)
|
||||
|
||||
**Ordering rationale:** S1+S2 ship the visible "less hacky" wins immediately (rows + editor layout),
|
||||
no infra. S3 lands the base-preview *experience* on cheap rails. S4 makes it *truthful* — the one
|
||||
slice with real infra cost, isolated and gated. S5 is discoverability polish.
|
||||
|
||||
---
|
||||
|
||||
## §4 Out of scope
|
||||
|
||||
- **Implementation / code / migration SQL.** Design only.
|
||||
- **Redesigning the generation engine** — Rubrum/caption/letterhead/HLpat styling are correct as of
|
||||
t-paliad-364/-365/-367. This PRD touches only the UX *around* it.
|
||||
- **Upgrading the LIVE editing preview to pixel-true** — m: structural suffices (G4). The truthful
|
||||
infra (post-S4) *could* later power a "truthful full-document" view in the editor, but that is a
|
||||
future opt-in, not this work.
|
||||
- **Converging the two entry points** — m: keep distinct (G5). We make them *consistent*, not one.
|
||||
- **Akte-first gating** — m: free-start stays first-class (G2).
|
||||
- **`/admin/templates` authoring redesign** — only consistency touch is that uploaded templates must
|
||||
be previewable via §2; no workflow rework.
|
||||
- **Multi-user concurrent editing** of one draft.
|
||||
- **The `[KEIN WERT]` product question** (require-Akte vs fill-what-we-can) — already resolved by
|
||||
t-paliad-364 (fill-what-we-can); not reopened here.
|
||||
|
||||
---
|
||||
|
||||
## §5 Open-question record (historical)
|
||||
|
||||
These were the open questions before m ratified them; kept for the record. All are now closed in §0.
|
||||
|
||||
- **OQ-G1** What specifically feels hacky? → closed (G1: a/b/c/d, not e/f).
|
||||
- **OQ-G2** Akte-first vs free-start? → closed (free-start first-class).
|
||||
- **OQ-G3** Base-preview fidelity? → closed (truthful).
|
||||
- **OQ-G4** Editor live-preview fidelity? → closed (structural suffices).
|
||||
- **OQ-G5** Converge or keep entry points distinct? → closed (distinct + consistent).
|
||||
- **OQ-Q1** Base-preview render infra? → closed (LibreOffice on-demand + cached).
|
||||
- **OQ-Q2** Where does the preview surface? → closed ("Vorschau" button → modal).
|
||||
- **OQ-Q3** Bearbeiten vs Generieren? → closed (one primary + ⋯ menu).
|
||||
- **OQ-Q4** Sidebar density? → closed (meta → header toolbar).
|
||||
|
||||
### Flags for the eventual coder (resolve in implementation chat)
|
||||
1. **LibreOffice deployment shape** — sidecar vs. in-image; the single-flight conversion worker.
|
||||
2. **Preview cache** — on-disk vs. a small table; eviction policy; confirm preview PNGs are framed as
|
||||
a regenerable cache, not a retained "document" (vs the §0.5.7 no-retention invariant).
|
||||
3. **DPI / page-image size** — fidelity vs. payload weight for the modal.
|
||||
4. **Sample-data source** — a single canned bag, or per-jurisdiction sample bags for nicer previews.
|
||||
5. **Base-picker vocabulary** — with the modal as the chooser, whether to keep "Vorlagenbasis" vs a
|
||||
plainer "Vorlage" label, and how to present Gitea bases vs uploaded templates as one list.
|
||||
Reference in New Issue
Block a user