832 lines
52 KiB
HTML
832 lines
52 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Geschichten — Writer Journey · Familienarchiv</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,400;9..144,500&family=DM+Sans:wght@300;400;500;600&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root{--color-page:#FAFAF7;--color-surface:#F5F4EE;--color-subtle:#EDECEA;--color-border:#D8D7D0;--color-text-muted:#6B6A63;--color-text:#1C1C18;--navy:#012851;--mint:#A1DCD8;--sand:#F0EFE9;--turquoise:#00C7B1;--blue-tint:#E6F1FB;--blue:#2D7DD2;--blue-dark:#185FA5;--green-tint:#E8F5EA;--green:#3D8C4A;--green-dark:#2E6E39;--orange-tint:#FEF0E6;--orange:#E8862A;--orange-dark:#B46820;--font-display:'Fraunces',Georgia,serif;--font-sans:'DM Sans',system-ui,sans-serif;--font-mono:'DM Mono',monospace;--radius-sm:4px;--radius-md:6px;--radius-lg:10px;--radius-xl:16px;--shadow-card:0 1px 3px rgba(28,28,24,.06),0 1px 2px rgba(28,28,24,.04);--shadow-raised:0 4px 12px rgba(28,28,24,.08),0 2px 4px rgba(28,28,24,.04);--shadow-overlay:0 8px 32px rgba(28,28,24,.12),0 2px 8px rgba(28,28,24,.06);}
|
||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
|
||
body{font-family:var(--font-sans);background:#E8E7E2;color:var(--color-text);font-size:14px;line-height:1.6;}
|
||
.doc{max-width:1200px;margin:0 auto;padding:48px 40px 120px;}
|
||
.doc-header{display:flex;justify-content:space-between;align-items:flex-end;padding-bottom:28px;border-bottom:1px solid var(--color-border);margin-bottom:48px;background:var(--color-page);margin:-48px -40px 48px;padding:48px 40px 28px;border-radius:var(--radius-xl) var(--radius-xl) 0 0;}
|
||
.doc-header h1{font-family:var(--font-display);font-size:28px;font-weight:500;letter-spacing:-.02em;margin-bottom:4px;}
|
||
.doc-header p{font-size:13px;color:var(--color-text-muted);max-width:680px;}
|
||
.doc-meta{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);text-align:right;line-height:1.9;}
|
||
.pill{display:inline-block;padding:2px 8px;border-radius:var(--radius-sm);font-size:10px;font-weight:500;letter-spacing:.05em;}
|
||
.pill-g{background:var(--green-tint);color:var(--green-dark);}
|
||
.section{margin-bottom:64px;}
|
||
.section-title{font-size:10px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--color-text-muted);padding-bottom:10px;border-bottom:1px solid var(--color-border);margin-bottom:24px;}
|
||
.prose{font-size:13px;color:var(--color-text-muted);line-height:1.65;max-width:720px;margin-bottom:20px;}
|
||
.jh{padding:20px 24px;border-radius:var(--radius-xl);margin-bottom:40px;display:flex;align-items:center;gap:16px;}
|
||
.jh .jn{font-family:var(--font-display);font-size:48px;font-weight:300;line-height:1;opacity:.5;}
|
||
.jh h2{font-family:var(--font-display);font-size:22px;font-weight:500;letter-spacing:-.02em;margin-bottom:4px;}
|
||
.jh p{font-size:13px;line-height:1.5;}.jh .fl{font-family:var(--font-mono);font-size:11px;margin-top:6px;opacity:.7;}
|
||
.jh-g{background:var(--green-tint);border:1px solid #A0D8A8;}.jh-g .jn{color:var(--green);}.jh-g p,.jh-g .fl{color:var(--green-dark);}
|
||
.scr{margin-bottom:56px;}
|
||
.scr-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;}
|
||
.scr-head h3{font-family:var(--font-display);font-size:20px;font-weight:500;letter-spacing:-.02em;}
|
||
.scr-id{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);padding:2px 8px;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-page);}
|
||
.scr-desc{font-size:12px;color:var(--color-text-muted);line-height:1.6;max-width:720px;margin-bottom:6px;}
|
||
.scr-var{font-size:11px;color:var(--color-text-muted);margin-bottom:20px;}.scr-var strong{color:var(--color-text);}
|
||
.previews{display:flex;gap:32px;flex-wrap:wrap;justify-content:center;align-items:flex-start;margin-bottom:20px;}
|
||
.prev-col{display:flex;flex-direction:column;align-items:center;gap:10px;}
|
||
.bp-lbl{font-family:var(--font-mono);font-size:10px;color:var(--color-text-muted);}
|
||
.desk{width:100%;max-width:1040px;background:var(--color-page);border-radius:var(--radius-xl);overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.06);display:flex;flex-direction:column;min-height:560px;}
|
||
.phone{width:320px;flex-shrink:0;background:var(--color-page);border-radius:36px;overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.07);display:flex;flex-direction:column;border:6px solid #1C1C18;}
|
||
.pst{padding:10px 20px 0;display:flex;justify-content:space-between;align-items:center;font-size:12px;background:var(--color-page);}.pst b{font-weight:600;}.pst span{font-size:10px;}
|
||
.pb{flex:1;overflow-y:auto;display:flex;flex-direction:column;}
|
||
.fa-nav{height:32px;background:var(--navy);display:flex;align-items:center;padding:0 12px;gap:8px;flex-shrink:0;}
|
||
.fa-logo{font-size:7px;font-weight:900;color:#fff;letter-spacing:.8px;border-bottom:2px solid var(--mint);padding-bottom:1px;}
|
||
.fa-link{font-size:5.5px;color:rgba(255,255,255,.4);font-weight:700;text-transform:uppercase;letter-spacing:.05em;}
|
||
.fa-link.active{color:var(--mint);}
|
||
.fa-nav-r{margin-left:auto;display:flex;gap:5px;align-items:center;}
|
||
.fa-av{width:16px;height:16px;background:rgba(255,255,255,.1);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:5px;font-weight:800;color:rgba(255,255,255,.5);}
|
||
.agent{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:24px;margin-top:20px;}
|
||
.agent h4{font-family:var(--font-mono);font-size:12px;font-weight:500;margin-bottom:8px;color:var(--navy);}
|
||
.agent pre{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);margin-bottom:12px;white-space:pre-wrap;}
|
||
.at{width:100%;border-collapse:collapse;font-size:11px;}
|
||
.at th{font-family:var(--font-mono);font-size:10px;font-weight:500;text-align:left;color:var(--color-text-muted);padding:6px 8px;border-bottom:2px solid var(--color-border);}
|
||
.at td{padding:5px 8px;border-bottom:1px solid var(--color-subtle);vertical-align:top;line-height:1.5;}
|
||
.at tr.grp td{font-family:var(--font-mono);font-size:9px;color:var(--color-text-muted);background:var(--color-subtle);font-weight:500;letter-spacing:.06em;text-transform:uppercase;padding:4px 8px;}
|
||
.at code{font-family:var(--font-mono);font-size:10px;color:var(--navy);background:rgba(1,40,81,.06);padding:1px 4px;border-radius:2px;}
|
||
|
||
/* ── Editor frame internals (scaled ~55%) ── */
|
||
.ed-topbar{background:#fff;border-bottom:1px solid #e4e2d7;display:flex;align-items:center;padding:0 14px;gap:8px;height:38px;flex-shrink:0;}
|
||
.ed-back{width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:9px;color:var(--color-text-muted);cursor:pointer;flex-shrink:0;}
|
||
.ed-title-label{font-family:var(--font-sans);font-size:10px;font-weight:500;color:var(--color-text);flex:1;}
|
||
.ed-status-pill{display:inline-flex;align-items:center;padding:2px 7px;border-radius:20px;font-size:8px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;flex-shrink:0;}
|
||
.ed-status-draft{background:#F0EFE9;color:#6B6A63;border:1px solid #D8D7D0;}
|
||
.ed-status-pub{background:var(--green-tint);color:var(--green-dark);border:1px solid #A0D8A8;}
|
||
.ed-delete-link{font-size:8px;font-weight:600;color:#DC4C3E;margin-left:8px;text-decoration:none;white-space:nowrap;}
|
||
.ed-split{display:flex;flex:1;overflow:hidden;}
|
||
.ed-left{flex:1;display:flex;flex-direction:column;padding:14px 16px;overflow-y:auto;gap:10px;}
|
||
.ed-title-input{font-family:var(--font-display);font-size:16px;font-weight:400;color:var(--color-text);border:none;border-bottom:1px solid var(--color-border);padding:4px 0 6px;width:100%;outline:none;background:transparent;letter-spacing:-.01em;}
|
||
.ed-title-input.placeholder{color:var(--color-text-muted);}
|
||
.ed-title-input.error{border-bottom-color:#DC4C3E;}
|
||
.ed-sep{height:1px;background:var(--color-border);margin:2px 0;}
|
||
.ed-toolbar{display:flex;align-items:center;gap:4px;}
|
||
.ed-tool{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--color-border);border-radius:var(--radius-sm);font-size:9px;font-weight:700;color:var(--color-text-muted);background:#fff;cursor:pointer;flex-shrink:0;}
|
||
.ed-tool:hover{background:var(--color-subtle);color:var(--color-text);}
|
||
.ed-tool.active{background:var(--navy);color:#fff;border-color:var(--navy);}
|
||
.ed-body{font-family:Georgia,serif;font-size:10px;line-height:1.7;color:var(--color-text);min-height:220px;resize:none;border:none;outline:none;background:transparent;width:100%;}
|
||
.ed-body.placeholder{color:var(--color-text-muted);font-style:italic;}
|
||
.ed-sidebar{width:210px;flex-shrink:0;border-left:1px solid #e4e2d7;background:#fff;display:flex;flex-direction:column;overflow-y:auto;}
|
||
.ed-sb-section{padding:12px 12px 10px;}
|
||
.ed-sb-title{font-size:8px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:8px;}
|
||
.ed-sb-divider{height:1px;background:#e4e2d7;margin:0;}
|
||
.ed-search-row{display:flex;align-items:center;gap:6px;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);padding:4px 8px;margin-bottom:6px;}
|
||
.ed-search-row svg-icon{font-size:8px;color:var(--color-text-muted);flex-shrink:0;}
|
||
.ed-search-input{font-family:var(--font-sans);font-size:9px;color:var(--color-text-muted);border:none;outline:none;background:transparent;flex:1;}
|
||
.ed-chip{display:inline-flex;align-items:center;gap:4px;padding:3px 7px;background:var(--sand);border:1px solid var(--color-border);border-radius:12px;font-size:8px;font-weight:500;color:var(--color-text);margin:0 4px 4px 0;}
|
||
.ed-chip-x{color:var(--color-text-muted);font-size:9px;cursor:pointer;margin-left:2px;line-height:1;}
|
||
.ed-chip.doc-chip{background:var(--blue-tint);border-color:#BCD8F4;color:var(--blue-dark);}
|
||
.ed-hint{font-size:8px;color:var(--color-text-muted);line-height:1.5;margin-top:4px;}
|
||
.ed-chips-wrap{display:flex;flex-wrap:wrap;min-height:22px;}
|
||
.ed-savebar{background:#fff;border-top:1px solid #e4e2d7;padding:9px 14px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0;gap:10px;}
|
||
.ed-savebar-hint{font-size:8px;color:var(--color-text-muted);}
|
||
.ed-savebar-actions{display:flex;align-items:center;gap:7px;}
|
||
.ed-btn-ghost{font-size:9px;font-weight:600;padding:5px 12px;border-radius:var(--radius-sm);border:1px solid var(--color-border);color:var(--color-text);background:#fff;cursor:pointer;white-space:nowrap;}
|
||
.ed-btn-ghost:hover{background:var(--color-subtle);}
|
||
.ed-btn-ghost.retract{color:#B46820;border-color:#E8D5B0;}
|
||
.ed-btn-ghost.retract:hover{background:var(--orange-tint);}
|
||
.ed-btn-primary{font-size:9px;font-weight:600;padding:5px 12px;border-radius:var(--radius-sm);background:var(--navy);color:#fff;border:none;cursor:pointer;white-space:nowrap;}
|
||
.ed-btn-primary:hover{background:#023570;}
|
||
.ed-btn-danger{font-size:9px;font-weight:600;padding:5px 12px;border-radius:var(--radius-sm);background:#DC4C3E;color:#fff;border:none;cursor:pointer;white-space:nowrap;}
|
||
.ed-error-hint{font-size:8px;color:#DC4C3E;margin-top:3px;}
|
||
|
||
/* ── Validation inset ── */
|
||
.validation-inset{background:#FFF5F5;border:1px solid #FECACA;border-radius:var(--radius-md);padding:14px 16px;margin-top:16px;max-width:600px;}
|
||
.validation-inset-label{font-size:9px;font-family:var(--font-mono);font-weight:500;color:#DC4C3E;margin-bottom:8px;letter-spacing:.04em;}
|
||
.vi-field{border:1px solid #DC4C3E;border-radius:var(--radius-sm);background:#fff;padding:6px 10px;font-family:var(--font-display);font-size:13px;color:var(--color-text-muted);font-style:italic;width:100%;margin-bottom:4px;}
|
||
.vi-err{font-size:9px;color:#DC4C3E;font-weight:500;}
|
||
|
||
/* ── Mobile editor ── */
|
||
.mob-topbar{background:#fff;border-bottom:1px solid #e4e2d7;display:flex;align-items:center;padding:0 10px;gap:6px;height:34px;flex-shrink:0;}
|
||
.mob-back{font-size:9px;color:var(--color-text-muted);}
|
||
.mob-title-label{font-family:var(--font-sans);font-size:9px;font-weight:500;color:var(--color-text);flex:1;}
|
||
.mob-body{flex:1;overflow-y:auto;padding:10px 12px;display:flex;flex-direction:column;gap:8px;}
|
||
.mob-title-input{font-family:var(--font-display);font-size:13px;color:var(--color-text-muted);font-style:italic;border:none;border-bottom:1px solid var(--color-border);padding:3px 0 5px;width:100%;background:transparent;outline:none;}
|
||
.mob-toolbar{display:flex;gap:3px;}
|
||
.mob-tool{width:20px;height:20px;display:flex;align-items:center;justify-content:center;border:1px solid var(--color-border);border-radius:var(--radius-sm);font-size:8px;font-weight:700;color:var(--color-text-muted);background:#fff;}
|
||
.mob-ed-body{font-family:Georgia,serif;font-size:9px;line-height:1.65;color:var(--color-text-muted);font-style:italic;min-height:160px;}
|
||
.mob-collapsible{background:#fff;border:1px solid #e4e2d7;border-radius:var(--radius-sm);overflow:hidden;}
|
||
.mob-coll-hdr{display:flex;align-items:center;justify-content:space-between;padding:8px 10px;font-size:9px;font-weight:600;color:var(--color-text);}
|
||
.mob-coll-chevron{font-size:10px;color:var(--color-text-muted);}
|
||
.mob-savebar{background:#fff;border-top:1px solid #e4e2d7;padding:8px 10px;display:flex;flex-direction:column;gap:6px;flex-shrink:0;}
|
||
.mob-btn-full{font-size:9px;font-weight:600;padding:7px 0;border-radius:var(--radius-sm);text-align:center;width:100%;}
|
||
.mob-btn-ghost-full{border:1px solid var(--color-border);color:var(--color-text);background:#fff;}
|
||
.mob-btn-primary-full{background:var(--navy);color:#fff;border:none;}
|
||
|
||
/* ── Delete dialog overlay ── */
|
||
.modal-scrim{position:relative;flex:1;display:flex;align-items:center;justify-content:center;background:rgba(28,28,24,.45);}
|
||
.modal-dialog{background:#fff;border-radius:var(--radius-lg);box-shadow:var(--shadow-overlay);width:300px;padding:24px 20px;display:flex;flex-direction:column;gap:14px;}
|
||
.modal-title{font-family:var(--font-display);font-size:14px;font-weight:500;color:var(--color-text);letter-spacing:-.01em;}
|
||
.modal-body{font-size:9px;color:var(--color-text-muted);line-height:1.65;}
|
||
.modal-actions{display:flex;gap:8px;justify-content:flex-end;}
|
||
|
||
/* ── Route/component table ── */
|
||
.routes-table{width:100%;border-collapse:collapse;font-size:12px;}
|
||
.routes-table th{font-family:var(--font-mono);font-size:10px;font-weight:500;text-align:left;color:var(--color-text-muted);padding:7px 10px;border-bottom:2px solid var(--color-border);}
|
||
.routes-table td{padding:7px 10px;border-bottom:1px solid var(--color-subtle);vertical-align:top;line-height:1.5;}
|
||
.routes-table td code{font-family:var(--font-mono);font-size:10px;color:var(--navy);background:rgba(1,40,81,.06);padding:1px 5px;border-radius:2px;}
|
||
|
||
/* ── Desk editor full-height helper ── */
|
||
.desk-editor{display:flex;flex-direction:column;height:560px;}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="doc">
|
||
|
||
<!-- ════════════════════════════════════════
|
||
DOC HEADER
|
||
════════════════════════════════════════ -->
|
||
<div class="doc-header">
|
||
<div>
|
||
<h1>Geschichten — Writer Journey</h1>
|
||
<p>Erstellung, Bearbeitung und Veröffentlichung von Geschichten durch BLOG_WRITERs. Routen /geschichten/new und /geschichten/[id]/edit mit geteiltem Layout: Texteditor links, Metadaten-Sidebar rechts.</p>
|
||
</div>
|
||
<div class="doc-meta">
|
||
<div><span class="pill pill-g">Final Spec</span></div>
|
||
<div>2026-05-02</div>
|
||
<div>@leonievoss</div>
|
||
<div style="margin-top:4px;font-size:10px;">Issue #381</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════════════════════════════════════
|
||
JOURNEY HEADER
|
||
════════════════════════════════════════ -->
|
||
<div class="jh jh-g">
|
||
<div class="jn">W</div>
|
||
<div>
|
||
<h2>Writer Journey</h2>
|
||
<p>BLOG_WRITERs erstellen und pflegen Geschichten über mehrere Sitzungen, verknüpfen historische Personen und Dokumente und entscheiden selbst über Veröffentlichung.</p>
|
||
<div class="fl">/geschichten/new · /geschichten/[id]/edit</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════════════════════════════════════
|
||
SECTION: KONZEPT
|
||
════════════════════════════════════════ -->
|
||
<div class="section">
|
||
<div class="section-title">Konzept</div>
|
||
|
||
<p class="prose">Geschichten werden in dedizierten Vollseiten-Routen verfasst – kein Inline-Edit, kein Modal. Dadurch hat der Writer maximale Konzentration auf den Text und alle Metadaten in einer einzigen Ansicht.</p>
|
||
|
||
<p class="prose">Das Desktop-Layout teilt die Seite im Verhältnis 70/30: links der Fließtext-Editor (Titel + Werkzeugleiste + Body), rechts eine schmale Sidebar für Personen-Verlinkung, Dokumenten-Anhänge und Status. Beide Spalten scrollen unabhängig voneinander.</p>
|
||
|
||
<p class="prose">Die Speicherleiste am unteren Rand ist sticky und verändert ihre Aktionen je nach aktuellem Status der Geschichte: Im Zustand ENTWURF bietet sie "Entwurf speichern" und "Veröffentlichen". Im Zustand VERÖFFENTLICHT bietet sie "Speichern" (live) und "Zurück zu Entwurf" (Retract). Ein "Löschen"-Link erscheint in der Topbar nur dann, wenn die Geschichte bereits existiert (kein Löschen beim Anlegen einer neuen Geschichte).</p>
|
||
|
||
<p class="prose">PersonMultiSelect und ein Dokument-Suche-Typeahead (beide bereits im Projekt als etablierte Patterns vorhanden) übernehmen die Verknüpfung von historischen Personen und Dokumenten. Für den Rich-Text-Body genügt ein minimaler contenteditable-Ansatz mit execCommand für Fett, Kursiv und Absätze – keine externe Bibliothek nötig für den MVP.</p>
|
||
</div>
|
||
|
||
<!-- ════════════════════════════════════════
|
||
SCREEN W-1: NEUER ENTWURF — DESKTOP
|
||
════════════════════════════════════════ -->
|
||
<div class="section">
|
||
<div class="section-title">Screens</div>
|
||
|
||
<div class="scr">
|
||
<div class="scr-head">
|
||
<h3>W-1 — /geschichten/new · Neuer Entwurf</h3>
|
||
<span class="scr-id">US-BLOG-001</span>
|
||
</div>
|
||
<div class="scr-desc">Leeres Editor-Layout für eine neue Geschichte. Kein "Löschen"-Link (Geschichte existiert noch nicht). Save-Bar im ENTWURF-Modus mit zwei Aktionen.</div>
|
||
<div class="scr-var"><strong>Desktop</strong> · 1040 px · min-height 560 px · Split 70/30</div>
|
||
|
||
<div class="previews">
|
||
<div class="prev-col">
|
||
<span class="bp-lbl">Desktop — 1040 px</span>
|
||
<div class="desk desk-editor">
|
||
<!-- Nav -->
|
||
<div class="fa-nav">
|
||
<span class="fa-logo">FAMILIENARCHIV</span>
|
||
<span class="fa-link">Dokumente</span>
|
||
<span class="fa-link">Personen</span>
|
||
<span class="fa-link active">Geschichten</span>
|
||
<span class="fa-link">Gespräche</span>
|
||
<div class="fa-nav-r">
|
||
<div class="fa-av">MR</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Top bar -->
|
||
<div class="ed-topbar">
|
||
<div class="ed-back">←</div>
|
||
<div class="ed-title-label">Neue Geschichte</div>
|
||
<div class="ed-status-pill ed-status-draft">ENTWURF</div>
|
||
</div>
|
||
|
||
<!-- Split area -->
|
||
<div class="ed-split" style="flex:1;overflow:hidden;">
|
||
|
||
<!-- Left: editor -->
|
||
<div class="ed-left">
|
||
<input class="ed-title-input placeholder" type="text" value="" placeholder="Titel der Geschichte" readonly>
|
||
<div class="ed-sep"></div>
|
||
<div class="ed-toolbar">
|
||
<div class="ed-tool" title="Fett (Strg+B)"><b>B</b></div>
|
||
<div class="ed-tool" title="Kursiv (Strg+I)"><i>I</i></div>
|
||
<div class="ed-tool" title="Absatz">¶</div>
|
||
</div>
|
||
<textarea class="ed-body placeholder" readonly placeholder="Schreibe hier deine Geschichte…" style="min-height:280px;"></textarea>
|
||
</div>
|
||
|
||
<!-- Right: sidebar -->
|
||
<div class="ed-sidebar">
|
||
<!-- Personen -->
|
||
<div class="ed-sb-section">
|
||
<div class="ed-sb-title">Personen</div>
|
||
<div class="ed-search-row">
|
||
<span style="font-size:9px;color:var(--color-text-muted);">🔍</span>
|
||
<div class="ed-search-input">Person suchen…</div>
|
||
</div>
|
||
<div class="ed-chips-wrap"></div>
|
||
<div class="ed-hint">Welche historischen Personen kommen in dieser Geschichte vor?</div>
|
||
</div>
|
||
<div class="ed-sb-divider"></div>
|
||
|
||
<!-- Dokumente -->
|
||
<div class="ed-sb-section">
|
||
<div class="ed-sb-title">Dokumente</div>
|
||
<div class="ed-search-row">
|
||
<span style="font-size:9px;color:var(--color-text-muted);">🔍</span>
|
||
<div class="ed-search-input">Dokument suchen…</div>
|
||
</div>
|
||
<div class="ed-chips-wrap"></div>
|
||
<div class="ed-hint">Welche Briefe oder Dokumente sind Teil dieser Geschichte?</div>
|
||
</div>
|
||
<div class="ed-sb-divider"></div>
|
||
|
||
<!-- Status -->
|
||
<div class="ed-sb-section">
|
||
<div class="ed-sb-title">Status</div>
|
||
<div class="ed-status-pill ed-status-draft" style="font-size:9px;">ENTWURF</div>
|
||
<div class="ed-hint" style="margin-top:6px;">Noch nicht öffentlich sichtbar.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Save bar -->
|
||
<div class="ed-savebar">
|
||
<span class="ed-savebar-hint">Alle Änderungen werden als Entwurf gespeichert.</span>
|
||
<div class="ed-savebar-actions">
|
||
<button class="ed-btn-ghost">Entwurf speichern</button>
|
||
<button class="ed-btn-primary">Veröffentlichen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Validation inset -->
|
||
<div class="validation-inset">
|
||
<div class="validation-inset-label">VALIDIERUNGS-STATE — Titel leer beim Speichern</div>
|
||
<input class="vi-field" type="text" placeholder="Titel der Geschichte" readonly>
|
||
<div class="vi-err">● Bitte gib einen Titel ein.</div>
|
||
</div>
|
||
|
||
<!-- impl-ref -->
|
||
<div class="agent" style="margin-top:20px;">
|
||
<h4>impl-ref · W-1</h4>
|
||
<table class="at">
|
||
<thead>
|
||
<tr><th>Element</th><th>Wert / Klassen</th><th>Anmerkung</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="grp"><td colspan="3">Layout</td></tr>
|
||
<tr><td>Seiten-Layout</td><td><code>flex flex-col h-screen</code></td><td>Feste Höhe, kein Scrollen der Hülle</td></tr>
|
||
<tr><td>Editor-Split</td><td><code>flex flex-1 overflow-hidden</code></td><td>Beide Panels scrollen eigenständig</td></tr>
|
||
<tr><td>Linkes Panel</td><td><code>flex-1 flex flex-col p-5 overflow-y-auto</code></td><td>Nimmt restliche Breite</td></tr>
|
||
<tr><td>Rechte Sidebar</td><td><code>w-[280px] shrink-0 border-l border-brand-sand bg-white p-4 overflow-y-auto</code></td><td>Feste Breite 280 px</td></tr>
|
||
<tr class="grp"><td colspan="3">Editor-Elemente</td></tr>
|
||
<tr><td>Titel-Input</td><td><code>w-full font-serif text-[22px] text-ink placeholder-ink-3 border-0 border-b border-transparent focus:border-brand-mint focus:outline-none pb-2 mb-4</code></td><td>Kein Rahmen, nur Unterstrich bei Fokus</td></tr>
|
||
<tr><td>Toolbar-Button</td><td><code>w-7 h-7 flex items-center justify-center rounded border border-line text-xs font-bold text-ink hover:bg-muted</code></td><td>28 × 28 px, je B / I / ¶</td></tr>
|
||
<tr><td>Body-Textarea</td><td><code>flex-1 w-full font-serif text-[13px] text-ink resize-none focus:outline-none min-h-[280px]</code></td><td>contenteditable oder textarea MVP</td></tr>
|
||
<tr class="grp"><td colspan="3">Speicherleiste</td></tr>
|
||
<tr><td>Save-Bar</td><td><code>sticky bottom-0 border-t border-brand-sand bg-white px-5 py-3 flex items-center justify-between</code></td><td>Immer sichtbar, sticky</td></tr>
|
||
<tr><td>"Entwurf speichern"</td><td><code>rounded border border-line px-4 py-2 text-sm font-medium text-ink hover:bg-muted</code></td><td>Ghost-Button</td></tr>
|
||
<tr><td>"Veröffentlichen"</td><td><code>rounded bg-primary text-primary-fg px-4 py-2 text-sm font-medium hover:bg-primary/90</code></td><td>Navy-Primär-Button</td></tr>
|
||
<tr class="grp"><td colspan="3">Komponenten</td></tr>
|
||
<tr><td>PersonMultiSelect</td><td>Wiederverwendung <code>$lib/components/PersonMultiSelect.svelte</code></td><td>Identisches Pattern wie Dokument-Bearbeitung</td></tr>
|
||
<tr><td>Dokument-Typeahead</td><td>Neue Komponente <code>$lib/components/DocumentTypeahead.svelte</code></td><td>GET /api/documents?search=; Chips mit Titel + Datum</td></tr>
|
||
<tr><td>Validierung Titel</td><td>Rotes <code>border-b border-red-500</code> + Fehlertext unter dem Input</td><td>Nur beim Submit-Versuch auslösen</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════════════════════════════════════
|
||
SCREEN W-2: GESPEICHERTER ENTWURF MIT ANHÄNGEN
|
||
════════════════════════════════════════ -->
|
||
<div class="scr">
|
||
<div class="scr-head">
|
||
<h3>W-2 — Entwurf mit Personen und Dokumenten verknüpft</h3>
|
||
<span class="scr-id">US-BLOG-002</span>
|
||
</div>
|
||
<div class="scr-desc">Gleiche Layout-Struktur wie W-1, aber mit ausgefülltem Titel, Fließtext und verknüpften Personen und Dokumenten. Status bleibt ENTWURF.</div>
|
||
<div class="scr-var"><strong>Desktop only</strong> · Mobile-Ansicht siehe W-4</div>
|
||
|
||
<div class="previews">
|
||
<div class="prev-col">
|
||
<span class="bp-lbl">Desktop — 1040 px</span>
|
||
<div class="desk desk-editor">
|
||
<div class="fa-nav">
|
||
<span class="fa-logo">FAMILIENARCHIV</span>
|
||
<span class="fa-link">Dokumente</span>
|
||
<span class="fa-link">Personen</span>
|
||
<span class="fa-link active">Geschichten</span>
|
||
<span class="fa-link">Gespräche</span>
|
||
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
|
||
</div>
|
||
|
||
<div class="ed-topbar">
|
||
<div class="ed-back">←</div>
|
||
<div class="ed-title-label">Der Sommer in Breslau</div>
|
||
<div class="ed-status-pill ed-status-draft">ENTWURF</div>
|
||
</div>
|
||
|
||
<div class="ed-split" style="flex:1;overflow:hidden;">
|
||
<div class="ed-left">
|
||
<input class="ed-title-input" type="text" value="Der Sommer in Breslau" readonly style="font-style:normal;color:var(--color-text);">
|
||
<div class="ed-sep"></div>
|
||
<div class="ed-toolbar">
|
||
<div class="ed-tool"><b>B</b></div>
|
||
<div class="ed-tool"><i>I</i></div>
|
||
<div class="ed-tool">¶</div>
|
||
</div>
|
||
<div class="ed-body" style="font-style:normal;color:var(--color-text);">
|
||
<p style="margin-bottom:8px;">Es war der Sommer 1927, als Franz Raddatz zum ersten Mal nach Breslau reiste. Die Briefe, die er seiner Frau Emma in diesen Wochen schickte, zeugen von einer tiefen Heimweh und gleichzeitig einer neuen Faszination für die lebhafte Stadt an der Oder.</p>
|
||
<p style="margin-bottom:8px;">Die Kinder warteten zu Hause in Lauban. Emma hielt die Familie zusammen, wie sie es immer getan hatte — mit Ruhe und einer unerschütterlichen Zuversicht, die aus jedem Satz ihrer Antwortbriefe sprach.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ed-sidebar">
|
||
<div class="ed-sb-section">
|
||
<div class="ed-sb-title">Personen</div>
|
||
<div class="ed-search-row">
|
||
<span style="font-size:9px;color:var(--color-text-muted);">🔍</span>
|
||
<div class="ed-search-input">Person suchen…</div>
|
||
</div>
|
||
<div class="ed-chips-wrap" style="margin-top:4px;">
|
||
<span class="ed-chip">Franz Raddatz <span class="ed-chip-x">×</span></span>
|
||
<span class="ed-chip">Emma Müller <span class="ed-chip-x">×</span></span>
|
||
</div>
|
||
<div class="ed-hint" style="margin-top:4px;">2 Personen verknüpft</div>
|
||
</div>
|
||
<div class="ed-sb-divider"></div>
|
||
|
||
<div class="ed-sb-section">
|
||
<div class="ed-sb-title">Dokumente</div>
|
||
<div class="ed-search-row">
|
||
<span style="font-size:9px;color:var(--color-text-muted);">🔍</span>
|
||
<div class="ed-search-input">Dokument suchen…</div>
|
||
</div>
|
||
<div class="ed-chips-wrap" style="margin-top:4px;">
|
||
<span class="ed-chip doc-chip">Brief vom 12. Juli 1938 <span class="ed-chip-x" style="color:var(--blue-dark);">×</span></span>
|
||
</div>
|
||
<div class="ed-hint" style="margin-top:4px;">1 Dokument verknüpft</div>
|
||
</div>
|
||
<div class="ed-sb-divider"></div>
|
||
|
||
<div class="ed-sb-section">
|
||
<div class="ed-sb-title">Status</div>
|
||
<div class="ed-status-pill ed-status-draft" style="font-size:9px;">ENTWURF</div>
|
||
<div class="ed-hint" style="margin-top:6px;">Noch nicht öffentlich sichtbar.</div>
|
||
</div>
|
||
|
||
<div class="ed-sb-section" style="border-top:1px solid #e4e2d7;">
|
||
<div class="ed-sb-title">Zuletzt gespeichert</div>
|
||
<div style="font-size:8px;color:var(--color-text-muted);">Heute, 14:32 Uhr</div>
|
||
<div style="font-size:8px;color:var(--color-text-muted);margin-top:2px;">von Marcel Raddatz</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ed-savebar">
|
||
<span class="ed-savebar-hint">Alle Änderungen werden als Entwurf gespeichert.</span>
|
||
<div class="ed-savebar-actions">
|
||
<button class="ed-btn-ghost">Entwurf speichern</button>
|
||
<button class="ed-btn-primary">Veröffentlichen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════════════════════════════════════
|
||
SCREEN W-3: VERÖFFENTLICHTE GESCHICHTE BEARBEITEN
|
||
════════════════════════════════════════ -->
|
||
<div class="scr">
|
||
<div class="scr-head">
|
||
<h3>W-3 — Veröffentlichte Geschichte bearbeiten /geschichten/[id]/edit</h3>
|
||
<span class="scr-id">US-BLOG-003</span>
|
||
</div>
|
||
<div class="scr-desc">Gleiche Layout-Struktur, aber Status VERÖFFENTLICHT. Die Speicherleiste zeigt andere Aktionen. Der "Löschen"-Link erscheint in der Topbar (Geschichte existiert bereits). Änderungen sind sofort live.</div>
|
||
<div class="scr-var"><strong>Desktop</strong> · Publish-Flow ist umgekehrt: Retract statt Publish</div>
|
||
|
||
<div class="previews">
|
||
<div class="prev-col">
|
||
<span class="bp-lbl">Desktop — 1040 px</span>
|
||
<div class="desk desk-editor">
|
||
<div class="fa-nav">
|
||
<span class="fa-logo">FAMILIENARCHIV</span>
|
||
<span class="fa-link">Dokumente</span>
|
||
<span class="fa-link">Personen</span>
|
||
<span class="fa-link active">Geschichten</span>
|
||
<span class="fa-link">Gespräche</span>
|
||
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
|
||
</div>
|
||
|
||
<div class="ed-topbar">
|
||
<div class="ed-back">←</div>
|
||
<div class="ed-title-label">Der Sommer in Breslau</div>
|
||
<div class="ed-status-pill ed-status-pub">VERÖFFENTLICHT</div>
|
||
<a class="ed-delete-link">Löschen</a>
|
||
</div>
|
||
|
||
<div class="ed-split" style="flex:1;overflow:hidden;">
|
||
<div class="ed-left">
|
||
<input class="ed-title-input" type="text" value="Der Sommer in Breslau" readonly style="color:var(--color-text);">
|
||
<div class="ed-sep"></div>
|
||
<div class="ed-toolbar">
|
||
<div class="ed-tool"><b>B</b></div>
|
||
<div class="ed-tool"><i>I</i></div>
|
||
<div class="ed-tool">¶</div>
|
||
</div>
|
||
<div class="ed-body" style="color:var(--color-text);">
|
||
<p style="margin-bottom:8px;">Es war der Sommer 1927, als Franz Raddatz zum ersten Mal nach Breslau reiste. Die Briefe, die er seiner Frau Emma in diesen Wochen schickte, zeugen von einer tiefen Heimweh und gleichzeitig einer neuen Faszination für die lebhafte Stadt an der Oder.</p>
|
||
<p style="margin-bottom:8px;">Die Kinder warteten zu Hause in Lauban. Emma hielt die Familie zusammen, wie sie es immer getan hatte — mit Ruhe und einer unerschütterlichen Zuversicht, die aus jedem Satz ihrer Antwortbriefe sprach.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ed-sidebar">
|
||
<div class="ed-sb-section">
|
||
<div class="ed-sb-title">Personen</div>
|
||
<div class="ed-search-row">
|
||
<span style="font-size:9px;color:var(--color-text-muted);">🔍</span>
|
||
<div class="ed-search-input">Person suchen…</div>
|
||
</div>
|
||
<div class="ed-chips-wrap" style="margin-top:4px;">
|
||
<span class="ed-chip">Franz Raddatz <span class="ed-chip-x">×</span></span>
|
||
<span class="ed-chip">Emma Müller <span class="ed-chip-x">×</span></span>
|
||
</div>
|
||
</div>
|
||
<div class="ed-sb-divider"></div>
|
||
|
||
<div class="ed-sb-section">
|
||
<div class="ed-sb-title">Dokumente</div>
|
||
<div class="ed-search-row">
|
||
<span style="font-size:9px;color:var(--color-text-muted);">🔍</span>
|
||
<div class="ed-search-input">Dokument suchen…</div>
|
||
</div>
|
||
<div class="ed-chips-wrap" style="margin-top:4px;">
|
||
<span class="ed-chip doc-chip">Brief vom 12. Juli 1938 <span class="ed-chip-x" style="color:var(--blue-dark);">×</span></span>
|
||
</div>
|
||
</div>
|
||
<div class="ed-sb-divider"></div>
|
||
|
||
<div class="ed-sb-section">
|
||
<div class="ed-sb-title">Status</div>
|
||
<div class="ed-status-pill ed-status-pub" style="font-size:9px;">VERÖFFENTLICHT</div>
|
||
<div class="ed-hint" style="margin-top:6px;color:var(--green-dark);">Öffentlich sichtbar für alle Leser.</div>
|
||
</div>
|
||
|
||
<div class="ed-sb-section" style="border-top:1px solid #e4e2d7;">
|
||
<div class="ed-sb-title">Veröffentlicht am</div>
|
||
<div style="font-size:8px;color:var(--color-text-muted);">28. April 2026</div>
|
||
<div style="font-size:8px;color:var(--color-text-muted);margin-top:2px;">von Marcel Raddatz</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ed-savebar">
|
||
<span class="ed-savebar-hint">Änderungen sind sofort live.</span>
|
||
<div class="ed-savebar-actions">
|
||
<button class="ed-btn-ghost retract">Zurück zu Entwurf</button>
|
||
<button class="ed-btn-primary">Speichern</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="agent" style="margin-top:20px;">
|
||
<h4>impl-ref · W-3 — Veröffentlicht-Zustand</h4>
|
||
<table class="at">
|
||
<thead>
|
||
<tr><th>Element</th><th>Wert / Klassen</th><th>Anmerkung</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>VERÖFFENTLICHT-Badge</td><td><code>rounded-full bg-green-100 text-green-800 text-[10px] font-bold px-2 py-0.5 uppercase tracking-wide</code></td><td>Grüne Pill, kein Rahmen</td></tr>
|
||
<tr><td>"Speichern"-Button</td><td><code>rounded bg-primary text-primary-fg px-4 py-2 text-sm font-medium</code></td><td>Immer aktiv, speichert sofort live</td></tr>
|
||
<tr><td>"Zurück zu Entwurf"</td><td><code>rounded border border-line px-4 py-2 text-sm font-medium text-amber-700 hover:bg-amber-50</code></td><td>Setzt status=DRAFT; Bestätigung optional</td></tr>
|
||
<tr><td>"Löschen"-Link</td><td><code>text-sm text-red-600 font-medium hover:underline</code></td><td>Nur sichtbar wenn Geschichte existiert; öffnet Bestätigungs-Dialog</td></tr>
|
||
<tr><td>Save-Bar-Hinweis</td><td>"Änderungen sind sofort live." (xs, muted)</td><td>Ersetzt den ENTWURF-Hinweis</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════════════════════════════════════
|
||
SCREEN W-4: MOBILE EDITOR
|
||
════════════════════════════════════════ -->
|
||
<div class="scr">
|
||
<div class="scr-head">
|
||
<h3>W-4 — Mobile Ansicht /geschichten/new</h3>
|
||
<span class="scr-id">US-BLOG-001 · 320 px</span>
|
||
</div>
|
||
<div class="scr-desc">Gestapeltes Layout ohne horizontale Teilung. Sidebar-Inhalte werden in einem ausklappbaren Bereich unterhalb des Body-Editors angezeigt. Speicherleiste mit vertikal gestapelten Buttons.</div>
|
||
<div class="scr-var"><strong>Phone</strong> · 320 px · Sidebar kollabiert; kein Split</div>
|
||
|
||
<div class="previews">
|
||
<div class="prev-col">
|
||
<span class="bp-lbl">Phone — 320 px</span>
|
||
<div class="phone" style="min-height:580px;">
|
||
<!-- Status bar -->
|
||
<div class="pst"><b>9:41</b><span>●●●●</span></div>
|
||
|
||
<!-- FA nav -->
|
||
<div class="fa-nav" style="height:28px;">
|
||
<span class="fa-logo" style="font-size:6px;">FAMILIENARCHIV</span>
|
||
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
|
||
</div>
|
||
|
||
<div class="pb" style="background:var(--color-page);">
|
||
<!-- Mobile top bar -->
|
||
<div class="mob-topbar">
|
||
<div class="mob-back">←</div>
|
||
<div class="mob-title-label">Neue Geschichte</div>
|
||
<div class="ed-status-pill ed-status-draft" style="font-size:7px;padding:2px 6px;">ENTWURF</div>
|
||
</div>
|
||
|
||
<!-- Scrollable body -->
|
||
<div class="mob-body">
|
||
<input class="mob-title-input" type="text" placeholder="Titel der Geschichte" readonly>
|
||
<div class="mob-toolbar">
|
||
<div class="mob-tool"><b>B</b></div>
|
||
<div class="mob-tool"><i>I</i></div>
|
||
<div class="mob-tool">¶</div>
|
||
</div>
|
||
<div class="mob-ed-body">Schreibe hier deine Geschichte…</div>
|
||
|
||
<!-- Collapsible sidebar on mobile -->
|
||
<div class="mob-collapsible">
|
||
<div class="mob-coll-hdr">
|
||
Personen & Dokumente
|
||
<span class="mob-coll-chevron">›</span>
|
||
</div>
|
||
<!-- collapsed by default; below shows expanded state for spec clarity -->
|
||
<div style="padding:8px 10px;border-top:1px solid #e4e2d7;display:flex;flex-direction:column;gap:8px;">
|
||
<div>
|
||
<div style="font-size:8px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:5px;">Personen</div>
|
||
<div class="ed-search-row" style="margin-bottom:0;">
|
||
<span style="font-size:9px;color:var(--color-text-muted);">🔍</span>
|
||
<div class="ed-search-input">Person suchen…</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="font-size:8px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted);margin-bottom:5px;">Dokumente</div>
|
||
<div class="ed-search-row" style="margin-bottom:0;">
|
||
<span style="font-size:9px;color:var(--color-text-muted);">🔍</span>
|
||
<div class="ed-search-input">Dokument suchen…</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Mobile save bar: stacked buttons -->
|
||
<div class="mob-savebar">
|
||
<button class="mob-btn-full mob-btn-ghost-full">Entwurf speichern</button>
|
||
<button class="mob-btn-full mob-btn-primary-full">Veröffentlichen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="agent" style="margin-top:20px;">
|
||
<h4>impl-ref · W-4 — Mobile</h4>
|
||
<table class="at">
|
||
<thead>
|
||
<tr><th>Element</th><th>Wert / Klassen</th><th>Anmerkung</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>Editor-Layout (Mobile)</td><td><code>flex flex-col h-screen</code></td><td>Kein horizontaler Split</td></tr>
|
||
<tr><td>Sidebar-Collapsible</td><td><code><details></code> oder Svelte <code>bind:open</code></td><td>Standardmäßig geschlossen; Chevron dreht sich</td></tr>
|
||
<tr><td>Save-Bar Mobile</td><td><code>flex flex-col gap-2 p-3 bg-white border-t</code></td><td>Buttons vertikal gestapelt, volle Breite</td></tr>
|
||
<tr><td>"Entwurf speichern" Mobile</td><td><code>w-full rounded border border-line py-2 text-sm font-medium text-ink</code></td><td>Ghost, volle Breite</td></tr>
|
||
<tr><td>"Veröffentlichen" Mobile</td><td><code>w-full rounded bg-primary text-primary-fg py-2 text-sm font-medium</code></td><td>Primary, volle Breite, oben</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════════════════════════════════════
|
||
SCREEN W-5: LÖSCHEN-BESTÄTIGUNG
|
||
════════════════════════════════════════ -->
|
||
<div class="scr">
|
||
<div class="scr-head">
|
||
<h3>W-5 — Lösch-Bestätigung (Dialog)</h3>
|
||
<span class="scr-id">US-BLOG-006</span>
|
||
</div>
|
||
<div class="scr-desc">Modal-Dialog über dem Editor (W-3-Zustand: veröffentlichte Geschichte). Dunkles Scrim überlagert den Editor. Dialog liegt zentriert darüber. Kein custom Dialog — Wiederverwendung des bestehenden confirm-Service.</div>
|
||
<div class="scr-var"><strong>Desktop</strong> · Ausgelöst durch Klick auf "Löschen" in der Topbar</div>
|
||
|
||
<div class="previews">
|
||
<div class="prev-col">
|
||
<span class="bp-lbl">Desktop — 1040 px (mit Modal)</span>
|
||
<div class="desk desk-editor" style="position:relative;">
|
||
<!-- Underlying editor (W-3 state, dimmed) -->
|
||
<div class="fa-nav">
|
||
<span class="fa-logo">FAMILIENARCHIV</span>
|
||
<span class="fa-link">Dokumente</span>
|
||
<span class="fa-link">Personen</span>
|
||
<span class="fa-link active">Geschichten</span>
|
||
<span class="fa-link">Gespräche</span>
|
||
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
|
||
</div>
|
||
<div class="ed-topbar" style="opacity:.4;">
|
||
<div class="ed-back">←</div>
|
||
<div class="ed-title-label">Der Sommer in Breslau</div>
|
||
<div class="ed-status-pill ed-status-pub">VERÖFFENTLICHT</div>
|
||
<a class="ed-delete-link">Löschen</a>
|
||
</div>
|
||
<div class="ed-split" style="flex:1;overflow:hidden;opacity:.4;">
|
||
<div class="ed-left">
|
||
<input class="ed-title-input" type="text" value="Der Sommer in Breslau" readonly style="color:var(--color-text);">
|
||
<div class="ed-sep"></div>
|
||
<div class="ed-body" style="color:var(--color-text);">
|
||
<p style="margin-bottom:8px;">Es war der Sommer 1927, als Franz Raddatz zum ersten Mal nach Breslau reiste…</p>
|
||
</div>
|
||
</div>
|
||
<div class="ed-sidebar">
|
||
<div class="ed-sb-section">
|
||
<div class="ed-sb-title">Personen</div>
|
||
<div class="ed-chips-wrap" style="margin-top:4px;">
|
||
<span class="ed-chip">Franz Raddatz</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="ed-savebar" style="opacity:.4;">
|
||
<span class="ed-savebar-hint">Änderungen sind sofort live.</span>
|
||
<div class="ed-savebar-actions">
|
||
<button class="ed-btn-ghost retract">Zurück zu Entwurf</button>
|
||
<button class="ed-btn-primary">Speichern</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Scrim + Dialog overlay -->
|
||
<div style="position:absolute;inset:0;background:rgba(28,28,24,.46);display:flex;align-items:center;justify-content:center;border-radius:var(--radius-xl);">
|
||
<div class="modal-dialog">
|
||
<div class="modal-title">Geschichte löschen?</div>
|
||
<div class="modal-body">Diese Aktion kann nicht rückgängig gemacht werden. Die Geschichte wird dauerhaft gelöscht und aus allen verlinkten Personen- und Dokumentseiten entfernt.</div>
|
||
<div class="modal-actions">
|
||
<button class="ed-btn-ghost" style="font-size:9px;padding:5px 12px;">Abbrechen</button>
|
||
<button class="ed-btn-danger" style="font-size:9px;padding:5px 12px;">Löschen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="agent" style="margin-top:20px;">
|
||
<h4>impl-ref · W-5 — Lösch-Dialog</h4>
|
||
<table class="at">
|
||
<thead>
|
||
<tr><th>Element</th><th>Wert / Anmerkung</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td>Dialog-Implementierung</td><td>Wiederverwendung <code>getConfirmService()</code> aus <code>$lib/services/confirm.svelte.js</code> — kein custom Dialog nötig</td></tr>
|
||
<tr><td>Scrim</td><td><code>fixed inset-0 bg-black/40 z-40 flex items-center justify-center</code></td></tr>
|
||
<tr><td>Dialog-Box</td><td><code>bg-white rounded-lg shadow-overlay w-[400px] p-6 flex flex-col gap-4 z-50</code></td></tr>
|
||
<tr><td>Titel</td><td><code>font-serif text-[18px] font-medium text-ink</code></td></tr>
|
||
<tr><td>"Abbrechen"</td><td>Ghost-Button — schließt Dialog, kein State-Wechsel</td></tr>
|
||
<tr><td>"Löschen"</td><td><code>rounded bg-red-600 text-white px-4 py-2 text-sm font-medium hover:bg-red-700</code> — DELETE /api/geschichten/{id}, dann redirect /geschichten</td></tr>
|
||
<tr><td>Nach Löschen</td><td>Redirect auf <code>/geschichten</code> (Index), Toast "Geschichte gelöscht"</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════════════════════════════════════
|
||
SECTION: LLM GUIDE
|
||
════════════════════════════════════════ -->
|
||
<div class="section">
|
||
<div class="section-title">Implementierungs-Guide für LLMs</div>
|
||
|
||
<p class="prose">Alle fünf Screens teilen dieselbe Svelte-Komponente <code style="font-family:var(--font-mono);font-size:12px;color:var(--navy);">GeschichteEditor.svelte</code>. Der Unterschied zwischen /geschichten/new und /geschichten/[id]/edit liegt ausschließlich in den Load-Daten und im initialen Status-Zustand.</p>
|
||
|
||
<!-- Route table -->
|
||
<table class="routes-table" style="margin-bottom:24px;">
|
||
<thead>
|
||
<tr><th>Route</th><th>Komponente</th><th>Load-Funktion</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>/geschichten/new</code></td>
|
||
<td><code>GeschichteEditor.svelte</code></td>
|
||
<td>Kein Load nötig — leerer Zustand, status=DRAFT</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>/geschichten/[id]/edit</code></td>
|
||
<td><code>GeschichteEditor.svelte</code></td>
|
||
<td>GET /api/geschichten/{id} → Geschichte by id; wirft 404 wenn nicht gefunden</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="agent">
|
||
<h4>Technische Entscheidungen</h4>
|
||
<table class="at">
|
||
<thead>
|
||
<tr><th>Thema</th><th>Entscheidung</th><th>Begründung</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="grp"><td colspan="3">Rich-Text-Editor</td></tr>
|
||
<tr>
|
||
<td>MVP-Implementierung</td>
|
||
<td>Minimales <code>contenteditable</code> div oder <code><textarea></code> mit <code>document.execCommand</code> für B/I/¶</td>
|
||
<td>Issue #381 erfordert nur Bold, Italic, Absatzumbrüche — keine Bibliothek, kein Bundle-Overhead</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Persistenz-Format</td>
|
||
<td>HTML-String im Backend (VARCHAR / TEXT)</td>
|
||
<td>Einfachstes Format; bei Bedarf später auf Markdown oder ProseMirror JSON migrierbar</td>
|
||
</tr>
|
||
<tr class="grp"><td colspan="3">Personen & Dokumente</td></tr>
|
||
<tr>
|
||
<td>PersonMultiSelect</td>
|
||
<td>Direktes Wiederverwenden von <code>$lib/components/PersonMultiSelect.svelte</code></td>
|
||
<td>Identisches Pattern wie im Dokument-Bearbeitungsformular — kein neues Rad erfinden</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Dokument-Typeahead</td>
|
||
<td>Neue Komponente <code>$lib/components/DocumentTypeahead.svelte</code></td>
|
||
<td>GET /api/documents?search= — gleicher Aufbau wie PersonTypeahead; Chips zeigen Titel + Datum</td>
|
||
</tr>
|
||
<tr class="grp"><td colspan="3">Permissions</td></tr>
|
||
<tr>
|
||
<td>Route Guard</td>
|
||
<td>Server-seitiger Check in +page.server.ts: wenn User kein BLOG_WRITE → redirect /geschichten</td>
|
||
<td>Niemals Editor-Controls in Lese-Ansichten zeigen; Client-seitige Prüfung reicht nicht</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Autorschaft</td>
|
||
<td>Jeder BLOG_WRITER kann jede Geschichte bearbeiten; <code>author</code>-Feld ist nur Anzeige</td>
|
||
<td>Familienarchiv ist kein Blog mit privaten Drafts; Kollaboration ist erwünscht</td>
|
||
</tr>
|
||
<tr class="grp"><td colspan="3">Status-Logik</td></tr>
|
||
<tr>
|
||
<td>Publish-Action</td>
|
||
<td>PATCH /api/geschichten/{id} mit <code>{ "status": "PUBLISHED" }</code></td>
|
||
<td>Kein separater Endpunkt nötig — Status ist ein Feld des Modells</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Retract-Action</td>
|
||
<td>PATCH /api/geschichten/{id} mit <code>{ "status": "DRAFT" }</code></td>
|
||
<td>Umkehrbar; keine separate Bestätigung (anders als Löschen)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>"Löschen"-Sichtbarkeit</td>
|
||
<td>Nur sichtbar wenn <code>data.geschichte !== null</code> (d.h. Edit-Route, nicht New-Route)</td>
|
||
<td>Kein Löschen für nicht-existierende Geschichten</td>
|
||
</tr>
|
||
<tr class="grp"><td colspan="3">Löschen</td></tr>
|
||
<tr>
|
||
<td>Bestätigungs-Dialog</td>
|
||
<td>Wiederverwendung <code>getConfirmService()</code> aus <code>$lib/services/confirm.svelte.js</code></td>
|
||
<td>Kein custom Dialog; bereits im Projekt vorhanden</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Nach DELETE</td>
|
||
<td>DELETE /api/geschichten/{id} → 204 → redirect /geschichten + Toast</td>
|
||
<td>Standard-Muster wie bei Personen und Dokumenten</td>
|
||
</tr>
|
||
<tr class="grp"><td colspan="3">Mobile Responsive</td></tr>
|
||
<tr>
|
||
<td>Breakpoint</td>
|
||
<td>Unter 640 px (sm): Split aufheben, Sidebar als Collapsible</td>
|
||
<td>Transcribers (60+) auf Laptop/Tablet; Reader (jünger) auf Phones — Responsive für Writer ist Minor</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Collapsible-Trigger</td>
|
||
<td>"Personen & Dokumente" mit Chevron; standardmäßig geschlossen</td>
|
||
<td>Body-Editor hat Priorität; Metadaten sind sekundär auf kleinen Bildschirmen</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /.doc -->
|
||
</body>
|
||
</html>
|