File size: 2,826 Bytes
abcf568 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | import { shouldHideDuplicateOptimisticAssistant } from '../../agent-response/visibility'
import type { StudioMessage } from '../../protocol/studio-agent-types'
import type { StudioCommandPanelSnapshot } from './store'
export interface StudioCommandRowView {
message: StudioMessage | null
isStreamingTarget: boolean
streamedText: string
showCaret: boolean
}
const emptyVisibleMessageIds: string[] = []
let visibleMessageIdsCache = {
messages: null as StudioMessage[] | null,
isBusy: false,
ids: emptyVisibleMessageIds,
}
export function selectVisibleMessageIds(snapshot: StudioCommandPanelSnapshot): string[] {
if (
visibleMessageIdsCache.messages === snapshot.messages
&& visibleMessageIdsCache.isBusy === snapshot.isBusy
) {
return visibleMessageIdsCache.ids
}
const ids = snapshot.messages
.filter((message, index, messages) => {
if (message.role === 'user') {
return true
}
if (shouldHideDuplicateOptimisticAssistant(messages, index)) {
return false
}
return shouldRenderAssistantMessage(message, {
isLast: index === messages.length - 1,
isBusy: snapshot.isBusy,
})
})
.map((message) => message.renderId ?? message.id)
visibleMessageIdsCache = {
messages: snapshot.messages,
isBusy: snapshot.isBusy,
ids,
}
return ids
}
export function selectRowView(
snapshot: StudioCommandPanelSnapshot,
messageId: string,
): StudioCommandRowView {
const message = snapshot.messages.find((entry) => (entry.renderId ?? entry.id) === messageId) ?? null
if (!message) {
return {
message: null,
isStreamingTarget: false,
streamedText: '',
showCaret: false,
}
}
const lastMessage = snapshot.messages.at(-1) ?? null
const streamIntoLastAssistant = Boolean(
lastMessage
&& lastMessage.role === 'assistant'
&& (snapshot.isBusy || snapshot.latestAssistantText || snapshot.animatedAssistantText)
)
const isStreamingTarget = streamIntoLastAssistant && lastMessage?.id === message.id
return {
message,
isStreamingTarget,
streamedText: isStreamingTarget ? snapshot.animatedAssistantText : '',
showCaret: Boolean(
isStreamingTarget
&& (snapshot.isBusy || snapshot.latestAssistantText !== snapshot.animatedAssistantText)
),
}
}
function shouldRenderAssistantMessage(
message: Extract<StudioMessage, { role: 'assistant' }>,
options: { isLast: boolean; isBusy: boolean },
): boolean {
const hasRenderableText = message.parts.some((part) => (
(part.type === 'text' || part.type === 'reasoning') && part.text.trim()
))
const hasToolParts = message.parts.some((part) => part.type === 'tool')
if (hasRenderableText || hasToolParts) {
return true
}
return options.isLast && options.isBusy
}
|