Three final UI/UX specs for the collaborative transcription system: - expandable-metadata-header-spec: labeled "Details" toggle with drawer - annotation-transcription-final-spec: annotation-backed transcription with block-level comment threads - transcription-read-mode-final-spec: clean split read mode with flowing prose and scroll sync Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
805 lines
59 KiB
HTML
805 lines
59 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8"/>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
<title>Transcription Read Mode — Final Spec (Clean Split)</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&family=Tinos:ital,wght@0,400;0,700;1,400&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;--accent-bg:rgba(161,220,216,.12);--blue-tint:#E6F1FB;--blue:#2D7DD2;--blue-dark:#185FA5;--green-tint:#E8F5EA;--green:#3D8C4A;--green-dark:#2E6E39;--orange-tint:#FEF0E6;--orange:#E8862A;--orange-dark:#B46820;--color-error:#DC4C3E;--font-display:'Fraunces',Georgia,serif;--font-sans:'DM Sans',system-ui,sans-serif;--font-mono:'DM Mono',monospace;--font-read:'Tinos',Georgia,serif;--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;}
|
|
|
|
.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:520px;}
|
|
|
|
.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 chrome ── */
|
|
.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;}
|
|
.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);}
|
|
|
|
/* ── Topbar ── */
|
|
.fa-topbar{background:#fff;border-bottom:1px solid #e4e2d7;display:flex;align-items:center;padding:0 12px;gap:6px;height:42px;flex-shrink:0;}
|
|
.fa-topbar .back{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:9px;color:var(--color-text-muted);}
|
|
.fa-topbar .title{font-family:Georgia,serif;font-size:11px;color:var(--navy);flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
|
|
.fa-chip{display:inline-flex;align-items:center;gap:2px;padding:1px 5px 1px 2px;background:var(--sand);border:1px solid #e4e2d7;border-radius:8px;white-space:nowrap;font-size:7px;color:var(--color-text);}
|
|
.fa-chip .av{width:12px;height:12px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:5px;font-weight:800;flex-shrink:0;}
|
|
.fa-chip .av.navy{background:var(--navy);color:var(--mint);}
|
|
.fa-chip .av.purple{background:#5A3080;color:#fff;}
|
|
.fa-topbar-btn{font-size:7px;font-weight:600;padding:3px 8px;border-radius:4px;border:1px solid var(--navy);color:var(--navy);background:transparent;display:flex;align-items:center;gap:3px;cursor:pointer;}
|
|
.fa-topbar-btn.ghost{border-color:var(--color-border);color:var(--color-text-muted);font-weight:500;}
|
|
.details-toggle{display:inline-flex;align-items:center;gap:3px;padding:2px 8px 2px 6px;border-radius:4px;font-size:7px;font-weight:600;color:var(--color-text-muted);cursor:pointer;border:1px solid var(--color-border);background:transparent;white-space:nowrap;}
|
|
|
|
/* ── Mode switcher ── */
|
|
.mode-sw{display:inline-flex;border:1px solid var(--color-border);border-radius:4px;overflow:hidden;font-size:6px;font-weight:600;}
|
|
.mode-sw span{padding:3px 8px;cursor:pointer;color:var(--color-text-muted);}
|
|
.mode-sw span.active{background:var(--navy);color:#fff;border-color:var(--navy);}
|
|
.mode-sw span:not(.active):hover{background:var(--sand);}
|
|
|
|
/* ── PDF area ── */
|
|
.pdf-area{background:#D4D0C8;flex:1;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;}
|
|
.paper{background:#FFFEF8;box-shadow:0 2px 8px rgba(0,0,0,.14);border-radius:1px;padding:9px 11px;display:flex;flex-direction:column;gap:2px;position:relative;}
|
|
.pl{height:3px;background:#C4BDB0;border-radius:1px;opacity:.5;margin-bottom:2px;}
|
|
.ps{height:2px;background:#C4BDB0;border-radius:1px;opacity:.28;margin-bottom:1.5px;}
|
|
|
|
/* ── Annotation rects (dimmed in read mode) ── */
|
|
.ann-rect{position:absolute;border-radius:2px;}
|
|
.ann-rect.trans{border:1.5px solid var(--turquoise);background:rgba(0,199,177,.1);}
|
|
.ann-rect.trans.dimmed{border-color:rgba(0,199,177,.3);background:rgba(0,199,177,.04);}
|
|
|
|
/* ── Split ── */
|
|
.split{display:flex;flex:1;overflow:hidden;}
|
|
.split-handle{width:4px;background:var(--color-border);cursor:col-resize;flex-shrink:0;display:flex;align-items:center;justify-content:center;}
|
|
.split-handle::after{content:'';width:2px;height:20px;background:var(--color-text-muted);border-radius:1px;opacity:.3;}
|
|
|
|
/* ── Read-mode text ── */
|
|
.read-text{font-family:var(--font-read);font-size:10px;line-height:1.85;color:var(--color-text);padding:16px 20px;}
|
|
.read-text .para{margin-bottom:10px;cursor:pointer;padding:2px 4px;border-radius:3px;transition:background .15s ease;}
|
|
.read-text .para:hover{background:rgba(0,199,177,.06);}
|
|
.read-text .para.highlighted{background:rgba(0,199,177,.1);transition:background .3s ease;}
|
|
.read-text .greeting{font-style:italic;}
|
|
.read-text .closing{margin-top:12px;text-align:right;font-style:italic;}
|
|
.read-text .illegible{color:var(--color-text-muted);font-style:italic;font-size:9px;}
|
|
|
|
/* ── Status bar ── */
|
|
.status-bar{background:var(--sand);border-top:1px solid #e4e2d7;height:18px;display:flex;align-items:center;padding:0 8px;font-size:7px;color:var(--color-text-muted);gap:8px;flex-shrink:0;}
|
|
|
|
/* ── Scroll sync highlight on PDF ── */
|
|
.ann-rect.highlight-flash{border-color:var(--turquoise) !important;background:rgba(0,199,177,.18) !important;transition:all .3s ease;animation:flash-fade 1.5s ease-out forwards;}
|
|
@keyframes flash-fade{0%{background:rgba(0,199,177,.18);border-color:var(--turquoise);}100%{background:rgba(0,199,177,.04);border-color:rgba(0,199,177,.3);}}
|
|
|
|
/* ── Agent 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;}
|
|
.agent pre{font-family:var(--font-mono);font-size:10px;color:#444440;margin-bottom:16px;line-height:1.8;white-space:pre-wrap;}
|
|
.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{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;color:var(--color-text);}
|
|
.llm h4{font-size:12px;font-weight:600;margin:16px 0 6px;color:var(--color-text);}
|
|
.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 pre{font-family:var(--font-mono);font-size:11px;background:var(--color-surface);padding:12px 16px;border-radius:var(--radius-md);overflow-x:auto;margin:8px 0 12px;line-height:1.6;}
|
|
.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);}
|
|
|
|
@media(max-width:900px){.doc{padding:24px 16px 80px;}}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="doc">
|
|
|
|
<div class="doc-header">
|
|
<div>
|
|
<h1>Transcription Read Mode — Final Spec</h1>
|
|
<p>A focused reading experience for completed transcriptions. Uses the <strong>clean split</strong> layout: PDF scan on the left, flowing prose on the right. All editing chrome is stripped — no block borders, no comment threads, no toolbars. The text reads like a letter, not like an editing interface.</p>
|
|
</div>
|
|
<div class="doc-meta">
|
|
Familienarchiv<br/>
|
|
<span class="pill pill-g">Final</span><br/>
|
|
2026-04-05 · @leonievoss
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
SECTION: DESIGN RATIONALE
|
|
═══════════════════════════════════════════════════════════════════════════ -->
|
|
<div class="section">
|
|
<div class="section-title">Design rationale</div>
|
|
<p class="prose">Transcribe mode is for editing. But most visits to a completed transcription are for <strong>reading</strong> — comparing the handwriting with the typed text, sharing with family, or just revisiting a letter. Read mode strips away all editing chrome and presents the transcription as flowing prose alongside the original scan.</p>
|
|
<p class="prose">The <strong>clean split</strong> was chosen over the full-page reader (PDF hidden) and the interleaved view (cropped PDF per block) because it preserves the familiar side-by-side layout from transcribe mode while dramatically reducing visual noise. Users can switch between reading and editing without re-learning the spatial layout.</p>
|
|
|
|
<div style="display:flex;gap:12px;flex-wrap:wrap;font-size:12px;margin-top:20px;">
|
|
<div style="background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-md);padding:10px 14px;flex:1;min-width:180px;">
|
|
<div style="font-weight:600;color:var(--green);margin-bottom:4px;">Kept</div>
|
|
<ul style="padding-left:16px;color:var(--color-text-muted);line-height:1.8;">
|
|
<li>Transcription text (flowing prose)</li>
|
|
<li>PDF scan viewer (same position)</li>
|
|
<li>Topbar (title, Details toggle, person chips)</li>
|
|
<li>Mode switcher (Lesen / Bearbeiten)</li>
|
|
<li>Resizable split handle</li>
|
|
<li>Scroll sync (click paragraph ↔ PDF)</li>
|
|
</ul>
|
|
</div>
|
|
<div style="background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-md);padding:10px 14px;flex:1;min-width:180px;">
|
|
<div style="font-weight:600;color:var(--color-error);margin-bottom:4px;">Removed</div>
|
|
<ul style="padding-left:16px;color:var(--color-text-muted);line-height:1.8;">
|
|
<li>Block borders & numbered badges</li>
|
|
<li>Contenteditable / cursor</li>
|
|
<li>Comment threads & “Kommentieren” buttons</li>
|
|
<li>Presence dots & user indicators</li>
|
|
<li>Hint strip (“Markiere eine Passage…”)</li>
|
|
<li>Drag handles, sort controls</li>
|
|
<li>Auto-save status indicator</li>
|
|
<li>Add-block CTA</li>
|
|
<li>History / “Verlauf” button</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
S1: DESKTOP — READ MODE
|
|
═══════════════════════════════════════════════════════════════════════════ -->
|
|
<div class="scr" id="s1">
|
|
<div class="scr-head"><h3>S1 — Desktop read mode</h3><span class="scr-id">S1</span></div>
|
|
<div class="scr-desc">Side-by-side split: PDF scan on the left with dimmed annotation outlines, flowing serif prose on the right. The mode switcher shows “Lesen” as active. Clicking a paragraph briefly highlights the matching PDF region (turquoise flash, 1.5s fade).</div>
|
|
<div class="scr-var"><strong>Primary reading state</strong> — the default view when a transcription exists.</div>
|
|
|
|
<div class="previews">
|
|
<div class="prev-col">
|
|
<div class="bp-lbl">Desktop · 1040px</div>
|
|
<div class="desk">
|
|
<div class="fa-nav">
|
|
<div class="fa-logo">FAMILIENARCHIV</div>
|
|
<div class="fa-link">Dokumente</div>
|
|
<div class="fa-link">Personen</div>
|
|
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
|
|
</div>
|
|
<div class="fa-topbar">
|
|
<div class="back">←</div>
|
|
<div class="title">Brief von Heinrich an Martha, 14. Mai 1943</div>
|
|
<div style="flex:1"></div>
|
|
<div class="fa-chip"><div class="av navy">HR</div> Heinrich R.</div>
|
|
<span style="font-size:7px;color:var(--color-text-muted);">→</span>
|
|
<div class="fa-chip"><div class="av purple">MR</div> Martha R.</div>
|
|
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
|
|
<div class="details-toggle">Details ▼</div>
|
|
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
|
|
<!-- Mode switcher: Lesen active -->
|
|
<div class="mode-sw"><span class="active">Lesen</span><span>Bearbeiten</span></div>
|
|
</div>
|
|
|
|
<div class="split" style="height:430px;">
|
|
<!-- PDF scan — annotations dimmed -->
|
|
<div style="flex:1;display:flex;flex-direction:column;">
|
|
<div class="pdf-area" style="flex:1;">
|
|
<div class="paper" style="width:55%;min-height:260px;position:relative;">
|
|
<div style="font-size:7px;color:#8A8070;font-style:italic;margin-bottom:4px;opacity:.7;">Liebe Martha,</div>
|
|
<div class="pl" style="width:90%;"></div><div class="ps" style="width:85%;"></div><div class="ps" style="width:92%;"></div>
|
|
<div class="pl" style="width:78%;"></div><div class="ps" style="width:88%;"></div><div class="ps" style="width:70%;"></div>
|
|
<div class="pl" style="width:84%;"></div><div class="ps" style="width:90%;"></div><div class="ps" style="width:60%;"></div>
|
|
<div class="pl" style="width:75%;"></div><div class="ps" style="width:82%;"></div>
|
|
<div style="font-size:6px;color:#8A8070;margin-top:6px;text-align:right;opacity:.7;">Dein Heinrich</div>
|
|
|
|
<!-- Dimmed annotation outlines — no numbered badges -->
|
|
<div class="ann-rect trans dimmed" style="left:2%;top:0%;width:50%;height:10%;"></div>
|
|
<div class="ann-rect trans dimmed" style="left:2%;top:14%;width:96%;height:32%;"></div>
|
|
<div class="ann-rect trans dimmed" style="left:2%;top:50%;width:96%;height:22%;"></div>
|
|
<div class="ann-rect trans dimmed" style="left:20%;top:80%;width:60%;height:12%;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="split-handle"></div>
|
|
|
|
<!-- Right panel: flowing prose, no block chrome -->
|
|
<div style="width:400px;display:flex;flex-direction:column;border-left:1px solid #e4e2d7;background:#fff;">
|
|
<div class="read-text" style="flex:1;overflow-y:auto;">
|
|
<div class="para greeting">Liebe Martha,</div>
|
|
<div class="para">ich schreibe Dir heute aus dem Lazarett in Breslau. Mach Dir keine Sorgen, es geht mir den Umständen entsprechend gut. Der Arzt sagt <span class="illegible">[unleserlich]</span> Wochen noch dauern wird.</div>
|
|
<div class="para">Die Kinder sollen wissen, dass ich an sie denke. Sag dem kleinen Fritz, er soll auf seine Mutter aufpassen. Und Lotte soll weiter so fleißig in der Schule sein.</div>
|
|
<div class="para closing">In ewiger Liebe,<br/>Dein Heinrich</div>
|
|
</div>
|
|
<div class="status-bar">
|
|
<span>4 Abschnitte</span>
|
|
<span style="margin-left:auto;">Zuletzt bearbeitet: Oma Inge, 14:23</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="agent">
|
|
<h4>S1 · Desktop read mode</h4>
|
|
<pre>/* Same side-by-side layout as transcribe mode, but the right panel renders
|
|
* the transcription as continuous flowing prose instead of block cards.
|
|
*
|
|
* Key differences from transcribe mode:
|
|
* - No block borders, headers, footers, or numbered badges
|
|
* - No contenteditable — text is plain rendered HTML
|
|
* - No comment threads, no "Kommentieren" buttons
|
|
* - No presence dots, no hint strip, no auto-save indicator
|
|
* - Annotation rects on PDF are dimmed (opacity ~0.3, no badges)
|
|
* - Still clickable for scroll-sync
|
|
* - Status bar shows: "4 Abschnitte · Zuletzt bearbeitet: Oma Inge, 14:23"
|
|
*
|
|
* Scroll sync:
|
|
* - Click paragraph → matching PDF annotation flashes turquoise (1.5s fade)
|
|
* - Click PDF annotation → matching paragraph gets subtle bg highlight (1.5s fade)
|
|
* - PDF auto-scrolls to center the annotation in the viewport */</pre>
|
|
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
|
|
<tr class="grp"><td colspan="3">Text panel</td></tr>
|
|
<tr><td>Font</td><td>Tinos (serif), 16px, line-height 1.85</td><td>Generous reading typography</td></tr>
|
|
<tr><td>Padding</td><td>24px 32px</td><td>Comfortable margins like a book page</td></tr>
|
|
<tr><td>Paragraphs</td><td>One <p> per transcription block</td><td>mb-4 between paragraphs</td></tr>
|
|
<tr><td>[unleserlich]</td><td>italic, text-ink-2, font-size: 0.9em</td><td>Subtle but readable</td></tr>
|
|
<tr><td>Hover</td><td>Subtle turquoise bg at 6% opacity</td><td>Hint that paragraphs are clickable</td></tr>
|
|
<tr class="grp"><td colspan="3">PDF panel</td></tr>
|
|
<tr><td>Annotations</td><td>Dimmed: border-opacity 0.3, bg-opacity 0.04</td><td>Still clickable for scroll-sync</td></tr>
|
|
<tr><td>Badges</td><td>Hidden</td><td>No numbered circles in read mode</td></tr>
|
|
<tr><td>Scroll sync</td><td>Click para → PDF scrolls, flash 1.5s</td><td>Turquoise tint at 18% → fade to 4%</td></tr>
|
|
<tr class="grp"><td colspan="3">Status bar</td></tr>
|
|
<tr><td>Content</td><td>"N Abschnitte · Zuletzt bearbeitet: Name, HH:mm"</td><td>Uses most recent updated_at across blocks</td></tr>
|
|
<tr><td>Height</td><td>28px, sand background</td><td>Same as transcribe mode status bar</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
S2: DESKTOP — SCROLL SYNC INTERACTION
|
|
═══════════════════════════════════════════════════════════════════════════ -->
|
|
<div class="scr" id="s2">
|
|
<div class="scr-head"><h3>S2 — Scroll sync highlight</h3><span class="scr-id">S2</span></div>
|
|
<div class="scr-desc">The user clicked the second paragraph. The matching PDF annotation flashes with a turquoise highlight that fades over 1.5 seconds. The paragraph itself gets a subtle background tint. This is the <strong>only interactive element</strong> in read mode — no editing, no comments.</div>
|
|
<div class="scr-var"><strong>Click-to-highlight interaction</strong> — bidirectional scroll sync between text and scan.</div>
|
|
|
|
<div class="previews">
|
|
<div class="prev-col">
|
|
<div class="bp-lbl">Desktop · 1040px</div>
|
|
<div class="desk">
|
|
<div class="fa-nav">
|
|
<div class="fa-logo">FAMILIENARCHIV</div>
|
|
<div class="fa-link">Dokumente</div>
|
|
<div class="fa-link">Personen</div>
|
|
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
|
|
</div>
|
|
<div class="fa-topbar">
|
|
<div class="back">←</div>
|
|
<div class="title">Brief von Heinrich an Martha, 14. Mai 1943</div>
|
|
<div style="flex:1"></div>
|
|
<div class="fa-chip"><div class="av navy">HR</div> Heinrich R.</div>
|
|
<span style="font-size:7px;color:var(--color-text-muted);">→</span>
|
|
<div class="fa-chip"><div class="av purple">MR</div> Martha R.</div>
|
|
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
|
|
<div class="details-toggle">Details ▼</div>
|
|
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
|
|
<div class="mode-sw"><span class="active">Lesen</span><span>Bearbeiten</span></div>
|
|
</div>
|
|
|
|
<div class="split" style="height:430px;">
|
|
<div style="flex:1;display:flex;flex-direction:column;">
|
|
<div class="pdf-area" style="flex:1;">
|
|
<div class="paper" style="width:55%;min-height:260px;position:relative;">
|
|
<div style="font-size:7px;color:#8A8070;font-style:italic;margin-bottom:4px;opacity:.7;">Liebe Martha,</div>
|
|
<div class="pl" style="width:90%;"></div><div class="ps" style="width:85%;"></div><div class="ps" style="width:92%;"></div>
|
|
<div class="pl" style="width:78%;"></div><div class="ps" style="width:88%;"></div><div class="ps" style="width:70%;"></div>
|
|
<div class="pl" style="width:84%;"></div><div class="ps" style="width:90%;"></div><div class="ps" style="width:60%;"></div>
|
|
<div class="pl" style="width:75%;"></div><div class="ps" style="width:82%;"></div>
|
|
<div style="font-size:6px;color:#8A8070;margin-top:6px;text-align:right;opacity:.7;">Dein Heinrich</div>
|
|
|
|
<div class="ann-rect trans dimmed" style="left:2%;top:0%;width:50%;height:10%;"></div>
|
|
<!-- THIS annotation is highlighted — user clicked paragraph 2 -->
|
|
<div class="ann-rect trans highlight-flash" style="left:2%;top:14%;width:96%;height:32%;"></div>
|
|
<div class="ann-rect trans dimmed" style="left:2%;top:50%;width:96%;height:22%;"></div>
|
|
<div class="ann-rect trans dimmed" style="left:20%;top:80%;width:60%;height:12%;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="split-handle"></div>
|
|
|
|
<div style="width:400px;display:flex;flex-direction:column;border-left:1px solid #e4e2d7;background:#fff;">
|
|
<div class="read-text" style="flex:1;overflow-y:auto;">
|
|
<div class="para greeting">Liebe Martha,</div>
|
|
<!-- THIS paragraph is highlighted -->
|
|
<div class="para highlighted">ich schreibe Dir heute aus dem Lazarett in Breslau. Mach Dir keine Sorgen, es geht mir den Umständen entsprechend gut. Der Arzt sagt <span class="illegible">[unleserlich]</span> Wochen noch dauern wird.</div>
|
|
<div class="para">Die Kinder sollen wissen, dass ich an sie denke. Sag dem kleinen Fritz, er soll auf seine Mutter aufpassen. Und Lotte soll weiter so fleißig in der Schule sein.</div>
|
|
<div class="para closing">In ewiger Liebe,<br/>Dein Heinrich</div>
|
|
</div>
|
|
<div class="status-bar">
|
|
<span>4 Abschnitte</span>
|
|
<span style="margin-left:auto;">Zuletzt bearbeitet: Oma Inge, 14:23</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="agent">
|
|
<h4>S2 · Scroll sync highlight</h4>
|
|
<pre>/* Bidirectional scroll sync with visual feedback.
|
|
*
|
|
* Text → PDF:
|
|
* 1. User clicks a paragraph
|
|
* 2. Paragraph gets .highlighted class (turquoise bg at 10%)
|
|
* 3. Matching annotation rect gets .highlight-flash class
|
|
* 4. PDF viewport scrolls to center the annotation
|
|
* 5. Both highlights fade over 1.5s via CSS animation
|
|
*
|
|
* PDF → Text:
|
|
* 1. User clicks a dimmed annotation rect
|
|
* 2. Annotation flashes (same .highlight-flash)
|
|
* 3. Matching paragraph gets .highlighted
|
|
* 4. Text panel scrolls to center the paragraph
|
|
* 5. Both fade over 1.5s
|
|
*
|
|
* Implementation: each paragraph has data-block-id matching the
|
|
* transcription block's annotation_id. The annotation rects already
|
|
* have annotation IDs from transcribe mode. */</pre>
|
|
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
|
|
<tr class="grp"><td colspan="3">Highlight animation</td></tr>
|
|
<tr><td>Paragraph bg</td><td>rgba(0,199,177,.10)</td><td>Turquoise at 10%, fades to 0</td></tr>
|
|
<tr><td>Annotation flash</td><td>rgba(0,199,177,.18) → .04</td><td>Border returns to .3 opacity</td></tr>
|
|
<tr><td>Duration</td><td>1.5s ease-out</td><td>CSS animation, no JS timers needed</td></tr>
|
|
<tr><td>Scroll behavior</td><td>smooth, block: center</td><td>scrollIntoView({ behavior: 'smooth', block: 'center' })</td></tr>
|
|
<tr class="grp"><td colspan="3">Data binding</td></tr>
|
|
<tr><td>Paragraph attr</td><td>data-block-id="{annotation_id}"</td><td>Links text to PDF annotation</td></tr>
|
|
<tr><td>Annotation attr</td><td>data-annotation-id="{id}"</td><td>Already exists from transcribe mode</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
S3: DESKTOP — NO TRANSCRIPTION YET
|
|
═══════════════════════════════════════════════════════════════════════════ -->
|
|
<div class="scr" id="s3">
|
|
<div class="scr-head"><h3>S3 — No transcription (empty state)</h3><span class="scr-id">S3</span></div>
|
|
<div class="scr-desc">When no transcription blocks exist, the mode switcher defaults to “Bearbeiten” and the right panel shows an empty state encouraging the user to start transcribing. The “Lesen” tab is disabled (greyed out).</div>
|
|
<div class="scr-var"><strong>Empty state</strong> — no read mode available until at least one block exists.</div>
|
|
|
|
<div class="previews">
|
|
<div class="prev-col">
|
|
<div class="bp-lbl">Desktop · 1040px</div>
|
|
<div class="desk" style="min-height:400px;">
|
|
<div class="fa-nav">
|
|
<div class="fa-logo">FAMILIENARCHIV</div>
|
|
<div class="fa-link">Dokumente</div>
|
|
<div class="fa-link">Personen</div>
|
|
<div class="fa-nav-r"><div class="fa-av">MR</div></div>
|
|
</div>
|
|
<div class="fa-topbar">
|
|
<div class="back">←</div>
|
|
<div class="title">Brief von Heinrich an Martha, 14. Mai 1943</div>
|
|
<div style="flex:1"></div>
|
|
<div class="details-toggle">Details ▼</div>
|
|
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
|
|
<!-- Mode switcher: Bearbeiten active, Lesen disabled -->
|
|
<div class="mode-sw"><span style="opacity:.35;cursor:not-allowed;">Lesen</span><span class="active">Bearbeiten</span></div>
|
|
</div>
|
|
|
|
<div class="split" style="height:320px;">
|
|
<div style="flex:1;display:flex;flex-direction:column;">
|
|
<div class="pdf-area" style="flex:1;">
|
|
<div class="paper" style="width:55%;min-height:200px;">
|
|
<div style="font-size:7px;color:#8A8070;font-style:italic;margin-bottom:4px;opacity:.7;">Liebe Martha,</div>
|
|
<div class="pl" style="width:90%;"></div><div class="ps" style="width:85%;"></div><div class="ps" style="width:92%;"></div>
|
|
<div class="pl" style="width:78%;"></div><div class="ps" style="width:88%;"></div>
|
|
<div style="font-size:6px;color:#8A8070;margin-top:6px;text-align:right;opacity:.7;">Dein Heinrich</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="split-handle"></div>
|
|
|
|
<div style="width:400px;display:flex;flex-direction:column;border-left:1px solid #e4e2d7;background:#fff;align-items:center;justify-content:center;text-align:center;padding:32px;">
|
|
<div style="width:48px;height:48px;border-radius:50%;background:var(--sand);display:flex;align-items:center;justify-content:center;margin-bottom:12px;">
|
|
<span style="font-size:20px;opacity:.5;">✎</span>
|
|
</div>
|
|
<div style="font-family:var(--font-sans);font-size:11px;font-weight:600;color:var(--color-text);margin-bottom:4px;">Noch keine Transkription</div>
|
|
<div style="font-family:var(--font-sans);font-size:10px;color:var(--color-text-muted);max-width:200px;line-height:1.6;">Zeichne Bereiche auf dem Scan und tippe den Text ab, um eine Transkription zu erstellen.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="agent">
|
|
<h4>S3 · Empty state</h4>
|
|
<pre>/* When transcription_blocks count is 0:
|
|
* - Mode switcher defaults to "Bearbeiten"
|
|
* - "Lesen" tab is disabled: opacity 0.35, cursor: not-allowed, not clickable
|
|
* - Right panel shows empty state with pencil icon, title, and description
|
|
* - No status bar (nothing to show)
|
|
*
|
|
* As soon as the first block is saved, "Lesen" becomes enabled.
|
|
* The mode does NOT auto-switch — user stays in Bearbeiten. */</pre>
|
|
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
|
|
<tr class="grp"><td colspan="3">Empty state</td></tr>
|
|
<tr><td>Icon</td><td>Pencil in 48px sand circle</td><td>Centered vertically in panel</td></tr>
|
|
<tr><td>Title</td><td>"Noch keine Transkription"</td><td>i18n key: transcription_empty_title</td></tr>
|
|
<tr><td>Description</td><td>"Zeichne Bereiche auf dem Scan…"</td><td>i18n key: transcription_empty_desc</td></tr>
|
|
<tr class="grp"><td colspan="3">Mode switcher</td></tr>
|
|
<tr><td>Lesen tab</td><td>Disabled: opacity .35, not-allowed</td><td>Enabled when block count > 0</td></tr>
|
|
<tr><td>Default</td><td>"Bearbeiten" active</td><td>Enters transcribe mode directly</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
S4: MOBILE — READ MODE
|
|
═══════════════════════════════════════════════════════════════════════════ -->
|
|
<div class="scr" id="s4">
|
|
<div class="scr-head"><h3>S4 — Mobile read mode</h3><span class="scr-id">S4</span></div>
|
|
<div class="scr-desc">On mobile, the split view becomes vertical: a collapsible PDF strip (70px) at the top, flowing text below. The mode switcher abbreviates “Bearbeiten” to “Bearb.” to fit. Tapping the PDF strip expands it; tapping again collapses. Paragraphs are still tappable for scroll-sync.</div>
|
|
<div class="scr-var"><strong>Mobile layout</strong> — stacked vertical with collapsible scan strip.</div>
|
|
|
|
<div class="previews">
|
|
<div class="prev-col">
|
|
<div class="bp-lbl">Mobile · 320px</div>
|
|
<div class="phone" style="height:620px;">
|
|
<div class="pst"><b>14:23</b><span>••• WiFi 🔋</span></div>
|
|
<div class="pb">
|
|
<div style="background:#fff;border-bottom:1px solid #e4e2d7;padding:6px 12px;display:flex;align-items:center;gap:6px;">
|
|
<span style="font-size:11px;color:var(--color-text-muted);">←</span>
|
|
<span style="font-family:Georgia,serif;font-size:11px;color:var(--navy);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">Brief von Heinrich, 14.05.1943</span>
|
|
<div class="mode-sw"><span class="active" style="font-size:7px;">Lesen</span><span style="font-size:7px;">Bearb.</span></div>
|
|
</div>
|
|
<!-- Collapsible PDF strip -->
|
|
<div style="background:#D4D0C8;height:70px;display:flex;align-items:center;justify-content:center;border-bottom:1px solid #C4C0B8;position:relative;">
|
|
<div style="background:#FFFEF8;width:42%;padding:5px 7px;box-shadow:0 1px 3px rgba(0,0,0,.1);border-radius:1px;">
|
|
<div style="font-size:5px;color:#8A8070;font-style:italic;opacity:.7;">Liebe Martha,</div>
|
|
<div style="height:2px;background:#C4BDB0;opacity:.4;margin:2px 0;width:80%;"></div>
|
|
<div style="height:1.5px;background:#C4BDB0;opacity:.2;margin:1px 0;width:90%;"></div>
|
|
</div>
|
|
<!-- Expand hint -->
|
|
<div style="position:absolute;bottom:3px;right:8px;font-size:6px;color:var(--color-text-muted);opacity:.6;">▲ Scan vergrößern</div>
|
|
</div>
|
|
<!-- Flowing text -->
|
|
<div style="flex:1;overflow-y:auto;padding:16px 16px;background:#fff;">
|
|
<div style="font-family:'Tinos',Georgia,serif;font-size:13px;line-height:1.9;color:var(--color-text);">
|
|
<p style="margin-bottom:10px;font-style:italic;">Liebe Martha,</p>
|
|
<p style="margin-bottom:10px;">ich schreibe Dir heute aus dem Lazarett in Breslau. Mach Dir keine Sorgen, es geht mir den Umständen entsprechend gut. Der Arzt sagt <em style="color:var(--color-text-muted);">[unleserlich]</em> Wochen noch dauern wird.</p>
|
|
<p style="margin-bottom:10px;">Die Kinder sollen wissen, dass ich an sie denke. Sag dem kleinen Fritz, er soll auf seine Mutter aufpassen. Und Lotte soll weiter so fleißig in der Schule sein.</p>
|
|
<p style="text-align:right;font-style:italic;">In ewiger Liebe,<br/>Dein Heinrich</p>
|
|
</div>
|
|
</div>
|
|
<!-- Status bar -->
|
|
<div style="background:var(--sand);border-top:1px solid #e4e2d7;padding:4px 12px;font-size:8px;color:var(--color-text-muted);display:flex;justify-content:space-between;">
|
|
<span>4 Abschnitte</span>
|
|
<span>Oma Inge, 14:23</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mobile: expanded PDF strip -->
|
|
<div class="prev-col">
|
|
<div class="bp-lbl">Mobile · 320px · scan expanded</div>
|
|
<div class="phone" style="height:620px;">
|
|
<div class="pst"><b>14:23</b><span>••• WiFi 🔋</span></div>
|
|
<div class="pb">
|
|
<div style="background:#fff;border-bottom:1px solid #e4e2d7;padding:6px 12px;display:flex;align-items:center;gap:6px;">
|
|
<span style="font-size:11px;color:var(--color-text-muted);">←</span>
|
|
<span style="font-family:Georgia,serif;font-size:11px;color:var(--navy);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">Brief von Heinrich, 14.05.1943</span>
|
|
<div class="mode-sw"><span class="active" style="font-size:7px;">Lesen</span><span style="font-size:7px;">Bearb.</span></div>
|
|
</div>
|
|
<!-- Expanded PDF strip -->
|
|
<div style="background:#D4D0C8;height:200px;display:flex;align-items:center;justify-content:center;border-bottom:1px solid #C4C0B8;position:relative;">
|
|
<div style="background:#FFFEF8;width:50%;padding:8px 10px;box-shadow:0 2px 6px rgba(0,0,0,.12);border-radius:1px;position:relative;">
|
|
<div style="font-size:6px;color:#8A8070;font-style:italic;opacity:.7;margin-bottom:3px;">Liebe Martha,</div>
|
|
<div class="pl" style="width:90%;height:2px;"></div><div class="ps" style="width:85%;height:1.5px;"></div><div class="ps" style="width:92%;height:1.5px;"></div>
|
|
<div class="pl" style="width:78%;height:2px;"></div><div class="ps" style="width:88%;height:1.5px;"></div>
|
|
<div class="pl" style="width:84%;height:2px;"></div><div class="ps" style="width:70%;height:1.5px;"></div>
|
|
<div style="font-size:5px;color:#8A8070;margin-top:4px;text-align:right;opacity:.7;">Dein Heinrich</div>
|
|
<!-- Dimmed annotations visible when expanded -->
|
|
<div class="ann-rect trans dimmed" style="left:3%;top:0%;width:45%;height:12%;"></div>
|
|
<div class="ann-rect trans dimmed" style="left:3%;top:16%;width:94%;height:35%;"></div>
|
|
</div>
|
|
<!-- Collapse hint -->
|
|
<div style="position:absolute;bottom:3px;right:8px;font-size:6px;color:var(--color-text-muted);opacity:.6;">▼ Scan verkleinern</div>
|
|
</div>
|
|
<!-- Text below (shorter) -->
|
|
<div style="flex:1;overflow-y:auto;padding:14px 16px;background:#fff;">
|
|
<div style="font-family:'Tinos',Georgia,serif;font-size:13px;line-height:1.9;color:var(--color-text);">
|
|
<p style="margin-bottom:10px;font-style:italic;">Liebe Martha,</p>
|
|
<p style="margin-bottom:10px;">ich schreibe Dir heute aus dem Lazarett in Breslau…</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="agent">
|
|
<h4>S4 · Mobile read mode</h4>
|
|
<pre>/* On viewports < 768px, the side-by-side split becomes vertical:
|
|
* - PDF scan strip at top (collapsed: 70px, expanded: ~50vh)
|
|
* - Flowing text below, full-width
|
|
* - Tap PDF strip to toggle expand/collapse
|
|
* - Expand hint text: "▲ Scan vergrößern" / "▼ Scan verkleinern"
|
|
*
|
|
* Mode switcher abbreviates: "Lesen | Bearb."
|
|
* Scroll-sync: tapping a paragraph briefly highlights the matching
|
|
* region in the expanded PDF. If PDF is collapsed, it auto-expands
|
|
* first, then scrolls to the annotation.
|
|
*
|
|
* Same status bar at the bottom, same flowing prose styling. */</pre>
|
|
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
|
|
<tr class="grp"><td colspan="3">PDF strip</td></tr>
|
|
<tr><td>Collapsed height</td><td>70px</td><td>Shows miniature scan preview</td></tr>
|
|
<tr><td>Expanded height</td><td>~50vh or max 300px</td><td>Smooth CSS transition (300ms ease)</td></tr>
|
|
<tr><td>Toggle</td><td>Tap anywhere on strip</td><td>Hint text in bottom-right corner</td></tr>
|
|
<tr class="grp"><td colspan="3">Text area</td></tr>
|
|
<tr><td>Font</td><td>Tinos, 15px, line-height 1.9</td><td>Slightly larger than desktop for touch</td></tr>
|
|
<tr><td>Padding</td><td>16px</td><td>Full-width, no wasted space</td></tr>
|
|
<tr class="grp"><td colspan="3">Mode switcher</td></tr>
|
|
<tr><td>Labels</td><td>"Lesen | Bearb."</td><td>Abbreviated to fit mobile topbar</td></tr>
|
|
<tr><td>Font size</td><td>10px</td><td>Compact but readable</td></tr>
|
|
<tr class="grp"><td colspan="3">Scroll sync on mobile</td></tr>
|
|
<tr><td>Tap paragraph</td><td>Expand PDF if collapsed, then highlight</td><td>Auto-expand + scroll + flash</td></tr>
|
|
<tr><td>Tap annotation</td><td>Collapse PDF, scroll text to paragraph</td><td>Smart collapse after showing match</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
S5: MODE SWITCHER DETAIL
|
|
═══════════════════════════════════════════════════════════════════════════ -->
|
|
<div class="scr" id="s5">
|
|
<div class="scr-head"><h3>S5 — Mode switcher states</h3><span class="scr-id">S5</span></div>
|
|
<div class="scr-desc">The mode switcher is a segmented control in the topbar that replaces the previous “Transkribieren” turquoise button. It governs three visual states: <strong>Lesen</strong> (read mode, this spec), <strong>Bearbeiten</strong> (transcribe/edit mode), and the existing <strong>Annotieren</strong> button (yellow comment annotations). The modes are mutually exclusive.</div>
|
|
<div class="scr-var"><strong>Segmented control</strong> — replacing the turquoise “Transkribieren” button.</div>
|
|
|
|
<div class="previews" style="gap:16px;">
|
|
<!-- State 1: Lesen active -->
|
|
<div class="prev-col" style="align-items:center;">
|
|
<div class="bp-lbl">Lesen active</div>
|
|
<div style="background:#fff;border:1px solid #e4e2d7;border-radius:var(--radius-md);padding:12px 16px;display:flex;align-items:center;gap:8px;">
|
|
<div class="mode-sw" style="font-size:8px;"><span class="active" style="padding:4px 12px;">Lesen</span><span style="padding:4px 12px;">Bearbeiten</span></div>
|
|
<div style="width:1px;height:20px;background:#e4e2d7;"></div>
|
|
<div class="fa-topbar-btn ghost" style="font-size:8px;padding:4px 10px;">✎ Annotieren</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- State 2: Bearbeiten active -->
|
|
<div class="prev-col" style="align-items:center;">
|
|
<div class="bp-lbl">Bearbeiten active</div>
|
|
<div style="background:#fff;border:1px solid #e4e2d7;border-radius:var(--radius-md);padding:12px 16px;display:flex;align-items:center;gap:8px;">
|
|
<div class="mode-sw" style="font-size:8px;"><span style="padding:4px 12px;">Lesen</span><span class="active" style="padding:4px 12px;">Bearbeiten</span></div>
|
|
<div style="width:1px;height:20px;background:#e4e2d7;"></div>
|
|
<div class="fa-topbar-btn ghost" style="font-size:8px;padding:4px 10px;">✎ Annotieren</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- State 3: Annotieren active -->
|
|
<div class="prev-col" style="align-items:center;">
|
|
<div class="bp-lbl">Annotieren active (separate button)</div>
|
|
<div style="background:#fff;border:1px solid #e4e2d7;border-radius:var(--radius-md);padding:12px 16px;display:flex;align-items:center;gap:8px;">
|
|
<div class="mode-sw" style="font-size:8px;"><span style="padding:4px 12px;opacity:.5;">Lesen</span><span style="padding:4px 12px;opacity:.5;">Bearbeiten</span></div>
|
|
<div style="width:1px;height:20px;background:#e4e2d7;"></div>
|
|
<div class="fa-topbar-btn" style="font-size:8px;padding:4px 10px;background:var(--navy);color:#fff;border-color:var(--navy);">✎ Annotieren</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- State 4: Lesen disabled (no blocks) -->
|
|
<div class="prev-col" style="align-items:center;">
|
|
<div class="bp-lbl">No transcription blocks</div>
|
|
<div style="background:#fff;border:1px solid #e4e2d7;border-radius:var(--radius-md);padding:12px 16px;display:flex;align-items:center;gap:8px;">
|
|
<div class="mode-sw" style="font-size:8px;"><span style="padding:4px 12px;opacity:.35;cursor:not-allowed;">Lesen</span><span class="active" style="padding:4px 12px;">Bearbeiten</span></div>
|
|
<div style="width:1px;height:20px;background:#e4e2d7;"></div>
|
|
<div class="fa-topbar-btn ghost" style="font-size:8px;padding:4px 10px;">✎ Annotieren</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="agent">
|
|
<h4>S5 · Mode switcher states</h4>
|
|
<pre>/* Three mutually exclusive modes:
|
|
*
|
|
* 1. Lesen (read) — this spec. Flowing prose, dimmed annotations, no editing.
|
|
* 2. Bearbeiten (edit) — annotation-transcription-final-spec. Block cards, contenteditable.
|
|
* 3. Annotieren — yellow comment annotations on PDF. Separate button, not in segmented control.
|
|
*
|
|
* The segmented control only contains Lesen + Bearbeiten.
|
|
* Annotieren is a separate button that, when active, deselects both Lesen and Bearbeiten
|
|
* (both appear deselected/dimmed in the segmented control).
|
|
*
|
|
* When the user clicks Annotieren while in read/transcribe mode:
|
|
* → Enter annotate mode, both segmented items dim
|
|
* When the user clicks a segmented item while in annotate mode:
|
|
* → Exit annotate mode, enter the selected mode
|
|
*
|
|
* State: let mode: 'read' | 'transcribe' | 'annotate' = $state(...)
|
|
* Default: 'read' if blocks.length > 0, else 'transcribe'
|
|
* The "Annotieren" button is hidden if !canAnnotate || !isPdf */</pre>
|
|
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
|
|
<tr class="grp"><td colspan="3">Segmented control</td></tr>
|
|
<tr><td>Items</td><td>"Lesen" | "Bearbeiten"</td><td>Mobile: "Lesen" | "Bearb."</td></tr>
|
|
<tr><td>Active style</td><td>bg:navy, color:#fff</td><td>Rounded within the pill</td></tr>
|
|
<tr><td>Inactive style</td><td>bg:transparent, color:muted</td><td>Hover: bg:sand</td></tr>
|
|
<tr><td>Dimmed style</td><td>Both items at opacity .5</td><td>Only when annotate mode is active</td></tr>
|
|
<tr><td>Disabled (Lesen)</td><td>opacity .35, cursor not-allowed</td><td>When no transcription blocks exist</td></tr>
|
|
<tr class="grp"><td colspan="3">Annotieren button</td></tr>
|
|
<tr><td>Default</td><td>Ghost style (border:muted)</td><td>Same as current topbar button</td></tr>
|
|
<tr><td>Active</td><td>bg:navy, color:#fff</td><td>Filled state when annotate mode on</td></tr>
|
|
<tr><td>Visibility</td><td>canAnnotate && isPdf</td><td>Hidden for non-PDF documents</td></tr>
|
|
<tr class="grp"><td colspan="3">Accessibility</td></tr>
|
|
<tr><td>Segmented</td><td>role="tablist", children role="tab"</td><td>aria-selected on active tab</td></tr>
|
|
<tr><td>Annotieren</td><td>aria-pressed={annotateMode}</td><td>Toggle button semantics</td></tr>
|
|
<tr><td>Disabled tab</td><td>aria-disabled="true", tabindex="-1"</td><td>Not focusable when no blocks</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ═══ LLM IMPLEMENTATION GUIDE ═══ -->
|
|
<div class="llm">
|
|
<h2>Implementation Guide — Transcription Read Mode (Clean Split)</h2>
|
|
|
|
<h3>1. Overview</h3>
|
|
<p>Read mode is the default view for documents that have transcription blocks. It reuses the same side-by-side split layout as transcribe mode but replaces the editable block cards with flowing serif prose. The goal is a distraction-free reading experience that still lets users compare handwriting with typed text.</p>
|
|
|
|
<h3>2. Mode State Management</h3>
|
|
<p>The document detail page manages a single <code>mode</code> state that governs the entire view:</p>
|
|
<pre>let mode: 'read' | 'transcribe' | 'annotate' = $state(
|
|
blocks.length > 0 ? 'read' : 'transcribe'
|
|
);</pre>
|
|
<ul>
|
|
<li><code>mode === 'read'</code> → this spec (flowing prose, dimmed annotations, no editing)</li>
|
|
<li><code>mode === 'transcribe'</code> → annotation-transcription-final-spec (block cards, contenteditable)</li>
|
|
<li><code>mode === 'annotate'</code> → yellow comment annotations on PDF</li>
|
|
<li>The segmented control in the topbar toggles between <code>'read'</code> and <code>'transcribe'</code>.</li>
|
|
<li>The “Annotieren” button toggles <code>'annotate'</code> on/off. When entering annotate mode, the previous mode (read or transcribe) is stored so the user returns to it when exiting.</li>
|
|
</ul>
|
|
|
|
<h3>3. Component Architecture</h3>
|
|
<h4>3a. New components</h4>
|
|
<table>
|
|
<thead><tr><th>Component</th><th>Purpose</th></tr></thead>
|
|
<tbody>
|
|
<tr><td><code>TranscriptionReadView.svelte</code></td><td>Right panel content in read mode. Renders transcription blocks as flowing prose (<code><article></code> with <code><p></code> per block). Handles scroll-sync click handlers.</td></tr>
|
|
<tr><td><code>ModeSwitcher.svelte</code></td><td>Segmented control (<code>Lesen | Bearbeiten</code>). Props: <code>mode</code> (bindable), <code>hasBlocks</code> (disables Lesen when false). Emits mode changes.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h4>3b. Modified components</h4>
|
|
<table>
|
|
<thead><tr><th>Component</th><th>Change</th></tr></thead>
|
|
<tbody>
|
|
<tr><td><code>DocumentTopBar.svelte</code></td><td>Replace the <code>Transkribieren</code> button with <code>ModeSwitcher</code>. Keep the <code>Annotieren</code> button separate. Add <code>mode</code> bindable prop.</td></tr>
|
|
<tr><td><code>[id]/+page.svelte</code></td><td>Add <code>mode</code> state. Conditionally render <code>TranscriptionReadView</code> vs <code>TranscriptionEditView</code> in the right panel based on <code>mode</code>.</td></tr>
|
|
<tr><td><code>PdfAnnotationLayer.svelte</code></td><td>Accept <code>dimmed</code> prop. When true: annotation rects get opacity 0.3, no numbered badges, but remain clickable for scroll-sync.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3>4. Read View Rendering</h3>
|
|
<h4>4a. Text rendering</h4>
|
|
<ul>
|
|
<li>Fetch transcription blocks from <code>GET /api/documents/{id}/transcription-blocks</code> (same endpoint as transcribe mode).</li>
|
|
<li>Render each block as a <code><p data-block-id="{block.annotation_id}"></code> inside an <code><article></code> element.</li>
|
|
<li>Typography: <code>font-family: Tinos, Georgia, serif; font-size: 16px; line-height: 1.85</code>.</li>
|
|
<li>Padding: <code>24px 32px</code> for comfortable reading margins.</li>
|
|
<li><code>[unleserlich]</code> markers: detect via regex <code>/\[unleserlich\]/g</code> and wrap in <code><em class="text-ink-2 italic text-[0.9em]"></code>.</li>
|
|
<li>Text is <strong>not</strong> contenteditable. No cursor, no selection highlights, no editing.</li>
|
|
</ul>
|
|
|
|
<h4>4b. Scroll sync</h4>
|
|
<ul>
|
|
<li>Each paragraph has a click handler that dispatches a <code>highlight-annotation</code> event with the <code>annotation_id</code>.</li>
|
|
<li>The PDF viewer listens for this event, scrolls to the annotation, and applies a CSS animation (<code>flash-fade</code>, 1.5s ease-out).</li>
|
|
<li>Reverse direction: clicking a dimmed annotation on the PDF dispatches <code>highlight-paragraph</code> with the <code>annotation_id</code>. The text panel scrolls the matching paragraph into view and applies a background highlight that fades.</li>
|
|
<li>Use <code>scrollIntoView({ behavior: 'smooth', block: 'center' })</code> for both directions.</li>
|
|
<li>The highlight CSS animation: <code>background rgba(0,199,177,.10) → transparent</code> over 1.5s.</li>
|
|
</ul>
|
|
|
|
<h3>5. PDF Annotations in Read Mode</h3>
|
|
<ul>
|
|
<li>Turquoise annotation rectangles are rendered but <strong>dimmed</strong>: border opacity 0.3, background opacity 0.04.</li>
|
|
<li>No numbered badges (the <code>.ann-num</code> elements are hidden via <code>display: none</code>).</li>
|
|
<li>Annotations remain clickable — they trigger scroll-sync to the matching paragraph.</li>
|
|
<li>When an annotation is flash-highlighted (via scroll-sync), it briefly returns to full opacity before fading back to dimmed.</li>
|
|
<li>Yellow comment annotations are not shown in read mode (they belong to annotate mode only).</li>
|
|
</ul>
|
|
|
|
<h3>6. Status Bar</h3>
|
|
<ul>
|
|
<li>Positioned at the bottom of the text panel (not the full viewport).</li>
|
|
<li>Content: <code>"{n} Abschnitte · Zuletzt bearbeitet: {userName}, {HH:mm}"</code></li>
|
|
<li>The “Zuletzt bearbeitet” timestamp is the most recent <code>updated_at</code> across all transcription blocks for this document.</li>
|
|
<li>The user name comes from the <code>updated_by</code> field of that most recently updated block.</li>
|
|
<li>i18n keys: <code>transcription_status_sections</code>, <code>transcription_status_last_edited</code>.</li>
|
|
</ul>
|
|
|
|
<h3>7. Mobile Layout (viewport < 768px)</h3>
|
|
<ul>
|
|
<li>The side-by-side split becomes vertical: PDF strip at top, text below.</li>
|
|
<li>PDF strip collapsed height: <code>70px</code>. Shows a miniature scan preview.</li>
|
|
<li>Tap strip to expand (~50vh, max 300px). Tap again to collapse. Smooth CSS transition (300ms ease).</li>
|
|
<li>Expand/collapse hint text in bottom-right corner of the strip.</li>
|
|
<li>Mode switcher abbreviation: “Lesen | Bearb.” (i18n key: <code>mode_edit_short</code>).</li>
|
|
<li>Scroll-sync on paragraph tap: if PDF is collapsed, auto-expand first, then scroll to annotation.</li>
|
|
<li>Text typography: <code>15px</code> (slightly larger than desktop) with <code>line-height: 1.9</code>.</li>
|
|
</ul>
|
|
|
|
<h3>8. Empty State</h3>
|
|
<ul>
|
|
<li>When <code>transcription_blocks</code> count is 0, “Lesen” tab is disabled (<code>opacity: 0.35</code>, <code>cursor: not-allowed</code>, <code>aria-disabled="true"</code>).</li>
|
|
<li>Mode defaults to <code>'transcribe'</code>.</li>
|
|
<li>Right panel shows empty state: pencil icon in 48px sand circle, title (“Noch keine Transkription”), description (“Zeichne Bereiche auf dem Scan…”).</li>
|
|
<li>As soon as the first block is saved, “Lesen” becomes clickable. Mode does not auto-switch.</li>
|
|
</ul>
|
|
|
|
<h3>9. i18n Keys</h3>
|
|
<table>
|
|
<thead><tr><th>Key</th><th>de</th><th>en</th></tr></thead>
|
|
<tbody>
|
|
<tr><td><code>mode_read</code></td><td>Lesen</td><td>Read</td></tr>
|
|
<tr><td><code>mode_edit</code></td><td>Bearbeiten</td><td>Edit</td></tr>
|
|
<tr><td><code>mode_edit_short</code></td><td>Bearb.</td><td>Edit</td></tr>
|
|
<tr><td><code>transcription_status_sections</code></td><td>{n} Abschnitte</td><td>{n} sections</td></tr>
|
|
<tr><td><code>transcription_status_last_edited</code></td><td>Zuletzt bearbeitet: {name}, {time}</td><td>Last edited: {name}, {time}</td></tr>
|
|
<tr><td><code>transcription_empty_title</code></td><td>Noch keine Transkription</td><td>No transcription yet</td></tr>
|
|
<tr><td><code>transcription_empty_desc</code></td><td>Zeichne Bereiche auf dem Scan und tippe den Text ab, um eine Transkription zu erstellen.</td><td>Draw regions on the scan and type the text to create a transcription.</td></tr>
|
|
<tr><td><code>scan_expand</code></td><td>Scan vergrößern</td><td>Expand scan</td></tr>
|
|
<tr><td><code>scan_collapse</code></td><td>Scan verkleinern</td><td>Collapse scan</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3>10. Accessibility</h3>
|
|
<ul>
|
|
<li>Mode switcher: <code>role="tablist"</code> with <code>role="tab"</code> children, <code>aria-selected</code> on active tab.</li>
|
|
<li>Disabled “Lesen” tab: <code>aria-disabled="true"</code>, <code>tabindex="-1"</code>.</li>
|
|
<li>Read view text: semantic HTML — <code><article></code> wrapping <code><p></code> elements. No <code>contenteditable</code>.</li>
|
|
<li>Paragraphs are clickable: <code>role="button"</code>, <code>tabindex="0"</code>, <code>aria-label="Abschnitt N — klicken um Scan-Position anzuzeigen"</code>.</li>
|
|
<li>PDF strip toggle on mobile: <code>role="button"</code>, <code>aria-expanded="{expanded}"</code>, <code>aria-label="Scan {expanded ? 'verkleinern' : 'vergrößern'}"</code>.</li>
|
|
<li>Scroll-sync animations respect <code>prefers-reduced-motion</code>: skip the 1.5s fade, apply instant highlight that disappears after 200ms.</li>
|
|
</ul>
|
|
</div>
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|