All checks were successful
CI / Unit & Component Tests (push) Successful in 3m23s
CI / OCR Service Tests (push) Successful in 24s
CI / Backend Unit Tests (push) Successful in 4m2s
CI / fail2ban Regex (push) Successful in 46s
CI / Semgrep Security Scan (push) Successful in 21s
CI / Compose Bucket Idempotency (push) Successful in 1m8s
nightly / deploy-staging (push) Successful in 2m44s
lesereisen-reader-spec.html — Issue #752 LR-0 type selector on /geschichten/new LR-1 REISE badge on the list LR-2 Journey reader (ordered cards, interlude asides, no position numbers) lesereisen-editor-spec.html — Issue #753 LE-1 empty JourneyEditor layout LE-2 editor with mixed items (documents + interludes, drag handles) LE-3 inline note-editing state LE-4 mobile layout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
728 lines
52 KiB
HTML
728 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>Lesereisen — Reader-Integration · 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-o{background:var(--orange-tint);color:var(--orange-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-o{background:var(--orange-tint);border:1px solid #F0C99A;}
|
||
.jh-o .jn{color:var(--orange);}
|
||
.jh-o p,.jh-o .fl{color:var(--orange-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;}
|
||
.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);}
|
||
.m-nav{height:26px;background:var(--navy);display:flex;align-items:center;padding:0 10px;gap:6px;flex-shrink:0;}
|
||
.m-logo{font-size:6px;font-weight:900;color:#fff;letter-spacing:.7px;border-bottom:1.5px solid var(--mint);padding-bottom:1px;}
|
||
.m-nav-r{margin-left:auto;display:flex;gap:4px;align-items:center;}
|
||
.m-av{width:14px;height:14px;background:rgba(255,255,255,.1);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:4.5px;font-weight:800;color:rgba(255,255,255,.5);}
|
||
.m-ham{display:flex;flex-direction:column;gap:2px;width:12px;}
|
||
.m-ham span{height:1.5px;background:rgba(255,255,255,.6);border-radius:1px;}
|
||
|
||
/* ── impl-ref table ── */
|
||
.agent{background:var(--color-text);color:#E8E8E2;padding:24px;border-radius:var(--radius-lg);margin-top:20px;}
|
||
.agent h4{font-size:9px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:#5A5A55;margin-bottom:12px;}
|
||
.at{width:100%;border-collapse:collapse;font-family:var(--font-mono);font-size:10px;}
|
||
.at thead tr{border-bottom:1px solid #2A2A26;}
|
||
.at th{text-align:left;padding:6px 10px;font-size:8px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:#5A5A55;font-family:var(--font-sans);}
|
||
.at td{padding:5px 10px;border-bottom:1px solid #1E1E1A;vertical-align:top;line-height:1.5;}
|
||
.at tr:last-child td{border-bottom:none;}
|
||
.at td:first-child{color:#7A7A72;}
|
||
.at td:nth-child(2){color:#E8E8E2;font-weight:500;}
|
||
.at td:nth-child(3){color:#5A5A55;}
|
||
.at .grp td{padding-top:14px;font-family:var(--font-sans);font-size:8px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:#3A3A36;}
|
||
|
||
/* ── LLM guide ── */
|
||
.llm{background:var(--color-page);border:2px solid var(--navy);border-radius:var(--radius-xl);padding:32px 40px;margin-top:64px;}
|
||
.llm h2{font-family:var(--font-display);font-size:22px;font-weight:500;letter-spacing:-.02em;margin-bottom:8px;color:var(--navy);}
|
||
.llm h3{font-size:14px;font-weight:600;margin:20px 0 8px;}
|
||
.llm h4{font-size:12px;font-weight:600;margin:14px 0 6px;color:var(--color-text-muted);}
|
||
.llm p,.llm li{font-size:13px;color:var(--color-text-muted);line-height:1.65;}
|
||
.llm ul,.llm ol{padding-left:20px;margin-bottom:12px;}
|
||
.llm li{margin-bottom:4px;}
|
||
.llm code{font-family:var(--font-mono);font-size:11px;background:var(--color-surface);padding:1px 5px;border-radius:3px;}
|
||
.llm table{width:100%;border-collapse:collapse;margin:12px 0;font-size:12px;}
|
||
.llm th,.llm td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--color-border);}
|
||
.llm th{font-weight:500;color:var(--color-text);font-size:11px;text-transform:uppercase;letter-spacing:.05em;}
|
||
.llm td{color:var(--color-text-muted);}
|
||
|
||
/* ── List row (re-used from reader-journey spec) ── */
|
||
.g-list-card{background:#fff;border:1px solid #E4E2D7;border-radius:4px;box-shadow:var(--shadow-card);overflow:hidden;}
|
||
.g-row{display:flex;gap:0;border-bottom:1px solid #F0EFE9;}
|
||
.g-row:last-child{border-bottom:none;}
|
||
.g-meta{width:88px;flex-shrink:0;padding:10px 10px 10px 12px;display:flex;flex-direction:column;gap:3px;border-right:1px solid #F0EFE9;}
|
||
.g-content{padding:10px 14px 10px 12px;flex:1;min-width:0;}
|
||
.g-av{width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:7px;font-weight:800;color:#fff;flex-shrink:0;margin-bottom:3px;}
|
||
.av-navy{background:#012851;} .av-purple{background:#534AB7;} .av-teal{background:#0E9488;}
|
||
.g-author{font-size:7px;font-weight:700;color:#1C1C18;line-height:1.3;}
|
||
.g-date{font-size:6.5px;color:#6B6A63;}
|
||
.g-chip{display:inline-flex;align-items:center;gap:2px;padding:1px 5px;background:#F5F4EE;border:1px solid #D8D7D0;border-radius:10px;font-size:6px;font-weight:500;color:#1C1C18;margin-top:2px;max-width:76px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
||
.g-title{font-family:Georgia,serif;font-size:11px;color:#012851;line-height:1.4;margin-bottom:2px;}
|
||
.g-excerpt{font-size:7.5px;color:#6B6A63;line-height:1.55;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
|
||
.g-filters{display:flex;gap:5px;align-items:center;padding:8px 12px;background:var(--color-page);border-bottom:1px solid #EDECEA;flex-wrap:wrap;}
|
||
.g-pill{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:6.5px;font-weight:700;border:1px solid #D8D7D0;color:#6B6A63;background:transparent;}
|
||
.g-pill.active{background:#012851;color:#fff;border-color:#012851;}
|
||
.g-page-hdr{display:flex;justify-content:space-between;align-items:center;padding:10px 14px 6px;}
|
||
.g-page-title{font-family:Georgia,serif;font-size:16px;font-weight:400;color:#012851;}
|
||
.g-new-btn{font-size:7px;font-weight:700;padding:4px 10px;border-radius:3px;background:#012851;color:#fff;border:none;display:flex;align-items:center;gap:3px;}
|
||
|
||
/* ── Journey badge in list ── */
|
||
.j-badge{display:inline-flex;align-items:center;padding:1px 5px;border-radius:3px;font-size:5.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;background:var(--orange-tint);color:var(--orange-dark);border:1px solid #F0C99A;margin-top:2px;}
|
||
|
||
/* ── Type selector cards ── */
|
||
.type-selector{display:flex;gap:12px;justify-content:center;padding:20px 24px;flex:1;align-items:center;background:#E8E7E2;}
|
||
.type-selector-inner{max-width:520px;width:100%;}
|
||
.type-selector-q{font-family:Georgia,serif;font-size:12px;font-weight:400;color:#6B6A63;text-align:center;margin-bottom:14px;}
|
||
.type-cards{display:flex;gap:10px;}
|
||
.type-card{flex:1;border:1px solid #D8D7D0;border-radius:6px;padding:12px 14px;cursor:pointer;background:#fff;display:flex;flex-direction:column;gap:5px;}
|
||
.type-card.selected{border-color:var(--orange);background:var(--orange-tint);box-shadow:0 0 0 2px rgba(232,134,42,.15);}
|
||
.type-card-icon{font-size:16px;margin-bottom:2px;}
|
||
.type-card-title{font-family:Georgia,serif;font-size:11px;font-weight:400;color:var(--navy);}
|
||
.type-card-desc{font-size:7.5px;color:#6B6A63;line-height:1.55;}
|
||
.type-card-check{width:14px;height:14px;border-radius:50%;background:var(--orange);display:flex;align-items:center;justify-content:center;margin-top:4px;align-self:flex-end;}
|
||
.type-card-check svg{width:8px;height:8px;}
|
||
.type-next-bar{display:flex;justify-content:flex-end;padding:8px 24px;background:#fff;border-top:1px solid #E4E2D7;}
|
||
.type-next-btn{font-size:8px;font-weight:700;padding:5px 14px;border-radius:3px;background:var(--navy);color:#fff;border:none;display:flex;align-items:center;gap:3px;}
|
||
|
||
/* ── Journey reader ── */
|
||
.jr-article{background:var(--color-page);border-radius:6px;padding:16px 20px;max-width:640px;margin:0 auto;}
|
||
.jr-back{font-size:7px;color:#6B6A63;margin-bottom:10px;display:flex;align-items:center;gap:2px;}
|
||
.jr-badge{display:inline-flex;align-items:center;padding:1px 6px;border-radius:3px;font-size:6px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;background:var(--orange-tint);color:var(--orange-dark);border:1px solid #F0C99A;margin-bottom:5px;}
|
||
.jr-title{font-family:Georgia,serif;font-size:18px;font-weight:400;color:#012851;line-height:1.3;margin-bottom:8px;}
|
||
.jr-metabar{display:flex;align-items:center;gap:6px;padding-bottom:8px;border-bottom:1px solid #EDECEA;margin-bottom:10px;}
|
||
.jr-metabar-r{margin-left:auto;display:flex;align-items:center;gap:6px;}
|
||
.jr-edit-btn{font-size:6.5px;font-weight:600;padding:2px 7px;border:1px solid #D8D7D0;border-radius:3px;color:#1C1C18;background:transparent;}
|
||
.jr-intro{font-family:Georgia,serif;font-size:8.5px;line-height:1.75;color:#6B6A63;font-style:italic;margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed #EDECEA;}
|
||
|
||
/* Journey items in reader */
|
||
.jr-item{display:flex;gap:7px;margin-bottom:9px;align-items:flex-start;}
|
||
.jr-num{width:18px;height:18px;border-radius:50%;background:#012851;color:#fff;display:flex;align-items:center;justify-content:center;font-size:7px;font-weight:700;flex-shrink:0;margin-top:1px;}
|
||
.jr-card{flex:1;background:#fff;border:1px solid #E4E2D7;border-radius:4px;padding:7px 9px;}
|
||
.jr-card-title{font-family:Georgia,serif;font-size:9px;color:#012851;line-height:1.3;margin-bottom:2px;font-weight:400;}
|
||
.jr-card-meta{font-size:6.5px;color:#6B6A63;margin-bottom:5px;}
|
||
.jr-card-link{font-size:7px;font-weight:600;color:#012851;display:flex;align-items:center;gap:2px;}
|
||
.jr-annotation{margin-top:6px;padding:5px 7px;border-left:2px solid var(--mint);background:#F5F4EE;border-radius:0 3px 3px 0;}
|
||
.jr-annotation-text{font-size:7.5px;font-style:italic;color:#6B6A63;line-height:1.55;}
|
||
.jr-interlude{margin:10px 0 10px 25px;padding:7px 9px;border-left:2px solid var(--orange);background:var(--orange-tint);border-radius:0 4px 4px 0;}
|
||
.jr-interlude-text{font-size:8px;font-style:italic;color:#1C1C18;line-height:1.65;}
|
||
|
||
/* Mobile list row */
|
||
.m-row{padding:9px 10px;border-bottom:1px solid #F0EFE9;background:#fff;}
|
||
.m-row-top{display:flex;align-items:center;gap:5px;margin-bottom:3px;}
|
||
.m-author-name{font-size:7px;font-weight:700;color:#1C1C18;}
|
||
.m-date{font-size:6.5px;color:#6B6A63;margin-left:auto;}
|
||
.m-title{font-family:Georgia,serif;font-size:10px;color:#012851;line-height:1.4;margin-bottom:2px;}
|
||
.m-excerpt{font-size:7px;color:#6B6A63;line-height:1.5;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
|
||
.m-filters{display:flex;gap:4px;padding:6px 10px;background:var(--color-page);border-bottom:1px solid #EDECEA;overflow-x:auto;flex-wrap:nowrap;}
|
||
.m-filters::-webkit-scrollbar{display:none;}
|
||
|
||
/* Mobile journey reader */
|
||
.mjr-article{background:#fff;border-radius:6px;padding:12px 12px 16px;}
|
||
.mjr-back{font-size:7px;color:#6B6A63;margin-bottom:7px;display:flex;align-items:center;gap:2px;}
|
||
.mjr-badge{display:inline-flex;padding:1px 5px;border-radius:3px;font-size:5.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;background:var(--orange-tint);color:var(--orange-dark);border:1px solid #F0C99A;margin-bottom:4px;}
|
||
.mjr-title{font-family:Georgia,serif;font-size:14px;font-weight:400;color:#012851;line-height:1.3;margin-bottom:6px;}
|
||
.mjr-metabar{display:flex;align-items:center;gap:5px;padding-bottom:6px;border-bottom:1px solid #EDECEA;margin-bottom:8px;}
|
||
.mjr-intro{font-family:Georgia,serif;font-size:8px;line-height:1.7;color:#6B6A63;font-style:italic;margin-bottom:9px;padding-bottom:7px;border-bottom:1px dashed #EDECEA;}
|
||
.mjr-item{display:flex;gap:5px;margin-bottom:7px;align-items:flex-start;}
|
||
.mjr-num{width:14px;height:14px;border-radius:50%;background:#012851;color:#fff;display:flex;align-items:center;justify-content:center;font-size:6px;font-weight:700;flex-shrink:0;margin-top:1px;}
|
||
.mjr-card{flex:1;background:#F5F4EE;border:1px solid #E4E2D7;border-radius:4px;padding:5px 7px;}
|
||
.mjr-card-title{font-family:Georgia,serif;font-size:8.5px;color:#012851;line-height:1.3;margin-bottom:1px;}
|
||
.mjr-card-meta{font-size:6px;color:#6B6A63;margin-bottom:4px;}
|
||
.mjr-card-link{font-size:6.5px;font-weight:600;color:#012851;}
|
||
.mjr-interlude{margin:7px 0 7px 19px;padding:5px 7px;border-left:2px solid var(--orange);background:var(--orange-tint);border-radius:0 3px 3px 0;}
|
||
.mjr-interlude-text{font-size:7.5px;font-style:italic;color:#1C1C18;line-height:1.6;}
|
||
|
||
/* ── Editor topbar (type selector screen) ── */
|
||
.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);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;}
|
||
|
||
@media(max-width:900px){.doc{padding:24px 16px 80px;}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="doc">
|
||
|
||
<!-- ═══ DOC HEADER ═══ -->
|
||
<div class="doc-header">
|
||
<div>
|
||
<h1>Lesereisen — Reader-Integration</h1>
|
||
<p>Typauswahl bei <code>/geschichten/new</code>, Journey-Badge auf der Übersichtsliste und die neue geordnete Leseansicht auf <code>/geschichten/[id]</code> wenn <code>type === 'JOURNEY'</code>. Bestehende Story-Ansichten bleiben unverändert.</p>
|
||
</div>
|
||
<div class="doc-meta">
|
||
Familienarchiv<br/>
|
||
<span class="pill pill-o">Final Spec</span><br/>
|
||
2026-06-07 · @leonievoss<br/>
|
||
<span style="font-size:10px;margin-top:4px;display:inline-block;">Issue #752</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ JOURNEY HEADER ═══ -->
|
||
<div class="jh jh-o">
|
||
<div class="jn">R</div>
|
||
<div>
|
||
<h2>Lesereisen — Reader</h2>
|
||
<p>Alle angemeldeten Familienmitglieder können Lesereisen entdecken und in Briefsequenzen mit Kuratoren-Notizen eintauchen. BLOG_WRITERs sehen zusätzlich Bearbeiten/Löschen-Aktionen.</p>
|
||
<div class="fl">/geschichten · /geschichten/new · /geschichten/[id]</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ KONZEPT ═══ -->
|
||
<div class="section">
|
||
<div class="section-title">Konzept</div>
|
||
<p class="prose">Eine <em>Lesereise</em> ist eine <code>Geschichte</code> mit <code>type === 'JOURNEY'</code>. Ihr Kerninhalt ist eine geordnete Sequenz von Briefen (<code>JourneyItem</code>s mit <code>document_id</code>) und Zwischentexten (<code>JourneyItem</code>s ohne <code>document_id</code>). Das optionale Feld <code>body</code> dient als Einleitung/Preface.</p>
|
||
<p class="prose">Diese Spec deckt drei Änderungen ab: (1) die Typauswahl auf <code>/geschichten/new</code> als vorgelagerter Schritt, (2) das „REISE"-Badge in der Übersichtsliste, und (3) die neue Journey-Leseansicht auf der Detailseite, die den bestehenden Prosa-Body durch eine nummerierte Briefliste ersetzt.</p>
|
||
<p class="prose">Dokument-Items zeigen Titel, Datum, Sender→Empfänger und einen Link zum Brief. Optionale Kuratoren-Notizen erscheinen als Annotation mit Mint-Linker-Rand unter dem Briefeintrag. Interlude-Items (kein Dokument) erscheinen als eingerückte Absätze mit orangenem linken Rand — klar vom Dokumenttyp unterscheidbar, aber harmonisch im Lesefluss.</p>
|
||
</div>
|
||
|
||
<!-- ═══ SCREEN LR-0: TYPE SELECTOR ═══ -->
|
||
<div class="section">
|
||
<div class="section-title">Screens — Typauswahl</div>
|
||
|
||
<div class="scr">
|
||
<div class="scr-head">
|
||
<h3>LR-0 — Typauswahl /geschichten/new</h3>
|
||
<span class="scr-id">Issue #752 · LR-0</span>
|
||
</div>
|
||
<p class="scr-desc">Neuer vorgelagerter Schritt beim Erstellen einer Geschichte. Zwei Karten zur Auswahl: „Geschichte" (Prosa) und „Lesereise" (Briefsequenz). Die ausgewählte Karte wird hervorgehoben. Erst nach Auswahl wird der „Weiter"-Button aktiv. Auswahl bleibt im URL-Param erhalten (<code>?type=JOURNEY</code>).</p>
|
||
<p class="scr-var"><strong>Varianten:</strong> Keine Auswahl (Weiter-Button inaktiv) · Lesereise gewählt (hier gezeigt) · Geschichte gewählt</p>
|
||
|
||
<div class="previews">
|
||
<div class="prev-col" style="width:100%;max-width:1040px;">
|
||
<span class="bp-lbl">Desktop — 1040px · Lesereise gewählt</span>
|
||
<div class="desk" style="min-height:320px;">
|
||
<div class="fa-nav">
|
||
<span class="fa-logo">ARCHIV</span>
|
||
<span style="width:1px;height:14px;background:rgba(255,255,255,.1);margin:0 2px;"></span>
|
||
<span class="fa-link">Dokumente</span>
|
||
<span class="fa-link">Personen</span>
|
||
<span class="fa-link active">Geschichten</span>
|
||
<span class="fa-link">Chronik</span>
|
||
<div class="fa-nav-r">
|
||
<div class="fa-av" style="background:#012851;color:var(--mint);font-size:5px;font-weight:800;">MR</div>
|
||
</div>
|
||
</div>
|
||
<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>
|
||
<div class="type-selector">
|
||
<div class="type-selector-inner">
|
||
<div class="type-selector-q">Was möchtest du erstellen?</div>
|
||
<div class="type-cards">
|
||
<!-- Story card -->
|
||
<div class="type-card">
|
||
<div class="type-card-icon">✍️</div>
|
||
<div class="type-card-title">Geschichte</div>
|
||
<div class="type-card-desc">Freier Prosatext über Familienerlebnisse, Erinnerungen oder historische Einordnungen — mit verlinkten Personen und Dokumenten.</div>
|
||
</div>
|
||
<!-- Journey card (selected) -->
|
||
<div class="type-card selected">
|
||
<div class="type-card-icon">📜</div>
|
||
<div class="type-card-title">Lesereise</div>
|
||
<div class="type-card-desc">Geordnete Briefsequenz mit optionalen Kuratoren-Notizen zwischen den Briefen — für chronologische Korrespondenz-Sammlungen.</div>
|
||
<div class="type-card-check">
|
||
<svg viewBox="0 0 10 10" fill="none"><path d="M2 5l2.5 2.5L8 3" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="type-next-bar">
|
||
<button class="type-next-btn">
|
||
Weiter
|
||
<svg width="7" height="7" viewBox="0 0 10 10" fill="none"><path d="M4 2l4 3-4 3" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="agent">
|
||
<h4>impl-ref — LR-0 Typauswahl</h4>
|
||
<table class="at">
|
||
<thead><tr><th>Element</th><th>Wert</th><th>Hinweise</th></tr></thead>
|
||
<tbody>
|
||
<tr class="grp"><td colspan="3">Layout</td></tr>
|
||
<tr><td>Selector area</td><td>flex flex-1 items-center justify-center bg-canvas px-6 py-10</td><td>zentriert, füllt restliche Höhe</td></tr>
|
||
<tr><td>Frage</td><td>font-serif text-sm text-ink-2 text-center mb-4</td><td></td></tr>
|
||
<tr><td>Karten-Grid</td><td>flex gap-4</td><td>2 gleich breite Karten; auf Mobile flex-col</td></tr>
|
||
<tr class="grp"><td colspan="3">Type-Karte</td></tr>
|
||
<tr><td>Karte (inaktiv)</td><td>border border-line rounded-md p-4 bg-white cursor-pointer hover:border-primary hover:bg-surface</td><td>focus-visible:ring-2 focus-visible:ring-primary</td></tr>
|
||
<tr><td>Karte (ausgewählt)</td><td>border-2 border-orange-500 bg-orange-50 shadow-sm</td><td>aria-pressed="true"; kein Tailwind-Kürzel — nutze CSS-var(--orange)</td></tr>
|
||
<tr><td>Check-Kreis</td><td>w-5 h-5 rounded-full bg-orange-500 flex items-center justify-center self-end mt-2</td><td>nur sichtbar wenn ausgewählt</td></tr>
|
||
<tr><td>Kartentitel</td><td>font-serif text-sm text-ink</td><td></td></tr>
|
||
<tr><td>Kartenbeschreibung</td><td>text-xs text-ink-3 leading-relaxed mt-1</td><td></td></tr>
|
||
<tr class="grp"><td colspan="3">Navigation</td></tr>
|
||
<tr><td>Weiter-Button</td><td>rounded border border-primary bg-primary text-white px-4 py-2 text-sm font-medium disabled:opacity-40</td><td>disabled wenn keine Karte ausgewählt</td></tr>
|
||
<tr><td>URL-Param</td><td>?type=STORY | ?type=JOURNEY</td><td>per goto() nach Klick auf Weiter; lesefreundlich bookmarkbar</td></tr>
|
||
<tr><td>Mobile</td><td>flex-col Karten; volle Breite</td><td>kein Scrollbedarf auf 320px</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ SCREEN LR-1: LIST WITH BADGE ═══ -->
|
||
<div class="section">
|
||
<div class="section-title">Screens — Übersichtsliste</div>
|
||
|
||
<div class="scr">
|
||
<div class="scr-head">
|
||
<h3>LR-1 — Reise-Badge in /geschichten</h3>
|
||
<span class="scr-id">Issue #752 · LR-1</span>
|
||
</div>
|
||
<p class="scr-desc">Die Übersichtsliste erhält ein kleines „REISE"-Badge in der Metaspalte einer Journey-Zeile — unterhalb von Datum und Personenchip. Zeilen mit <code>type === 'STORY'</code> bleiben unverändert. Das Badge ist nicht klickbar, dient als reine visuelle Unterscheidung.</p>
|
||
<p class="scr-var"><strong>Varianten:</strong> Mischte Liste (hier gezeigt) · Nur-Journey-Filter · Nur-Story-Ansicht (unverändert)</p>
|
||
|
||
<div class="previews">
|
||
<!-- Desktop -->
|
||
<div class="prev-col" style="width:100%;max-width:1040px;">
|
||
<span class="bp-lbl">Desktop — 1040px · gemischte Liste</span>
|
||
<div class="desk">
|
||
<div class="fa-nav">
|
||
<span class="fa-logo">ARCHIV</span>
|
||
<span style="width:1px;height:14px;background:rgba(255,255,255,.1);margin:0 2px;"></span>
|
||
<span class="fa-link">Dokumente</span>
|
||
<span class="fa-link">Personen</span>
|
||
<span class="fa-link active">Geschichten</span>
|
||
<span class="fa-link">Chronik</span>
|
||
<div class="fa-nav-r">
|
||
<div class="fa-av" style="background:#012851;color:var(--mint);font-size:5px;font-weight:800;">MR</div>
|
||
</div>
|
||
</div>
|
||
<div style="background:#E8E7E2;flex:1;padding:14px 16px;">
|
||
<div class="g-page-hdr" style="padding:0 0 8px;">
|
||
<span class="g-page-title">Geschichten</span>
|
||
<button class="g-new-btn">
|
||
<svg width="7" height="7" viewBox="0 0 10 10" fill="none"><path d="M5 1v8M1 5h8" stroke="#fff" stroke-width="2" stroke-linecap="round"/></svg>
|
||
Neue Geschichte
|
||
</button>
|
||
</div>
|
||
<div class="g-list-card">
|
||
<div class="g-filters">
|
||
<span class="g-pill active">Alle</span>
|
||
<span class="g-pill">Franz Raddatz</span>
|
||
<span class="g-pill">Emma Müller</span>
|
||
<span class="g-pill" style="border-style:dashed;color:#6B6A63;">+ Person wählen</span>
|
||
</div>
|
||
<!-- Row 1: Story (no badge) -->
|
||
<div class="g-row">
|
||
<div class="g-meta">
|
||
<div class="g-av av-navy">MR</div>
|
||
<div class="g-author">Maria Raddatz</div>
|
||
<div class="g-date">14. März 2025</div>
|
||
<span class="g-chip">
|
||
<span style="width:8px;height:8px;border-radius:50%;background:#012851;display:inline-flex;align-items:center;justify-content:center;font-size:4.5px;font-weight:800;color:var(--mint);flex-shrink:0;">FR</span>
|
||
Franz Raddatz
|
||
</span>
|
||
</div>
|
||
<div class="g-content">
|
||
<div class="g-title">Der Sommer in Breslau</div>
|
||
<div class="g-excerpt">Oma erzählte oft vom letzten Sommer vor dem Krieg, als die Familie noch vollständig zusammen war und niemand ahnte, was kommen würde…</div>
|
||
</div>
|
||
</div>
|
||
<!-- Row 2: Journey (badge!) -->
|
||
<div class="g-row">
|
||
<div class="g-meta">
|
||
<div class="g-av av-purple">KR</div>
|
||
<div class="g-author">Klaus Raddatz</div>
|
||
<div class="g-date">15. Mai 2025</div>
|
||
<span class="g-chip">
|
||
<span style="width:8px;height:8px;border-radius:50%;background:#012851;display:inline-flex;align-items:center;justify-content:center;font-size:4.5px;font-weight:800;color:var(--mint);flex-shrink:0;">FR</span>
|
||
Franz Raddatz
|
||
</span>
|
||
<span class="j-badge">REISE</span>
|
||
</div>
|
||
<div class="g-content">
|
||
<div class="g-title">Briefe aus Breslau 1938–1942</div>
|
||
<div class="g-excerpt">Eine Lesereise durch den Briefwechsel zwischen Franz und Emma — von den letzten Friedenssommern bis zum Ende des Krieges.</div>
|
||
</div>
|
||
</div>
|
||
<!-- Row 3: Story -->
|
||
<div class="g-row">
|
||
<div class="g-meta">
|
||
<div class="g-av av-teal">GK</div>
|
||
<div class="g-author">Gertrud Koch</div>
|
||
<div class="g-date">18. Okt. 2024</div>
|
||
<span class="g-chip">
|
||
<span style="width:8px;height:8px;border-radius:50%;background:#534AB7;display:inline-flex;align-items:center;justify-content:center;font-size:4.5px;font-weight:800;color:#fff;flex-shrink:0;">EM</span>
|
||
Emma Müller
|
||
</span>
|
||
</div>
|
||
<div class="g-content">
|
||
<div class="g-title">Die Hochzeit im Krieg</div>
|
||
<div class="g-excerpt">1943, mitten im Chaos — Emma bestand darauf, dass das Fest stattfand. Ihr Bruder kam auf Fronturlaub, drei Tage nur, aber es reichte…</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Mobile -->
|
||
<div class="prev-col">
|
||
<span class="bp-lbl">Mobile — 320px</span>
|
||
<div class="phone">
|
||
<div class="pst"><b>9:41</b><span>●●●</span></div>
|
||
<div class="pb">
|
||
<div class="m-nav">
|
||
<span class="m-logo">ARCHIV</span>
|
||
<div class="m-nav-r">
|
||
<div class="m-av">MR</div>
|
||
<div class="m-ham"><span></span><span></span><span></span></div>
|
||
</div>
|
||
</div>
|
||
<div style="background:#E8E7E2;flex:1;display:flex;flex-direction:column;">
|
||
<div style="padding:8px 10px 4px;">
|
||
<span style="font-family:Georgia,serif;font-size:13px;color:#012851;">Geschichten</span>
|
||
</div>
|
||
<div class="m-filters">
|
||
<span class="g-pill active" style="font-size:6px;padding:2px 7px;">Alle</span>
|
||
<span class="g-pill" style="font-size:6px;padding:2px 7px;">Franz Raddatz</span>
|
||
<span class="g-pill" style="font-size:6px;padding:2px 7px;border-style:dashed;">+ Person…</span>
|
||
</div>
|
||
<div style="background:#fff;flex:1;">
|
||
<!-- Story row -->
|
||
<div class="m-row">
|
||
<div class="m-row-top">
|
||
<div class="g-av av-navy" style="width:16px;height:16px;font-size:5.5px;">MR</div>
|
||
<span class="m-author-name">Maria Raddatz</span>
|
||
<span class="m-date">14. Mrz. 2025</span>
|
||
</div>
|
||
<div class="m-title">Der Sommer in Breslau</div>
|
||
<div class="m-excerpt">Oma erzählte oft vom letzten Sommer vor dem Krieg…</div>
|
||
</div>
|
||
<!-- Journey row (badge) -->
|
||
<div class="m-row">
|
||
<div class="m-row-top">
|
||
<div class="g-av av-purple" style="width:16px;height:16px;font-size:5.5px;">KR</div>
|
||
<span class="m-author-name">Klaus Raddatz</span>
|
||
<span class="m-date">15. Mai 2025</span>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:5px;margin-bottom:3px;">
|
||
<div class="m-title" style="margin-bottom:0;">Briefe aus Breslau 1938–1942</div>
|
||
<span class="j-badge" style="flex-shrink:0;">REISE</span>
|
||
</div>
|
||
<div class="m-excerpt">Eine Lesereise durch den Briefwechsel zwischen Franz und Emma…</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="agent">
|
||
<h4>impl-ref — LR-1 Journey-Badge in der Liste</h4>
|
||
<table class="at">
|
||
<thead><tr><th>Element</th><th>Wert</th><th>Hinweise</th></tr></thead>
|
||
<tbody>
|
||
<tr class="grp"><td colspan="3">Badge</td></tr>
|
||
<tr><td>Journey badge</td><td>inline-flex items-center px-1.5 py-px rounded-sm text-[10px] font-bold uppercase tracking-wide bg-orange-50 text-orange-700 border border-orange-200</td><td>nur wenn type === 'JOURNEY'</td></tr>
|
||
<tr><td>Position Desktop</td><td>unterhalb Datum-Text und Personenchip in der Metaspalte (g-meta)</td><td>kein extra Abstand nötig — gap-1 der Flex-Spalte reicht</td></tr>
|
||
<tr><td>Position Mobile</td><td>inline flex items-center gap-1.5 neben Titel</td><td>Titel + Badge in einem flex-Wrapper; badge shrink-0</td></tr>
|
||
<tr><td>aria-label</td><td>aria-label="Lesereise"</td><td>Badge ist span, kein interaktives Element</td></tr>
|
||
<tr class="grp"><td colspan="3">Bedingte Logik</td></tr>
|
||
<tr><td>Svelte guard</td><td>{#if geschichte.type === 'JOURNEY'}<span …>REISE</span>{/if}</td><td>kein Badge für STORY</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ SCREEN LR-2: JOURNEY READER ═══ -->
|
||
<div class="section">
|
||
<div class="section-title">Screens — Journey-Leseansicht</div>
|
||
|
||
<div class="scr">
|
||
<div class="scr-head">
|
||
<h3>LR-2 — Journey-Detail /geschichten/[id]</h3>
|
||
<span class="scr-id">Issue #752 · LR-2</span>
|
||
</div>
|
||
<p class="scr-desc">Wenn <code>type === 'JOURNEY'</code> ersetzt die geordnete Briefliste den Prosa-Body. Optional zeigt ein Einleitungsabsatz (<code>body</code>) vor den Items. Jedes Item ist entweder ein Briefeintrag (Kartentitel, Datum, Link) oder ein Interlude-Absatz (orangener linker Rand, kursiv). Die Reihenfolge ergibt sich von oben nach unten — keine Nummern. Briefeinträge können eine optionale Kuratoren-Annotation unter dem Link zeigen.</p>
|
||
<p class="scr-var"><strong>Varianten:</strong> Leserin ohne Schreibrecht · BLOG_WRITER (Bearbeiten/Löschen sichtbar — hier gezeigt) · Mobile</p>
|
||
|
||
<div class="previews">
|
||
<!-- Desktop -->
|
||
<div class="prev-col" style="width:100%;max-width:1040px;">
|
||
<span class="bp-lbl">Desktop — 1040px · BLOG_WRITER-Ansicht</span>
|
||
<div class="desk" style="min-height:600px;">
|
||
<div class="fa-nav">
|
||
<span class="fa-logo">ARCHIV</span>
|
||
<span style="width:1px;height:14px;background:rgba(255,255,255,.1);margin:0 2px;"></span>
|
||
<span class="fa-link">Dokumente</span>
|
||
<span class="fa-link">Personen</span>
|
||
<span class="fa-link active">Geschichten</span>
|
||
<span class="fa-link">Chronik</span>
|
||
<div class="fa-nav-r">
|
||
<div class="fa-av" style="background:#012851;color:var(--mint);font-size:5px;font-weight:800;">MR</div>
|
||
</div>
|
||
</div>
|
||
<div style="background:#E8E7E2;flex:1;padding:16px 20px;">
|
||
<div class="jr-article">
|
||
<div class="jr-back">
|
||
<svg width="7" height="7" viewBox="0 0 10 10" fill="none"><path d="M6 2L2 5l4 3" stroke="#6B6A63" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
Zurück zu Geschichten
|
||
</div>
|
||
<div class="jr-badge">LESEREISE</div>
|
||
<div class="jr-title">Briefe aus Breslau 1938–1942</div>
|
||
<div class="jr-metabar">
|
||
<div class="g-av av-purple" style="width:20px;height:20px;font-size:6.5px;">KR</div>
|
||
<div>
|
||
<div style="font-size:7.5px;font-weight:700;color:#1C1C18;line-height:1.2;">Klaus Raddatz</div>
|
||
<div style="font-size:6.5px;color:#6B6A63;">zusammengestellt am 15. Mai 2025</div>
|
||
</div>
|
||
<div class="jr-metabar-r">
|
||
<button class="jr-edit-btn">Bearbeiten</button>
|
||
<span style="font-size:6.5px;font-weight:600;color:#DC4C3E;">Löschen</span>
|
||
</div>
|
||
</div>
|
||
<!-- Intro -->
|
||
<div class="jr-intro">Der Briefwechsel zwischen Franz Raddatz und seiner Schwester Emma umspannt vier Jahre — von den letzten unbeschwerten Sommerwochen 1938 bis zum Kriegsende. Diese Lesereise folgt den Briefen in chronologischer Reihenfolge.</div>
|
||
<!-- Item 1: Document, no annotation -->
|
||
<div class="jr-item">
|
||
<div class="jr-card">
|
||
<div class="jr-card-title">Brief vom 12. Juli 1938</div>
|
||
<div class="jr-card-meta">12. Juli 1938 · von Franz Raddatz an Emma Müller</div>
|
||
<div class="jr-card-link">
|
||
<svg width="8" height="8" viewBox="0 0 10 12" fill="none"><rect x="1" y="1" width="8" height="10" rx="1" stroke="#012851" stroke-width="1"/><path d="M3 4h4M3 6.5h4M3 9h2" stroke="#012851" stroke-width=".7" stroke-linecap="round"/></svg>
|
||
Brief öffnen
|
||
<svg width="7" height="7" viewBox="0 0 10 10" fill="none"><path d="M4 2l4 3-4 3" stroke="#012851" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Interlude -->
|
||
<div class="jr-interlude">
|
||
<div class="jr-interlude-text">Im Sommer 1938 schrieb Franz voller Zuversicht — er hatte kaum eine Ahnung, wie bald sich die Welt um ihn herum verändern würde. Seine Briefe aus dieser Zeit tragen eine Leichtigkeit, die in den späteren Kriegsjahren vollständig verschwindet.</div>
|
||
</div>
|
||
<!-- Item 2: Document with annotation -->
|
||
<div class="jr-item">
|
||
<div class="jr-card">
|
||
<div class="jr-card-title">Postkarte aus Breslau, August 1938</div>
|
||
<div class="jr-card-meta">22. Aug. 1938 · von Franz Raddatz an Emma Müller</div>
|
||
<div class="jr-card-link">
|
||
<svg width="8" height="8" viewBox="0 0 10 12" fill="none"><rect x="1" y="1" width="8" height="10" rx="1" stroke="#012851" stroke-width="1"/><path d="M3 4h4M3 6.5h4M3 9h2" stroke="#012851" stroke-width=".7" stroke-linecap="round"/></svg>
|
||
Brief öffnen
|
||
<svg width="7" height="7" viewBox="0 0 10 10" fill="none"><path d="M4 2l4 3-4 3" stroke="#012851" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
</div>
|
||
<div class="jr-annotation">
|
||
<div class="jr-annotation-text">Diese Karte ist ungewöhnlich kurz für Franz — vier Zeilen, fast hastig. Ein Zeichen der aufkommenden Unruhe in den Nachrichten, oder schlicht die Hitze des Augusts?</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Item 3: Document -->
|
||
<div class="jr-item">
|
||
<div class="jr-card">
|
||
<div class="jr-card-title">Brief vom 3. September 1939</div>
|
||
<div class="jr-card-meta">3. Sept. 1939 · von Emma Müller an Franz Raddatz</div>
|
||
<div class="jr-card-link">
|
||
<svg width="8" height="8" viewBox="0 0 10 12" fill="none"><rect x="1" y="1" width="8" height="10" rx="1" stroke="#012851" stroke-width="1"/><path d="M3 4h4M3 6.5h4M3 9h2" stroke="#012851" stroke-width=".7" stroke-linecap="round"/></svg>
|
||
Brief öffnen
|
||
<svg width="7" height="7" viewBox="0 0 10 10" fill="none"><path d="M4 2l4 3-4 3" stroke="#012851" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Mobile -->
|
||
<div class="prev-col">
|
||
<span class="bp-lbl">Mobile — 320px · Leserin</span>
|
||
<div class="phone" style="min-height:520px;">
|
||
<div class="pst"><b>9:41</b><span>●●●</span></div>
|
||
<div class="pb">
|
||
<div class="m-nav">
|
||
<span class="m-logo">ARCHIV</span>
|
||
<div class="m-nav-r">
|
||
<div class="m-av">MR</div>
|
||
<div class="m-ham"><span></span><span></span><span></span></div>
|
||
</div>
|
||
</div>
|
||
<div style="background:#E8E7E2;flex:1;padding:10px;">
|
||
<div class="mjr-article">
|
||
<div class="mjr-back">
|
||
<svg width="6" height="6" viewBox="0 0 10 10" fill="none"><path d="M6 2L2 5l4 3" stroke="#6B6A63" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
Zurück
|
||
</div>
|
||
<div class="mjr-badge">LESEREISE</div>
|
||
<div class="mjr-title">Briefe aus Breslau 1938–1942</div>
|
||
<div class="mjr-metabar">
|
||
<div class="g-av av-purple" style="width:16px;height:16px;font-size:5.5px;flex-shrink:0;">KR</div>
|
||
<div>
|
||
<div style="font-size:7px;font-weight:700;color:#1C1C18;">Klaus Raddatz</div>
|
||
<div style="font-size:6px;color:#6B6A63;">15. Mai 2025</div>
|
||
</div>
|
||
<div style="margin-left:auto;font-size:12px;color:#6B6A63;">···</div>
|
||
</div>
|
||
<div style="height:1px;background:#EDECEA;margin-bottom:8px;"></div>
|
||
<div class="mjr-intro">Der Briefwechsel zwischen Franz und Emma — von 1938 bis Kriegsende.</div>
|
||
<!-- Item 1 -->
|
||
<div class="mjr-item">
|
||
<div class="mjr-card">
|
||
<div class="mjr-card-title">Brief vom 12. Juli 1938</div>
|
||
<div class="mjr-card-meta">12. Juli 1938 · Franz → Emma</div>
|
||
<div class="mjr-card-link">Brief öffnen →</div>
|
||
</div>
|
||
</div>
|
||
<!-- Interlude -->
|
||
<div class="mjr-interlude">
|
||
<div class="mjr-interlude-text">Im Sommer 1938 schrieb Franz voller Zuversicht — er hatte kaum eine Ahnung, wie bald sich die Welt um ihn herum verändern würde.</div>
|
||
</div>
|
||
<!-- Item 2 -->
|
||
<div class="mjr-item">
|
||
<div class="mjr-card">
|
||
<div class="mjr-card-title">Postkarte Aug. 1938</div>
|
||
<div class="mjr-card-meta">22. Aug. 1938 · Franz → Emma</div>
|
||
<div class="mjr-card-link">Brief öffnen →</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="agent">
|
||
<h4>impl-ref — LR-2 Journey-Leseansicht</h4>
|
||
<table class="at">
|
||
<thead><tr><th>Element</th><th>Wert</th><th>Hinweise</th></tr></thead>
|
||
<tbody>
|
||
<tr class="grp"><td colspan="3">Seitenstruktur</td></tr>
|
||
<tr><td>Bedingte Logik</td><td>{#if geschichte.type === 'JOURNEY'} JourneyReader {:else} StoryReader {/if}</td><td>in +page.svelte von /geschichten/[id]</td></tr>
|
||
<tr><td>Artikel-Container</td><td>max-w-3xl mx-auto px-4 py-8</td><td>gleich wie StoryReader</td></tr>
|
||
<tr><td>Journey-Badge</td><td>inline-flex px-2 py-px rounded-sm text-[10px] font-bold uppercase tracking-widest bg-orange-50 text-orange-700 border border-orange-200 mb-2</td><td>über dem Titel; nicht für STORY</td></tr>
|
||
<tr><td>Titel</td><td>font-serif text-3xl text-ink leading-tight mb-4</td><td>gleich wie Story</td></tr>
|
||
<tr><td>Metabar</td><td>flex items-center gap-3 pb-4 border-b border-subtle mb-4</td><td>gleich wie Story</td></tr>
|
||
<tr><td>Bearbeiten/Löschen</td><td>nur BLOG_WRITE; auf Mobile im ··· BottomSheet</td><td>gleich wie Story</td></tr>
|
||
<tr class="grp"><td colspan="3">Intro-Absatz</td></tr>
|
||
<tr><td>Intro (body)</td><td>font-serif text-sm text-ink-2 italic leading-relaxed mb-6 pb-4 border-b border-dashed border-subtle</td><td>nur rendern wenn body nicht leer; kein HTML-Rendering — plaintext</td></tr>
|
||
<tr class="grp"><td colspan="3">Dokument-Item</td></tr>
|
||
<tr><td>Item-Zeile</td><td>mb-3</td><td>kein flex nötig — Karte ist full-width</td></tr>
|
||
<tr><td>Dokumentkarte</td><td>bg-white border border-line rounded-sm p-3</td><td></td></tr>
|
||
<tr><td>Brieftitel</td><td>font-serif text-sm text-ink leading-snug mb-0.5</td><td>document.title</td></tr>
|
||
<tr><td>Briefmeta</td><td>text-xs text-ink-3 mb-2</td><td>formatDate(document.documentDate) · "von X an Y"</td></tr>
|
||
<tr><td>Brief öffnen Link</td><td>inline-flex items-center gap-1 text-xs font-semibold text-ink hover:text-primary</td><td>href="/documents/{item.document.id}"</td></tr>
|
||
<tr class="grp"><td colspan="3">Kuratoren-Annotation</td></tr>
|
||
<tr><td>Annotation</td><td>mt-3 pl-3 border-l-2 border-mint bg-surface rounded-r-sm py-1.5 pr-2</td><td>nur rendern wenn item.note vorhanden</td></tr>
|
||
<tr><td>Annotations-Text</td><td>text-xs italic text-ink-2 leading-relaxed</td><td></td></tr>
|
||
<tr class="grp"><td colspan="3">Interlude-Item</td></tr>
|
||
<tr><td>Interlude-Block</td><td>pl-3 border-l-2 border-orange-400 bg-orange-50 rounded-r-sm py-2 pr-3 my-4</td><td>item.document === null</td></tr>
|
||
<tr><td>Interlude-Text</td><td>text-xs italic text-ink leading-relaxed</td><td>item.note; plaintext</td></tr>
|
||
<tr class="grp"><td colspan="3">Mobile</td></tr>
|
||
<tr><td>··· Menü</td><td>ml-auto text-ink-3; öffnet BottomSheet mit Bearbeiten + Löschen</td><td>BLOG_WRITE; gleich wie Story</td></tr>
|
||
<tr><td>Touch Target (Brief öffnen)</td><td>min-h-[44px] durch padding auf der Karte</td><td>WCAG 2.2 AA</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ LLM IMPLEMENTATION GUIDE ═══ -->
|
||
<div class="llm">
|
||
<h2>Implementation Guide — Lesereisen Reader</h2>
|
||
|
||
<h3>Geänderte Views und Routen</h3>
|
||
<table>
|
||
<thead><tr><th>View</th><th>Route</th><th>Änderung</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>Neue Geschichte</td><td>/geschichten/new</td><td>Neuer Typauswahl-Schritt als first render; setzt ?type=STORY|JOURNEY</td></tr>
|
||
<tr><td>Geschichten-Liste</td><td>/geschichten</td><td>Journey-Badge in GeschichtenCard wenn type === 'JOURNEY'</td></tr>
|
||
<tr><td>Geschichte-Detail</td><td>/geschichten/[id]</td><td>Bedingte Verzweigung: JourneyReader | StoryReader</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>Neue Komponenten</h3>
|
||
<ul>
|
||
<li><code>JourneyReader.svelte</code> — rendert Intro + Items-Liste; Props: <code>geschichte: GeschichteDetail</code></li>
|
||
<li><code>JourneyItemCard.svelte</code> — ein Dokument-Item mit optionaler Annotation; Props: <code>item: JourneyItem, position: number</code></li>
|
||
<li><code>JourneyInterlude.svelte</code> — ein reiner Text-Interlude; Props: <code>note: string</code></li>
|
||
</ul>
|
||
|
||
<h3>Datenmodell (nach #750)</h3>
|
||
<ul>
|
||
<li><code>GeschichteType: 'STORY' | 'JOURNEY'</code></li>
|
||
<li><code>JourneyItem: { id: UUID, position: number, document: DocumentSummary | null, note: string | null }</code></li>
|
||
<li><code>Geschichte.items</code> — geordnete Liste (nach <code>position</code> ASC); für STORY leer</li>
|
||
<li><code>Geschichte.body</code> — für JOURNEY der optionale Einleitungstext (plaintext, kein HTML); für STORY der Rich-Text-Body</li>
|
||
</ul>
|
||
|
||
<h3>Typauswahl — Implementierungshinweise</h3>
|
||
<ul>
|
||
<li>Die Typauswahl ist ein Schritt INNERHALB der <code>/geschichten/new</code>-Route — kein eigener URL, kein <code>goto()</code>. Zustand <code>let selectedType: GeschichteType | null = null</code> in der Komponente.</li>
|
||
<li>Erst wenn <code>selectedType !== null</code> ist der „Weiter"-Button aktiviert (<code>disabled={!selectedType}</code>).</li>
|
||
<li>Nach Klick auf „Weiter": wenn <code>selectedType === 'JOURNEY'</code> → <code>goto('/geschichten/new?type=JOURNEY')</code> und zeige den Journey-Editor (aus Issue #753); wenn <code>STORY</code> → bestehender GeschichteEditor (unverändert).</li>
|
||
<li>Die Karten verwenden <code>role="radio"</code> und <code>aria-checked</code> für Accessibility. Keyboard: Arrow-Keys wechseln zwischen den Karten, Space/Enter wählt aus.</li>
|
||
</ul>
|
||
|
||
<h3>Journey-Badge — Implementierungshinweise</h3>
|
||
<ul>
|
||
<li>Badge nur in <code>GeschichtenCard.svelte</code> hinzufügen — keine Änderung an der Listenlogik oder dem API-Aufruf.</li>
|
||
<li>Text: „REISE" (Kurzform für die Metaspalte); <code>aria-label="Lesereise"</code> für den Badge-Span.</li>
|
||
</ul>
|
||
|
||
<h3>Journey-Reader — Implementierungshinweise</h3>
|
||
<ul>
|
||
<li>Items werden bereits geordnet vom Backend geliefert (<code>ORDER BY position ASC</code>). Keine client-seitige Sortierung nötig.</li>
|
||
<li>Ein Item ist Interlude wenn <code>item.document === null</code>. In diesem Fall: <code>JourneyInterlude</code>-Komponente rendern.</li>
|
||
<li>Der Intro-Absatz (<code>body</code>) wird als Plaintext gerendert — <em>nicht</em> als innerHTML. Im Editor wird es als einfaches Textarea gespeichert, kein HTML.</li>
|
||
</ul>
|
||
|
||
<h3>Berechtigungen</h3>
|
||
<ul>
|
||
<li>„Bearbeiten" und „Löschen" nur für <code>currentUser.permissions.includes('BLOG_WRITE')</code> — gleich wie Story.</li>
|
||
<li>Auf Mobile: Bearbeiten/Löschen im BottomSheet hinter ··· — gleich wie Story.</li>
|
||
</ul>
|
||
|
||
<h3>Barrierefreiheit</h3>
|
||
<ul>
|
||
<li>Items-Liste: <code><ol></code> semantisch für die geordnete Briefliste. Interludes sind <code><li></code>-Elemente mit <code>aria-label="Kuratorennotiz"</code>.</li>
|
||
<li>„Brief öffnen"-Link: beschreibender Text mit Briefdatum im <code>aria-label</code>, z.B. <code>aria-label="Brief vom 12. Juli 1938 öffnen"</code>.</li>
|
||
<li>Touch-Targets: jede Dokumentkarte hat mindestens 44px Höhe durch den Padding der Karte.</li>
|
||
<li>Fokusring: <code>focus-visible:ring-2 focus-visible:ring-primary</code> auf allen Links.</li>
|
||
</ul>
|
||
</div>
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|