272 lines
15 KiB
Markdown
272 lines
15 KiB
Markdown
# Project Agent Guide
|
|
|
|
## Project overview
|
|
|
|
This repository contains **juri-merger**, a German-language SvelteKit application branded as
|
|
**beA-Edit**. Users add one or more ZIP archives in the browser, inspect and reorder supported
|
|
attachments, optionally group attachments into named sub-documents, and export merged PDFs.
|
|
Nested ZIP archives and XJustiz metadata are handled client-side; there is no upload, server-side
|
|
archive processing, database, analytics, or persistence of case data.
|
|
|
|
The application also contains German imprint and privacy-policy routes.
|
|
|
|
## Technology stack
|
|
|
|
- SvelteKit 2 and Svelte 5, with runes mode forced for project source files
|
|
- TypeScript in strict mode
|
|
- Vite 8
|
|
- Tailwind CSS 4 through `@tailwindcss/vite`
|
|
- shadcn-svelte 1 with the `nova` style and CSS-variable theme tokens
|
|
- bits-ui, tailwind-variants, clsx, and tailwind-merge
|
|
- Lucide Svelte icons plus project-specific SVG icon components
|
|
- fflate for ZIP extraction and image-export ZIP creation
|
|
- pdf-lib for PDF assembly, `@cantoo/pdf-lib` for encryption, and pdfjs-dist for rendering
|
|
- `@sveltejs/adapter-node`
|
|
- pnpm with a committed lockfile
|
|
- Prettier with Svelte and Tailwind plugins
|
|
|
|
Use **pnpm**, not npm or yarn. The production container uses Node 24 and pnpm 11.15.1.
|
|
|
|
## Commands
|
|
|
|
```sh
|
|
pnpm install # Install dependencies
|
|
pnpm dev # Start the development server
|
|
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 lint # Check repository formatting with Prettier
|
|
pnpm format # Format the repository with Prettier
|
|
```
|
|
|
|
For code changes, run at least:
|
|
|
|
```sh
|
|
pnpm check
|
|
pnpm test:e2e
|
|
pnpm lint
|
|
pnpm build
|
|
```
|
|
|
|
For documentation-only changes, a targeted Prettier check is sufficient. If pre-existing warnings
|
|
or formatting failures remain outside changed files, report them instead of making unrelated edits.
|
|
|
|
The Docker image can be built from the repository root. It installs with `--frozen-lockfile`, builds
|
|
and prunes dependencies, then runs `node build/index.js` as a non-root user on port 3000.
|
|
|
|
## Repository structure
|
|
|
|
```text
|
|
src/
|
|
├── app.html # German HTML shell
|
|
├── app.d.ts # SvelteKit application declarations
|
|
├── lib/
|
|
│ ├── actions/ripple.ts # Button ripple Svelte action
|
|
│ ├── assets/ # Favicon and local Nunito fonts
|
|
│ ├── components/
|
|
│ │ ├── AttachmentPreview.svelte # Browser-rendered PDF/image thumbnails
|
|
│ │ ├── BeaArchiveProcessing.svelte
|
|
│ │ ├── BeaWorkspaceControls.svelte # beA workspace toolbar, settings popover, dialogs
|
|
│ │ ├── ProcessedZipArchiveEditor.svelte
|
|
│ │ ├── SubDocumentEditor.svelte
|
|
│ │ ├── 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
|
|
│ │ ├── RasterizationWarning.svelte
|
|
│ │ ├── ZipDropzone.svelte # beA-specific wrapper around FileDropzone
|
|
│ │ ├── icons/ # Project-specific SVG components
|
|
│ │ └── ui/ # Local shadcn-svelte component source
|
|
│ ├── services/
|
|
│ │ ├── xml-reading.service.ts # Namespace-tolerant XJustiz parsing
|
|
│ │ └── zip-inflating.service.ts # Async fflate wrapper
|
|
│ ├── pdf-processing.ts # Reusable PDF merge helper
|
|
│ ├── pdf-thumbnails.ts # Managed pdfjs thumbnail loading and cleanup
|
|
│ ├── utils.ts # cn() and shared component utility types
|
|
│ └── zip-processing.ts # Archive model, transforms, and PDF export
|
|
└── routes/
|
|
├── +layout.svelte # Shared logo header and branded footer
|
|
├── +page.svelte # Toolbox hub and global beA ZIP drop target
|
|
├── layout.css # Tailwind, shadcn, theme, fonts, global CSS
|
|
├── tools/
|
|
│ ├── +layout.svelte # Shared back-navigation shell
|
|
│ ├── bea/+page.svelte # Existing ZIP archive workflow
|
|
│ └── {merge,separate,watermark,encrypt,decrypt,compress,convert}/
|
|
├── datenschutz/+page.svelte # Privacy policy
|
|
└── impressum/+page.svelte # Imprint
|
|
static/ # Public logo, footer art, and robots.txt
|
|
components.json # shadcn-svelte registry configuration
|
|
Dockerfile # Node adapter production image
|
|
plan/ # Design and implementation notes
|
|
```
|
|
|
|
Generated directories such as `.svelte-kit/`, `build/`, and `node_modules/` must not be edited or
|
|
committed.
|
|
|
|
## Application flow and state
|
|
|
|
- `/` is the toolbox hub. A ZIP dropped anywhere on that route is queued in memory and forwarded to
|
|
`/tools/bea`; the file is not uploaded or persisted.
|
|
- `tools/bea/+page.svelte` owns selected files, processed archives, pending work, and the
|
|
thumbnail-width preference. Thumbnail width is the only persisted UI setting and uses localStorage.
|
|
- The loaded workspace renders `BeaWorkspaceControls.svelte`, a top-aligned toolbar with the
|
|
workspace status and two archive lifecycle actions:
|
|
- **“Neue ZIP bearbeiten”** (replace) opens a ZIP picker and, after the user confirms the
|
|
replacement dialog, calls `replaceWithZipFiles(files)` which resets the workspace and enqueues the
|
|
new files against the incremented generation. Files picked for replacement stay in temporary
|
|
component state until confirmation; cancelling the picker or the dialog must never mutate the
|
|
current workspace.
|
|
- **“Weitere ZIP hinzufügen”** (append) lives in the settings popover, uses the plain append path
|
|
(`handleFilesSelected`), and never clears current archives. Keep the two labels distinct:
|
|
replace is “Neue ZIP bearbeiten”, append is “Weitere ZIP hinzufügen”.
|
|
- `BeaWorkspaceControls.svelte` owns only ephemeral UI state (popover/dialog open state and pending
|
|
replacement files). The route remains the source of truth for files, jobs, archives, and the
|
|
persisted thumbnail width.
|
|
- `resetWorkspace()` is the single teardown helper: it increments `archiveGeneration` and clears
|
|
selected files, processed archives, and jobs. Never clear those arrays without going through it.
|
|
- Accepted ZIP types and the file filter for the dropzone and both compact pickers are centralized in
|
|
`src/lib/zip-selection.ts`; `ZipFilePicker.svelte` resets its input after every selection so the
|
|
same file can be chosen again.
|
|
- ZIP files are processed concurrently. `archiveGeneration` prevents results from an old batch from
|
|
reappearing after the user deletes all archives **or** confirms a replacement.
|
|
- `ProcessedZipArchiveEditor.svelte` edits one archive and emits immutable replacements through
|
|
`onArchiveChange`; the route-level archive array remains the source of truth.
|
|
- Attachments may remain loose or belong to exactly one sub-document. Drag-and-drop supports
|
|
reordering and moving attachments between lists, while sub-document headers can be reordered.
|
|
- Selection mode creates sub-documents from loose attachments. Exiting with **Fertig** or Escape
|
|
clears the pending selection.
|
|
- Per-archive export supports one flattened PDF or separate PDFs for sub-documents. Each
|
|
sub-document also has its own direct download action that exports only that sub-document's
|
|
attachments. Bulk export from the page always creates one flattened PDF per archive.
|
|
|
|
When changing archive transforms, preserve these invariants:
|
|
|
|
- An attachment move must not duplicate or discard data.
|
|
- Sub-document IDs remain stable across edits and reordering.
|
|
- Single-PDF order is all sub-documents in order, each attachment in order, followed by loose
|
|
attachments in order.
|
|
- Dissolving a sub-document appends its attachments to the loose list.
|
|
- State helpers in `zip-processing.ts` should return new archive/list objects rather than mutating
|
|
component-owned state.
|
|
|
|
## ZIP, XML, and PDF processing
|
|
|
|
All case-file processing is intentionally local to the browser.
|
|
|
|
- `extractZipArchives` recursively inflates nested ZIP files.
|
|
- Ignore macOS metadata (`__MACOSX`, `.DS_Store`, and `._*`) and unsupported entries.
|
|
- `xjustiz_nachricht.xml` is metadata, not an export attachment. Its sender, receiver, and declared
|
|
document names determine the generated PDF name and preferred attachment order.
|
|
- XJustiz element matching must remain namespace tolerant. Malformed or absent metadata falls back
|
|
safely instead of preventing extraction.
|
|
- Supported attachments are PDFs and the image extensions listed in `zip-processing.ts`. Update the
|
|
extension classification and downstream preview/export behavior together when adding formats.
|
|
- PDF attachments are copied with pdf-lib. JPEG and PNG files can be embedded directly; other image
|
|
formats are decoded through browser image/canvas APIs and converted to PNG first.
|
|
- `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.
|
|
- Empty archives/export groups should continue to fail explicitly rather than creating invalid PDFs.
|
|
|
|
Browser-only APIs used by this flow include `File`, `DOMParser`, `Image`, canvas, `Blob`, `URL`,
|
|
localStorage, and `crypto.randomUUID()`. Do not invoke browser-only work during SSR or at module
|
|
initialization. Keep it in event handlers, `onMount`, effects guarded by hydration, or functions only
|
|
called in the browser.
|
|
|
|
## Svelte conventions
|
|
|
|
- Use Svelte 5 runes and current event syntax: `$props()`, `$state`, `$derived`, `$effect`, snippets,
|
|
`{@render ...}`, and handlers such as `onclick` rather than legacy APIs.
|
|
- Keep component scripts typed with `<script lang="ts">`.
|
|
- Define a local `Props` type for non-trivial component props.
|
|
- Use callback props for child-to-parent updates; do not mutate prop objects in place.
|
|
- Use `$lib/...` aliases for application imports instead of long relative paths.
|
|
- Name reusable components in PascalCase and route files according to SvelteKit conventions.
|
|
- Preserve semantic HTML, accessible names, visible focus states, and keyboard alternatives. Do not
|
|
make drag-and-drop the only way to perform a critical action.
|
|
- Clean up global listeners, object URLs, PDF loading tasks, and other browser resources.
|
|
|
|
The Vite configuration forces runes mode for project files, so do not introduce legacy Svelte
|
|
component patterns.
|
|
|
|
## shadcn-svelte and component conventions
|
|
|
|
`components.json` configures the official shadcn-svelte registry, the `nova` style, Lucide icons, and
|
|
these aliases:
|
|
|
|
- UI components: `$lib/components/ui`
|
|
- General components: `$lib/components`
|
|
- Utilities: `$lib/utils`
|
|
|
|
Use existing local UI components before creating replacements. Follow the exports actually present
|
|
on disk: the card is composed through `import * as Card from '$lib/components/ui/card'`, while the
|
|
current button is a default import from `$lib/components/ui/button.svelte`.
|
|
|
|
When adding a registry component, use the project runner, inspect every generated file, and preserve
|
|
local customizations:
|
|
|
|
```sh
|
|
pnpm dlx shadcn-svelte@latest add <component>
|
|
```
|
|
|
|
Do not overwrite or bulk-update customized components without explicit approval. Use `cn()` for
|
|
conditional class merging and existing variants before adding one-off component styling.
|
|
|
|
## Styling conventions
|
|
|
|
Use Tailwind utilities in Svelte markup and semantic theme tokens from `src/routes/layout.css`.
|
|
|
|
- Prefer `bg-background`, `text-foreground`, `bg-primary`, `text-muted-foreground`, `border-border`,
|
|
and other configured semantic utilities over raw brand colors.
|
|
- Preserve the established Nominandum visual language: pale blue-gray background, white cards, dark
|
|
blue primary text, red/coral secondary actions, rounded containers, and Nunito typography.
|
|
- Use `flex`/`grid` with `gap-*`, responsive variants, and `size-*` when width and height are equal.
|
|
- Use `cn()` rather than constructing partial Tailwind class names. Keep complete class names visible
|
|
to Tailwind's scanner.
|
|
- Prefer component variants and theme variables over overriding UI-component colors in call sites.
|
|
- Add global CSS only for imports, tokens, font faces, animations, or behavior that utilities cannot
|
|
reasonably express.
|
|
- Tailwind 4 is configured through Vite and `layout.css`; there is no `tailwind.config` file.
|
|
- Maintain contrast, responsive layouts, drag-state feedback, and visible focus treatment.
|
|
|
|
Theme-level changes belong in `src/routes/layout.css`. Do not reintroduce the removed Skeleton theme
|
|
or refer to the old `nom-theme.css` architecture.
|
|
|
|
## Formatting
|
|
|
|
Follow the checked-in Prettier configuration:
|
|
|
|
- Tabs for indentation
|
|
- Single quotes in scripts and styles where supported
|
|
- No trailing commas
|
|
- 100-character print width
|
|
- Svelte-aware and Tailwind-aware formatting
|
|
|
|
Use `pnpm exec prettier --write <changed-files>` for targeted formatting. Avoid formatting unrelated
|
|
files solely to clean up existing differences.
|
|
|
|
## Product and content constraints
|
|
|
|
- User-facing copy is German unless a task explicitly requests another language.
|
|
- Preserve legal text in `datenschutz/+page.svelte` and `impressum/+page.svelte` unless explicitly
|
|
asked to change it.
|
|
- Keep the Nominandum logo, footer artwork, and theme consistent across routes.
|
|
- ZIP selection accepts multiple files matching `.zip`, `application/zip`, or
|
|
`application/x-zip-compressed`.
|
|
- Resetting the file input after selection is intentional so the same archive can be selected again.
|
|
- Do not add uploads, network requests, server processing, analytics, or case-data persistence without
|
|
an explicit requirement and privacy review.
|
|
|
|
## Change discipline
|
|
|
|
- Keep changes scoped and preserve unrelated work, including untracked files.
|
|
- Inspect existing components, archive helpers, and theme tokens before adding abstractions.
|
|
- Keep browser processing behavior and archive-order invariants covered when editing shared helpers.
|
|
- Do not hand-edit generated output or lockfile contents. Update the lockfile only through pnpm when
|
|
dependency changes are required.
|
|
- Avoid dependencies when the task can be completed with Svelte, browser APIs, Tailwind, or existing
|
|
packages.
|
|
- After implementation, review changed files, run relevant checks, and mention remaining warnings or
|
|
limitations.
|