@corbits/react-ui 0.1.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 (255) hide show
  1. package/LICENSE +501 -0
  2. package/README.md +125 -0
  3. package/dist/blocks/login/auth-layout.d.ts +38 -0
  4. package/dist/blocks/login/auth-layout.js +52 -0
  5. package/dist/blocks/login/login-form.d.ts +60 -0
  6. package/dist/blocks/login/login-form.js +151 -0
  7. package/dist/hooks/use-command-palette-navigation.d.ts +25 -0
  8. package/dist/hooks/use-command-palette-navigation.js +52 -0
  9. package/dist/hooks/use-controllable-state.d.ts +28 -0
  10. package/dist/hooks/use-controllable-state.js +44 -0
  11. package/dist/hooks/use-delayed-autofocus.d.ts +9 -0
  12. package/dist/hooks/use-delayed-autofocus.js +20 -0
  13. package/dist/hooks/use-dismissable-popover.d.ts +37 -0
  14. package/dist/hooks/use-dismissable-popover.js +64 -0
  15. package/dist/hooks/use-list-selection.d.ts +66 -0
  16. package/dist/hooks/use-list-selection.js +147 -0
  17. package/dist/hooks/use-prefers-reduced-motion.d.ts +13 -0
  18. package/dist/hooks/use-prefers-reduced-motion.js +28 -0
  19. package/dist/hooks/use-scroll-current-into-view.d.ts +19 -0
  20. package/dist/hooks/use-scroll-current-into-view.js +33 -0
  21. package/dist/index.d.ts +122 -0
  22. package/dist/index.js +131 -0
  23. package/dist/lib/artifact.d.ts +72 -0
  24. package/dist/lib/artifact.js +67 -0
  25. package/dist/lib/chart-geometry.d.ts +62 -0
  26. package/dist/lib/chart-geometry.js +86 -0
  27. package/dist/lib/chart-palette.d.ts +76 -0
  28. package/dist/lib/chart-palette.js +92 -0
  29. package/dist/lib/chat-message.d.ts +115 -0
  30. package/dist/lib/chat-message.js +41 -0
  31. package/dist/lib/chat-parts.d.ts +64 -0
  32. package/dist/lib/chat-parts.js +16 -0
  33. package/dist/lib/csv.d.ts +60 -0
  34. package/dist/lib/csv.js +108 -0
  35. package/dist/lib/metrics.d.ts +70 -0
  36. package/dist/lib/metrics.js +101 -0
  37. package/dist/lib/now-item.d.ts +65 -0
  38. package/dist/lib/now-item.js +59 -0
  39. package/dist/lib/relative-time.d.ts +9 -0
  40. package/dist/lib/relative-time.js +47 -0
  41. package/dist/lib/theme.d.ts +30 -0
  42. package/dist/lib/theme.js +77 -0
  43. package/dist/lib/url.d.ts +26 -0
  44. package/dist/lib/url.js +37 -0
  45. package/dist/lib/utils.d.ts +2 -0
  46. package/dist/lib/utils.js +5 -0
  47. package/dist/lib/workflow-display-flow.d.ts +83 -0
  48. package/dist/lib/workflow-display-flow.js +104 -0
  49. package/dist/lib/workflow-registry.d.ts +90 -0
  50. package/dist/lib/workflow-registry.js +56 -0
  51. package/dist/lib/workflow-run-error.d.ts +31 -0
  52. package/dist/lib/workflow-run-error.js +105 -0
  53. package/dist/lib/workflow-run-progress.d.ts +97 -0
  54. package/dist/lib/workflow-run-progress.js +135 -0
  55. package/dist/lib/workflow-run.d.ts +43 -0
  56. package/dist/lib/workflow-run.js +56 -0
  57. package/dist/styles.css +3080 -0
  58. package/dist/test/render-hook.d.ts +14 -0
  59. package/dist/test/render-hook.js +38 -0
  60. package/dist/theme.css +628 -0
  61. package/dist/ui/agent-turn.d.ts +26 -0
  62. package/dist/ui/agent-turn.js +60 -0
  63. package/dist/ui/animated-number.d.ts +25 -0
  64. package/dist/ui/animated-number.js +63 -0
  65. package/dist/ui/approval-card.d.ts +46 -0
  66. package/dist/ui/approval-card.js +116 -0
  67. package/dist/ui/artifact-body.d.ts +26 -0
  68. package/dist/ui/artifact-body.js +170 -0
  69. package/dist/ui/artifact-notice.d.ts +21 -0
  70. package/dist/ui/artifact-notice.js +36 -0
  71. package/dist/ui/avatar.d.ts +37 -0
  72. package/dist/ui/avatar.js +73 -0
  73. package/dist/ui/badge.d.ts +10 -0
  74. package/dist/ui/badge.js +35 -0
  75. package/dist/ui/bar-chart.d.ts +38 -0
  76. package/dist/ui/bar-chart.js +118 -0
  77. package/dist/ui/block-card.d.ts +29 -0
  78. package/dist/ui/block-card.js +66 -0
  79. package/dist/ui/boot-screen.d.ts +23 -0
  80. package/dist/ui/boot-screen.js +49 -0
  81. package/dist/ui/bulk-action-bar.d.ts +37 -0
  82. package/dist/ui/bulk-action-bar.js +64 -0
  83. package/dist/ui/button.d.ts +12 -0
  84. package/dist/ui/button.js +73 -0
  85. package/dist/ui/card.d.ts +6 -0
  86. package/dist/ui/card.js +37 -0
  87. package/dist/ui/category-bars.d.ts +20 -0
  88. package/dist/ui/category-bars.js +69 -0
  89. package/dist/ui/chart-frame.d.ts +54 -0
  90. package/dist/ui/chart-frame.js +108 -0
  91. package/dist/ui/chat-composer.d.ts +24 -0
  92. package/dist/ui/chat-composer.js +32 -0
  93. package/dist/ui/chat-dock-timing.d.ts +15 -0
  94. package/dist/ui/chat-dock-timing.js +11 -0
  95. package/dist/ui/chat-input.d.ts +38 -0
  96. package/dist/ui/chat-input.js +138 -0
  97. package/dist/ui/chat-panel.d.ts +20 -0
  98. package/dist/ui/chat-panel.js +56 -0
  99. package/dist/ui/chat-thread.d.ts +28 -0
  100. package/dist/ui/chat-thread.js +61 -0
  101. package/dist/ui/checkbox.d.ts +25 -0
  102. package/dist/ui/checkbox.js +63 -0
  103. package/dist/ui/command-palette.d.ts +100 -0
  104. package/dist/ui/command-palette.js +341 -0
  105. package/dist/ui/compare-body.d.ts +52 -0
  106. package/dist/ui/compare-body.js +140 -0
  107. package/dist/ui/confirm-button.d.ts +29 -0
  108. package/dist/ui/confirm-button.js +41 -0
  109. package/dist/ui/corbits-mark.d.ts +29 -0
  110. package/dist/ui/corbits-mark.js +33 -0
  111. package/dist/ui/csv-table.d.ts +22 -0
  112. package/dist/ui/csv-table.js +85 -0
  113. package/dist/ui/dialog.d.ts +37 -0
  114. package/dist/ui/dialog.js +105 -0
  115. package/dist/ui/dictation-status-line.d.ts +20 -0
  116. package/dist/ui/dictation-status-line.js +38 -0
  117. package/dist/ui/dither-canvas.d.ts +31 -0
  118. package/dist/ui/dither-canvas.js +169 -0
  119. package/dist/ui/embed-body.d.ts +30 -0
  120. package/dist/ui/embed-body.js +92 -0
  121. package/dist/ui/empty-state.d.ts +26 -0
  122. package/dist/ui/empty-state.js +42 -0
  123. package/dist/ui/file-input.d.ts +25 -0
  124. package/dist/ui/file-input.js +66 -0
  125. package/dist/ui/filter-bar.d.ts +42 -0
  126. package/dist/ui/filter-bar.js +95 -0
  127. package/dist/ui/filter-chip.d.ts +14 -0
  128. package/dist/ui/filter-chip.js +23 -0
  129. package/dist/ui/gate-block.d.ts +16 -0
  130. package/dist/ui/gate-block.js +37 -0
  131. package/dist/ui/horizontal-stepper.d.ts +13 -0
  132. package/dist/ui/horizontal-stepper.js +65 -0
  133. package/dist/ui/input.d.ts +2 -0
  134. package/dist/ui/input.js +14 -0
  135. package/dist/ui/inspector-shell.d.ts +47 -0
  136. package/dist/ui/inspector-shell.js +98 -0
  137. package/dist/ui/intake-form.d.ts +39 -0
  138. package/dist/ui/intake-form.js +119 -0
  139. package/dist/ui/kind-card-grid.d.ts +23 -0
  140. package/dist/ui/kind-card-grid.js +43 -0
  141. package/dist/ui/library-page-header.d.ts +20 -0
  142. package/dist/ui/library-page-header.js +43 -0
  143. package/dist/ui/list-detail.d.ts +28 -0
  144. package/dist/ui/list-detail.js +55 -0
  145. package/dist/ui/live-run-banner.d.ts +13 -0
  146. package/dist/ui/live-run-banner.js +23 -0
  147. package/dist/ui/live-status-line.d.ts +22 -0
  148. package/dist/ui/live-status-line.js +37 -0
  149. package/dist/ui/menu.d.ts +18 -0
  150. package/dist/ui/menu.js +45 -0
  151. package/dist/ui/message-bubble.d.ts +24 -0
  152. package/dist/ui/message-bubble.js +64 -0
  153. package/dist/ui/mic-button.d.ts +35 -0
  154. package/dist/ui/mic-button.js +61 -0
  155. package/dist/ui/mic-permission-dialog.d.ts +20 -0
  156. package/dist/ui/mic-permission-dialog.js +50 -0
  157. package/dist/ui/notifications-bell.d.ts +28 -0
  158. package/dist/ui/notifications-bell.js +69 -0
  159. package/dist/ui/now-cards.d.ts +37 -0
  160. package/dist/ui/now-cards.js +116 -0
  161. package/dist/ui/page-panel.d.ts +37 -0
  162. package/dist/ui/page-panel.js +23 -0
  163. package/dist/ui/page-shell.d.ts +21 -0
  164. package/dist/ui/page-shell.js +26 -0
  165. package/dist/ui/parts-renderer.d.ts +22 -0
  166. package/dist/ui/parts-renderer.js +234 -0
  167. package/dist/ui/profile-card.d.ts +39 -0
  168. package/dist/ui/profile-card.js +124 -0
  169. package/dist/ui/progress-checklist.d.ts +25 -0
  170. package/dist/ui/progress-checklist.js +70 -0
  171. package/dist/ui/provider-mark.d.ts +32 -0
  172. package/dist/ui/provider-mark.js +31 -0
  173. package/dist/ui/quick-reply-chips.d.ts +14 -0
  174. package/dist/ui/quick-reply-chips.js +24 -0
  175. package/dist/ui/quote-card.d.ts +39 -0
  176. package/dist/ui/quote-card.js +66 -0
  177. package/dist/ui/reasoning-block.d.ts +35 -0
  178. package/dist/ui/reasoning-block.js +60 -0
  179. package/dist/ui/research-body.d.ts +70 -0
  180. package/dist/ui/research-body.js +270 -0
  181. package/dist/ui/rich-empty-state.d.ts +35 -0
  182. package/dist/ui/rich-empty-state.js +65 -0
  183. package/dist/ui/run-now-button.d.ts +23 -0
  184. package/dist/ui/run-now-button.js +42 -0
  185. package/dist/ui/section.d.ts +20 -0
  186. package/dist/ui/section.js +48 -0
  187. package/dist/ui/select.d.ts +2 -0
  188. package/dist/ui/select.js +22 -0
  189. package/dist/ui/selection-checkbox.d.ts +42 -0
  190. package/dist/ui/selection-checkbox.js +54 -0
  191. package/dist/ui/settings-panel.d.ts +35 -0
  192. package/dist/ui/settings-panel.js +68 -0
  193. package/dist/ui/shimmer-text.d.ts +19 -0
  194. package/dist/ui/shimmer-text.js +20 -0
  195. package/dist/ui/sidebar-item-row.d.ts +40 -0
  196. package/dist/ui/sidebar-item-row.js +64 -0
  197. package/dist/ui/sidebar-panel.d.ts +25 -0
  198. package/dist/ui/sidebar-panel.js +56 -0
  199. package/dist/ui/sidebar.d.ts +46 -0
  200. package/dist/ui/sidebar.js +111 -0
  201. package/dist/ui/skeleton.d.ts +13 -0
  202. package/dist/ui/skeleton.js +20 -0
  203. package/dist/ui/sortable-table.d.ts +43 -0
  204. package/dist/ui/sortable-table.js +103 -0
  205. package/dist/ui/sparkline.d.ts +27 -0
  206. package/dist/ui/sparkline.js +68 -0
  207. package/dist/ui/stat-grid.d.ts +55 -0
  208. package/dist/ui/stat-grid.js +87 -0
  209. package/dist/ui/status-dot.d.ts +13 -0
  210. package/dist/ui/status-dot.js +51 -0
  211. package/dist/ui/step-list.d.ts +21 -0
  212. package/dist/ui/step-list.js +77 -0
  213. package/dist/ui/switch.d.ts +23 -0
  214. package/dist/ui/switch.js +29 -0
  215. package/dist/ui/table.d.ts +8 -0
  216. package/dist/ui/table.js +48 -0
  217. package/dist/ui/tabs.d.ts +44 -0
  218. package/dist/ui/tabs.js +101 -0
  219. package/dist/ui/textarea.d.ts +8 -0
  220. package/dist/ui/textarea.js +32 -0
  221. package/dist/ui/theme-provider.d.ts +36 -0
  222. package/dist/ui/theme-provider.js +126 -0
  223. package/dist/ui/theme-toggle.d.ts +11 -0
  224. package/dist/ui/theme-toggle.js +32 -0
  225. package/dist/ui/thinking-indicator.d.ts +7 -0
  226. package/dist/ui/thinking-indicator.js +19 -0
  227. package/dist/ui/thinking-label.d.ts +19 -0
  228. package/dist/ui/thinking-label.js +54 -0
  229. package/dist/ui/thinking-mark.d.ts +10 -0
  230. package/dist/ui/thinking-mark.js +153 -0
  231. package/dist/ui/time-series-chart.d.ts +55 -0
  232. package/dist/ui/time-series-chart.js +249 -0
  233. package/dist/ui/toast.d.ts +16 -0
  234. package/dist/ui/toast.js +41 -0
  235. package/dist/ui/token-mosaic.d.ts +22 -0
  236. package/dist/ui/token-mosaic.js +65 -0
  237. package/dist/ui/tool-block.d.ts +54 -0
  238. package/dist/ui/tool-block.js +170 -0
  239. package/dist/ui/tool-narrative.d.ts +22 -0
  240. package/dist/ui/tool-narrative.js +77 -0
  241. package/dist/ui/tool-picker.d.ts +44 -0
  242. package/dist/ui/tool-picker.js +200 -0
  243. package/dist/ui/tooltip.d.ts +21 -0
  244. package/dist/ui/tooltip.js +48 -0
  245. package/dist/ui/top-bar.d.ts +53 -0
  246. package/dist/ui/top-bar.js +98 -0
  247. package/dist/ui/trace-waterfall.d.ts +38 -0
  248. package/dist/ui/trace-waterfall.js +121 -0
  249. package/dist/ui/typing-indicator.d.ts +9 -0
  250. package/dist/ui/typing-indicator.js +25 -0
  251. package/dist/ui/view-toggle.d.ts +17 -0
  252. package/dist/ui/view-toggle.js +46 -0
  253. package/dist/ui/voice-waveform.d.ts +16 -0
  254. package/dist/ui/voice-waveform.js +23 -0
  255. package/package.json +583 -0
