fix: cr
This commit is contained in:
@@ -30,7 +30,9 @@ 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 { assistantActionsSeqs, deriveChatFlow, type ChatFlowItem } from './chat-flow.ts'
|
||||
import {
|
||||
assistantActionsSeqs, deriveChatFlow, withholdActionsTurn, type ChatFlowItem,
|
||||
} from './chat-flow.ts'
|
||||
import { AssistantMarkdown } from './AssistantMarkdown.tsx'
|
||||
import { GenericCommandCard } from './GenericCommandCard.tsx'
|
||||
import { GenericToolCard } from './GenericToolCard.tsx'
|
||||
@@ -236,6 +238,9 @@ export function ChatView({ useSession, useSessions, useStore, renderSlot, sessio
|
||||
const cwd = useSessions(s => s.byId[sessionId]?.cwd)
|
||||
const running = useSession(s => s.running)
|
||||
const runningCalls = useSession(s => s.runningCalls)
|
||||
// Primitive turn (or null): stable across chunk storms so this parent does
|
||||
// not re-render per token the way a partial.blocks subscribe would.
|
||||
const withholdTurn = useSession(s => withholdActionsTurn(s.running, s.partial, s.runningCalls))
|
||||
const codeDispatches = useSession(s => s.codeDispatches)
|
||||
const openState = useSession(s => s.openState)
|
||||
const openErrorMessage = useSession(s => s.openError === null ? null : `${s.openError.message}(${s.openError.code})`)
|
||||
@@ -244,9 +249,12 @@ export function ChatView({ useSession, useSessions, useStore, renderSlot, sessio
|
||||
const selectedCallId = useStore(s => s.selection?.callId)
|
||||
|
||||
const items = useMemo(() => deriveChatFlow(nodes), [nodes])
|
||||
// Only the last content assistant of each turn owns IconActions; mid-turn
|
||||
// text (before tools) omits `time` so AssistantMarkdown stays chrome-free.
|
||||
const actionSeqs = useMemo(() => assistantActionsSeqs(nodes), [nodes])
|
||||
// Settled turn-tail content only; a running turn withholds its whole seat so
|
||||
// mid-turn narration does not flash copy/branch/clock while tools run.
|
||||
const actionSeqs = useMemo(
|
||||
() => assistantActionsSeqs(nodes, withholdTurn),
|
||||
[nodes, withholdTurn],
|
||||
)
|
||||
|
||||
const listRef = useRef<HTMLDivElement | null>(null)
|
||||
const atBottomRef = useRef(true)
|
||||
|
||||
@@ -31,16 +31,43 @@ function rendersNothing(node: ConversationNode): boolean {
|
||||
|| ((b.kind === 'text' || b.kind === 'reasoning') && b.text.trim() === ''))
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn whose content assistants must stay chrome-free while the turn is still
|
||||
* running. Prefers the streaming partial, else the first in-flight tool call;
|
||||
* returns null when `running` is false or neither signal exists yet (a brand-new
|
||||
* turn before the first step must not strip a prior settled answer's seat).
|
||||
* @param running - snapshot `running` bit.
|
||||
* @param partial - in-flight assistant partial, or null.
|
||||
* @param runningCalls - in-flight tool rows (same turn while tools execute).
|
||||
* @returns Turn to withhold, or null.
|
||||
*/
|
||||
export function withholdActionsTurn(
|
||||
running: boolean,
|
||||
partial: { turn: number } | null,
|
||||
runningCalls: readonly { turn: number }[],
|
||||
): number | null {
|
||||
if (!running) return null
|
||||
if (partial !== null) return partial.turn
|
||||
return runningCalls[0]?.turn ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
* Seq set of assistants that own IconActions: the last content-text assistant
|
||||
* in each turn. Mid-turn narration (text before tools) stays chrome-free.
|
||||
* in each *settled* turn. Mid-turn narration and every content assistant of a
|
||||
* still-running turn stay chrome-free (no flash while tools run or the next
|
||||
* step streams).
|
||||
* @param nodes - snapshot nodes (surface order).
|
||||
* @param withholdTurn - active turn from {@link withholdActionsTurn}, or null.
|
||||
* @returns Seq values ChatView may pass as `time` into AssistantMarkdown.
|
||||
*/
|
||||
export function assistantActionsSeqs(nodes: readonly ConversationNode[]): ReadonlySet<number> {
|
||||
export function assistantActionsSeqs(
|
||||
nodes: readonly ConversationNode[],
|
||||
withholdTurn: number | null = null,
|
||||
): ReadonlySet<number> {
|
||||
const lastByTurn = new Map<number, number>()
|
||||
for (const node of nodes) {
|
||||
if (node.kind !== 'assistant' || !hasContentText(node.blocks)) continue
|
||||
if (withholdTurn !== null && node.turn === withholdTurn) continue
|
||||
lastByTurn.set(node.turn, node.seq)
|
||||
}
|
||||
return new Set(lastByTurn.values())
|
||||
|
||||
Reference in New Issue
Block a user