@tangle-network/ui 1.0.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 (220) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +33 -0
  4. package/dist/active-sessions-store-CeOmXgv5.d.ts +85 -0
  5. package/dist/artifact-pane-DvJyPWV4.d.ts +24 -0
  6. package/dist/auth.d.ts +74 -0
  7. package/dist/auth.js +15 -0
  8. package/dist/button-CMQuQEW_.d.ts +17 -0
  9. package/dist/chat.d.ts +232 -0
  10. package/dist/chat.js +30 -0
  11. package/dist/chunk-2NFQRQOD.js +1009 -0
  12. package/dist/chunk-2VH6PUXD.js +186 -0
  13. package/dist/chunk-34A66VBG.js +214 -0
  14. package/dist/chunk-3OI2QKFD.js +0 -0
  15. package/dist/chunk-4CLN43XT.js +45 -0
  16. package/dist/chunk-54SQQMMM.js +156 -0
  17. package/dist/chunk-5Z5ZYMOJ.js +0 -0
  18. package/dist/chunk-66BNMOVT.js +167 -0
  19. package/dist/chunk-6BGQA4BQ.js +0 -0
  20. package/dist/chunk-7UO2ZMRQ.js +133 -0
  21. package/dist/chunk-BX6AQMUS.js +183 -0
  22. package/dist/chunk-CD53GZOM.js +59 -0
  23. package/dist/chunk-CSAIKY36.js +54 -0
  24. package/dist/chunk-EEE55AVS.js +1201 -0
  25. package/dist/chunk-GYPQXTJU.js +230 -0
  26. package/dist/chunk-HFL6R6IF.js +37 -0
  27. package/dist/chunk-HJKCSXCH.js +737 -0
  28. package/dist/chunk-LISXUB4D.js +1222 -0
  29. package/dist/chunk-LQS34IGP.js +0 -0
  30. package/dist/chunk-MKTSMWVD.js +109 -0
  31. package/dist/chunk-NKDZ7GZE.js +192 -0
  32. package/dist/chunk-OEX7NZE3.js +321 -0
  33. package/dist/chunk-Q56BYXQF.js +61 -0
  34. package/dist/chunk-Q7EIIWTC.js +0 -0
  35. package/dist/chunk-REJESC5U.js +117 -0
  36. package/dist/chunk-RQGKSCEZ.js +0 -0
  37. package/dist/chunk-RQHJBTEU.js +10 -0
  38. package/dist/chunk-TMFOPHHN.js +299 -0
  39. package/dist/chunk-XGKULLYE.js +40 -0
  40. package/dist/chunk-XIHMJ7ZQ.js +614 -0
  41. package/dist/chunk-YJ2G3XO5.js +1048 -0
  42. package/dist/chunk-YNN4O57I.js +754 -0
  43. package/dist/code-block-DjXf8eOG.d.ts +19 -0
  44. package/dist/document-editor-pane-A5LT5H4N.js +12 -0
  45. package/dist/document-editor-pane-DyDEX_Zm.d.ts +124 -0
  46. package/dist/editor.d.ts +120 -0
  47. package/dist/editor.js +34 -0
  48. package/dist/files.d.ts +175 -0
  49. package/dist/files.js +20 -0
  50. package/dist/hooks.d.ts +56 -0
  51. package/dist/hooks.js +41 -0
  52. package/dist/index.d.ts +43 -0
  53. package/dist/index.js +446 -0
  54. package/dist/markdown.d.ts +15 -0
  55. package/dist/markdown.js +14 -0
  56. package/dist/message-BHWbxBtT.d.ts +15 -0
  57. package/dist/openui.d.ts +115 -0
  58. package/dist/openui.js +12 -0
  59. package/dist/parts-dj7AcUg0.d.ts +36 -0
  60. package/dist/primitives.d.ts +332 -0
  61. package/dist/primitives.js +191 -0
  62. package/dist/run-PfLmDAox.d.ts +41 -0
  63. package/dist/run.d.ts +69 -0
  64. package/dist/run.js +36 -0
  65. package/dist/sdk-hooks.d.ts +285 -0
  66. package/dist/sdk-hooks.js +31 -0
  67. package/dist/stores.d.ts +17 -0
  68. package/dist/stores.js +76 -0
  69. package/dist/tool-call-feed-Bs3MyQMT.d.ts +68 -0
  70. package/dist/tool-display-z4JcDmMQ.d.ts +32 -0
  71. package/dist/tool-previews.d.ts +48 -0
  72. package/dist/tool-previews.js +21 -0
  73. package/dist/types.d.ts +19 -0
  74. package/dist/types.js +1 -0
  75. package/dist/utils.d.ts +45 -0
  76. package/dist/utils.js +32 -0
  77. package/package.json +193 -0
  78. package/src/auth/auth.tsx +228 -0
  79. package/src/auth/index.ts +13 -0
  80. package/src/auth/login-layout.tsx +46 -0
  81. package/src/chat/agent-timeline.stories.tsx +429 -0
  82. package/src/chat/agent-timeline.tsx +360 -0
  83. package/src/chat/chat-container.tsx +486 -0
  84. package/src/chat/chat-input.stories.tsx +142 -0
  85. package/src/chat/chat-input.tsx +389 -0
  86. package/src/chat/chat-message.stories.tsx +237 -0
  87. package/src/chat/chat-message.tsx +129 -0
  88. package/src/chat/index.ts +18 -0
  89. package/src/chat/message-list.stories.tsx +336 -0
  90. package/src/chat/message-list.tsx +79 -0
  91. package/src/chat/thinking-indicator.stories.tsx +56 -0
  92. package/src/chat/thinking-indicator.tsx +30 -0
  93. package/src/chat/user-message.stories.tsx +92 -0
  94. package/src/chat/user-message.tsx +43 -0
  95. package/src/editor/document-editor-pane.tsx +351 -0
  96. package/src/editor/editor-provider.tsx +428 -0
  97. package/src/editor/editor-toolbar.tsx +130 -0
  98. package/src/editor/index.ts +31 -0
  99. package/src/editor/markdown-conversion.ts +21 -0
  100. package/src/editor/markdown-document-editor.tsx +137 -0
  101. package/src/editor/tiptap-editor.tsx +331 -0
  102. package/src/editor/use-editor.ts +221 -0
  103. package/src/files/file-artifact-pane.tsx +183 -0
  104. package/src/files/file-preview.tsx +342 -0
  105. package/src/files/file-tabs.tsx +71 -0
  106. package/src/files/file-tree.tsx +258 -0
  107. package/src/files/index.ts +17 -0
  108. package/src/files/rich-file-tree.stories.tsx +104 -0
  109. package/src/files/rich-file-tree.test.tsx +42 -0
  110. package/src/files/rich-file-tree.tsx +232 -0
  111. package/src/hooks/index.ts +10 -0
  112. package/src/hooks/use-auth.ts +153 -0
  113. package/src/hooks/use-auto-scroll.ts +59 -0
  114. package/src/hooks/use-dropdown-menu.ts +40 -0
  115. package/src/hooks/use-live-time.test.tsx +40 -0
  116. package/src/hooks/use-live-time.ts +27 -0
  117. package/src/hooks/use-realtime-session.ts +319 -0
  118. package/src/hooks/use-run-collapse-state.ts +25 -0
  119. package/src/hooks/use-run-groups.ts +111 -0
  120. package/src/hooks/use-sdk-session.ts +575 -0
  121. package/src/hooks/use-sse-stream.ts +475 -0
  122. package/src/hooks/use-tool-call-stream.ts +96 -0
  123. package/src/index.ts +14 -0
  124. package/src/lib/utils.ts +6 -0
  125. package/src/markdown/code-block.tsx +198 -0
  126. package/src/markdown/index.ts +2 -0
  127. package/src/markdown/markdown.stories.tsx +190 -0
  128. package/src/markdown/markdown.tsx +62 -0
  129. package/src/openui/index.ts +20 -0
  130. package/src/openui/openui-artifact-renderer.tsx +542 -0
  131. package/src/primitives/artifact-pane.tsx +91 -0
  132. package/src/primitives/avatar.stories.tsx +95 -0
  133. package/src/primitives/avatar.tsx +47 -0
  134. package/src/primitives/badge.stories.tsx +57 -0
  135. package/src/primitives/badge.tsx +97 -0
  136. package/src/primitives/button.stories.tsx +48 -0
  137. package/src/primitives/button.tsx +115 -0
  138. package/src/primitives/card.stories.tsx +53 -0
  139. package/src/primitives/card.tsx +98 -0
  140. package/src/primitives/code-block.stories.tsx +115 -0
  141. package/src/primitives/code-block.tsx +22 -0
  142. package/src/primitives/design-tokens.stories.tsx +162 -0
  143. package/src/primitives/dialog.stories.tsx +176 -0
  144. package/src/primitives/dialog.tsx +137 -0
  145. package/src/primitives/drop-zone.stories.tsx +123 -0
  146. package/src/primitives/drop-zone.tsx +131 -0
  147. package/src/primitives/dropdown-menu.stories.tsx +122 -0
  148. package/src/primitives/dropdown-menu.tsx +214 -0
  149. package/src/primitives/empty-state.stories.tsx +81 -0
  150. package/src/primitives/empty-state.tsx +40 -0
  151. package/src/primitives/index.ts +118 -0
  152. package/src/primitives/input.stories.tsx +113 -0
  153. package/src/primitives/input.tsx +136 -0
  154. package/src/primitives/label.stories.tsx +84 -0
  155. package/src/primitives/label.tsx +24 -0
  156. package/src/primitives/progress.stories.tsx +93 -0
  157. package/src/primitives/progress.tsx +50 -0
  158. package/src/primitives/segmented-control.test.tsx +328 -0
  159. package/src/primitives/segmented-control.tsx +154 -0
  160. package/src/primitives/select.stories.tsx +164 -0
  161. package/src/primitives/select.tsx +158 -0
  162. package/src/primitives/sidebar-drop-zone.stories.tsx +100 -0
  163. package/src/primitives/sidebar-drop-zone.tsx +149 -0
  164. package/src/primitives/skeleton.stories.tsx +79 -0
  165. package/src/primitives/skeleton.tsx +55 -0
  166. package/src/primitives/stat-card.stories.tsx +137 -0
  167. package/src/primitives/stat-card.tsx +97 -0
  168. package/src/primitives/switch.stories.tsx +85 -0
  169. package/src/primitives/switch.tsx +28 -0
  170. package/src/primitives/table.stories.tsx +170 -0
  171. package/src/primitives/table.tsx +116 -0
  172. package/src/primitives/tabs.stories.tsx +180 -0
  173. package/src/primitives/tabs.tsx +71 -0
  174. package/src/primitives/terminal-display.stories.tsx +191 -0
  175. package/src/primitives/terminal-display.tsx +189 -0
  176. package/src/primitives/theme-toggle.stories.tsx +32 -0
  177. package/src/primitives/theme-toggle.tsx +96 -0
  178. package/src/primitives/toast.stories.tsx +155 -0
  179. package/src/primitives/toast.tsx +190 -0
  180. package/src/primitives/upload-progress.stories.tsx +120 -0
  181. package/src/primitives/upload-progress.tsx +110 -0
  182. package/src/run/expanded-tool-detail.stories.tsx +182 -0
  183. package/src/run/expanded-tool-detail.tsx +186 -0
  184. package/src/run/index.ts +13 -0
  185. package/src/run/inline-thinking-item.stories.tsx +136 -0
  186. package/src/run/inline-thinking-item.tsx +120 -0
  187. package/src/run/inline-tool-item.stories.tsx +222 -0
  188. package/src/run/inline-tool-item.tsx +190 -0
  189. package/src/run/run-group.stories.tsx +322 -0
  190. package/src/run/run-group.tsx +569 -0
  191. package/src/run/run-item-primitives.tsx +17 -0
  192. package/src/run/tool-call-feed.stories.tsx +294 -0
  193. package/src/run/tool-call-feed.tsx +192 -0
  194. package/src/run/tool-call-step.stories.tsx +198 -0
  195. package/src/run/tool-call-step.tsx +240 -0
  196. package/src/sdk-hooks.ts +38 -0
  197. package/src/stores/active-sessions-store.ts +455 -0
  198. package/src/stores/chat-store.ts +43 -0
  199. package/src/stores/index.ts +2 -0
  200. package/src/tool-previews/command-preview.tsx +116 -0
  201. package/src/tool-previews/diff-preview.tsx +85 -0
  202. package/src/tool-previews/glob-results-preview.tsx +98 -0
  203. package/src/tool-previews/grep-results-preview.tsx +157 -0
  204. package/src/tool-previews/index.ts +22 -0
  205. package/src/tool-previews/preview-primitives.tsx +84 -0
  206. package/src/tool-previews/question-preview.tsx +101 -0
  207. package/src/tool-previews/web-search-preview.tsx +117 -0
  208. package/src/tool-previews/write-file-preview.tsx +80 -0
  209. package/src/types/branding.ts +11 -0
  210. package/src/types/index.ts +5 -0
  211. package/src/types/message.ts +13 -0
  212. package/src/types/parts.ts +51 -0
  213. package/src/types/run.ts +56 -0
  214. package/src/types/tool-display.ts +41 -0
  215. package/src/utils/copy-text.ts +30 -0
  216. package/src/utils/format.test.ts +43 -0
  217. package/src/utils/format.ts +56 -0
  218. package/src/utils/index.ts +10 -0
  219. package/src/utils/time-ago.ts +9 -0
  220. package/src/utils/tool-display.ts +238 -0
