Files
bea-edit/agents.md
T
2026-07-22 21:28:50 +02:00

10 KiB
Raw Blame History

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 in src/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

  1. User adds ZIP files via drag/drop or file picker (ZipDropzone).
  2. +page.svelte starts async extraction for each file (tracked via archiveGeneration so stale in-flight extractions are discarded on bulk delete).
  3. Each ZIP is processed into one or more ProcessedZipArchive entries.
  4. ProcessedZipArchiveEditor shows thumbnails, allows reordering, and can download the merged PDF.
  5. AttachmentPreview renders image previews directly and PDF previews via pdfjs-dist.
  6. 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; imports tw-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 .dark override.
  • 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 around fflate unzip.
  • 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 as Card.*).
  • 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 create
  • src/lib/utils.ts — shared cn() helper (clsx + tailwind-merge) and WithElementRef/WithoutChild typing helpers used by shadcn components.

Button system

src/lib/components/ui/button.svelte is a shadcn-sveltestyle primitive adapted to the nom theme:

  • Built with tailwind-variants (buttonVariants) and the shared cn() helper.
  • Variants: primary, secondary (gradient secondary → --secondary-end), bordered, tertiary, success.
  • Sizes: standard (h-8, rounded-16px), large (h-12, rounded-24px).
  • Renders either a <button> or an <a> (when href is set); both apply the use:ripple action.
  • Accepts an optional icon prop (a Svelte component) rendered after children.
  • ButtonProps merges HTMLButtonAttributes + HTMLAnchorAttributes with WithElementRef and variant/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.xml is 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 ProcessedZipArchive now has attachments ("loose") plus an ordered subDocuments: SubDocument[] list. Each SubDocument is a named, ordered subset of ZipAttachments 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); mergeProcessedZipArchive flattens the archive via flattenArchiveForMerge (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: single flips the whole archive into one PDF (sub-docs flattened first); separate produces 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 localStorage under thumbnailWidth (key constant THUMBNAIL_WIDTH_STORAGE_KEY) and clamped to 100..400; hydration is gated by a thumbnailWidthHydrated flag so the initial render does not fight onMount.
  • 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 archiveGeneration so 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. Esc exits 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 new SubDocument block (named "Teildokument N"), clears the selection, and keeps selection mode on.
    • Sub-documents render below the loose list as a vertical stack of SubDocumentEditor blocks. 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 a AttachmentLocation of { kind: 'loose', index } or { kind: 'subDocument', id, index }) for attachment moves, and application/x-juri-merger-block for 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.
  • SubDocumentEditor.svelte is the new sub-document block component. It emits onSubDocumentChange, onRemove, onAttachmentMove, and block-level drag callbacks up to the parent; the parent stays the source of truth via onArchiveChange.

Dependencies worth noting

  • fflate for ZIP extraction
  • pdf-lib for PDF generation/merging
  • pdfjs-dist for PDF thumbnail previews
  • bits-ui, shadcn-svelte (CLI/runtime), clsx, tailwind-merge, class-variance-authority, tailwind-variants, tw-animate-css for the shadcn-svelte UI layer
  • @lucide/svelte and a set of local SVG icon components for iconography
  • @fontsource-variable/geist font; custom Nunito variable font bundled under src/lib/assets/
  • @tailwindcss/vite, @tailwindcss/forms, @tailwindcss/typography for styling

shadcn-svelte configuration

  • components.json: style nova, iconLibrary lucide, baseColor neutral, Tailwind CSS path src/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.svelte is the canonical adapted example (custom variants + ripple + icon prop).

Current working tree observations

  • Branch is features/shadcn.
  • agents.md is being updated to match the post-shadcn-integration state.
  • plan/shadcn-svelte-plan.md tracks the in-progress migration (only button + card generated so far; broader UI replacement is still optional/incremental).
  • Untracked: plan/, and several icons under src/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).