This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user