@@ -0,0 +1,429 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { useState } from 'react'
3
+ import { Code, FileText } from 'lucide-react'
4
+ import { AgentTimeline } from './agent-timeline'
5
+ import type { AgentTimelineItem } from './agent-timeline'
6
+
7
+ const meta: Meta<typeof AgentTimeline> = {
8
+ title: 'Chat/AgentTimeline',
9
+ component: AgentTimeline,
10
+ parameters: {
11
+ layout: 'fullscreen',
12
+ backgrounds: { default: 'dark' },
13
+ },
14
+ decorators: [
15
+ (Story) => (
16
+ <div className="min-h-screen bg-[var(--bg-root)]">
17
+ <Story />
18
+ </div>
19
+ ),
20
+ ],
21
+ }
22
+
23
+ export default meta
24
+ type Story = StoryObj<typeof AgentTimeline>
25
+
26
+ const NOW = Date.now()
27
+ const t = (offsetSeconds: number) => new Date(NOW - offsetSeconds * 1000)
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Full coding session — agent answers a TypeScript question with tool use
31
+ // ---------------------------------------------------------------------------
32
+
33
+ const codingSession: AgentTimelineItem[] = [
34
+ {
35
+ id: 'user-1',
36
+ kind: 'message',
37
+ role: 'user',
38
+ content:
39
+ 'I need a generic `Result<T, E>` type with helper functions for TypeScript. No external deps.',
40
+ timestamp: t(180),
41
+ },
42
+ {
43
+ id: 'status-start',
44
+ kind: 'status',
45
+ label: 'Planning implementation',
46
+ detail: 'Analyzing requirements — generic Result type with Ok/Err constructors and combinators.',
47
+ tone: 'info',
48
+ },
49
+ {
50
+ id: 'tool-glob',
51
+ kind: 'tool',
52
+ call: {
53
+ id: 'tc-1',
54
+ type: 'glob',
55
+ label: 'Find **/*.ts in src/',
56
+ status: 'success',
57
+ output:
58
+ 'src/index.ts\nsrc/types.ts\nsrc/utils/format.ts\nsrc/utils/parse.ts',
59
+ duration: 18,
60
+ },
61
+ },
62
+ {
63
+ id: 'tool-read',
64
+ kind: 'tool',
65
+ call: {
66
+ id: 'tc-2',
67
+ type: 'read',
68
+ label: 'Read src/types.ts',
69
+ status: 'success',
70
+ output:
71
+ '// Existing types\nexport type Maybe<T> = T | null | undefined\nexport type Nullable<T> = T | null',
72
+ duration: 31,
73
+ },
74
+ },
75
+ {
76
+ id: 'asst-1',
77
+ kind: 'message',
78
+ role: 'assistant',
79
+ content:
80
+ "Your existing `types.ts` has `Maybe` and `Nullable` — I'll add `Result<T, E>` alongside them and write it to the same file.",
81
+ timestamp: t(150),
82
+ },
83
+ {
84
+ id: 'tool-write',
85
+ kind: 'tool',
86
+ call: {
87
+ id: 'tc-3',
88
+ type: 'write',
89
+ label: 'Write src/types.ts',
90
+ status: 'success',
91
+ output: 'File written — 47 lines added.',
92
+ duration: 22,
93
+ },
94
+ },
95
+ {
96
+ id: 'tool-bash-test',
97
+ kind: 'tool',
98
+ call: {
99
+ id: 'tc-4',
100
+ type: 'bash',
101
+ label: 'npx tsc --noEmit',
102
+ status: 'success',
103
+ detail: 'npx tsc --noEmit --strict',
104
+ output: '✓ No type errors',
105
+ duration: 1840,
106
+ },
107
+ },
108
+ {
109
+ id: 'status-done',
110
+ kind: 'status',
111
+ label: 'Type-checks clean',
112
+ tone: 'success',
113
+ },
114
+ {
115
+ id: 'asst-final',
116
+ kind: 'message',
117
+ role: 'assistant',
118
+ content: `Here's the \`Result<T, E>\` implementation added to \`src/types.ts\`:
119
+
120
+ \`\`\`typescript
121
+ export type Ok<T> = { readonly ok: true; readonly value: T }
122
+ export type Err<E> = { readonly ok: false; readonly error: E }
123
+ export type Result<T, E = Error> = Ok<T> | Err<E>
124
+
125
+ export const ok = <T>(value: T): Ok<T> => ({ ok: true, value })
126
+ export const err = <E>(error: E): Err<E> => ({ ok: false, error })
127
+
128
+ export function map<T, U, E>(
129
+ result: Result<T, E>,
130
+ fn: (value: T) => U,
131
+ ): Result<U, E> {
132
+ return result.ok ? ok(fn(result.value)) : result
133
+ }
134
+
135
+ export function flatMap<T, U, E>(
136
+ result: Result<T, E>,
137
+ fn: (value: T) => Result<U, E>,
138
+ ): Result<U, E> {
139
+ return result.ok ? fn(result.value) : result
140
+ }
141
+
142
+ export function unwrapOr<T, E>(result: Result<T, E>, fallback: T): T {
143
+ return result.ok ? result.value : fallback
144
+ }
145
+ \`\`\`
146
+
147
+ All helpers are zero-dependency and fully typed. \`tsc --strict\` passes clean.`,
148
+ timestamp: t(120),
149
+ },
150
+ {
151
+ id: 'artifact-types',
152
+ kind: 'artifact',
153
+ title: 'src/types.ts',
154
+ description: 'Result<T, E> type + ok / err / map / flatMap / unwrapOr helpers',
155
+ tone: 'success',
156
+ icon: <FileText className="h-4 w-4" />,
157
+ meta: <>47 lines added &middot; TypeScript &middot; Zero deps</>,
158
+ },
159
+ ]
160
+
161
+ export const CodingSession: Story = {
162
+ args: {
163
+ items: codingSession,
164
+ },
165
+ }
166
+
167
+ // ---------------------------------------------------------------------------
168
+ // Session with thinking indicator active
169
+ // ---------------------------------------------------------------------------
170
+
171
+ export const WhileThinking: Story = {
172
+ args: {
173
+ items: [
174
+ {
175
+ id: 'user-1',
176
+ kind: 'message',
177
+ role: 'user',
178
+ content: 'Implement a distributed rate limiter using Redis sorted sets.',
179
+ timestamp: new Date(),
180
+ },
181
+ {
182
+ id: 'status-planning',
183
+ kind: 'status',
184
+ label: 'Reading codebase',
185
+ tone: 'info',
186
+ },
187
+ {
188
+ id: 'tool-glob',
189
+ kind: 'tool',
190
+ call: {
191
+ id: 'tc-1',
192
+ type: 'glob',
193
+ label: 'Find **/*.ts in src/',
194
+ status: 'success',
195
+ duration: 14,
196
+ },
197
+ },
198
+ ],
199
+ isThinking: true,
200
+ },
201
+ }
202
+
203
+ // ---------------------------------------------------------------------------
204
+ // Tool group — parallel reads shown together
205
+ // ---------------------------------------------------------------------------
206
+
207
+ export const WithToolGroup: Story = {
208
+ args: {
209
+ items: [
210
+ {
211
+ id: 'user-1',
212
+ kind: 'message',
213
+ role: 'user',
214
+ content:
215
+ 'Audit all the route handlers in src/routes/ and tell me which ones are missing auth middleware.',
216
+ timestamp: t(90),
217
+ },
218
+ {
219
+ id: 'tool-group-reads',
220
+ kind: 'tool_group',
221
+ title: 'Reading route files',
222
+ calls: [
223
+ {
224
+ id: 'tc-1',
225
+ type: 'read',
226
+ label: 'Read src/routes/users.ts',
227
+ status: 'success',
228
+ output: "router.get('/', authMiddleware, listUsers)\nrouter.post('/', authMiddleware, createUser)",
229
+ duration: 24,
230
+ },
231
+ {
232
+ id: 'tc-2',
233
+ type: 'read',
234
+ label: 'Read src/routes/sessions.ts',
235
+ status: 'success',
236
+ output: "router.get('/', listSessions) // missing auth!\nrouter.delete('/:id', authMiddleware, deleteSession)",
237
+ duration: 19,
238
+ },
239
+ {
240
+ id: 'tc-3',
241
+ type: 'read',
242
+ label: 'Read src/routes/health.ts',
243
+ status: 'success',
244
+ output: "router.get('/', healthCheck) // intentionally public",
245
+ duration: 11,
246
+ },
247
+ ],
248
+ },
249
+ {
250
+ id: 'status-warn',
251
+ kind: 'status',
252
+ label: 'Auth gap found in sessions route',
253
+ detail: 'GET /sessions is publicly accessible — no authMiddleware applied.',
254
+ tone: 'warning',
255
+ },
256
+ {
257
+ id: 'asst-1',
258
+ kind: 'message',
259
+ role: 'assistant',
260
+ content: `Found one unprotected route:
261
+
262
+ | Route | File | Auth |
263
+ |-------|------|------|
264
+ | \`GET /users\` | users.ts | ✅ |
265
+ | \`POST /users\` | users.ts | ✅ |
266
+ | \`GET /sessions\` | sessions.ts | ❌ Missing |
267
+ | \`DELETE /sessions/:id\` | sessions.ts | ✅ |
268
+ | \`GET /health\` | health.ts | ✅ (intentional) |
269
+
270
+ **Fix:** add \`authMiddleware\` to \`router.get('/', ...)\` in \`sessions.ts\`.`,
271
+ timestamp: t(60),
272
+ },
273
+ ],
274
+ },
275
+ }
276
+
277
+ // ---------------------------------------------------------------------------
278
+ // Error state
279
+ // ---------------------------------------------------------------------------
280
+
281
+ export const WithError: Story = {
282
+ args: {
283
+ items: [
284
+ {
285
+ id: 'user-1',
286
+ kind: 'message',
287
+ role: 'user',
288
+ content: 'Run the test suite.',
289
+ timestamp: t(30),
290
+ },
291
+ {
292
+ id: 'tool-bash',
293
+ kind: 'tool',
294
+ call: {
295
+ id: 'tc-1',
296
+ type: 'bash',
297
+ label: 'pnpm test',
298
+ status: 'error',
299
+ detail: 'pnpm test --run',
300
+ output:
301
+ 'FAIL src/utils/parse.test.ts\n ✕ parseDate handles invalid input (12ms)\n Expected: { ok: false }\n Received: { ok: true, value: Invalid Date }\n\nTest Suites: 1 failed, 4 passed\nTests: 1 failed, 31 passed',
302
+ duration: 3400,
303
+ },
304
+ },
305
+ {
306
+ id: 'status-error',
307
+ kind: 'status',
308
+ label: '1 test failed — parseDate accepts Invalid Date',
309
+ detail: 'src/utils/parse.test.ts:14 — parseDate should return Err for non-date strings.',
310
+ tone: 'error',
311
+ },
312
+ {
313
+ id: 'asst-1',
314
+ kind: 'message',
315
+ role: 'assistant',
316
+ content: '`parseDate` returns `ok(new Date(str))` without validating `isNaN`. Fix: check `isNaN(result.getTime())` and return `err(...)` instead.',
317
+ timestamp: t(10),
318
+ },
319
+ ],
320
+ },
321
+ }
322
+
323
+ // ---------------------------------------------------------------------------
324
+ // Artifact handoff
325
+ // ---------------------------------------------------------------------------
326
+
327
+ export const WithArtifact: Story = {
328
+ args: {
329
+ items: [
330
+ {
331
+ id: 'user-1',
332
+ kind: 'message',
333
+ role: 'user',
334
+ content: 'Generate an OpenAPI spec for the users API.',
335
+ timestamp: t(60),
336
+ },
337
+ {
338
+ id: 'tool-write',
339
+ kind: 'tool',
340
+ call: {
341
+ id: 'tc-1',
342
+ type: 'write',
343
+ label: 'Write openapi.yaml',
344
+ status: 'success',
345
+ output: '187 lines written.',
346
+ duration: 44,
347
+ },
348
+ },
349
+ {
350
+ id: 'asst-1',
351
+ kind: 'message',
352
+ role: 'assistant',
353
+ content: 'OpenAPI 3.1 spec generated with all CRUD endpoints, request/response schemas, and error codes.',
354
+ timestamp: t(30),
355
+ },
356
+ {
357
+ id: 'artifact-spec',
358
+ kind: 'artifact',
359
+ title: 'openapi.yaml',
360
+ description: 'OpenAPI 3.1 specification — Users API with auth, CRUD, and error schemas',
361
+ tone: 'success',
362
+ icon: <Code className="h-4 w-4" />,
363
+ meta: <>187 lines &middot; OpenAPI 3.1 &middot; YAML</>,
364
+ onClick: () => alert('Open openapi.yaml'),
365
+ },
366
+ ],
367
+ },
368
+ }
369
+
370
+ // ---------------------------------------------------------------------------
371
+ // Empty state
372
+ // ---------------------------------------------------------------------------
373
+
374
+ export const Empty: Story = {
375
+ args: {
376
+ items: [],
377
+ emptyState: (
378
+ <div className="text-center text-sm text-muted-foreground">
379
+ No messages yet — start a conversation.
380
+ </div>
381
+ ),
382
+ },
383
+ }
384
+
385
+ // ---------------------------------------------------------------------------
386
+ // All status tones
387
+ // ---------------------------------------------------------------------------
388
+
389
+ export const StatusTones: Story = {
390
+ args: {
391
+ items: [
392
+ {
393
+ id: 's-default',
394
+ kind: 'status',
395
+ label: 'Default status',
396
+ detail: 'Neutral informational message.',
397
+ tone: 'default',
398
+ },
399
+ {
400
+ id: 's-info',
401
+ kind: 'status',
402
+ label: 'Info status',
403
+ detail: 'Agent is reading files.',
404
+ tone: 'info',
405
+ },
406
+ {
407
+ id: 's-success',
408
+ kind: 'status',
409
+ label: 'Success status',
410
+ detail: 'All tests passed.',
411
+ tone: 'success',
412
+ },
413
+ {
414
+ id: 's-warning',
415
+ kind: 'status',
416
+ label: 'Warning status',
417
+ detail: 'Unprotected route detected.',
418
+ tone: 'warning',
419
+ },
420
+ {
421
+ id: 's-error',
422
+ kind: 'status',
423
+ label: 'Error status',
424
+ detail: '3 tests failed.',
425
+ tone: 'error',
426
+ },
427
+ ],
428
+ },
429
+ }