@usetheo/ui 0.10.0-next.0 → 0.11.0-next.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 (43) hide show
  1. package/CHANGELOG.md +119 -3
  2. package/README.md +22 -21
  3. package/dist/chunk-BX7A5GUV.js +78 -0
  4. package/dist/chunk-BX7A5GUV.js.map +1 -0
  5. package/dist/{chunk-H3ANHVEL.js → chunk-DKQAHZG2.js} +4 -4
  6. package/dist/{chunk-H3ANHVEL.js.map → chunk-DKQAHZG2.js.map} +1 -1
  7. package/dist/{chunk-DAKIL5PC.js → chunk-IPEYGWA7.js} +3 -3
  8. package/dist/{chunk-DAKIL5PC.js.map → chunk-IPEYGWA7.js.map} +1 -1
  9. package/dist/chunk-IWSLOBYG.js +199 -0
  10. package/dist/chunk-IWSLOBYG.js.map +1 -0
  11. package/dist/chunk-MI5CXMZU.js +171 -0
  12. package/dist/chunk-MI5CXMZU.js.map +1 -0
  13. package/dist/chunk-QJGGTIUN.js +110 -0
  14. package/dist/chunk-QJGGTIUN.js.map +1 -0
  15. package/dist/chunk-R2PAGRDP.js +152 -0
  16. package/dist/chunk-R2PAGRDP.js.map +1 -0
  17. package/dist/{chunk-QU6RLHYH.js → chunk-TNBJ36XJ.js} +3 -3
  18. package/dist/{chunk-QU6RLHYH.js.map → chunk-TNBJ36XJ.js.map} +1 -1
  19. package/dist/components.css +1 -1
  20. package/dist/composites/agent-stream/index.js +3 -3
  21. package/dist/composites/data-table/index.js +10 -0
  22. package/dist/composites/data-table/index.js.map +1 -0
  23. package/dist/composites/page-shell/index.js +7 -0
  24. package/dist/composites/page-shell/index.js.map +1 -0
  25. package/dist/composites/rule-editor/index.js +2 -2
  26. package/dist/composites/skill-editor/index.js +2 -2
  27. package/dist/index.d.ts +281 -12
  28. package/dist/index.js +47 -42
  29. package/dist/index.js.map +1 -1
  30. package/dist/primitives/action-bar/index.js +4 -0
  31. package/dist/primitives/action-bar/index.js.map +1 -0
  32. package/dist/primitives/dropdown-menu/index.js +4 -0
  33. package/dist/primitives/dropdown-menu/index.js.map +1 -0
  34. package/dist/primitives/pin-input/index.js +4 -0
  35. package/dist/primitives/pin-input/index.js.map +1 -0
  36. package/llms.txt +4 -3
  37. package/package.json +63 -43
  38. package/registry/index.json +30 -0
  39. package/registry/r/action-bar.json +22 -0
  40. package/registry/r/data-table.json +27 -0
  41. package/registry/r/dropdown-menu.json +23 -0
  42. package/registry/r/page-shell.json +25 -0
  43. package/registry/r/pin-input.json +20 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,106 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.11.0-next.0] - 2026-05-25
