@tanstack/ai-devtools-core 0.3.38 → 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.
Files changed (52) hide show
  1. package/dist/Shell/DKCFIT6H.js +12142 -0
  2. package/dist/Shell/SLOX2TPU.js +12119 -0
  3. package/dist/Shell/Z6HLJOVZ.js +10170 -0
  4. package/dist/dev.js +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/production/Shell/DKCFIT6H.js +12142 -0
  7. package/dist/production/Shell/SLOX2TPU.js +12119 -0
  8. package/dist/production/Shell/Z6HLJOVZ.js +10170 -0
  9. package/dist/production/dev.js +1 -1
  10. package/dist/production/index.js +1 -1
  11. package/dist/production/server.js +1 -1
  12. package/dist/server.js +1 -1
  13. package/package.json +4 -4
  14. package/src/components/Shell.tsx +74 -30
  15. package/src/components/conversation/IterationCard.tsx +261 -12
  16. package/src/components/conversation/IterationTimeline.tsx +66 -16
  17. package/src/components/conversation/index.ts +0 -9
  18. package/src/components/hooks/FixtureNamePopover.tsx +69 -0
  19. package/src/components/hooks/GenerationPanel.tsx +847 -0
  20. package/src/components/hooks/HookDashboard.tsx +292 -0
  21. package/src/components/hooks/HookDetails.tsx +1757 -0
  22. package/src/components/hooks/ToolFixtureForm.tsx +347 -0
  23. package/src/components/hooks/hook-dashboard-model.ts +134 -0
  24. package/src/components/hooks/index.ts +4 -0
  25. package/src/components/hooks/preview-messages.ts +117 -0
  26. package/src/components/hooks/preview-model.ts +195 -0
  27. package/src/components/utils/format.ts +0 -58
  28. package/src/store/ai-context.tsx +1165 -159
  29. package/src/store/ai-store.ts +0 -1
  30. package/src/store/hook-registry.ts +815 -0
  31. package/src/store/message-event-utils.ts +60 -0
  32. package/src/styles/use-styles.ts +1558 -99
  33. package/dist/Shell/MRWZEHAJ.js +0 -6225
  34. package/dist/Shell/RRRNZJ5E.js +0 -7560
  35. package/dist/production/Shell/MRWZEHAJ.js +0 -6225
  36. package/dist/production/Shell/RRRNZJ5E.js +0 -7560
  37. package/src/components/ConversationDetails.tsx +0 -216
  38. package/src/components/ConversationsList.tsx +0 -21
  39. package/src/components/conversation/ActivityEventsTab.tsx +0 -57
  40. package/src/components/conversation/ChunkBadges.tsx +0 -51
  41. package/src/components/conversation/ChunkItem.tsx +0 -273
  42. package/src/components/conversation/ChunksCollapsible.tsx +0 -56
  43. package/src/components/conversation/ChunksTab.tsx +0 -87
  44. package/src/components/conversation/ConversationHeader.tsx +0 -106
  45. package/src/components/conversation/ConversationTabs.tsx +0 -205
  46. package/src/components/conversation/MessageCard.tsx +0 -174
  47. package/src/components/conversation/MessageGroup.tsx +0 -101
  48. package/src/components/conversation/MessagesTab.tsx +0 -30
  49. package/src/components/conversation/SummariesTab.tsx +0 -91
  50. package/src/components/conversation/ToolCallDisplay.tsx +0 -99
  51. package/src/components/list/ConversationRow.tsx +0 -85
  52. 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
- }