refactor: extract makeCoalescer helper for last-row update-in-place pattern
thinkingTokensRow/statusRow/pluginInstallRow were three copy-pasted module-level row/text pairs implementing the same 'collapse repeated ticks into one updating row' pattern. Extracted a small makeCoalescer(cls, icon) factory returning an update(api, text) closure; each call site is now a single line instead of the duplicated guard-and-update-or-create block against api.mutableRow.
This commit is contained in:
parent
c222418d76
commit
a99508719e
1 changed files with 39 additions and 48 deletions
|
|
@ -1893,26 +1893,36 @@ window.marked = marked;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Coalescing target for the live `thinking_tokens` counter.
|
// Coalescing helper: collapses a burst of same-kind ticks into ONE
|
||||||
// Holds the row + its text node so consecutive ticks update in place
|
// row that updates in place instead of appending a fresh note per
|
||||||
// instead of appending a note each. Reset implicitly: we only reuse
|
// tick. Holds the row + its text node in a closure; reuses them only
|
||||||
// it while it's still the last row rendered (nextElementSibling ===
|
// while the row is still the last one rendered (nextElementSibling
|
||||||
// null), so any other event starts fresh.
|
// === null) — any other event in between starts a fresh row. `cls`
|
||||||
let thinkingTokensRow = null;
|
// and `icon` are fixed per coalescer instance (mirrors `api.mutableRow`'s
|
||||||
let thinkingTokensText = null;
|
// signature); `api` is passed per-call since `renderStream` receives a
|
||||||
// Same coalescing trick for claude's generic `status` subtype (bare
|
// fresh one each invocation.
|
||||||
// "still working" ticks with no detail beyond the label itself). Without
|
function makeCoalescer(cls, icon) {
|
||||||
// this every tick got its own row, so a compaction (which emits a burst
|
let row = null;
|
||||||
// of these with no other signal) looked like a wall of identical noise
|
let text = null;
|
||||||
// rather than one "still going" line.
|
return function update(api, newText) {
|
||||||
let statusRow = null;
|
if (row && row.isConnected && row.nextElementSibling === null) {
|
||||||
let statusText = null;
|
text.nodeValue = newText;
|
||||||
// Same coalescing for `plugin_install`: claude emits a `started` tick
|
} else {
|
||||||
// then a `completed` tick per plugin — without coalescing that's two
|
[row, text] = api.mutableRow(cls, newText, icon);
|
||||||
// separate rows ("loading…" then "✓ done") for what's really one event
|
}
|
||||||
// from the operator's point of view; update in place instead.
|
};
|
||||||
let pluginInstallRow = null;
|
}
|
||||||
let pluginInstallText = null;
|
// Live `thinking_tokens` counter — claude streams many of these per turn.
|
||||||
|
const updateThinkingTokens = makeCoalescer('note', '🧠');
|
||||||
|
// Bare `status` ticks (claude's generic "still working" signal) — the
|
||||||
|
// only signal during a compaction pass (no dedicated "compacting…"
|
||||||
|
// event), so without coalescing a compaction looked like a wall of
|
||||||
|
// identical `⚙ status` rows.
|
||||||
|
const updateStatus = makeCoalescer('note');
|
||||||
|
// `plugin_install`: claude emits a `started` tick then a `completed`
|
||||||
|
// tick per plugin — without coalescing that's two rows ("loading…"
|
||||||
|
// then "✓ done") for what's really one event.
|
||||||
|
const updatePluginInstall = makeCoalescer('note');
|
||||||
function renderStream(v, api) {
|
function renderStream(v, api) {
|
||||||
// Drop claude's result line and rate-limit — noise. TurnEnd
|
// Drop claude's result line and rate-limit — noise. TurnEnd
|
||||||
// communicates pass/fail; rate-limit events are noisy status chatter.
|
// communicates pass/fail; rate-limit events are noisy status chatter.
|
||||||
|
|
@ -1943,19 +1953,12 @@ window.marked = marked;
|
||||||
// Live thinking-token counter — claude streams many of these per
|
// Live thinking-token counter — claude streams many of these per
|
||||||
// turn (a running `estimated_tokens` total while it thinks). Collapse
|
// turn (a running `estimated_tokens` total while it thinks). Collapse
|
||||||
// consecutive ticks into ONE in-place-updating row instead of a note
|
// consecutive ticks into ONE in-place-updating row instead of a note
|
||||||
// per tick. Reuse our row only while it's still the last one
|
// per tick (see `makeCoalescer` above).
|
||||||
// in the scrollback; once any other event renders after it, the next
|
|
||||||
// tick starts a fresh row.
|
|
||||||
if (v.subtype === 'thinking_tokens') {
|
if (v.subtype === 'thinking_tokens') {
|
||||||
const n = v.estimated_tokens;
|
const n = v.estimated_tokens;
|
||||||
const text = 'thinking … '
|
const text = 'thinking … '
|
||||||
+ (n != null ? '~' + Number(n).toLocaleString() + ' tokens' : '');
|
+ (n != null ? '~' + Number(n).toLocaleString() + ' tokens' : '');
|
||||||
if (thinkingTokensRow && thinkingTokensRow.isConnected
|
updateThinkingTokens(api, text);
|
||||||
&& thinkingTokensRow.nextElementSibling === null) {
|
|
||||||
thinkingTokensText.nodeValue = text;
|
|
||||||
} else {
|
|
||||||
[thinkingTokensRow, thinkingTokensText] = api.mutableRow('note', text, '🧠');
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// plugin_install: claude is loading/finishing a plugin (MCP server or
|
// plugin_install: claude is loading/finishing a plugin (MCP server or
|
||||||
|
|
@ -1965,13 +1968,7 @@ window.marked = marked;
|
||||||
const status = v.status === 'completed' ? '✓ done'
|
const status = v.status === 'completed' ? '✓ done'
|
||||||
: v.status === 'started' ? 'loading…'
|
: v.status === 'started' ? 'loading…'
|
||||||
: (v.status || '?');
|
: (v.status || '?');
|
||||||
const text = '⚙ plugin install · ' + status;
|
updatePluginInstall(api, '⚙ plugin install · ' + status);
|
||||||
if (pluginInstallRow && pluginInstallRow.isConnected
|
|
||||||
&& pluginInstallRow.nextElementSibling === null) {
|
|
||||||
pluginInstallText.nodeValue = text;
|
|
||||||
} else {
|
|
||||||
[pluginInstallRow, pluginInstallText] = api.mutableRow('note', text);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// commands_changed: the set of available slash commands changed (usually
|
// commands_changed: the set of available slash commands changed (usually
|
||||||
|
|
@ -2015,19 +2012,13 @@ window.marked = marked;
|
||||||
}
|
}
|
||||||
// Bare `status` ticks (claude's own generic "still working" signal,
|
// Bare `status` ticks (claude's own generic "still working" signal,
|
||||||
// no detail beyond the label) — collapse consecutive ticks into one
|
// no detail beyond the label) — collapse consecutive ticks into one
|
||||||
// updating row instead of a fresh note each, same pattern as
|
// updating row instead of a fresh note each (see `makeCoalescer`
|
||||||
// `thinking_tokens` above. These are the only signal we get during a
|
// above). These are the only signal we get during a compaction pass
|
||||||
// compaction pass (no dedicated "compacting…" event from claude), so
|
// (no dedicated "compacting…" event from claude), so without
|
||||||
// without collapsing a compaction looked like a wall of identical
|
// collapsing a compaction looked like a wall of identical `⚙ status`
|
||||||
// `⚙ status` rows followed by silence.
|
// rows followed by silence.
|
||||||
if (v.subtype === 'status') {
|
if (v.subtype === 'status') {
|
||||||
const text = '⚙ status';
|
updateStatus(api, '⚙ status');
|
||||||
if (statusRow && statusRow.isConnected
|
|
||||||
&& statusRow.nextElementSibling === null) {
|
|
||||||
statusText.nodeValue = text;
|
|
||||||
} else {
|
|
||||||
[statusRow, statusText] = api.mutableRow('note', text);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Other system subtypes (context_window_exceeded, etc.) — render a
|
// Other system subtypes (context_window_exceeded, etc.) — render a
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue