feat: expandable ⋯ dividers in Recast Unknown (v1.12.4)
Click any gap divider to reveal the hidden segments between two Unknown
passages inline — shows speaker + text so context is clear before
making an assignment. Displays line count ("42 lines hidden — click
to expand") so users know what they are opening.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7a75d1ecf4
commit
87cac1f5f5
@ -9,6 +9,13 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
||||
|
||||
---
|
||||
|
||||
## [1.12.4] — 2026-06-27
|
||||
|
||||
### Added
|
||||
- **Expandable context dividers in Recast Unknown**: the `⋯` gaps between scattered Unknown segments now show a count ("42 lines hidden — click to expand") and expand inline on click, revealing all segments between two Unknown passages so the user can see who is speaking before and after to make a better assignment.
|
||||
|
||||
---
|
||||
|
||||
## [1.12.3] — 2026-06-27
|
||||
|
||||
### Added
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
|
||||
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" href="/static/style.css?v=1.12.3">
|
||||
<link rel="stylesheet" href="/static/style.css?v=1.12.4">
|
||||
|
||||
|
||||
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
|
||||
@ -336,7 +336,7 @@
|
||||
<script src="/static/vendor/wavesurfer-regions.min.js"></script>
|
||||
|
||||
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
|
||||
<script src="/static/loader.js?v=1.12.3"></script>
|
||||
<script src="/static/loader.js?v=1.12.4"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -881,14 +881,35 @@ STRIKTE FORMAT- UND TEXTREGELN:
|
||||
feed.appendChild(frag); trim(); renderRoster();
|
||||
},
|
||||
note(text) { const r = document.createElement('div'); r.className = 'ab-cv-note'; r.textContent = text; feed.appendChild(r); trim(); },
|
||||
// Visual gap (three dots) marking that the passages before/after are not
|
||||
// contiguous — used by Recast unknown, which only shows scattered segments.
|
||||
divider() {
|
||||
// Visual gap marking that the passages before/after are not contiguous.
|
||||
// prevIdx / nextIdx are indices into _audiobook.segments for the gap boundaries.
|
||||
// Click expands the divider to show all segments in the gap inline.
|
||||
divider(prevIdx, nextIdx) {
|
||||
this.clearProcessing();
|
||||
const r = document.createElement('div');
|
||||
r.className = 'ab-cv-divider';
|
||||
r.textContent = '⋯';
|
||||
r.title = 'There is more text before and after this passage';
|
||||
r.className = 'ab-cv-divider ab-cv-divider-expand';
|
||||
const gapFrom = Math.max(0, (prevIdx >= 0 ? prevIdx + 1 : 0));
|
||||
const segsAll = _audiobook.segments || [];
|
||||
const gapTo = Math.min(segsAll.length - 1, (nextIdx >= 0 ? nextIdx - 1 : segsAll.length - 1));
|
||||
const gapCount = Math.max(0, gapTo - gapFrom + 1);
|
||||
r.innerHTML = `⋯ <span class="ab-cv-divider-hint">${gapCount > 0 ? gapCount + ' line' + (gapCount !== 1 ? 's' : '') + ' hidden — ' : ''}click to expand</span>`;
|
||||
r.title = 'Click to reveal the text between these passages';
|
||||
r.addEventListener('click', () => {
|
||||
const gap = segsAll.slice(gapFrom, gapTo + 1);
|
||||
if (!gap.length) { r.remove(); return; }
|
||||
const frag = document.createDocumentFragment();
|
||||
for (const s of gap) {
|
||||
const isNarr = s.type !== 'dialogue' || !s.speaker || s.speaker.toLowerCase() === 'narrator';
|
||||
const spk = isNarr ? 'Narrator' : s.speaker;
|
||||
const c = colorFor(spk);
|
||||
const row = document.createElement('div');
|
||||
row.className = 'ab-cv-row ab-cv-ctx-row' + (isNarr ? ' is-narr' : '');
|
||||
row.__seg = s;
|
||||
row.innerHTML = `<span class="ab-cv-spk" style="color:${c}" title="Click to assign character">${escHtml(spk)}${s.emotion ? ' <span style="text-transform:lowercase;font-weight:normal;opacity:.8">(${escHtml(s.emotion)})</span>' : ''}</span><span class="ab-cv-txt">${highlightText(s.text || '')}</span>`;
|
||||
frag.appendChild(row);
|
||||
}
|
||||
r.replaceWith(frag);
|
||||
});
|
||||
feed.appendChild(r); trim();
|
||||
},
|
||||
// Page-break marker in the casting feed (from source PDF page boundaries).
|
||||
@ -1028,7 +1049,7 @@ async function audiobookRecastUnknown(overrideUrl, overrideModel) {
|
||||
|
||||
// These Unknown lines come from scattered places in the document. Mark a
|
||||
// gap with "⋯" whenever this passage isn't directly after the previous one.
|
||||
if (i !== prevIdx + 1) view.divider();
|
||||
if (i !== prevIdx + 1) view.divider(prevIdx, i);
|
||||
prevIdx = i;
|
||||
|
||||
const start = Math.max(0, i - 12);
|
||||
|
||||
@ -4513,6 +4513,10 @@ code { background: var(--panel); border-radius: 4px; padding: 1px 5px; font-fami
|
||||
|
||||
/* ── Recast unknown: gap between non-contiguous passages ───────────────── */
|
||||
.ab-cv-divider { text-align: center; color: var(--subtext); font-size: 20px; letter-spacing: .35em; line-height: 1; padding: 10px 0 6px; opacity: .55; user-select: none; }
|
||||
.ab-cv-divider-expand { cursor: pointer; font-size: 14px; letter-spacing: normal; transition: opacity .15s; }
|
||||
.ab-cv-divider-expand:hover { opacity: .9; color: var(--accent); }
|
||||
.ab-cv-divider-hint { font-size: 11px; font-weight: 500; text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 2px; vertical-align: middle; }
|
||||
.ab-cv-ctx-row { opacity: .7; background: color-mix(in srgb, var(--panel) 60%, transparent); border-left: 2px solid var(--border); }
|
||||
.reader-synth-prog { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; }
|
||||
.reader-synth-track { width: 130px; height: 6px; border-radius: 4px; background: var(--panel); overflow: hidden; }
|
||||
.reader-synth-fill { height: 100%; width: 0; background: rgba(234,179,8,.9); transition: width .15s; }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user