|
|
@@ -25,7 +25,6 @@ import type {
|
|
|
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'
|
|
|
import { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
|
import type { ChatViewSlotProps } from '../contract/slots.ts'
|
|
|
-import type { SelectionTarget } from '../contract/views.ts'
|
|
|
import { deriveChatFlow, type ChatFlowItem } from './chat-flow.ts'
|
|
|
import { AssistantMarkdown } from './AssistantMarkdown.tsx'
|
|
|
import { GenericToolCard } from './GenericToolCard.tsx'
|
|
|
@@ -36,7 +35,7 @@ import css from './ChatView.module.css'
|
|
|
|
|
|
const FOLLOW_THRESHOLD = 24
|
|
|
|
|
|
-type OpenDetails = (target: SelectionTarget) => void
|
|
|
+type OpenFile = (path: string) => void
|
|
|
|
|
|
/** The declared toolview hole's render share (stable framework binding, passed through memoized rows). */
|
|
|
type RenderToolRow = ChatViewSlotProps['renderSlot']
|
|
|
@@ -49,19 +48,17 @@ type UseConversation = SnapshotSelectorHook<ConversationSnapshot>
|
|
|
* top-level call (same registrations, same fallback), nested by the parent.
|
|
|
* A started-but-unsettled sub-call arrives as the RunningToolCall shape and
|
|
|
* renders the running state exactly as a native in-flight row. */
|
|
|
-const SubCallRow = memo(function SubCallRow({ renderSlot, node, onOpenDetails, selected }: {
|
|
|
+const SubCallRow = memo(function SubCallRow({ renderSlot, node, openFile, selected }: {
|
|
|
renderSlot: RenderToolRow
|
|
|
node: CodeSubCall
|
|
|
- onOpenDetails: OpenDetails
|
|
|
+ openFile: OpenFile
|
|
|
selected: boolean
|
|
|
}) {
|
|
|
const settled = 'kind' in node
|
|
|
const toolName = settled ? node.call?.name ?? '' : node.name
|
|
|
- const seq = settled ? node.seq : node.time
|
|
|
const owner = useMemo(() => ({
|
|
|
- callId: node.callId, toolName, block: node,
|
|
|
- openDetails: () => { onOpenDetails({ turnSeq: seq, callId: node.callId, toolName }) },
|
|
|
- }), [node, toolName, seq, onOpenDetails])
|
|
|
+ callId: node.callId, toolName, block: node, openFile,
|
|
|
+ }), [node, toolName, openFile])
|
|
|
return (
|
|
|
<div className={css.callRow} data-selected={selected || undefined}>
|
|
|
{renderSlot('conversation.chat.toolview', owner, {
|
|
|
@@ -77,14 +74,12 @@ const SubCallRow = memo(function SubCallRow({ renderSlot, node, onOpenDetails, s
|
|
|
* GenericToolCard at this render site. A `run_code` call additionally
|
|
|
* renders its logged sub-dispatches as always-visible indented rows —
|
|
|
* each one the same keyed-slot dispatch as a native top-level call. */
|
|
|
-const CallRow = memo(function CallRow({ renderSlot, callId, toolName, block, seq, onOpenDetails, selected, subCalls, selectedCallId }: {
|
|
|
+const CallRow = memo(function CallRow({ renderSlot, callId, toolName, block, openFile, selected, subCalls, selectedCallId }: {
|
|
|
renderSlot: RenderToolRow
|
|
|
callId: string
|
|
|
toolName: string
|
|
|
block: ToolResultNode | RunningToolCall
|
|
|
- /** Surface seq for finalized results; the call's turn for running calls. */
|
|
|
- seq: number
|
|
|
- onOpenDetails: OpenDetails
|
|
|
+ openFile: OpenFile
|
|
|
selected: boolean
|
|
|
/** `run_code` sub-dispatches in dispatch order (reference-stable per
|
|
|
* parent; running entries settle in place); undefined for ordinary calls. */
|
|
|
@@ -93,9 +88,8 @@ const CallRow = memo(function CallRow({ renderSlot, callId, toolName, block, seq
|
|
|
selectedCallId?: string | undefined
|
|
|
}) {
|
|
|
const owner = useMemo(() => ({
|
|
|
- callId, toolName, block,
|
|
|
- openDetails: () => { onOpenDetails({ turnSeq: seq, callId, toolName }) },
|
|
|
- }), [callId, toolName, block, seq, onOpenDetails])
|
|
|
+ callId, toolName, block, openFile,
|
|
|
+ }), [callId, toolName, block, openFile])
|
|
|
return (
|
|
|
<div className={css.callRow} data-selected={selected || undefined}>
|
|
|
{renderSlot('conversation.chat.toolview', owner, {
|
|
|
@@ -109,7 +103,7 @@ const CallRow = memo(function CallRow({ renderSlot, callId, toolName, block, seq
|
|
|
key={node.callId}
|
|
|
renderSlot={renderSlot}
|
|
|
node={node}
|
|
|
- onOpenDetails={onOpenDetails}
|
|
|
+ openFile={openFile}
|
|
|
selected={node.callId === selectedCallId}
|
|
|
/>
|
|
|
))}
|
|
|
@@ -120,10 +114,10 @@ const CallRow = memo(function CallRow({ renderSlot, callId, toolName, block, seq
|
|
|
})
|
|
|
|
|
|
/** Consecutive tool results as one step-run group (figma VERTICAL gap10). */
|
|
|
-const ToolGroup = memo(function ToolGroup({ renderSlot, results, onOpenDetails, selectedCallId, codeDispatches }: {
|
|
|
+const ToolGroup = memo(function ToolGroup({ renderSlot, results, openFile, selectedCallId, codeDispatches }: {
|
|
|
renderSlot: RenderToolRow
|
|
|
results: readonly ToolResultNode[]
|
|
|
- onOpenDetails: OpenDetails
|
|
|
+ openFile: OpenFile
|
|
|
/** Only set when the selected call lives in THIS group, top-level or nested (memo economy). */
|
|
|
selectedCallId: string | undefined
|
|
|
/** Sub-dispatch index off the snapshot (map reference is chunk-storm stable). */
|
|
|
@@ -138,8 +132,7 @@ const ToolGroup = memo(function ToolGroup({ renderSlot, results, onOpenDetails,
|
|
|
callId={node.callId}
|
|
|
toolName={node.call?.name ?? ''}
|
|
|
block={node}
|
|
|
- seq={node.seq}
|
|
|
- onOpenDetails={onOpenDetails}
|
|
|
+ openFile={openFile}
|
|
|
selected={node.callId === selectedCallId}
|
|
|
subCalls={codeDispatches.get(node.callId)}
|
|
|
selectedCallId={selectedCallId}
|
|
|
@@ -167,7 +160,7 @@ function StreamingTail({ useSession, onGrow }: {
|
|
|
* The chat view slot entry: pure component over the composed props (tool rows
|
|
|
* render through the declared keyed hole's renderSlot share).
|
|
|
*/
|
|
|
-export function ChatView({ useSession, useStore, renderSlot, openDetails, loadOlder }: ChatViewSlotProps) {
|
|
|
+export function ChatView({ useSession, useStore, renderSlot, openFile, loadOlder }: ChatViewSlotProps) {
|
|
|
const nodes = useSession(s => s.nodes)
|
|
|
const runningCalls = useSession(s => s.runningCalls)
|
|
|
const codeDispatches = useSession(s => s.codeDispatches)
|
|
|
@@ -265,7 +258,7 @@ export function ChatView({ useSession, useStore, renderSlot, openDetails, loadOl
|
|
|
key={item.key}
|
|
|
renderSlot={renderSlot}
|
|
|
results={item.results}
|
|
|
- onOpenDetails={openDetails}
|
|
|
+ openFile={openFile}
|
|
|
selectedCallId={inGroup ? selectedCallId : undefined}
|
|
|
codeDispatches={codeDispatches}
|
|
|
/>
|
|
|
@@ -304,8 +297,7 @@ export function ChatView({ useSession, useStore, renderSlot, openDetails, loadOl
|
|
|
callId={call.callId}
|
|
|
toolName={call.name}
|
|
|
block={call}
|
|
|
- seq={call.turn}
|
|
|
- onOpenDetails={openDetails}
|
|
|
+ openFile={openFile}
|
|
|
selected={call.callId === selectedCallId}
|
|
|
subCalls={codeDispatches.get(call.callId)}
|
|
|
selectedCallId={selectedCallId}
|