refactoring, numbering plan

This commit is contained in:
rehlert committed 2026-10-28 16:09:53 +01:00
1 parent b5279434b2
commit 3b04039aa9
47 files changed
+3485 -953

No files matched your search

+67 -14
View File
@@ -38,7 +38,7 @@ pnpm build # Create the adapter-node production build
pnpm preview # Preview the production build
pnpm check # Run Svelte and TypeScript diagnostics
pnpm check:watch # Run diagnostics in watch mode
pnpm test:e2e # Run the Playwright toolbox tests in Chromium
pnpm test:e2e # Run the toolbox specs (tests/e2e) and the unit specs (tests/unit)
pnpm test:pwa # PWA offline/update tests against the production build (requires pnpm build)
pnpm lint # Check repository formatting with Prettier
pnpm format # Format the repository with Prettier
@@ -74,9 +74,13 @@ src/
│ │ ├── BatchFileList.svelte # Shared batch rows: status chips, downloads, retry
│ │ ├── BeaArchiveProcessing.svelte
│ │ ├── BeaWorkspaceControls.svelte # beA workspace toolbar, settings popover, dialogs
│ │ ├── ExhibitFileList.svelte # Reorderable Anlagen list: labels, renames, results
│ │ ├── ProcessedZipArchiveEditor.svelte
│ │ ├── PaginationPreview.svelte # Rendered page preview incl. shrink hints and page nav
│ │ ├── PdfSplitDialog.svelte # Page-level PDF splitting into Teildokumente
│ │ ├── SinglePdfTool.svelte # Shell for one-PDF tools (header, dropzone, slots)
│ │ ├── SubDocumentEditor.svelte
│ │ ├── ToolError.svelte # Shared error banner (role="alert")
│ │ ├── FileDropzone.svelte # Shared PDF/image/ZIP file picker and drop target
│ │ ├── ZipFilePicker.svelte # Compact ZIP picker button with hidden resettable input
│ │ ├── PdfPageGrid.svelte # Accessible PDF thumbnail selection grid
@@ -89,13 +93,18 @@ src/
│ │ ├── xml-reading.service.ts # Namespace-tolerant XJustiz parsing
│ │ └── zip-inflating.service.ts # Async fflate wrapper
│ ├── batch.ts # Sequential batch engine for multi-file tools
│ ├── download.ts # Blob/download helpers (pdfBytesToBlob, downloadBlobFile)
│ ├── exhibit-naming.ts # Anlagen label, start number and file-name helpers
│ ├── latest-task.ts # "Only the latest async call wins" tokens
│ ├── pdf-compression.ts # Shared compression presets and rasterize pipeline
│ ├── pdf-errors.ts # Shared encrypted-PDF detection and German messages
│ ├── pdf-errors.ts # Shared PDF error classification and German messages
│ ├── pdf-metadata.ts # PDF metadata inspection and cleaning (pdf-lib only)
│ ├── pdf-overlay.ts # Shared overlay positions, preview CSS and color parsing
│ ├── pdf-pagination.ts # Sheet-number labels and the pagination pipeline
│ ├── pdf-preview-text.ts # Canvas text measurement for HTML previews
│ ├── pdf-processing.ts # Reusable PDF merge helper
│ ├── pdf-text-overlay.ts # Shared framed/rotated text overlay drawing
│ ├── pdf-session.svelte.ts # Shared single-PDF lifecycle (file, bytes, handle, errors)
│ ├── pdf-text-overlay.ts # Shared framed/rotated text and image overlay drawing
│ ├── pdf-thumbnails.ts # Managed pdfjs thumbnails, on-demand page previews, cleanup
│ ├── utils.ts # cn() and shared component utility types
│ └── zip-processing.ts # Archive model, transforms, and PDF export
@@ -107,13 +116,15 @@ src/
├── tools/
│ ├── +layout.svelte # Shared back-navigation shell
│ ├── bea/+page.svelte # Existing ZIP archive workflow
│ └── {pagination,merge,separate,stamp,watermark,encrypt,decrypt,compress,convert,rotate,metadata}/
│ └── {exhibits,pagination,merge,separate,stamp,watermark,encrypt,decrypt,compress,convert,rotate,metadata}/
├── datenschutz/+page.svelte # Privacy policy
└── impressum/+page.svelte # Imprint
static/ # Public logo, footer art, PWA icons, and robots.txt
components.json # shadcn-svelte registry configuration
Dockerfile # Node adapter production image
playwright.pwa.config.ts # Playwright config for `pnpm test:pwa`
tests/e2e/ # Playwright toolbox specs
tests/unit/ # Pure-function specs (run by the same Playwright config)
plan/ # Design and implementation notes
```
@@ -156,6 +167,13 @@ committed.
`buildPaginationLabel` — the single source for both the live preview and the export — and stamps
all pages through `drawTextOverlay`. The page number is always on every page; the optional free
text (Aktenzeichen) follows its own “every page” / “first page only” scope.
- `/tools/exhibits` is the Anlagen tool and the only batch tool with an order. One dropped PDF is
one Anlage: `src/lib/exhibit-naming.ts` owns the label (`buildExhibitLabel`, no padded numbers in
the stamp) and the optional file name (`buildExhibitFileName`, `{prefixSlug}_{paddedNumber}_…`,
padding from `getExhibitNumberWidth`). Numbers are frozen per row when the run starts
(`assignments` map), so reordering or changing the start number never rewrites a created Anlage —
the list keeps showing the frozen label while pending rows follow the live order. The stamp is
`drawTextOverlay` with `framed: true`, `compensatePageRotation: true`, first page by default.
- `/tools/metadata` is a single-PDF inspection tool. `src/lib/pdf-metadata.ts` reads document
properties with pdf-lib only (no rasterization), returns a discriminated `{ status: 'encrypted' }`
result for password-protected files, and detects embedded-file markers by scanning raw bytes.
@@ -242,11 +260,33 @@ All case-file processing is intentionally local to the browser.
semantics cannot drift apart.
- `src/lib/pdf-text-overlay.ts` owns text fitting, the rotated-box placement and the optional
`/Rotate` compensation. Stamp keeps `compensatePageRotation` off so its output does not change;
pagination turns it on so numbers stay at the visual position on rotated scans. Share it instead
of re-implementing overlay maths in a route.
pagination turns it on so numbers stay at the visual position on rotated scans. `drawImageOverlay`
is the image counterpart. Share both instead of re-implementing overlay maths in a route, and take
the margin/padding presets (`TEXT_OVERLAY_MARGIN`, `TEXT_OVERLAY_PADDING`,
`MIN_TEXT_OVERLAY_SIZE`) from the module rather than redefining them locally.
- `src/lib/pdf-errors.ts` classifies PDF failures (`classifyPdfError`, `TextOverlayError`,
`isEncryptedPdfError`) and owns the German copy (`pdfLoadErrorMessage`, `pdfProcessingErrorMessage`,
both with a tool-specific fallback). All tools use it; never match on pdf-lib/pdfjs error text in a
route, and never copy the password hint locally.
- `src/lib/pdf-session.svelte.ts` (`createPdfSession`) is the shared single-PDF lifecycle for rotate,
separate and pagination: it reads the bytes once, builds the tool handle, maps load errors and
invalidates stale loads. Keep new single-PDF tools on it instead of re-declaring
`file`/`bytes`/`handle`/`isLoading`/`loadingError` and a generation counter.
- `src/lib/latest-task.ts` (`createLatestToken`) is the guard for "only the newest async call may
publish its result" (file loads, preview renders). Do not add another hand-rolled counter.
- Markup is shared the same way: `ToolError.svelte` owns the red `role="alert"` banner (never
inline the classes in a route), `SinglePdfTool.svelte` owns the header + dropzone + toolbar +
footer layout for one-PDF tools (rotate, separate — keep new ones on it), and
`PaginationPreview.svelte` owns the page preview with its shrink hints. Tool-specific copy is
passed in as props; keep it out of the shared components.
- `src/lib/pending-files.ts` hands files between routes. Queue with an explicit target
(`queuePendingFiles(files, 'bea' | 'pagination')`) and consume with `takePendingFiles(target)` so a
tool cannot silently swallow a file queued for another one.
- `src/lib/pdf-thumbnails.ts` offers two loaders: `loadPdfDocument` (all thumbnails, for pickers and
page grids) and `loadPdfPageRenderer` (one page at a time, for long documents). Both revoke object
URLs and tear down the pdfjs loading task in `destroy()`.
page grids) and `loadPdfPageRenderer` (one page at a time, for long documents). Both share
`PdfPageImage` and one rendering helper, revoke object URLs and tear down the pdfjs loading task —
including when the initial `getDocument` promise rejects. Superseded renders throw
`PreviewRenderCancelledError`, which callers swallow instead of logging.
- `AttachmentPreview.svelte` dynamically loads pdfjs-dist in `onMount`, copies PDF bytes before
rendering, and revokes object URLs during cleanup. Preserve those memory and data-integrity
safeguards.
@@ -264,11 +304,18 @@ files. Follow the established compress retrofit when adding the pattern to anoth
`plan/done/batch-processing-plan.md` for the full design.
- `src/lib/batch.ts` owns the engine: `createBatchItem(file)`, `runBatch(items, process, update,
options?)` and `formatBytes`. `runBatch` processes strictly sequentially (one PDF in flight for
memory safety), never throws — per-item failures are caught and written to the item as a German
error message (a string, or a formatter that can inspect the thrown error) — and consults the
optional `shouldProcess` predicate so rows removed mid-run are skipped.
- `src/lib/components/BatchFileList.svelte` renders the rows: name, size, status chip
options?)`, `formatBytes`, `batchStatusLabel`/`batchStatusClass` and
`shouldWarnAboutBatchMemory`. `process` is `(file, item) => Promise<{ bytes, name }>`: the item is
passed so a tool can resolve per-row data frozen at run start (the Anlagen tool resolves its
exhibit number that way; `(file) => …` callbacks stay valid). `runBatch` processes strictly
sequentially (one PDF in flight for memory safety), never throws — per-item failures are caught
and written to the item as a German error message (a string, or a formatter that can inspect the
thrown error) — and consults the optional `shouldProcess` predicate so rows removed mid-run are
skipped.
- `src/lib/batch.ts` also owns the bulk export: `uniqueEntryName` (duplicate suffixes) and
`downloadBatchZip(items, zipBaseName)` → `{toolname}_{date}.zip`. Keep new batch tools on it
instead of building ZIPs inside a component.
- `src/lib/components/BatchFileList.svelte` renders the rows: name, size, status chip (`batchStatusClass`/`batchStatusLabel`)
(Wartet / Verarbeite… / Fertig ✓ / Fehler), per-row download, remove, and retry buttons with
accessible names including the file name, a progress line (“Datei 3 von 7 wird verarbeitet…”), a
total result-size summary, a memory warning (>20 files or any file >25 MB), and the bulk
@@ -280,8 +327,14 @@ options?)` and `formatBytes`. `runBatch` processes strictly sequentially (one PD
Files too large to rasterize (>100 MB) are marked as error rows immediately on selection instead
of blocking the batch. Keep thumbnails, previews, and page-specific selection to a single dropped
file; multi-file runs apply the tool to every page of every file.
- `src/lib/components/ExhibitFileList.svelte` is the other batch list, for tools where order and a
derived per-row label matter (currently `/tools/exhibits`): drag handle plus up/down buttons that
both call one `onMove(from, to)`, position badge, the resolved label, the renamed file name, and
the same status/download/retry/remove/ZIP surface as `BatchFileList`. Reordering and preview
selection are disabled while a run is active.
- Per-tool ZIP base names: compress uses `komprimiert`, watermark `wasserzeichen`, stamp `stempel`,
encrypt `geschuetzt`, decrypt `entsperrt`. Choose an analogous German name per tool and cover the
exhibits `anlagen`, encrypt `geschuetzt`, decrypt `entsperrt`. Choose an analogous German name
per tool and cover the
batch flow with Playwright tests (multi-file ZIP contents, failure continuation, retry, removal).
## Svelte conventions