Lesereisen — Journey-Editor

Kuratierungs-Oberfläche für JourneyEditor auf /geschichten/[id]/edit (wenn type === 'JOURNEY'). Geordnete Briefliste mit Drag-to-Reorder, Dokumenten-Picker, Interlude-Notizen und Inline-Annotation-Editing. Ersetzt den TipTap-Editor für den Journey-Typ.

Familienarchiv
Final Spec
2026-06-07 · @leonievoss
Issue #753
E

Journey-Editor

BLOG_WRITERs kuratieren eine geordnete Briefsequenz — Briefe hinzufügen, Zwischentexte einfügen, Reihenfolge per Drag anpassen, Notizen inline bearbeiten.

/geschichten/[id]/edit (type === 'JOURNEY')
Konzept

Der JourneyEditor ist eine parallele Implementierung zum bestehenden GeschichteEditor und wird auf derselben Edit-Route eingeblendet wenn type === 'JOURNEY'. Das Split-Layout (70/30) bleibt erhalten: links die Briefliste, rechts die Sidebar mit Personen und Status.

Die linke Fläche zeigt: oben einen optionalen Einleitungs-Textarea (body), darunter die geordnete Itemliste, ganz unten eine Aktionsleiste mit „+ Brief hinzufügen" und „+ Zwischentext hinzufügen". Jedes Item hat einen Drag-Handle, eine Positionsnummer, den Inhalt und einen Entfernen-Button.

Dokument-Items zeigen Titel und Kurz-Metadaten. Eine „Notiz hinzufügen/bearbeiten"-Aktion expandiert ein Textarea direkt unterhalb des Items — kein Modal, kein separates Formular. Interlude-Items (reiner Zwischentext) zeigen direkt ein editierbares Textarea mit orangenem Hintergrund zur klaren visuellen Unterscheidung.

Speicheraktionen: Speichern (bei veröffentlichter Journey) oder „Entwurf speichern" + „Veröffentlichen" (bei DRAFT). Die Savebarlogik ist identisch zum GeschichteEditor. Alle Mutationen lösen sofort einen API-Call aus und aktualisieren den lokalen Zustand optimistisch — kein separates Save für einzelne Items.

Screens — Leerer Editor

LE-1 — Journey-Editor leer

Issue #753 · LE-1

Ausgangszustand einer neuen oder leeren Lesereise. Titel-Input oben. Darunter ein optionaler Einleitungs-Textarea. Leere Itemliste mit Leerstate-Text. Aktionsleiste mit zwei Buttons. Sidebar: Personen-Verknüpfung und Status-Anzeige. Keine Items → „Veröffentlichen" noch nicht aktiv (Disabled-Hint erscheint).

Varianten: Neuer Entwurf ohne Titel (hier gezeigt) · Mit Titel, leere Liste

Desktop — 1040px · Neuer Entwurf
Dokumente Personen Geschichten Chronik
MR
Neue Lesereise REISE
ENTWURF
Einleitung (optional)
Briefe & Zwischentexte
Noch keine Einträge. Füge den ersten Brief oder einen Zwischentext hinzu.
Personen
🔍
Person suchen…
Welche historischen Personen kommen in dieser Lesereise vor?
Status
ENTWURF
Noch nicht öffentlich sichtbar. Füge mindestens einen Brief hinzu, um zu veröffentlichen.
Alle Änderungen werden als Entwurf gespeichert.

impl-ref — LE-1 Leerer Editor

