From b08310ac1d75227ead893e50c6d53d5deba575c8 Mon Sep 17 00:00:00 2001 From: Turtle Date: Mon, 7 Sep 2026 17:36:21 +0800 Subject: [PATCH] fix(web): strip bold markers from think summaries --- .../ui-chat/src/client/chat/ReasoningRow.tsx | 6 ++-- .../tests/reasoning-row.client.spec.tsx | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/client/ui-chat/src/client/chat/ReasoningRow.tsx b/packages/client/ui-chat/src/client/chat/ReasoningRow.tsx index 81b10a7c49..bc7859b364 100644 --- a/packages/client/ui-chat/src/client/chat/ReasoningRow.tsx +++ b/packages/client/ui-chat/src/client/chat/ReasoningRow.tsx @@ -17,7 +17,9 @@ function latestLine(text: string): string { } /** - * Render one assistant reasoning block as the Think disclosure row. + * Render one assistant reasoning block as the Think disclosure row. The + * collapsed summary omits double-asterisk markers; expanded content preserves + * the complete text. * @param props.text - complete or streaming reasoning text. * @param props.running - whether this block is the streaming tail. * @param props.t - conversation locale seat for the running status. @@ -25,7 +27,7 @@ function latestLine(text: string): string { */ export function ReasoningRow({ text, running, t }: { text: string; running: boolean; t: ChatViewSlotProps['t'] }) { const [expanded, setExpanded] = useState(false) - const summary = running ? latestLine(text) : firstLine(text) + const summary = (running ? latestLine(text) : firstLine(text)).replaceAll('**', '') return (
{ expect(row.getAttribute('aria-expanded')).toBe('false') }) + it.each([ + { + label: 'settled', + text: '**Comparing checkout and merge bases**\nKeep **reviewing**', + streaming: false, + }, + { + label: 'streaming', + text: 'Inspect the session\n**Comparing checkout and merge bases**', + streaming: true, + }, + ])('strips double-asterisk markers from the $label summary without changing the reasoning body', ({ text, streaming }) => { + const view = render( + , + ) + + expect(view.getByText('Comparing checkout and merge bases')).toBeTruthy() + expect(view.queryByText('**Comparing checkout and merge bases**')).toBeNull() + + fireEvent.click(view.getByText('思考')) + expect(view.container.querySelector('[class*="thinkBody"]')?.textContent).toBe(text) + }) + it('expanded Think drops the inline summary and renders plain prose, no IN card', () => { const view = render(