# 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` 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/`). 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.