From cbfed8702bd8bcc022766c593da2721ec97f5dc0 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:19:19 -0400 Subject: [PATCH] diag(book): surface chain + line distribution in the text-frame inspector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/index.html | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/index.html b/src/index.html index 55b7116..0631323 100644 --- a/src/index.html +++ b/src/index.html @@ -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).`; + } } }