@corbits/react-ui 0.1.0 → 0.3.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 (170) hide show
  1. package/LICENSE +174 -499
  2. package/README.md +228 -85
  3. package/dist/blocks/login/auth-layout.d.ts +1 -1
  4. package/dist/blocks/login/auth-layout.js +3 -33
  5. package/dist/blocks/login/login-form.js +6 -119
  6. package/dist/hooks/use-command-palette-navigation.js +18 -17
  7. package/dist/hooks/use-controllable-state.d.ts +2 -2
  8. package/dist/hooks/use-controllable-state.js +23 -15
  9. package/dist/hooks/use-delayed-autofocus.js +10 -11
  10. package/dist/hooks/use-dismissable-popover.d.ts +2 -2
  11. package/dist/hooks/use-dismissable-popover.js +21 -29
  12. package/dist/hooks/use-list-selection.js +54 -85
  13. package/dist/hooks/use-prefers-reduced-motion.js +8 -4
  14. package/dist/hooks/use-scroll-current-into-view.js +5 -7
  15. package/dist/index.d.ts +0 -11
  16. package/dist/index.js +10 -19
  17. package/dist/lib/artifact.js +16 -35
  18. package/dist/lib/chart-geometry.js +33 -20
  19. package/dist/lib/chart-palette.js +17 -31
  20. package/dist/lib/chat-message.js +32 -13
  21. package/dist/lib/chat-parts.js +2 -1
  22. package/dist/lib/csv.js +42 -26
  23. package/dist/lib/metrics.js +30 -48
  24. package/dist/lib/now-item.js +26 -25
  25. package/dist/lib/relative-time.js +14 -29
  26. package/dist/lib/theme.js +32 -30
  27. package/dist/lib/url.js +9 -8
  28. package/dist/lib/workflow-display-flow.js +37 -33
  29. package/dist/lib/workflow-registry.d.ts +0 -41
  30. package/dist/lib/workflow-registry.js +13 -23
  31. package/dist/lib/workflow-run-error.js +16 -20
  32. package/dist/lib/workflow-run-progress.js +94 -52
  33. package/dist/lib/workflow-run.js +17 -13
  34. package/dist/styles.css +257 -19
  35. package/dist/test/render-hook.js +11 -12
  36. package/dist/theme.css +92 -14
  37. package/dist/ui/agent-turn.d.ts +1 -1
  38. package/dist/ui/agent-turn.js +4 -37
  39. package/dist/ui/animated-number.js +15 -28
  40. package/dist/ui/approval-card.js +5 -90
  41. package/dist/ui/artifact-body.js +47 -125
  42. package/dist/ui/artifact-notice.d.ts +1 -1
  43. package/dist/ui/artifact-notice.js +9 -16
  44. package/dist/ui/avatar.d.ts +21 -4
  45. package/dist/ui/avatar.js +40 -49
  46. package/dist/ui/badge.js +4 -12
  47. package/dist/ui/bar-chart.d.ts +1 -1
  48. package/dist/ui/bar-chart.js +22 -74
  49. package/dist/ui/block-card.js +7 -45
  50. package/dist/ui/boot-screen.d.ts +1 -1
  51. package/dist/ui/boot-screen.js +4 -33
  52. package/dist/ui/bulk-action-bar.d.ts +1 -1
  53. package/dist/ui/bulk-action-bar.js +14 -33
  54. package/dist/ui/button.js +10 -24
  55. package/dist/ui/card.js +5 -25
  56. package/dist/ui/category-bars.js +14 -59
  57. package/dist/ui/chart-frame.d.ts +2 -2
  58. package/dist/ui/chart-frame.js +6 -85
  59. package/dist/ui/chat-composer.js +4 -16
  60. package/dist/ui/chat-dock-timing.js +7 -3
  61. package/dist/ui/chat-input.js +20 -100
  62. package/dist/ui/chat-panel.d.ts +1 -1
  63. package/dist/ui/chat-panel.js +7 -41
  64. package/dist/ui/chat-thread.js +13 -31
  65. package/dist/ui/checkbox.js +10 -52
  66. package/dist/ui/command-palette.js +85 -246
  67. package/dist/ui/compare-body.d.ts +1 -1
  68. package/dist/ui/compare-body.js +28 -98
  69. package/dist/ui/confirm-button.js +8 -15
  70. package/dist/ui/corbits-mark.js +7 -16
  71. package/dist/ui/csv-table.d.ts +1 -1
  72. package/dist/ui/csv-table.js +8 -50
  73. package/dist/ui/dialog.d.ts +1 -1
  74. package/dist/ui/dialog.js +32 -62
  75. package/dist/ui/dictation-status-line.js +7 -25
  76. package/dist/ui/dither-canvas.d.ts +1 -1
  77. package/dist/ui/dither-canvas.js +31 -56
  78. package/dist/ui/embed-body.d.ts +1 -1
  79. package/dist/ui/embed-body.js +7 -56
  80. package/dist/ui/empty-state.d.ts +1 -1
  81. package/dist/ui/empty-state.js +3 -26
  82. package/dist/ui/file-input.js +17 -37
  83. package/dist/ui/filter-bar.d.ts +1 -1
  84. package/dist/ui/filter-bar.js +11 -71
  85. package/dist/ui/filter-chip.d.ts +1 -1
  86. package/dist/ui/filter-chip.js +7 -15
  87. package/dist/ui/gate-block.d.ts +2 -1
  88. package/dist/ui/gate-block.js +4 -29
  89. package/dist/ui/horizontal-stepper.d.ts +1 -1
  90. package/dist/ui/horizontal-stepper.js +18 -45
  91. package/dist/ui/input.js +1 -6
  92. package/dist/ui/inspector-shell.d.ts +5 -5
  93. package/dist/ui/inspector-shell.js +14 -81
  94. package/dist/ui/intake-form.d.ts +1 -1
  95. package/dist/ui/intake-form.js +21 -97
  96. package/dist/ui/kind-card-grid.js +9 -31
  97. package/dist/ui/library-page-header.js +4 -32
  98. package/dist/ui/list-detail.js +5 -35
  99. package/dist/ui/live-run-banner.d.ts +1 -1
  100. package/dist/ui/live-run-banner.js +5 -15
  101. package/dist/ui/live-status-line.js +6 -24
  102. package/dist/ui/menu.js +7 -24
  103. package/dist/ui/message-bubble.d.ts +3 -3
  104. package/dist/ui/message-bubble.js +5 -44
  105. package/dist/ui/mic-button.d.ts +1 -1
  106. package/dist/ui/mic-button.js +15 -21
  107. package/dist/ui/mic-permission-dialog.js +5 -41
  108. package/dist/ui/notifications-bell.js +9 -43
  109. package/dist/ui/now-cards.d.ts +2 -2
  110. package/dist/ui/now-cards.js +16 -94
  111. package/dist/ui/page-panel.js +11 -11
  112. package/dist/ui/page-shell.js +4 -11
  113. package/dist/ui/parts-renderer.js +51 -149
  114. package/dist/ui/profile-card.js +7 -111
  115. package/dist/ui/progress-checklist.js +11 -49
  116. package/dist/ui/provider-mark.d.ts +2 -2
  117. package/dist/ui/provider-mark.js +9 -16
  118. package/dist/ui/quick-reply-chips.js +6 -14
  119. package/dist/ui/quote-card.js +12 -32
  120. package/dist/ui/reasoning-block.d.ts +1 -1
  121. package/dist/ui/reasoning-block.js +14 -33
  122. package/dist/ui/research-body.js +32 -213
  123. package/dist/ui/rich-empty-state.js +6 -48
  124. package/dist/ui/run-now-button.js +14 -15
  125. package/dist/ui/section.d.ts +1 -1
  126. package/dist/ui/section.js +4 -40
  127. package/dist/ui/select.js +1 -15
  128. package/dist/ui/selection-checkbox.d.ts +1 -1
  129. package/dist/ui/selection-checkbox.js +11 -29
  130. package/dist/ui/settings-panel.js +7 -50
  131. package/dist/ui/shimmer-text.js +3 -6
  132. package/dist/ui/sidebar-item-row.d.ts +1 -1
  133. package/dist/ui/sidebar-item-row.js +9 -39
  134. package/dist/ui/sidebar-panel.js +15 -40
  135. package/dist/ui/sidebar.js +15 -76
  136. package/dist/ui/skeleton.js +3 -7
  137. package/dist/ui/sortable-table.js +29 -72
  138. package/dist/ui/sparkline.js +14 -38
  139. package/dist/ui/stat-grid.js +17 -52
  140. package/dist/ui/status-dot.d.ts +1 -1
  141. package/dist/ui/status-dot.js +8 -20
  142. package/dist/ui/step-list.d.ts +1 -0
  143. package/dist/ui/step-list.js +11 -50
  144. package/dist/ui/switch.js +4 -16
  145. package/dist/ui/table.js +7 -32
  146. package/dist/ui/tabs.js +39 -54
  147. package/dist/ui/textarea.js +10 -19
  148. package/dist/ui/theme-provider.js +54 -78
  149. package/dist/ui/theme-toggle.js +13 -16
  150. package/dist/ui/thinking-indicator.js +3 -15
  151. package/dist/ui/thinking-label.js +16 -35
  152. package/dist/ui/thinking-mark.d.ts +1 -1
  153. package/dist/ui/thinking-mark.js +4 -145
  154. package/dist/ui/time-series-chart.js +43 -174
  155. package/dist/ui/toast.js +12 -20
  156. package/dist/ui/token-mosaic.js +5 -43
  157. package/dist/ui/tool-block.js +18 -70
  158. package/dist/ui/tool-narrative.d.ts +1 -1
  159. package/dist/ui/tool-narrative.js +10 -57
  160. package/dist/ui/tool-picker.js +57 -145
  161. package/dist/ui/tooltip.d.ts +1 -1
  162. package/dist/ui/tooltip.js +4 -33
  163. package/dist/ui/top-bar.js +14 -72
  164. package/dist/ui/trace-waterfall.d.ts +1 -1
  165. package/dist/ui/trace-waterfall.js +21 -93
  166. package/dist/ui/typing-indicator.d.ts +1 -1
  167. package/dist/ui/typing-indicator.js +4 -18
  168. package/dist/ui/view-toggle.js +10 -29
  169. package/dist/ui/voice-waveform.js +3 -11
  170. package/package.json +96 -535
