@tanstack/ai-devtools-core 0.3.37 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Shell/DKCFIT6H.js +12142 -0
- package/dist/Shell/SLOX2TPU.js +12119 -0
- package/dist/Shell/Z6HLJOVZ.js +10170 -0
- package/dist/dev.js +1 -1
- package/dist/index.js +1 -1
- package/dist/production/Shell/DKCFIT6H.js +12142 -0
- package/dist/production/Shell/SLOX2TPU.js +12119 -0
- package/dist/production/Shell/Z6HLJOVZ.js +10170 -0
- package/dist/production/dev.js +1 -1
- package/dist/production/index.js +1 -1
- package/dist/production/server.js +1 -1
- package/dist/server.js +1 -1
- package/package.json +4 -4
- package/src/components/Shell.tsx +74 -30
- package/src/components/conversation/IterationCard.tsx +261 -12
- package/src/components/conversation/IterationTimeline.tsx +66 -16
- package/src/components/conversation/index.ts +0 -9
- package/src/components/hooks/FixtureNamePopover.tsx +69 -0
- package/src/components/hooks/GenerationPanel.tsx +847 -0
- package/src/components/hooks/HookDashboard.tsx +292 -0
- package/src/components/hooks/HookDetails.tsx +1757 -0
- package/src/components/hooks/ToolFixtureForm.tsx +347 -0
- package/src/components/hooks/hook-dashboard-model.ts +134 -0
- package/src/components/hooks/index.ts +4 -0
- package/src/components/hooks/preview-messages.ts +117 -0
- package/src/components/hooks/preview-model.ts +195 -0
- package/src/components/utils/format.ts +0 -58
- package/src/store/ai-context.tsx +1165 -159
- package/src/store/ai-store.ts +0 -1
- package/src/store/hook-registry.ts +815 -0
- package/src/store/message-event-utils.ts +60 -0
- package/src/styles/use-styles.ts +1558 -99
- package/dist/Shell/MRWZEHAJ.js +0 -6225
- package/dist/Shell/RRRNZJ5E.js +0 -7560
- package/dist/production/Shell/MRWZEHAJ.js +0 -6225
- package/dist/production/Shell/RRRNZJ5E.js +0 -7560
- package/src/components/ConversationDetails.tsx +0 -216
- package/src/components/ConversationsList.tsx +0 -21
- package/src/components/conversation/ActivityEventsTab.tsx +0 -57
- package/src/components/conversation/ChunkBadges.tsx +0 -51
- package/src/components/conversation/ChunkItem.tsx +0 -273
- package/src/components/conversation/ChunksCollapsible.tsx +0 -56
- package/src/components/conversation/ChunksTab.tsx +0 -87
- package/src/components/conversation/ConversationHeader.tsx +0 -106
- package/src/components/conversation/ConversationTabs.tsx +0 -205
- package/src/components/conversation/MessageCard.tsx +0 -174
- package/src/components/conversation/MessageGroup.tsx +0 -101
- package/src/components/conversation/MessagesTab.tsx +0 -30
- package/src/components/conversation/SummariesTab.tsx +0 -91
- package/src/components/conversation/ToolCallDisplay.tsx +0 -99
- package/src/components/list/ConversationRow.tsx +0 -85
- package/src/components/list/index.ts +0 -1
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import { Show, createEffect, createSignal } from 'solid-js'
|
|
2
|
-
import { useStyles } from '../styles/use-styles'
|
|
3
|
-
import { useAIStore } from '../store/ai-context'
|
|
4
|
-
import {
|
|
5
|
-
ActivityEventsTab,
|
|
6
|
-
ChunksTab,
|
|
7
|
-
ConversationHeader,
|
|
8
|
-
ConversationTabs,
|
|
9
|
-
IterationTimeline,
|
|
10
|
-
MessagesTab,
|
|
11
|
-
SummariesTab,
|
|
12
|
-
} from './conversation'
|
|
13
|
-
import type { TabType } from './conversation'
|
|
14
|
-
import type { Conversation } from '../store/ai-context'
|
|
15
|
-
import type { Component } from 'solid-js'
|
|
16
|
-
|
|
17
|
-
export const ConversationDetails: Component = () => {
|
|
18
|
-
const { state } = useAIStore()
|
|
19
|
-
const styles = useStyles()
|
|
20
|
-
const [activeTab, setActiveTab] = createSignal<TabType>('messages')
|
|
21
|
-
|
|
22
|
-
const activeConversation = (): Conversation | undefined => {
|
|
23
|
-
if (!state.activeConversationId) return undefined
|
|
24
|
-
return state.conversations[state.activeConversationId]
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const hasIterations = () => {
|
|
28
|
-
const conv = activeConversation()
|
|
29
|
-
return conv && conv.iterations.length > 0
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const hasActivityTabs = () => {
|
|
33
|
-
const conv = activeConversation()
|
|
34
|
-
if (!conv) return false
|
|
35
|
-
return (
|
|
36
|
-
conv.hasSummarize ||
|
|
37
|
-
conv.hasImage ||
|
|
38
|
-
conv.hasSpeech ||
|
|
39
|
-
conv.hasTranscription ||
|
|
40
|
-
conv.hasVideo
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Update active tab when conversation changes (only for non-iteration views)
|
|
45
|
-
createEffect(() => {
|
|
46
|
-
const conv = activeConversation()
|
|
47
|
-
if (!conv) return
|
|
48
|
-
|
|
49
|
-
// If iterations exist, the timeline is the primary view — only set tab for activity
|
|
50
|
-
if (conv.iterations.length > 0) {
|
|
51
|
-
if (conv.hasSummarize || (conv.summaries && conv.summaries.length > 0)) {
|
|
52
|
-
setActiveTab('summaries')
|
|
53
|
-
} else if (
|
|
54
|
-
conv.hasImage ||
|
|
55
|
-
(conv.imageEvents && conv.imageEvents.length > 0)
|
|
56
|
-
) {
|
|
57
|
-
setActiveTab('image')
|
|
58
|
-
}
|
|
59
|
-
return
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// No iterations — use flat message/chunk view
|
|
63
|
-
if (conv.type === 'server') {
|
|
64
|
-
if (conv.chunks.length > 0) {
|
|
65
|
-
setActiveTab('chunks')
|
|
66
|
-
} else if (
|
|
67
|
-
conv.hasSummarize ||
|
|
68
|
-
(conv.summaries && conv.summaries.length > 0)
|
|
69
|
-
) {
|
|
70
|
-
setActiveTab('summaries')
|
|
71
|
-
} else if (
|
|
72
|
-
conv.hasImage ||
|
|
73
|
-
(conv.imageEvents && conv.imageEvents.length > 0)
|
|
74
|
-
) {
|
|
75
|
-
setActiveTab('image')
|
|
76
|
-
} else if (
|
|
77
|
-
conv.hasSpeech ||
|
|
78
|
-
(conv.speechEvents && conv.speechEvents.length > 0)
|
|
79
|
-
) {
|
|
80
|
-
setActiveTab('speech')
|
|
81
|
-
} else if (
|
|
82
|
-
conv.hasTranscription ||
|
|
83
|
-
(conv.transcriptionEvents && conv.transcriptionEvents.length > 0)
|
|
84
|
-
) {
|
|
85
|
-
setActiveTab('transcription')
|
|
86
|
-
} else if (
|
|
87
|
-
conv.hasVideo ||
|
|
88
|
-
(conv.videoEvents && conv.videoEvents.length > 0)
|
|
89
|
-
) {
|
|
90
|
-
setActiveTab('video')
|
|
91
|
-
} else {
|
|
92
|
-
setActiveTab('chunks')
|
|
93
|
-
}
|
|
94
|
-
} else {
|
|
95
|
-
if (conv.messages.length > 0) {
|
|
96
|
-
setActiveTab('messages')
|
|
97
|
-
} else if (
|
|
98
|
-
conv.hasImage ||
|
|
99
|
-
(conv.imageEvents && conv.imageEvents.length > 0)
|
|
100
|
-
) {
|
|
101
|
-
setActiveTab('image')
|
|
102
|
-
} else {
|
|
103
|
-
setActiveTab('messages')
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<Show
|
|
110
|
-
when={activeConversation()}
|
|
111
|
-
fallback={
|
|
112
|
-
<div class={styles().conversationDetails.emptyState}>
|
|
113
|
-
Select a conversation to view details
|
|
114
|
-
</div>
|
|
115
|
-
}
|
|
116
|
-
>
|
|
117
|
-
{(conv) => (
|
|
118
|
-
<div class={styles().conversationDetails.container}>
|
|
119
|
-
<ConversationHeader conversation={conv()} />
|
|
120
|
-
|
|
121
|
-
{/* Primary view: iteration timeline when iterations exist */}
|
|
122
|
-
<Show when={hasIterations()}>
|
|
123
|
-
<div class={styles().conversationDetails.contentArea}>
|
|
124
|
-
<IterationTimeline
|
|
125
|
-
iterations={conv().iterations}
|
|
126
|
-
messages={conv().messages}
|
|
127
|
-
/>
|
|
128
|
-
</div>
|
|
129
|
-
</Show>
|
|
130
|
-
|
|
131
|
-
{/* Fallback: flat message/chunk view when no iterations */}
|
|
132
|
-
<Show when={!hasIterations()}>
|
|
133
|
-
<ConversationTabs
|
|
134
|
-
conversation={conv()}
|
|
135
|
-
activeTab={activeTab()}
|
|
136
|
-
onTabChange={setActiveTab}
|
|
137
|
-
/>
|
|
138
|
-
<div class={styles().conversationDetails.contentArea}>
|
|
139
|
-
<Show when={activeTab() === 'messages'}>
|
|
140
|
-
<MessagesTab messages={conv().messages} />
|
|
141
|
-
</Show>
|
|
142
|
-
<Show when={activeTab() === 'chunks'}>
|
|
143
|
-
<ChunksTab chunks={conv().chunks} messages={conv().messages} />
|
|
144
|
-
</Show>
|
|
145
|
-
<Show when={activeTab() === 'summaries'}>
|
|
146
|
-
<SummariesTab summaries={conv().summaries ?? []} />
|
|
147
|
-
</Show>
|
|
148
|
-
<Show when={activeTab() === 'image'}>
|
|
149
|
-
<ActivityEventsTab
|
|
150
|
-
title="Image Activity"
|
|
151
|
-
events={conv().imageEvents ?? []}
|
|
152
|
-
/>
|
|
153
|
-
</Show>
|
|
154
|
-
<Show when={activeTab() === 'speech'}>
|
|
155
|
-
<ActivityEventsTab
|
|
156
|
-
title="Speech Activity"
|
|
157
|
-
events={conv().speechEvents ?? []}
|
|
158
|
-
/>
|
|
159
|
-
</Show>
|
|
160
|
-
<Show when={activeTab() === 'transcription'}>
|
|
161
|
-
<ActivityEventsTab
|
|
162
|
-
title="Transcription Activity"
|
|
163
|
-
events={conv().transcriptionEvents ?? []}
|
|
164
|
-
/>
|
|
165
|
-
</Show>
|
|
166
|
-
<Show when={activeTab() === 'video'}>
|
|
167
|
-
<ActivityEventsTab
|
|
168
|
-
title="Video Activity"
|
|
169
|
-
events={conv().videoEvents ?? []}
|
|
170
|
-
/>
|
|
171
|
-
</Show>
|
|
172
|
-
</div>
|
|
173
|
-
</Show>
|
|
174
|
-
|
|
175
|
-
{/* Activity tabs shown below iterations when relevant */}
|
|
176
|
-
<Show when={hasIterations() && hasActivityTabs()}>
|
|
177
|
-
<ConversationTabs
|
|
178
|
-
conversation={conv()}
|
|
179
|
-
activeTab={activeTab()}
|
|
180
|
-
onTabChange={setActiveTab}
|
|
181
|
-
/>
|
|
182
|
-
<div class={styles().conversationDetails.contentArea}>
|
|
183
|
-
<Show when={activeTab() === 'summaries'}>
|
|
184
|
-
<SummariesTab summaries={conv().summaries ?? []} />
|
|
185
|
-
</Show>
|
|
186
|
-
<Show when={activeTab() === 'image'}>
|
|
187
|
-
<ActivityEventsTab
|
|
188
|
-
title="Image Activity"
|
|
189
|
-
events={conv().imageEvents ?? []}
|
|
190
|
-
/>
|
|
191
|
-
</Show>
|
|
192
|
-
<Show when={activeTab() === 'speech'}>
|
|
193
|
-
<ActivityEventsTab
|
|
194
|
-
title="Speech Activity"
|
|
195
|
-
events={conv().speechEvents ?? []}
|
|
196
|
-
/>
|
|
197
|
-
</Show>
|
|
198
|
-
<Show when={activeTab() === 'transcription'}>
|
|
199
|
-
<ActivityEventsTab
|
|
200
|
-
title="Transcription Activity"
|
|
201
|
-
events={conv().transcriptionEvents ?? []}
|
|
202
|
-
/>
|
|
203
|
-
</Show>
|
|
204
|
-
<Show when={activeTab() === 'video'}>
|
|
205
|
-
<ActivityEventsTab
|
|
206
|
-
title="Video Activity"
|
|
207
|
-
events={conv().videoEvents ?? []}
|
|
208
|
-
/>
|
|
209
|
-
</Show>
|
|
210
|
-
</div>
|
|
211
|
-
</Show>
|
|
212
|
-
</div>
|
|
213
|
-
)}
|
|
214
|
-
</Show>
|
|
215
|
-
)
|
|
216
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { For } from 'solid-js'
|
|
2
|
-
import { useStyles } from '../styles/use-styles'
|
|
3
|
-
import { useAIStore } from '../store/ai-context'
|
|
4
|
-
import { ConversationRow } from './list'
|
|
5
|
-
import type { Conversation } from '../store/ai-context'
|
|
6
|
-
import type { Component } from 'solid-js'
|
|
7
|
-
|
|
8
|
-
export const ConversationsList: Component = () => {
|
|
9
|
-
const { state } = useAIStore()
|
|
10
|
-
const styles = useStyles()
|
|
11
|
-
|
|
12
|
-
const conversations = () => Object.values(state.conversations)
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<div class={styles().utilList}>
|
|
16
|
-
<For each={conversations()}>
|
|
17
|
-
{(conv: Conversation) => <ConversationRow conversation={conv} />}
|
|
18
|
-
</For>
|
|
19
|
-
</div>
|
|
20
|
-
)
|
|
21
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { For, Show } from 'solid-js'
|
|
2
|
-
import { useStyles } from '../../styles/use-styles'
|
|
3
|
-
import type { Component } from 'solid-js'
|
|
4
|
-
import type { ActivityEvent } from '../../store/ai-context'
|
|
5
|
-
|
|
6
|
-
interface ActivityEventsTabProps {
|
|
7
|
-
title: string
|
|
8
|
-
events: Array<ActivityEvent>
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const ActivityEventsTab: Component<ActivityEventsTabProps> = (props) => {
|
|
12
|
-
const styles = useStyles()
|
|
13
|
-
|
|
14
|
-
const formattedTimestamp = (timestamp: number) =>
|
|
15
|
-
new Date(timestamp).toLocaleTimeString()
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<Show
|
|
19
|
-
when={props.events.length > 0}
|
|
20
|
-
fallback={
|
|
21
|
-
<div class={styles().conversationDetails.noChunks}>No events yet</div>
|
|
22
|
-
}
|
|
23
|
-
>
|
|
24
|
-
<div class={styles().conversationDetails.streamContainer}>
|
|
25
|
-
<div class={styles().conversationDetails.streamHeader}>
|
|
26
|
-
<div class={styles().conversationDetails.streamHeaderRow}>
|
|
27
|
-
<div class={styles().conversationDetails.streamTitle}>
|
|
28
|
-
{props.title}
|
|
29
|
-
</div>
|
|
30
|
-
<div
|
|
31
|
-
class={`${styles().conversationDetails.chunkBadge} ${styles().conversationDetails.chunkBadgeCount}`}
|
|
32
|
-
>
|
|
33
|
-
{props.events.length} events
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
<div class={styles().conversationDetails.streamSubtitle}>
|
|
37
|
-
Activity event log
|
|
38
|
-
</div>
|
|
39
|
-
</div>
|
|
40
|
-
<div class={styles().conversationDetails.messageGroups}>
|
|
41
|
-
<For each={props.events}>
|
|
42
|
-
{(event) => (
|
|
43
|
-
<div class={styles().detailSection}>
|
|
44
|
-
<div class={styles().detailSectionHeader}>
|
|
45
|
-
{event.name} · {formattedTimestamp(event.timestamp)}
|
|
46
|
-
</div>
|
|
47
|
-
<pre class={styles().conversationDetails.jsonPreview}>
|
|
48
|
-
{JSON.stringify(event.payload, null, 2)}
|
|
49
|
-
</pre>
|
|
50
|
-
</div>
|
|
51
|
-
)}
|
|
52
|
-
</For>
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
</Show>
|
|
56
|
-
)
|
|
57
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { Show } from 'solid-js'
|
|
2
|
-
import { useStyles } from '../../styles/use-styles'
|
|
3
|
-
import type { Component } from 'solid-js'
|
|
4
|
-
import type { Chunk } from '../../store/ai-store'
|
|
5
|
-
|
|
6
|
-
interface ChunkBadgesProps {
|
|
7
|
-
chunks: Array<Chunk>
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const ChunkBadges: Component<ChunkBadgesProps> = (props) => {
|
|
11
|
-
const styles = useStyles()
|
|
12
|
-
|
|
13
|
-
const hasToolCalls = () => props.chunks.some((c) => c.type === 'tool_call')
|
|
14
|
-
const hasErrors = () => props.chunks.some((c) => c.type === 'error')
|
|
15
|
-
const hasApproval = () => props.chunks.some((c) => c.type === 'approval')
|
|
16
|
-
const finishReason = () =>
|
|
17
|
-
props.chunks.find((c) => c.type === 'done')?.finishReason
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<>
|
|
21
|
-
<Show when={hasToolCalls()}>
|
|
22
|
-
<span
|
|
23
|
-
class={`${styles().conversationDetails.chunkBadge} ${styles().conversationDetails.chunkBadgeTool}`}
|
|
24
|
-
>
|
|
25
|
-
🔧 Tool Calls
|
|
26
|
-
</span>
|
|
27
|
-
</Show>
|
|
28
|
-
<Show when={hasErrors()}>
|
|
29
|
-
<span
|
|
30
|
-
class={`${styles().conversationDetails.chunkBadge} ${styles().conversationDetails.chunkBadgeError}`}
|
|
31
|
-
>
|
|
32
|
-
❌ Error
|
|
33
|
-
</span>
|
|
34
|
-
</Show>
|
|
35
|
-
<Show when={hasApproval()}>
|
|
36
|
-
<span
|
|
37
|
-
class={`${styles().conversationDetails.chunkBadge} ${styles().conversationDetails.chunkBadgeApproval}`}
|
|
38
|
-
>
|
|
39
|
-
⚠️ Approval
|
|
40
|
-
</span>
|
|
41
|
-
</Show>
|
|
42
|
-
<Show when={finishReason()}>
|
|
43
|
-
<span
|
|
44
|
-
class={`${styles().conversationDetails.chunkBadge} ${styles().conversationDetails.chunkBadgeSuccess}`}
|
|
45
|
-
>
|
|
46
|
-
✓ {finishReason()}
|
|
47
|
-
</span>
|
|
48
|
-
</Show>
|
|
49
|
-
</>
|
|
50
|
-
)
|
|
51
|
-
}
|
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
import { Show, createSignal } from 'solid-js'
|
|
2
|
-
import { JsonTree } from '@tanstack/devtools-ui'
|
|
3
|
-
import { useStyles } from '../../styles/use-styles'
|
|
4
|
-
import { formatDuration, formatTimestamp, getChunkTypeColor } from '../utils'
|
|
5
|
-
import type { Component } from 'solid-js'
|
|
6
|
-
import type { Chunk } from '../../store/ai-store'
|
|
7
|
-
|
|
8
|
-
interface ChunkItemProps {
|
|
9
|
-
chunk: Chunk
|
|
10
|
-
index: number
|
|
11
|
-
variant?: 'small' | 'large'
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const ChunkItem: Component<ChunkItemProps> = (props) => {
|
|
15
|
-
const styles = useStyles()
|
|
16
|
-
const [showRaw, setShowRaw] = createSignal(false)
|
|
17
|
-
const isLarge = () => props.variant === 'large'
|
|
18
|
-
const chunkCount = () => props.chunk.chunkCount || 1
|
|
19
|
-
|
|
20
|
-
const parseToolArguments = (): Record<string, unknown> => {
|
|
21
|
-
try {
|
|
22
|
-
return JSON.parse(props.chunk.arguments || '{}') as Record<
|
|
23
|
-
string,
|
|
24
|
-
unknown
|
|
25
|
-
>
|
|
26
|
-
} catch {
|
|
27
|
-
return { raw: props.chunk.arguments }
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const parseToolResult = (): Record<string, unknown> => {
|
|
32
|
-
try {
|
|
33
|
-
if (typeof props.chunk.result === 'string') {
|
|
34
|
-
return JSON.parse(props.chunk.result) as Record<string, unknown>
|
|
35
|
-
}
|
|
36
|
-
return props.chunk.result as Record<string, unknown>
|
|
37
|
-
} catch {
|
|
38
|
-
return { raw: props.chunk.result }
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<div
|
|
44
|
-
class={
|
|
45
|
-
isLarge()
|
|
46
|
-
? styles().conversationDetails.chunkItemLarge
|
|
47
|
-
: styles().conversationDetails.chunkItem
|
|
48
|
-
}
|
|
49
|
-
>
|
|
50
|
-
{/* Chunk Header */}
|
|
51
|
-
<div
|
|
52
|
-
class={
|
|
53
|
-
isLarge()
|
|
54
|
-
? styles().conversationDetails.chunkHeaderLarge
|
|
55
|
-
: styles().conversationDetails.chunkHeader
|
|
56
|
-
}
|
|
57
|
-
>
|
|
58
|
-
{/* Chunk Number */}
|
|
59
|
-
<div
|
|
60
|
-
class={
|
|
61
|
-
isLarge()
|
|
62
|
-
? styles().conversationDetails.chunkNumberLarge
|
|
63
|
-
: styles().conversationDetails.chunkNumber
|
|
64
|
-
}
|
|
65
|
-
>
|
|
66
|
-
#{props.index + 1}
|
|
67
|
-
<Show when={chunkCount() > 1}>
|
|
68
|
-
<span style={{ 'margin-left': '4px', opacity: 0.7 }}>
|
|
69
|
-
({chunkCount()} chunks)
|
|
70
|
-
</span>
|
|
71
|
-
</Show>
|
|
72
|
-
</div>
|
|
73
|
-
|
|
74
|
-
{/* Type Badge */}
|
|
75
|
-
<div
|
|
76
|
-
class={
|
|
77
|
-
isLarge()
|
|
78
|
-
? styles().conversationDetails.chunkTypeBadgeLarge
|
|
79
|
-
: styles().conversationDetails.chunkTypeBadge
|
|
80
|
-
}
|
|
81
|
-
>
|
|
82
|
-
<div
|
|
83
|
-
class={
|
|
84
|
-
isLarge()
|
|
85
|
-
? styles().conversationDetails.chunkTypeDotLarge
|
|
86
|
-
: styles().conversationDetails.chunkTypeDot
|
|
87
|
-
}
|
|
88
|
-
style={{ background: getChunkTypeColor(props.chunk.type) }}
|
|
89
|
-
/>
|
|
90
|
-
<div
|
|
91
|
-
class={
|
|
92
|
-
isLarge()
|
|
93
|
-
? styles().conversationDetails.chunkTypeLabelLarge
|
|
94
|
-
: styles().conversationDetails.chunkTypeLabel
|
|
95
|
-
}
|
|
96
|
-
>
|
|
97
|
-
{props.chunk.type}
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
|
|
101
|
-
{/* Tool Name Badge */}
|
|
102
|
-
<Show when={props.chunk.toolName}>
|
|
103
|
-
<div
|
|
104
|
-
class={
|
|
105
|
-
isLarge()
|
|
106
|
-
? styles().conversationDetails.chunkToolBadgeLarge
|
|
107
|
-
: styles().conversationDetails.chunkToolBadge
|
|
108
|
-
}
|
|
109
|
-
>
|
|
110
|
-
🔧 {props.chunk.toolName}
|
|
111
|
-
</div>
|
|
112
|
-
</Show>
|
|
113
|
-
|
|
114
|
-
{/* Timestamp */}
|
|
115
|
-
<div
|
|
116
|
-
class={
|
|
117
|
-
isLarge()
|
|
118
|
-
? styles().conversationDetails.chunkTimestampLarge
|
|
119
|
-
: styles().conversationDetails.chunkTimestamp
|
|
120
|
-
}
|
|
121
|
-
>
|
|
122
|
-
{formatTimestamp(props.chunk.timestamp)}
|
|
123
|
-
</div>
|
|
124
|
-
|
|
125
|
-
{/* Toggle Raw JSON Button */}
|
|
126
|
-
<button
|
|
127
|
-
onClick={() => setShowRaw(!showRaw())}
|
|
128
|
-
class={`${
|
|
129
|
-
isLarge()
|
|
130
|
-
? styles().conversationDetails.rawJsonButtonLarge
|
|
131
|
-
: styles().conversationDetails.rawJsonButton
|
|
132
|
-
} ${
|
|
133
|
-
showRaw()
|
|
134
|
-
? styles().conversationDetails.rawJsonButtonActive
|
|
135
|
-
: styles().conversationDetails.rawJsonButtonInactive
|
|
136
|
-
}`}
|
|
137
|
-
title={showRaw() ? 'Show formatted' : 'Show raw JSON'}
|
|
138
|
-
>
|
|
139
|
-
{'{}'}
|
|
140
|
-
</button>
|
|
141
|
-
</div>
|
|
142
|
-
|
|
143
|
-
{/* Chunk Content */}
|
|
144
|
-
<Show when={!showRaw()}>
|
|
145
|
-
<Show when={props.chunk.content || props.chunk.delta}>
|
|
146
|
-
<div
|
|
147
|
-
class={
|
|
148
|
-
isLarge()
|
|
149
|
-
? styles().conversationDetails.chunkContentLarge
|
|
150
|
-
: styles().conversationDetails.chunkContent
|
|
151
|
-
}
|
|
152
|
-
>
|
|
153
|
-
{props.chunk.content}
|
|
154
|
-
</div>
|
|
155
|
-
</Show>
|
|
156
|
-
<Show when={props.chunk.error}>
|
|
157
|
-
<div
|
|
158
|
-
class={
|
|
159
|
-
isLarge()
|
|
160
|
-
? styles().conversationDetails.chunkErrorLarge
|
|
161
|
-
: styles().conversationDetails.chunkError
|
|
162
|
-
}
|
|
163
|
-
>
|
|
164
|
-
❌ {props.chunk.error}
|
|
165
|
-
</div>
|
|
166
|
-
</Show>
|
|
167
|
-
<Show when={props.chunk.finishReason}>
|
|
168
|
-
<div
|
|
169
|
-
class={
|
|
170
|
-
isLarge()
|
|
171
|
-
? styles().conversationDetails.chunkFinishLarge
|
|
172
|
-
: styles().conversationDetails.chunkFinish
|
|
173
|
-
}
|
|
174
|
-
>
|
|
175
|
-
✓{' '}
|
|
176
|
-
{isLarge()
|
|
177
|
-
? `Finish: ${props.chunk.finishReason}`
|
|
178
|
-
: props.chunk.finishReason}
|
|
179
|
-
</div>
|
|
180
|
-
</Show>
|
|
181
|
-
<Show when={props.chunk.type === 'approval'}>
|
|
182
|
-
<div
|
|
183
|
-
class={
|
|
184
|
-
isLarge()
|
|
185
|
-
? styles().conversationDetails.chunkApprovalLarge
|
|
186
|
-
: styles().conversationDetails.chunkApproval
|
|
187
|
-
}
|
|
188
|
-
>
|
|
189
|
-
<div
|
|
190
|
-
class={
|
|
191
|
-
isLarge()
|
|
192
|
-
? styles().conversationDetails.chunkApprovalTitleLarge
|
|
193
|
-
: styles().conversationDetails.chunkApprovalTitle
|
|
194
|
-
}
|
|
195
|
-
>
|
|
196
|
-
⚠️ Approval Required
|
|
197
|
-
</div>
|
|
198
|
-
<Show when={props.chunk.input}>
|
|
199
|
-
<div
|
|
200
|
-
class={
|
|
201
|
-
isLarge()
|
|
202
|
-
? styles().conversationDetails.chunkApprovalInputLarge
|
|
203
|
-
: styles().conversationDetails.chunkApprovalInput
|
|
204
|
-
}
|
|
205
|
-
>
|
|
206
|
-
Input: {JSON.stringify(props.chunk.input, null, 2)}
|
|
207
|
-
</div>
|
|
208
|
-
</Show>
|
|
209
|
-
</div>
|
|
210
|
-
</Show>
|
|
211
|
-
<Show when={props.chunk.type === 'tool_call' && props.chunk.arguments}>
|
|
212
|
-
<div class={styles().conversationDetails.chunkToolCall}>
|
|
213
|
-
<div class={styles().conversationDetails.chunkToolCallHeader}>
|
|
214
|
-
<span class={styles().conversationDetails.chunkToolCallTitle}>
|
|
215
|
-
📤 Tool Arguments
|
|
216
|
-
</span>
|
|
217
|
-
</div>
|
|
218
|
-
<div class={styles().conversationDetails.toolJsonContainer}>
|
|
219
|
-
<JsonTree
|
|
220
|
-
value={parseToolArguments()}
|
|
221
|
-
defaultExpansionDepth={2}
|
|
222
|
-
copyable
|
|
223
|
-
/>
|
|
224
|
-
</div>
|
|
225
|
-
</div>
|
|
226
|
-
</Show>
|
|
227
|
-
<Show
|
|
228
|
-
when={
|
|
229
|
-
props.chunk.type === 'tool_result' &&
|
|
230
|
-
props.chunk.result !== undefined
|
|
231
|
-
}
|
|
232
|
-
>
|
|
233
|
-
<div class={styles().conversationDetails.chunkToolResult}>
|
|
234
|
-
<div class={styles().conversationDetails.chunkToolResultHeader}>
|
|
235
|
-
<span class={styles().conversationDetails.chunkToolResultTitle}>
|
|
236
|
-
✓ Tool Result
|
|
237
|
-
</span>
|
|
238
|
-
<Show
|
|
239
|
-
when={
|
|
240
|
-
props.chunk.duration !== undefined && props.chunk.duration > 0
|
|
241
|
-
}
|
|
242
|
-
>
|
|
243
|
-
<span class={styles().conversationDetails.durationBadge}>
|
|
244
|
-
⏱️ {formatDuration(props.chunk.duration)}
|
|
245
|
-
</span>
|
|
246
|
-
</Show>
|
|
247
|
-
</div>
|
|
248
|
-
<div class={styles().conversationDetails.toolJsonContainer}>
|
|
249
|
-
<JsonTree
|
|
250
|
-
value={parseToolResult()}
|
|
251
|
-
defaultExpansionDepth={2}
|
|
252
|
-
copyable
|
|
253
|
-
/>
|
|
254
|
-
</div>
|
|
255
|
-
</div>
|
|
256
|
-
</Show>
|
|
257
|
-
</Show>
|
|
258
|
-
|
|
259
|
-
{/* Raw JSON View */}
|
|
260
|
-
<Show when={showRaw()}>
|
|
261
|
-
<div
|
|
262
|
-
class={
|
|
263
|
-
isLarge()
|
|
264
|
-
? styles().conversationDetails.rawJsonLarge
|
|
265
|
-
: styles().conversationDetails.rawJson
|
|
266
|
-
}
|
|
267
|
-
>
|
|
268
|
-
{JSON.stringify(props.chunk, null, 2)}
|
|
269
|
-
</div>
|
|
270
|
-
</Show>
|
|
271
|
-
</div>
|
|
272
|
-
)
|
|
273
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { For, Show } from 'solid-js'
|
|
2
|
-
import { useStyles } from '../../styles/use-styles'
|
|
3
|
-
import { ChunkItem } from './ChunkItem'
|
|
4
|
-
import { ChunkBadges } from './ChunkBadges'
|
|
5
|
-
import type { Component } from 'solid-js'
|
|
6
|
-
import type { Chunk } from '../../store/ai-store'
|
|
7
|
-
|
|
8
|
-
interface ChunksCollapsibleProps {
|
|
9
|
-
chunks: Array<Chunk>
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const ChunksCollapsible: Component<ChunksCollapsibleProps> = (props) => {
|
|
13
|
-
const styles = useStyles()
|
|
14
|
-
|
|
15
|
-
const accumulatedContent = () =>
|
|
16
|
-
props.chunks
|
|
17
|
-
.filter((c) => c.type === 'content' && (c.content || c.delta))
|
|
18
|
-
.map((c) => c.delta || c.content)
|
|
19
|
-
.join('')
|
|
20
|
-
|
|
21
|
-
// Total raw chunks = sum of all chunkCounts
|
|
22
|
-
const totalRawChunks = () =>
|
|
23
|
-
props.chunks.reduce((sum, c) => sum + (c.chunkCount || 1), 0)
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<details class={styles().conversationDetails.chunksDetails}>
|
|
27
|
-
<summary class={styles().conversationDetails.chunksSummary}>
|
|
28
|
-
<div class={styles().conversationDetails.chunksSummaryRow}>
|
|
29
|
-
<span class={styles().conversationDetails.chunksSummaryArrow}>▶</span>
|
|
30
|
-
<span class={styles().conversationDetails.chunksSummaryTitle}>
|
|
31
|
-
📦 {totalRawChunks()} chunks
|
|
32
|
-
</span>
|
|
33
|
-
<ChunkBadges chunks={props.chunks} />
|
|
34
|
-
</div>
|
|
35
|
-
{/* Accumulated Content Preview */}
|
|
36
|
-
<Show when={accumulatedContent()}>
|
|
37
|
-
<div
|
|
38
|
-
class={styles().conversationDetails.contentPreview}
|
|
39
|
-
title={accumulatedContent()}
|
|
40
|
-
>
|
|
41
|
-
{accumulatedContent()}
|
|
42
|
-
</div>
|
|
43
|
-
</Show>
|
|
44
|
-
</summary>
|
|
45
|
-
<div class={styles().conversationDetails.chunksContainer}>
|
|
46
|
-
<div class={styles().conversationDetails.chunksList}>
|
|
47
|
-
<For each={props.chunks}>
|
|
48
|
-
{(chunk, index) => (
|
|
49
|
-
<ChunkItem chunk={chunk} index={index()} variant="small" />
|
|
50
|
-
)}
|
|
51
|
-
</For>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
</details>
|
|
55
|
-
)
|
|
56
|
-
}
|