Make MessageBlock output reactive via Show

This commit is contained in:
Shantur Rathore
2025-11-30 20:27:54 +00:00
parent 1e4d949d35
commit 5ce41217e9

View File

@@ -226,12 +226,6 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
const store = createMemo(() => messageStoreBus.getOrCreate(props.instanceId)) const store = createMemo(() => messageStoreBus.getOrCreate(props.instanceId))
const messageIds = createMemo(() => store().getSessionMessageIds(props.sessionId)) const messageIds = createMemo(() => store().getSessionMessageIds(props.sessionId))
createEffect(() => {
const ids = messageIds()
console.info("[MessageStreamV2] messageIds", { sessionId: props.sessionId, ids })
})
const sessionRevision = createMemo(() => store().getSessionRevision(props.sessionId)) const sessionRevision = createMemo(() => store().getSessionRevision(props.sessionId))
const usageSnapshot = createMemo(() => store().getSessionUsage(props.sessionId)) const usageSnapshot = createMemo(() => store().getSessionUsage(props.sessionId))
const sessionInfo = createMemo(() => const sessionInfo = createMemo(() =>
@@ -703,7 +697,6 @@ function MessageBlock(props: MessageBlockProps) {
const block = createMemo<MessageDisplayBlock | null>(() => { const block = createMemo<MessageDisplayBlock | null>(() => {
const current = record() const current = record()
if (!current) { if (!current) {
console.warn("[MessageBlock] missing record", { messageId: props.messageId, sessionId: props.sessionId })
return null return null
} }
@@ -877,107 +870,108 @@ function MessageBlock(props: MessageBlockProps) {
return resultBlock return resultBlock
}) })
const resolvedBlock = block()
if (!resolvedBlock) return null
return ( return (
<div class="message-stream-block" data-message-id={resolvedBlock.record.id}> <Show when={block()} keyed>
<For each={resolvedBlock.items}> {(resolvedBlock) => (
{(item) => ( <div class="message-stream-block" data-message-id={resolvedBlock.record.id}>
<Switch> <For each={resolvedBlock.items}>
<Match when={item.type === "content"}> {(item) => (
<MessageItem <Switch>
record={(item as ContentDisplayItem).record} <Match when={item.type === "content"}>
messageInfo={(item as ContentDisplayItem).messageInfo} <MessageItem
combinedParts={(item as ContentDisplayItem).parts} record={(item as ContentDisplayItem).record}
orderedParts={(item as ContentDisplayItem).parts} messageInfo={(item as ContentDisplayItem).messageInfo}
instanceId={props.instanceId} combinedParts={(item as ContentDisplayItem).parts}
sessionId={props.sessionId} orderedParts={(item as ContentDisplayItem).parts}
isQueued={(item as ContentDisplayItem).isQueued} instanceId={props.instanceId}
showAgentMeta={(item as ContentDisplayItem).showAgentMeta} sessionId={props.sessionId}
onRevert={props.onRevert} isQueued={(item as ContentDisplayItem).isQueued}
onFork={props.onFork} showAgentMeta={(item as ContentDisplayItem).showAgentMeta}
/> onRevert={props.onRevert}
</Match> onFork={props.onFork}
<Match when={item.type === "tool"}> />
{(() => { </Match>
const toolItem = item as ToolDisplayItem <Match when={item.type === "tool"}>
const toolState = toolItem.toolPart.state as ToolState | undefined {(() => {
const hasToolState = const toolItem = item as ToolDisplayItem
Boolean(toolState) && (isToolStateRunning(toolState) || isToolStateCompleted(toolState) || isToolStateError(toolState)) const toolState = toolItem.toolPart.state as ToolState | undefined
const taskSessionId = hasToolState ? extractTaskSessionId(toolState) : "" const hasToolState =
const taskLocation = taskSessionId ? findTaskSessionLocation(taskSessionId) : null Boolean(toolState) && (isToolStateRunning(toolState) || isToolStateCompleted(toolState) || isToolStateError(toolState))
const handleGoToTaskSession = (event: MouseEvent) => { const taskSessionId = hasToolState ? extractTaskSessionId(toolState) : ""
event.preventDefault() const taskLocation = taskSessionId ? findTaskSessionLocation(taskSessionId) : null
event.stopPropagation() const handleGoToTaskSession = (event: MouseEvent) => {
if (!taskLocation) return event.preventDefault()
navigateToTaskSession(taskLocation) event.stopPropagation()
} if (!taskLocation) return
navigateToTaskSession(taskLocation)
}
return ( return (
<div class="tool-call-message" data-key={toolItem.key}> <div class="tool-call-message" data-key={toolItem.key}>
<div class="tool-call-header-label"> <div class="tool-call-header-label">
<div class="tool-call-header-meta"> <div class="tool-call-header-meta">
<span class="tool-call-icon">{TOOL_ICON}</span> <span class="tool-call-icon">{TOOL_ICON}</span>
<span>Tool Call</span> <span>Tool Call</span>
<span class="tool-name">{toolItem.toolPart.tool || "unknown"}</span> <span class="tool-name">{toolItem.toolPart.tool || "unknown"}</span>
</div>
<Show when={taskSessionId}>
<button
class="tool-call-header-button"
type="button"
disabled={!taskLocation}
onClick={handleGoToTaskSession}
title={!taskLocation ? "Session not available yet" : "Go to session"}
>
Go to Session
</button>
</Show>
</div>
<ToolCall
toolCall={toolItem.toolPart}
toolCallId={toolItem.key}
messageId={toolItem.messageId}
messageVersion={toolItem.messageVersion}
partVersion={toolItem.partVersion}
instanceId={props.instanceId}
sessionId={props.sessionId}
/>
</div> </div>
<Show when={taskSessionId}> )
<button })()}
class="tool-call-header-button" </Match>
type="button" <Match when={item.type === "step-start"}>
disabled={!taskLocation} <StepCard
onClick={handleGoToTaskSession} kind="start"
title={!taskLocation ? "Session not available yet" : "Go to session"} part={(item as StepDisplayItem).part}
> messageInfo={(item as StepDisplayItem).messageInfo}
Go to Session showAgentMeta
</button> />
</Show> </Match>
</div> <Match when={item.type === "step-finish"}>
<ToolCall <StepCard
toolCall={toolItem.toolPart} kind="finish"
toolCallId={toolItem.key} part={(item as StepDisplayItem).part}
messageId={toolItem.messageId} messageInfo={(item as StepDisplayItem).messageInfo}
messageVersion={toolItem.messageVersion} showUsage={props.showUsageMetrics}
partVersion={toolItem.partVersion} borderColor={(item as StepDisplayItem).accentColor}
instanceId={props.instanceId} />
sessionId={props.sessionId} </Match>
/> <Match when={item.type === "reasoning"}>
</div> <ReasoningCard
) part={(item as ReasoningDisplayItem).part}
})()} messageInfo={(item as ReasoningDisplayItem).messageInfo}
</Match> instanceId={props.instanceId}
<Match when={item.type === "step-start"}> sessionId={props.sessionId}
<StepCard showAgentMeta={(item as ReasoningDisplayItem).showAgentMeta}
kind="start" defaultExpanded={(item as ReasoningDisplayItem).defaultExpanded}
part={(item as StepDisplayItem).part} />
messageInfo={(item as StepDisplayItem).messageInfo} </Match>
showAgentMeta </Switch>
/> )}
</Match> </For>
<Match when={item.type === "step-finish"}> </div>
<StepCard )}
kind="finish" </Show>
part={(item as StepDisplayItem).part}
messageInfo={(item as StepDisplayItem).messageInfo}
showUsage={props.showUsageMetrics}
borderColor={(item as StepDisplayItem).accentColor}
/>
</Match>
<Match when={item.type === "reasoning"}>
<ReasoningCard
part={(item as ReasoningDisplayItem).part}
messageInfo={(item as ReasoningDisplayItem).messageInfo}
instanceId={props.instanceId}
sessionId={props.sessionId}
showAgentMeta={(item as ReasoningDisplayItem).showAgentMeta}
defaultExpanded={(item as ReasoningDisplayItem).defaultExpanded}
/>
</Match>
</Switch>
)}
</For>
</div>
) )
} }