@@ -0,0 +1,14 @@
1
+ export type RenderedHook<T> = {
2
+ readonly result: {
3
+ readonly current: T;
4
+ };
5
+ rerender(): void;
6
+ unmount(): void;
7
+ };
8
+ /**
9
+ * The smallest possible hook-testing harness: mount a component that calls
10
+ * `callback` and stashes its return value, re-render it on demand, and clean
11
+ * up. No `@testing-library` dependency — `act` plus `react-dom/client` cover
12
+ * everything a hook test in this package needs.
13
+ */
14
+ export declare function renderHook<T>(callback: () => T): RenderedHook<T>;
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { act } from "react";
3
+ import { createRoot } from "react-dom/client";
4
+ /**
5
+ * The smallest possible hook-testing harness: mount a component that calls
6
+ * `callback` and stashes its return value, re-render it on demand, and clean
7
+ * up. No `@testing-library` dependency — `act` plus `react-dom/client` cover
8
+ * everything a hook test in this package needs.
9
+ */ export function renderHook(callback) {
10
+ const container = document.createElement("div");
11
+ document.body.appendChild(container);
12
+ let root;
13
+ const resultBox = {
14
+ current: undefined
15
+ };
16
+ function Probe() {
17
+ resultBox.current = callback();
18
+ return null;
19
+ }
20
+ act(()=>{
21
+ root = createRoot(container);
22
+ root.render(/*#__PURE__*/ _jsx(Probe, {}));
23
+ });
24
+ return {
25
+ result: resultBox,
26
+ rerender () {
27
+ act(()=>{
28
+ root.render(/*#__PURE__*/ _jsx(Probe, {}));
29
+ });
30
+ },
31
+ unmount () {
32
+ act(()=>{
33
+ root.unmount();
34
+ });
35
+ container.remove();
36
+ }
37
+ };
38
+ }
package/dist/theme.css ADDED
@@ -0,0 +1,628 @@
1
+ /* The Corbits theme: design tokens, motion, and the base layer.
2
+ This is the one source for all of it — dist/styles.css is compiled from it,
3
+ and dist/theme.css is this file, so the two can never drift.
4
+
5
+ The @source below is why a Tailwind v4 consumer needs a single directive.
6
+ Tailwind resolves it relative to *this file*, so it means src/ when we build
7
+ the standalone stylesheet and dist/ when a consumer imports it out of
8
+ node_modules. Either way it points at the code that uses the classes. */
9
+ @source "./";
10
+
11
+ @custom-variant dark (&:is(.dark *));
12
+
13
+ /* Corbits brand tokens.
14
+
15
+ Backgrounds are White in light and pure Black in dark — never cream. Cream is
16
+ an *element* color: it carries text, borders and surfaces on dark grounds
17
+ only, and gray carries them on light grounds. The two element neutrals never
18
+ cross over — gray never sits on black, cream never sits on white.
19
+
20
+ Because the ground is correct, the orange needs no correction: brand dark
21
+ orange #bf6b20 measures 5.34:1 against black and is the dark --primary as
22
+ shipped by the brand. #d4791f is no longer a primary; it is the hover/pressed
23
+ step, darker than #e98428 in light (ink label 4.65:1) and brighter than
24
+ #bf6b20 in dark (black label 6.55:1), so one hex serves both modes.
25
+
26
+ --primary-emphasis is orange as *text or border* on the ground, where the
27
+ fill orange does not qualify (#e98428 on white is only 2.44:1).
28
+ --input is the control-boundary token and clears the 3:1 UI-component
29
+ threshold on both the page and the card; --border stays soft and decorative.
30
+
31
+ --primary-foreground is the ink label, not white. White on the primary
32
+ orange measures 2.69:1 — a hard AA failure — so please do not "correct" it
33
+ to white; the contrast gate will reject it anyway.
34
+
35
+ Every pair below is enforced by scripts/contrast-test.mjs, which runs as part
36
+ of `npm run build` against the published dist/styles.css. */
37
+ :root {
38
+ --radius: 0.5rem;
39
+
40
+ /* One named icon-size scale, keyed to where a glyph sits rather than an
41
+ abstract xs/sm/md/lg — a call site names its context, not a pixel value,
42
+ so nothing hardcodes its own size or repeats a `size=` prop. Matches the
43
+ owner's mock CSS 1:1 (mock-spec.md §1): rail nav glyphs sit at 15px,
44
+ top-bar icon buttons at 16px inside a 32x32 hit target, inline button
45
+ glyphs (and dense list/menu-item icons — a row and a context-menu item
46
+ read at the same density) at 13px, and the row-selection checkbox tick
47
+ at 10px. Consume as `text-icon-nav` / `text-icon-topbar` /
48
+ `text-icon-button` / `text-icon-checkbox` (Tailwind font-size utilities,
49
+ re-exposed below) on the glyph's wrapping element, sized in `em` against
50
+ that font-size — this is also the fix for icons rendering unsized
51
+ (the browser's 300x150 replaced-element fallback) wherever no ancestor
52
+ rule or `size=` prop already constrained them. */
53
+ --icon-size-nav: 0.9375rem;
54
+ --icon-size-topbar: 1rem;
55
+ --icon-size-button: 0.8125rem;
56
+ --icon-size-checkbox: 0.625rem;
57
+
58
+ --background: #ffffff;
59
+ --foreground: #2b2627;
60
+ --card: #f2f4f5;
61
+ --card-foreground: #2b2627;
62
+ --popover: #ffffff;
63
+ --popover-foreground: #2b2627;
64
+ --primary: #e98428;
65
+ --primary-foreground: #2b2627;
66
+ --primary-active: #d4791f;
67
+ --primary-emphasis: #9a5416;
68
+ --secondary: #c5d2de;
69
+ --secondary-foreground: #2b2627;
70
+ --muted: #eceff1;
71
+ --muted-foreground: #5c5555;
72
+ --thinking-base: #77716a;
73
+ --accent: #c5d2de;
74
+ --accent-foreground: #2b2627;
75
+ --success: #c1d1be;
76
+ --success-foreground: #2b2627;
77
+ --destructive: #9e2b25;
78
+ --destructive-foreground: #ffffff;
79
+ --border: #dfe3e6;
80
+ --border-strong: #c3ccd2;
81
+ --input: #8a8080;
82
+ --ring: #9a5416;
83
+
84
+ /* Status text, not a fill/foreground pair — used where a step or delivery
85
+ state needs to read as "good"/"caution" beyond what --success/--destructive
86
+ already carry (those are fills). --warn also colours a medium/high
87
+ RiskBadge; low risk stays --muted-foreground so only the levels worth
88
+ flagging draw the eye. Matches Workbench's app.css exactly, so a block
89
+ lifted from a consumer app keeps its colours. */
90
+ --ok: #37904a;
91
+ --warn: #b7791f;
92
+
93
+ /* The chrome's elevation. The design is flat — floating chrome separates
94
+ itself with --border-strong, not depth — so the token ships as `none`
95
+ and exists as the single knob if elevation ever returns. */
96
+ --shadow: none;
97
+
98
+ /* Chart series, a fixed order rather than a pool — slot 1 is always the first
99
+ series in the data. See lib/chart-palette.ts for why these steps and this
100
+ ordering. The orange family is deliberately absent: orange is the reserved
101
+ action accent and never paints a passive data series. Green- and red-family
102
+ slots never neighbour each other — under deuteranopia adjacent green/red
103
+ collapse into one hue — and adjacent same-family steps differ by lightness,
104
+ a channel colour-vision deficiency preserves. Every step clears 3:1 against
105
+ the page and the card in both modes (re-checked by the contrast gate). */
106
+ --chart-1: #1f5fa8;
107
+ --chart-2: #37904a;
108
+ --chart-3: #1f6132;
109
+ --chart-4: #143e70;
110
+ --chart-5: #9e2b25;
111
+
112
+ /* Named durations for the three motion sizes the theme actually uses:
113
+ micro for a hover/pressed state or an icon swap, standard for a toast or
114
+ dropdown, large for a dialog, drawer or panel swap. Declared here, on
115
+ :root, so a consumer can also read `var(--duration-standard)` directly
116
+ in a hand-written transition — the @theme inline block below re-exposes
117
+ them under Tailwind's real `--transition-duration-*` namespace so
118
+ `duration-standard` etc. compile to a utility class too. The theme's
119
+ existing hand-tuned keyframes/transitions (220ms/280ms/160ms) predate
120
+ these and are left as they are; treat these three as the anchors for
121
+ new motion going forward, not a retrofit of what already shipped. */
122
+ --duration-micro: 150ms;
123
+ --duration-standard: 200ms;
124
+ --duration-large: 300ms;
125
+ }
126
+
127
+ .dark {
128
+ --background: #000000;
129
+ --foreground: #e4d5bc;
130
+ --card: #2b2627;
131
+ --card-foreground: #e4d5bc;
132
+ --popover: #332c2d;
133
+ --popover-foreground: #e4d5bc;
134
+ --primary: #bf6b20;
135
+ --primary-foreground: #000000;
136
+ --primary-active: #d4791f;
137
+ --primary-emphasis: #e98428;
138
+ --secondary: #2d455c;
139
+ --secondary-foreground: #e4d5bc;
140
+ --muted: #1a1718;
141
+ --muted-foreground: #a99c86;
142
+ --thinking-base: #aaa39a;
143
+ --accent: #2d455c;
144
+ --accent-foreground: #e4d5bc;
145
+ --success: #425a3d;
146
+ --success-foreground: #e4d5bc;
147
+ --destructive: #e6796f;
148
+ --destructive-foreground: #000000;
149
+ --border: #423a3b;
150
+ --border-strong: #5c5254;
151
+ --input: #8a8080;
152
+ --ring: #e98428;
153
+ --shadow: none;
154
+
155
+ --ok: #5cb573;
156
+ --warn: #d9a441;
157
+
158
+ --chart-1: #5a95d8;
159
+ --chart-2: #63a86a;
160
+ --chart-3: #9fd8a8;
161
+ --chart-4: #9db8e8;
162
+ --chart-5: #cc4f47;
163
+ color-scheme: dark;
164
+ }
165
+
166
+ /* Dark by default: a fresh install on a dark-OS host gets dark tokens with
167
+ zero JS and zero explicit `.dark` class. The `:not(.dark):not(.light)`
168
+ guard means ThemeProvider still wins in both directions once it mounts —
169
+ an explicit "dark" adds `.dark` (already true, no-op here) and an explicit
170
+ "light" adds `.light`, which this selector excludes.
171
+
172
+ Native CSS cannot attach one declaration list to both `.dark` (any OS) and
173
+ a media-qualified `:root` without `light-dark()` — and `light-dark()` would
174
+ break the contrast gate, which parses hex off `.dark {`. So the two blocks
175
+ stay token-identical on purpose; tests/theme.test.ts fails the build if
176
+ they drift. `color-scheme: dark` is part of that shared set so zero-JS
177
+ dark paints native controls dark too. */
178
+ @media (prefers-color-scheme: dark) {
179
+ :root:not(.dark):not(.light) {
180
+ --background: #000000;
181
+ --foreground: #e4d5bc;
182
+ --card: #2b2627;
183
+ --card-foreground: #e4d5bc;
184
+ --popover: #332c2d;
185
+ --popover-foreground: #e4d5bc;
186
+ --primary: #bf6b20;
187
+ --primary-foreground: #000000;
188
+ --primary-active: #d4791f;
189
+ --primary-emphasis: #e98428;
190
+ --secondary: #2d455c;
191
+ --secondary-foreground: #e4d5bc;
192
+ --muted: #1a1718;
193
+ --muted-foreground: #a99c86;
194
+ --thinking-base: #aaa39a;
195
+ --accent: #2d455c;
196
+ --accent-foreground: #e4d5bc;
197
+ --success: #425a3d;
198
+ --success-foreground: #e4d5bc;
199
+ --destructive: #e6796f;
200
+ --destructive-foreground: #000000;
201
+ --border: #423a3b;
202
+ --border-strong: #5c5254;
203
+ --input: #8a8080;
204
+ --ring: #e98428;
205
+ --shadow: none;
206
+
207
+ --ok: #5cb573;
208
+ --warn: #d9a441;
209
+
210
+ --chart-1: #5a95d8;
211
+ --chart-2: #63a86a;
212
+ --chart-3: #9fd8a8;
213
+ --chart-4: #9db8e8;
214
+ --chart-5: #cc4f47;
215
+ color-scheme: dark;
216
+ }
217
+ }
218
+
219
+ /* Named preset overlays. Applied as data-theme on the same root that carries
220
+ `.dark` (see ThemeProvider). Only surface tints shift — foreground, primary,
221
+ and chart tokens stay on the base theme so the contrast gate and the brand
222
+ orange remain authoritative. */
223
+ :root[data-theme="warm"] {
224
+ --card: #f3efe8;
225
+ --muted: #ebe6df;
226
+ --border: #e2d9ce;
227
+ --border-strong: #c9bcab;
228
+ --secondary: #d4cfc6;
229
+ --secondary-foreground: #2b2627;
230
+ --accent: #d4cfc6;
231
+ --accent-foreground: #2b2627;
232
+ }
233
+
234
+ .dark[data-theme="warm"] {
235
+ --card: #322a26;
236
+ --muted: #1c1614;
237
+ --border: #4a3f3a;
238
+ --border-strong: #665750;
239
+ --secondary: #3d342e;
240
+ --secondary-foreground: #e4d5bc;
241
+ --accent: #3d342e;
242
+ --accent-foreground: #e4d5bc;
243
+ }
244
+
245
+ :root[data-theme="cool"] {
246
+ --card: #eef2f6;
247
+ --muted: #e8edf2;
248
+ --border: #d5dde6;
249
+ --border-strong: #b6c3d0;
250
+ --secondary: #b8c9d9;
251
+ --secondary-foreground: #2b2627;
252
+ --accent: #b8c9d9;
253
+ --accent-foreground: #2b2627;
254
+ }
255
+
256
+ .dark[data-theme="cool"] {
257
+ --card: #1e242c;
258
+ --muted: #14181e;
259
+ --border: #3a424c;
260
+ --border-strong: #525c68;
261
+ --secondary: #2a3d52;
262
+ --secondary-foreground: #e4d5bc;
263
+ --accent: #2a3d52;
264
+ --accent-foreground: #e4d5bc;
265
+ }
266
+
267
+ /* SidebarItemRow's own restyle points, deliberately outside `@theme inline`:
268
+ a Tailwind `bg-primary/10`/`rounded-md` pair is undefeatable without a
269
+ specificity war, since the utility and its override compile to the same
270
+ specificity and whichever rule is later in the sheet wins. A CSS variable
271
+ read at render time has none of that — a consumer sets it once on their own
272
+ root/scope and the row already reads it, no `!important` and no rebuild.
273
+ `color-mix` re-derives from `--primary` so both modes stay correct without
274
+ a `.dark` override of their own. */
275
+ :root {
276
+ --sidebar-row-radius: var(--radius);
277
+ --sidebar-row-selected-bg: color-mix(in srgb, var(--primary) 10%, transparent);
278
+ }
279
+
280
+ @theme inline {
281
+ --color-background: var(--background);
282
+ --color-foreground: var(--foreground);
283
+ --color-card: var(--card);
284
+ --color-card-foreground: var(--card-foreground);
285
+ --color-popover: var(--popover);
286
+ --color-popover-foreground: var(--popover-foreground);
287
+ --color-primary: var(--primary);
288
+ --color-primary-foreground: var(--primary-foreground);
289
+ --color-primary-active: var(--primary-active);
290
+ --color-primary-emphasis: var(--primary-emphasis);
291
+ --color-secondary: var(--secondary);
292
+ --color-secondary-foreground: var(--secondary-foreground);
293
+ --color-muted: var(--muted);
294
+ --color-muted-foreground: var(--muted-foreground);
295
+ --color-thinking-base: var(--thinking-base);
296
+ --color-accent: var(--accent);
297
+ --color-accent-foreground: var(--accent-foreground);
298
+ --color-success: var(--success);
299
+ --color-success-foreground: var(--success-foreground);
300
+ --color-destructive: var(--destructive);
301
+ --color-destructive-foreground: var(--destructive-foreground);
302
+ --color-border: var(--border);
303
+ --color-border-strong: var(--border-strong);
304
+ --color-input: var(--input);
305
+ --color-ring: var(--ring);
306
+ --color-ok: var(--ok);
307
+ --color-warn: var(--warn);
308
+ --shadow-surface: var(--shadow);
309
+
310
+ /* The brand faces are named directly and are not bundled — the package ships
311
+ no font files. A consumer that loads Red Hat Display / Space Mono gets the
312
+ brand type; one that does not falls through the stack. To point these at a
313
+ face loaded under a generated name, override --font-sans / --font-mono. */
314
+ --font-sans: "Red Hat Display", "Open Sans", Roboto, Arial, sans-serif;
315
+ --font-mono: "Space Mono", "Fira Code", Monaco, Consolas, monospace;
316
+
317
+ --radius-sm: calc(var(--radius) - 2px);
318
+ --radius-md: var(--radius);
319
+ --radius-lg: calc(var(--radius) + 2px);
320
+
321
+ --text-icon-nav: var(--icon-size-nav);
322
+ --text-icon-topbar: var(--icon-size-topbar);
323
+ --text-icon-button: var(--icon-size-button);
324
+ --text-icon-checkbox: var(--icon-size-checkbox);
325
+
326
+ /* Overrides Tailwind's built-in ease-out with the brand's decelerate curve,
327
+ so every existing and new `ease-out` usage picks it up in one place. */
328
+ --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
329
+
330
+ /* Symmetric in-out curve for morphs — something growing/shrinking in place
331
+ rather than entering or exiting — paired with `--ease-out`'s decelerate
332
+ shape so the two read as one curve family. */
333
+ --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
334
+
335
+ /* A springier decelerate for entrances that should feel like they land with
336
+ a touch of overshoot — a dock, a popover, a toast arriving — distinct
337
+ from `--ease-out`'s straight decelerate, which suits exits and resizes
338
+ that must never overshoot their target. */
339
+ --ease-spring: cubic-bezier(0.2, 0.9, 0.3, 1.15);
340
+
341
+ /* Tailwind v4's real namespace for duration utilities is
342
+ `--transition-duration-*`, not `--duration-*` — an unreferenced name in
343
+ this inline block is dropped from the build entirely, so re-exporting
344
+ under the correct namespace is what actually emits `duration-micro`,
345
+ `duration-standard` and `duration-large` as utility classes. */
346
+ --transition-duration-micro: var(--duration-micro);
347
+ --transition-duration-standard: var(--duration-standard);
348
+ --transition-duration-large: var(--duration-large);
349
+ }
350
+
351
+ /* Dialog motion. Written as real keyframes rather than pulled in from an
352
+ animation plugin: one dependency fewer, and a consumer who inherits
353
+ dialog.tsx can read and edit the animation in the same repo as the markup.
354
+ Only `transform` is animated — Tailwind centers the dialog with the separate
355
+ `translate` property, so the two compose and the box never jumps. */
356
+ @keyframes corbits-fade-in {
357
+ from {
358
+ opacity: 0;
359
+ }
360
+ to {
361
+ opacity: 1;
362
+ }
363
+ }
364
+
365
+ @keyframes corbits-fade-out {
366
+ from {
367
+ opacity: 1;
368
+ }
369
+ to {
370
+ opacity: 0;
371
+ }
372
+ }
373
+
374
+ @keyframes corbits-dialog-in {
375
+ from {
376
+ opacity: 0;
377
+ transform: scale(0.96);
378
+ }
379
+ to {
380
+ opacity: 1;
381
+ transform: scale(1);
382
+ }
383
+ }
384
+
385
+ @keyframes corbits-dialog-out {
386
+ from {
387
+ opacity: 1;
388
+ transform: scale(1);
389
+ }
390
+ to {
391
+ opacity: 0;
392
+ transform: scale(0.96);
393
+ }
394
+ }
395
+
396
+ /* Drawer motion: the sheet slides from the edge it is anchored to. Only
397
+ `transform` moves, so the panel is composited and never reflows the page
398
+ under it. Used by the drawer variant of ui/dialog. */
399
+ @keyframes corbits-drawer-in-right {
400
+ from {
401
+ transform: translateX(100%);
402
+ }
403
+ to {
404
+ transform: translateX(0);
405
+ }
406
+ }
407
+
408
+ @keyframes corbits-drawer-out-right {
409
+ from {
410
+ transform: translateX(0);
411
+ }
412
+ to {
413
+ transform: translateX(100%);
414
+ }
415
+ }
416
+
417
+ @keyframes corbits-drawer-in-left {
418
+ from {
419
+ transform: translateX(-100%);
420
+ }
421
+ to {
422
+ transform: translateX(0);
423
+ }
424
+ }
425
+
426
+ @keyframes corbits-drawer-out-left {
427
+ from {
428
+ transform: translateX(0);
429
+ }
430
+ to {
431
+ transform: translateX(-100%);
432
+ }
433
+ }
434
+
435
+ /* The live-status ring. Scales and fades outward from the dot rather than
436
+ pulsing its opacity in place, so "still changing" reads at 8px where an
437
+ opacity blink does not. Used by ui/status-dot. */
438
+ @keyframes corbits-status-pulse {
439
+ 0% {
440
+ opacity: 0.6;
441
+ transform: scale(1);
442
+ }
443
+ 100% {
444
+ opacity: 0;
445
+ transform: scale(2.6);
446
+ }
447
+ }
448
+
449
+ /* The block card's header dot: a plain opacity blink, distinct from the
450
+ status dot's outward-scaling ring — this one sits inline in a dense header
451
+ row where a scaling ring would collide with the title text next to it.
452
+ Used by ui/block-card. */
453
+ @keyframes corbits-block-pulse {
454
+ 0%,
455
+ 100% {
456
+ opacity: 1;
457
+ }
458
+ 50% {
459
+ opacity: 0.35;
460
+ }
461
+ }
462
+
463
+ /* The boot screen's progress sweep. An indeterminate bar rather than a spinner:
464
+ it says "still working" without implying a percentage nobody knows. Ships
465
+ by ui/boot-screen. */
466
+ @keyframes corbits-boot-sweep {
467
+ 0% {
468
+ transform: translateX(-100%);
469
+ }
470
+ 100% {
471
+ transform: translateX(300%);
472
+ }
473
+ }
474
+
475
+ /* The block swap: a small slide-and-fade for whichever block just became
476
+ active, including the empty-state's own transition into the first block.
477
+ Keyed by block id so React remounts a fresh node per swap and this plays
478
+ every time. Used by ui/parts-renderer and ui/tool-block. */
479
+ @keyframes corbits-rail-block-in {
480
+ from {
481
+ opacity: 0;
482
+ transform: translateY(6px) scale(0.98);
483
+ }
484
+ to {
485
+ opacity: 1;
486
+ transform: translateY(0) scale(1);
487
+ }
488
+ }
489
+
490
+ /* The typing indicator's three dots. Each dot offsets its own
491
+ animation-delay, so the bounce travels left to right. Used by
492
+ ui/typing-indicator. */
493
+ @keyframes corbits-typing-bounce {
494
+ 0%,
495
+ 80%,
496
+ 100% {
497
+ opacity: 0.35;
498
+ transform: translateY(0);
499
+ }
500
+ 40% {
501
+ opacity: 1;
502
+ transform: translateY(-3px);
503
+ }
504
+ }
505
+ /* SelectionCheckbox's pop on select: a small overshoot so the check reads as
506
+ a deliberate snap rather than a plain fade. No `--spring-*` token exists
507
+ yet in this theme — the overshoot curve below is a literal cubic-bezier
508
+ rather than a token reference; re-point it at a shared spring token if one
509
+ lands. Used by ui/selection-checkbox. */
510
+ @keyframes corbits-selection-pop {
511
+ 0% {
512
+ transform: scale(0.6);
513
+ }
514
+ 60% {
515
+ transform: scale(1.15);
516
+ }
517
+ 100% {
518
+ transform: scale(1);
519
+ }
520
+ }
521
+
522
+ /* BulkActionBar's rise into view. Plays on an inner element, not the
523
+ outer fixed/centered one — the outer element's horizontal centering
524
+ transform is a plain, unconditional class rather than something this
525
+ keyframe has to restate, so a `prefers-reduced-motion` viewer (who never
526
+ runs this animation at all) still ends up centered. Used by
527
+ ui/bulk-action-bar. */
528
+ @keyframes corbits-bulk-action-bar-in {
529
+ from {
530
+ opacity: 0;
531
+ transform: translateY(12px);
532
+ }
533
+ to {
534
+ opacity: 1;
535
+ transform: translateY(0);
536
+ }
537
+ }
538
+
539
+ /* ShimmerText's sweep: a `background-clip: text` gradient sliding across
540
+ its own background-size, so the brighter band travels through the words.
541
+ Used by ui/shimmer-text (and, through it, ui/thinking-label). */
542
+ @keyframes corbits-shimmer-sweep {
543
+ 0% {
544
+ background-position: 150% 0;
545
+ }
546
+ 100% {
547
+ background-position: -50% 0;
548
+ }
549
+ }
550
+
551
+ /* Toast motion: a pure 160ms fade, centered above the bottom edge — no
552
+ slide, no lift. Sonner enters and exits by transitioning translateY, so
553
+ the transform is pinned (!important, since sonner injects its stylesheet
554
+ at runtime after any linked sheet) and only opacity is left to move. The
555
+ durations stay normal-weight declarations so the reduced-motion override
556
+ below still collapses them. Ships by ui/toast. */
557
+ [data-sonner-toaster] [data-sonner-toast].corbits-toast {
558
+ left: 50%;
559
+ transform: translateX(-50%) !important;
560
+ transition-property: opacity;
561
+ transition-duration: 160ms;
562
+ transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
563
+ }
564
+ [data-sonner-toaster] [data-sonner-toast].corbits-toast[data-removed="true"] {
565
+ transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
566
+ }
567
+
568
+ /* Motion is opt-out at the foundation, not per component: anything animated
569
+ anywhere collapses to a single frame when the OS asks for reduced motion.
570
+ Component families added later inherit this for free. */
571
+ @media (prefers-reduced-motion: reduce) {
572
+ *,
573
+ *::before,
574
+ *::after {
575
+ animation-duration: 0.01ms !important;
576
+ animation-iteration-count: 1 !important;
577
+ transition-duration: 0.01ms !important;
578
+ scroll-behavior: auto !important;
579
+ }
580
+ }
581
+
582
+ /* The theme's base layer, so a consumer
583
+ gets the default border color, the ground, the type and — critically — one
584
+ focus ring for the whole app. Before this shipped, three primitives each
585
+ re-declared the same focus-visible outline by hand. Plain CSS rather than
586
+ @apply so a consumer reading the shipped CSS sees the same rules written here. */
587
+ @layer base {
588
+ * {
589
+ border-color: var(--border);
590
+ }
591
+ body {
592
+ background-color: var(--background);
593
+ color: var(--foreground);
594
+ font-family: var(--font-sans);
595
+ -webkit-font-smoothing: antialiased;
596
+ -moz-osx-font-smoothing: grayscale;
597
+ }
598
+ :focus-visible {
599
+ outline: 2px solid var(--ring);
600
+ outline-offset: 2px;
601
+ }
602
+ }
603
+
604
+ /* Ridge-study motion: keep geometry and timing in the original 100 x 70 space. */
605
+ @keyframes corbits-thinking-sweep {
606
+ 0% { transform: translateX(8px); opacity: 0; }
607
+ 8%, 86% { opacity: 1; }
608
+ 100% { transform: translateX(130px); opacity: 0; }
609
+ }
610
+ @keyframes corbits-thinking-rise {
611
+ 0% { transform: translateY(88px); }
612
+ 100% { transform: translateY(-30px); }
613
+ }
614
+ @keyframes corbits-thinking-wave {
615
+ 0% { transform: scale(.03); opacity: 0; }
616
+ 10% { opacity: 1; }
617
+ 75% { opacity: .9; }
618
+ 100% { transform: scale(1.35); opacity: 0; }
619
+ }
620
+ @media (prefers-reduced-motion: reduce) {
621
+ /* Static poses land the gradient mid-stroke. These offsets mirror the SVG
622
+ geometry in thinking-mark.tsx — change the rect/circle geometry there and
623
+ these poses have to be re-picked to match. */
624
+ .corbits-thinking-silk, .corbits-thinking-strata, .corbits-thinking-echo { animation: none; }
625
+ .corbits-thinking-silk { transform: translateX(44px); }
626
+ .corbits-thinking-strata { transform: translateY(30px); }
627
+ .corbits-thinking-echo { transform: scale(.6); }
628
+ }
@@ -0,0 +1,26 @@
1
+ import type { ReactNode } from "react";
2
+ import { type AgentIdentity, type ChatMessage } from "../lib/chat-message.js";
3
+ export type AgentTurnProps = {
4
+ readonly message: ChatMessage;
5
+ readonly identity: AgentIdentity;
6
+ /** Rich body for the answer — a markdown renderer. */
7
+ readonly children?: ReactNode;
8
+ readonly now?: number;
9
+ /** e.g. "Thought for 4s". Shown on the reasoning block once it stops streaming. */
10
+ readonly reasoningDurationLabel?: string;
11
+ readonly className?: string;
12
+ };
13
+ /**
14
+ * One agent reply, in the order it happened: what it thought, what it did, what
15
+ * it said.
16
+ *
17
+ * Parts are rendered in the order the model produced them rather than grouped
18
+ * by kind — a turn that thinks, calls a tool, thinks again and then answers
19
+ * reads as a sequence, and sorting it into "all thinking / all tools / answer"
20
+ * describes a turn that never happened.
21
+ *
22
+ * The avatar is `aria-hidden` and the turn is a labelled region instead: a
23
+ * screen reader should hear "Ada said …" once, not two initials followed by the
24
+ * name.
25
+ */
26
+ export declare function AgentTurn({ message, identity, children, now, reasoningDurationLabel, className, }: AgentTurnProps): import("react").JSX.Element;