Files
bea-edit/plan/done/start-page-toolbox-plan.md
2026-10-06 22:52:35 +02:00

260 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Plan: Start-page redesign — PDF toolbox
## Goal
Today the start page (`src/routes/+page.svelte`) offers exactly one entry point: the ZIP dropzone
that starts the beA archive workflow. This redesign turns the start page into a **toolbox hub**:
1. The existing **ZIP upload** (beA workflow) stays available — visually prominent as the
"hero" action of the product.
2. Eight additional tools become reachable as tiles from the start page:
- **Wasserzeichen** — add a watermark to a PDF
- **Signieren** — sign a PDF
- **Passwort setzen** — encrypt a PDF with a password
- **Passwort entfernen** — unlock a password-protected PDF
- **Konvertieren** — convert a PDF
- **Komprimieren** — compress a PDF
- **Seiten entfernen** — remove pages from (separate) a PDF
- **PDFs zusammenfügen** — merge PDFs
All processing remains **100 % client-side** (no uploads, no network requests, no persistence),
in line with the project's privacy constraints.
---
## Target UX
### Start page (`/`)
- The Nominandum header/footer and overall visual language stay untouched.
- A compact hero area with the product name and a one-line value proposition.
- A **large primary tile** for the beA workflow ("ZIP-Archiv bearbeiten") — it keeps the
full-height dropzone feel: dragging a ZIP anywhere on the start page still starts the workflow.
- A **grid of secondary tool tiles** (2–3 columns responsive, `grid` + `gap-*`, white cards on the
pale blue-gray background, Lucide icon + German title + one-line description, visible focus and
keyboard operability — tiles are real links/buttons, not divs).
- German copy throughout (project constraint).
### Tool pages
- Each tool gets its **own route** under `src/routes/tools/…` (see D1):
`tools/bea`, `tools/merge`, `tools/separate`, `tools/watermark`, `tools/sign`,
`tools/encrypt`, `tools/decrypt`, `tools/convert`, `tools/compress`.
- A shared layout (`tools/+layout.svelte`) provides a consistent shell: back-link to `/`,
tool title, and the file-pick area.
- The existing ZIP workflow component tree (`ZipDropzone`, `ProcessedZipArchiveEditor`,
`SubDocumentEditor`, `AttachmentPreview`) moves to `tools/bea` essentially unchanged.
---
## Technical approach per tool
| Tool | Route | Input | Processing | Output |
| ----------------------- | ----------------- | ------------------------ | ------------------------------------ | ---------------------- |
| beA workflow | `tools/bea` | `.zip` (multiple) | unchanged existing pipeline | merged/separate PDFs |
| Merge | `tools/merge` | multiple PDFs | pdf-lib copy of pages in order | one PDF |
| Separate / remove pages | `tools/separate` | one PDF | pdf-lib copy with page selection | one PDF |
| Watermark | `tools/watermark` | one PDF | pdf-lib overlay on every page | one PDF |
| Sign | `tools/sign` | one PDF | pdf-lib places signature image | one PDF |
| Encrypt | `tools/encrypt` | one PDF | encryption library (see D5) | password-protected PDF |
| Unlock | `tools/decrypt` | protected PDF + password | pdfjs (password) → rebuild (see D6) | unprotected PDF |
| Compress | `tools/compress` | one PDF | pdfjs rasterize + re-encode (see D7) | smaller PDF |
| Convert | `tools/convert` | one PDF | pdfjs render pages (see D4) | images or PDF variant |
### 1. Merge PDFs
- Load each selected PDF with pdf-lib, copy pages in the (reorderable) list order into a new
document. This is a generalization of the existing `mergeProcessedZipArchive` logic — extract a
reusable `mergePdfFiles(files: File[]): Promise<Uint8Array>` helper.
### 2. Separate / remove pages
- Render page thumbnails (reuse the pdfjs pattern from `AttachmentPreview.svelte`), show a page
grid with selection; the user deselects/removes pages and downloads the remaining document.
- Page selection must also work with keyboard focus (project accessibility rule).
### 3. Watermark
- Options: text (or uploaded PNG/JPG image), position (center/diagonal/corners), opacity, font
size, page range (all / selected). Drawn with pdf-lib `drawText`/`drawImage` on each selected page.
- No new dependency.
### 4. Sign
- Default proposal (see D3): **visual signature**. The user draws a signature in a canvas element
(mouse/touch/pen), or types a name in a handwriting-like font, or uploads a signature image.
The signature is exported as a transparent PNG and placed by clicking/tapping the desired
position on a rendered page (pdfjs preview + pdf-lib placement).
- PAdES/cryptographic signing is **not** part of the default scope.
### 5. Set password (encrypt)
- pdf-lib does **not** support encryption → a new dependency is unavoidable (see D5).
Primary candidate: `@cantoo/pdf-lib` (drop-in fork of pdf-lib with AES encryption support),
alternative: `qpdf-wasm`.
- UI: password + confirmation field, optional permissions flags (print/copy allowed?).
### 6. Unlock (remove password)
- pdfjs-dist can open password-protected PDFs when given the password; pdf-lib cannot load them.
- Default approach (see D6): open with pdfjs using the password, then **rasterize** each page to a
canvas and rebuild a PDF via pdf-lib. Known tradeoff: text becomes images (no selectable text,
larger file, no vector quality). This is documented to the user in the UI.
- Failing passwords must surface a clear German error message, not a console-only error.
### 7. Compress
- Browser-only compression is realistically rasterization-based: render each page with pdfjs at a
chosen DPI/scale, encode as JPEG at a chosen quality, rebuild the PDF.
- UI presents target presets (e.g. "Starke Komprimierung" / "Ausgewogen" / "Leicht") and shows the
resulting size estimate before download. Same vector→raster caveat as unlock, shown in the UI.
### 8. Convert
- Default scope (see D4): **PDF → images** (PNG or JPEG, one file per page, downloaded as separate
files or a ZIP via fflate — fflate is already a dependency).
- Other directions (images/Office → PDF) already exist in the beA flow for images; extending
convert beyond PDF→images is out of scope for v1.
---
## Shared infrastructure (new `src/lib` pieces)
- **`PdfDropzone` component** — generalization of `ZipDropzone` with configurable `accept` filter
(`.pdf` vs `.zip`) and drag state. `ZipDropzone` is refactored on top of it (or replaced).
- **`usePdfThumbnails` helper / `PdfPageGrid` component** — shared page-thumbnail rendering for
separate/sign/watermark/convert/compress, modeled on `AttachmentPreview.svelte`'s memory rules
(dynamic pdfjs import, byte copies before render, revoked object URLs, destroyed loading tasks).
- **`downloadBlob()` helper** — the current inline `downloadPdfBytes` in `+page.svelte` moves to a
shared `src/lib/download.ts` and gains a generic filename/MIME signature.
- **Tool registry** (`src/lib/tools.ts`) — typed list of `{ slug, title, description, icon }`
consumed by the start page tile grid, so adding a tool later means one entry + one route.
- All browser-only work stays in event handlers / `onMount` (SSR-safe, per `agents.md`).
- File-size/memory guard: cap or warn on very large PDFs before rasterization-heavy tools
(unlock/compress/convert).
## Dependencies
- **New runtime dependency** (only for encrypt): `@cantoo/pdf-lib` **or** `qpdf-wasm` — decision D5.
All other tools run on the existing stack (pdf-lib, pdfjs-dist, fflate).
---
## Implementation phases
Each phase ends with `pnpm check`, `pnpm lint`, `pnpm build` and a manual browser pass
(prettier formatting on changed files).
### Phase 0 — Groundwork
1. Create `tools/+layout.svelte` shell and `src/lib/tools.ts` registry.
2. Move the existing ZIP workflow from `+page.svelte` to `tools/bea/+page.svelte` unchanged
(keep the settings popover, thumbnail width localStorage, bulk export).
3. Build the new start page: hero + beA tile + (initially) tool tiles that link to stub routes
with a "coming soon" state? — no: see D8; default is to ship phases together and leave
tiles hidden until their tool exists.
4. Update `agents.md` repository structure and flow sections.
### Phase 1 — No-new-dependency tools
1. `PdfDropzone`, `download.ts`, `PdfPageGrid`/thumbnail helper.
2. `tools/merge` (reusable `mergePdfFiles` helper).
3. `tools/separate` (page grid, selection, export).
4. `tools/watermark`.
### Phase 2 — Signature
1. `tools/sign`: canvas signature capture (draw/type/upload), placement preview, export.
### Phase 3 — Password tools
1. Add the chosen encryption dependency; `tools/encrypt`.
2. `tools/decrypt` (pdfjs password + rasterized rebuild) with explicit quality warning in UI.
### Phase 4 — Rasterization tools
1. `tools/compress` (DPI/quality presets, size preview).
2. `tools/convert` (PDF → PNG/JPEG, separate files or ZIP).
### Phase 5 — Polish
1. Start-page tile copy and icons review (German, Nominandum visual language).
2. Final `check`/`lint`/`build`, update `agents.md`, README feature list.
---
## Open decisions (please confirm before implementation)
Defaults are marked ✅.
### D1 — One page per tool vs. single-page app
- ✅ **A. Separate routes** (`/tools/<slug>`). Better deep-linking/bookmarking, per-tool state is
isolated, back-navigation is natural, and SvelteKit gives us the structure for free.
- B. Single start page where each tile opens an in-place panel/modal. Keeps everything on `/` but
concentrates a lot of state in one component and hurts URL shareability.
### D2 — Where does the ZIP workflow live?
- ✅ **A. `/tools/bea`, start page shows it as the large primary tile.** Drag-a-ZIP-anywhere on the
start page still routes into the workflow, preserving today's fastest path.
- B. ZIP workflow stays on `/` and tools branch off it. Rejected: makes `/` a mixed
dropzone+dashboard and complicates the page.
### D3 — What does "sign" mean?
- ✅ **A. Visual signature** (drawn/typed/uploaded image placed on the page). No crypto, no
certificates, no PAdES.
- B. Cryptographic/PAdES signing. Large scope (certificate handling, new heavy dependencies,
legal implications) — recommend a separate project if needed.
### D4 — What does "convert" mean (v1)?
- ✅ **A. PDF → PNG/JPEG** (per-page images, optionally bundled into a ZIP with fflate).
- B. PDF → PDF/A. There is no solid browser-only PDF/A converter; would need a new heavy
dependency or a server.
- C. Images/Office → PDF. Image→PDF already exists inside the beA flow; Office formats are
out of scope.
### D5 — Encryption dependency
- ✅ **A. `@cantoo/pdf-lib`** — API-compatible fork of pdf-lib with encryption; smallest migration
risk (drop-in swap of the pdf-lib import in the encrypt tool only).
- B. `qpdf-wasm` — battle-tested engine, but WASM bundle size and wiring cost are significant.
- C. Server-side encryption — violates the no-upload constraint; rejected.
### D6 — Unlock strategy
- ✅ **A. pdfjs (with password) → rasterize pages → rebuild with pdf-lib.** Works fully in the
browser; document the vector→raster quality loss in the UI before the user commits.
- B. Await/patch pdf-lib fork decryption support — currently not available for browser use.
- C. Server-side qpdf — violates the no-upload constraint; rejected.
### D7 — Compress strategy
- ✅ **Rasterize-and-re-encode** (pdfjs render + JPEG at chosen quality). Predictable, browser-only;
shows the same quality caveat as unlock.
- B. "Structure-only" optimization (remove metadata, deduplicate objects). pdf-lib offers little
here; savings are usually marginal. Could be added as a light pre-step within the same tool.
### D8 — Release strategy for tiles
- ✅ **A. Ship it whole / hide unfinished tiles.** Only tiles whose route works are rendered
(driven by the tool registry), so phases can be merged without dead links.
- B. Show all tiles immediately with "in Kürze verfügbar" badges — acceptable only if the
redesign itself must go live before all tools are done.
---
## Risks and mitigations
- **Rasterization quality/memory** (unlock, compress, convert): large PDFs can exhaust browser
memory when rendering many pages at high DPI. Mitigate with sequential page processing, DPI
caps, and an upfront file-size warning.
- **`@cantoo/pdf-lib` maturity**: it is a fork; keep the pdf-lib import isolated to the encrypt
tool so a swap back (or to qpdf-wasm) stays local.
- **Scope creep on "sign"/"convert"**: the open decisions above pin v1 scope deliberately; crypto
signing and PDF/A conversion are explicitly out.
- **Regression risk for the existing workflow**: Phase 0 is a pure move of the current page;
verify sub-document editing, XJustiz naming, and bulk export still work after relocation.