subdocument-splitting-plan
This commit is contained in:
1 parent
f09384d2e6
commit
ca06ca2050
47 files changed
+4097
-136
No files matched your search
@@ -0,0 +1,315 @@
|
||||
# Plan: beA settings menu and “next ZIP” workflow
|
||||
|
||||
**Status: Done**
|
||||
|
||||
## Current review
|
||||
|
||||
The loaded-archive state in `src/routes/tools/bea/+page.svelte` uses a native `<details>` menu fixed
|
||||
in the lower-right corner. I reviewed it at 1440 × 900 and 390 × 844 with a real one-PDF ZIP.
|
||||
|
||||
### What is not working
|
||||
|
||||
- The floating gear is detached from the archive it controls and competes with the footer/content
|
||||
edge. On mobile, the open panel overlaps the archive card.
|
||||
- Two long actions are forced side by side into a 288 px panel. Their labels visibly clip to
|
||||
“Archive als PDF” and “Archive”, so the difference between export and delete is unclear.
|
||||
- The emoji gear does not match the app's Lucide icon language. Its accessible name is the English
|
||||
word “Settings” in an otherwise German interface.
|
||||
- View configuration, bulk export, and destructive session deletion are mixed without hierarchy.
|
||||
“Alle ZIP-Archive löschen” receives nearly the same visual weight as export and has no
|
||||
confirmation.
|
||||
- “Thumbnail Breite” is an implementation-oriented, partly English label. The control should be
|
||||
described as the user-visible result: “Vorschaugröße”.
|
||||
- Native `<details>` does not provide the expected menu behavior here: no reliable outside-click
|
||||
dismissal, weak focus management, and no deliberate focus return.
|
||||
- Once any ZIP has loaded, the dropzone disappears. Although the route can append files internally,
|
||||
the user has no control to open another ZIP or begin a clean next task.
|
||||
- The loaded archive is vertically centered in a viewport-height container. This creates a large
|
||||
gap below “beA-Edit” on both desktop and mobile and makes the fixed settings control feel even more
|
||||
disconnected.
|
||||
|
||||
## Product decision
|
||||
|
||||
Use two explicit archive lifecycle actions:
|
||||
|
||||
1. **“Neue ZIP bearbeiten”** is the prominent next-task action. It replaces the current workspace
|
||||
only after the user has selected a valid ZIP and confirmed the replacement. This directly serves
|
||||
the requested “I am done; give me the next ZIP” workflow.
|
||||
2. **“Weitere ZIP hinzufügen”** remains available inside the actions panel for users intentionally
|
||||
working with multiple ZIPs at once. It appends to the current workspace, preserving the behavior
|
||||
already supported by `handleFilesSelected`.
|
||||
|
||||
The replacement confirmation is shown _after_ file selection. Cancelling either the system picker or
|
||||
confirmation must leave the current work untouched. The confirmation says that the current view and
|
||||
unexported changes will be removed, while files already downloaded remain on the device.
|
||||
|
||||
Do not try to infer that a user is “done” from a download click. A download does not prove that every
|
||||
wanted output was saved, and the browser cannot reliably verify what happened to the file afterward.
|
||||
|
||||
## UX direction
|
||||
|
||||
### Desktop
|
||||
|
||||
```text
|
||||
← Zurück
|
||||
|
||||
beA-Edit 1 ZIP · 1 Archiv geöffnet [+ Neue ZIP bearbeiten] [⚙ Einstellungen]
|
||||
──────────────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
┌ Current archive editor ────────────────────────────────────────────────────────────────┐
|
||||
│ archive name [Auswählen] [PDF herunterladen] │
|
||||
│ attachment previews … │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Settings popover, aligned to the right edge of the toolbar:
|
||||
|
||||
```text
|
||||
┌ Einstellungen ─────────────────────────────┐
|
||||
│ ANSICHT │
|
||||
│ Vorschaugröße 200 px │
|
||||
│ Klein ─────────●────────────── Groß │
|
||||
│ │
|
||||
│ ARCHIV-AKTIONEN │
|
||||
│ [+ Weitere ZIP hinzufügen] │
|
||||
│ [⇩ Alle Archive als PDF herunterladen] │ shown only when useful
|
||||
│ ───────────────────────────────────────── │
|
||||
│ [🗑 Aktuelle Bearbeitung leeren] │ quiet destructive action
|
||||
└───────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Mobile
|
||||
|
||||
The toolbar becomes a two-row block immediately below the title and remains in normal document flow.
|
||||
The status occupies the first row; the two buttons share the second row. The popover uses
|
||||
`width: min(22rem, calc(100vw - 2rem))`, stacks every action vertically, and relies on collision-aware
|
||||
positioning rather than fixed bottom/right coordinates. No control or panel may cover the archive
|
||||
header.
|
||||
|
||||
The loaded workspace becomes top-aligned (`justify-start`) instead of viewport-centered, removing the
|
||||
large empty band between the page heading and first archive.
|
||||
|
||||
## Visual system
|
||||
|
||||
Preserve the existing Nominandum identity rather than introducing a new theme:
|
||||
|
||||
- **Ink** `#122F62`: headings, primary actions, and active controls.
|
||||
- **Workspace** `#EEF4F6`: page background.
|
||||
- **Paper** `#FFFFFF`: editor and popover surfaces.
|
||||
- **Handoff coral** `#EA544B`: restrained accent only; do not use it to make deletion look primary.
|
||||
- **Success** `#4E8A72`: completed processing state.
|
||||
- Typography remains **Nunito** for interface copy; use the existing Geist utility face only where
|
||||
numeric/file metadata already uses it.
|
||||
- Use existing radii and semantic CSS variables from `src/routes/layout.css`; no theme-level CSS
|
||||
changes should be necessary.
|
||||
|
||||
The distinctive element is the **archive handoff row**: current-work status on the left and “Neue ZIP
|
||||
bearbeiten” on the right. It expresses the actual legal-document rhythm—finish one beA export, hand
|
||||
it off, open the next—rather than presenting a generic floating settings cog.
|
||||
|
||||
Self-critique: a card full of settings tabs would be a generic dashboard solution and is unnecessary
|
||||
for one view preference. The revised direction removes the floating action, keeps the next-case task
|
||||
visible, and spends visual emphasis only on the archive handoff.
|
||||
|
||||
## Interaction details
|
||||
|
||||
### Toolbar
|
||||
|
||||
- Status copy derives from state:
|
||||
- `1 ZIP · 1 Archiv geöffnet`
|
||||
- `2 ZIPs · 3 Archive geöffnet`
|
||||
- while work is pending, append `· 1 wird geöffnet` in an `aria-live="polite"` status.
|
||||
- “Neue ZIP bearbeiten” uses Lucide `FilePlus2` and remains available after the first archive or an
|
||||
error. It opens a ZIP-only file picker.
|
||||
- “Einstellungen” uses Lucide `Settings2`, has a visible German label at desktop sizes, and retains
|
||||
an explicit accessible name at all widths.
|
||||
- Do not make the toolbar fixed or sticky in v1. Normal flow avoids covering previews and footer
|
||||
content; sticky behavior can be reconsidered after testing long archives.
|
||||
|
||||
### New ZIP replacement
|
||||
|
||||
1. User activates “Neue ZIP bearbeiten”.
|
||||
2. The system file picker opens and accepts the same ZIP types as `ZipDropzone`.
|
||||
3. If the picker is cancelled, nothing changes.
|
||||
4. If valid files are selected, show an `AlertDialog`:
|
||||
- title: **“Aktuelle Bearbeitung ersetzen?”**
|
||||
- description: **“Archive und nicht heruntergeladene Änderungen in dieser Ansicht werden
|
||||
entfernt. Bereits heruntergeladene PDFs bleiben erhalten.”**
|
||||
- cancel: **“Abbrechen”**
|
||||
- confirm: **“Neue ZIP öffnen”**
|
||||
5. On confirm, increment `archiveGeneration`, clear selected files, processed archives, and jobs, then
|
||||
enqueue the newly selected files against the new generation.
|
||||
6. If extraction fails, show the existing processing error state for the new ZIP and allow retry or
|
||||
removal; do not restore the old workspace implicitly.
|
||||
|
||||
Keep the selected replacement files in temporary component state until confirmation. This prevents a
|
||||
cancelled dialog from mutating or clearing the current workspace. Reset the underlying file input
|
||||
after every selection so choosing the same ZIP again still fires `change`.
|
||||
|
||||
### Append, export, and clear
|
||||
|
||||
- “Weitere ZIP hinzufügen” uses the existing append path and never clears current archives.
|
||||
- Show bulk export only when there are at least two processed archives; one archive already has its
|
||||
own clearer “PDF herunterladen” action.
|
||||
- Rename bulk export to **“Alle Archive als PDF herunterladen”**.
|
||||
- Rename deletion to **“Aktuelle Bearbeitung leeren”** and place it after a separator as a quiet
|
||||
destructive menu item with `Trash2`.
|
||||
- Clearing requires its own `AlertDialog`; confirmation calls the existing reset path and returns the
|
||||
user to the initial ZIP dropzone.
|
||||
- Disable replacement, append, export, and clear while a destructive transition is being committed,
|
||||
but allow a new selection while archive extraction is in progress; generation guarding prevents
|
||||
stale results from returning.
|
||||
|
||||
### View setting
|
||||
|
||||
- Replace the raw range with the shadcn `Slider` labeled **“Vorschaugröße”**.
|
||||
- Keep the current `100–400 px` range, `1 px` step, clamp function, and `thumbnailWidth`
|
||||
localStorage key for backward compatibility.
|
||||
- Display the current value as tabular numerals and include “Klein”/“Groß” endpoint hints.
|
||||
- The label and current value must be associated with the slider for assistive technology. Arrow-key
|
||||
adjustment remains available.
|
||||
|
||||
## Component and state design
|
||||
|
||||
### New `BeaWorkspaceControls.svelte`
|
||||
|
||||
Create `src/lib/components/BeaWorkspaceControls.svelte` to keep the already state-heavy route from
|
||||
absorbing menu markup. Suggested props:
|
||||
|
||||
```ts
|
||||
type Props = {
|
||||
selectedZipCount: number;
|
||||
processedArchiveCount: number;
|
||||
pendingZipCount: number;
|
||||
thumbnailWidth: number;
|
||||
onThumbnailWidthChange: (value: number) => void;
|
||||
onRequestReplacement: () => void;
|
||||
onAppendFiles: (files: File[]) => void;
|
||||
onExportAll: () => void;
|
||||
onClear: () => void;
|
||||
};
|
||||
```
|
||||
|
||||
The component owns only ephemeral UI state (popover/dialog open state and temporarily selected
|
||||
replacement files). The route remains the source of truth for files, jobs, archives, and the
|
||||
persisted thumbnail width.
|
||||
|
||||
Centralize the ZIP accept string and validation used by the initial dropzone and both compact pickers.
|
||||
Prefer extracting a small reusable ZIP picker/input rather than duplicating MIME and extension checks
|
||||
inside the controls component.
|
||||
|
||||
### Route changes
|
||||
|
||||
In `src/routes/tools/bea/+page.svelte`:
|
||||
|
||||
- Replace `deleteAllZipArchives` with a single `resetWorkspace()` helper that increments
|
||||
`archiveGeneration` and clears all three state arrays.
|
||||
- Add `replaceWithZipFiles(files)` that calls `resetWorkspace()` and then
|
||||
`handleFilesSelected(files)`; the new jobs must capture the incremented generation.
|
||||
- Keep `handleFilesSelected` as the append operation.
|
||||
- Render `BeaWorkspaceControls` whenever at least one selected file/job/archive exists.
|
||||
- Remove the fixed `<details>` block.
|
||||
- Top-align the loaded-workspace container and render status/controls before processing and editor
|
||||
cards.
|
||||
- Preserve `thumbnailWidth` hydration and persistence exactly as today.
|
||||
|
||||
### shadcn-svelte pieces
|
||||
|
||||
Add only the required official components with the pnpm CLI, reviewing generated files and imports:
|
||||
|
||||
- `popover`
|
||||
- `slider`
|
||||
- `separator`
|
||||
- `alert-dialog`
|
||||
|
||||
Do not overwrite the customized button component. The project currently imports
|
||||
`$lib/components/ui/button.svelte` directly, so adapt generated imports to the local export shape if
|
||||
the registry assumes a barrel. Use Lucide Svelte icons (`FilePlus2`, `Settings2`, `Download`,
|
||||
`Trash2`) with `data-icon` inside buttons.
|
||||
|
||||
## Files
|
||||
|
||||
- `src/routes/tools/bea/+page.svelte` — workspace lifecycle, reset/replace handlers, and layout.
|
||||
- `src/lib/components/BeaWorkspaceControls.svelte` — new toolbar, popover, dialogs, and picker
|
||||
coordination.
|
||||
- `src/lib/components/ZipDropzone.svelte` and/or a new small ZIP picker helper — shared accept and
|
||||
selection behavior.
|
||||
- `src/lib/components/ui/{popover,slider,separator,alert-dialog}/` — official shadcn-svelte source.
|
||||
- `tests/e2e/toolbox.spec.ts` — archive handoff, menu, keyboard, and responsive coverage.
|
||||
- `agents.md` — document append versus replace semantics and the shared reset invariant.
|
||||
|
||||
No changes are expected in `zip-processing.ts`, archive transforms, export ordering, or server code.
|
||||
|
||||
## Implementation phases
|
||||
|
||||
1. **Shared ZIP selection and state helpers**
|
||||
- Centralize accepted ZIP types.
|
||||
- Introduce `resetWorkspace` and `replaceWithZipFiles`.
|
||||
- Verify generation guarding when replacement occurs during extraction.
|
||||
2. **Workspace toolbar and layout**
|
||||
- Add the top-aligned status/action row.
|
||||
- Implement the replacement picker and confirmation without removing current work on cancel.
|
||||
- Remove the fixed gear and viewport-centering.
|
||||
3. **Settings popover**
|
||||
- Add the responsive popover, slider, append, conditional bulk export, and confirmed clear.
|
||||
- Verify keyboard focus, Escape/outside-click dismissal, and focus return.
|
||||
4. **Tests and polish**
|
||||
- Add e2e coverage below.
|
||||
- Run desktop/mobile browser review and remove any clipped or overlapping labels.
|
||||
- Update `agents.md` and run the full validation set.
|
||||
|
||||
## Verification
|
||||
|
||||
### Automated
|
||||
|
||||
Add Playwright cases for:
|
||||
|
||||
- Load ZIP A → “Neue ZIP bearbeiten” appears → select ZIP B → cancel confirmation → ZIP A remains.
|
||||
- Repeat replacement → confirm → only ZIP B is shown and no late result from ZIP A can reappear.
|
||||
- Cancel the system file picker → workspace is unchanged.
|
||||
- Select the same ZIP twice through replacement → the second `change` is processed.
|
||||
- “Weitere ZIP hinzufügen” appends ZIP B and both archive editors remain visible.
|
||||
- Bulk export is absent for one processed archive and available for two or more.
|
||||
- “Aktuelle Bearbeitung leeren” cancel preserves state; confirm returns to the initial dropzone.
|
||||
- Slider changes preview width, works with arrow keys, and survives a reload through localStorage.
|
||||
- Settings opens with a German accessible name, closes on Escape/outside click, and returns focus to
|
||||
its trigger.
|
||||
- At 390 × 844, toolbar actions and every popover label fit without horizontal clipping; the panel
|
||||
stays inside the viewport and does not cover the archive header.
|
||||
|
||||
Then run:
|
||||
|
||||
```sh
|
||||
pnpm check
|
||||
pnpm test:e2e
|
||||
pnpm lint
|
||||
pnpm build
|
||||
```
|
||||
|
||||
### Manual
|
||||
|
||||
- Review 1440 × 900 and 390 × 844 with one archive, multiple archives, an extraction in progress,
|
||||
and an extraction error.
|
||||
- Confirm file processing remains entirely local and no case data is persisted.
|
||||
- Confirm tab order is title/status → new ZIP → settings → popover controls → archive controls.
|
||||
- Confirm reduced-motion mode introduces no menu animation requirement.
|
||||
|
||||
## Risks and mitigations
|
||||
|
||||
- **Accidental loss during replacement:** current state is not cleared until valid files are selected
|
||||
and the user confirms.
|
||||
- **Stale async extraction:** every reset increments `archiveGeneration`; old jobs cannot append
|
||||
results afterward.
|
||||
- **Duplicate picker logic:** centralize accept/filter/reset behavior before adding compact actions.
|
||||
- **Component bloat for a small menu:** use one popover and shared alert-dialog primitives; do not add
|
||||
tabs, a permanent sidebar, or a settings route.
|
||||
- **Ambiguous “new” versus “additional”:** keep the labels distinct and stable everywhere:
|
||||
“Neue ZIP bearbeiten” replaces; “Weitere ZIP hinzufügen” appends.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Persisting archives or edits across reloads.
|
||||
- Detecting whether the operating system completed a download.
|
||||
- Undoing a confirmed workspace replacement.
|
||||
- Changing PDF composition, attachment ordering, or sub-document behavior.
|
||||
- A global application settings page.
|
||||
Reference in new issue
Block a user