10 KiB
10 KiB
Project notes for agents
Project snapshot
- Repo name:
juri-merger - App name/branding:
beA-Edit, published by Nominandum GmbH. - Tech stack: SvelteKit + Svelte 5 (runes) + TypeScript + Vite + Tailwind v4 (CSS-first).
- UI system: shadcn-svelte (style
nova, lucide icons) layered on a custom "nom" theme via CSS variables insrc/routes/layout.css. - Core purpose: upload one or more ZIP files, extract relevant PDFs/images, preview them, optionally reorder attachments, and download a merged PDF.
- Active branch:
features/shadcn(shadcn-svelte integration in progress).
Main user flow
- User adds ZIP files via drag/drop or file picker (
ZipDropzone). +page.sveltestarts async extraction for each file (tracked viaarchiveGenerationso stale in-flight extractions are discarded on bulk delete).- Each ZIP is processed into one or more
ProcessedZipArchiveentries. ProcessedZipArchiveEditorshows thumbnails, allows reordering, and can download the merged PDF.AttachmentPreviewrenders image previews directly and PDF previews viapdfjs-dist.- A fixed bottom-right settings drawer (
<details>) exposes export all and delete all archive actions plus the thumbnail-width slider.
Important source files
src/routes/+page.svelte— top-level page state, file selection, localStorage-backed thumbnail width, async ZIP processing, bulk export/delete, settings drawer.src/routes/+layout.svelte— app shell: header with logo and footer with Nominandum GmbH legal/imprint block.src/routes/layout.css— Tailwind v4 entrypoint; importstw-animate-css,shadcn-svelte/tailwind.css, Geist + Nunito fonts; defines the semantic token layer (--background,--primary,--secondary,--accent,--destructive,--radius, etc.) mapping shadcn tokens onto the nom palette, including a.darkoverride.src/lib/zip-processing.ts— archive extraction, recursive nested ZIP handling, metadata ordering, PDF merge/export, image conversion.src/lib/services/zip-inflating.service.ts— thin wrapper aroundfflateunzip.src/lib/services/xml-reading.service.ts— parses XJustiz XML metadata (sender,receiver,documentNames).src/lib/components/ZipDropzone.svelte— file picker + drag/drop ZIP intake.src/lib/components/ProcessedZipArchiveEditor.svelte— archive editor (name input, drag reordering, per-archive PDF download, selection mode + sub-document management, export-mode switch).src/lib/components/SubDocumentEditor.svelte— sub-document block editor (editable name, count, dissolve action, draggable header for block reorder, internal horizontal reorderable attachment list).src/lib/components/AttachmentPreview.svelte— preview generation for images/PDFs.src/lib/components/ui/button.svelte— shadcn-backed button atom (see "Button system" below).src/lib/components/ui/card/— shadcn card composition (Root/Content/Header/Title/Description/Footer/Action, re-exported asCard.*).src/lib/actions/ripple.ts— Svelte action that renders a click ripple animation on an element.src/lib/components/icons/— hand-rolled SVG icon components (IconSettings,IconFile,IconMenu,IconNeedle,IconOffice,IconPerson,IconTag,IconTrash,IconChain,IconUnknown). If an icon is missing, report it, and we see if we can createsrc/lib/utils.ts— sharedcn()helper (clsx + tailwind-merge) andWithElementRef/WithoutChildtyping helpers used by shadcn components.
Button system
src/lib/components/ui/button.svelte is a shadcn-svelte–style primitive adapted to the nom theme:
- Built with
tailwind-variants(buttonVariants) and the sharedcn()helper. - Variants:
primary,secondary(gradientsecondary → --secondary-end),bordered,tertiary,success. - Sizes:
standard(h-8, rounded-16px),large(h-12, rounded-24px). - Renders either a
<button>or an<a>(whenhrefis set); both apply theuse:rippleaction. - Accepts an optional
iconprop (a Svelte component) rendered afterchildren. ButtonPropsmergesHTMLButtonAttributes+HTMLAnchorAttributeswithWithElementRefandvariant/size/icon.- Used across the editor, settings drawer, and the throwaway test buttons on the home
<section>(still present on the branch).
ZIP/PDF processing details
- ZIP entries are filtered to ignore macOS metadata (
__MACOSX,.DS_Store,._*). - Relevant attachments are PDFs and common image formats.
- Nested ZIP archives are processed recursively.
xjustiz_nachricht.xmlis used to derive archive naming and ordering.- Ordering prefers document names from metadata before falling back to original attachment order.
- PDF merging uses
pdf-lib; images are embedded as PNG/JPG, with browser-side canvas conversion for other image types. - Sub-documents: a
ProcessedZipArchivenow hasattachments("loose") plus an orderedsubDocuments: SubDocument[]list. EachSubDocumentis a named, ordered subset ofZipAttachments that live alongside the loose ones. Sub-documents are created on demand by the user (selection mode in the editor) — when none exist, the archive behaves exactly as before. - The merge loop is shared via
mergeAttachments(attachments);mergeProcessedZipArchiveflattens the archive viaflattenArchiveForMerge(sub-documents first, then loose) so backwards compatibility holds. - Pure helpers in
zip-processing.ts:createSubDocument,moveSubDocument,moveAttachmentInPlace,moveAttachmentIntoSubDocument,removeAttachmentFromSubDocument,dissolveSubDocument,flattenArchiveForMerge,buildArchiveExport(archive, 'single' | 'separate'). Cross-list moves preserve the total attachment count invariant. - Export modes:
singleflips the whole archive into one PDF (sub-docs flattened first);separateproduces one PDF per non-empty sub-document ({archiveName} - {subName}.pdf) plus one for any residual loose attachments.
UI/state details
- Thumbnail width is stored in
localStorageunderthumbnailWidth(key constantTHUMBNAIL_WIDTH_STORAGE_KEY) and clamped to100..400; hydration is gated by athumbnailWidthHydratedflag so the initial render does not fightonMount. - Processing progress is shown as a count of pending ZIP files (
pendingZipCount). - The editor supports manual attachment reordering via drag-and-drop.
- The settings drawer contains: "Alle ZIP-Archive als PDF exportieren", "Alle ZIP-Archive löschen", and the thumbnail-width range input.
- Bulk delete bumps
archiveGenerationso otherwise-orphaned async extractions no-op instead of re-populating state. - Sub-document editing (
ProcessedZipArchiveEditor.svelte):- An "Auswählen" / "Fertig" toggle in the header puts the editor into selection mode. Only then are checkboxes rendered on each loose thumbnail (wrapped in a
<label>for keyboard operability — Tab + Space); in normal mode the editor looks identical to today.Escexits selection mode and clears the selection. Drag-to-reorder is suspended while in selection mode. - An inline action bar with "Teildokument erstellen" appears whenever
selectionMode && selectedPaths.size > 0. Creating a sub-document removes the selected attachments from the loose list, appends a newSubDocumentblock (named "Teildokument N"), clears the selection, and keeps selection mode on. - Sub-documents render below the loose list as a vertical stack of
SubDocumentEditorblocks. Each block has an editable name, a count, an "Auflösen" action (returns its attachments to the end of the loose list), a draggable header (::) to reorder the block, and its own horizontal reorderable attachment list. - Drag-and-drop is generalised: HTML5 DnD payloads use MIME type
application/x-juri-merger-attachment(carrying aAttachmentLocationof{ kind: 'loose', index }or{ kind: 'subDocument', id, index }) for attachment moves, andapplication/x-juri-merger-blockfor reordering whole sub-document blocks. Supported moves: loose↔loose, loose→sub, sub→loose, sub attachment↔sub attachment (same or different sub), and sub-document block↔sub-document block. - Export control: with no sub-documents, the existing single "PDF herunterladen" button behaves unchanged. With sub-documents present, a segmented control ("Eine PDF" / "Getrennt") lets the user choose the export mode; "Eine PDF" downloads one flattened PDF, "Getrennt" downloads one PDF per sub-document sequentially.
- An "Auswählen" / "Fertig" toggle in the header puts the editor into selection mode. Only then are checkboxes rendered on each loose thumbnail (wrapped in a
SubDocumentEditor.svelteis the new sub-document block component. It emitsonSubDocumentChange,onRemove,onAttachmentMove, and block-level drag callbacks up to the parent; the parent stays the source of truth viaonArchiveChange.
Dependencies worth noting
fflatefor ZIP extractionpdf-libfor PDF generation/mergingpdfjs-distfor PDF thumbnail previewsbits-ui,shadcn-svelte(CLI/runtime),clsx,tailwind-merge,class-variance-authority,tailwind-variants,tw-animate-cssfor the shadcn-svelte UI layer@lucide/svelteand a set of local SVG icon components for iconography@fontsource-variable/geistfont; customNunitovariable font bundled undersrc/lib/assets/@tailwindcss/vite,@tailwindcss/forms,@tailwindcss/typographyfor styling
shadcn-svelte configuration
components.json: stylenova,iconLibrarylucide,baseColorneutral, Tailwind CSS pathsrc/routes/layout.css.- Aliases:
components → $lib/components,ui → $lib/components/ui,utils → $lib/utils,hooks → $lib/hooks,lib → $lib. src/lib/hooks/exists but is currently empty.- Generated components live under
src/lib/components/ui/;button.svelteis the canonical adapted example (custom variants + ripple + icon prop).
Current working tree observations
- Branch is
features/shadcn. agents.mdis being updated to match the post-shadcn-integration state.plan/shadcn-svelte-plan.mdtracks the in-progress migration (onlybutton+cardgenerated so far; broader UI replacement is still optional/incremental).- Untracked:
plan/, and several icons undersrc/lib/components/icons/(IconChain,IconFile,IconMenu,IconNeedle,IconOffice,IconPerson,IconTag,IconTrash,IconUnknown). - Modified (uncommitted):
agents.md,src/lib/components/icons/IconSettings.svelte,src/lib/components/ui/button.svelte,src/routes/+page.svelte,.idea/workspace.xml. src/lib/services/is now committed (was previously untracked).