Whole-Turn folding chose the final answer by position, so a Turn that continued after answering (a notice reply, record keeping, or a tool call) hid the real answer with the process. A folded Turn now hides only its process; every assistant-step reply row stays visible. Settings -> General -> Replies After Collapse (ui-chat.foldedReplies, default "all") switches back to the previous final-answer-only rule; the choice applies immediately and is hidden in Verbose.
55 lines
2.4 KiB
TypeScript
55 lines
2.4 KiB
TypeScript
import { memo, useCallback, useMemo } from 'react'
|
|
import type { InjectFace, PropsRenderSlots } from '@deepseek-ai/dsh-client-ui-slots'
|
|
import type { ChatNodeViewProps, PresentationInjected, TurnTailOwnerProps } from '../contract/slots.ts'
|
|
import { AssistantMarkdown } from './AssistantMarkdown.tsx'
|
|
|
|
type AssistantNodeViewProps = ChatNodeViewProps<'assistant-step'> & InjectFace<PresentationInjected>
|
|
& PropsRenderSlots<'conversation.chat.reasoning.body' | 'conversation.chat.message.images'>
|
|
|
|
/** Streaming, settled, and interrupted Assistant states share one keyed renderer instance. */
|
|
export const AssistantNodeView = memo(function AssistantNodeView({
|
|
node, groupPart, useDisclosure, useGroupAction, useTurnData, turnProcess, openFile, loadImage, fileMentions,
|
|
usePresentation, renderSlot, t,
|
|
}: AssistantNodeViewProps) {
|
|
const data = node.data
|
|
const turn = node.location.kind === 'turn' || node.location.kind === 'step'
|
|
? node.location.turn
|
|
: undefined
|
|
const tail = useTurnData('turn-tail')
|
|
const owner = useMemo<TurnTailOwnerProps | undefined>(() => {
|
|
if (turn?.status !== 'closed' || data.finalNode === undefined) return undefined
|
|
if (tail?.closing?.finalNode.seq !== data.finalNode.seq) return undefined
|
|
return { turn, seq: data.finalNode.seq, openFile }
|
|
}, [data.finalNode, openFile, tail, turn])
|
|
const mentions = useMemo(
|
|
() => owner === undefined ? undefined : fileMentions(owner),
|
|
[fileMentions, owner],
|
|
)
|
|
const keepReplies = usePresentation(policy => policy.foldedReplies === 'all')
|
|
// A kept reply row stays visible while the folded Turn hides its inline reasoning.
|
|
const reasoningHidden = turnProcess !== undefined
|
|
&& turnProcess.foldable
|
|
&& (keepReplies
|
|
? groupPart !== 'reasoning'
|
|
: turnProcess.spec.answerStep === data.step && turnProcess.spec.inlineReasoning)
|
|
&& !turnProcess.open
|
|
const revealProcess = useCallback(() => { turnProcess?.setOpen(true) }, [turnProcess])
|
|
return (
|
|
<AssistantMarkdown
|
|
blocks={data.blocks}
|
|
groupPart={groupPart}
|
|
useDisclosure={useDisclosure}
|
|
streaming={data.status === 'running'}
|
|
interrupted={data.status === 'interrupted'}
|
|
loadImage={loadImage}
|
|
reasoningHidden={reasoningHidden}
|
|
useGroupAction={useGroupAction}
|
|
usePresentation={usePresentation}
|
|
renderSlot={renderSlot}
|
|
revealProcess={revealProcess}
|
|
mentions={mentions}
|
|
t={t}
|
|
/>
|
|
)
|
|
})
|