898 lines
70 KiB
HTML
898 lines
70 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8"/>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
<title>Annotation-Backed Transcription — 3 Variations</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;--accent-bg:rgba(161,220,216,.12);--blue-tint:#E6F1FB;--blue:#2D7DD2;--blue-dark:#185FA5;--purple-tint:#EEEDFE;--purple:#534AB7;--purple-dark:#3C3489;--green-tint:#E8F5EA;--green:#3D8C4A;--green-dark:#2E6E39;--orange-tint:#FEF0E6;--orange:#E8862A;--orange-dark:#B46820;--yellow-tint:#FDF6D8;--yellow-text:#8A6800;--color-error:#DC4C3E;--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-b{background:var(--blue-tint);color:var(--blue-dark);}
|
|
.pill-g{background:var(--green-tint);color:var(--green-dark);}
|
|
|
|
.section{margin-bottom:64px;}
|
|
.section-title{font-size:10px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--color-text-muted);padding-bottom:10px;border-bottom:1px solid var(--color-border);margin-bottom:24px;}
|
|
.prose{font-size:13px;color:var(--color-text-muted);line-height:1.65;max-width:720px;margin-bottom:20px;}
|
|
|
|
.jh{padding:20px 24px;border-radius:var(--radius-xl);margin-bottom:40px;display:flex;align-items:center;gap:16px;}
|
|
.jh .jn{font-family:var(--font-display);font-size:48px;font-weight:300;line-height:1;opacity:.5;}
|
|
.jh h2{font-family:var(--font-display);font-size:22px;font-weight:500;letter-spacing:-.02em;margin-bottom:4px;}
|
|
.jh p{font-size:13px;line-height:1.5;}.jh .fl{font-family:var(--font-mono);font-size:11px;margin-top:6px;opacity:.7;}
|
|
.jh-b{background:var(--blue-tint);border:1px solid #A4CFF4;}.jh-b .jn{color:var(--blue);}.jh-b p,.jh-b .fl{color:var(--blue-dark);}
|
|
|
|
.scr{margin-bottom:56px;}
|
|
.scr-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;}
|
|
.scr-head h3{font-family:var(--font-display);font-size:20px;font-weight:500;letter-spacing:-.02em;}
|
|
.scr-id{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);padding:2px 8px;border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-page);}
|
|
.scr-desc{font-size:12px;color:var(--color-text-muted);line-height:1.6;max-width:720px;margin-bottom:6px;}
|
|
.scr-var{font-size:11px;color:var(--color-text-muted);margin-bottom:20px;}.scr-var strong{color:var(--color-text);}
|
|
|
|
.previews{display:flex;gap:32px;flex-wrap:wrap;justify-content:center;align-items:flex-start;margin-bottom:20px;}
|
|
.prev-col{display:flex;flex-direction:column;align-items:center;gap:10px;}
|
|
.bp-lbl{font-family:var(--font-mono);font-size:10px;color:var(--color-text-muted);}
|
|
|
|
.desk{width:100%;max-width:1040px;background:var(--color-page);border-radius:var(--radius-xl);overflow:hidden;box-shadow:var(--shadow-overlay),0 0 0 1px rgba(0,0,0,.06);display:flex;flex-direction:column;min-height: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);}
|
|
|
|
.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%;background:var(--navy);display:flex;align-items:center;justify-content:center;font-size:5px;font-weight:800;color:var(--mint);}
|
|
.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;}
|
|
.fa-topbar-btn.active{background:var(--navy);color:#fff;border-color:var(--navy);}
|
|
.fa-topbar-btn.ghost{border-color:var(--color-border);color:var(--color-text-muted);font-weight:500;}
|
|
.fa-topbar-btn.transcribe{background:var(--turquoise);color:var(--navy);border-color:var(--turquoise);font-weight:700;}
|
|
|
|
/* ── PDF + paper ── */
|
|
.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 rectangles on PDF ── */
|
|
.ann-rect{position:absolute;border-radius:2px;pointer-events:auto;cursor:pointer;transition:all .15s ease;}
|
|
.ann-rect.comment{border:1.5px solid rgba(255,200,0,.6);background:rgba(255,200,0,.15);}
|
|
.ann-rect.comment:hover{background:rgba(255,200,0,.3);}
|
|
.ann-rect.trans{border:1.5px solid var(--turquoise);background:rgba(0,199,177,.1);}
|
|
.ann-rect.trans:hover{background:rgba(0,199,177,.2);}
|
|
.ann-rect.trans.active{background:rgba(0,199,177,.25);box-shadow:0 0 0 2px var(--turquoise);}
|
|
.ann-rect .ann-num{position:absolute;top:-8px;left:-8px;width:16px;height:16px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:7px;font-weight:700;color:#fff;box-shadow:0 1px 3px rgba(0,0,0,.3);}
|
|
.ann-rect.trans .ann-num{background:var(--navy);}
|
|
.ann-rect.comment .ann-num{background:var(--orange);}
|
|
.ann-rect .ann-badge{position:absolute;bottom:-8px;right:-8px;background:var(--navy);color:#fff;font-size:6px;font-weight:700;padding:1px 4px;border-radius:8px;min-width:14px;text-align:center;box-shadow:0 1px 2px rgba(0,0,0,.3);}
|
|
|
|
/* ── Split + panels ── */
|
|
.split{display:flex;flex:1;overflow:hidden;}
|
|
.split-left{flex:1;display:flex;flex-direction:column;overflow:hidden;position:relative;}
|
|
.split-right{display:flex;flex-direction:column;overflow:hidden;border-left:1px solid #e4e2d7;}
|
|
.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;}
|
|
|
|
/* ── Transcript blocks ── */
|
|
.tblock{margin-bottom:6px;border:1px solid var(--color-border);border-radius:5px;overflow:hidden;transition:all .15s ease;}
|
|
.tblock.active{border-color:var(--turquoise);box-shadow:0 0 0 1px var(--turquoise);}
|
|
.tblock.empty{border-style:dashed;opacity:.7;}
|
|
.tblock-head{display:flex;align-items:center;gap:4px;padding:3px 8px;font-size:6px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--color-text-muted);}
|
|
.tblock-head.active-bg{background:rgba(0,199,177,.08);}
|
|
.tblock-head .num{width:14px;height:14px;border-radius:50%;background:var(--navy);color:#fff;display:flex;align-items:center;justify-content:center;font-size:6px;font-weight:700;flex-shrink:0;}
|
|
.tblock-body{padding:5px 8px;font-family:Georgia,serif;font-size:9px;line-height:1.65;color:var(--color-text);min-height:18px;}
|
|
.tblock-body.editing{background:var(--color-page);cursor:text;}
|
|
.tblock-body .illegible{color:var(--color-text-muted);font-style:italic;}
|
|
.tblock-footer{display:flex;align-items:center;gap:4px;padding:2px 8px;border-top:1px solid var(--color-subtle);font-size:6px;color:var(--color-text-muted);}
|
|
|
|
.trans-cursor{display:inline-block;width:1px;height:10px;background:var(--blue);animation:blink 1s infinite;margin-left:1px;}
|
|
@keyframes blink{0%,50%{opacity:1}51%,100%{opacity:0}}
|
|
|
|
/* ── Presence ── */
|
|
.presence{display:flex;align-items:center;gap:3px;font-size:7px;color:var(--color-text-muted);}
|
|
.presence-dot{width:5px;height:5px;border-radius:50%;}
|
|
.hl-blue{border-left:2px solid var(--blue);padding-left:6px;background:rgba(45,125,210,.04);}
|
|
.hl-purple{border-left:2px solid var(--purple);padding-left:6px;background:rgba(83,74,183,.04);}
|
|
|
|
/* ── Comment / thread UI ── */
|
|
.inline-thread{margin:3px 8px 5px;padding:5px 8px;border-radius:4px;border-left:2px solid var(--orange);background:var(--orange-tint);font-size:8px;color:var(--color-text);}
|
|
.inline-thread .thread-head{font-size:6px;font-weight:600;color:var(--orange-dark);margin-bottom:2px;display:flex;align-items:center;gap:3px;}
|
|
.inline-thread .thread-msg{display:flex;gap:3px;align-items:flex-start;margin-bottom:2px;}
|
|
.inline-thread .thread-av{width:12px;height:12px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:5px;font-weight:800;color:#fff;flex-shrink:0;}
|
|
.inline-thread .thread-reply{display:flex;gap:3px;margin-top:3px;}
|
|
.inline-thread input{flex:1;font-size:7px;padding:2px 5px;border:1px solid var(--color-border);border-radius:3px;background:#fff;}
|
|
.inline-thread .resolve-btn{font-size:6px;font-weight:600;color:var(--green-dark);padding:2px 5px;cursor:pointer;}
|
|
|
|
/* ── Margin notes ── */
|
|
.margin-note{position:absolute;right:-130px;width:120px;background:#fff;border:1px solid var(--color-border);border-radius:4px;box-shadow:var(--shadow-card);padding:5px 6px;font-size:7px;color:var(--color-text);line-height:1.5;}
|
|
.margin-note::before{content:'';position:absolute;left:-6px;top:8px;width:6px;height:1px;background:var(--color-border);}
|
|
.margin-note .mn-head{font-size:5px;font-weight:600;color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:2px;}
|
|
.margin-note .mn-av{width:10px;height:10px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:4px;font-weight:800;color:#fff;flex-shrink:0;}
|
|
|
|
/* ── Hint strip ── */
|
|
.hint-strip{display:flex;align-items:center;gap:6px;padding:0 12px;height:22px;border-top:1px dashed;flex-shrink:0;font-size:7px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;}
|
|
.hint-strip.trans-hint{background:rgba(0,199,177,.06);border-color:rgba(0,199,177,.3);color:var(--navy);}
|
|
.hint-strip .hint-step{display:flex;align-items:center;gap:3px;font-weight:500;color:var(--color-text-muted);text-transform:none;letter-spacing:0;}
|
|
|
|
/* ── Status + tabs ── */
|
|
.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;}
|
|
.status-saved{color:var(--green-dark);}
|
|
|
|
.bp-tabs{background:#fff;border-top:1px solid #e4e2d7;display:flex;align-items:center;height:24px;padding:0 8px;flex-shrink:0;}
|
|
.bp-tab{font-size:7px;font-weight:500;padding:0 6px;color:var(--color-text-muted);height:100%;display:flex;align-items:center;border-bottom:2px solid transparent;}
|
|
.bp-tab.active{color:var(--navy);border-bottom-color:var(--navy);}
|
|
.bp-badge{margin-left:3px;background:var(--navy);color:#fff;border-radius:6px;padding:0 3px;font-size:5px;font-weight:700;}
|
|
|
|
/* ── Connector lines between PDF and transcript ── */
|
|
.connector{position:absolute;pointer-events:none;}
|
|
.connector line{stroke:var(--turquoise);stroke-width:1;stroke-dasharray:3,2;opacity:.5;}
|
|
.connector line.active{opacity:1;stroke-width:1.5;stroke-dasharray:none;}
|
|
|
|
/* ── 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 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);}
|
|
|
|
@media(max-width:900px){.doc{padding:24px 16px 80px;}}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="doc">
|
|
|
|
<div class="doc-header">
|
|
<div>
|
|
<h1>Annotation-Backed Transcription</h1>
|
|
<p>Three variations that reuse the existing annotation system (draw rectangle on PDF → linked content) as the backbone for transcription. Annotations get a <code>type</code> field: <code>"comment"</code> (existing behavior) or <code>"transcription"</code> (new — links a PDF region to a transcript block). Comments move from the annotation side panel into the transcription editor.</p>
|
|
</div>
|
|
<div class="doc-meta">
|
|
Familienarchiv<br/>
|
|
<span class="pill pill-g">Reuse-first</span><br/>
|
|
2026-04-04 · @leonievoss
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<div class="section-title">Core idea</div>
|
|
<p class="prose">Today, annotations are rectangles on the PDF that open a comment thread in the side panel. The insight: if we add a <code>type</code> field to <code>DocumentAnnotation</code>, the same draw-a-rectangle gesture can create either a <strong>comment annotation</strong> (existing) or a <strong>transcription annotation</strong> (new). A transcription annotation links a PDF region to an editable text block. The existing <code>AnnotationLayer</code>, <code>PdfViewer</code>, and <code>CommentThread</code> components all stay — we layer new behavior on top.</p>
|
|
<p class="prose">Comments no longer live under annotations. Instead, they live <strong>inside the transcript</strong> — anchored to text ranges, specific blocks, or as margin notes. This frees annotation rectangles to be purely spatial markers: “this region of the scan corresponds to this text.”</p>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<div class="section-title">What stays, what changes</div>
|
|
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;font-size:12px;line-height:1.6;">
|
|
<div style="background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;">
|
|
<div style="font-weight:600;color:var(--navy);margin-bottom:6px;">Reused as-is</div>
|
|
<ul style="padding-left:16px;color:var(--color-text-muted);">
|
|
<li><code>AnnotationLayer</code> — draw rects on PDF</li>
|
|
<li><code>PdfViewer</code> — render, zoom, page nav</li>
|
|
<li><code>CommentThread</code> — threaded replies, mentions</li>
|
|
<li><code>DocumentAnnotation</code> model — add <code>type</code> field</li>
|
|
<li><code>DocumentComment</code> model — unchanged</li>
|
|
<li><code>AnnotateHintStrip</code> — new copy for transcribe mode</li>
|
|
</ul>
|
|
</div>
|
|
<div style="background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;">
|
|
<div style="font-weight:600;color:var(--orange);margin-bottom:6px;">Repurposed</div>
|
|
<ul style="padding-left:16px;color:var(--color-text-muted);">
|
|
<li><code>AnnotationSidePanel</code> → becomes the transcript editor panel (same slot, different content)</li>
|
|
<li><code>annotateMode</code> state → split into <code>annotateMode</code> + <code>transcribeMode</code></li>
|
|
<li>Annotation color → turquoise for transcription, yellow for comments</li>
|
|
</ul>
|
|
</div>
|
|
<div style="background:#fff;border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;">
|
|
<div style="font-weight:600;color:var(--green);margin-bottom:6px;">New</div>
|
|
<ul style="padding-left:16px;color:var(--color-text-muted);">
|
|
<li><code>transcription_blocks</code> table — annotation_id, text, sort_order</li>
|
|
<li>Transcript editor component (right panel)</li>
|
|
<li>Inline comment anchoring (text-range or block-level)</li>
|
|
<li><code>type</code> column on <code>document_annotations</code></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="jh jh-b">
|
|
<div class="jn">T</div>
|
|
<div><h2>Draw-to-transcribe</h2><p>Draw a rectangle around a passage on the scan. A transcript block appears in the editor, linked to that region. Type what you read. Rinse and repeat down the page. Others can join and work on different blocks.</p><div class="fl">Reuses: AnnotationLayer + PdfViewer + CommentThread · New: TranscriptBlock + type:transcription</div></div>
|
|
</div>
|
|
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
V1 — INLINE COMMENT THREADS IN TRANSCRIPT BLOCKS
|
|
═══════════════════════════════════════════════════════════════════════════ -->
|
|
<div class="scr" id="v1">
|
|
<div class="scr-head"><h3>V1 — Inline comment threads in transcript blocks</h3><span class="scr-id">V1</span></div>
|
|
<div class="scr-desc">Each annotation rectangle on the PDF creates a numbered transcript block in the right panel. Comments are inline threads <em>inside</em> each block — highlight a word or phrase, click “Diskutieren”, and a thread appears below the block text. Threads use the existing <code>CommentThread</code> component but are anchored to a text range within a block. Both annotation types (turquoise for transcription, yellow for comment) coexist on the same PDF.</div>
|
|
<div class="scr-var"><strong>Annotation-backed blocks + inline text-anchored threads</strong> — Google Docs-style comments within structured blocks.</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="presence" style="margin-right:4px;"><div class="presence-dot" style="background:var(--blue);"></div> Du</div>
|
|
<div class="presence" style="margin-right:4px;"><div class="presence-dot" style="background:var(--purple);"></div> Oma Inge</div>
|
|
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
|
|
<div class="fa-topbar-btn transcribe">✎ Transkribieren</div>
|
|
<div class="fa-topbar-btn ghost">Annotieren</div>
|
|
</div>
|
|
|
|
<!-- Hint strip -->
|
|
<div class="hint-strip trans-hint">
|
|
<span>Transkribieren</span>
|
|
<span class="hint-step">— Markiere eine Textpassage im Scan, um einen Transkriptions-Block anzulegen</span>
|
|
</div>
|
|
|
|
<div class="split" style="height:380px;">
|
|
<!-- PDF with annotation rectangles -->
|
|
<div class="split-left">
|
|
<div class="pdf-area" style="flex:1;">
|
|
<div class="paper" style="width:55%;min-height:220px;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>
|
|
|
|
<!-- Transcription annotations (turquoise) -->
|
|
<div class="ann-rect trans" style="left:2%;top:0%;width:50%;height:10%;">
|
|
<div class="ann-num">1</div>
|
|
</div>
|
|
<div class="ann-rect trans active" style="left:2%;top:14%;width:96%;height:32%;">
|
|
<div class="ann-num">2</div>
|
|
</div>
|
|
<div class="ann-rect trans" style="left:2%;top:50%;width:96%;height:22%;">
|
|
<div class="ann-num">3</div>
|
|
</div>
|
|
<div class="ann-rect trans" style="left:20%;top:80%;width:60%;height:12%;">
|
|
<div class="ann-num">4</div>
|
|
</div>
|
|
|
|
<!-- Comment annotation (yellow) — coexists -->
|
|
<div class="ann-rect comment" style="left:52%;top:28%;width:35%;height:8%;">
|
|
<div class="ann-num">💬</div>
|
|
<div class="ann-badge">2</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="split-handle"></div>
|
|
|
|
<!-- Transcript editor -->
|
|
<div class="split-right" style="width:380px;">
|
|
<div style="background:#fff;border-bottom:1px solid #e4e2d7;display:flex;align-items:center;padding:4px 8px;gap:6px;flex-shrink:0;">
|
|
<span style="font-size:7px;font-weight:600;color:var(--navy);">4 Blöcke</span>
|
|
<div style="flex:1;"></div>
|
|
<span style="font-size:7px;color:var(--green-dark);">✓ Gespeichert</span>
|
|
</div>
|
|
|
|
<div style="flex:1;overflow-y:auto;padding:6px 8px;background:#fff;display:flex;flex-direction:column;gap:4px;">
|
|
|
|
<!-- Block 1 — Greeting (done) -->
|
|
<div class="tblock">
|
|
<div class="tblock-head"><div class="num">1</div> Anrede <span style="margin-left:auto;color:var(--green-dark);">✓</span></div>
|
|
<div class="tblock-body">Liebe Martha,</div>
|
|
</div>
|
|
|
|
<!-- Block 2 — Main body (active, being edited) -->
|
|
<div class="tblock active">
|
|
<div class="tblock-head active-bg">
|
|
<div class="num">2</div> Hauptteil
|
|
<div class="presence" style="margin-left:auto;"><div class="presence-dot" style="background:var(--purple);width:4px;height:4px;"></div> Oma Inge</div>
|
|
</div>
|
|
<div class="tblock-body editing hl-purple">ich schreibe Dir heute aus dem Lazarett in <span style="background:rgba(232,134,42,.15);border-bottom:2px solid var(--orange);padding:0 1px;">Breslau</span>. 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>
|
|
|
|
<!-- Inline thread on "Breslau" -->
|
|
<div class="inline-thread">
|
|
<div class="thread-head">💬 Diskussion — “Breslau”</div>
|
|
<div class="thread-msg">
|
|
<div class="thread-av" style="background:var(--purple);">OI</div>
|
|
<div><strong style="font-size:7px;">Oma Inge</strong> · Ich bin sicher, das ist “Breslau” — Heinrich war dort im Lazarett.</div>
|
|
</div>
|
|
<div class="thread-msg">
|
|
<div class="thread-av" style="background:var(--blue);">DU</div>
|
|
<div><strong style="font-size:7px;">Du</strong> · Stimmt, danke! Lass ich so.</div>
|
|
</div>
|
|
<div class="thread-reply">
|
|
<input placeholder="Antworten..."/>
|
|
<div class="resolve-btn">✓ Lösen</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Block 3 — Family (being edited by current user) -->
|
|
<div class="tblock active" style="border-color:var(--blue);box-shadow:0 0 0 1px var(--blue);">
|
|
<div class="tblock-head" style="background:rgba(45,125,210,.06);">
|
|
<div class="num">3</div> Familie
|
|
<div class="presence" style="margin-left:auto;"><div class="presence-dot" style="background:var(--blue);width:4px;height:4px;"></div> Du</div>
|
|
</div>
|
|
<div class="tblock-body editing hl-blue">Die Kinder sollen wissen, dass ich an sie denke. Sag dem kleinen Fritz, er soll auf seine Mutter aufpassen.<span class="trans-cursor"></span></div>
|
|
</div>
|
|
|
|
<!-- Block 4 — Closing (done) -->
|
|
<div class="tblock">
|
|
<div class="tblock-head"><div class="num">4</div> Schluss <span style="margin-left:auto;color:var(--green-dark);">✓</span></div>
|
|
<div class="tblock-body">In ewiger Liebe,<br/>Dein Heinrich</div>
|
|
</div>
|
|
|
|
<!-- Add block CTA -->
|
|
<div class="tblock empty" style="text-align:center;padding:8px;font-size:7px;color:var(--color-text-muted);cursor:pointer;">
|
|
Markiere eine weitere Passage im Scan, um Block 5 anzulegen
|
|
</div>
|
|
</div>
|
|
|
|
<div class="status-bar">
|
|
<span>Block 2 aktiv</span>
|
|
<span>Oma Inge · Block 2</span>
|
|
<span style="margin-left:auto;">1 offene Diskussion</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-tabs">
|
|
<div class="bp-tab">Metadaten</div>
|
|
<div class="bp-tab">Verlauf</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mobile -->
|
|
<div class="previews" style="margin-top:20px;">
|
|
<div class="prev-col">
|
|
<div class="bp-lbl">Mobile · 320px</div>
|
|
<div class="phone">
|
|
<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>
|
|
<span style="font-size:7px;font-weight:700;padding:2px 6px;border-radius:3px;background:var(--turquoise);color:var(--navy);">Transkr.</span>
|
|
</div>
|
|
<!-- PDF strip with annotation rectangles -->
|
|
<div style="background:#D4D0C8;height:90px;display:flex;align-items:center;justify-content:center;position:relative;border-bottom:2px solid var(--turquoise);">
|
|
<div style="background:#FFFEF8;width:45%;padding:6px 8px;box-shadow:0 1px 4px rgba(0,0,0,.12);border-radius:1px;position:relative;">
|
|
<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:.25;margin:1px 0;width:90%;"></div>
|
|
<div style="height:1.5px;background:#C4BDB0;opacity:.25;margin:1px 0;width:70%;"></div>
|
|
<!-- Turquoise annotations visible as thin outlines -->
|
|
<div style="position:absolute;left:2%;top:0;width:50%;height:18%;border:1px solid var(--turquoise);border-radius:1px;opacity:.5;"></div>
|
|
<div style="position:absolute;left:2%;top:22%;width:96%;height:35%;border:1px solid var(--turquoise);border-radius:1px;background:rgba(0,199,177,.1);"></div>
|
|
</div>
|
|
</div>
|
|
<!-- Block list -->
|
|
<div style="flex:1;overflow-y:auto;padding:8px 12px;background:#fff;">
|
|
<div style="display:flex;align-items:center;gap:4px;margin-bottom:6px;">
|
|
<span style="font-size:8px;font-weight:600;color:var(--navy);">4 Blöcke</span>
|
|
<span style="font-size:7px;color:var(--green-dark);margin-left:auto;">✓ Gespeichert</span>
|
|
</div>
|
|
<div style="border:1px solid var(--color-border);border-radius:5px;overflow:hidden;margin-bottom:6px;">
|
|
<div style="padding:3px 8px;font-size:6px;font-weight:600;color:var(--color-text-muted);display:flex;align-items:center;gap:3px;background:var(--sand);"><div style="width:12px;height:12px;border-radius:50%;background:var(--navy);color:#fff;display:flex;align-items:center;justify-content:center;font-size:5px;font-weight:700;">1</div> Anrede <span style="margin-left:auto;color:var(--green-dark);">✓</span></div>
|
|
<div style="padding:4px 8px;font-family:Georgia,serif;font-size:10px;line-height:1.6;">Liebe Martha,</div>
|
|
</div>
|
|
<div style="border:1px solid var(--turquoise);border-radius:5px;overflow:hidden;margin-bottom:6px;box-shadow:0 0 0 1px var(--turquoise);">
|
|
<div style="padding:3px 8px;font-size:6px;font-weight:600;color:var(--color-text-muted);display:flex;align-items:center;gap:3px;background:rgba(0,199,177,.08);"><div style="width:12px;height:12px;border-radius:50%;background:var(--navy);color:#fff;display:flex;align-items:center;justify-content:center;font-size:5px;font-weight:700;">2</div> Hauptteil <span style="font-size:5px;color:var(--purple);margin-left:auto;">Oma Inge</span></div>
|
|
<div style="padding:4px 8px;font-family:Georgia,serif;font-size:10px;line-height:1.6;border-left:2px solid var(--purple);">ich schreibe Dir heute aus dem Lazarett in Breslau...</div>
|
|
</div>
|
|
<div style="border:1px solid var(--blue);border-radius:5px;overflow:hidden;margin-bottom:6px;box-shadow:0 0 0 1px var(--blue);">
|
|
<div style="padding:3px 8px;font-size:6px;font-weight:600;color:var(--color-text-muted);display:flex;align-items:center;gap:3px;background:rgba(45,125,210,.06);"><div style="width:12px;height:12px;border-radius:50%;background:var(--navy);color:#fff;display:flex;align-items:center;justify-content:center;font-size:5px;font-weight:700;">3</div> Familie <span style="font-size:5px;color:var(--blue);margin-left:auto;">Du</span></div>
|
|
<div style="padding:4px 8px;font-family:Georgia,serif;font-size:10px;line-height:1.6;border-left:2px solid var(--blue);">Die Kinder sollen wissen...<span class="trans-cursor"></span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="agent">
|
|
<h4>V1 · Inline comment threads in transcript blocks</h4>
|
|
<pre>/* Core flow: enter transcribe mode → crosshair cursor on PDF → draw rect → creates:
|
|
* 1. DocumentAnnotation(type:"transcription", turquoise) in the DB
|
|
* 2. TranscriptionBlock(annotation_id, text:"", sort_order:N) in the DB
|
|
* 3. Editable block in the right panel, linked to the annotation
|
|
* Clicking an annotation rect on PDF scrolls to + highlights the matching block.
|
|
* Clicking a block header highlights the matching rect on PDF.
|
|
* Comments: select text within a block → "Diskutieren" → creates a CommentThread
|
|
* anchored to (block_id, char_offset_start, char_offset_end).
|
|
* Existing yellow comment annotations continue to work as before — they open the
|
|
* AnnotationSidePanel. Only turquoise annotations feed the transcript editor.
|
|
* This reuses: AnnotationLayer (draw), PdfViewer (render), CommentThread (replies/mentions).
|
|
* Mobile: PDF collapses to 90px strip, blocks stack vertically below. */</pre>
|
|
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
|
|
<tr class="grp"><td colspan="3">Annotation reuse</td></tr>
|
|
<tr><td>Draw gesture</td><td>Existing AnnotationLayer.onDraw(rect)</td><td>Same pointer events. crosshair cursor.</td></tr>
|
|
<tr><td>Annotation color</td><td>turquoise (#00C7B1) for transcription</td><td>Yellow kept for comment annotations</td></tr>
|
|
<tr><td>Annotation type</td><td>New column: type VARCHAR "transcription"|"comment"</td><td>Default "comment" for backward compat</td></tr>
|
|
<tr><td>Number badge</td><td>16px navy circle, top-left of rect</td><td>Sort order number, matches block number</td></tr>
|
|
<tr class="grp"><td colspan="3">Transcript blocks (right panel)</td></tr>
|
|
<tr><td>Block card</td><td>border:1px line, radius:5px, active: turquoise glow</td><td>Header: number + label + presence. Body: contenteditable.</td></tr>
|
|
<tr><td>Inline thread</td><td>orange left-border, orange-tint bg, below block body</td><td>Text-anchored via char offset. Reuses CommentThread.</td></tr>
|
|
<tr><td>Block label</td><td>Editable text, defaults: Anrede, Hauptteil, Schluss</td><td>Double-click to rename</td></tr>
|
|
<tr class="grp"><td colspan="3">Interaction</td></tr>
|
|
<tr><td>Click rect → block</td><td>scrollIntoView + active state on block</td><td>Turquoise glow on both rect and block</td></tr>
|
|
<tr><td>Click block → rect</td><td>PDF scrolls/zooms to show the annotation</td><td>If multi-page: switches page</td></tr>
|
|
<tr><td>Delete block</td><td>Deletes annotation + block + threads</td><td>Confirm dialog if threads exist</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
V2 — MARGIN NOTES (MANUSCRIPT-STYLE)
|
|
═══════════════════════════════════════════════════════════════════════════ -->
|
|
<div class="scr" id="v2">
|
|
<div class="scr-head"><h3>V2 — Margin notes (manuscript-style)</h3><span class="scr-id">V2</span></div>
|
|
<div class="scr-desc">Same annotation-backed transcript blocks, but comments appear as <strong>margin notes</strong> beside the blocks rather than inline threads. Small note cards float to the right of the block they refer to, connected by a thin line — like handwritten marginalia on a manuscript. This feels more appropriate for a letter archive and avoids the visual weight of inline thread UIs. Notes can be replies (click existing note to add) or new (click the margin area next to a block).</div>
|
|
<div class="scr-var"><strong>Annotation-backed blocks + margin notes</strong> — lightweight, manuscript-style commenting that doesn’t break the reading flow.</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="presence" style="margin-right:4px;"><div class="presence-dot" style="background:var(--blue);"></div> Du</div>
|
|
<div class="presence" style="margin-right:4px;"><div class="presence-dot" style="background:var(--purple);"></div> Oma Inge</div>
|
|
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
|
|
<div class="fa-topbar-btn transcribe">✎ Transkribieren</div>
|
|
</div>
|
|
|
|
<div class="hint-strip trans-hint">
|
|
<span>Transkribieren</span>
|
|
<span class="hint-step">— Markiere Passagen im Scan. Klicke rechts neben einen Block für eine Randnotiz.</span>
|
|
</div>
|
|
|
|
<div class="split" style="height:380px;">
|
|
<div class="split-left">
|
|
<div class="pdf-area" style="flex:1;">
|
|
<div class="paper" style="width:55%;min-height:220px;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" style="left:2%;top:0%;width:50%;height:10%;"><div class="ann-num">1</div></div>
|
|
<div class="ann-rect trans active" style="left:2%;top:14%;width:96%;height:32%;"><div class="ann-num">2</div></div>
|
|
<div class="ann-rect trans" style="left:2%;top:50%;width:96%;height:22%;"><div class="ann-num">3</div></div>
|
|
<div class="ann-rect trans" style="left:20%;top:80%;width:60%;height:12%;"><div class="ann-num">4</div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="split-handle"></div>
|
|
|
|
<!-- Transcript + margin area -->
|
|
<div class="split-right" style="width:400px;display:flex;flex-direction:column;">
|
|
<div style="background:#fff;border-bottom:1px solid #e4e2d7;display:flex;align-items:center;padding:4px 8px;gap:6px;flex-shrink:0;">
|
|
<span style="font-size:7px;font-weight:600;color:var(--navy);">4 Blöcke</span>
|
|
<span style="font-size:7px;color:var(--color-text-muted);margin-left:4px;">2 Randnotizen</span>
|
|
<div style="flex:1;"></div>
|
|
<span style="font-size:7px;color:var(--green-dark);">✓ Gespeichert</span>
|
|
</div>
|
|
|
|
<div style="flex:1;overflow-y:auto;display:flex;">
|
|
<!-- Blocks column -->
|
|
<div style="flex:1;padding:6px 8px;display:flex;flex-direction:column;gap:4px;min-width:0;">
|
|
|
|
<div class="tblock">
|
|
<div class="tblock-head"><div class="num">1</div> Anrede <span style="margin-left:auto;color:var(--green-dark);">✓</span></div>
|
|
<div class="tblock-body">Liebe Martha,</div>
|
|
</div>
|
|
|
|
<div class="tblock active" style="position:relative;">
|
|
<div class="tblock-head active-bg">
|
|
<div class="num">2</div> Hauptteil
|
|
<div class="presence" style="margin-left:auto;"><div class="presence-dot" style="background:var(--purple);width:4px;height:4px;"></div> Oma Inge</div>
|
|
</div>
|
|
<div class="tblock-body editing hl-purple">ich schreibe Dir heute aus dem Lazarett in <strong style="color:var(--orange);text-decoration:underline;text-decoration-color:var(--orange);text-underline-offset:2px;">Breslau</strong>. 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>
|
|
|
|
<div class="tblock active" style="border-color:var(--blue);box-shadow:0 0 0 1px var(--blue);">
|
|
<div class="tblock-head" style="background:rgba(45,125,210,.06);">
|
|
<div class="num">3</div> Familie
|
|
<div class="presence" style="margin-left:auto;"><div class="presence-dot" style="background:var(--blue);width:4px;height:4px;"></div> Du</div>
|
|
</div>
|
|
<div class="tblock-body editing hl-blue">Die Kinder sollen wissen, dass ich an sie denke. Sag dem kleinen Fritz, er soll auf seine Mutter aufpassen.<span class="trans-cursor"></span></div>
|
|
</div>
|
|
|
|
<div class="tblock">
|
|
<div class="tblock-head"><div class="num">4</div> Schluss <span style="margin-left:auto;color:var(--green-dark);">✓</span></div>
|
|
<div class="tblock-body">In ewiger Liebe,<br/>Dein Heinrich</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Margin notes column -->
|
|
<div style="width:130px;flex-shrink:0;position:relative;padding:6px 4px 6px 0;">
|
|
<!-- Note for "Breslau" in Block 2 -->
|
|
<div style="position:relative;margin-bottom:8px;margin-top:48px;">
|
|
<div style="position:absolute;left:0;top:8px;width:8px;height:1px;background:var(--color-border);"></div>
|
|
<div style="margin-left:10px;background:#fff;border:1px solid var(--color-border);border-radius:4px;box-shadow:var(--shadow-card);padding:5px 6px;font-size:7px;color:var(--color-text);line-height:1.5;">
|
|
<div style="font-size:5px;font-weight:600;color:var(--orange-dark);text-transform:uppercase;letter-spacing:.05em;margin-bottom:2px;">“Breslau” · Block 2</div>
|
|
<div style="display:flex;gap:3px;align-items:flex-start;margin-bottom:3px;">
|
|
<div style="width:10px;height:10px;border-radius:50%;background:var(--purple);display:flex;align-items:center;justify-content:center;font-size:4px;font-weight:800;color:#fff;flex-shrink:0;">OI</div>
|
|
<div>Ich bin sicher: “Breslau”. Heinrich war dort stationiert.</div>
|
|
</div>
|
|
<div style="display:flex;gap:3px;align-items:flex-start;">
|
|
<div style="width:10px;height:10px;border-radius:50%;background:var(--blue);display:flex;align-items:center;justify-content:center;font-size:4px;font-weight:800;color:#fff;flex-shrink:0;">DU</div>
|
|
<div>Danke, klingt richtig!</div>
|
|
</div>
|
|
<div style="margin-top:3px;padding-top:3px;border-top:1px solid var(--color-subtle);display:flex;gap:2px;">
|
|
<input style="flex:1;font-size:6px;padding:2px 4px;border:1px solid var(--color-border);border-radius:2px;" placeholder="Antworten..."/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Note for [unleserlich] in Block 2 -->
|
|
<div style="position:relative;margin-bottom:8px;">
|
|
<div style="position:absolute;left:0;top:8px;width:8px;height:1px;background:var(--color-border);"></div>
|
|
<div style="margin-left:10px;background:#fff;border:1px solid var(--color-border);border-radius:4px;box-shadow:var(--shadow-card);padding:5px 6px;font-size:7px;color:var(--color-text);line-height:1.5;">
|
|
<div style="font-size:5px;font-weight:600;color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:2px;">[unleserlich] · Block 2</div>
|
|
<div style="display:flex;gap:3px;align-items:flex-start;">
|
|
<div style="width:10px;height:10px;border-radius:50%;background:var(--blue);display:flex;align-items:center;justify-content:center;font-size:4px;font-weight:800;color:#fff;flex-shrink:0;">DU</div>
|
|
<div>Könnte “sechs” oder “acht” sein. Wer hat die Originale?</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="status-bar">
|
|
<span>Block 3 aktiv</span>
|
|
<span style="margin-left:auto;">2 offene Notizen</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-tabs">
|
|
<div class="bp-tab">Metadaten</div>
|
|
<div class="bp-tab">Verlauf</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="agent">
|
|
<h4>V2 · Margin notes (manuscript-style)</h4>
|
|
<pre>/* Same annotation-backed blocks as V1, different comment mechanism.
|
|
* Comments appear as small cards in a 130px margin column to the right of the blocks.
|
|
* Each note is connected to its source text via a thin horizontal line.
|
|
* Notes are positioned vertically to align with the text they reference.
|
|
* If notes would overlap, they stack downward with 8px gap.
|
|
*
|
|
* Creating a note: select text in a block → "Randnotiz" button or right-click context menu.
|
|
* Or: click the margin area next to a block to create a general block note.
|
|
* Note data model: CommentThread (documentId, blockId, charOffsetStart, charOffsetEnd).
|
|
*
|
|
* Advantages: doesn't disrupt reading flow. Feels like marginalia on a manuscript.
|
|
* Disadvantages: narrow notes column — long discussions get cramped.
|
|
* For long threads: clicking "4 weitere..." expands the note into a popover.
|
|
* Mobile: margin notes collapse to icons (small circles). Tap to expand inline. */</pre>
|
|
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
|
|
<tr class="grp"><td colspan="3">Margin column</td></tr>
|
|
<tr><td>Width</td><td>130px, flex-shrink:0</td><td>To the right of blocks column</td></tr>
|
|
<tr><td>Note card</td><td>bg:white, border:line, radius:4px, shadow:card</td><td>7px body text, 5px header</td></tr>
|
|
<tr><td>Connector line</td><td>8px wide, 1px solid line, horizontal</td><td>Connects left edge of note to block boundary</td></tr>
|
|
<tr><td>Vertical position</td><td>Aligned to the referenced text line</td><td>Stack with 8px gap if overlapping</td></tr>
|
|
<tr class="grp"><td colspan="3">Interactions</td></tr>
|
|
<tr><td>Create</td><td>Select text → "Randnotiz" or click margin</td><td>Block-level or text-range-level</td></tr>
|
|
<tr><td>Reply</td><td>Input at bottom of note card</td><td>Existing CommentThread reply logic</td></tr>
|
|
<tr><td>Overflow</td><td>"4 weitere..." link → expand to popover</td><td>Popover uses full CommentThread component</td></tr>
|
|
<tr><td>Mobile</td><td>Notes collapse to 10px circles</td><td>Tap to expand inline below the block</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
V3 — UNIFIED COMMENT TIMELINE + ANNOTATION BLOCKS
|
|
═══════════════════════════════════════════════════════════════════════════ -->
|
|
<div class="scr" id="v3">
|
|
<div class="scr-head"><h3>V3 — Unified comment timeline</h3><span class="scr-id">V3</span></div>
|
|
<div class="scr-desc">Same annotation-backed blocks, but comments live in the <strong>existing bottom panel Discussion tab</strong> — not inside the transcript at all. Each comment in the timeline gets a reference tag showing which block (and optionally which word) it refers to. Clicking the tag scrolls both the transcript and PDF to the referenced location. This is the least invasive approach: the transcript editor stays clean and focused on text, while discussion happens in the familiar bottom panel.</div>
|
|
<div class="scr-var"><strong>Annotation-backed blocks + bottom panel discussion</strong> — clean editor, familiar comment UI, reference tags link comments to blocks.</div>
|
|
|
|
<div class="previews">
|
|
<div class="prev-col">
|
|
<div class="bp-lbl">Desktop · 1040px</div>
|
|
<div class="desk" style="min-height:580px;">
|
|
<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="presence" style="margin-right:4px;"><div class="presence-dot" style="background:var(--blue);"></div> Du</div>
|
|
<div class="presence" style="margin-right:4px;"><div class="presence-dot" style="background:var(--purple);"></div> Oma Inge</div>
|
|
<div style="width:1px;height:16px;background:#e4e2d7;margin:0 4px;"></div>
|
|
<div class="fa-topbar-btn transcribe">✎ Transkribieren</div>
|
|
</div>
|
|
|
|
<div class="hint-strip trans-hint">
|
|
<span>Transkribieren</span>
|
|
<span class="hint-step">— Markiere Passagen im Scan. Nutze die Diskussion unten für Fragen.</span>
|
|
</div>
|
|
|
|
<div class="split" style="height:280px;">
|
|
<div class="split-left">
|
|
<div class="pdf-area" style="flex:1;">
|
|
<div class="paper" style="width:55%;min-height:160px;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" style="left:2%;top:0%;width:50%;height:10%;"><div class="ann-num">1</div></div>
|
|
<div class="ann-rect trans" style="left:2%;top:14%;width:96%;height:32%;"><div class="ann-num">2</div></div>
|
|
<div class="ann-rect trans active" style="left:2%;top:50%;width:96%;height:22%;"><div class="ann-num">3</div></div>
|
|
<div class="ann-rect trans" style="left:20%;top:80%;width:60%;height:12%;"><div class="ann-num">4</div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="split-handle"></div>
|
|
|
|
<!-- Clean transcript editor — no inline comments -->
|
|
<div class="split-right" style="width:380px;">
|
|
<div style="background:#fff;border-bottom:1px solid #e4e2d7;display:flex;align-items:center;padding:4px 8px;gap:6px;flex-shrink:0;">
|
|
<span style="font-size:7px;font-weight:600;color:var(--navy);">4 Blöcke</span>
|
|
<div style="flex:1;"></div>
|
|
<span style="font-size:7px;color:var(--green-dark);">✓ Gespeichert</span>
|
|
</div>
|
|
|
|
<div style="flex:1;overflow-y:auto;padding:6px 8px;background:#fff;display:flex;flex-direction:column;gap:4px;">
|
|
<div class="tblock">
|
|
<div class="tblock-head"><div class="num">1</div> Anrede <span style="margin-left:auto;color:var(--green-dark);">✓</span></div>
|
|
<div class="tblock-body">Liebe Martha,</div>
|
|
</div>
|
|
|
|
<div class="tblock">
|
|
<div class="tblock-head">
|
|
<div class="num">2</div> Hauptteil
|
|
<span style="margin-left:auto;font-size:6px;color:var(--orange);">💬 2</span>
|
|
</div>
|
|
<div class="tblock-body">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>
|
|
|
|
<div class="tblock active" style="border-color:var(--blue);box-shadow:0 0 0 1px var(--blue);">
|
|
<div class="tblock-head" style="background:rgba(45,125,210,.06);">
|
|
<div class="num">3</div> Familie
|
|
<div class="presence" style="margin-left:auto;"><div class="presence-dot" style="background:var(--blue);width:4px;height:4px;"></div> Du</div>
|
|
</div>
|
|
<div class="tblock-body editing hl-blue">Die Kinder sollen wissen, dass ich an sie denke. Sag dem kleinen Fritz, er soll auf seine Mutter aufpassen.<span class="trans-cursor"></span></div>
|
|
</div>
|
|
|
|
<div class="tblock">
|
|
<div class="tblock-head"><div class="num">4</div> Schluss <span style="margin-left:auto;color:var(--green-dark);">✓</span></div>
|
|
<div class="tblock-body">In ewiger Liebe,<br/>Dein Heinrich</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="status-bar">
|
|
<span>Block 3 aktiv</span>
|
|
<span style="margin-left:auto;">Oma Inge sieht zu</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bottom panel — Discussion tab with block-reference tags -->
|
|
<div style="display:flex;flex-direction:column;flex-shrink:0;height:140px;border-top:1px solid #e4e2d7;">
|
|
<div style="height:6px;background:#fff;display:flex;align-items:center;justify-content:center;cursor:ns-resize;flex-shrink:0;">
|
|
<div style="width:40px;height:3px;background:#e4e2d7;border-radius:2px;"></div>
|
|
</div>
|
|
<div class="bp-tabs" style="border-top:none;">
|
|
<div class="bp-tab">Metadaten</div>
|
|
<div class="bp-tab active">Diskussion <span class="bp-badge">3</span></div>
|
|
<div class="bp-tab">Verlauf</div>
|
|
</div>
|
|
|
|
<div style="flex:1;overflow-y:auto;padding:8px 12px;background:#fff;">
|
|
<!-- Comment 1 — references Block 2 + "Breslau" -->
|
|
<div style="display:flex;gap:6px;align-items:flex-start;margin-bottom:8px;padding-bottom:8px;border-bottom:1px solid var(--color-subtle);">
|
|
<div style="width:20px;height:20px;border-radius:50%;background:var(--purple);display:flex;align-items:center;justify-content:center;font-size:7px;font-weight:800;color:#fff;flex-shrink:0;">OI</div>
|
|
<div style="flex:1;">
|
|
<div style="display:flex;align-items:center;gap:4px;margin-bottom:2px;">
|
|
<span style="font-size:8px;font-weight:600;">Oma Inge</span>
|
|
<span style="font-size:7px;color:var(--color-text-muted);">· vor 12 Min.</span>
|
|
<!-- Block reference tag -->
|
|
<span style="font-size:6px;font-weight:600;padding:1px 5px;border-radius:3px;background:var(--accent-bg);color:var(--navy);border:1px solid var(--mint);cursor:pointer;margin-left:auto;">§2 “Breslau”</span>
|
|
</div>
|
|
<div style="font-size:9px;color:var(--color-text);line-height:1.5;">Ich bin mir sicher, das ist “Breslau” — Heinrich war dort im Lazarett stationiert, das steht auch in dem Brief vom März.</div>
|
|
<div style="font-size:8px;color:var(--blue);margin-top:2px;cursor:pointer;">Antworten</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Comment 2 — references Block 2 + [unleserlich] -->
|
|
<div style="display:flex;gap:6px;align-items:flex-start;margin-bottom:8px;padding-bottom:8px;border-bottom:1px solid var(--color-subtle);">
|
|
<div style="width:20px;height:20px;border-radius:50%;background:var(--blue);display:flex;align-items:center;justify-content:center;font-size:7px;font-weight:800;color:#fff;flex-shrink:0;">DU</div>
|
|
<div style="flex:1;">
|
|
<div style="display:flex;align-items:center;gap:4px;margin-bottom:2px;">
|
|
<span style="font-size:8px;font-weight:600;">Du</span>
|
|
<span style="font-size:7px;color:var(--color-text-muted);">· vor 5 Min.</span>
|
|
<span style="font-size:6px;font-weight:600;padding:1px 5px;border-radius:3px;background:var(--sand);color:var(--color-text-muted);border:1px solid var(--color-border);cursor:pointer;margin-left:auto;">§2 [unleserlich]</span>
|
|
</div>
|
|
<div style="font-size:9px;color:var(--color-text);line-height:1.5;">Könnte “sechs” oder “acht” Wochen sein. Wer hat Zugang zu den Originalen, um nachzuschauen?</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- New comment input -->
|
|
<div style="display:flex;gap:6px;align-items:flex-start;">
|
|
<div style="width:20px;height:20px;border-radius:50%;background:var(--navy);display:flex;align-items:center;justify-content:center;font-size:7px;font-weight:800;color:var(--mint);flex-shrink:0;">MR</div>
|
|
<div style="flex:1;display:flex;gap:4px;">
|
|
<input style="flex:1;font-size:8px;padding:5px 8px;border:1px solid var(--color-border);border-radius:4px;background:var(--color-page);" placeholder="Kommentar schreiben... (Block-Nr. wird automatisch zugeordnet)"/>
|
|
<button style="font-size:7px;font-weight:600;padding:5px 10px;border-radius:4px;background:var(--navy);color:#fff;border:none;cursor:pointer;">Senden</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="agent">
|
|
<h4>V3 · Unified comment timeline</h4>
|
|
<pre>/* Simplest comment approach. The transcript editor is clean — no inline threads, no margins.
|
|
* All discussion happens in the existing bottom panel Discussion tab.
|
|
* Change: each comment gets an optional block_id + char_offset reference.
|
|
* When the user is editing a block and posts a comment, the reference is auto-attached.
|
|
* The reference renders as a clickable tag: "§2 'Breslau'" in accent-bg.
|
|
* Clicking the tag: scrolls transcript to block + highlights text, scrolls PDF to annotation.
|
|
* Block headers show a small orange chat-bubble count when comments reference that block.
|
|
*
|
|
* How comments get block references:
|
|
* 1. Auto: if cursor is in a block when posting, that block is referenced.
|
|
* 2. Manual: select text in a block → right-click → "In Diskussion erwähnen" → opens comment
|
|
* input in bottom panel with the reference pre-filled.
|
|
* 3. The §N tag in the comment is clickable — navigates to block + PDF region.
|
|
*
|
|
* Reuses: CommentThread (unchanged), bottom panel (unchanged), PanelDiscussion (add ref tag UI).
|
|
* New: block_id + char_offset_start + char_offset_end on DocumentComment (nullable, backward compat).
|
|
* Pro: least invasive, transcript stays clean. Con: discussion is physically separated from text. */</pre>
|
|
<table class="at"><thead><tr><th>Element</th><th>Value</th><th>Notes</th></tr></thead><tbody>
|
|
<tr class="grp"><td colspan="3">Comment reference tag</td></tr>
|
|
<tr><td>Tag</td><td>6px/600, accent-bg, mint border, radius:3px</td><td>Shows: §N + quoted text (max 20 chars)</td></tr>
|
|
<tr><td>Click</td><td>Scrolls transcript to block + PDF to annotation</td><td>Both get active/highlight state</td></tr>
|
|
<tr><td>Auto-attach</td><td>If cursor in block when posting → ref auto-set</td><td>Can be removed before sending</td></tr>
|
|
<tr class="grp"><td colspan="3">Block header badge</td></tr>
|
|
<tr><td>Badge</td><td>orange chat-bubble icon + count, 6px</td><td>Click opens bottom panel filtered to that block</td></tr>
|
|
<tr class="grp"><td colspan="3">Data model</td></tr>
|
|
<tr><td>DocumentComment</td><td>+ block_id (nullable UUID), + char_start, + char_end</td><td>All nullable — backward compat with existing comments</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ═══ LLM IMPLEMENTATION GUIDE ═══ -->
|
|
<div class="llm">
|
|
<h2>Implementation Guide — Annotation-Backed Transcription</h2>
|
|
|
|
<h3>1. Variation Comparison</h3>
|
|
<table>
|
|
<thead><tr><th>Var.</th><th>Comment mechanism</th><th>Transcript editor</th><th>Reuse level</th><th>Complexity</th></tr></thead>
|
|
<tbody>
|
|
<tr><td><strong>V1</strong></td><td>Inline threads (Google Docs-style)</td><td>Blocks with embedded thread UI</td><td>High (AnnotationLayer, CommentThread)</td><td>Medium</td></tr>
|
|
<tr><td><strong>V2</strong></td><td>Margin notes (manuscript-style)</td><td>Blocks + 130px margin column</td><td>High (AnnotationLayer, CommentThread)</td><td>Medium</td></tr>
|
|
<tr><td><strong>V3</strong></td><td>Bottom panel discussion + reference tags</td><td>Clean blocks, no inline comments</td><td>Very high (everything reused)</td><td>Low</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3>2. Shared Foundation (all three variations)</h3>
|
|
|
|
<h4>Data model changes</h4>
|
|
<ul>
|
|
<li><strong><code>document_annotations</code></strong>: add <code>type VARCHAR DEFAULT 'comment'</code>. Values: <code>'comment'</code> (existing behavior) or <code>'transcription'</code>.</li>
|
|
<li><strong>New table <code>transcription_blocks</code></strong>:
|
|
<code>id UUID PK, annotation_id UUID FK, document_id UUID FK, text TEXT, label VARCHAR, sort_order INT, created_by UUID, updated_by UUID, updated_at TIMESTAMP</code></li>
|
|
<li>The full transcript = <code>SELECT text FROM transcription_blocks WHERE document_id = ? ORDER BY sort_order</code>, concatenated.</li>
|
|
<li><strong>Backward compatibility</strong>: the existing <code>Document.transcription</code> field becomes a computed read-only view (concatenation of blocks). Write operations go through blocks.</li>
|
|
</ul>
|
|
|
|
<h4>Annotation color convention</h4>
|
|
<table>
|
|
<thead><tr><th>Type</th><th>Color</th><th>Hex</th><th>Behavior on click</th></tr></thead>
|
|
<tbody>
|
|
<tr><td>Comment</td><td>Yellow</td><td><code>#FFFF00</code></td><td>Opens AnnotationSidePanel (existing)</td></tr>
|
|
<tr><td>Transcription</td><td>Turquoise</td><td><code>#00C7B1</code></td><td>Highlights matching block in transcript editor</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h4>Component reuse map</h4>
|
|
<table>
|
|
<thead><tr><th>Existing component</th><th>Change needed</th></tr></thead>
|
|
<tbody>
|
|
<tr><td><code>AnnotationLayer.svelte</code></td><td>Pass <code>type</code> to <code>onDraw</code> callback. Render turquoise vs yellow based on annotation type. Add number badges for transcription annotations.</td></tr>
|
|
<tr><td><code>PdfViewer.svelte</code></td><td>Split <code>handleAnnotationDraw</code> into two paths based on current mode (annotate vs transcribe). Route <code>handleAnnotationClick</code> to either side panel or transcript editor.</td></tr>
|
|
<tr><td><code>AnnotationSidePanel.svelte</code></td><td>No change — still handles comment-type annotations.</td></tr>
|
|
<tr><td><code>CommentThread.svelte</code></td><td>Reused in V1 (inline threads), V2 (margin note popovers), V3 (bottom panel). No changes needed to the component itself.</td></tr>
|
|
<tr><td><code>AnnotateHintStrip.svelte</code></td><td>New variant or prop for transcribe mode copy: “Markiere eine Textpassage im Scan.”</td></tr>
|
|
<tr><td><code>DocumentBottomPanel.svelte</code></td><td>V3: add block reference tags to discussion tab. V1/V2: remove Transcription tab (now inline).</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3>3. Recommended Approach</h3>
|
|
<p><strong>Start with V3</strong> (unified comment timeline) — it requires the least new UI and reuses the most existing components. The transcript editor is clean and focused. Comments flow naturally into the existing bottom panel Discussion tab. The only new UI element is the reference tag on comments.</p>
|
|
<p>Then <strong>layer V2 margin notes as an enhancement</strong>: users who prefer seeing comments next to the text can toggle a “Randnotizen” mode that pulls relevant comments from the timeline into margin cards. This is purely a view layer change — the data model stays the same.</p>
|
|
<p>V1 (inline threads) is the most feature-rich but also the most visually heavy. Consider it for a future iteration if users report that switching between transcript and discussion tab is too much friction.</p>
|
|
|
|
<h3>4. Workflow: Draw-to-Transcribe</h3>
|
|
<ol>
|
|
<li>User enters <strong>Transcribe mode</strong> (topbar button, turquoise). Hint strip appears.</li>
|
|
<li>Crosshair cursor on PDF (same as annotate mode). User draws a rectangle around a handwriting passage.</li>
|
|
<li><code>AnnotationLayer.onDraw(rect)</code> fires. <code>PdfViewer</code> calls <code>POST /api/documents/{id}/annotations</code> with <code>type: "transcription"</code>.</li>
|
|
<li>Backend creates <code>DocumentAnnotation</code> + <code>TranscriptionBlock</code> (empty text, next sort_order).</li>
|
|
<li>Frontend receives the created annotation + block. The transcript editor scrolls to the new empty block and focuses it.</li>
|
|
<li>User types the transcription. Auto-save debounces to <code>PATCH /api/transcription-blocks/{blockId}</code>.</li>
|
|
<li>Repeat: draw next rectangle, type next block.</li>
|
|
</ol>
|
|
|
|
<h3>5. Accessibility</h3>
|
|
<ul>
|
|
<li>Transcription blocks: <code>role="region"</code> with <code>aria-label="Transkriptions-Block N: [label]"</code></li>
|
|
<li>Block body: <code>contenteditable</code> with <code>aria-multiline="true"</code></li>
|
|
<li>Number badges on PDF: <code>aria-label="Transkriptions-Bereich N"</code></li>
|
|
<li>Comment reference tags: <code>role="link"</code> with descriptive <code>aria-label</code></li>
|
|
<li>Focus order: hint strip → PDF (for drawing) → transcript blocks (in sort order) → bottom panel</li>
|
|
<li>Keyboard: Tab between blocks, Enter to edit, Escape to deselect. Ctrl+Shift+N to create new block (prompts draw on PDF).</li>
|
|
</ul>
|
|
</div>
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|