c06f9041fc
# Conflicts: # .agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.i18n.yaml # docs/config-catalog.md # docs/cordis-catalog/events.md # docs/cordis-catalog/services.md # docs/core-data-structures/core.md # docs/core-data-structures/core.zh.md # docs/core-data-structures/llm-streaming.i18n.yaml # docs/core-data-structures/llm-streaming.md # docs/core-data-structures/llm-streaming.zh.md # docs/event-producer-consumer.md # docs/module-graph.md # docs/subsystems/attachment.i18n.yaml # docs/subsystems/attachment.md # docs/subsystems/attachment.zh.md # docs/subsystems/core.i18n.yaml # examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl # packages/README.i18n.yaml # packages/README.md # packages/README.zh.md # packages/client/runtime/package.json # packages/client/ui-conversation/package.json # packages/client/ui-conversation/src/client/chat/AssistantMarkdown.tsx # packages/client/ui-conversation/src/client/chat/ChatView.tsx # packages/client/ui-conversation/src/client/chat/MessageItem.tsx # packages/client/ui-conversation/src/client/index.ts # packages/client/ui-conversation/tests/input-bar.spec.tsx # packages/compact/compact-basic/README.i18n.yaml # packages/compact/compact-basic/README.md # packages/compact/compact-basic/README.zh.md # packages/host/apiproxy/src/api-proxy.ts # packages/host/apiproxy/src/api/index.ts # packages/host/apiproxy/src/api/sessions.ts # packages/host/apiproxy/src/index.ts # packages/host/apiproxy/tests/api-proxy-models.spec.ts # packages/self-modification/tool-cordis/src/api-catalog.ts # pnpm-lock.yaml # scripts/type-equiv.manifest.json
62 lines
2.3 KiB
TypeScript
62 lines
2.3 KiB
TypeScript
import { memo, useMemo } from 'react'
|
|
import { JsonBlock } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
import type { ChatNodeOwnerProps, ChatViewSlotProps } from '../contract/slots.ts'
|
|
import type { ChatNode } from '../contract/chat-nodes.ts'
|
|
import css from './ChatView.module.css'
|
|
|
|
interface ChatNodeSeatProps extends ChatNodeOwnerProps {
|
|
readonly nodeKey: string
|
|
readonly useSession: ChatViewSlotProps['useSession']
|
|
readonly renderSlot: ChatViewSlotProps['renderSlot']
|
|
readonly t: ChatViewSlotProps['t']
|
|
}
|
|
|
|
type RoutedChatNodeOwner = {
|
|
[Kind in ChatNode['kind']]: ChatNodeOwnerProps & { readonly node: ChatNode<Kind> }
|
|
}[ChatNode['kind']]
|
|
|
|
/** Subscribe and dispatch one stable Context key without observing sibling Nodes. */
|
|
export const ChatNodeSeat = memo(function ChatNodeSeat({
|
|
nodeKey, selectedCallId, cwd, openFile, inspectCall, forkAt,
|
|
loadImage, fileMentions, useSession, renderSlot, t,
|
|
}: ChatNodeSeatProps) {
|
|
const node = useSession(snapshot => snapshot.chat.nodes.get(nodeKey))
|
|
const routedNode = node as ChatNode | undefined
|
|
const owner = useMemo<ChatNodeOwnerProps | null>(() => node === undefined
|
|
? null
|
|
: {
|
|
selectedCallId,
|
|
cwd,
|
|
openFile,
|
|
inspectCall,
|
|
forkAt,
|
|
loadImage,
|
|
fileMentions,
|
|
}, [node, selectedCallId, cwd, openFile, inspectCall, forkAt, loadImage, fileMentions])
|
|
if (routedNode === undefined || owner === null) return null
|
|
// Runtime dispatch owns the correlation: every Node's discriminant is the
|
|
// keyed-slot entry passed alongside that same Node. TypeScript does not
|
|
// distribute an object containing a union into a union of objects itself.
|
|
const routedOwner = { ...owner, node: routedNode } as RoutedChatNodeOwner
|
|
return (
|
|
<div
|
|
className={css.flowItem}
|
|
data-chat-anchor-key={routedNode.key}
|
|
data-chat-flow-key={routedNode.key}
|
|
data-chat-flow-kind={routedNode.kind}
|
|
>
|
|
{renderSlot('conversation.chat.node', routedOwner, {
|
|
entryKey: routedNode.kind,
|
|
hookContext: nodeKey,
|
|
fallback: (
|
|
<JsonBlock
|
|
label={t('message.unknownSurface', { type: routedNode.kind })}
|
|
payload={routedNode.data}
|
|
truncatedLabel={total => t('json.truncated', { total })}
|
|
/>
|
|
),
|
|
})}
|
|
</div>
|
|
)
|
|
})
|