@@ -6,72 +6,33 @@ const STATE_LABEL = {
6
6
  pending: "Pending",
7
7
  active: "Now",
8
8
  done: "Done",
9
- failed: "Failed"
9
+ failed: "Failed",
10
10
  };
11
11
  const MARK_CLASS = {
12
12
  pending: "border-input text-muted-foreground",
13
13
  active: "border-primary-emphasis bg-primary/10 text-primary-emphasis",
14
14
  done: "border-transparent bg-success text-success-foreground",
15
- failed: "border-transparent bg-destructive text-destructive-foreground"
15
+ failed: "border-transparent bg-destructive text-destructive-foreground",
16
16
  };
17
17
  const LABEL_CLASS = {
18
18
  pending: "text-muted-foreground",
19
19
  active: "font-medium text-primary-emphasis",
20
20
  done: "text-foreground",
21
- failed: "font-medium text-destructive"
21
+ failed: "font-medium text-destructive",
22
22
  };
23
23
  /** One step row — a mark carrying its own glyph and colour, a name, and the
24
- * status spelled out in text so it never depends on colour alone. */ export function StepListRow({ step, className }) {
25
- return /*#__PURE__*/ _jsxs("li", {
26
- "data-step-status": step.status,
27
- className: cn("grid grid-cols-[20px_minmax(0,1fr)_auto] items-start gap-2 py-1.5", className),
28
- children: [
29
- /*#__PURE__*/ _jsx("span", {
30
- "aria-hidden": true,
31
- className: cn("mt-0.5 grid size-5 place-items-center rounded-sm border text-[10px] font-semibold", MARK_CLASS[step.status]),
32
- children: step.status === "done" ? /*#__PURE__*/ _jsx(Check, {
33
- className: "size-3"
34
- }) : step.status === "failed" ? /*#__PURE__*/ _jsx(X, {
35
- className: "size-3"
36
- }) : step.status === "active" ? /*#__PURE__*/ _jsx(StatusDot, {
37
- label: "",
38
- tone: "emphasis",
39
- live: true
40
- }) : null
41
- }),
42
- /*#__PURE__*/ _jsxs("span", {
43
- className: "min-w-0",
44
- children: [
45
- /*#__PURE__*/ _jsx("span", {
46
- className: cn("block truncate text-sm", LABEL_CLASS[step.status]),
47
- children: step.name
48
- }),
49
- step.meta === undefined ? null : /*#__PURE__*/ _jsx("span", {
50
- className: "block truncate text-xs text-muted-foreground",
51
- children: step.meta
52
- })
53
- ]
54
- }),
55
- /*#__PURE__*/ _jsx("span", {
56
- className: "shrink-0 text-[10.5px] font-bold uppercase tracking-[0.05em] text-muted-foreground",
57
- children: STATE_LABEL[step.status]
58
- })
59
- ]
60
- });
24
+ * status spelled out in text so it never depends on colour alone. */
25
+ export function StepListRow({ step, className }) {
26
+ return (_jsxs("li", { "data-step-status": step.status, className: cn("grid grid-cols-[20px_minmax(0,1fr)_auto] items-start gap-2 py-1.5", className), children: [_jsx("span", { "aria-hidden": true, className: cn("mt-0.5 grid size-5 place-items-center rounded-sm border text-[10px] font-semibold", MARK_CLASS[step.status]), children: step.status === "done" ? (_jsx(Check, { className: "size-3" })) : step.status === "failed" ? (_jsx(X, { className: "size-3" })) : step.status === "active" ? (_jsx(StatusDot, { label: "", tone: "emphasis", live: true })) : null }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: cn("block truncate text-sm", LABEL_CLASS[step.status]), children: step.name }), step.meta === undefined ? null : (_jsx("span", { className: "block truncate text-xs text-muted-foreground", children: step.meta }))] }), _jsx("span", { className: "shrink-0 text-[10.5px] font-bold uppercase tracking-[0.05em] text-muted-foreground", children: STATE_LABEL[step.status] })] }));
61
27
  }
