@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,322 @@
1
+ import { useState } from 'react'
2
+ import type { Meta, StoryObj } from '@storybook/react'
3
+ import { RunGroup } from './run-group'
4
+ import type { Run } from '../types/run'
5
+ import type { SessionPart, ToolPart, ReasoningPart, TextPart } from '../types/parts'
6
+ import type { AgentBranding } from '../types/branding'
7
+
8
+ const NOW = Date.now()
9
+
10
+ // -- Helpers --
11
+
12
+ function msg(id: string) {
13
+ return { id, role: 'assistant' as const }
14
+ }
15
+
16
+ function toolPart(
17
+ id: string,
18
+ tool: string,
19
+ status: ToolPart['state']['status'],
20
+ input: unknown,
21
+ output?: unknown,
22
+ error?: string,
23
+ durationMs?: number,
24
+ ): ToolPart {
25
+ const start = NOW - (durationMs ?? 0) - 1000
26
+ return {
27
+ type: 'tool',
28
+ id,
29
+ tool,
30
+ state: {
31
+ status,
32
+ input,
33
+ output,
34
+ error,
35
+ time: {
36
+ start,
37
+ end: status === 'completed' || status === 'error' ? start + (durationMs ?? 500) : undefined,
38
+ },
39
+ },
40
+ }
41
+ }
42
+
43
+ function reasoningPart(text: string, durationMs: number): ReasoningPart {
44
+ return {
45
+ type: 'reasoning',
46
+ text,
47
+ time: { start: NOW - durationMs - 2000, end: NOW - 2000 },
48
+ }
49
+ }
50
+
51
+ function textPart(text: string): TextPart {
52
+ return { type: 'text', text }
53
+ }
54
+
55
+ // -- Scenario: complete debugging run --
56
+
57
+ const debugMsg1 = msg('msg-1')
58
+ const debugMsg2 = msg('msg-2')
59
+ const debugMsg3 = msg('msg-3')
60
+
61
+ const debugPartMap: Record<string, SessionPart[]> = {
62
+ 'msg-1': [
63
+ reasoningPart(
64
+ `The user wants me to fix the failing test. Let me read the failing test first, then look at the implementation to understand the root cause.
65
+
66
+ I should avoid guessing — grep for the actual error message pattern to find where it originates, then trace backwards to the source.`,
67
+ 8400,
68
+ ),
69
+ toolPart('t1', 'bash', 'completed', { command: 'pnpm test --run 2>&1 | head -40' },
70
+ `FAIL src/run/run-group.test.ts\n ● RunGroup › renders tool parts\n\n TypeError: Cannot read properties of undefined (reading 'map')\n at RunGroup (src/run/run-group.tsx:42:40)\n at render (/node_modules/@testing-library/react/pure.js:55:22)`,
71
+ undefined, 1240),
72
+ toolPart('t2', 'read', 'completed', { file_path: '/home/user/project/src/run/run-group.test.ts' },
73
+ `import { render } from '@testing-library/react'\nimport { RunGroup } from './run-group'\n\nconst mockRun = { id: 'run-1', messages: [{ id: 'msg-a', role: 'assistant' }], ... }\n\nit('renders tool parts', () => {\n render(<RunGroup run={mockRun} partMap={undefined} collapsed={false} onToggle={() => {}} />)\n})`,
74
+ undefined, 58),
75
+ ],
76
+ 'msg-2': [
77
+ toolPart('t3', 'grep', 'completed', { pattern: 'partMap', path: 'src/run/run-group.tsx' },
78
+ `src/run/run-group.tsx:184: const msgParts = partMap[msg.id] ?? []\nsrc/run/run-group.tsx:287: }, [run.messages, partMap])`,
79
+ undefined, 44),
80
+ toolPart('t4', 'edit', 'completed',
81
+ { file_path: '/home/user/project/src/run/run-group.tsx', old_string: '{ run, partMap, collapsed', new_string: '{ run, partMap = {}, collapsed' },
82
+ 'Edit applied.',
83
+ undefined, 30),
84
+ toolPart('t5', 'bash', 'completed', { command: 'pnpm test --run src/run/run-group.test.ts' },
85
+ ` ✓ src/run/run-group.test.ts (3)\n\nTest Files 1 passed (1)\n Tests 3 passed (3)\n Duration 1.21s`,
86
+ undefined, 1210),
87
+ ],
88
+ 'msg-3': [
89
+ textPart(
90
+ 'Fixed. The test was passing `partMap={undefined}` but the component destructured it without a default. Adding `partMap = {}` as a default parameter makes the component safe when callers omit the prop. All 3 tests pass.',
91
+ ),
92
+ ],
93
+ }
94
+
95
+ const debugRun: Run = {
96
+ id: 'run-debug',
97
+ messages: [debugMsg1, debugMsg2, debugMsg3],
98
+ isComplete: true,
99
+ isStreaming: false,
100
+ summaryText: 'Fixed failing RunGroup test by adding default parameter for partMap.',
101
+ finalTextPart: { messageId: 'msg-3', partIndex: 0, text: 'Fixed. The test was passing...' },
102
+ stats: {
103
+ toolCount: 5,
104
+ messageCount: 3,
105
+ thinkingDurationMs: 8400,
106
+ textPartCount: 1,
107
+ toolCategories: new Set(['command', 'read', 'search', 'edit']),
108
+ },
109
+ }
110
+
111
+ // -- Scenario: streaming run --
112
+
113
+ const streamMsg1 = msg('stream-msg-1')
114
+
115
+ const streamPartMap: Record<string, SessionPart[]> = {
116
+ 'stream-msg-1': [
117
+ reasoningPart(
118
+ `The user wants me to add minute-level formatting to formatDuration. I need to:\n1. Update the implementation\n2. Update the tests\n3. Run tests to verify`,
119
+ 0, // still active — would normally have no endTime but for story we set a duration
120
+ ),
121
+ toolPart('st1', 'read', 'completed', { file_path: '/home/user/project/src/utils/format.ts' },
122
+ `export function formatDuration(ms: number): string {\n if (ms < 1000) return \`\${ms}ms\`\n return \`\${(ms / 1000).toFixed(1)}s\`\n}`,
123
+ undefined, 42),
124
+ toolPart('st2', 'write', 'running', { file_path: '/home/user/project/src/utils/format.ts' },
125
+ undefined, undefined, undefined),
126
+ ],
127
+ }
128
+
129
+ const streamRun: Run = {
130
+ id: 'run-stream',
131
+ messages: [streamMsg1],
132
+ isComplete: false,
133
+ isStreaming: true,
134
+ summaryText: null,
135
+ finalTextPart: null,
136
+ stats: {
137
+ toolCount: 2,
138
+ messageCount: 1,
139
+ thinkingDurationMs: 6200,
140
+ textPartCount: 0,
141
+ toolCategories: new Set(['read', 'write']),
142
+ },
143
+ }
144
+
145
+ // -- Scenario: error run --
146
+
147
+ const errMsg1 = msg('err-msg-1')
148
+
149
+ const errPartMap: Record<string, SessionPart[]> = {
150
+ 'err-msg-1': [
151
+ toolPart('e1', 'bash', 'completed', { command: 'pnpm build' },
152
+ undefined,
153
+ `error TS2322: Type 'string | undefined' is not assignable to type 'string'.\n src/utils/format.ts:8:5\n\nerror TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'.\n src/run/run-item-primitives.tsx:6:48\n\nFound 2 errors.`,
154
+ 3200),
155
+ toolPart('e2', 'edit', 'completed',
156
+ { file_path: '/home/user/project/src/utils/format.ts', old_string: 'ms: number', new_string: 'ms: number | undefined' },
157
+ 'Edit applied.', undefined, 31),
158
+ toolPart('e3', 'edit', 'error',
159
+ { file_path: '/home/user/project/src/run/run-item-primitives.tsx' },
160
+ undefined,
161
+ 'Edit failed: old_string not found in file. The text may have been modified by a previous edit.',
162
+ 22),
163
+ textPart('The second edit failed because the file was already modified. Let me re-read it and apply the correct patch.'),
164
+ ],
165
+ }
166
+
167
+ const errRun: Run = {
168
+ id: 'run-error',
169
+ messages: [errMsg1],
170
+ isComplete: true,
171
+ isStreaming: false,
172
+ summaryText: 'Attempted to fix TypeScript build errors; one edit failed.',
173
+ finalTextPart: null,
174
+ stats: {
175
+ toolCount: 3,
176
+ messageCount: 1,
177
+ thinkingDurationMs: 0,
178
+ textPartCount: 1,
179
+ toolCategories: new Set(['command', 'edit']),
180
+ },
181
+ }
182
+
183
+ // -- Scenario: single tool call (minimal) --
184
+
185
+ const minimalMsg = msg('min-msg-1')
186
+ const minimalPartMap: Record<string, SessionPart[]> = {
187
+ 'min-msg-1': [
188
+ toolPart('m1', 'bash', 'completed', { command: 'ls -la' },
189
+ 'total 48\ndrwxr-xr-x 8 user user 4096 Mar 30 12:00 .\ndrwxr-xr-x 22 user user 4096 Mar 30 11:58 ..',
190
+ undefined, 88),
191
+ textPart('The directory is empty except for the standard dot entries.'),
192
+ ],
193
+ }
194
+
195
+ const minimalRun: Run = {
196
+ id: 'run-minimal',
197
+ messages: [minimalMsg],
198
+ isComplete: true,
199
+ isStreaming: false,
200
+ summaryText: 'Listed directory contents.',
201
+ finalTextPart: { messageId: 'min-msg-1', partIndex: 1, text: 'The directory is empty...' },
202
+ stats: {
203
+ toolCount: 1,
204
+ messageCount: 1,
205
+ thinkingDurationMs: 0,
206
+ textPartCount: 1,
207
+ toolCategories: new Set(['command']),
208
+ },
209
+ }
210
+
211
+ // -- Custom branding --
212
+
213
+ const customBranding: AgentBranding = {
214
+ label: 'Sandbox Agent',
215
+ accentClass: 'text-violet-400',
216
+ bgClass: 'bg-violet-500/8',
217
+ containerBgClass: 'bg-muted/60',
218
+ borderClass: 'border-violet-500/30',
219
+ iconClass: '',
220
+ textClass: 'text-violet-300',
221
+ }
222
+
223
+ // -- Controlled wrapper --
224
+
225
+ function ControlledRunGroup(props: Omit<React.ComponentProps<typeof RunGroup>, 'collapsed' | 'onToggle'>) {
226
+ const [collapsed, setCollapsed] = useState(false)
227
+ return (
228
+ <RunGroup
229
+ {...props}
230
+ collapsed={collapsed}
231
+ onToggle={() => setCollapsed((c) => !c)}
232
+ />
233
+ )
234
+ }
235
+
236
+ // --
237
+
238
+ const meta: Meta<typeof RunGroup> = {
239
+ title: 'Run/RunGroup',
240
+ component: RunGroup,
241
+ parameters: {
242
+ layout: 'fullscreen',
243
+ backgrounds: { default: 'dark' },
244
+ },
245
+ }
246
+
247
+ export default meta
248
+ type Story = StoryObj<typeof RunGroup>
249
+
250
+ export const CompleteDebuggingRun: Story = {
251
+ name: 'Complete — debugging run',
252
+ render: () => (
253
+ <div className="p-6 max-w-3xl">
254
+ <ControlledRunGroup run={debugRun} partMap={debugPartMap} />
255
+ </div>
256
+ ),
257
+ }
258
+
259
+ export const Streaming: Story = {
260
+ name: 'Streaming — in progress',
261
+ render: () => (
262
+ <div className="p-6 max-w-3xl">
263
+ <ControlledRunGroup run={streamRun} partMap={streamPartMap} />
264
+ </div>
265
+ ),
266
+ }
267
+
268
+ export const WithErrors: Story = {
269
+ name: 'Complete — with tool errors',
270
+ render: () => (
271
+ <div className="p-6 max-w-3xl">
272
+ <ControlledRunGroup run={errRun} partMap={errPartMap} />
273
+ </div>
274
+ ),
275
+ }
276
+
277
+ export const Minimal: Story = {
278
+ name: 'Minimal — single tool + text',
279
+ render: () => (
280
+ <div className="p-6 max-w-3xl">
281
+ <ControlledRunGroup run={minimalRun} partMap={minimalPartMap} />
282
+ </div>
283
+ ),
284
+ }
285
+
286
+ export const Collapsed: Story = {
287
+ name: 'Collapsed state',
288
+ render: () => (
289
+ <div className="p-6 max-w-3xl">
290
+ <RunGroup
291
+ run={debugRun}
292
+ partMap={debugPartMap}
293
+ collapsed={true}
294
+ onToggle={() => {}}
295
+ />
296
+ </div>
297
+ ),
298
+ }
299
+
300
+ export const CustomBranding: Story = {
301
+ name: 'Custom branding',
302
+ render: () => (
303
+ <div className="p-6 max-w-3xl">
304
+ <ControlledRunGroup
305
+ run={debugRun}
306
+ partMap={debugPartMap}
307
+ branding={customBranding}
308
+ />
309
+ </div>
310
+ ),
311
+ }
312
+
313
+ export const MultipleRuns: Story = {
314
+ name: 'Multiple consecutive runs',
315
+ render: () => (
316
+ <div className="p-6 max-w-3xl space-y-4">
317
+ <ControlledRunGroup run={minimalRun} partMap={minimalPartMap} />
318
+ <ControlledRunGroup run={debugRun} partMap={debugPartMap} />
319
+ <ControlledRunGroup run={errRun} partMap={errPartMap} />
320
+ </div>
321
+ ),
322
+ }