@elabs-ai/components-ai 4.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 (180) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -0
  3. package/dist/_audio-player-media-chrome-KA5DY54G.js +81 -0
  4. package/dist/_audio-player-media-chrome-KA5DY54G.js.map +1 -0
  5. package/dist/_flow-boundary-D63PJ65S.js +186 -0
  6. package/dist/_flow-boundary-D63PJ65S.js.map +1 -0
  7. package/dist/_interactive-terminal-xterm-ATJ5EW3G.js +11 -0
  8. package/dist/_interactive-terminal-xterm-ATJ5EW3G.js.map +1 -0
  9. package/dist/_persona-rive-RFR2EUWP.js +164 -0
  10. package/dist/_persona-rive-RFR2EUWP.js.map +1 -0
  11. package/dist/index.d.ts +4079 -0
  12. package/dist/index.js +11770 -0
  13. package/dist/index.js.map +1 -0
  14. package/package.json +87 -0
  15. package/src/_audio-player-media-chrome.tsx +146 -0
  16. package/src/_chat-shell-rail.tsx +18 -0
  17. package/src/_code-block-theme.test.ts +134 -0
  18. package/src/_code-block-theme.ts +184 -0
  19. package/src/_flow-boundary.tsx +237 -0
  20. package/src/_flow-lazy.ts +34 -0
  21. package/src/_interactive-terminal-xterm.ts +32 -0
  22. package/src/_lazy-mermaid.test.ts +101 -0
  23. package/src/_lazy-mermaid.ts +117 -0
  24. package/src/_persona-rive.tsx +252 -0
  25. package/src/_streamdown-i18n.ts +119 -0
  26. package/src/agent-timeline.stories.tsx +66 -0
  27. package/src/agent-timeline.test.tsx +74 -0
  28. package/src/agent-timeline.tsx +106 -0
  29. package/src/agent.tsx +113 -0
  30. package/src/agentic-workspace.stories.tsx +1099 -0
  31. package/src/artifact.stories.tsx +62 -0
  32. package/src/artifact.test.tsx +112 -0
  33. package/src/artifact.tsx +167 -0
  34. package/src/asset-preview.stories.tsx +100 -0
  35. package/src/asset-preview.test.tsx +126 -0
  36. package/src/asset-preview.tsx +218 -0
  37. package/src/attachments.tsx +393 -0
  38. package/src/audio-player.stories.tsx +99 -0
  39. package/src/audio-player.tsx +170 -0
  40. package/src/blocks-ai-composer.stories.tsx +83 -0
  41. package/src/canvas.stories.tsx +142 -0
  42. package/src/canvas.tsx +29 -0
  43. package/src/chain-of-thought.tsx +193 -0
  44. package/src/chat-greeting.stories.tsx +96 -0
  45. package/src/chat-greeting.test.tsx +91 -0
  46. package/src/chat-greeting.tsx +91 -0
  47. package/src/chat-shell.stories.tsx +123 -0
  48. package/src/chat-shell.test.tsx +113 -0
  49. package/src/chat-shell.tsx +97 -0
  50. package/src/chat.stories.tsx +99 -0
  51. package/src/checkpoint.tsx +54 -0
  52. package/src/code-block.stories.tsx +81 -0
  53. package/src/code-block.test.tsx +191 -0
  54. package/src/code-block.tsx +673 -0
  55. package/src/commit.tsx +343 -0
  56. package/src/composer.stories.tsx +188 -0
  57. package/src/composer.test.tsx +182 -0
  58. package/src/composer.tsx +202 -0
  59. package/src/confirmation.stories.tsx +61 -0
  60. package/src/confirmation.test.tsx +136 -0
  61. package/src/confirmation.tsx +273 -0
  62. package/src/connection.tsx +23 -0
  63. package/src/context-panel.stories.tsx +137 -0
  64. package/src/context-panel.test.tsx +198 -0
  65. package/src/context-panel.tsx +609 -0
  66. package/src/context.stories.tsx +16 -0
  67. package/src/context.tsx +363 -0
  68. package/src/controls.tsx +20 -0
  69. package/src/conversation.stories.tsx +26 -0
  70. package/src/conversation.test.tsx +123 -0
  71. package/src/conversation.tsx +143 -0
  72. package/src/dark-theme-variant.test.ts +47 -0
  73. package/src/edge.tsx +33 -0
  74. package/src/environment-variables.tsx +304 -0
  75. package/src/file-tree.stories.tsx +55 -0
  76. package/src/file-tree.test.tsx +101 -0
  77. package/src/file-tree.tsx +390 -0
  78. package/src/gallery.stories.tsx +182 -0
  79. package/src/gallery.test.tsx +150 -0
  80. package/src/gallery.tsx +737 -0
  81. package/src/grouped-parts.stories.tsx +104 -0
  82. package/src/grouped-parts.test.tsx +167 -0
  83. package/src/grouped-parts.tsx +247 -0
  84. package/src/image.stories.tsx +67 -0
  85. package/src/image.test.tsx +105 -0
  86. package/src/image.tsx +115 -0
  87. package/src/index.ts +93 -0
  88. package/src/inline-citation.stories.tsx +91 -0
  89. package/src/inline-citation.test.tsx +96 -0
  90. package/src/inline-citation.tsx +314 -0
  91. package/src/interactive-terminal.stories.tsx +165 -0
  92. package/src/interactive-terminal.test.tsx +448 -0
  93. package/src/interactive-terminal.tsx +444 -0
  94. package/src/jsx-preview.stories.tsx +265 -0
  95. package/src/jsx-preview.test.tsx +75 -0
  96. package/src/jsx-preview.tsx +409 -0
  97. package/src/markdown-view.stories.tsx +57 -0
  98. package/src/markdown-view.test.tsx +36 -0
  99. package/src/markdown-view.tsx +113 -0
  100. package/src/message-edit.stories.tsx +150 -0
  101. package/src/message-edit.test.tsx +115 -0
  102. package/src/message-edit.tsx +336 -0
  103. package/src/message-feedback.stories.tsx +53 -0
  104. package/src/message-feedback.test.tsx +62 -0
  105. package/src/message-feedback.tsx +117 -0
  106. package/src/message-form-spec.ts +349 -0
  107. package/src/message-form.stories.tsx +182 -0
  108. package/src/message-form.test.tsx +227 -0
  109. package/src/message-form.tsx +845 -0
  110. package/src/message-table-spec.ts +281 -0
  111. package/src/message-table.stories.tsx +176 -0
  112. package/src/message-table.test.tsx +144 -0
  113. package/src/message-table.tsx +366 -0
  114. package/src/message.stories.tsx +262 -0
  115. package/src/message.test.tsx +508 -0
  116. package/src/message.tsx +619 -0
  117. package/src/mic-selector.tsx +339 -0
  118. package/src/microcopy.test.tsx +123 -0
  119. package/src/model-selector.stories.tsx +73 -0
  120. package/src/model-selector.test.tsx +55 -0
  121. package/src/model-selector.tsx +225 -0
  122. package/src/motion-config.tsx +49 -0
  123. package/src/node.tsx +71 -0
  124. package/src/open-in-chat.tsx +340 -0
  125. package/src/package-info.tsx +205 -0
  126. package/src/panel.tsx +20 -0
  127. package/src/part-groups.ts +258 -0
  128. package/src/persona-sources.ts +59 -0
  129. package/src/persona.stories.tsx +111 -0
  130. package/src/persona.test.tsx +67 -0
  131. package/src/persona.tsx +148 -0
  132. package/src/plan.tsx +135 -0
  133. package/src/prompt-input.stories.tsx +261 -0
  134. package/src/prompt-input.test.tsx +392 -0
  135. package/src/prompt-input.tsx +1668 -0
  136. package/src/queue.tsx +237 -0
  137. package/src/reasoning.stories.tsx +81 -0
  138. package/src/reasoning.test.tsx +50 -0
  139. package/src/reasoning.tsx +229 -0
  140. package/src/sandbox.stories.tsx +62 -0
  141. package/src/sandbox.test.tsx +42 -0
  142. package/src/sandbox.tsx +149 -0
  143. package/src/schema-display.tsx +412 -0
  144. package/src/selection-toolbar.stories.tsx +86 -0
  145. package/src/selection-toolbar.test.tsx +89 -0
  146. package/src/selection-toolbar.tsx +192 -0
  147. package/src/shimmer.stories.tsx +10 -0
  148. package/src/shimmer.tsx +81 -0
  149. package/src/snippet.stories.tsx +19 -0
  150. package/src/snippet.test.tsx +116 -0
  151. package/src/snippet.tsx +138 -0
  152. package/src/sources.stories.tsx +34 -0
  153. package/src/sources.test.tsx +129 -0
  154. package/src/sources.tsx +93 -0
  155. package/src/speech-input.tsx +312 -0
  156. package/src/stack-trace.tsx +479 -0
  157. package/src/streamdown-i18n.test.tsx +163 -0
  158. package/src/suggestion.stories.tsx +75 -0
  159. package/src/suggestion.test.tsx +72 -0
  160. package/src/suggestion.tsx +120 -0
  161. package/src/task.stories.tsx +22 -0
  162. package/src/task.test.tsx +36 -0
  163. package/src/task.tsx +89 -0
  164. package/src/templates-ai-assistant.stories.tsx +142 -0
  165. package/src/terminal.tsx +244 -0
  166. package/src/test-results.stories.tsx +177 -0
  167. package/src/test-results.test.tsx +241 -0
  168. package/src/test-results.tsx +408 -0
  169. package/src/tool-result-card.stories.tsx +143 -0
  170. package/src/tool-result-card.test.tsx +72 -0
  171. package/src/tool-result-card.tsx +86 -0
  172. package/src/tool.stories.tsx +60 -0
  173. package/src/tool.test.tsx +63 -0
  174. package/src/tool.tsx +235 -0
  175. package/src/toolbar.tsx +44 -0
  176. package/src/transcription.tsx +118 -0
  177. package/src/voice-selector.tsx +469 -0
  178. package/src/web-preview.stories.tsx +67 -0
  179. package/src/web-preview.test.tsx +117 -0
  180. package/src/web-preview.tsx +283 -0