62
28
  /**
63
29
  * A run's steps and how far along each one is. `aria-live="polite"` so a
64
30
  * step flipping to done or failed is announced without stealing focus from
65
31
  * wherever the reader actually is — the same rule `progress-checklist.tsx`
66
32
  * follows for a long-running operation's checklist.
67
- */ export function StepList({ steps, label = "Steps", className }) {
68
- if (steps.length === 0) return null;
69
- return /*#__PURE__*/ _jsx("ol", {
70
- "aria-label": label,
71
- "aria-live": "polite",
72
- className: cn("flex flex-col", className),
73
- children: steps.map((step)=>/*#__PURE__*/ _jsx(StepListRow, {
74
- step: step
75
- }, step.id))
76
- });
33
+ */
34
+ export function StepList({ steps, label = "Steps", className }) {
35
+ if (steps.length === 0)
36
+ return null;
37
+ return (_jsx("ol", { "aria-label": label, "aria-live": "polite", className: cn("flex flex-col", className), children: steps.map((step) => (_jsx(StepListRow, { step: step }, step.id))) }));
77
38
  }
package/dist/ui/switch.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
3
  import { cn } from "../lib/utils.js";
3
4
  /**
@@ -10,20 +11,7 @@ import { cn } from "../lib/utils.js";
10
11
  *
11
12
  * The knob is a `<span>` inside the button rather than a `::after`, so its
12
13
  * transform animates independently of the track's colour transition.
13
- */ export function Switch({ checked, onCheckedChange, label, id, disabled = false, describedBy, className }) {
14
- return /*#__PURE__*/ _jsx("button", {
15
- type: "button",
16
- id: id,
17
- role: "switch",
18
- "aria-checked": checked,
19
- "aria-label": label,
20
- "aria-describedby": describedBy,
21
- disabled: disabled,
22
- onClick: ()=>onCheckedChange(!checked),
23
- className: cn("inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-transparent p-0.5 transition-colors disabled:opacity-50", checked ? "bg-primary" : "bg-input", className),
24
- children: /*#__PURE__*/ _jsx("span", {
25
- "aria-hidden": true,
26
- className: cn("size-4 rounded-full bg-background transition-transform", checked && "translate-x-4")
27
- })
28
- });
14
+ */
15
+ export function Switch({ checked, onCheckedChange, label, id, disabled = false, describedBy, className, }) {
16
+ return (_jsx("button", { type: "button", id: id, role: "switch", "aria-checked": checked, "aria-label": label, "aria-describedby": describedBy, disabled: disabled, onClick: () => onCheckedChange(!checked), className: cn("inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-transparent p-0.5 transition-colors disabled:opacity-50", checked ? "bg-primary" : "bg-input", className), children: _jsx("span", { "aria-hidden": true, className: cn("size-4 rounded-full bg-background transition-transform", checked && "translate-x-4") }) }));
29
17
  }
package/dist/ui/table.js CHANGED
@@ -1,48 +1,23 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cn } from "../lib/utils.js";
3
3
  export function Table({ className, ...props }) {
4
- return /*#__PURE__*/ _jsx("div", {
5
- className: "relative w-full overflow-x-auto",
6
- children: /*#__PURE__*/ _jsx("table", {
7
- "data-slot": "table",
8
- className: cn("w-full caption-bottom text-sm", className),
9
- ...props
10
- })
11
- });
4
+ return (_jsx("div", { className: "relative w-full overflow-x-auto", children: _jsx("table", { "data-slot": "table", className: cn("w-full caption-bottom text-sm", className), ...props }) }));
12
5
  }
13
6
  export function TableHeader({ className, ...props }) {
14
- return /*#__PURE__*/ _jsx("thead", {
15
- className: cn("[&_tr]:border-b [&_tr]:border-border", className),
16
- ...props
17
- });
7
+ return (_jsx("thead", { className: cn("[&_tr]:border-b [&_tr]:border-border", className), ...props }));
18
8
  }
19
9
  export function TableBody({ className, ...props }) {
20
- return /*#__PURE__*/ _jsx("tbody", {
21
- className: cn("[&_tr:last-child]:border-0", className),
22
- ...props
23
- });
10
+ return (_jsx("tbody", { className: cn("[&_tr:last-child]:border-0", className), ...props }));
24
11
  }
25
12
  export function TableRow({ className, ...props }) {
26
- return /*#__PURE__*/ _jsx("tr", {
27
- className: cn("border-b border-border transition-colors hover:bg-muted", className),
28
- ...props
29
- });
13
+ return (_jsx("tr", { className: cn("border-b border-border transition-colors hover:bg-muted", className), ...props }));
30
14
  }
31
15
  export function TableHead({ className, ...props }) {
32
- return /*#__PURE__*/ _jsx("th", {
33
- className: cn("h-10 px-3 text-left align-middle font-medium text-muted-foreground", className),
34
- ...props
35
- });
16
+ return (_jsx("th", { className: cn("h-10 px-3 text-left align-middle font-medium text-muted-foreground", className), ...props }));
36
17
  }
37
18
  export function TableCell({ className, ...props }) {
38
- return /*#__PURE__*/ _jsx("td", {
39
- className: cn("p-3 align-middle", className),
40
- ...props
41
- });
19
+ return _jsx("td", { className: cn("p-3 align-middle", className), ...props });
42
20
  }
43
21
  export function TableCaption({ className, ...props }) {
44
- return /*#__PURE__*/ _jsx("caption", {
45
- className: cn("mt-3 text-sm text-muted-foreground", className),
46
- ...props
47
- });
22
+ return (_jsx("caption", { className: cn("mt-3 text-sm text-muted-foreground", className), ...props }));
48
23
  }
package/dist/ui/tabs.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { useId, useRef } from "react";
3
4
  import { cn } from "../lib/utils.js";
@@ -24,78 +25,62 @@ import { cn } from "../lib/utils.js";
24
25
  * next Tab press escapes from the wrong place. Selection by click does *not*
25
26
  * move focus — the clicked tab already has it — and focus is never pushed into
26
27
  * the panel, which is the "improvement" that stops people arrowing on.
27
- */ export function Tabs({ tabs, active, onChange, label, children, variant = "underline", className }) {
28
+ */
29
+ export function Tabs({ tabs, active, onChange, label, children, variant = "underline", className, }) {
28
30
  const base = useId();
29
- const tabId = (id)=>`${base}-tab-${id}`;
30
- const panelId = (id)=>`${base}-panel-${id}`;
31
+ const tabId = (id) => `${base}-tab-${id}`;
32
+ const panelId = (id) => `${base}-panel-${id}`;
31
33
  // Every tab button is rendered whatever the selection, so the node to focus
32
34
  // already exists at the moment the key is handled — no effect, no timer.
33
35
  const buttons = useRef(new Map());
34
- /** Arrow/Home/End selection: change the tab *and* take focus with it. */ function focusTab(id) {
36
+ /** Arrow/Home/End selection: change the tab *and* take focus with it. */
37
+ function focusTab(id) {
35
38
  onChange(id);
36
39
  buttons.current.get(id)?.focus();
37
40
  }
38
41
  function move(delta) {
39
- const index = tabs.findIndex((tab)=>tab.id === active);
42
+ const index = tabs.findIndex((tab) => tab.id === active);
40
43
  const next = tabs[(index + delta + tabs.length) % tabs.length];
41
- if (next !== undefined) focusTab(next.id);
44
+ if (next !== undefined)
45
+ focusTab(next.id);
42
46
  }
43
- return /*#__PURE__*/ _jsxs("div", {
44
- className: cn("flex flex-col gap-4", className),
45
- children: [
46
- /*#__PURE__*/ _jsx("div", {
47
- role: "tablist",
48
- "aria-label": label,
49
- onKeyDown: (event)=>{
47
+ return (_jsxs("div", { className: cn("flex flex-col gap-4", className), children: [_jsx("div", { role: "tablist", "aria-label": label, onKeyDown: (event) => {
50
48
  if (event.key === "ArrowRight") {
51
49
  event.preventDefault();
52
50
  move(1);
53
- } else if (event.key === "ArrowLeft") {
51
+ }
52
+ else if (event.key === "ArrowLeft") {
54
53
  event.preventDefault();
55
54
  move(-1);
56
- } else if (event.key === "Home") {
55
+ }
56
+ else if (event.key === "Home") {
57
57
  event.preventDefault();
58
- if (tabs[0] !== undefined) focusTab(tabs[0].id);
59
- } else if (event.key === "End") {
58
+ if (tabs[0] !== undefined)
59
+ focusTab(tabs[0].id);
60
+ }
61
+ else if (event.key === "End") {
60
62
  event.preventDefault();
61
63
  const last = tabs[tabs.length - 1];
62
- if (last !== undefined) focusTab(last.id);
64
+ if (last !== undefined)
65
+ focusTab(last.id);
63
66
  }
64
- },
65
- className: cn("flex flex-wrap items-center", variant === "underline" ? "gap-1 border-b border-border" : "gap-1 rounded-md border border-border p-1"),
66
- children: tabs.map((tab)=>{
67
+ }, className: cn("flex flex-wrap items-center", variant === "underline"
68
+ ? "gap-1 border-b border-border"
69
+ : "gap-1 rounded-md border border-border p-1"), children: tabs.map((tab) => {
67
70
  const selected = tab.id === active;
68
- return /*#__PURE__*/ _jsxs("button", {
69
- ref: (node)=>{
70
- if (node === null) buttons.current.delete(tab.id);
71
- else buttons.current.set(tab.id, node);
72
- },
73
- type: "button",
74
- role: "tab",
75
- id: tabId(tab.id),
76
- "aria-selected": selected,
77
- "aria-controls": panelId(tab.id),
78
- tabIndex: selected ? 0 : -1,
79
- onClick: ()=>onChange(tab.id),
80
- className: cn("inline-flex items-center gap-1.5 px-3 py-2 text-sm font-medium transition-colors", variant === "underline" ? // inactive, so selecting a tab does not shift the row.
81
- selected ? "-mb-px border-b-2 border-primary-emphasis text-foreground" : "-mb-px border-b-2 border-transparent text-muted-foreground hover:text-foreground" : selected ? "rounded-sm bg-muted text-foreground" : "rounded-sm text-muted-foreground hover:text-foreground"),
82
- children: [
83
- tab.label,
84
- tab.count === undefined ? null : /*#__PURE__*/ _jsx("span", {
85
- className: "rounded-sm bg-muted px-1.5 font-mono text-xs text-muted-foreground",
86
- children: tab.count
87
- })
88
- ]
89
- }, tab.id);
90
- })
91
- }),
92
- /*#__PURE__*/ _jsx("div", {
93
- role: "tabpanel",
94
- id: panelId(active),
95
- "aria-labelledby": tabId(active),
96
- tabIndex: 0,
97
- children: children(active)
98
- })
99
- ]
100
- });
71
+ return (_jsxs("button", { ref: (node) => {
72
+ if (node === null)
73
+ buttons.current.delete(tab.id);
74
+ else
75
+ buttons.current.set(tab.id, node);
76
+ }, type: "button", role: "tab", id: tabId(tab.id), "aria-selected": selected, "aria-controls": panelId(tab.id), tabIndex: selected ? 0 : -1, onClick: () => onChange(tab.id), className: cn("inline-flex items-center gap-1.5 px-3 py-2 text-sm font-medium transition-colors", variant === "underline"
77
+ ? // The 2px border exists on both states, transparent when
78
+ // inactive, so selecting a tab does not shift the row.
79
+ selected
80
+ ? "-mb-px border-b-2 border-primary-emphasis text-foreground"
81
+ : "-mb-px border-b-2 border-transparent text-muted-foreground hover:text-foreground"
82
+ : selected
83
+ ? "rounded-sm bg-muted text-foreground"
84
+ : "rounded-sm text-muted-foreground hover:text-foreground"), children: [tab.label, tab.count === undefined ? null : (_jsx("span", { className: "rounded-sm bg-muted px-1.5 font-mono text-xs text-muted-foreground", children: tab.count }))] }, tab.id));
85
+ }) }), _jsx("div", { role: "tabpanel", id: panelId(active), "aria-labelledby": tabId(active), tabIndex: 0, children: children(active) })] }));
101
86
  }
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
3
  import { useEffect, useRef } from "react";
3
4
  import { cn } from "../lib/utils.js";
@@ -6,27 +7,17 @@ import { cn } from "../lib/utils.js";
6
7
  const DEFAULT_MAX_HEIGHT_PX = 200;
7
8
  export function Textarea({ className, autoResize, maxHeight = DEFAULT_MAX_HEIGHT_PX, onChange, ...props }) {
8
9
  const ref = useRef(null);
9
- const resize = (element)=>{
10
+ const resize = (element) => {
10
11
  element.style.height = "auto";
11
12
  element.style.height = `${Math.min(element.scrollHeight, maxHeight)}px`;
12
13
  };
13
- useEffect(()=>{
14
- if (autoResize && ref.current) resize(ref.current);
15
- }, [
16
- autoResize,
17
- maxHeight
18
- ]);
19
- return /*#__PURE__*/ _jsx("textarea", {
20
- ref: ref,
21
- "data-slot": "textarea",
22
- style: autoResize ? {
23
- maxHeight
24
- } : undefined,
25
- className: cn("flex min-h-16 w-full min-w-0 rounded-md border border-input bg-card px-3 py-2 text-sm shadow-xs transition-colors", autoResize && "resize-none overflow-y-auto", "placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground", "aria-invalid:border-destructive disabled:cursor-not-allowed disabled:opacity-50", className),
26
- onChange: (event)=>{
27
- if (autoResize) resize(event.currentTarget);
14
+ useEffect(() => {
15
+ if (autoResize && ref.current)
16
+ resize(ref.current);
17
+ }, [autoResize, maxHeight]);
18
+ return (_jsx("textarea", { ref: ref, "data-slot": "textarea", style: autoResize ? { maxHeight } : undefined, className: cn("flex min-h-16 w-full min-w-0 rounded-md border border-input bg-card px-3 py-2 text-sm shadow-xs transition-colors", autoResize && "resize-none overflow-y-auto", "placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground", "aria-invalid:border-destructive disabled:cursor-not-allowed disabled:opacity-50", className), onChange: (event) => {
19
+ if (autoResize)
20
+ resize(event.currentTarget);
28
21
  onChange?.(event);
29
- },
30
- ...props
31
- });
22
+ }, ...props }));
32
23
  }
@@ -1,27 +1,26 @@
1
+ "use client";
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
3
- import { DEFAULT_THEME_STORAGE_KEY, applyThemeToRoot, nextThemeMode, parseThemePreference, resolveThemeMode, serializeThemePreference } from "../lib/theme.js";
4
- const ThemeContext = /*#__PURE__*/ createContext(null);
5
- function readSystemPrefersDark() {
6
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
7
- return false;
8
- }
9
- return window.matchMedia("(prefers-color-scheme: dark)").matches;
10
- }
3
+ import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react";
4
+ import { DEFAULT_THEME_STORAGE_KEY, applyThemeToRoot, nextThemeMode, parseThemePreference, resolveThemeMode, serializeThemePreference, } from "../lib/theme.js";
5
+ const ThemeContext = createContext(null);
11
6
  function readStoredPreference(storageKey, fallback) {
12
- if (typeof window === "undefined") return fallback;
7
+ if (typeof window === "undefined")
8
+ return fallback;
13
9
  try {
14
10
  return parseThemePreference(window.localStorage.getItem(storageKey), fallback);
15
- } catch {
11
+ }
12
+ catch {
16
13
  return fallback;
17
14
  }
18
15
  }
19
16
  function writeStoredPreference(storageKey, preference) {
20
- if (typeof window === "undefined") return;
17
+ if (typeof window === "undefined")
18
+ return;
21
19
  try {
22
20
  window.localStorage.setItem(storageKey, serializeThemePreference(preference));
23
- } catch {
24
- // Quota / private mode — preference still applies for this session.
21
+ }
22
+ catch {
23
+ // Quota / private mode — preference still applies for this session.
25
24
  }
26
25
  }
27
26
  /**
@@ -30,92 +29,69 @@ function writeStoredPreference(storageKey, preference) {
30
29
  * The host mounts this once near the root and keys `storageKey` by user when
31
30
  * signed in. Mode is applied as `.dark` on the root (see theme.css); presets
32
31
  * as `data-theme`. Components never import storage — they call `useTheme`.
33
- */ export function ThemeProvider({ children, storageKey = DEFAULT_THEME_STORAGE_KEY, defaultMode = "system", defaultPreset = "default", root = null }) {
34
- const fallback = useMemo(()=>({
35
- mode: defaultMode,
36
- preset: defaultPreset
37
- }), [
38
- defaultMode,
39
- defaultPreset
40
- ]);
41
- const [preference, setPreference] = useState(()=>readStoredPreference(storageKey, fallback));
42
- const [systemPrefersDark, setSystemPrefersDark] = useState(readSystemPrefersDark);
43
- // Re-read when the host changes the storage key (e.g. user signs in).
44
- useEffect(()=>{
32
+ */
33
+ export function ThemeProvider({ children, storageKey = DEFAULT_THEME_STORAGE_KEY, defaultMode = "system", defaultPreset = "default", root = null, }) {
34
+ const fallback = useMemo(() => ({ mode: defaultMode, preset: defaultPreset }), [defaultMode, defaultPreset]);
35
+ // Both start from what the server renders; the effects below read
36
+ // localStorage and matchMedia after mount so hydration never mismatches.
37
+ const [preference, setPreference] = useState(fallback);
38
+ const [systemPrefersDark, setSystemPrefersDark] = useState(false);
39
+ // Re-read on mount and when the host changes the storage key (e.g. user signs in).
40
+ useEffect(() => {
45
41
  setPreference(readStoredPreference(storageKey, fallback));
46
- }, [
47
- storageKey,
48
- fallback
49
- ]);
50
- useEffect(()=>{
51
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
42
+ }, [storageKey, fallback]);
43
+ useEffect(() => {
44
+ if (typeof window === "undefined" ||
45
+ typeof window.matchMedia !== "function") {
52
46
  return;
53
47
  }
54
48
  const media = window.matchMedia("(prefers-color-scheme: dark)");
55
- const onChange = ()=>setSystemPrefersDark(media.matches);
49
+ const onChange = () => setSystemPrefersDark(media.matches);
56
50
  onChange();
57
51
  media.addEventListener("change", onChange);
58
- return ()=>media.removeEventListener("change", onChange);
52
+ return () => media.removeEventListener("change", onChange);
59
53
  }, []);
60
54
  const resolvedMode = resolveThemeMode(preference.mode, systemPrefersDark);
61
- useEffect(()=>{
62
- const target = root ?? (typeof document !== "undefined" ? document.documentElement : null);
63
- if (target === null) return;
55
+ useEffect(() => {
56
+ const target = root ??
57
+ (typeof document !== "undefined" ? document.documentElement : null);
58
+ if (target === null)
59
+ return;
64
60
  applyThemeToRoot(target, resolvedMode, preference.preset);
65
- }, [
66
- root,
67
- resolvedMode,
68
- preference.preset
69
- ]);
70
- const setMode = useCallback((mode)=>{
71
- setPreference((current)=>{
72
- const next = {
73
- mode,
74
- preset: current.preset
75
- };
61
+ }, [root, resolvedMode, preference.preset]);
62
+ const setMode = useCallback((mode) => {
63
+ setPreference((current) => {
64
+ const next = { mode, preset: current.preset };
76
65
  writeStoredPreference(storageKey, next);
77
66
  return next;
78
67
  });
79
- }, [
80
- storageKey
81
- ]);
82
- const setPreset = useCallback((preset)=>{
83
- setPreference((current)=>{
84
- const next = {
85
- mode: current.mode,
86
- preset
87
- };
68
+ }, [storageKey]);
69
+ const setPreset = useCallback((preset) => {
70
+ setPreference((current) => {
71
+ const next = { mode: current.mode, preset };
88
72
  writeStoredPreference(storageKey, next);
89
73
  return next;
90
74
  });
91
- }, [
92
- storageKey
93
- ]);
94
- const cycleMode = useCallback(()=>{
75
+ }, [storageKey]);
76
+ const cycleMode = useCallback(() => {
95
77
  setMode(nextThemeMode(preference.mode));
96
- }, [
97
- preference.mode,
98
- setMode
99
- ]);
100
- const value = useMemo(()=>({
101
- mode: preference.mode,
102
- setMode,
103
- cycleMode,
104
- resolvedMode,
105
- preset: preference.preset,
106
- setPreset
107
- }), [
78
+ }, [preference.mode, setMode]);
79
+ const value = useMemo(() => ({
80
+ mode: preference.mode,
81
+ setMode,
82
+ cycleMode,
83
+ resolvedMode,
84
+ preset: preference.preset,
85
+ setPreset,
86
+ }), [
108
87
  preference.mode,
109
88
  preference.preset,
110
89
  setMode,
111
90
  cycleMode,
112
91
  resolvedMode,
113
- setPreset
92
+ setPreset,
114
93
  ]);
115
- return /*#__PURE__*/ _jsx(ThemeContext.Provider, {
116
- value: value,
117
- children: children
118
- });
94
+ return (_jsx(ThemeContext.Provider, { value: value, children: children }));
119
95
  }
120
96
  export function useTheme() {
121
97
  const value = useContext(ThemeContext);
@@ -1,32 +1,29 @@
1
+ "use client";
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
3
  import { Monitor, Moon, Sun } from "lucide-react";
3
4
  import { Button } from "./button.js";
4
5
  import { useTheme } from "./theme-provider.js";
5
6
  function iconFor(mode) {
6
- if (mode === "light") return /*#__PURE__*/ _jsx(Sun, {});
7
- if (mode === "dark") return /*#__PURE__*/ _jsx(Moon, {});
8
- return /*#__PURE__*/ _jsx(Monitor, {});
7
+ if (mode === "light")
8
+ return _jsx(Sun, {});
9
+ if (mode === "dark")
10
+ return _jsx(Moon, {});
11
+ return _jsx(Monitor, {});
9
12
  }
10
13
  function labelFor(mode) {
11
- if (mode === "light") return "Light theme";
12
- if (mode === "dark") return "Dark theme";
14
+ if (mode === "light")
15
+ return "Light theme";
16
+ if (mode === "dark")
17
+ return "Dark theme";
13
18
  return "System theme";
14
19
  }
15
20
  /**
16
21
  * Cycles system → light → dark. Intended for the host identity dock (or any
17
22
  * chrome that needs a one-control theme switch). Uses `useTheme`, so it must
18
23
  * sit under `ThemeProvider`.
19
- */ export function ThemeToggle({ label, variant = "ghost", size = "sm", ...props }) {
24
+ */
25
+ export function ThemeToggle({ label, variant = "ghost", size = "sm", ...props }) {
20
26
  const { mode, cycleMode } = useTheme();
21
27
  const name = label ?? labelFor(mode);
22
- return /*#__PURE__*/ _jsx(Button, {
23
- ...props,
24
- type: "button",
25
- variant: variant,
26
- size: size,
27
- onClick: cycleMode,
28
- title: name,
29
- "aria-label": name,
30
- children: iconFor(mode)
31
- });
28
+ return (_jsx(Button, { ...props, type: "button", variant: variant, size: size, onClick: cycleMode, title: name, "aria-label": name, children: iconFor(mode) }));
32
29
  }
@@ -1,19 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from "../lib/utils.js";
3
3
  import { ThinkingMark } from "./thinking-mark.js";
4
- /** A thinking status with Silk, Strata, or Echo motion. Defaults to Silk. */ export function ThinkingIndicator({ label = "Thinking...", className, style, ...markProps }) {
5
- return /*#__PURE__*/ _jsxs("span", {
6
- role: "status",
7
- className: cn("inline-flex items-center gap-1.5 text-sm text-muted-foreground", className),
8
- style: style,
9
- children: [
10
- /*#__PURE__*/ _jsx(ThinkingMark, {
11
- ...markProps,
12
- className: "-translate-y-[8%]"
13
- }),
14
- /*#__PURE__*/ _jsx("span", {
15
- children: label
16
- })
17
- ]
18
- });
4
+ /** A thinking status with Silk, Strata, or Echo motion. Defaults to Silk. */
5
+ export function ThinkingIndicator({ label = "Thinking...", className, style, ...markProps }) {
6
+ return (_jsxs("span", { role: "status", className: cn("inline-flex items-center gap-1.5 text-sm text-muted-foreground", className), style: style, children: [_jsx(ThinkingMark, { ...markProps, className: "-translate-y-[8%]" }), _jsx("span", { children: label })] }));
19
7
  }