adds features
Build and Push Docker Image / build (push) Successful in 1m28s

This commit is contained in:
2026-08-27 07:38:35 +02:00
parent 891026ccbb
commit adaae41e44
33 changed files with 2771 additions and 396 deletions
+181 -58
View File
@@ -2,99 +2,214 @@
## Project overview
This repository contains **juri-merger**, a German-language SvelteKit application whose current UI is branded as **beA-Edit**. The main route lets users select or drag ZIP files in the browser. It currently displays the selected filenames; no server-side upload or archive-processing flow is present in this version.
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 legal pages for the imprint and privacy policy.
The application also contains German imprint and privacy-policy routes.
## Technology stack
- SvelteKit 2
- Svelte 5 with runes mode forced for project source files
- TypeScript with strict mode
- 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`
- Skeleton 4 with a custom `nominandum` theme
- pnpm and `pnpm-lock.yaml`
- `@sveltejs/adapter-auto`
- Prettier with the Svelte and Tailwind plugins
- 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, when installing packages or running project scripts.
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 a production build
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 lint # Check formatting with Prettier
pnpm test:e2e # Run the Playwright toolbox tests in Chromium
pnpm lint # Check repository formatting with Prettier
pnpm format # Format the repository with Prettier
```
There is currently no automated unit or end-to-end test suite. For code changes, run at least:
For code changes, run at least:
```sh
pnpm check
pnpm test:e2e
pnpm lint
pnpm build
```
If pre-existing warnings remain outside the files being changed, report them rather than making unrelated edits.
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 document shell and Skeleton theme selection
├── app.d.ts # SvelteKit application type declarations
├── app.html # German HTML shell
├── app.d.ts # SvelteKit application declarations
├── lib/
│ ├── assets/ # Bundled favicon and Nunito font files
│ ├── components/ # Reusable Svelte components
│ ├── Button.svelte
│ │ ├── Card.svelte
│ │ ── ZipDropzone.svelte
├── index.ts # Public `$lib` exports
└── nom-theme.css # Custom Skeleton theme tokens
│ ├── actions/ripple.ts # Button ripple Svelte action
│ ├── assets/ # Favicon and local Nunito fonts
│ ├── components/
│ │ ├── AttachmentPreview.svelte # Browser-rendered PDF/image thumbnails
│ │ ── ProcessedZipArchiveEditor.svelte
│ ├── SubDocumentEditor.svelte
│ ├── PdfDropzone.svelte # Shared PDF/image/ZIP file picker and drop target
│ │ ├── PdfPageGrid.svelte # Accessible PDF thumbnail selection grid
│ │ ├── ZipDropzone.svelte # beA-specific wrapper around PdfDropzone
│ │ ├── 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 # Main ZIP-selection page
├── layout.css # Tailwind/Skeleton imports and global font setup
├── datenschutz/+page.svelte # Privacy policy
└── impressum/+page.svelte # Imprint
static/ # Files served unchanged from the site root
├── +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.
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.
- ZIP files are processed concurrently. `archiveGeneration` prevents results from an old batch from
reappearing after the user deletes all archives.
- `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. Prefer `$props()`, `$state`, `$derived`, snippets, `{@render ...}`, and handlers such as `onclick` over legacy APIs.
- 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 communication, as `ZipDropzone.svelte` does with `onFilesSelected`.
- Use `$lib/...` for imports from `src/lib` rather than long relative paths.
- Name reusable component files in PascalCase and route files according to SvelteKit conventions.
- Keep browser-only APIs such as `File`, `FileList`, and drag-and-drop handling in components that execute in the browser. Do not introduce server-side upload behavior unless requested.
- Preserve semantic HTML. Interactive drop zones must remain keyboard-operable buttons with an accessible name.
- 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 application files, so do not add legacy Svelte component patterns.
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 utility classes in Svelte markup** for component and route styling. Orient new work around the existing classes in `+layout.svelte`, `+page.svelte`, `Card.svelte`, and `ZipDropzone.svelte`.
Use Tailwind utilities in Svelte markup and semantic theme tokens from `src/routes/layout.css`.
- Prefer existing theme utilities such as `bg-primary-50`, `text-primary-900`, `border-primary-500`, and `rounded-container`.
- Use the custom `primary`, `secondary`, `tertiary`, and `surface` scales defined in `src/lib/nom-theme.css` instead of adding arbitrary brand colors.
- Keep the established visual language: cool blue-gray page background, white cards, dark blue headings, subtle shadows, rounded containers, and the Nunito typeface.
- Use responsive Tailwind variants and ensure layouts work on narrow screens.
- Prefer utilities over new component-level `<style>` blocks or inline styles. Add global CSS only when defining shared theme primitives, imports, font faces, or behavior that cannot reasonably be expressed with utilities.
- Tailwind 4 is configured through Vite and `src/routes/layout.css`; there is no `tailwind.config` file.
- Keep complete utility class names visible to Tailwind. For variants, map each state to complete class strings rather than constructing fragments such as `text-${color}`.
- Preserve clear focus states, adequate contrast, and visible drag-and-drop feedback.
- 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.
`src/app.html` applies `data-theme="nominandum"`. Theme-level changes belong in `src/lib/nom-theme.css`; global Tailwind and Skeleton imports belong in `src/routes/layout.css`.
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
@@ -106,21 +221,29 @@ Follow the checked-in Prettier configuration:
- 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.
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 the request explicitly asks for content changes.
- Keep the shared Nominandum logo, footer artwork, and theme consistent across routes.
- ZIP selection is client-side and accepts `.zip`, `application/zip`, and `application/x-zip-compressed` files.
- Resetting the file input after selection is intentional so the same archive can be selected twice.
- Do not add network requests, file uploads, analytics, or persistence without an explicit requirement.
- 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 to the request and preserve unrelated work, including untracked files.
- Inspect existing components and theme tokens before introducing new abstractions.
- Do not hand-edit generated output or lockfile contents. Update the lockfile only through pnpm when dependency changes are required.
- Avoid adding dependencies when the task can be completed with Svelte, browser APIs, Tailwind, or existing Skeleton packages.
- After implementation, review the changed files, run the relevant checks, and mention any remaining warnings or limitations.
- 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.