@@ -0,0 +1,1099 @@
1
+ import { useState } from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react-vite";
3
+ import {
4
+ Avatar,
5
+ AvatarFallback,
6
+ Badge,
7
+ Button,
8
+ Collapsible,
9
+ CollapsibleContent,
10
+ CollapsibleTrigger,
11
+ DropdownMenu,
12
+ DropdownMenuContent,
13
+ DropdownMenuItem,
14
+ DropdownMenuLabel,
15
+ DropdownMenuSeparator,
16
+ DropdownMenuTrigger,
17
+ Separator,
18
+ Sidebar,
19
+ SidebarContent,
20
+ SidebarFooter,
21
+ SidebarGroup,
22
+ SidebarGroupAction,
23
+ SidebarGroupContent,
24
+ SidebarGroupLabel,
25
+ SidebarHeader,
26
+ SidebarInset,
27
+ SidebarMenu,
28
+ SidebarMenuButton,
29
+ SidebarMenuItem,
30
+ SidebarMenuSub,
31
+ SidebarMenuSubButton,
32
+ SidebarMenuSubItem,
33
+ SidebarProvider,
34
+ SidebarRail,
35
+ SidebarTrigger,
36
+ } from "@elabs-ai/components-ui";
37
+ import { BrandLogo } from "@elabs-ai/components-icons";
38
+ // Story-only composition: the agentic-workspace SHOWCASE renders REAL @elabs-ai/components-charts
39
+ // surfaces — AutoChart (in a ChartFrame: expand / flip-to-table / download) and KPI
40
+ // MetricCards with sparklines. @elabs-ai/components-charts is a devDependency of @elabs-ai/components-ai used
41
+ // ONLY by stories — never imported by shipped src (the package's dist stays
42
+ // decoupled from its sibling). See research/ai-charts/01-ai-chart-integration-plan.md.
43
+ import {
44
+ AutoChart,
45
+ ChartFrame,
46
+ Line,
47
+ LineChart,
48
+ MetricCard,
49
+ MetricGrid,
50
+ } from "@elabs-ai/components-charts";
51
+ import {
52
+ Boxes,
53
+ Calculator,
54
+ ChevronRight,
55
+ ChevronsUpDown,
56
+ Copy,
57
+ Database,
58
+ FolderKanban,
59
+ Globe,
60
+ History,
61
+ LogOut,
62
+ MessageSquarePlus,
63
+ PencilLine,
64
+ RefreshCw,
65
+ Search,
66
+ Settings,
67
+ Share2,
68
+ Sparkles,
69
+ ThumbsDown,
70
+ ThumbsUp,
71
+ } from "lucide-react";
72
+
73
+ import { AgentStep, AgentTimeline } from "./agent-timeline";
74
+ import { AssetPreview } from "./asset-preview";
75
+ import { ChatShell } from "./chat-shell";
76
+ import { Checkpoint, CheckpointIcon, CheckpointTrigger } from "./checkpoint";
77
+ import {
78
+ ApprovalCard,
79
+ ApprovalCardAccepted,
80
+ ApprovalCardActions,
81
+ ApprovalCardApprove,
82
+ ApprovalCardDeny,
83
+ ApprovalCardDescription,
84
+ ApprovalCardRejected,
85
+ ApprovalCardRequest,
86
+ ApprovalCardTitle,
87
+ } from "./confirmation";
88
+ import {
89
+ Context,
90
+ ContextContent,
91
+ ContextContentFooter,
92
+ ContextContentHeader,
93
+ ContextTrigger,
94
+ } from "./context";
95
+ import {
96
+ ContextPanel,
97
+ ContextPanelBody,
98
+ ContextPanelDetail,
99
+ ContextPanelHeader,
100
+ ContextPanelProvider,
101
+ ContextPanelSection,
102
+ ContextPanelTrigger,
103
+ useContextPanel,
104
+ type ContextAsset,
105
+ } from "./context-panel";
106
+ import { Conversation, ConversationContent, ConversationScrollButton } from "./conversation";
107
+ import { ProducedAssetTree } from "./file-tree";
108
+ import {
109
+ EvidenceChip,
110
+ InlineCitation,
111
+ InlineCitationCard,
112
+ InlineCitationCardBody,
113
+ InlineCitationCarousel,
114
+ InlineCitationCarouselContent,
115
+ InlineCitationCarouselHeader,
116
+ InlineCitationCarouselIndex,
117
+ InlineCitationCarouselItem,
118
+ InlineCitationCarouselNext,
119
+ InlineCitationCarouselPrev,
120
+ InlineCitationQuote,
121
+ InlineCitationSource,
122
+ InlineCitationText,
123
+ } from "./inline-citation";
124
+ import {
125
+ AgentMessage,
126
+ MessageAction,
127
+ MessageActions,
128
+ MessageContent,
129
+ MessageResponse,
130
+ UserMessage,
131
+ } from "./message";
132
+ import {
133
+ Plan,
134
+ PlanAction,
135
+ PlanContent,
136
+ PlanDescription,
137
+ PlanFooter,
138
+ PlanHeader,
139
+ PlanTitle,
140
+ PlanTrigger,
141
+ } from "./plan";
142
+ import {
143
+ PromptInputActionAddAttachments,
144
+ PromptInputActionMenu,
145
+ PromptInputActionMenuContent,
146
+ PromptInputActionMenuTrigger,
147
+ PromptInputButton,
148
+ } from "./prompt-input";
149
+ import { Composer } from "./composer";
150
+ import { Reasoning, ReasoningContent, ReasoningTrigger } from "./reasoning";
151
+ import { Shimmer } from "./shimmer";
152
+ import { SourceList } from "./sources";
153
+ import { Suggestion, Suggestions } from "./suggestion";
154
+ import { Task, TaskContent, TaskItem, TaskTrigger } from "./task";
155
+ import { Tool, ToolContent, ToolDetails, ToolHeader, ToolInput, ToolOutput } from "./tool";
156
+ import { ToolResultCard } from "./tool-result-card";
157
+
158
+ /* -------------------------------------------------------------------------- */
159
+ /* Fixture data — the "managed" workspace entities */
160
+ /* -------------------------------------------------------------------------- */
161
+
162
+ // Q3 board-note figures, rendered as REAL @elabs-ai/components-charts surfaces in the showcase:
163
+ // a ChartFrame-wrapped AutoChart (expand / flip-to-table / download) plus KPI
164
+ // MetricCards with sparkline visuals.
165
+ const revenueByRegion = [
166
+ { region: "NA", q2: 18_500_000, q3: 21_200_000 },
167
+ { region: "EMEA", q2: 12_400_000, q3: 13_800_000 },
168
+ { region: "APAC", q2: 8_600_000, q3: 9_400_000 },
169
+ { region: "LATAM", q2: 3_400_000, q3: 3_800_000 },
170
+ ];
171
+
172
+ const revenueColumns = [
173
+ { key: "region", header: "Region" },
174
+ { key: "q2", header: "Q2 (USD)" },
175
+ { key: "q3", header: "Q3 (USD)" },
176
+ ];
177
+
178
+ /** Tiny token-driven sparkline for a MetricCard `visual` slot. */
179
+ function Sparkline({ values, tone }: { values: number[]; tone: string }) {
180
+ const data = values.map((v, i) => ({ date: new Date(2025, i, 1), v }));
181
+ return (
182
+ <div className="h-9 w-full">
183
+ <LineChart
184
+ data={data}
185
+ aspectRatio={undefined}
186
+ margin={{ top: 4, right: 0, bottom: 4, left: 0 }}
187
+ >
188
+ <Line dataKey="v" stroke={tone} strokeWidth={2} />
189
+ </LineChart>
190
+ </div>
191
+ );
192
+ }
193
+
194
+ const PROJECTS = [
195
+ { id: "atlas", name: "Atlas", tag: "Finance Copilot" },
196
+ { id: "nova", name: "Nova", tag: "Support Triage" },
197
+ { id: "orion", name: "Orion", tag: "Sales Insights" },
198
+ ];
199
+
200
+ const SESSIONS = [
201
+ { id: "q3-revenue", title: "Q3 revenue board note" },
202
+ { id: "churn", title: "Churn cohort analysis" },
203
+ { id: "schema", title: "Warehouse schema review" },
204
+ { id: "pipeline", title: "Pipeline triage" },
205
+ ];
206
+
207
+ const ASSET_CATEGORIES = [
208
+ { id: "reports", name: "Reports", count: 4 },
209
+ { id: "charts", name: "Charts", count: 5 },
210
+ { id: "exports", name: "Exports", count: 3 },
211
+ ];
212
+
213
+ const SUGGESTIONS = [
214
+ "Break revenue down by region",
215
+ "Draft a CFO email",
216
+ "Compare against forecast",
217
+ ];
218
+
219
+ const BOARD_NOTE_MD = `# Q3 FY26 Revenue — Board Note
220
+
221
+ **Total revenue: $48.2M, +12.4% QoQ** (Q2: $42.9M) — ~9% ahead of plan.
222
+
223
+ ## Highlights
224
+ - Net-new ARR: **$6.1M**
225
+ - Gross margin: **79%** (+1.2 pts QoQ)
226
+ - Top driver: **EMEA expansion (+$2.3M)**
227
+
228
+ ## Watch items
229
+ - Services revenue flat QoQ
230
+ - NRR steady at 114%
231
+
232
+ _Figures reconcile to the Q3 10-Q and \`finance.revenue\`._
233
+ `;
234
+
235
+ const EXEC_SUMMARY_MD = `# Q3 FY26 — Executive summary
236
+
237
+ Revenue **$48.2M (+12.4% QoQ)**, ~9% ahead of plan. EMEA was the largest
238
+ regional driver (+$2.3M); gross margin improved to **79%**.
239
+ `;
240
+
241
+ const REVENUE_SQL = `select quarter,
242
+ region,
243
+ sum(amount) as revenue
244
+ from finance.revenue
245
+ where quarter in ('2026-Q2', '2026-Q3')
246
+ group by 1, 2
247
+ order by 1, 2;`;
248
+
249
+ const REVENUE_CSV = `region,q2,q3
250
+ NA,18500000,21200000
251
+ EMEA,12400000,13800000
252
+ APAC,8600000,9400000
253
+ LATAM,3400000,3800000`;
254
+
255
+ const FINAL_ANSWER_MD = `Here's the headline for the board:
256
+
257
+ **Q3 revenue came in at $48.2M, up 12.4% from Q2 ($42.9M)** and ~9% ahead of forecast. Growth was led by **Cloud subscriptions (+18%)**, with **EMEA the largest regional driver (+$2.3M)**. Gross margin improved to **79%** and net-new ARR was **$6.1M**.
258
+
259
+ I drafted a one-page board note and exported the supporting figures — both are in the **Produced assets** panel on the right.`;
260
+
261
+ /** The run's grounding — shared by the answer footer and the context rail. */
262
+ const GROUNDING_SOURCES = [
263
+ { href: "https://example.com/q3-10q", title: "Q3 10-Q filing" },
264
+ { href: "https://example.com/earnings-deck", title: "Q3 earnings deck" },
265
+ { href: "https://docs.example.com/finance-revenue", title: "finance.revenue — warehouse table" },
266
+ ];
267
+
268
+ /** Produced assets — the `ContextAsset` units the ContextPanel drill-in focuses. */
269
+ const PRODUCED_ASSETS: ContextAsset[] = [
270
+ {
271
+ id: "board-note",
272
+ name: "board-note.md",
273
+ path: "reports/board-note.md",
274
+ type: "markdown",
275
+ content: BOARD_NOTE_MD,
276
+ },
277
+ {
278
+ id: "exec-summary",
279
+ name: "exec-summary.md",
280
+ path: "reports/exec-summary.md",
281
+ type: "markdown",
282
+ content: EXEC_SUMMARY_MD,
283
+ },
284
+ {
285
+ id: "q3-vs-q2",
286
+ name: "q3-vs-q2.csv",
287
+ path: "exports/q3-vs-q2.csv",
288
+ type: "csv",
289
+ content: REVENUE_CSV,
290
+ },
291
+ {
292
+ id: "revenue-sql",
293
+ name: "revenue.sql",
294
+ path: "exports/revenue.sql",
295
+ type: "sql",
296
+ content: REVENUE_SQL,
297
+ },
298
+ ];
299
+
300
+ /* -------------------------------------------------------------------------- */
301
+ /* Left rail — the double-sided shell's primary navigation */
302
+ /* -------------------------------------------------------------------------- */
303
+
304
+ interface WorkspaceSidebarProps {
305
+ activeProject: string;
306
+ onSelectProject: (id: string) => void;
307
+ activeSession: string;
308
+ onSelectSession: (id: string) => void;
309
+ onNewChat: () => void;
310
+ }
311
+
312
+ function WorkspaceSidebar({
313
+ activeProject,
314
+ onSelectProject,
315
+ activeSession,
316
+ onSelectSession,
317
+ onNewChat,
318
+ }: WorkspaceSidebarProps) {
319
+ return (
320
+ <Sidebar side="left" variant="sidebar" collapsible="icon">
321
+ <SidebarHeader>
322
+ <SidebarMenu>
323
+ <SidebarMenuItem>
324
+ <SidebarMenuButton size="lg" tooltip="Atlas workspace">
325
+ <span className="flex aspect-square size-8 shrink-0 items-center justify-center group-data-[collapsible=icon]:size-4">
326
+ <BrandLogo variant="mark" className="size-full" aria-hidden />
327
+ </span>
328
+ <span className="grid flex-1 text-start leading-tight">
329
+ <span className="truncate text-body font-semibold">Atlas</span>
330
+ <span className="truncate text-meta text-muted-foreground">Agentic workspace</span>
331
+ </span>
332
+ </SidebarMenuButton>
333
+ </SidebarMenuItem>
334
+ </SidebarMenu>
335
+ </SidebarHeader>
336
+
337
+ <SidebarContent>
338
+ {/* Primary action */}
339
+ <SidebarGroup>
340
+ <SidebarGroupContent>
341
+ <SidebarMenu>
342
+ <SidebarMenuItem>
343
+ <SidebarMenuButton
344
+ tooltip="New chat"
345
+ onClick={onNewChat}
346
+ className="bg-primary text-primary-foreground hover:bg-primary/90 hover:text-primary-foreground"
347
+ >
348
+ <MessageSquarePlus />
349
+ <span>New chat</span>
350
+ </SidebarMenuButton>
351
+ </SidebarMenuItem>
352
+ </SidebarMenu>
353
+ </SidebarGroupContent>
354
+ </SidebarGroup>
355
+
356
+ {/* AI Projects */}
357
+ <SidebarGroup>
358
+ <SidebarGroupLabel>AI Projects</SidebarGroupLabel>
359
+ <SidebarGroupAction title="New project">
360
+ <ChevronRight className="size-4 rotate-90 opacity-0" aria-hidden />
361
+ <span className="sr-only">New project</span>
362
+ </SidebarGroupAction>
363
+ <SidebarGroupContent>
364
+ <SidebarMenu>
365
+ <Collapsible defaultOpen className="group/collapsible">
366
+ <SidebarMenuItem>
367
+ <CollapsibleTrigger asChild>
368
+ <SidebarMenuButton tooltip="AI Projects">
369
+ <FolderKanban />
370
+ <span className="flex-1 truncate text-start">Projects</span>
371
+ <ChevronRight className="size-4 transition-transform group-data-[state=open]/collapsible:rotate-90" />
372
+ </SidebarMenuButton>
373
+ </CollapsibleTrigger>
374
+ <CollapsibleContent>
375
+ <SidebarMenuSub>
376
+ {PROJECTS.map((p) => (
377
+ <SidebarMenuSubItem key={p.id}>
378
+ <SidebarMenuSubButton asChild isActive={activeProject === p.id}>
379
+ <a
380
+ href={`#project-${p.id}`}
381
+ onClick={(e) => {
382
+ e.preventDefault();
383
+ onSelectProject(p.id);
384
+ }}
385
+ >
386
+ <span className="flex-1 truncate">{p.name}</span>
387
+ <span className="text-meta text-muted-foreground">{p.tag}</span>
388
+ </a>
389
+ </SidebarMenuSubButton>
390
+ </SidebarMenuSubItem>
391
+ ))}
392
+ </SidebarMenuSub>
393
+ </CollapsibleContent>
394
+ </SidebarMenuItem>
395
+ </Collapsible>
396
+ </SidebarMenu>
397
+ </SidebarGroupContent>
398
+ </SidebarGroup>
399
+
400
+ {/* Recent Sessions */}
401
+ <SidebarGroup>
402
+ <SidebarGroupLabel>Recent Sessions</SidebarGroupLabel>
403
+ <SidebarGroupContent>
404
+ <SidebarMenu>
405
+ <Collapsible defaultOpen className="group/collapsible">
406
+ <SidebarMenuItem>
407
+ <CollapsibleTrigger asChild>
408
+ <SidebarMenuButton tooltip="Recent Sessions">
409
+ <History />
410
+ <span className="flex-1 truncate text-start">Sessions</span>
411
+ <ChevronRight className="size-4 transition-transform group-data-[state=open]/collapsible:rotate-90" />
412
+ </SidebarMenuButton>
413
+ </CollapsibleTrigger>
414
+ <CollapsibleContent>
415
+ <SidebarMenuSub>
416
+ {SESSIONS.map((s) => (
417
+ <SidebarMenuSubItem key={s.id}>
418
+ <SidebarMenuSubButton asChild isActive={activeSession === s.id}>
419
+ <a
420
+ href={`#session-${s.id}`}
421
+ onClick={(e) => {
422
+ e.preventDefault();
423
+ onSelectSession(s.id);
424
+ }}
425
+ >
426
+ <span className="truncate">{s.title}</span>
427
+ </a>
428
+ </SidebarMenuSubButton>
429
+ </SidebarMenuSubItem>
430
+ ))}
431
+ </SidebarMenuSub>
432
+ </CollapsibleContent>
433
+ </SidebarMenuItem>
434
+ </Collapsible>
435
+ </SidebarMenu>
436
+ </SidebarGroupContent>
437
+ </SidebarGroup>
438
+
439
+ {/* Produced Assets */}
440
+ <SidebarGroup>
441
+ <SidebarGroupLabel>Produced Assets</SidebarGroupLabel>
442
+ <SidebarGroupContent>
443
+ <SidebarMenu>
444
+ <Collapsible defaultOpen className="group/collapsible">
445
+ <SidebarMenuItem>
446
+ <CollapsibleTrigger asChild>
447
+ <SidebarMenuButton tooltip="Produced Assets">
448
+ <Boxes />
449
+ <span className="flex-1 truncate text-start">Assets</span>
450
+ <ChevronRight className="size-4 transition-transform group-data-[state=open]/collapsible:rotate-90" />
451
+ </SidebarMenuButton>
452
+ </CollapsibleTrigger>
453
+ <CollapsibleContent>
454
+ <SidebarMenuSub>
455
+ {ASSET_CATEGORIES.map((c) => (
456
+ <SidebarMenuSubItem key={c.id}>
457
+ <SidebarMenuSubButton asChild>
458
+ <a href={`#assets-${c.id}`}>
459
+ <span className="flex-1 truncate">{c.name}</span>
460
+ <span className="text-meta tabular-nums text-muted-foreground">
461
+ {c.count}
462
+ </span>
463
+ </a>
464
+ </SidebarMenuSubButton>
465
+ </SidebarMenuSubItem>
466
+ ))}
467
+ </SidebarMenuSub>
468
+ </CollapsibleContent>
469
+ </SidebarMenuItem>
470
+ </Collapsible>
471
+ </SidebarMenu>
472
+ </SidebarGroupContent>
473
+ </SidebarGroup>
474
+ </SidebarContent>
475
+
476
+ <SidebarFooter>
477
+ <SidebarMenu>
478
+ <SidebarMenuItem>
479
+ <DropdownMenu>
480
+ <DropdownMenuTrigger asChild>
481
+ <SidebarMenuButton size="lg" tooltip="Account">
482
+ <Avatar className="size-7 rounded-md">
483
+ <AvatarFallback className="rounded-md text-meta">AR</AvatarFallback>
484
+ </Avatar>
485
+ <span className="grid flex-1 text-start leading-tight">
486
+ <span className="truncate text-body font-medium">Avery Rao</span>
487
+ <span className="truncate text-meta text-muted-foreground">avery@acme.co</span>
488
+ </span>
489
+ <ChevronsUpDown className="ms-auto size-4" />
490
+ </SidebarMenuButton>
491
+ </DropdownMenuTrigger>
492
+ <DropdownMenuContent side="right" align="end" className="w-48">
493
+ <DropdownMenuLabel>Account</DropdownMenuLabel>
494
+ <DropdownMenuItem>
495
+ <Settings className="me-2 size-4" />
496
+ Settings
497
+ </DropdownMenuItem>
498
+ <DropdownMenuSeparator />
499
+ <DropdownMenuItem>
500
+ <LogOut className="me-2 size-4" />
501
+ Sign out
502
+ </DropdownMenuItem>
503
+ </DropdownMenuContent>
504
+ </DropdownMenu>
505
+ </SidebarMenuItem>
506
+ </SidebarMenu>
507
+ </SidebarFooter>
508
+
509
+ <SidebarRail />
510
+ </Sidebar>
511
+ );
512
+ }
513
+
514
+ /* -------------------------------------------------------------------------- */
515
+ /* Right rail — the real ContextPanel compound (#193, research 09) */
516
+ /* -------------------------------------------------------------------------- */
517
+
518
+ /** Root view — grounding + the produced-asset tree (drill-in on select). */
519
+ function ContextRailRoot() {
520
+ const { state, actions } = useContextPanel();
521
+ return (
522
+ <>
523
+ <ContextPanelSection label="Grounding">
524
+ <SourceList sources={GROUNDING_SOURCES} />
525
+ </ContextPanelSection>
526
+ <ContextPanelSection label="Produced assets">
527
+ <ProducedAssetTree
528
+ assets={PRODUCED_ASSETS}
529
+ selectedId={state.selectedAsset?.id}
530
+ onSelect={actions.openDetail}
531
+ />
532
+ </ContextPanelSection>
533
+ </>
534
+ );
535
+ }
536
+
537
+ /** Detail view — the focused asset behind the drill-in, with BACK in the header. */
538
+ function ContextRailDetail() {
539
+ const { state } = useContextPanel();
540
+ return (
541
+ <ContextPanelDetail>
542
+ {state.selectedAsset ? (
543
+ <AssetPreview asset={state.selectedAsset} />
544
+ ) : (
545
+ <p className="text-body text-muted-foreground">Select a produced asset to preview it…</p>
546
+ )}
547
+ </ContextPanelDetail>
548
+ );
549
+ }
550
+
551
+ /**
552
+ * The always-mounted right rail: a `ContextPanel` sibling of the chat under the
553
+ * workspace-wrapping provider — animated width collapse via the canonical
554
+ * `useCollapsiblePanel`, root ↔ detail drill-in with focus management.
555
+ */
556
+ function WorkspaceContextPanel() {
557
+ return (
558
+ <ContextPanel>
559
+ <ContextPanelHeader title="Context">
560
+ <Context usedTokens={42000} maxTokens={200000} modelId="anthropic/claude-opus-4">
561
+ <ContextTrigger />
562
+ <ContextContent>
563
+ <ContextContentHeader />
564
+ <ContextContentFooter />
565
+ </ContextContent>
566
+ </Context>
567
+ </ContextPanelHeader>
568
+ <ContextPanelBody root={<ContextRailRoot />} detail={<ContextRailDetail />} />
569
+ </ContextPanel>
570
+ );
571
+ }
572
+
573
+ /* -------------------------------------------------------------------------- */
574
+ /* The agentic transcript — a full reasoning → plan → tools → answer run */
575
+ /* -------------------------------------------------------------------------- */
576
+
577
+ function AgentRun() {
578
+ // Human-in-the-loop approval for the "post to Slack" side effect.
579
+ const [approved, setApproved] = useState<boolean | null>(null);
580
+ const confirmationState = approved === null ? "approval-requested" : "approval-responded";
581
+
582
+ return (
583
+ <>
584
+ {/* The user's request */}
585
+ <UserMessage>
586
+ <MessageContent>
587
+ Summarize Q3 revenue vs Q2, draft a board-ready note, and post the final note to #finance.
588
+ </MessageContent>
589
+ </UserMessage>
590
+
591
+ {/* Thinking */}
592
+ <Reasoning duration={8}>
593
+ <ReasoningTrigger />
594
+ <ReasoningContent>
595
+ {`The user wants a board-ready Q3 revenue note plus a Slack post.
596
+
597
+ I'll ground the numbers in the Q3 filing and reconcile them against the warehouse so the figures match what finance reports. Posting to Slack is a side effect, so I'll pause for approval before sending.`}
598
+ </ReasoningContent>
599
+ </Reasoning>
600
+
601
+ {/* Proposed plan — a proposal, not an executed step (stays distinct from
602
+ the timeline); collapsed: only the chart + the answer stay focal. */}
603
+ <Plan>
604
+ <PlanHeader>
605
+ <div className="min-w-0">
606
+ <PlanTitle>Draft the Q3 board note</PlanTitle>
607
+ <PlanDescription>
608
+ Retrieve filings, reconcile to the warehouse, then summarize.
609
+ </PlanDescription>
610
+ </div>
611
+ <PlanAction>
612
+ <PlanTrigger />
613
+ </PlanAction>
614
+ </PlanHeader>
615
+ <PlanContent>
616
+ <ol className="list-decimal space-y-1 ps-5 text-body text-muted-foreground">
617
+ <li>Pull the Q3 10-Q and Q2 board pack</li>
618
+ <li>
619
+ Reconcile to <code>finance.revenue</code> in the warehouse
620
+ </li>
621
+ <li>Compute QoQ growth, ARR and gross margin</li>
622
+ <li>Draft a one-page note and export the figures</li>
623
+ </ol>
624
+ </PlanContent>
625
+ <PlanFooter>
626
+ <span className="text-meta text-muted-foreground">
627
+ 4 steps · grounded in filings + warehouse
628
+ </span>
629
+ </PlanFooter>
630
+ </Plan>
631
+
632
+ {/* Step timeline — the canonical execution-trace rail (#192) */}
633
+ <AgentTimeline aria-label="Agent progress">
634
+ <AgentStep
635
+ icon={Search}
636
+ status="complete"
637
+ name="Searched financial filings"
638
+ summary="3 documents · Q3 10-Q, earnings deck, board pack Q2"
639
+ />
640
+ <AgentStep
641
+ icon={Database}
642
+ status="complete"
643
+ name="Queried finance.revenue"
644
+ summary="Aggregated by quarter & region"
645
+ />
646
+ <AgentStep
647
+ icon={Calculator}
648
+ status="running"
649
+ name="Computing QoQ deltas"
650
+ summary="Growth, ARR, margin"
651
+ />
652
+ <AgentStep icon={PencilLine} status="pending" name="Draft the board note" />
653
+ </AgentTimeline>
654
+
655
+ {/* Tool call — document search; JSON behind the default-collapsed details */}
656
+ <Tool>
657
+ <ToolHeader
658
+ type="tool-searchFilings"
659
+ state="output-available"
660
+ summary="3 documents found"
661
+ />
662
+ <ToolContent>
663
+ <ToolDetails>
664
+ <ToolInput
665
+ input={{
666
+ query: "Q3 revenue, ARR, gross margin",
667
+ period: "2026-Q3",
668
+ sources: ["10-Q", "earnings deck"],
669
+ }}
670
+ />
671
+ <ToolOutput
672
+ output={{
673
+ hits: 3,
674
+ documents: ["Q3-10Q.pdf", "earnings-deck.pdf", "board-pack-Q2.pdf"],
675
+ }}
676
+ errorText={undefined}
677
+ />
678
+ </ToolDetails>
679
+ </ToolContent>
680
+ </Tool>
681
+
682
+ {/* Tool call — warehouse query (dynamic tool) */}
683
+ <Tool>
684
+ <ToolHeader
685
+ type="dynamic-tool"
686
+ toolName="queryWarehouse"
687
+ state="output-available"
688
+ title="Query warehouse"
689
+ summary="8 rows · +12.4% QoQ"
690
+ />
691
+ <ToolContent>
692
+ <ToolDetails>
693
+ <ToolInput
694
+ input={{
695
+ sql: "select quarter, region, sum(amount) revenue from finance.revenue where quarter in ('2026-Q2','2026-Q3') group by 1,2",
696
+ }}
697
+ />
698
+ <ToolOutput
699
+ output={{ rows: 8, q3_total: 48200000, q2_total: 42900000, qoq_growth: 0.124 }}
700
+ errorText={undefined}
701
+ />
702
+ </ToolDetails>
703
+ </ToolContent>
704
+ </Tool>
705
+
706
+ {/* The produced chart — the elevation-channel headline (#192): a
707
+ ToolResultCard hosting a real @elabs-ai/components-charts ChartFrame/AutoChart as
708
+ children, with the tool's technical view behind ToolDetails. */}
709
+ <ToolResultCard
710
+ title="Revenue by region — Q3 vs Q2"
711
+ summary="Reconciled to finance.revenue · USD"
712
+ status="complete"
713
+ details={
714
+ <ToolDetails>
715
+ <ToolInput
716
+ input={{
717
+ chart: "bar",
718
+ x: "region",
719
+ series: ["q2", "q3"],
720
+ metric: "revenue",
721
+ }}
722
+ />
723
+ </ToolDetails>
724
+ }
725
+ >
726
+ <ChartFrame
727
+ data={revenueByRegion}
728
+ columns={revenueColumns}
729
+ height={260}
730
+ detail={
731
+ <div className="space-y-2 p-4 text-body">
732
+ <p className="font-medium text-foreground">Q3 highlights</p>
733
+ <ul className="space-y-1 text-muted-foreground">
734
+ <li>Total $48.2M · +12.4% QoQ</li>
735
+ <li>EMEA the largest driver (+$2.3M)</li>
736
+ <li>Cloud subscriptions +18%</li>
737
+ <li>Gross margin 79% · net-new ARR $6.1M</li>
738
+ </ul>
739
+ </div>
740
+ }
741
+ >
742
+ <AutoChart
743
+ spec={{
744
+ type: "bar",
745
+ x: "region",
746
+ series: [
747
+ { key: "q2", label: "Q2" },
748
+ { key: "q3", label: "Q3" },
749
+ ],
750
+ valueFormat: "compact",
751
+ data: revenueByRegion,
752
+ }}
753
+ height={210}
754
+ />
755
+ </ChartFrame>
756
+ </ToolResultCard>
757
+
758
+ {/* Human-in-the-loop approval — question / consequence / role-named actions */}
759
+ <ApprovalCard
760
+ state={confirmationState}
761
+ approval={{ id: "appr_finance", approved: approved ?? undefined }}
762
+ >
763
+ <ApprovalCardRequest>
764
+ <ApprovalCardTitle>
765
+ Post the final board note to <strong>#finance</strong> in Slack?
766
+ </ApprovalCardTitle>
767
+ <ApprovalCardDescription>
768
+ Sends the one-page note to the #finance channel. Posting is a side effect — nothing else
769
+ changes until you approve.
770
+ </ApprovalCardDescription>
771
+ <ApprovalCardActions>
772
+ <ApprovalCardDeny onClick={() => setApproved(false)}>Deny</ApprovalCardDeny>
773
+ <ApprovalCardApprove onClick={() => setApproved(true)}>
774
+ Approve &amp; post
775
+ </ApprovalCardApprove>
776
+ </ApprovalCardActions>
777
+ </ApprovalCardRequest>
778
+ <ApprovalCardAccepted>
779
+ <ApprovalCardTitle>Approved — note posted to #finance.</ApprovalCardTitle>
780
+ </ApprovalCardAccepted>
781
+ <ApprovalCardRejected>
782
+ <ApprovalCardTitle>Denied — nothing was posted.</ApprovalCardTitle>
783
+ </ApprovalCardRejected>
784
+ </ApprovalCard>
785
+
786
+ {/* What got done — collapsed run summary on the canonical rail; the
787
+ produced files live in the Produced-assets rail, not inline pills. */}
788
+ <Task>
789
+ <TaskTrigger title="Compiled the board note and exports" />
790
+ <TaskContent>
791
+ <TaskItem>Reconciled 8 warehouse rows against the Q3 filing (0 variances)</TaskItem>
792
+ <TaskItem>Wrote board-note.md and q3-vs-q2.csv — see Produced assets</TaskItem>
793
+ <TaskItem>Rendered the revenue-by-region chart</TaskItem>
794
+ </TaskContent>
795
+ </Task>
796
+
797
+ {/* Restore point */}
798
+ <Checkpoint>
799
+ <CheckpointTrigger tooltip="Restore to this checkpoint" onClick={() => undefined}>
800
+ <CheckpointIcon />
801
+ Checkpoint · figures reconciled
802
+ </CheckpointTrigger>
803
+ </Checkpoint>
804
+
805
+ {/* The grounded answer — the green-rail final-answer emphasis; KPI band,
806
+ prose, evidence and grounding are COMPOSED children. */}
807
+ <AgentMessage emphasis="answer">
808
+ <MessageContent>
809
+ {/* KPI band: the headline KPI spans wider (featured) and reads one
810
+ rung larger (emphasis="headline"). */}
811
+ <MetricGrid columns={4} featured={0} className="mb-4">
812
+ <MetricCard
813
+ emphasis="headline"
814
+ label="Q3 revenue"
815
+ value="$48.2M"
816
+ delta="12.4%"
817
+ deltaDirection="up"
818
+ description="vs $42.9M in Q2"
819
+ visual={<Sparkline values={[39, 41, 40, 43, 45, 48.2]} tone="var(--chart-1)" />}
820
+ />
821
+ <MetricCard
822
+ label="Net-new ARR"
823
+ value="$6.1M"
824
+ delta="9%"
825
+ deltaDirection="up"
826
+ description="~9% ahead of plan"
827
+ visual={<Sparkline values={[3.8, 4.4, 4.9, 5.3, 5.7, 6.1]} tone="var(--chart-2)" />}
828
+ />
829
+ <MetricCard
830
+ label="Gross margin"
831
+ value="79%"
832
+ delta="1.2 pp"
833
+ deltaDirection="up"
834
+ description="vs 77.8% in Q2"
835
+ visual={<Sparkline values={[76, 76.5, 77, 77.8, 78.4, 79]} tone="var(--chart-3)" />}
836
+ />
837
+ <MetricCard
838
+ label="Cloud subscriptions"
839
+ value="$23.4M"
840
+ delta="18%"
841
+ deltaDirection="up"
842
+ description="largest growth driver"
843
+ visual={<Sparkline values={[9, 11, 12.5, 14, 16, 18]} tone="var(--chart-4)" />}
844
+ />
845
+ </MetricGrid>
846
+ <MessageResponse>{FINAL_ANSWER_MD}</MessageResponse>
847
+ <p className="text-body text-muted-foreground">
848
+ Figures reconcile to the Q3 filing and the warehouse table{" "}
849
+ <InlineCitation>
850
+ <InlineCitationText>finance.revenue</InlineCitationText>
851
+ <InlineCitationCard>
852
+ <EvidenceChip
853
+ sources={[
854
+ "https://example.com/q3-10q",
855
+ "https://docs.example.com/finance-revenue",
856
+ ]}
857
+ />
858
+ <InlineCitationCardBody>
859
+ <InlineCitationCarousel>
860
+ <InlineCitationCarouselHeader>
861
+ <InlineCitationCarouselPrev />
862
+ <InlineCitationCarouselNext />
863
+ <InlineCitationCarouselIndex />
864
+ </InlineCitationCarouselHeader>
865
+ <InlineCitationCarouselContent>
866
+ <InlineCitationCarouselItem>
867
+ <InlineCitationSource
868
+ title="Q3 10-Q filing"
869
+ url="https://example.com/q3-10q"
870
+ description="Quarterly revenue, ARR and margin."
871
+ />
872
+ <InlineCitationQuote>
873
+ Total revenue of $48.2M, up 12.4% sequentially.
874
+ </InlineCitationQuote>
875
+ </InlineCitationCarouselItem>
876
+ <InlineCitationCarouselItem>
877
+ <InlineCitationSource
878
+ title="finance.revenue table"
879
+ url="https://docs.example.com/finance-revenue"
880
+ description="Source-of-truth revenue by quarter and region."
881
+ />
882
+ </InlineCitationCarouselItem>
883
+ </InlineCitationCarouselContent>
884
+ </InlineCitationCarousel>
885
+ </InlineCitationCardBody>
886
+ </InlineCitationCard>
887
+ </InlineCitation>
888
+ .
889
+ </p>
890
+
891
+ <SourceList sources={GROUNDING_SOURCES} />
892
+
893
+ <MessageActions>
894
+ <MessageAction tooltip="Copy" label="Copy message">
895
+ <Copy className="size-4" />
896
+ </MessageAction>
897
+ <MessageAction tooltip="Regenerate" label="Regenerate response">
898
+ <RefreshCw className="size-4" />
899
+ </MessageAction>
900
+ <MessageAction tooltip="Good response" label="Good response">
901
+ <ThumbsUp className="size-4" />
902
+ </MessageAction>
903
+ <MessageAction tooltip="Bad response" label="Bad response">
904
+ <ThumbsDown className="size-4" />
905
+ </MessageAction>
906
+ </MessageActions>
907
+ </MessageContent>
908
+ </AgentMessage>
909
+ </>
910
+ );
911
+ }
912
+
913
+ /* -------------------------------------------------------------------------- */
914
+ /* The double-sided agentic workspace */
915
+ /* -------------------------------------------------------------------------- */
916
+
917
+ interface FollowUp {
918
+ id: string;
919
+ role: "user" | "assistant";
920
+ text: string;
921
+ }
922
+
923
+ function AgenticWorkspace({ defaultSidebarOpen = true }: { defaultSidebarOpen?: boolean }) {
924
+ const [activeProject, setActiveProject] = useState("atlas");
925
+ const [activeSession, setActiveSession] = useState("q3-revenue");
926
+ const [followUps, setFollowUps] = useState<FollowUp[]>([]);
927
+ const [status, setStatus] = useState<"ready" | "submitted">("ready");
928
+
929
+ const projectName = PROJECTS.find((p) => p.id === activeProject)?.name ?? "Atlas";
930
+ const sessionTitle = SESSIONS.find((s) => s.id === activeSession)?.title ?? "New session";
931
+
932
+ const send = (text: string) => {
933
+ const trimmed = text.trim();
934
+ if (!trimmed) return;
935
+ setFollowUps((prev) => [...prev, { id: `u${prev.length}`, role: "user", text: trimmed }]);
936
+ setStatus("submitted");
937
+ window.setTimeout(() => {
938
+ setFollowUps((prev) => [
939
+ ...prev,
940
+ {
941
+ id: `a${prev.length}`,
942
+ role: "assistant",
943
+ text: "Done — I updated the board note and refreshed the figures in the Produced assets panel.",
944
+ },
945
+ ]);
946
+ setStatus("ready");
947
+ }, 700);
948
+ };
949
+
950
+ return (
951
+ <div className="h-dvh w-full overflow-hidden bg-background text-foreground">
952
+ {/* The provider wraps the WORKSPACE so the header trigger and the panel
953
+ share lifted state (research 09 — the Sidebar/SidebarInset relation). */}
954
+ <ContextPanelProvider>
955
+ <SidebarProvider defaultOpen={defaultSidebarOpen} className="h-full min-h-0">
956
+ <WorkspaceSidebar
957
+ activeProject={activeProject}
958
+ onSelectProject={setActiveProject}
959
+ activeSession={activeSession}
960
+ onSelectSession={setActiveSession}
961
+ onNewChat={() => {
962
+ setFollowUps([]);
963
+ setStatus("ready");
964
+ }}
965
+ />
966
+
967
+ <SidebarInset className="flex min-w-0 flex-col">
968
+ {/* Header bar — h-12 to align with the right ContextPanelHeader and
969
+ the library header convention (ChatShell/ContextPanel). */}
970
+ <header className="flex h-12 shrink-0 items-center gap-2 border-b bg-background px-3">
971
+ <SidebarTrigger className="-ms-1" />
972
+ <Separator orientation="vertical" className="me-1 h-5" />
973
+ <div className="flex min-w-0 items-center gap-1.5 text-body">
974
+ <span className="truncate font-medium">{projectName}</span>
975
+ <ChevronRight className="size-3.5 shrink-0 text-muted-foreground" />
976
+ <span className="truncate text-muted-foreground">{sessionTitle}</span>
977
+ </div>
978
+ <div className="ms-auto flex items-center gap-1.5">
979
+ <Badge variant="secondary" className="gap-1 whitespace-nowrap font-mono text-meta">
980
+ <Sparkles className="size-3" />
981
+ claude-opus-4.8
982
+ </Badge>
983
+ <ContextPanelTrigger />
984
+ {/* Header chrome action — `ghost`, not the form-field `outline`
985
+ (#194, research 02 §3a BTN-1). */}
986
+ <Button variant="ghost" size="sm">
987
+ <Share2 className="size-4" />
988
+ Share
989
+ </Button>
990
+ </div>
991
+ </header>
992
+
993
+ {/* Main agentic chat — immersive (bare) so it fills the pane the
994
+ SidebarInset already bounds, with no redundant card frame. */}
995
+ <div className="min-h-0 flex-1">
996
+ <ChatShell
997
+ variant="bare"
998
+ composer={
999
+ <div className="space-y-2">
1000
+ <Suggestions>
1001
+ {SUGGESTIONS.map((s) => (
1002
+ <Suggestion key={s} suggestion={s} onClick={send} />
1003
+ ))}
1004
+ </Suggestions>
1005
+ <Composer
1006
+ placeholder="Ask the agent to analyze, draft or export…"
1007
+ status={status === "submitted" ? "Generating…" : "Awaiting your input"}
1008
+ sendStatus={status}
1009
+ showVoice={false}
1010
+ onSubmit={(message) => {
1011
+ if (message.text) send(message.text);
1012
+ }}
1013
+ tools={
1014
+ <>
1015
+ <PromptInputActionMenu>
1016
+ <PromptInputActionMenuTrigger />
1017
+ <PromptInputActionMenuContent>
1018
+ <PromptInputActionAddAttachments />
1019
+ </PromptInputActionMenuContent>
1020
+ </PromptInputActionMenu>
1021
+ <PromptInputButton tooltip="Web search">
1022
+ <Globe className="size-4" />
1023
+ </PromptInputButton>
1024
+ <PromptInputButton tooltip="Connect data">
1025
+ <Database className="size-4" />
1026
+ </PromptInputButton>
1027
+ </>
1028
+ }
1029
+ />
1030
+ </div>
1031
+ }
1032
+ >
1033
+ <Conversation className="flex-1">
1034
+ <ConversationContent>
1035
+ <AgentRun />
1036
+
1037
+ {followUps.map((m) =>
1038
+ m.role === "user" ? (
1039
+ <UserMessage key={m.id}>
1040
+ <MessageContent>{m.text}</MessageContent>
1041
+ </UserMessage>
1042
+ ) : (
1043
+ <AgentMessage key={m.id}>
1044
+ <MessageContent>
1045
+ <MessageResponse>{m.text}</MessageResponse>
1046
+ </MessageContent>
1047
+ </AgentMessage>
1048
+ ),
1049
+ )}
1050
+
1051
+ {status === "submitted" ? (
1052
+ <AgentMessage>
1053
+ <MessageContent>
1054
+ <Shimmer>Analyzing…</Shimmer>
1055
+ </MessageContent>
1056
+ </AgentMessage>
1057
+ ) : null}
1058
+ </ConversationContent>
1059
+ <ConversationScrollButton />
1060
+ </Conversation>
1061
+ </ChatShell>
1062
+ </div>
1063
+ </SidebarInset>
1064
+
1065
+ <WorkspaceContextPanel />
1066
+ </SidebarProvider>
1067
+ </ContextPanelProvider>
1068
+ </div>
1069
+ );
1070
+ }
1071
+
1072
+ /* -------------------------------------------------------------------------- */
1073
+ /* Stories */
1074
+ /* -------------------------------------------------------------------------- */
1075
+
1076
+ const meta = {
1077
+ title: "Patterns/Scenarios/Agentic AI Workspace",
1078
+ component: AgenticWorkspace,
1079
+ parameters: {
1080
+ layout: "fullscreen",
1081
+ docs: {
1082
+ description: {
1083
+ component:
1084
+ "A full-page demo scenario composed from the @elabs-ai/components-* grammar: a double-sided application shell (collapsible left nav + the real ContextPanel right rail with animated collapse and asset drill-in) wrapping a complete agentic AI interaction — reasoning, a plan, the AgentTimeline execution rail, tools with JSON behind disclosure, a ToolResultCard chart headline, an ApprovalCard decision, a task summary, a checkpoint, and a grounded AgentMessage answer with an EvidenceChip and a SourceList.",
1085
+ },
1086
+ },
1087
+ },
1088
+ } satisfies Meta<typeof AgenticWorkspace>;
1089
+
1090
+ export default meta;
1091
+ type Story = StoryObj<typeof meta>;
1092
+
1093
+ /** The full double-sided agentic workspace. */
1094
+ export const Default: Story = {};
1095
+
1096
+ /** Same scenario with the left navigation collapsed to an icon rail. */
1097
+ export const CollapsedNav: Story = {
1098
+ args: { defaultSidebarOpen: false },
1099
+ };