// AssistantMarkdown: renders assistant blocks in order — markdown text body, // reasoning as the figma Think summary row (expand = indented gray text), // other-block JSON fallback. Tool-call heads are NOT rendered here: the chat // view groups them into tool rows through its keyed toolview slot (figma // step-summary flow). Shared by finalized nodes and the streaming partial // (pulse marker). import { memo } from 'react' import type { AssistantBlock } from '@deepseek-ai/dsh-client-runtime/client' import { IconThinkOutline14, JsonBlock, MarkdownText } from '@deepseek-ai/dsh-client-ui-primitives' import { ToolRow } from './ToolRow.tsx' import css from './AssistantMarkdown.module.css' export interface AssistantMarkdownProps { blocks: readonly AssistantBlock[] streaming: boolean /** Frozen partial of an aborted turn: rendered with a 已停止 marker, no pulse. */ interrupted?: boolean | undefined } function firstLine(text: string): string { const nl = text.indexOf('\n') return nl === -1 ? text : text.slice(0, nl) } /** Reasoning block as the Think variant summary row (figma 39:28304). */ function ThinkRow({ text, running }: { text: string; running: boolean }) { return ( } title="Think" summary={firstLine(text)} body={text} state={running ? 'running' : 'ok'} expandOnRowClick /> ) } export const AssistantMarkdown = memo(function AssistantMarkdown({ blocks, streaming, interrupted }: AssistantMarkdownProps) { const last = blocks.length - 1 // Tool-call heads render as tool rows in the chat view's grouping pass, so // a node that is only those heads (or empty) would paint an empty root // between tool groups — skip the shell unless something visible remains. const hasVisible = streaming || interrupted === true || blocks.some(block => block.kind !== 'tool-call') if (!hasVisible) return null return (
{blocks.map((block, i) => { switch (block.kind) { case 'text': return case 'reasoning': return // Grouped into tool rows by ChatView; hasVisible above skips an empty shell. case 'tool-call': return null default: return } })} {streaming && } {interrupted && 已停止}
) })