fix(rag): stop the per-run blocks from folding — the container is the one disclosure
The owner refinement (2026-10-06): the per-run thinking and tool-call blocks inside the Reasoning bubble were individually collapsible details elements — a fold inside the fold. A tool frame closed the previous thinking block, so the visible thinking vanished mid-turn; and the pinned-tail follow died on the append of every run after the first (the pin was captured AFTER the new block's height growth contaminated the 32px reading). - app.js: the in-container runs are now plain divs — createThinkingRunEl (div.thinking + static .thinking-summary label + .thinking-text) and createToolRunEl (div.tool-calls-disclosure + static .tool-calls-summary label + the .tool-calls list); they never open or fold (the container is the ONE open/fold control, open through the turn, folded at settle — unchanged); the pin capture moves BEFORE ensureThinkingRun/ensureToolRun append (plus the first-frame edge: no container yet -> always pinned); closeCurrentRun just ends the run; closeAllRuns stays the defensive legacy guard; restoreTimeline/addSegments (shared.js) use the div builders; the legacy no-segments restore path keeps the old details-based blocks byte-identical - styles.css: the in-container runs are distinct by label + rail (brand-ink Thinking label on a brand-soft rail; accent-ink Tool call(s) (N) label on a neutral rail); the interactive treatment (cursor + chevron) is scoped to the legacy <summary>; the inner windows stay disabled (the container's .reasoning-body is the ONE live scroll surface) - unit + E2E: the D5-refinement contract is pinned — one details.reasoning disclosure, per-run blocks as plain divs, zero nested <details> inside the container, the container as the only open/fold control, the .reasoning-body window auto-follows the tail while streaming unit+integration 3738 passed (coverage 99%), ruff + pyright clean, all five affected E2E suites pass in isolation (reasoning_bubble 6, thinking_timeline 6, thinking_display 5, thinking_scroll 8, tool_call_compact 4), verified live: the follow holds through a long thinking stream, the tool-frame transition, and the second thinking run; restore renders the div structure without client-side errors LLM-Generated: true
This commit is contained in:
+21
-17
@@ -277,18 +277,20 @@ function createReasoningWrap() {
|
||||
/* Phase 158 (task 03, D4/D5): the SHARED page's per-run timeline —
|
||||
* the parity copy of the chat page's restoreTimeline: the record's
|
||||
* `segments` array (the per-run order, task 01's ChatMessage field)
|
||||
* renders each CONTIGUOUS thinking run as its own details.thinking
|
||||
* block and each CONTIGUOUS run of tool entries as ONE
|
||||
* details.tool-calls-disclosure ("Tool call(s) (N)" per run), in
|
||||
* arrival order, ABOVE the answer — a pure render, so everything is
|
||||
* created CLOSED (D5; the same collapsed convention as
|
||||
* addThinkingBlock / addToolLines). Phase 161 (task 02, D5/D6): the
|
||||
* renders each CONTIGUOUS thinking run as its own .thinking block and
|
||||
* each CONTIGUOUS run of tool entries as ONE .tool-calls-disclosure
|
||||
* ("Tool call(s) (N)" per run), in arrival order, ABOVE the answer —
|
||||
* a pure render. Phase 161 (task 02, D5/D6): the
|
||||
* per-run timeline renders inside ONE details.reasoning container —
|
||||
* the local parity builder above, created CLOSED (the pure-render
|
||||
* convention) and inserted ONCE before the bubble (the shared
|
||||
* page's existing insertion convention); every per-run block
|
||||
* appends into the container's .reasoning-body (the per-run bubble
|
||||
* anchor is superseded — arrival order preserved). The D6 at-rest
|
||||
* anchor is superseded — arrival order preserved). The D5 refinement
|
||||
* (owner 2026-10-06, parity with app.js): the in-container runs are
|
||||
* plain divs (NEVER collapsible — the container is the ONE
|
||||
* disclosure; the per-run blocks are always visible while the
|
||||
* container is open). The D6 at-rest
|
||||
* step count uses the SAME two-increment semantics as app.js's
|
||||
* restoreTimeline (a parity copy, not an import — contiguous runs:
|
||||
* each thinking entry is ONE run; the first entry of a new
|
||||
@@ -327,24 +329,23 @@ function addSegments(wrap, segments) {
|
||||
if (typeof entry.text !== "string") continue; // malformed: skipped
|
||||
if (toolDisc) toolDisc = null; // the run is sealed — the count is final
|
||||
runs += 1; // D6: each thinking entry is ONE run (parity with app.js — a thinking entry after a tool group starts a run; two adjacent thinking entries are TWO runs)
|
||||
const block = document.createElement("details");
|
||||
const block = document.createElement("div"); // D5 refinement (owner 2026-10-06): the in-container run is a plain div — never collapsible (the container is the ONE disclosure)
|
||||
block.className = "thinking";
|
||||
block.open = false; // D5: collapsed (the addThinkingBlock convention)
|
||||
const summary = document.createElement("summary");
|
||||
summary.textContent = "Thinking"; // the phase-17 literal (D3 — position distinguishes the runs)
|
||||
const label = document.createElement("span");
|
||||
label.className = "thinking-summary";
|
||||
label.textContent = "Thinking"; // the phase-17 literal (D3 — position distinguishes the runs)
|
||||
const textEl = document.createElement("div");
|
||||
textEl.className = "thinking-text";
|
||||
textEl.innerHTML = renderMarkdown(entry.text); // escape-first, XSS-safe (parity with addThinkingBlock)
|
||||
block.append(summary, textEl);
|
||||
block.append(label, textEl);
|
||||
runsEl.appendChild(block); // Phase 161 D5: inside the container, in arrival order
|
||||
} else if (entry.kind === "tool") {
|
||||
if (typeof entry.name !== "string") continue; // malformed: skipped
|
||||
if (!toolDisc) {
|
||||
toolDisc = document.createElement("details");
|
||||
toolDisc = document.createElement("div"); // D5 refinement: the in-container run is a plain div (never collapsible)
|
||||
toolDisc.className = "tool-calls-disclosure";
|
||||
toolDisc.open = false; // D5: collapsed (parity with addToolLines — phase 117 D3)
|
||||
runs += 1; // D6: the first entry of a NEW contiguous group is ONE run (parity with app.js — a group of N entries is ONE run)
|
||||
const summary = document.createElement("summary");
|
||||
const summary = document.createElement("span");
|
||||
summary.className = "tool-calls-summary";
|
||||
toolDisc.appendChild(summary);
|
||||
const container = document.createElement("div");
|
||||
@@ -405,9 +406,12 @@ function addSegments(wrap, segments) {
|
||||
}
|
||||
// The per-run count (parity with addToolLines — one disclosure
|
||||
// per contiguous run, D3) rides every append; the final
|
||||
// append leaves the final count.
|
||||
// append leaves the final count. The D5 refinement (owner
|
||||
// 2026-10-06): the target is the .tool-calls-summary CLASS —
|
||||
// the legacy `<summary>` and the in-container `<span>` both
|
||||
// carry it, so ONE query serves both.
|
||||
const n = container.children.length;
|
||||
toolDisc.querySelector("summary").textContent =
|
||||
toolDisc.querySelector(".tool-calls-summary").textContent =
|
||||
`Tool call${n === 1 ? "" : "s"} (${n})`;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user