ElementWertHinweise
Seitenstruktur
Bedingte Verzweigung{#if geschichte.type === 'JOURNEY'}<JourneyEditor />{:else}<GeschichteEditor />{/if}in edit/+page.svelte; Props: geschichte: Geschichte
Split-Layoutflex flex-1 overflow-hidden (gleich wie GeschichteEditor)70/30; Sidebar identisch
Topbar-Badge„REISE" Pill neben dem Titel-Labelorange; kein interaktives Element; zeigt Typ
Titel-Input
Titel-Inputfont-serif text-2xl border-b border-line pb-2 w-full bg-transparent outline-nonebind:value={title}; gleiche Validierung wie GeschichteEditor (required)
Einleitungs-Textarea
Intro-Textareafont-serif text-sm italic text-ink-3 leading-relaxed w-full resize-none bg-transparent outline-none border-none py-1bind:value={body}; plaintext; auto-resize per rows-attr oder JS
Labeltext-[10px] font-bold uppercase tracking-widest text-ink-3 mb-1„EINLEITUNG (OPTIONAL)"
Leerstate
Leerstate-Containerpy-8 text-center border border-dashed border-line rounded-sm bg-surfaceverschwindet sobald erstes Item vorhanden
Leerstate-Textfont-serif text-xs text-ink-3 italic
Veröffentlichen-Button
Disabled-Zustanddisabled={items.length === 0 || !title.trim()}opacity-40 + cursor-not-allowed; keine Tooltip nötig — Sidebar-Hint erklärt es
Screens — Editor mit Einträgen

LE-2 — Journey-Editor mit Einträgen

Issue #753 · LE-2

Gefüllte Itemliste mit gemischten Typen: Dokument-Item ohne Notiz, Interlude-Item (reiner Zwischentext), Dokument-Item mit bestehender Notiz. Jedes Item zeigt Drag-Handle links, Positionsnummer, Inhalt und Entfernen-Button. Aktionsleiste bleibt unter der Liste sichtbar.

Varianten: Veröffentlichte Journey (hier gezeigt) · Entwurf · Mobile

Desktop — 1040px · VERÖFFENTLICHT
Dokumente Personen Geschichten
KR
Lesereise bearbeiten REISE
VERÖFFENTLICHT
Löschen
Einleitung (optional)
Briefe & Zwischentexte
1
Brief vom 12. Juli 1938
12. Juli 1938 · von Franz Raddatz an Emma Müller
Notiz hinzufügen
×
Zwischentext
×
2
Postkarte aus Breslau, August 1938
22. Aug. 1938 · von Franz Raddatz an Emma Müller
Notiz entfernen
×
3
Brief vom 3. September 1939
3. Sept. 1939 · von Emma Müller an Franz Raddatz
Notiz hinzufügen
×
Personen
🔍
Person suchen…
FR Franz Raddatz × EM Emma Müller ×
Status
VERÖFFENTLICHT
Änderungen gehen sofort live.
Änderungen sofort live — Leser sehen die aktuelle Version.

impl-ref — LE-2 Items-Liste

ElementWertHinweise
Item-Zeile allgemein
Item-Containerflex items-stretch bg-white border border-line rounded-sm mb-2 overflow-hiddeninterlude: bg-orange-50 border-orange-200--color-interlude-bg / --color-interlude-border CSS tokens
Drag-Handlew-4 bg-surface border-r border-line flex items-center justify-center cursor-grab shrink-0aria-label="Reihenfolge ändern"; cursor-grabbing während Drag
Positions-Nr.w-5 text-[10px] font-bold text-ink-3 flex items-start justify-center pt-2 shrink-0aus Array-Index, nicht item.position
Entfernen-Buttonw-6 flex items-start justify-center pt-2 shrink-0× aria-label="Eintrag entfernen"; hover: text-red-500; Confirm nur wenn note vorhanden
Dokument-Item
Brieftiteltext-[11px] font-semibold text-ink leading-snug mb-0.5document.title
Briefmetatext-xs text-ink-3formatDate(doc.documentDate) · "von X" oder "von X an Y"
Notiz-Textarea (sichtbar)w-full min-h-[40px] font-serif text-xs italic bg-surface border border-line rounded-sm p-1.5 resize-none focus:border-primary focus:bg-white mt-2auto-expand; bind:value={item.note}
„Notiz hinzufügen" Linktext-xs font-semibold text-blue-600text-xs text-ink-3 underline hover:text-accenttogglet Notiz-Textarea
„Notiz entfernen" Linktext-xs text-ink-3 inline-flex items-center gap-1 mt-1zeigt sich wenn note.trim() nicht leer; setzt note = '' und blendet Textarea aus
Interlude-Item
Interlude-Containerbg-orange-50 border-orange-200--color-interlude-bg left-accent border via --color-interlude-borderkein Positions-Kreis; Positions-Spalte zeigt Icon statt Zahl
Label „Zwischentext"text-orange-700color: var(--color-interlude-label)immer sichtbar; nicht editierbar
Zwischentext-Textareaborder-orange-200 focus:border-orange-400border-line focus-visible:ring-focus-ringbind:value={item.note}; auto-expand; min 44px für Touch-Target
Aktionsleiste
Add Barflex gap-2 pt-2 pb-1immer unten sichtbar, auch wenn Liste gefüllt
„Brief hinzufügen" Buttonborder border-dashed border-line rounded-sm px-3 py-1.5 text-xs font-semibold text-ink-2 hover:border-primary hover:text-primary flex items-center gap-1öffnet existierende DocumentPicker-Komponente als Dropdown/Modal
„Zwischentext hinzufügen" Buttongleich wie Brief-Buttonfügt neues Interlude-Item am Ende ein; Fokus auf das neue Textarea
Drag-to-Reorder
Bibliothek@dnd-kit/core oder svelte-dnd-actioncreateBlockDragDrop<JourneyItemView> aus $lib/document/transcription/useBlockDragDrop.sveltekein externes Package; pointer-Events + data-drag-handle / data-block-wrapper Kontrakt
Reorder-API-CallPUT /api/geschichten/{id}/items/reorder — body: [{id, position}] für alle Itemsnach jedem Drop ausgelöst; optimistisch: lokalen State sofort aktualisieren
AccessibilityDrag-Handle: role="button" tabIndex=0; Keyboard: Space startet Drag, Arrow hoch/runter verschiebt, Space/Enter bestätigt, Esc abbrichtWCAG 2.1 SC 2.1.1
Screens — Inline-Notiz-Editing

LE-3 — Notiz-Textarea wird geöffnet

Issue #753 · LE-3

Wenn der Nutzer auf „Notiz hinzufügen" klickt, expandiert das Item um ein Textarea direkt unterhalb der Briefmeta — kein Modal. Der Fokus springt automatisch in das Textarea. Das Textarea hat einen blauen Fokusring als Orientierungshilfe. Ein API-PATCH wird beim Verlassen des Textareas (blur) ausgelöst, nicht bei jedem Tastendruck.

Inset-Ansicht — kein vollständiger Seiten-Mockup nötig

Inset — Notiz-Textarea geöffnet (Fokus)
1
Brief vom 12. Juli 1938
12. Juli 1938 · Franz → Emma
Notiz hinzufügen
×
2
Postkarte aus Breslau, August 1938
22. Aug. 1938 · Franz → Emma
Wird gespeichert, wenn du das Feld verlässt.
Notiz entfernen
×

impl-ref — LE-3 Inline-Notiz

ElementWertHinweise
Toggleverhalten
Lokaler Statelet noteOpen = item.note !== null and item.note !== ''öffnet sich automatisch wenn Notiz bereits vorhanden
„Notiz hinzufügen" KlicknoteOpen = true; tick().then(() => noteTextarea.focus())Fokus nach Svelte-Tick um DOM-Update abzuwarten
Textarea blur-Handleron:blur={() => saveNote(item.id, note)}PATCH /api/geschichten/{id}/items/{itemId} mit {note}
Leere Notiz on blurwenn note.trim() === '' → noteOpen = false; note = nullverhindert leere Notizen im Backend
Fokus-Styling
Fokus-Ringfocus:border-primary focus:ring-2 focus:ring-primary/20 focus:bg-whitesichtbarer Ring für Keyboard-Navigation; ring-offset für Abstand
Spar-Hinttext-[9px] text-ink-3 mt-1„Wird gespeichert, wenn du das Feld verlässt."; verschwindet wenn noteOpen = false
Barrierefreiheit
aria-label Textareaaria-label="Kuratoren-Notiz für {document.title}"spezifisch; Screen-Reader nennt Brief-Kontext
aria-expanded Togglearia-expanded={noteOpen} auf „Notiz hinzufügen"-Buttonkommuniziert Expand-State
Screens — Mobile Editor

LE-4 — Mobile Journey-Editor

Issue #753 · LE-4

Auf Mobile (320px) entfällt die Sidebar-Split. Die Personen- und Status-Sektion werden als ausklappbare Sektionen unter der Itemliste gezeigt. Drag-to-Reorder ist auf Mobile durch Long-Press aktiviert. Die Aktionsleiste scrollt mit dem Inhalt.

Primäre Zielgruppe für den Editor: Desktop/Tablet. Mobile ist sekundär — alle Funktionen erreichbar, aber Drag ist schwerer bedienbar.

Mobile — 320px · mit Einträgen
9:41●●●
Lesereise bearbeiten
VERÖFF.
Brief vom 12. Juli 1938
12. Juli 1938 · Franz → Emma
×
Zwischentext
Im Sommer 1938 schrieb Franz voller Zuversicht…
×
Postkarte Aug. 1938
22. Aug. 1938 · Franz → Emma
Diese Karte ist ungewöhnlich kurz für Franz…
×
Personen
Status & Speichern

impl-ref — LE-4 Mobile

ElementWertHinweise
Layout-Anpassungen
Split entfällt@media (max-width: 768px): flex-col; Sidebar-Sektionen als Collapsibles am Endegleich wie GeschichteEditor auf Mobile
Collapsiblesdetails/summary oder eigene boolean-Toggle; Personen + Status separatgeschlossen beim ersten Laden; Fokus öffnet
Touch & Drag
Drag auf MobileMove-Up/Down Buttons statt Drag (44px touch targets)dnd-kit unterstützt Touch nativ → Pointer-Drag nur Desktop; Keyboard via Pfeil-Buttons
Touch Target Itemsmin-h-[44px] für jede Item-ZeileWCAG 2.2 AA; durch Padding gesichert
Add-Buttonsflex-1; volle verfügbare Breite geteiltmin-h-[44px] als Touch-Target
Savebar
Savebar Mobileflex gap-2; „Zurück zu Entwurf" komprimiert zu „Entwurf"Volltext passt nicht auf 320px

Implementation Guide — Journey-Editor

Neue Komponente

DateiTypBeschreibung
src/lib/geschichte/JourneyEditor.svelteSvelte-KomponenteHauptkomponente; Props: geschichte: Geschichte
src/lib/geschichte/JourneyItemRow.svelteSvelte-KomponenteEine Zeile (Dokument oder Interlude); Props: item: JourneyItem, position: number, Events: remove, noteChange

Edit-Page-Integration

API-Calls

AktionEndpointBody
Brief hinzufügenPOST /api/geschichten/{id}/items{documentId: UUID}
Zwischentext hinzufügenPOST /api/geschichten/{id}/items{note: string}
Notiz speichern/bearbeitenPATCH /api/geschichten/{id}/items/{itemId}{note: string | null}
Item entfernenDELETE /api/geschichten/{id}/items/{itemId}
Reihenfolge speichernPUT /api/geschichten/{id}/items/reorder[{id: UUID, position: number}]

Optimistische Updates

DocumentPicker-Integration

Drag-to-Reorder

Barrierefreiheit

Abgrenzung zu GeschichteEditor