Skip to content

Commit f0fdb9f

Browse files
committed
fix: stabilize streaming text merges
1 parent 65f1115 commit f0fdb9f

3 files changed

Lines changed: 43 additions & 10 deletions

File tree

src/components/Messages.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ export function Messages({
308308
!normalizedSummaryText ||
309309
normalizedSummaryText === summaryTitle ||
310310
summaryLines.length <= 1;
311+
const showReasoningBody = !shouldHideReasoningBody && summaryText;
311312
return (
312313
<div key={item.id} className="tool-inline reasoning-inline">
313314
<button
@@ -330,7 +331,7 @@ export function Messages({
330331
/>
331332
<span className="tool-inline-value">{summaryTitle}</span>
332333
</button>
333-
{!shouldHideReasoningBody && summaryText && (
334+
{showReasoningBody && (
334335
<Markdown
335336
value={summaryText}
336337
className={`reasoning-inline-detail markdown ${
@@ -403,6 +404,7 @@ export function Messages({
403404
: summary.value;
404405
const shouldFadeCommand =
405406
isCommand && !isExpanded && (summaryValue?.length ?? 0) > 80;
407+
const showToolOutput = isExpanded && (!isFileChange || !hasChanges);
406408
return (
407409
<div
408410
key={item.id}
@@ -490,7 +492,7 @@ export function Messages({
490492
{isExpanded && isFileChange && !hasChanges && item.detail && (
491493
<Markdown value={item.detail} className="item-text markdown" />
492494
)}
493-
{isExpanded && summary.output && (!isFileChange || !hasChanges) && (
495+
{showToolOutput && summary.output && (
494496
<Markdown
495497
value={summary.output}
496498
className="tool-inline-output markdown"

src/hooks/useThreadsReducer.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,31 @@ export const initialState: ThreadState = {
103103
lastAgentMessageByThread: {},
104104
};
105105

106+
function mergeStreamingText(existing: string, delta: string) {
107+
if (!delta) {
108+
return existing;
109+
}
110+
if (!existing) {
111+
return delta;
112+
}
113+
if (delta === existing) {
114+
return existing;
115+
}
116+
if (delta.startsWith(existing)) {
117+
return delta;
118+
}
119+
if (existing.startsWith(delta)) {
120+
return existing;
121+
}
122+
const maxOverlap = Math.min(existing.length, delta.length);
123+
for (let length = maxOverlap; length > 0; length -= 1) {
124+
if (existing.endsWith(delta.slice(0, length))) {
125+
return `${existing}${delta.slice(length)}`;
126+
}
127+
}
128+
return `${existing}${delta}`;
129+
}
130+
106131
export function threadReducer(state: ThreadState, action: ThreadAction): ThreadState {
107132
switch (action.type) {
108133
case "setActiveThreadId":
@@ -343,7 +368,7 @@ export function threadReducer(state: ThreadState, action: ThreadAction): ThreadS
343368
const existing = list[index];
344369
list[index] = {
345370
...existing,
346-
text: `${existing.text}${action.delta}`,
371+
text: mergeStreamingText(existing.text, action.delta),
347372
};
348373
} else {
349374
list.push({
@@ -432,7 +457,10 @@ export function threadReducer(state: ThreadState, action: ThreadAction): ThreadS
432457
};
433458
const updated: ConversationItem = {
434459
...base,
435-
summary: `${"summary" in base ? base.summary : ""}${action.delta}`,
460+
summary: mergeStreamingText(
461+
"summary" in base ? base.summary : "",
462+
action.delta,
463+
),
436464
} as ConversationItem;
437465
const next = index >= 0 ? [...list] : [...list, updated];
438466
if (index >= 0) {
@@ -460,7 +488,10 @@ export function threadReducer(state: ThreadState, action: ThreadAction): ThreadS
460488
};
461489
const updated: ConversationItem = {
462490
...base,
463-
content: `${"content" in base ? base.content : ""}${action.delta}`,
491+
content: mergeStreamingText(
492+
"content" in base ? base.content : "",
493+
action.delta,
494+
),
464495
} as ConversationItem;
465496
const next = index >= 0 ? [...list] : [...list, updated];
466497
if (index >= 0) {
@@ -483,7 +514,7 @@ export function threadReducer(state: ThreadState, action: ThreadAction): ThreadS
483514
const existing = list[index];
484515
const updated: ConversationItem = {
485516
...existing,
486-
output: `${existing.output ?? ""}${action.delta}`,
517+
output: mergeStreamingText(existing.output ?? "", action.delta),
487518
} as ConversationItem;
488519
const next = [...list];
489520
next[index] = updated;

src/styles/base.css

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
--surface-context-core: rgba(16, 20, 30, 0.96);
8787
}
8888

89-
@media (prefers-color-scheme: light) {
89+
@media (prefers-color-scheme: light) {
9090
:root {
9191
--text-primary: #1a1d24;
9292
--text-strong: #0e1118;
@@ -135,9 +135,9 @@
135135
--text-accent: rgba(45, 93, 170, 0.7);
136136
--shadow-accent: rgba(90, 140, 210, 0.18);
137137
--status-success: rgba(30, 155, 110, 0.9);
138-
--status-warning: rgba(215, 120, 20, 0.9);
139-
--status-error: rgba(200, 45, 45, 0.9);
140-
--status-unknown: rgba(17, 20, 28, 0.25);
138+
--status-warning: rgba(215, 120, 20, 0.9);
139+
--status-error: rgba(200, 45, 45, 0.9);
140+
--status-unknown: rgba(17, 20, 28, 0.25);
141141
--select-caret: rgba(15, 23, 36, 0.45);
142142
}
143143

0 commit comments

Comments
 (0)