@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,62 @@
1
+ /**
2
+ * The arithmetic every chart in this registry does before it draws anything:
3
+ * pick round axis numbers, map a value onto a pixel, turn points into a path,
4
+ * and shorten a number without lying about it.
5
+ *
6
+ * It is plain functions over plain numbers, with no SVG and no React, so the
7
+ * hard part of a chart is the part you can read in isolation and check by hand.
8
+ * That is also the reason there is no charting dependency here: the geometry a
9
+ * bar chart and a line chart actually need is this file, and a library would
10
+ * bring a renderer, a theme layer and an interaction model that then have to be
11
+ * fought to match everything else.
12
+ */
13
+ /**
14
+ * Axis ticks on round numbers, covering `[0, max]`.
15
+ *
16
+ * Rounds the *step* to 1, 2, 5 or 10 × a power of ten, then extends the top of
17
+ * the axis to a multiple of it. Ticks at 0 / 2,500 / 5,000 are read at a glance;
18
+ * ticks at 0 / 2,317 / 4,634 — what you get from dividing the data range — have
19
+ * to be decoded, and the reader stops trusting their own estimate of where a bar
20
+ * lands.
21
+ *
22
+ * Always starts at zero. A bar chart with a truncated baseline overstates
23
+ * differences by whatever fraction you cut off, which is the most effective way
24
+ * to mislead with a chart and the easiest to do by accident.
25
+ */
26
+ export declare function niceTicks(max: number, count?: number): readonly number[];
27
+ /**
28
+ * Maps `value` from `[0, domainMax]` onto `[0, rangeMax]` pixels.
29
+ *
30
+ * Guards a zero domain by returning zero rather than `NaN`. An all-zero series
31
+ * is a real thing to plot — "nothing happened" — and one `NaN` in a path
32
+ * attribute makes the entire path vanish, so a flat line at the baseline is both
33
+ * correct and the only readable failure mode.
34
+ */
35
+ export declare function scaleLinear(value: number, domainMax: number, rangeMax: number): number;
36
+ export type Point = {
37
+ readonly x: number;
38
+ readonly y: number;
39
+ };
40
+ /**
41
+ * An SVG path through points, as straight segments.
42
+ *
43
+ * Straight, not smoothed. A spline through data invents values between the
44
+ * points — it will overshoot a local maximum and draw a peak that was never
45
+ * measured. Curves are for decoration; these are measurements.
46
+ */
47
+ export declare function linePath(points: readonly Point[]): string;
48
+ /**
49
+ * The filled region under a line: the line itself, then down to `baselineY`
50
+ * and back to the first x, closed. The baseline is passed in rather than
51
+ * assumed to be the box edge so the fill lands on the plot's zero line, inside
52
+ * whatever padding the chart drew around it.
53
+ */
54
+ export declare function areaPath(points: readonly Point[], baselineY: number): string;
55
+ /**
56
+ * "1,284" · "12.9K" · "4.2M".
57
+ *
58
+ * Compacts only past a thousand, and keeps one decimal so 12.9K and 13.4K stay
59
+ * distinguishable. Below a thousand the exact number is short enough to show,
60
+ * and rounding it would be a loss with no gain.
61
+ */
62
+ export declare function formatCompact(value: number): string;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The arithmetic every chart in this registry does before it draws anything:
3
+ * pick round axis numbers, map a value onto a pixel, turn points into a path,
4
+ * and shorten a number without lying about it.
5
+ *
6
+ * It is plain functions over plain numbers, with no SVG and no React, so the
7
+ * hard part of a chart is the part you can read in isolation and check by hand.
8
+ * That is also the reason there is no charting dependency here: the geometry a
9
+ * bar chart and a line chart actually need is this file, and a library would
10
+ * bring a renderer, a theme layer and an interaction model that then have to be
11
+ * fought to match everything else.
12
+ */ /**
13
+ * Axis ticks on round numbers, covering `[0, max]`.
14
+ *
15
+ * Rounds the *step* to 1, 2, 5 or 10 × a power of ten, then extends the top of
16
+ * the axis to a multiple of it. Ticks at 0 / 2,500 / 5,000 are read at a glance;
17
+ * ticks at 0 / 2,317 / 4,634 — what you get from dividing the data range — have
18
+ * to be decoded, and the reader stops trusting their own estimate of where a bar
19
+ * lands.
20
+ *
21
+ * Always starts at zero. A bar chart with a truncated baseline overstates
22
+ * differences by whatever fraction you cut off, which is the most effective way
23
+ * to mislead with a chart and the easiest to do by accident.
24
+ */ export function niceTicks(max, count = 4) {
25
+ if (!Number.isFinite(max) || max <= 0) return [
26
+ 0,
27
+ 1
28
+ ];
29
+ const rough = max / count;
30
+ const magnitude = 10 ** Math.floor(Math.log10(rough));
31
+ const normalized = rough / magnitude;
32
+ const step = (normalized <= 1 ? 1 : normalized <= 2 ? 2 : normalized <= 5 ? 5 : 10) * magnitude;
33
+ const top = Math.ceil(max / step) * step;
34
+ const ticks = [];
35
+ // Accumulating `value += step` drifts on fractional steps (0.1 + 0.2 …);
36
+ // multiplying an integer index does not.
37
+ for(let index = 0; index * step <= top + step / 2; index += 1){
38
+ ticks.push(index * step);
39
+ }
40
+ return ticks;
41
+ }
42
+ /**
43
+ * Maps `value` from `[0, domainMax]` onto `[0, rangeMax]` pixels.
44
+ *
45
+ * Guards a zero domain by returning zero rather than `NaN`. An all-zero series
46
+ * is a real thing to plot — "nothing happened" — and one `NaN` in a path
47
+ * attribute makes the entire path vanish, so a flat line at the baseline is both
48
+ * correct and the only readable failure mode.
49
+ */ export function scaleLinear(value, domainMax, rangeMax) {
50
+ if (domainMax <= 0) return 0;
51
+ return value / domainMax * rangeMax;
52
+ }
53
+ /**
54
+ * An SVG path through points, as straight segments.
55
+ *
56
+ * Straight, not smoothed. A spline through data invents values between the
57
+ * points — it will overshoot a local maximum and draw a peak that was never
58
+ * measured. Curves are for decoration; these are measurements.
59
+ */ export function linePath(points) {
60
+ if (points.length === 0) return "";
61
+ return points.map((point, index)=>`${index === 0 ? "M" : "L"}${point.x} ${point.y}`).join(" ");
62
+ }
63
+ /**
64
+ * The filled region under a line: the line itself, then down to `baselineY`
65
+ * and back to the first x, closed. The baseline is passed in rather than
66
+ * assumed to be the box edge so the fill lands on the plot's zero line, inside
67
+ * whatever padding the chart drew around it.
68
+ */ export function areaPath(points, baselineY) {
69
+ if (points.length === 0) return "";
70
+ const first = points[0];
71
+ const last = points[points.length - 1];
72
+ if (first === undefined || last === undefined) return "";
73
+ return `${linePath(points)} L${last.x} ${baselineY} L${first.x} ${baselineY} Z`;
74
+ }
75
+ /**
76
+ * "1,284" · "12.9K" · "4.2M".
77
+ *
78
+ * Compacts only past a thousand, and keeps one decimal so 12.9K and 13.4K stay
79
+ * distinguishable. Below a thousand the exact number is short enough to show,
80
+ * and rounding it would be a loss with no gain.
81
+ */ export function formatCompact(value) {
82
+ const magnitude = Math.abs(value);
83
+ if (magnitude >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`;
84
+ if (magnitude >= 10_000) return `${(value / 1000).toFixed(1)}K`;
85
+ return value.toLocaleString();
86
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Series colour, assigned by position and never by value.
3
+ *
4
+ * The five `--chart-*` tokens are a *fixed order*, not a pool. Slot 1 is always
5
+ * the first series in the data, slot 2 the second, and so on — so filtering a
6
+ * series out never repaints the survivors. A chart whose colours move when the
7
+ * legend changes destroys the one thing colour is doing here, which is identity.
8
+ *
9
+ * The ramp is blue, green, deep green, deep blue, red — and never orange.
10
+ * Orange is the reserved action accent: it marks the one thing on a screen a
11
+ * user can act on, so it must never paint a passive data series, where it would
12
+ * read as a call to action that is not there. Each step holds ≥3:1 against both
13
+ * the page and the card in both modes (the theme's contrast gate re-checks that
14
+ * on every build). Colour-vision safety comes from the ordering: green- and
15
+ * red-family slots never neighbour each other — under deuteranopia adjacent
16
+ * green/red collapse into one hue — and adjacent same-family steps differ by
17
+ * lightness, a channel colour-vision deficiency preserves. That separation is
18
+ * legal **only** alongside a second, non-colour channel — which is why every
19
+ * chart in this registry ships a legend, direct labels, a per-slot dash and a
20
+ * data table rather than treating them as polish.
21
+ */
22
+ /** How many distinct series the palette can carry. Past this, fold. */
23
+ export declare const CHART_SERIES_SLOTS = 5;
24
+ export type ChartSeriesSlot = {
25
+ /** Stable slot key — a readable name for the token family, and a React key. */
26
+ readonly key: string;
27
+ /** CSS colour for strokes, fills, and legend swatches. */
28
+ readonly color: string;
29
+ /** SVG `stroke-dasharray` — the second channel. `undefined` means solid. */
30
+ readonly dash: string | undefined;
31
+ };
32
+ /**
33
+ * The ramp itself, in slot order, for callers that lay out a legend or paint a
34
+ * stacked mosaic and need the whole sequence rather than one slot. Same rules
35
+ * as `seriesColor`: fixed order, never cycled, orange never appears.
36
+ */
37
+ export declare const CHART_SERIES: readonly ChartSeriesSlot[];
38
+ /**
39
+ * The CSS colour for series `index`.
40
+ *
41
+ * Out-of-range indices get the muted ink, not a sixth invented hue and not slot
42
+ * 1 again. Cycling would give two different series the same colour — a silent
43
+ * misread — and generating a hue would put an unvalidated colour on screen.
44
+ * Grey says "these are lumped together", which is the truth once you are past
45
+ * five; use `foldSeries` so the label says so too.
46
+ */
47
+ export declare function seriesColor(index: number): string;
48
+ /**
49
+ * The stroke pattern for series `index` — the second channel, so a line chart
50
+ * is not read by colour alone.
51
+ *
52
+ * Colour carries identity; the dash carries it again for anyone who cannot
53
+ * separate two of the five hues, and for anyone printing this in grey. Slot 1
54
+ * is solid because the commonest chart has one series and a dashed lone line
55
+ * reads as "projected". The patterns get longer as the slots go on, so adjacent
56
+ * slots differ in period as well as in rhythm.
57
+ *
58
+ * Returned as an SVG `stroke-dasharray`; `undefined` means solid. Out-of-range
59
+ * indices are solid too, matching `seriesColor`'s grey — past five, the tail is
60
+ * one lumped-together thing and does not need a pattern of its own.
61
+ */
62
+ export declare function seriesDash(index: number): string | undefined;
63
+ export type FoldedSeries<T> = {
64
+ readonly named: readonly T[];
65
+ /** Everything past the last slot. Empty when it all fit. */
66
+ readonly rest: readonly T[];
67
+ };
68
+ /**
69
+ * Splits a series list into the ones that get their own colour and the tail.
70
+ *
71
+ * Five is not a styling limit, it is a reading limit: nobody tracks nine hues
72
+ * against a legend, and the sixth colour would have to be either a repeat or an
73
+ * unvalidated one. Sum `rest` into an "Other" series, or facet into small
74
+ * multiples — both are better charts than a nine-colour one.
75
+ */
76
+ export declare function foldSeries<T>(series: readonly T[]): FoldedSeries<T>;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Series colour, assigned by position and never by value.
3
+ *
4
+ * The five `--chart-*` tokens are a *fixed order*, not a pool. Slot 1 is always
5
+ * the first series in the data, slot 2 the second, and so on — so filtering a
6
+ * series out never repaints the survivors. A chart whose colours move when the
7
+ * legend changes destroys the one thing colour is doing here, which is identity.
8
+ *
9
+ * The ramp is blue, green, deep green, deep blue, red — and never orange.
10
+ * Orange is the reserved action accent: it marks the one thing on a screen a
11
+ * user can act on, so it must never paint a passive data series, where it would
12
+ * read as a call to action that is not there. Each step holds ≥3:1 against both
13
+ * the page and the card in both modes (the theme's contrast gate re-checks that
14
+ * on every build). Colour-vision safety comes from the ordering: green- and
15
+ * red-family slots never neighbour each other — under deuteranopia adjacent
16
+ * green/red collapse into one hue — and adjacent same-family steps differ by
17
+ * lightness, a channel colour-vision deficiency preserves. That separation is
18
+ * legal **only** alongside a second, non-colour channel — which is why every
19
+ * chart in this registry ships a legend, direct labels, a per-slot dash and a
20
+ * data table rather than treating them as polish.
21
+ */ /** How many distinct series the palette can carry. Past this, fold. */ export const CHART_SERIES_SLOTS = 5;
22
+ /**
23
+ * The ramp itself, in slot order, for callers that lay out a legend or paint a
24
+ * stacked mosaic and need the whole sequence rather than one slot. Same rules
25
+ * as `seriesColor`: fixed order, never cycled, orange never appears.
26
+ */ export const CHART_SERIES = [
27
+ {
28
+ key: "blue",
29
+ color: "var(--chart-1)",
30
+ dash: undefined
31
+ },
32
+ {
33
+ key: "green",
34
+ color: "var(--chart-2)",
35
+ dash: "6 3"
36
+ },
37
+ {
38
+ key: "green-deep",
39
+ color: "var(--chart-3)",
40
+ dash: "2 3"
41
+ },
42
+ {
43
+ key: "blue-deep",
44
+ color: "var(--chart-4)",
45
+ dash: "10 3 2 3"
46
+ },
47
+ {
48
+ key: "red",
49
+ color: "var(--chart-5)",
50
+ dash: "1 3"
51
+ }
52
+ ];
53
+ /**
54
+ * The CSS colour for series `index`.
55
+ *
56
+ * Out-of-range indices get the muted ink, not a sixth invented hue and not slot
57
+ * 1 again. Cycling would give two different series the same colour — a silent
58
+ * misread — and generating a hue would put an unvalidated colour on screen.
59
+ * Grey says "these are lumped together", which is the truth once you are past
60
+ * five; use `foldSeries` so the label says so too.
61
+ */ export function seriesColor(index) {
62
+ return CHART_SERIES[index]?.color ?? "var(--muted-foreground)";
63
+ }
64
+ /**
65
+ * The stroke pattern for series `index` — the second channel, so a line chart
66
+ * is not read by colour alone.
67
+ *
68
+ * Colour carries identity; the dash carries it again for anyone who cannot
69
+ * separate two of the five hues, and for anyone printing this in grey. Slot 1
70
+ * is solid because the commonest chart has one series and a dashed lone line
71
+ * reads as "projected". The patterns get longer as the slots go on, so adjacent
72
+ * slots differ in period as well as in rhythm.
73
+ *
74
+ * Returned as an SVG `stroke-dasharray`; `undefined` means solid. Out-of-range
75
+ * indices are solid too, matching `seriesColor`'s grey — past five, the tail is
76
+ * one lumped-together thing and does not need a pattern of its own.
77
+ */ export function seriesDash(index) {
78
+ return CHART_SERIES[index]?.dash;
79
+ }
80
+ /**
81
+ * Splits a series list into the ones that get their own colour and the tail.
82
+ *
83
+ * Five is not a styling limit, it is a reading limit: nobody tracks nine hues
84
+ * against a legend, and the sixth colour would have to be either a repeat or an
85
+ * unvalidated one. Sum `rest` into an "Other" series, or facet into small
86
+ * multiples — both are better charts than a nine-colour one.
87
+ */ export function foldSeries(series) {
88
+ return {
89
+ named: series.slice(0, CHART_SERIES_SLOTS),
90
+ rest: series.slice(CHART_SERIES_SLOTS)
91
+ };
92
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * The conversation data model. Agent-agnostic on purpose: nothing here names a
3
+ * particular assistant, and the identity shown in the header is a prop.
4
+ *
5
+ * A message is an ordered list of parts rather than a string, because a single
6
+ * agent reply genuinely interleaves kinds — think, call a tool, think again,
7
+ * answer. Flattening that to `text` plus a `toolCalls` array loses the order,
8
+ * and the order is the only thing that makes a transcript readable.
9
+ */
10
+ export type ChatRole = "user" | "agent" | "system";
11
+ /** Delivery state of a message the user sent. Absent on received messages. */
12
+ export type MessageStatus = "sending" | "sent" | "failed";
13
+ export type TextPart = {
14
+ readonly type: "text";
15
+ readonly text: string;
16
+ };
17
+ /** Thinking text. One cumulative string per turn — there are no segment boundaries. */
18
+ export type ReasoningPart = {
19
+ readonly type: "reasoning";
20
+ readonly text: string;
21
+ };
22
+ /**
23
+ * Tool lifecycle, restricted to states a caller can actually distinguish:
24
+ * running, finished, or finished badly. There is no "input streaming" state
25
+ * because a surface cannot render one usefully.
26
+ */
27
+ export type ToolPartState = "running" | "done" | "error";
28
+ export type ToolPart = {
29
+ readonly type: "tool";
30
+ readonly toolCallId: string;
31
+ /** Raw tool identifier, e.g. `slack__post_message`. */
32
+ readonly toolName: string;
33
+ readonly state: ToolPartState;
34
+ /** Human phrasing for what the tool did. Falls back to `toolName` when absent. */
35
+ readonly label?: string;
36
+ readonly input?: Readonly<Record<string, unknown>>;
37
+ /** Result or failure text, already stringified by the host. */
38
+ readonly output?: string;
39
+ };
40
+ export type FilePart = {
41
+ readonly type: "file";
42
+ readonly name: string;
43
+ readonly mediaType: string;
44
+ /** Resolved by the host — this model never fetches bytes. */
45
+ readonly url?: string;
46
+ };
47
+ export type MessagePart = TextPart | ReasoningPart | ToolPart | FilePart;
48
+ export type ChatMessage = {
49
+ readonly id: string;
50
+ readonly role: ChatRole;
51
+ readonly parts: readonly MessagePart[];
52
+ /** ISO timestamp. */
53
+ readonly createdAt: string;
54
+ readonly status?: MessageStatus;
55
+ };
56
+ /** A tappable suggestion the agent offered. */
57
+ export type QuickReply = {
58
+ readonly id: string;
59
+ readonly label: string;
60
+ /** Sent instead of the label when present. */
61
+ readonly value?: string;
62
+ };
63
+ /** Who the user is talking to. The host names its own agent. */
64
+ export type AgentIdentity = {
65
+ readonly name: string;
66
+ readonly tagline?: string;
67
+ /** Two or three characters for the avatar. Derived from `name` when absent. */
68
+ readonly initials?: string;
69
+ };
70
+ /**
71
+ * Work the agent handed to a helper of its own.
72
+ *
73
+ * The same four states as a tool call plus `queued`, because a delegated task
74
+ * that has not started yet is a thing the user can see and a tool call is not.
75
+ * `tools` is the helper's own calls, so a subagent's detail is the same
76
+ * narrative the main transcript already renders rather than a second format.
77
+ */
78
+ export type SubagentState = "queued" | "running" | "done" | "error";
79
+ export type SubagentRun = {
80
+ readonly id: string;
81
+ /** What this helper is, e.g. "Researcher". Named by the host, not the registry. */
82
+ readonly name: string;
83
+ /** The one-line instruction it was given. */
84
+ readonly task?: string;
85
+ readonly state: SubagentState;
86
+ /** ISO timestamp. Absent while queued. */
87
+ readonly startedAt?: string;
88
+ readonly tools?: readonly ToolPart[];
89
+ /** Its answer, once it has one. */
90
+ readonly result?: string;
91
+ };
92
+ /** One conversation in the thread list. */
93
+ export type ChatThreadSummary = {
94
+ readonly id: string;
95
+ readonly title: string;
96
+ /** ISO timestamp of the most recent message. */
97
+ readonly updatedAt: string;
98
+ readonly preview?: string;
99
+ };
100
+ /** The answer text: every text part joined, thinking and tools excluded. */
101
+ export declare function messageText(message: ChatMessage): string;
102
+ /** The turn's thinking, if the agent exposed any. */
103
+ export declare function reasoningText(message: ChatMessage): string;
104
+ export declare function toolParts(message: ChatMessage): readonly ToolPart[];
105
+ /** True while any tool in the turn is still running — drives the live marker. */
106
+ export declare function isTurnWorking(message: ChatMessage): boolean;
107
+ /**
108
+ * A readable name for a tool id. `slack__post_message` → "Post message
109
+ * (Slack)"; `mail_send` → "Mail send". Never shows a raw snake_case id, and
110
+ * never invents a label for a tool it does not recognise — it only reformats
111
+ * what it was given, so a new tool reads correctly with no table to update.
112
+ */
113
+ export declare function toolLabel(part: ToolPart): string;
114
+ /** Avatar initials from a display name, capped at two characters. */
115
+ export declare function agentInitials(identity: AgentIdentity): string;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The conversation data model. Agent-agnostic on purpose: nothing here names a
3
+ * particular assistant, and the identity shown in the header is a prop.
4
+ *
5
+ * A message is an ordered list of parts rather than a string, because a single
6
+ * agent reply genuinely interleaves kinds — think, call a tool, think again,
7
+ * answer. Flattening that to `text` plus a `toolCalls` array loses the order,
8
+ * and the order is the only thing that makes a transcript readable.
9
+ */ /** The answer text: every text part joined, thinking and tools excluded. */ export function messageText(message) {
10
+ return message.parts.filter((part)=>part.type === "text").map((part)=>part.text).join("");
11
+ }
12
+ /** The turn's thinking, if the agent exposed any. */ export function reasoningText(message) {
13
+ return message.parts.filter((part)=>part.type === "reasoning").map((part)=>part.text).join("");
14
+ }
15
+ export function toolParts(message) {
16
+ return message.parts.filter((part)=>part.type === "tool");
17
+ }
18
+ /** True while any tool in the turn is still running — drives the live marker. */ export function isTurnWorking(message) {
19
+ return toolParts(message).some((part)=>part.state === "running");
20
+ }
21
+ /**
22
+ * A readable name for a tool id. `slack__post_message` → "Post message
23
+ * (Slack)"; `mail_send` → "Mail send". Never shows a raw snake_case id, and
24
+ * never invents a label for a tool it does not recognise — it only reformats
25
+ * what it was given, so a new tool reads correctly with no table to update.
26
+ */ export function toolLabel(part) {
27
+ if (part.label !== undefined && part.label.length > 0) return part.label;
28
+ const [provider, ...rest] = part.toolName.split("__");
29
+ const humanise = (value)=>{
30
+ const words = value.replace(/[_-]+/g, " ").trim();
31
+ return words.charAt(0).toUpperCase() + words.slice(1);
32
+ };
33
+ if (rest.length > 0 && provider !== undefined) {
34
+ return `${humanise(rest.join("__"))} (${humanise(provider)})`;
35
+ }
36
+ return humanise(part.toolName);
37
+ }
38
+ /** Avatar initials from a display name, capped at two characters. */ export function agentInitials(identity) {
39
+ if (identity.initials !== undefined) return identity.initials;
40
+ return identity.name.split(/\s+/).map((word)=>word.charAt(0)).join("").slice(0, 2).toUpperCase() || "··";
41
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The structural shape of a message's content: an ordered list of `Part`s
3
+ * rather than a string, because a single turn genuinely interleaves kinds —
4
+ * think, call a tool, emit a generative block, answer.
5
+ *
6
+ * This mirrors the wire shape a host's own parsed/validated part type would
7
+ * take (text, reasoning, tool-trace, block, file, event) but carries no
8
+ * validation of its own — parsing untrusted data into this shape is the
9
+ * host's job, at its own trust boundary, with whatever schema library it
10
+ * already uses. This package only renders a `Part[]` it is handed.
11
+ *
12
+ * Members are named `Part*` rather than the bare `TextPart` / `FilePart` a
13
+ * host might reach for on its own, because `lib/chat-message.ts` already
14
+ * publishes those names for its own, simpler message model — the two are
15
+ * deliberately separate types.
16
+ */
17
+ export type PartText = {
18
+ readonly kind: "text";
19
+ readonly text: string;
20
+ };
21
+ /** Thinking text, plus how long the agent spent producing it. */
22
+ export type PartReasoning = {
23
+ readonly kind: "reasoning";
24
+ readonly text: string;
25
+ /** Wall-clock milliseconds spent on this reasoning segment, once known. */
26
+ readonly durationMs?: number;
27
+ };
28
+ /**
29
+ * A tool call's lifecycle. Six states, not three: a tool call is not only
30
+ * running-or-finished, it can wait on human approval and that approval can be
31
+ * refused — a state distinct from an execution error. See `ToolBlockState`
32
+ * (`ui/tool-block.tsx`) for the matching render-side union.
33
+ */
34
+ export type ToolTraceStatus = "pending" | "running" | "output-available" | "error" | "approval-requested" | "output-denied";
35
+ export type PartToolTrace = {
36
+ readonly kind: "tool-trace";
37
+ readonly toolCallId: string;
38
+ readonly name: string;
39
+ readonly status: ToolTraceStatus;
40
+ readonly input?: unknown;
41
+ readonly output?: unknown;
42
+ };
43
+ /** A generative-UI block. `data` is opaque here — a host-specific registry parses it. */
44
+ export type PartBlock = {
45
+ readonly kind: "block";
46
+ readonly block: {
47
+ readonly type: string;
48
+ readonly data: unknown;
49
+ };
50
+ };
51
+ export type PartFile = {
52
+ readonly kind: "file";
53
+ readonly name: string;
54
+ readonly mediaType: string;
55
+ /** Resolved by the host — this model never fetches bytes. */
56
+ readonly url?: string;
57
+ };
58
+ /** A system/lifecycle line, e.g. "agent joined". `data` carries whatever the event needs. */
59
+ export type PartEvent = {
60
+ readonly kind: "event";
61
+ readonly event: string;
62
+ readonly data?: unknown;
63
+ };
64
+ export type Part = PartText | PartReasoning | PartToolTrace | PartBlock | PartFile | PartEvent;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The structural shape of a message's content: an ordered list of `Part`s
3
+ * rather than a string, because a single turn genuinely interleaves kinds —
4
+ * think, call a tool, emit a generative block, answer.
5
+ *
6
+ * This mirrors the wire shape a host's own parsed/validated part type would
7
+ * take (text, reasoning, tool-trace, block, file, event) but carries no
8
+ * validation of its own — parsing untrusted data into this shape is the
9
+ * host's job, at its own trust boundary, with whatever schema library it
10
+ * already uses. This package only renders a `Part[]` it is handed.
11
+ *
12
+ * Members are named `Part*` rather than the bare `TextPart` / `FilePart` a
13
+ * host might reach for on its own, because `lib/chat-message.ts` already
14
+ * publishes those names for its own, simpler message model — the two are
15
+ * deliberately separate types.
16
+ */ export { };
@@ -0,0 +1,60 @@
1
+ /**
2
+ * A CSV parser sized for previewing a file in a browser tab, not for ingesting
3
+ * one.
4
+ *
5
+ * The distinction matters and drives every decision here. A preview has a
6
+ * hostile input (a user's arbitrary file), a hard latency budget (the tab must
7
+ * stay responsive), and a low cost of being approximate (the user can always
8
+ * download the original). So this refuses oversized input up front instead of
9
+ * streaming it, caps what it hands back instead of virtualising, and reports
10
+ * "not tabular" instead of guessing — a ragged file rendered as a grid puts
11
+ * cells under the wrong headers, which is a worse failure than showing raw text.
12
+ */
13
+ /** Rows past this are dropped; the caller must say so in the UI. */
14
+ export declare const CSV_ROW_CAP = 500;
15
+ /** Columns past this are dropped; the caller must say so in the UI. */
16
+ export declare const CSV_COLUMN_CAP = 40;
17
+ /**
18
+ * Refuse to parse beyond this. 2 MiB of CSV is roughly 20k rows — already far
19
+ * past what anyone reads on screen, and enough DOM to lock the tab.
20
+ */
21
+ export declare const CSV_MAX_PREVIEW_BYTES: number;
22
+ export type ParsedCsv = {
23
+ readonly headers: readonly string[];
24
+ readonly rows: readonly (readonly string[])[];
25
+ };
26
+ /**
27
+ * True UTF-8 length, not `string.length`.
28
+ *
29
+ * `"é".length` is 1 but it costs 2 bytes, and a file of CJK text is three times
30
+ * its character count — sizing the guard by character count would let a file
31
+ * three times over budget through.
32
+ */
33
+ export declare function utf8ByteLength(text: string): number;
34
+ /**
35
+ * Parses CSV text, or returns `null` when it is too large to parse safely.
36
+ *
37
+ * `null` rather than a throw: "too big to preview" is an expected outcome of
38
+ * showing a user their own file, not an exceptional one, and the caller's
39
+ * response is to render a different surface rather than to catch.
40
+ */
41
+ export declare function parseCsv(text: string): ParsedCsv | null;
42
+ /**
43
+ * Whether the parse is rectangular enough to render as a grid.
44
+ *
45
+ * Requires at least two columns and every row to match the header width. A
46
+ * single-column "CSV" is prose that happens to have no commas, and a ragged one
47
+ * has no reliable column alignment — in both cases raw text tells the truth and
48
+ * a table does not.
49
+ */
50
+ export declare function isTabular(parsed: ParsedCsv): boolean;
51
+ export type CappedCsv = {
52
+ readonly headers: readonly string[];
53
+ readonly rows: readonly (readonly string[])[];
54
+ readonly totalRows: number;
55
+ readonly totalColumns: number;
56
+ readonly rowsTruncated: boolean;
57
+ readonly columnsTruncated: boolean;
58
+ };
59
+ /** Trims a parse to the caps, keeping the original totals so the UI can say so. */
60
+ export declare function capCsv(parsed: ParsedCsv): CappedCsv;