11
+
12
+ Minor (additive, zero breaking change) — ships Brief #5 from the
13
+ TheoCloud dashboard team, closing 3 measured Deep Review findings.
14
+ Five new components: 3 brief-asks (PinInput, DataTable, PageShell)
15
+ + 2 explicit pre-requisites (DropdownMenu, ActionBar) that the
16
+ brief assumed existed but didn't.
17
+
18
+ Plan: `.claude/knowledge-base/plans/dashboard-primitives-brief-5-plan.md`
19
+ ADR: `.claude/knowledge-base/decisions/page-shell-composite-pattern.md`
20
+ Brief: `theo/docs/handoff/2026-05-25-theo-ui-cloud-dashboard-brief-5.md`
21
+
22
+ ### Added
23
+
24
+ - **`<DropdownMenu>` primitive (NEW, Brief #5 pre-req)** — accessible
25
+ menu built on `@radix-ui/react-dropdown-menu` (already a bundled
26
+ dep, no new peer needed). Sub-components attached via
27
+ `Object.assign`: `Trigger`, `Portal`, `Content`, `Item`,
28
+ `CheckboxItem`, `RadioItem`, `Label`, `Separator`, `Shortcut`,
29
+ `Group`, `Sub`, `SubTrigger`, `SubContent`, `RadioGroup`. Styled
30
+ with `@usetheo/ui` design tokens. Consolidates 5 prior direct-
31
+ Radix usages (`model-selector`, `intent-selector`, `agent-profile`,
32
+ `theme-switcher`, `theo-code-shell`) under a single styled
33
+ wrapper. 6 unit tests + 5 Ladle stories. SSR-safe.
34
+ - **`<ActionBar>` primitive (NEW, Brief #5 pre-req)** — page-top
35
+ action strip with three optional slots: search input (flex-1,
36
+ grows to fill), filter icon button, primary action button
37
+ (right-aligned). Returns `null` when no slots are provided.
38
+ Primary action supports `loading` state with `Loader2` spinner.
39
+ Usable standalone or composed inside `<PageShell>`. 6 unit tests
40
+ + 5 Ladle stories.
41
+ - **`<PinInput>` primitive (NEW)** — multi-slot OTP / code input
42
+ with auto-advance focus, paste handling (whitespace stripped),
43
+ arrow-key navigation, backspace clearing + focus back. Default
44
+ 6 slots, configurable. `inputMode="numeric"` (default, triggers
45
+ mobile numeric keyboard via `pattern="[0-9]*"`) or
46
+ `alphanumeric` (auto-uppercase). Optional `mask` renders bullets.
47
+ Optional `error` state applies destructive border. `onComplete`
48
+ fires once on transitions to complete (NOT on mount with pre-
49
+ filled value — verified via test). Closes Deep Review § 2.12 P2
50
+ (Verification page off-brand single-input pattern). 17 unit
51
+ tests + 7 Ladle stories.
52
+ - **`<DataTable>` composite (NEW)** — generic, sortable,
53
+ expandable composite over `<Table>` + `<Pagination>` +
54
+ `<Skeleton>` + `<EmptyState>` + `<DropdownMenu>`. Generic over
55
+ `T` (e.g. `DataTable<Domain>`). Sortable headers (controlled via
56
+ `onSortChange` OR uncontrolled client-side). Sticky header
57
+ (default true). Expandable rows with `expandable(row)` callback
58
+ — multi-row default, opt-in `expandMode="single"`. Row actions
59
+ via `rowActions(row)` opens DropdownMenu. Client-side pagination
60
+ with `pageSize`; sort changes reset to page 0. Loading state
61
+ renders 5 skeleton rows. Empty state delegates to `<EmptyState>`
62
+ or custom `emptyState` prop. Expanded row `colSpan` correctly
63
+ accounts for chevron + actions columns (EC-1 fix). pageSize<=0
64
+ clamps to 1 graceful degradation. Closes Deep Review Top-5
65
+ fix #2, § 2.2 P1, § 2.4 P1 (card-grid → sortable table for
66
+ Domains + Projects). 19 unit tests + 8 Ladle stories.
67
+ - **`<PageShell>` composite (NEW)** — page-level scaffold. Title
68
+ + optional description + optional ActionBar (when search /
69
+ primaryAction / onFilterClick provided), then one of four
70
+ mutually-exclusive content states with strict precedence:
71
+ loading > error > empty > children. Default loading is a
72
+ centered spinner Card; `loadingNode?` escape hatch for custom
73
+ skeletons. Error renders Card with message + optional retry
74
+ button + optional docs link. Empty delegates to `<EmptyState>`.
75
+ `aria-busy="true"` on the `<main>` element while loading. Does
76
+ NOT manage `document.title` (D3 scope-narrowing); consumers
77
+ wire `onTitleChange?` callback to their own hook. Dedupes
78
+ ~20 LOC × 13 dashboard pages of boilerplate. 15 unit tests + 6
79
+ Ladle stories.
80
+
81
+ ### Notes
82
+
83
+ - Edge-case review surfaced 1 MUST FIX (DataTable expanded row
84
+ colSpan miscalculation when rowActions present) + 14 SHOULD TEST
85
+ + 7 DOCUMENT — all incorporated into TDD blocks before
86
+ implementation.
87
+ - D3 scope-narrowing: PageShell does NOT include `useSetPageTitle`
88
+ / `PageMetaProvider` — those are consumer-scope hooks. The
89
+ library exposes only the visible heading + an `onTitleChange?`
90
+ callback for the consumer to wire their own title management.
91
+ - DropdownMenu consolidation is opt-in: the 5 existing direct-
92
+ Radix usages stay untouched in this release; migration is a
93
+ follow-up PR.
94
+ - Zero new peer-deps. `@radix-ui/react-dropdown-menu` was already
95
+ bundled.
96
+
97
+ ### Bundle delta (consumer canary)
98
+
99
+ Measured against TheoCloud dashboard (no consumer migration to the
100
+ new primitives yet — pure version bump):
101
+
102
+ | Metric | 0.10.0-next.0 | 0.11.0-next.0 | Δ |
103
+ |---|---|---|---|
104
+ | `@usetheo/ui` chunk | TBD | TBD | TBD |
105
+ | TOTAL initial JS | TBD | TBD | TBD |
106
+
107
+ (Evidence file:
108
+ `.claude/knowledge-base/baselines/2026-05-26-post-brief-5/theocloud-bundle-delta.txt`)
109
+
10
110
  ## [0.10.0-next.0] - 2026-05-25
11
111
 
12
112
  Minor (additive, zero breaking change) — fixes a publishing-pipeline
@@ -87,10 +187,26 @@ Brief: `theo/docs/handoff/2026-05-24-theo-ui-subpath-tree-shaking-brief-4.md`
87
187
  | **Shared chunks (`dist/chunk-*.js`)** | 0 | 119 | + |
88
188
 
89
189
  The barrel shrank because all component code now lives in shared
90
- chunks. Consumer-side bundle delta against TheoCloud dashboard:
91
- **TBD — see Phase 8 evidence file** at
190
+ chunks. **Consumer-side bundle delta against TheoCloud dashboard
191
+ (measured 2026-05-25):**
192
+
193
+ | Metric | 0.9.0-next.0 | 0.10.0-next.0 | Δ |
194
+ |---|---|---|---|
195
+ | `@usetheo/ui` chunk | 36.96 KB brotli | 10.96 KB brotli | **−26.00 KB (−70.3%)** |
196
+ | TOTAL initial JS | 176.27 KB brotli | 134.68 KB brotli | −41.59 KB (−23.6%) |
197
+
198
+ Per-chunk cap (50 KB): passes with 39 KB headroom (was 13 KB).
199
+ Total hard gate (240 KB): passes with 105 KB headroom (was 64 KB).
200
+
201
+ **Notable:** the savings were realized WITHOUT migrating consumer
202
+ imports to subpath form. The barrel benefits from tree-shaking now
203
+ because `dist/index.js` is structured as a collection of
204
+ per-component re-exports from shared chunks — Vite/Rollup's
205
+ tree-shaker can drop individual chunks per consumer usage. Subpath-
206
+ form migration is expected to yield additional savings on top.
207
+
208
+ Evidence file:
92
209
  `.claude/knowledge-base/baselines/2026-05-25-post-subpath/theocloud-bundle-delta.txt`
93
- once measured.
94
210
 
95
211
  ### Migration (consumer-side, opt-in)
96
212
 
package/README.md CHANGED
@@ -13,8 +13,8 @@ A React component library built for AI agent surfaces and cloud dashboards. **10
13
13
  <!-- BEGIN:counts -->
14
14
  [![license](https://img.shields.io/badge/license-Apache--2.0-7C3AED?style=flat-square)](./LICENSE)
15
15
  [![react](https://img.shields.io/badge/react-18+-7C3AED?style=flat-square&logo=react&logoColor=white)](https://react.dev)
16
- [![tests](https://img.shields.io/badge/tests-1281%20passing-success?style=flat-square)](#quality-gates)
17
- [![components](https://img.shields.io/badge/components-134-7C3AED?style=flat-square)](#component-catalog)
16
+ [![tests](https://img.shields.io/badge/tests-1344%20passing-success?style=flat-square)](#quality-gates)
17
+ [![components](https://img.shields.io/badge/components-139-7C3AED?style=flat-square)](#component-catalog)
18
18
  [![shadcn](https://img.shields.io/badge/shadcn-compatible-000?style=flat-square)](https://ui.shadcn.com/docs/registry)
19
19
  <!-- END:counts -->
20
20
 
@@ -133,32 +133,33 @@ import { ThemeProvider, ThemeScript } from "@usetheo/ui";
133
133
  ## Component catalog
134
134
 
135
135
  <!-- BEGIN:component-catalog-intro -->
136
- **134 components**, organized by mechanical rule: a *primitive* imports no other `@usetheo/ui` component; a *composite* does.
136
+ **139 components**, organized by mechanical rule: a *primitive* imports no other `@usetheo/ui` component; a *composite* does.
137
137
  <!-- END:component-catalog-intro -->
138
138
 
139
139
  <details>
140
140
  <summary>
141
141
  <!-- BEGIN:primitives-count -->
142
- **Primitives** (89) — building blocks
142
+ **Primitives** (92) — building blocks
143
143
  <!-- END:primitives-count -->
144
144
  </summary>
145
145
 
146
146
  <!-- BEGIN:primitives -->
147
- `AgentErrorCard` · `AgentEvent` · `AgentHandoff` · `AgentProfile` · `AgentStartingState` · `AgentStreaming`
148
- `Alert` · `ArtifactPreview` · `AttachmentChip` · `AuditLogEntry` · `AutoCompactNotice` · `Avatar`
149
- `Badge` · `BrowserControls` · `BuildLogStream` · `Button` · `CapabilityIndicator` · `Card`
150
- `ChatThread` · `Checkbox` · `ContextCard` · `ContextWindowBar` · `CopyButton` · `CostMeter`
151
- `CreatedFilesCard` · `CronJobCard` · `DangerZone` · `Dialog` · `DiffViewer` · `EmptyState`
152
- `FolderContextCard` · `FolderSelector` · `FormField` · `HookConfig` · `HookEventLog` · `Input`
153
- `IntentSelector` · `Label` · `LaneBoard` · `LoginSplit` · `MCPServerCard` · `MemoryEditor`
154
- `MentionMenu` · `MetricsPanel` · `ModelCard` · `ModelSelector` · `Pagination` · `PermissionMatrix`
155
- `PlanBadge` · `Progress` · `ProgressChecklist` · `ProjectSwitcher` · `QuickActionChips` · `RadioGroup`
156
- `RecentFoldersList` · `RuleCard` · `RunStats` · `RunningTasksPanel` · `ScrollArea` · `Select`
157
- `SessionListItem` · `SessionTimeline` · `Sheet` · `Sidebar` · `Skeleton` · `SkillCard`
158
- `SocialAuthRow` · `StatTile` · `StatusDot` · `StepsRail` · `SubAgentDispatch` · `Switch`
159
- `SystemPromptEditor` · `Table` · `Tabs` · `TaskNode` · `TaskPlan` · `TerminalPanel`
160
- `Textarea` · `Timestamp` · `Toast` · `Toaster` · `TokenUsageChart` · `ToolCall`
161
- `ToolCallCard` · `ToolResult` · `ToolsList` · `Tooltip` · `TopNav`
147
+ `ActionBar` · `AgentErrorCard` · `AgentEvent` · `AgentHandoff` · `AgentProfile` · `AgentStartingState`
148
+ `AgentStreaming` · `Alert` · `ArtifactPreview` · `AttachmentChip` · `AuditLogEntry` · `AutoCompactNotice`
149
+ `Avatar` · `Badge` · `BrowserControls` · `BuildLogStream` · `Button` · `CapabilityIndicator`
150
+ `Card` · `ChatThread` · `Checkbox` · `ContextCard` · `ContextWindowBar` · `CopyButton`
151
+ `CostMeter` · `CreatedFilesCard` · `CronJobCard` · `DangerZone` · `Dialog` · `DiffViewer`
152
+ `DropdownMenu` · `EmptyState` · `FolderContextCard` · `FolderSelector` · `FormField` · `HookConfig`
153
+ `HookEventLog` · `Input` · `IntentSelector` · `Label` · `LaneBoard` · `LoginSplit`
154
+ `MCPServerCard` · `MemoryEditor` · `MentionMenu` · `MetricsPanel` · `ModelCard` · `ModelSelector`
155
+ `Pagination` · `PermissionMatrix` · `PinInput` · `PlanBadge` · `Progress` · `ProgressChecklist`
156
+ `ProjectSwitcher` · `QuickActionChips` · `RadioGroup` · `RecentFoldersList` · `RuleCard` · `RunStats`
157
+ `RunningTasksPanel` · `ScrollArea` · `Select` · `SessionListItem` · `SessionTimeline` · `Sheet`
158
+ `Sidebar` · `Skeleton` · `SkillCard` · `SocialAuthRow` · `StatTile` · `StatusDot`
159
+ `StepsRail` · `SubAgentDispatch` · `Switch` · `SystemPromptEditor` · `Table` · `Tabs`
160
+ `TaskNode` · `TaskPlan` · `TerminalPanel` · `Textarea` · `Timestamp` · `Toast`
161
+ `Toaster` · `TokenUsageChart` · `ToolCall` · `ToolCallCard` · `ToolResult` · `ToolsList`
162
+ `Tooltip` · `TopNav`
162
163
  <!-- END:primitives -->
163
164
 
164
165
  </details>
@@ -166,12 +167,12 @@ import { ThemeProvider, ThemeScript } from "@usetheo/ui";
166
167
  <details>
167
168
  <summary>
168
169
  <!-- BEGIN:composites-count -->
169
- **Composites** (45) — assembled flows
170
+ **Composites** (47) — assembled flows
170
171
  <!-- END:composites-count -->
171
172
  </summary>
172
173
 
173
174
  <!-- BEGIN:composites -->
174
- `AccountMenu` · `AgentComposer` · `AgentEditor` · `AgentStream` · `AgentTimeline` · `ApprovalCard` · `ChatComposer` · `ChatMessage` · `ChatMessageAction` · `ChatMessageActions` · `ChatMessageBranch` · `ChatMessageBranchContent` · `ChatMessageBranchNext` · `ChatMessageBranchPage` · `ChatMessageBranchPrevious` · `ChatMessageBranchSelector` · `ChatMessageContent` · `ChatMessageResponse` · `ChatMessageRoot` · `ChatMessageToolbar` · `CodeBlock` · `CommandPalette` · `ConfirmDialog` · `CronJobsList` · `DataPart` · `DeploymentRow` · `DomainConfig` · `EnvVarEditor` · `FilePart` · `MCPServerList` · `PermissionModal` · `PreviewEnvCard` · `PreviewPanel` · `ProjectCard` · `ReasoningPart` · `RollbackUI` · `RuleEditor` · `SkillEditor` · `SkillsList` · `SourceDocumentPart` · `SourceUrlPart` · `TaskHeader` · `TextPart` · `ToolCallPart` · `UsageMeter`
175
+ `AccountMenu` · `AgentComposer` · `AgentEditor` · `AgentStream` · `AgentTimeline` · `ApprovalCard` · `ChatComposer` · `ChatMessage` · `ChatMessageAction` · `ChatMessageActions` · `ChatMessageBranch` · `ChatMessageBranchContent` · `ChatMessageBranchNext` · `ChatMessageBranchPage` · `ChatMessageBranchPrevious` · `ChatMessageBranchSelector` · `ChatMessageContent` · `ChatMessageResponse` · `ChatMessageRoot` · `ChatMessageToolbar` · `CodeBlock` · `CommandPalette` · `ConfirmDialog` · `CronJobsList` · `DataPart` · `DataTable` · `DeploymentRow` · `DomainConfig` · `EnvVarEditor` · `FilePart` · `MCPServerList` · `PageShell` · `PermissionModal` · `PreviewEnvCard` · `PreviewPanel` · `ProjectCard` · `ReasoningPart` · `RollbackUI` · `RuleEditor` · `SkillEditor` · `SkillsList` · `SourceDocumentPart` · `SourceUrlPart` · `TaskHeader` · `TextPart` · `ToolCallPart` · `UsageMeter`
175
176
  <!-- END:composites -->
176
177
 
177
178
  </details>
@@ -0,0 +1,78 @@
1
+ import { cn } from './chunk-ZSRJCIWF.js';
2
+ import { Search, Filter, Loader2 } from 'lucide-react';
3
+ import { forwardRef } from 'react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ var ActionBar = forwardRef(
7
+ ({ className, search, primaryAction, onFilterClick, ...props }, ref) => {
8
+ if (!search && !primaryAction && !onFilterClick) {
9
+ return null;
10
+ }
11
+ const PrimaryIcon = primaryAction?.icon;
12
+ const isLoading = primaryAction?.loading === true;
13
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex w-full items-center gap-2", className), ...props, children: [
14
+ search ? /* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
15
+ /* @__PURE__ */ jsx(
16
+ Search,
17
+ {
18
+ "aria-hidden": "true",
19
+ className: "-translate-y-1/2 absolute top-1/2 left-3 size-4 text-muted-foreground"
20
+ }
21
+ ),
22
+ /* @__PURE__ */ jsx(
23
+ "input",
24
+ {
25
+ type: "search",
26
+ placeholder: search.placeholder,
27
+ value: search.value,
28
+ onChange: (e) => search.onChange(e.target.value),
29
+ className: cn(
30
+ "w-full rounded-md border border-border/40 bg-card py-2 pr-3 pl-9",
31
+ "font-sans text-body-sm text-foreground placeholder:text-muted-foreground",
32
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
33
+ )
34
+ }
35
+ )
36
+ ] }) : null,
37
+ onFilterClick !== void 0 ? /* @__PURE__ */ jsx(
38
+ "button",
39
+ {
40
+ type: "button",
41
+ onClick: onFilterClick,
42
+ "aria-label": "Filter",
43
+ className: cn(
44
+ "inline-flex size-9 items-center justify-center rounded-md border border-border/40",
45
+ "text-muted-foreground transition-colors",
46
+ "hover:bg-muted hover:text-foreground",
47
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
48
+ ),
49
+ children: /* @__PURE__ */ jsx(Filter, { "aria-hidden": "true", className: "size-4" })
50
+ }
51
+ ) : null,
52
+ primaryAction !== void 0 ? /* @__PURE__ */ jsxs(
53
+ "button",
54
+ {
55
+ type: "button",
56
+ onClick: primaryAction.onClick,
57
+ disabled: isLoading,
58
+ className: cn(
59
+ "ml-auto inline-flex items-center gap-2 rounded-md bg-primary px-3 py-2",
60
+ "font-medium font-sans text-body-sm text-primary-foreground",
61
+ "transition-colors hover:bg-primary-deep",
62
+ "disabled:cursor-not-allowed disabled:opacity-60",
63
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
64
+ ),
65
+ children: [
66
+ isLoading ? /* @__PURE__ */ jsx(Loader2, { "aria-hidden": "true", className: "size-4 animate-spin" }) : PrimaryIcon ? /* @__PURE__ */ jsx(PrimaryIcon, { "aria-hidden": "true", className: "size-4" }) : null,
67
+ primaryAction.label
68
+ ]
69
+ }
70
+ ) : null
71
+ ] });
72
+ }
73
+ );
74
+ ActionBar.displayName = "ActionBar";
75
+
76
+ export { ActionBar };
77
+ //# sourceMappingURL=chunk-BX7A5GUV.js.map
78
+ //# sourceMappingURL=chunk-BX7A5GUV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/primitives/action-bar/action-bar.tsx"],"names":[],"mappings":";;;;;AAsCA,IAAM,SAAA,GAAY,UAAA;AAAA,EAChB,CAAC,EAAE,SAAA,EAAW,MAAA,EAAQ,eAAe,aAAA,EAAe,GAAG,KAAA,EAAM,EAAG,GAAA,KAAQ;AACtE,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,aAAA,IAAiB,CAAC,aAAA,EAAe;AAC/C,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,MAAM,cAAc,aAAA,EAAe,IAAA;AACnC,IAAA,MAAM,SAAA,GAAY,eAAe,OAAA,KAAY,IAAA;AAE7C,IAAA,uBACE,IAAA,CAAC,SAAI,GAAA,EAAU,SAAA,EAAW,GAAG,gCAAA,EAAkC,SAAS,CAAA,EAAI,GAAG,KAAA,EAC5E,QAAA,EAAA;AAAA,MAAA,MAAA,mBACC,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,iBAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,aAAA,EAAY,MAAA;AAAA,YACZ,SAAA,EAAU;AAAA;AAAA,SACZ;AAAA,wBACA,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,QAAA;AAAA,YACL,aAAa,MAAA,CAAO,WAAA;AAAA,YACpB,OAAO,MAAA,CAAO,KAAA;AAAA,YACd,UAAU,CAAC,CAAA,KAAM,OAAO,QAAA,CAAS,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,YAC/C,SAAA,EAAW,EAAA;AAAA,cACT,kEAAA;AAAA,cACA,0EAAA;AAAA,cACA;AAAA;AACF;AAAA;AACF,OAAA,EACF,CAAA,GACE,IAAA;AAAA,MACH,kBAAkB,MAAA,mBACjB,GAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UACC,IAAA,EAAK,QAAA;AAAA,UACL,OAAA,EAAS,aAAA;AAAA,UACT,YAAA,EAAW,QAAA;AAAA,UACX,SAAA,EAAW,EAAA;AAAA,YACT,mFAAA;AAAA,YACA,yCAAA;AAAA,YACA,sCAAA;AAAA,YACA;AAAA,WACF;AAAA,UAEA,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAO,aAAA,EAAY,MAAA,EAAO,WAAU,QAAA,EAAS;AAAA;AAAA,OAChD,GACE,IAAA;AAAA,MACH,kBAAkB,MAAA,mBACjB,IAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UACC,IAAA,EAAK,QAAA;AAAA,UACL,SAAS,aAAA,CAAc,OAAA;AAAA,UACvB,QAAA,EAAU,SAAA;AAAA,UACV,SAAA,EAAW,EAAA;AAAA,YACT,wEAAA;AAAA,YACA,4DAAA;AAAA,YACA,yCAAA;AAAA,YACA,iDAAA;AAAA,YACA;AAAA,WACF;AAAA,UAEC,QAAA,EAAA;AAAA,YAAA,SAAA,mBACC,GAAA,CAAC,OAAA,EAAA,EAAQ,aAAA,EAAY,MAAA,EAAO,WAAU,qBAAA,EAAsB,CAAA,GAC1D,WAAA,mBACF,GAAA,CAAC,WAAA,EAAA,EAAY,aAAA,EAAY,MAAA,EAAO,SAAA,EAAU,UAAS,CAAA,GACjD,IAAA;AAAA,YACH,aAAA,CAAc;AAAA;AAAA;AAAA,OACjB,GACE;AAAA,KAAA,EACN,CAAA;AAAA,EAEJ;AACF;AACA,SAAA,CAAU,WAAA,GAAc,WAAA","file":"chunk-BX7A5GUV.js","sourcesContent":["import { Filter, Loader2, Search } from \"lucide-react\";\nimport { forwardRef } from \"react\";\nimport type { ElementType, HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../../../lib/cn.js\";\n\n/**\n * ActionBar — page-top action strip primitive.\n *\n * A horizontal flexbox row with three optional slots:\n * - Search input (flex-1, grows to fill)\n * - Filter icon button (next to search)\n * - Primary action button (right-aligned)\n *\n * Returns `null` when no props are provided — don't render an empty\n * bar. Used standalone or composed inside `<PageShell>` (Brief #5).\n *\n * @example\n * <ActionBar\n * search={{ placeholder: \"Search projects…\", value: q, onChange: setQ }}\n * primaryAction={{ label: \"New project\", icon: Plus, onClick: openModal }}\n * />\n */\n\nexport interface ActionBarProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n search?: {\n placeholder: string;\n value: string;\n onChange: (value: string) => void;\n };\n primaryAction?: {\n label: ReactNode;\n icon?: ElementType;\n onClick: () => void;\n loading?: boolean;\n };\n onFilterClick?: () => void;\n}\n\nconst ActionBar = forwardRef<HTMLDivElement, ActionBarProps>(\n ({ className, search, primaryAction, onFilterClick, ...props }, ref) => {\n if (!search && !primaryAction && !onFilterClick) {\n return null;\n }\n\n const PrimaryIcon = primaryAction?.icon;\n const isLoading = primaryAction?.loading === true;\n\n return (\n <div ref={ref} className={cn(\"flex w-full items-center gap-2\", className)} {...props}>\n {search ? (\n <div className=\"relative flex-1\">\n <Search\n aria-hidden=\"true\"\n className=\"-translate-y-1/2 absolute top-1/2 left-3 size-4 text-muted-foreground\"\n />\n <input\n type=\"search\"\n placeholder={search.placeholder}\n value={search.value}\n onChange={(e) => search.onChange(e.target.value)}\n className={cn(\n \"w-full rounded-md border border-border/40 bg-card py-2 pr-3 pl-9\",\n \"font-sans text-body-sm text-foreground placeholder:text-muted-foreground\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n />\n </div>\n ) : null}\n {onFilterClick !== undefined ? (\n <button\n type=\"button\"\n onClick={onFilterClick}\n aria-label=\"Filter\"\n className={cn(\n \"inline-flex size-9 items-center justify-center rounded-md border border-border/40\",\n \"text-muted-foreground transition-colors\",\n \"hover:bg-muted hover:text-foreground\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n >\n <Filter aria-hidden=\"true\" className=\"size-4\" />\n </button>\n ) : null}\n {primaryAction !== undefined ? (\n <button\n type=\"button\"\n onClick={primaryAction.onClick}\n disabled={isLoading}\n className={cn(\n \"ml-auto inline-flex items-center gap-2 rounded-md bg-primary px-3 py-2\",\n \"font-medium font-sans text-body-sm text-primary-foreground\",\n \"transition-colors hover:bg-primary-deep\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n )}\n >\n {isLoading ? (\n <Loader2 aria-hidden=\"true\" className=\"size-4 animate-spin\" />\n ) : PrimaryIcon ? (\n <PrimaryIcon aria-hidden=\"true\" className=\"size-4\" />\n ) : null}\n {primaryAction.label}\n </button>\n ) : null}\n </div>\n );\n },\n);\nActionBar.displayName = \"ActionBar\";\n\nexport { ActionBar };\n"]}
@@ -1,8 +1,8 @@
1
- import { ChatMessage } from './chunk-VI5M7KJ2.js';
2
1
  import { ApprovalCard } from './chunk-SF6R5VMQ.js';
2
+ import { ChatMessage } from './chunk-VI5M7KJ2.js';
3
3
  import { ToolCallCard } from './chunk-CWFMFKDI.js';
4
- import { AgentErrorCard } from './chunk-HGPBGLNP.js';
5
4
  import { AgentStreaming } from './chunk-ETEIDY34.js';
5
+ import { AgentErrorCard } from './chunk-HGPBGLNP.js';
6
6
  import { LiveRegionProvider } from './chunk-UGKI466V.js';
7
7
  import { cn } from './chunk-ZSRJCIWF.js';
8
8
  import { forwardRef } from 'react';
@@ -79,5 +79,5 @@ var AgentStream = forwardRef(
79
79
  AgentStream.displayName = "AgentStream";
80
80
 
81
81
  export { AgentStream };
82
- //# sourceMappingURL=chunk-H3ANHVEL.js.map
83
- //# sourceMappingURL=chunk-H3ANHVEL.js.map
82
+ //# sourceMappingURL=chunk-DKQAHZG2.js.map
83
+ //# sourceMappingURL=chunk-DKQAHZG2.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/composites/agent-stream/agent-stream.tsx"],"names":[],"mappings":";;;;;;;;;;AA2FA,IAAM,WAAA,GAAc,UAAA;AAAA,EAClB,CAAC,EAAE,SAAA,EAAW,KAAA,EAAO,GAAG,OAAM,EAAG,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAK/B,GAAA,CAAC,kBAAA,EAAA,EAAmB,KAAA,EAAO,IAAA,EACzB,QAAA,kBAAA,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,IAAA,EAAK,KAAA;AAAA,QACL,WAAA,EAAU,QAAA;AAAA,QACV,eAAA,EAAc,WAAA;AAAA,QAGd,aAAA,EAAY,OAAA;AAAA,QACZ,SAAA,EAAW,EAAA,CAAG,qBAAA,EAAuB,SAAS,CAAA;AAAA,QAC7C,GAAG,KAAA;AAAA,QAEH,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACnB,UAAA,IAAI,IAAA,CAAK,IAAA,KAAS,SAAA,EAAW,uBAAO,GAAA,CAAC,eAA0B,OAAA,EAAS,IAAA,CAAK,OAAA,EAAA,EAAvB,IAAA,CAAK,EAA2B,CAAA;AACtF,UAAA,IAAI,KAAK,IAAA,KAAS,WAAA;AAChB,YAAA,uBACE,GAAA;AAAA,cAAC,YAAA;AAAA,cAAA;AAAA,gBAEC,MAAM,IAAA,CAAK,IAAA;AAAA,gBACX,MAAM,IAAA,CAAK,IAAA;AAAA,gBACX,QAAQ,IAAA,CAAK,MAAA;AAAA,gBACb,QAAQ,IAAA,CAAK,MAAA;AAAA,gBACb,QAAQ,IAAA,CAAK,MAAA;AAAA,gBACb,iBAAiB,IAAA,CAAK,eAAA;AAAA,gBACtB,WAAW,IAAA,CAAK;AAAA,eAAA;AAAA,cAPX,IAAA,CAAK;AAAA,aAQZ;AAEJ,UAAA,IAAI,KAAK,IAAA,KAAS,UAAA;AAChB,YAAA,uBACE,GAAA;AAAA,cAAC,YAAA;AAAA,cAAA;AAAA,gBAEC,UAAU,IAAA,CAAK,QAAA;AAAA,gBACf,OAAO,IAAA,CAAK,KAAA;AAAA,gBACZ,SAAS,IAAA,CAAK,OAAA;AAAA,gBACd,aAAa,IAAA,CAAK,WAAA;AAAA,gBAClB,SAAS,IAAA,CAAK,OAAA;AAAA,gBACd,WAAW,IAAA,CAAK,SAAA;AAAA,gBAChB,QAAQ,IAAA,CAAK,MAAA;AAAA,gBACb,UAAU,IAAA,CAAK;AAAA,eAAA;AAAA,cARV,IAAA,CAAK;AAAA,aASZ;AAEJ,UAAA,IAAI,KAAK,IAAA,KAAS,OAAA;AAChB,YAAA,uBACE,GAAA;AAAA,cAAC,cAAA;AAAA,cAAA;AAAA,gBAEC,MAAM,IAAA,CAAK,SAAA;AAAA,gBACX,OAAO,IAAA,CAAK,KAAA;AAAA,gBACZ,QAAQ,IAAA,CAAK,MAAA;AAAA,gBACb,SAAS,IAAA,CAAK,OAAA;AAAA,gBACd,WAAW,IAAA,CAAK;AAAA,eAAA;AAAA,cALX,IAAA,CAAK;AAAA,aAMZ;AAEJ,UAAA,IAAI,KAAK,IAAA,KAAS,WAAA;AAChB,YAAA,uBAAO,GAAA,CAAC,kBAA6B,KAAA,EAAO,IAAA,CAAK,OAAO,OAAA,EAAS,IAAA,CAAK,OAAA,EAAA,EAA1C,IAAA,CAAK,EAA8C,CAAA;AACjF,UAAA,IAAI,IAAA,CAAK,SAAS,QAAA,EAAU,2BAAQ,KAAA,EAAA,EAAmB,QAAA,EAAA,IAAA,CAAK,IAAA,EAAA,EAAf,IAAA,CAAK,EAAe,CAAA;AACjE,UAAA,OAAO,IAAA;AAAA,QACT,CAAC;AAAA;AAAA,KACH,EACF;AAAA;AAEJ;AACA,WAAA,CAAY,WAAA,GAAc,aAAA","file":"chunk-H3ANHVEL.js","sourcesContent":["import { forwardRef } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../../../lib/cn.js\";\nimport { LiveRegionProvider } from \"../../../lib/live-region-context.js\";\nimport type { IconComponent } from \"../../../lib/types.js\";\nimport type { UIMessage } from \"../../../types/chat.js\";\nimport { AgentErrorCard, type AgentErrorKind } from \"../../primitives/agent-error-card/index.js\";\nimport { AgentStreaming } from \"../../primitives/agent-streaming/index.js\";\nimport { ToolCallCard, type ToolCallStatus } from \"../../primitives/tool-call-card/index.js\";\nimport { ApprovalCard, type ApprovalSeverity } from \"../approval-card/index.js\";\nimport { ChatMessage } from \"../chat-message/index.js\";\n\n/**\n * AgentStream — the canonical conversation surface for a code agent.\n *\n * Interleaves chat messages (user + assistant), tool invocations, approval\n * gates, errors, and the live streaming indicator. Mirrors how Claude Code\n * presents work to the user: conversation-centric, tool calls embedded,\n * approvals pause the flow inline, errors surface where they happen.\n *\n * Items are rendered in array order. The consumer fully controls the data;\n * AgentStream is a pure presentational composite over its child primitives.\n */\n\ninterface ToolCallStreamItem {\n kind: \"tool-call\";\n id: string;\n tool: ReactNode;\n icon?: IconComponent;\n target?: ReactNode;\n status: ToolCallStatus;\n output?: ReactNode;\n defaultExpanded?: boolean;\n timestamp?: ReactNode;\n}\n\ninterface ApprovalStreamItem {\n kind: \"approval\";\n id: string;\n severity?: ApprovalSeverity;\n title: ReactNode;\n request: ReactNode;\n description?: ReactNode;\n details?: ReactNode;\n onApprove?: () => void;\n onDeny?: () => void;\n onAlways?: () => void;\n}\n\ninterface ErrorStreamItem {\n kind: \"error\";\n id: string;\n errorKind?: AgentErrorKind;\n title: ReactNode;\n detail?: ReactNode;\n actions?: ReactNode;\n timestamp?: ReactNode;\n}\n\ninterface StreamingStreamItem {\n kind: \"streaming\";\n id: string;\n model?: ReactNode;\n partial?: ReactNode;\n}\n\ninterface MessageStreamItem {\n kind: \"message\";\n id: string;\n message: UIMessage;\n}\n\ninterface CustomStreamItem {\n kind: \"custom\";\n id: string;\n /** Arbitrary node — escape hatch for inline diff cards, etc. */\n node: ReactNode;\n}\n\nexport type AgentStreamItem =\n | MessageStreamItem\n | ToolCallStreamItem\n | ApprovalStreamItem\n | ErrorStreamItem\n | StreamingStreamItem\n | CustomStreamItem;\n\ninterface AgentStreamProps extends HTMLAttributes<HTMLDivElement> {\n items: AgentStreamItem[];\n}\n\nconst AgentStream = forwardRef<HTMLDivElement, AgentStreamProps>(\n ({ className, items, ...props }, ref) => (\n // T4.1 (MF-4): AgentStream is the canonical live region for the stream\n // surface. Wrap children in LiveRegionProvider so nested AgentStreaming,\n // AgentErrorCard, AutoCompactNotice, Skeleton, etc. don't declare their\n // own aria-live (which would cause double announcements).\n <LiveRegionProvider value={true}>\n <div\n ref={ref}\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n // MEDIUM-001: explicit aria-atomic=\"false\" so VoiceOver/macOS doesn't\n // reannounce the entire stream on each new item.\n aria-atomic=\"false\"\n className={cn(\"flex flex-col gap-3\", className)}\n {...props}\n >\n {items.map((item) => {\n if (item.kind === \"message\") return <ChatMessage key={item.id} message={item.message} />;\n if (item.kind === \"tool-call\")\n return (\n <ToolCallCard\n key={item.id}\n tool={item.tool}\n icon={item.icon}\n target={item.target}\n status={item.status}\n output={item.output}\n defaultExpanded={item.defaultExpanded}\n timestamp={item.timestamp}\n />\n );\n if (item.kind === \"approval\")\n return (\n <ApprovalCard\n key={item.id}\n severity={item.severity}\n title={item.title}\n request={item.request}\n description={item.description}\n details={item.details}\n onApprove={item.onApprove}\n onDeny={item.onDeny}\n onAlways={item.onAlways}\n />\n );\n if (item.kind === \"error\")\n return (\n <AgentErrorCard\n key={item.id}\n kind={item.errorKind}\n title={item.title}\n detail={item.detail}\n actions={item.actions}\n timestamp={item.timestamp}\n />\n );\n if (item.kind === \"streaming\")\n return <AgentStreaming key={item.id} model={item.model} partial={item.partial} />;\n if (item.kind === \"custom\") return <div key={item.id}>{item.node}</div>;\n return null;\n })}\n </div>\n </LiveRegionProvider>\n ),\n);\nAgentStream.displayName = \"AgentStream\";\n\nexport { AgentStream };\n"]}
1
+ {"version":3,"sources":["../src/components/composites/agent-stream/agent-stream.tsx"],"names":[],"mappings":";;;;;;;;;;AA2FA,IAAM,WAAA,GAAc,UAAA;AAAA,EAClB,CAAC,EAAE,SAAA,EAAW,KAAA,EAAO,GAAG,OAAM,EAAG,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAK/B,GAAA,CAAC,kBAAA,EAAA,EAAmB,KAAA,EAAO,IAAA,EACzB,QAAA,kBAAA,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,IAAA,EAAK,KAAA;AAAA,QACL,WAAA,EAAU,QAAA;AAAA,QACV,eAAA,EAAc,WAAA;AAAA,QAGd,aAAA,EAAY,OAAA;AAAA,QACZ,SAAA,EAAW,EAAA,CAAG,qBAAA,EAAuB,SAAS,CAAA;AAAA,QAC7C,GAAG,KAAA;AAAA,QAEH,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACnB,UAAA,IAAI,IAAA,CAAK,IAAA,KAAS,SAAA,EAAW,uBAAO,GAAA,CAAC,eAA0B,OAAA,EAAS,IAAA,CAAK,OAAA,EAAA,EAAvB,IAAA,CAAK,EAA2B,CAAA;AACtF,UAAA,IAAI,KAAK,IAAA,KAAS,WAAA;AAChB,YAAA,uBACE,GAAA;AAAA,cAAC,YAAA;AAAA,cAAA;AAAA,gBAEC,MAAM,IAAA,CAAK,IAAA;AAAA,gBACX,MAAM,IAAA,CAAK,IAAA;AAAA,gBACX,QAAQ,IAAA,CAAK,MAAA;AAAA,gBACb,QAAQ,IAAA,CAAK,MAAA;AAAA,gBACb,QAAQ,IAAA,CAAK,MAAA;AAAA,gBACb,iBAAiB,IAAA,CAAK,eAAA;AAAA,gBACtB,WAAW,IAAA,CAAK;AAAA,eAAA;AAAA,cAPX,IAAA,CAAK;AAAA,aAQZ;AAEJ,UAAA,IAAI,KAAK,IAAA,KAAS,UAAA;AAChB,YAAA,uBACE,GAAA;AAAA,cAAC,YAAA;AAAA,cAAA;AAAA,gBAEC,UAAU,IAAA,CAAK,QAAA;AAAA,gBACf,OAAO,IAAA,CAAK,KAAA;AAAA,gBACZ,SAAS,IAAA,CAAK,OAAA;AAAA,gBACd,aAAa,IAAA,CAAK,WAAA;AAAA,gBAClB,SAAS,IAAA,CAAK,OAAA;AAAA,gBACd,WAAW,IAAA,CAAK,SAAA;AAAA,gBAChB,QAAQ,IAAA,CAAK,MAAA;AAAA,gBACb,UAAU,IAAA,CAAK;AAAA,eAAA;AAAA,cARV,IAAA,CAAK;AAAA,aASZ;AAEJ,UAAA,IAAI,KAAK,IAAA,KAAS,OAAA;AAChB,YAAA,uBACE,GAAA;AAAA,cAAC,cAAA;AAAA,cAAA;AAAA,gBAEC,MAAM,IAAA,CAAK,SAAA;AAAA,gBACX,OAAO,IAAA,CAAK,KAAA;AAAA,gBACZ,QAAQ,IAAA,CAAK,MAAA;AAAA,gBACb,SAAS,IAAA,CAAK,OAAA;AAAA,gBACd,WAAW,IAAA,CAAK;AAAA,eAAA;AAAA,cALX,IAAA,CAAK;AAAA,aAMZ;AAEJ,UAAA,IAAI,KAAK,IAAA,KAAS,WAAA;AAChB,YAAA,uBAAO,GAAA,CAAC,kBAA6B,KAAA,EAAO,IAAA,CAAK,OAAO,OAAA,EAAS,IAAA,CAAK,OAAA,EAAA,EAA1C,IAAA,CAAK,EAA8C,CAAA;AACjF,UAAA,IAAI,IAAA,CAAK,SAAS,QAAA,EAAU,2BAAQ,KAAA,EAAA,EAAmB,QAAA,EAAA,IAAA,CAAK,IAAA,EAAA,EAAf,IAAA,CAAK,EAAe,CAAA;AACjE,UAAA,OAAO,IAAA;AAAA,QACT,CAAC;AAAA;AAAA,KACH,EACF;AAAA;AAEJ;AACA,WAAA,CAAY,WAAA,GAAc,aAAA","file":"chunk-DKQAHZG2.js","sourcesContent":["import { forwardRef } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../../../lib/cn.js\";\nimport { LiveRegionProvider } from \"../../../lib/live-region-context.js\";\nimport type { IconComponent } from \"../../../lib/types.js\";\nimport type { UIMessage } from \"../../../types/chat.js\";\nimport { AgentErrorCard, type AgentErrorKind } from \"../../primitives/agent-error-card/index.js\";\nimport { AgentStreaming } from \"../../primitives/agent-streaming/index.js\";\nimport { ToolCallCard, type ToolCallStatus } from \"../../primitives/tool-call-card/index.js\";\nimport { ApprovalCard, type ApprovalSeverity } from \"../approval-card/index.js\";\nimport { ChatMessage } from \"../chat-message/index.js\";\n\n/**\n * AgentStream — the canonical conversation surface for a code agent.\n *\n * Interleaves chat messages (user + assistant), tool invocations, approval\n * gates, errors, and the live streaming indicator. Mirrors how Claude Code\n * presents work to the user: conversation-centric, tool calls embedded,\n * approvals pause the flow inline, errors surface where they happen.\n *\n * Items are rendered in array order. The consumer fully controls the data;\n * AgentStream is a pure presentational composite over its child primitives.\n */\n\ninterface ToolCallStreamItem {\n kind: \"tool-call\";\n id: string;\n tool: ReactNode;\n icon?: IconComponent;\n target?: ReactNode;\n status: ToolCallStatus;\n output?: ReactNode;\n defaultExpanded?: boolean;\n timestamp?: ReactNode;\n}\n\ninterface ApprovalStreamItem {\n kind: \"approval\";\n id: string;\n severity?: ApprovalSeverity;\n title: ReactNode;\n request: ReactNode;\n description?: ReactNode;\n details?: ReactNode;\n onApprove?: () => void;\n onDeny?: () => void;\n onAlways?: () => void;\n}\n\ninterface ErrorStreamItem {\n kind: \"error\";\n id: string;\n errorKind?: AgentErrorKind;\n title: ReactNode;\n detail?: ReactNode;\n actions?: ReactNode;\n timestamp?: ReactNode;\n}\n\ninterface StreamingStreamItem {\n kind: \"streaming\";\n id: string;\n model?: ReactNode;\n partial?: ReactNode;\n}\n\ninterface MessageStreamItem {\n kind: \"message\";\n id: string;\n message: UIMessage;\n}\n\ninterface CustomStreamItem {\n kind: \"custom\";\n id: string;\n /** Arbitrary node — escape hatch for inline diff cards, etc. */\n node: ReactNode;\n}\n\nexport type AgentStreamItem =\n | MessageStreamItem\n | ToolCallStreamItem\n | ApprovalStreamItem\n | ErrorStreamItem\n | StreamingStreamItem\n | CustomStreamItem;\n\ninterface AgentStreamProps extends HTMLAttributes<HTMLDivElement> {\n items: AgentStreamItem[];\n}\n\nconst AgentStream = forwardRef<HTMLDivElement, AgentStreamProps>(\n ({ className, items, ...props }, ref) => (\n // T4.1 (MF-4): AgentStream is the canonical live region for the stream\n // surface. Wrap children in LiveRegionProvider so nested AgentStreaming,\n // AgentErrorCard, AutoCompactNotice, Skeleton, etc. don't declare their\n // own aria-live (which would cause double announcements).\n <LiveRegionProvider value={true}>\n <div\n ref={ref}\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n // MEDIUM-001: explicit aria-atomic=\"false\" so VoiceOver/macOS doesn't\n // reannounce the entire stream on each new item.\n aria-atomic=\"false\"\n className={cn(\"flex flex-col gap-3\", className)}\n {...props}\n >\n {items.map((item) => {\n if (item.kind === \"message\") return <ChatMessage key={item.id} message={item.message} />;\n if (item.kind === \"tool-call\")\n return (\n <ToolCallCard\n key={item.id}\n tool={item.tool}\n icon={item.icon}\n target={item.target}\n status={item.status}\n output={item.output}\n defaultExpanded={item.defaultExpanded}\n timestamp={item.timestamp}\n />\n );\n if (item.kind === \"approval\")\n return (\n <ApprovalCard\n key={item.id}\n severity={item.severity}\n title={item.title}\n request={item.request}\n description={item.description}\n details={item.details}\n onApprove={item.onApprove}\n onDeny={item.onDeny}\n onAlways={item.onAlways}\n />\n );\n if (item.kind === \"error\")\n return (\n <AgentErrorCard\n key={item.id}\n kind={item.errorKind}\n title={item.title}\n detail={item.detail}\n actions={item.actions}\n timestamp={item.timestamp}\n />\n );\n if (item.kind === \"streaming\")\n return <AgentStreaming key={item.id} model={item.model} partial={item.partial} />;\n if (item.kind === \"custom\") return <div key={item.id}>{item.node}</div>;\n return null;\n })}\n </div>\n </LiveRegionProvider>\n ),\n);\nAgentStream.displayName = \"AgentStream\";\n\nexport { AgentStream };\n"]}
@@ -1,6 +1,6 @@
1
1
  import { ALL_MODES, MODE_LABEL } from './chunk-VM4RMQQN.js';
2
- import { Switch } from './chunk-3HOXC25T.js';
3
2
  import { Textarea } from './chunk-WWNH5ENT.js';
3
+ import { Switch } from './chunk-3HOXC25T.js';
4
4
  import { Select } from './chunk-EP25QJ4N.js';
5
5
  import { FormField } from './chunk-TK24HQJJ.js';
6
6
  import { Input } from './chunk-H3VJMFJQ.js';
@@ -182,5 +182,5 @@ function SkillEditor({
182
182
  }
183
183
 
184
184
  export { SkillEditor };
185
- //# sourceMappingURL=chunk-DAKIL5PC.js.map
186
- //# sourceMappingURL=chunk-DAKIL5PC.js.map
185
+ //# sourceMappingURL=chunk-IPEYGWA7.js.map
186
+ //# sourceMappingURL=chunk-IPEYGWA7.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/composites/skill-editor/skill-editor.tsx"],"names":[],"mappings":"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","file":"chunk-DAKIL5PC.js","sourcesContent":["import { useState } from \"react\";\nimport type { FormEvent, HTMLAttributes } from \"react\";\nimport { cn } from \"../../../lib/cn.js\";\nimport { ALL_MODES, MODE_LABEL, type Mode } from \"../../../types/mode.js\";\nimport { Button } from \"../../primitives/button/index.js\";\nimport { FormField } from \"../../primitives/form-field/index.js\";\nimport { Input } from \"../../primitives/input/index.js\";\nimport { Select } from \"../../primitives/select/index.js\";\nimport type { Skill, SkillSource, SkillState } from \"../../primitives/skill-card/index.js\";\nimport { Switch } from \"../../primitives/switch/index.js\";\nimport { Textarea } from \"../../primitives/textarea/index.js\";\n\n/**\n * SkillEditor — form for creating or editing a Skill.\n */\n\ntype SkillDraft = Omit<Skill, \"id\"> & {\n id?: string;\n instructions?: string;\n};\n\ninterface SkillEditorProps extends Omit<HTMLAttributes<HTMLFormElement>, \"onSubmit\" | \"onChange\"> {\n initial?: Partial<Skill> & { instructions?: string };\n onSave: (draft: SkillDraft) => void;\n onCancel?: () => void;\n onDelete?: () => void;\n}\n\nconst SOURCES: Array<{ id: SkillSource; label: string }> = [\n { id: \"user\", label: \"User — defined by you\" },\n { id: \"project\", label: \"Project — lives in this workspace\" },\n { id: \"plugin\", label: \"Plugin — imported from a package\" },\n { id: \"builtin\", label: \"Built-in — shipped with Theo\" },\n];\n\nexport function SkillEditor({\n className,\n initial,\n onSave,\n onCancel,\n onDelete,\n ...formProps\n}: SkillEditorProps) {\n const [name, setName] = useState(initial?.name ?? \"\");\n const [description, setDescription] = useState(\n typeof initial?.description === \"string\" ? initial.description : \"\",\n );\n const [instructions, setInstructions] = useState(initial?.instructions ?? \"\");\n const [source, setSource] = useState<SkillSource>(initial?.source ?? \"user\");\n const [allowedToolsRaw, setAllowedToolsRaw] = useState(initial?.allowedTools?.join(\", \") ?? \"\");\n const [triggersRaw, setTriggersRaw] = useState(initial?.triggers?.join(\", \") ?? \"\");\n const [enabled, setEnabled] = useState<SkillState>(initial?.state ?? \"enabled\");\n const [modes, setModes] = useState<Mode[]>(initial?.modes ?? []);\n\n // Note: state is only seeded once on mount. To reset the form when editing\n // a different skill, use the React `key` pattern at the call site:\n // <SkillEditor key={skill.id} initial={skill} ... />\n const toggleMode = (m: Mode) =>\n setModes((prev) => (prev.includes(m) ? prev.filter((x) => x !== m) : [...prev, m]));\n\n const canSave = name.trim().length > 0;\n const handleSubmit = (e: FormEvent) => {\n e.preventDefault();\n if (!canSave) return;\n onSave({\n id: initial?.id,\n name: name.trim(),\n description: description.trim() || undefined,\n instructions: instructions.trim() || undefined,\n source,\n state: enabled,\n allowedTools: allowedToolsRaw\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean),\n triggers: triggersRaw\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean),\n modes: modes.length > 0 ? modes : undefined,\n });\n };\n\n return (\n <form\n onSubmit={handleSubmit}\n className={cn(\"flex h-full flex-col gap-4\", className)}\n {...formProps}\n >\n <FormField>\n <FormField.Label>Name</FormField.Label>\n <FormField.Control>\n <Input\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"diff-explainer\"\n required\n />\n </FormField.Control>\n <FormField.Hint>Kebab-case identifier the agent uses to invoke this skill.</FormField.Hint>\n </FormField>\n\n <FormField>\n <FormField.Label>Description</FormField.Label>\n <FormField.Control>\n <Input\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Explain a diff in plain English.\"\n />\n </FormField.Control>\n </FormField>\n\n <FormField className=\"flex-1\">\n <FormField.Label>Instructions</FormField.Label>\n <FormField.Control>\n <Textarea\n value={instructions}\n onChange={(e) => setInstructions(e.target.value)}\n rows={6}\n placeholder=\"When invoked, read the diff via Read or Grep and produce a concise summary of intent + risk.\"\n className=\"min-h-[10rem] flex-1 font-mono text-code-sm\"\n />\n </FormField.Control>\n <FormField.Hint>Sub-prompt loaded when the skill is invoked.</FormField.Hint>\n </FormField>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <FormField>\n <FormField.Label>Source</FormField.Label>\n <FormField.Control>\n <Select\n value={source}\n onValueChange={(v) => {\n // Re-audit Issue 7: narrow Select string value against\n // SOURCES.id before casting. Silent no-op for unknown.\n const next = SOURCES.find((s) => s.id === v);\n if (next) setSource(next.id);\n }}\n >\n <Select.Trigger aria-label=\"Select skill source\">\n <Select.Value />\n </Select.Trigger>\n <Select.Content>\n {SOURCES.map((s) => (\n <Select.Item key={s.id} value={s.id}>\n {s.label}\n </Select.Item>\n ))}\n </Select.Content>\n </Select>\n </FormField.Control>\n </FormField>\n <FormField>\n <FormField.Label>Allowed tools</FormField.Label>\n <FormField.Control>\n <Input\n value={allowedToolsRaw}\n onChange={(e) => setAllowedToolsRaw(e.target.value)}\n placeholder=\"Read, Grep, Bash\"\n />\n </FormField.Control>\n </FormField>\n </div>\n\n <FormField>\n <FormField.Label>Triggers</FormField.Label>\n <FormField.Control>\n <Input\n value={triggersRaw}\n onChange={(e) => setTriggersRaw(e.target.value)}\n placeholder=\"explain diff, summarize change\"\n />\n </FormField.Control>\n <FormField.Hint>Optional keywords / patterns for auto-discovery.</FormField.Hint>\n </FormField>\n\n <FormField>\n <FormField.Label>Active modes</FormField.Label>\n <div className=\"flex flex-wrap gap-1.5\">\n {ALL_MODES.map((m) => {\n const on = modes.includes(m);\n return (\n <button\n key={m}\n type=\"button\"\n onClick={() => toggleMode(m)}\n aria-pressed={on}\n className={cn(\n \"inline-flex h-7 items-center rounded-full border px-3 font-mono text-body-sm transition-colors\",\n on\n ? \"border-primary bg-primary/15 text-primary\"\n : \"border-border/60 bg-card text-muted-foreground hover:text-foreground\",\n )}\n >\n {MODE_LABEL[m]}\n </button>\n );\n })}\n </div>\n <FormField.Hint>\n {modes.length === 0\n ? \"Empty = global (available in every mode).\"\n : `Only visible in: ${modes.map((m) => MODE_LABEL[m]).join(\", \")}.`}\n </FormField.Hint>\n </FormField>\n\n <div className=\"flex items-center gap-3\">\n <Switch\n checked={enabled === \"enabled\"}\n onCheckedChange={(v) => setEnabled(v ? \"enabled\" : \"disabled\")}\n aria-label=\"Enabled\"\n />\n <span className=\"text-body-sm text-muted-foreground\">\n {enabled === \"enabled\"\n ? \"Enabled — available to the agent.\"\n : \"Disabled — kept but hidden from the agent.\"}\n </span>\n </div>\n\n <footer className=\"flex items-center justify-between gap-2 border-border/40 border-t pt-4\">\n <div>\n {onDelete ? (\n <Button type=\"button\" variant=\"ghost\" onClick={onDelete}>\n Delete\n </Button>\n ) : null}\n </div>\n <div className=\"flex items-center gap-2\">\n {onCancel ? (\n <Button type=\"button\" variant=\"secondary\" onClick={onCancel}>\n Cancel\n </Button>\n ) : null}\n <Button type=\"submit\" disabled={!canSave}>\n {initial?.id ? \"Save changes\" : \"Create skill\"}\n </Button>\n </div>\n </footer>\n </form>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/components/composites/skill-editor/skill-editor.tsx"],"names":[],"mappings":"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","file":"chunk-IPEYGWA7.js","sourcesContent":["import { useState } from \"react\";\nimport type { FormEvent, HTMLAttributes } from \"react\";\nimport { cn } from \"../../../lib/cn.js\";\nimport { ALL_MODES, MODE_LABEL, type Mode } from \"../../../types/mode.js\";\nimport { Button } from \"../../primitives/button/index.js\";\nimport { FormField } from \"../../primitives/form-field/index.js\";\nimport { Input } from \"../../primitives/input/index.js\";\nimport { Select } from \"../../primitives/select/index.js\";\nimport type { Skill, SkillSource, SkillState } from \"../../primitives/skill-card/index.js\";\nimport { Switch } from \"../../primitives/switch/index.js\";\nimport { Textarea } from \"../../primitives/textarea/index.js\";\n\n/**\n * SkillEditor — form for creating or editing a Skill.\n */\n\ntype SkillDraft = Omit<Skill, \"id\"> & {\n id?: string;\n instructions?: string;\n};\n\ninterface SkillEditorProps extends Omit<HTMLAttributes<HTMLFormElement>, \"onSubmit\" | \"onChange\"> {\n initial?: Partial<Skill> & { instructions?: string };\n onSave: (draft: SkillDraft) => void;\n onCancel?: () => void;\n onDelete?: () => void;\n}\n\nconst SOURCES: Array<{ id: SkillSource; label: string }> = [\n { id: \"user\", label: \"User — defined by you\" },\n { id: \"project\", label: \"Project — lives in this workspace\" },\n { id: \"plugin\", label: \"Plugin — imported from a package\" },\n { id: \"builtin\", label: \"Built-in — shipped with Theo\" },\n];\n\nexport function SkillEditor({\n className,\n initial,\n onSave,\n onCancel,\n onDelete,\n ...formProps\n}: SkillEditorProps) {\n const [name, setName] = useState(initial?.name ?? \"\");\n const [description, setDescription] = useState(\n typeof initial?.description === \"string\" ? initial.description : \"\",\n );\n const [instructions, setInstructions] = useState(initial?.instructions ?? \"\");\n const [source, setSource] = useState<SkillSource>(initial?.source ?? \"user\");\n const [allowedToolsRaw, setAllowedToolsRaw] = useState(initial?.allowedTools?.join(\", \") ?? \"\");\n const [triggersRaw, setTriggersRaw] = useState(initial?.triggers?.join(\", \") ?? \"\");\n const [enabled, setEnabled] = useState<SkillState>(initial?.state ?? \"enabled\");\n const [modes, setModes] = useState<Mode[]>(initial?.modes ?? []);\n\n // Note: state is only seeded once on mount. To reset the form when editing\n // a different skill, use the React `key` pattern at the call site:\n // <SkillEditor key={skill.id} initial={skill} ... />\n const toggleMode = (m: Mode) =>\n setModes((prev) => (prev.includes(m) ? prev.filter((x) => x !== m) : [...prev, m]));\n\n const canSave = name.trim().length > 0;\n const handleSubmit = (e: FormEvent) => {\n e.preventDefault();\n if (!canSave) return;\n onSave({\n id: initial?.id,\n name: name.trim(),\n description: description.trim() || undefined,\n instructions: instructions.trim() || undefined,\n source,\n state: enabled,\n allowedTools: allowedToolsRaw\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean),\n triggers: triggersRaw\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean),\n modes: modes.length > 0 ? modes : undefined,\n });\n };\n\n return (\n <form\n onSubmit={handleSubmit}\n className={cn(\"flex h-full flex-col gap-4\", className)}\n {...formProps}\n >\n <FormField>\n <FormField.Label>Name</FormField.Label>\n <FormField.Control>\n <Input\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"diff-explainer\"\n required\n />\n </FormField.Control>\n <FormField.Hint>Kebab-case identifier the agent uses to invoke this skill.</FormField.Hint>\n </FormField>\n\n <FormField>\n <FormField.Label>Description</FormField.Label>\n <FormField.Control>\n <Input\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Explain a diff in plain English.\"\n />\n </FormField.Control>\n </FormField>\n\n <FormField className=\"flex-1\">\n <FormField.Label>Instructions</FormField.Label>\n <FormField.Control>\n <Textarea\n value={instructions}\n onChange={(e) => setInstructions(e.target.value)}\n rows={6}\n placeholder=\"When invoked, read the diff via Read or Grep and produce a concise summary of intent + risk.\"\n className=\"min-h-[10rem] flex-1 font-mono text-code-sm\"\n />\n </FormField.Control>\n <FormField.Hint>Sub-prompt loaded when the skill is invoked.</FormField.Hint>\n </FormField>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <FormField>\n <FormField.Label>Source</FormField.Label>\n <FormField.Control>\n <Select\n value={source}\n onValueChange={(v) => {\n // Re-audit Issue 7: narrow Select string value against\n // SOURCES.id before casting. Silent no-op for unknown.\n const next = SOURCES.find((s) => s.id === v);\n if (next) setSource(next.id);\n }}\n >\n <Select.Trigger aria-label=\"Select skill source\">\n <Select.Value />\n </Select.Trigger>\n <Select.Content>\n {SOURCES.map((s) => (\n <Select.Item key={s.id} value={s.id}>\n {s.label}\n </Select.Item>\n ))}\n </Select.Content>\n </Select>\n </FormField.Control>\n </FormField>\n <FormField>\n <FormField.Label>Allowed tools</FormField.Label>\n <FormField.Control>\n <Input\n value={allowedToolsRaw}\n onChange={(e) => setAllowedToolsRaw(e.target.value)}\n placeholder=\"Read, Grep, Bash\"\n />\n </FormField.Control>\n </FormField>\n </div>\n\n <FormField>\n <FormField.Label>Triggers</FormField.Label>\n <FormField.Control>\n <Input\n value={triggersRaw}\n onChange={(e) => setTriggersRaw(e.target.value)}\n placeholder=\"explain diff, summarize change\"\n />\n </FormField.Control>\n <FormField.Hint>Optional keywords / patterns for auto-discovery.</FormField.Hint>\n </FormField>\n\n <FormField>\n <FormField.Label>Active modes</FormField.Label>\n <div className=\"flex flex-wrap gap-1.5\">\n {ALL_MODES.map((m) => {\n const on = modes.includes(m);\n return (\n <button\n key={m}\n type=\"button\"\n onClick={() => toggleMode(m)}\n aria-pressed={on}\n className={cn(\n \"inline-flex h-7 items-center rounded-full border px-3 font-mono text-body-sm transition-colors\",\n on\n ? \"border-primary bg-primary/15 text-primary\"\n : \"border-border/60 bg-card text-muted-foreground hover:text-foreground\",\n )}\n >\n {MODE_LABEL[m]}\n </button>\n );\n })}\n </div>\n <FormField.Hint>\n {modes.length === 0\n ? \"Empty = global (available in every mode).\"\n : `Only visible in: ${modes.map((m) => MODE_LABEL[m]).join(\", \")}.`}\n </FormField.Hint>\n </FormField>\n\n <div className=\"flex items-center gap-3\">\n <Switch\n checked={enabled === \"enabled\"}\n onCheckedChange={(v) => setEnabled(v ? \"enabled\" : \"disabled\")}\n aria-label=\"Enabled\"\n />\n <span className=\"text-body-sm text-muted-foreground\">\n {enabled === \"enabled\"\n ? \"Enabled — available to the agent.\"\n : \"Disabled — kept but hidden from the agent.\"}\n </span>\n </div>\n\n <footer className=\"flex items-center justify-between gap-2 border-border/40 border-t pt-4\">\n <div>\n {onDelete ? (\n <Button type=\"button\" variant=\"ghost\" onClick={onDelete}>\n Delete\n </Button>\n ) : null}\n </div>\n <div className=\"flex items-center gap-2\">\n {onCancel ? (\n <Button type=\"button\" variant=\"secondary\" onClick={onCancel}>\n Cancel\n </Button>\n ) : null}\n <Button type=\"submit\" disabled={!canSave}>\n {initial?.id ? \"Save changes\" : \"Create skill\"}\n </Button>\n </div>\n </footer>\n </form>\n );\n}\n"]}