|
5 | 5 | import ChatMessageAssistant from './ChatMessageAssistant.svelte'; |
6 | 6 | import ChatMessageUser from './ChatMessageUser.svelte'; |
7 | 7 | import ChatMessageSystem from './ChatMessageSystem.svelte'; |
| 8 | + import { conversationsStore } from '$lib/stores/conversations.svelte'; |
8 | 9 |
|
9 | 10 | interface Props { |
10 | 11 | class?: string; |
|
22 | 23 | onNavigateToSibling?: (siblingId: string) => void; |
23 | 24 | onRegenerateWithBranching?: (message: DatabaseMessage, modelOverride?: string) => void; |
24 | 25 | siblingInfo?: ChatMessageSiblingInfo | null; |
| 26 | + toolParentIds?: string[]; |
25 | 27 | } |
26 | 28 |
|
27 | 29 | let { |
|
35 | 37 | onEditUserMessagePreserveResponses, |
36 | 38 | onNavigateToSibling, |
37 | 39 | onRegenerateWithBranching, |
38 | | - siblingInfo = null |
| 40 | + siblingInfo = null, |
| 41 | + toolParentIds |
39 | 42 | }: Props = $props(); |
40 | 43 |
|
| 44 | + type MessageWithToolExtras = DatabaseMessage & { |
| 45 | + _actionTargetId?: string; |
| 46 | + _toolMessagesCollected?: { toolCallId?: string | null; parsed: unknown }[]; |
| 47 | + }; |
| 48 | +
|
| 49 | + const actionTargetId = $derived((message as MessageWithToolExtras)._actionTargetId ?? message.id); |
| 50 | +
|
| 51 | + function getActionTarget(): DatabaseMessage { |
| 52 | + return conversationsStore.activeMessages.find((m) => m.id === actionTargetId) ?? message; |
| 53 | + } |
| 54 | +
|
41 | 55 | let deletionInfo = $state<{ |
42 | 56 | totalCount: number; |
43 | 57 | userMessages: number; |
|
95 | 109 | } |
96 | 110 |
|
97 | 111 | function handleConfirmDelete() { |
98 | | - onDelete?.(message); |
| 112 | + const target = getActionTarget(); |
| 113 | + onDelete?.(target); |
99 | 114 | showDeleteDialog = false; |
100 | 115 | } |
101 | 116 |
|
102 | 117 | async function handleDelete() { |
103 | | - deletionInfo = await chatStore.getDeletionInfo(message.id); |
| 118 | + const target = getActionTarget(); |
| 119 | + deletionInfo = await chatStore.getDeletionInfo(target.id); |
104 | 120 | showDeleteDialog = true; |
105 | 121 | } |
106 | 122 |
|
|
136 | 152 | } |
137 | 153 |
|
138 | 154 | function handleRegenerate(modelOverride?: string) { |
139 | | - onRegenerateWithBranching?.(message, modelOverride); |
| 155 | + const target = getActionTarget(); |
| 156 | + onRegenerateWithBranching?.(target, modelOverride); |
140 | 157 | } |
141 | 158 |
|
142 | 159 | function handleContinue() { |
143 | | - onContinueAssistantMessage?.(message); |
| 160 | + const target = getActionTarget(); |
| 161 | + onContinueAssistantMessage?.(target); |
144 | 162 | } |
145 | 163 |
|
146 | 164 | function handleSaveEdit() { |
|
213 | 231 | {showDeleteDialog} |
214 | 232 | {siblingInfo} |
215 | 233 | /> |
216 | | -{:else} |
| 234 | +{:else if message.role === 'assistant'} |
217 | 235 | <ChatMessageAssistant |
218 | 236 | bind:textareaElement |
219 | 237 | class={className} |
|
240 | 258 | {siblingInfo} |
241 | 259 | {thinkingContent} |
242 | 260 | {toolCallContent} |
| 261 | + toolParentIds={toolParentIds ?? [message.id]} |
| 262 | + toolMessagesCollected={(message as MessageWithToolExtras)._toolMessagesCollected} |
243 | 263 | /> |
| 264 | +{:else if message.role === 'tool'} |
| 265 | + <!-- Tool messages are rendered inline inside their parent assistant's reasoning block. |
| 266 | + Skip standalone rendering to avoid duplicate bubbles. --> |
| 267 | + <!-- Intentionally left blank --> |
244 | 268 | {/if} |
0 commit comments