diag(book): surface chain + line distribution in the text-frame inspector

When a text-frame is selected in a thread, the status line now shows
the actual paginator output:

  Frame 2 of 3 in this thread · 12 lines here; chain: [18 · 12 · 5]

That makes it obvious whether the chain walk found every frame and
how many lines landed in each. If the chain shows length 2 when the
user expects 3, the chain walk dropped a frame (probably a stale
nextFrame ref). If the chain looks right but line counts are wrong
in the 3rd frame, the bug is in the paginator.

Also logs a console.warn + appends '⚠ outgoing link is broken' to
the status when this frame's outgoing nextFrame ref points at a
frameId that doesn't exist in state.layers (deleted frame /
deleted page).

Diagnostic-only — no behavior change. Helps pin down the
'cross-page flow stops after 2 frames' bug.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.7 committed 2026-06-04 22:19:19 -04:00
1 parent 3d41eaaf7f
commit cbfed8702b
1 file changed
+32 -2
+32 -2
View File
@@ -23675,10 +23675,40 @@
const isHead = layer === head;
const status = document.getElementById('bookTextFrameStatus');
if (status) {
// Diagnostic detail: chain summary so we can spot when the
// walk doesn't find a downstream frame (e.g., frame across
// pages that wasn't resolved or has a stale ref).
let detail = '';
if (head?.sourceChapter) {
try {
const entry = (book.pages || []).find(p => p.id === head.sourceChapter);
if (entry) {
const paragraphs = _flattenChapterToParagraphs(entry.chapterMarkdown || '');
const typo = _getFrameTypography(head);
const pag = _paginateParagraphsIntoFrames(paragraphs, chain, typo);
const lineCounts = pag.perFrame.map(pf => pf.lines.length).join(' · ');
const myCount = pag.perFrame[myIndex]?.lines.length || 0;
detail = ` · ${myCount} lines here; chain: [${lineCounts}]`;
if (pag.hasMoreText) detail += ` · overset (${pag.totalLinesPlaced} of ${pag.totalLinesNeeded} fit)`;
}
} catch {}
}
if (chain.length > 1) {
status.textContent = `Frame ${myIndex + 1} of ${chain.length} in this thread.${isHead ? ' (Head — chapter source goes here.)' : ''}`;
status.textContent = `Frame ${myIndex + 1} of ${chain.length} in this thread.${isHead ? ' (Head — chapter source goes here.)' : ''}${detail}`;
} else {
status.textContent = 'Solo frame — link in or out to extend into a thread.';
status.textContent = `Solo frame — link in or out to extend into a thread.${detail}`;
}
// Warn the user (and the console) when the chain walk drops
// a downstream frame. Most common cause: a stale nextFrame
// ref pointing at a frameId that no longer exists (page or
// frame was deleted).
const expectedNext = layer.nextFrame?.frameId;
if (expectedNext && !chain.includes(layer.nextFrame ? _findBookFrameLayer(o => o.frameId === expectedNext) : null)) {
const found = _findBookFrameLayer(o => o.frameId === expectedNext);
if (!found) {
console.warn('[book threading] nextFrame ref points at a missing frameId:', expectedNext, '— clearing the broken link from layer', layer.id);
status.textContent += ` ⚠ outgoing link is broken (target frame not found).`;
}
}
}