@nanisoft/prism-ui 0.10.1 → 0.13.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 (334) hide show
  1. package/LICENSE +21 -21
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/blocks/app-shell-01/app-shell.js +4 -4
  4. package/dist/blocks/app-shell-01/index.d.ts +2 -2
  5. package/dist/blocks/app-shell-01/index.js +1 -1
  6. package/dist/blocks/auth-form-01/auth-form.d.ts +11 -1
  7. package/dist/blocks/auth-form-01/auth-form.d.ts.map +1 -1
  8. package/dist/blocks/auth-form-01/auth-form.js +11 -8
  9. package/dist/blocks/auth-form-01/index.d.ts +2 -2
  10. package/dist/blocks/auth-form-01/index.js +1 -1
  11. package/dist/blocks/cta-01/cta.d.ts +26 -21
  12. package/dist/blocks/cta-01/cta.d.ts.map +1 -1
  13. package/dist/blocks/cta-01/cta.js +2 -2
  14. package/dist/blocks/cta-01/index.d.ts +2 -2
  15. package/dist/blocks/cta-01/index.js +1 -1
  16. package/dist/blocks/data-table-01/data-table.js +7 -7
  17. package/dist/blocks/data-table-01/index.d.ts +2 -2
  18. package/dist/blocks/data-table-01/index.js +1 -1
  19. package/dist/blocks/empty-state-01/empty-state.d.ts +143 -0
  20. package/dist/blocks/empty-state-01/empty-state.d.ts.map +1 -0
  21. package/dist/blocks/empty-state-01/empty-state.js +104 -0
  22. package/dist/blocks/empty-state-01/index.d.ts +3 -0
  23. package/dist/blocks/empty-state-01/index.d.ts.map +1 -0
  24. package/dist/blocks/empty-state-01/index.js +1 -0
  25. package/dist/blocks/feature-grid-01/feature-grid.d.ts +28 -1
  26. package/dist/blocks/feature-grid-01/feature-grid.d.ts.map +1 -1
  27. package/dist/blocks/feature-grid-01/feature-grid.js +34 -3
  28. package/dist/blocks/feature-grid-01/index.d.ts +2 -2
  29. package/dist/blocks/feature-grid-01/index.js +1 -1
  30. package/dist/blocks/hero-01/hero.d.ts +49 -17
  31. package/dist/blocks/hero-01/hero.d.ts.map +1 -1
  32. package/dist/blocks/hero-01/hero.js +14 -9
  33. package/dist/blocks/hero-01/index.d.ts +2 -1
  34. package/dist/blocks/hero-01/index.d.ts.map +1 -1
  35. package/dist/blocks/hero-01/index.js +1 -1
  36. package/dist/blocks/index.d.ts +20 -20
  37. package/dist/blocks/index.js +19 -19
  38. package/dist/blocks/instrument-panel-01/index.d.ts +2 -2
  39. package/dist/blocks/instrument-panel-01/index.js +1 -1
  40. package/dist/blocks/instrument-panel-01/instrument-panel.js +1 -1
  41. package/dist/blocks/logo-strip-01/index.d.ts +2 -2
  42. package/dist/blocks/logo-strip-01/index.js +1 -1
  43. package/dist/blocks/logo-strip-01/logo-strip.d.ts +1 -1
  44. package/dist/blocks/logo-strip-01/logo-strip.js +2 -2
  45. package/dist/blocks/note-grid-01/index.d.ts +2 -2
  46. package/dist/blocks/note-grid-01/index.js +1 -1
  47. package/dist/blocks/note-grid-01/note-grid.d.ts +1 -1
  48. package/dist/blocks/note-grid-01/note-grid.js +1 -1
  49. package/dist/blocks/page-header-01/index.d.ts +2 -2
  50. package/dist/blocks/page-header-01/index.js +1 -1
  51. package/dist/blocks/page-header-01/page-header.d.ts +1 -1
  52. package/dist/blocks/page-header-01/page-header.js +4 -4
  53. package/dist/blocks/pricing-01/index.d.ts +1 -1
  54. package/dist/blocks/pricing-01/index.js +1 -1
  55. package/dist/blocks/pricing-01/pricing.d.ts +8 -1
  56. package/dist/blocks/pricing-01/pricing.d.ts.map +1 -1
  57. package/dist/blocks/pricing-01/pricing.js +15 -5
  58. package/dist/blocks/process-flow-01/index.d.ts +3 -0
  59. package/dist/blocks/process-flow-01/index.d.ts.map +1 -0
  60. package/dist/blocks/process-flow-01/index.js +1 -0
  61. package/dist/blocks/process-flow-01/process-flow.d.ts +118 -0
  62. package/dist/blocks/process-flow-01/process-flow.d.ts.map +1 -0
  63. package/dist/blocks/process-flow-01/process-flow.js +71 -0
  64. package/dist/blocks/process-rail-01/index.d.ts +2 -2
  65. package/dist/blocks/process-rail-01/index.js +1 -1
  66. package/dist/blocks/process-rail-01/process-rail.d.ts +1 -1
  67. package/dist/blocks/process-rail-01/process-rail.js +2 -2
  68. package/dist/blocks/product-grid-01/index.d.ts +2 -2
  69. package/dist/blocks/product-grid-01/index.js +1 -1
  70. package/dist/blocks/product-grid-01/product-grid.d.ts +37 -5
  71. package/dist/blocks/product-grid-01/product-grid.d.ts.map +1 -1
  72. package/dist/blocks/product-grid-01/product-grid.js +20 -6
  73. package/dist/blocks/run-console-01/index.d.ts +3 -0
  74. package/dist/blocks/run-console-01/index.d.ts.map +1 -0
  75. package/dist/blocks/run-console-01/index.js +1 -0
  76. package/dist/blocks/run-console-01/run-console.d.ts +111 -0
  77. package/dist/blocks/run-console-01/run-console.d.ts.map +1 -0
  78. package/dist/blocks/run-console-01/run-console.js +48 -0
  79. package/dist/blocks/settings-panel-01/index.d.ts +2 -2
  80. package/dist/blocks/settings-panel-01/index.js +1 -1
  81. package/dist/blocks/settings-panel-01/settings-panel.d.ts +11 -1
  82. package/dist/blocks/settings-panel-01/settings-panel.d.ts.map +1 -1
  83. package/dist/blocks/settings-panel-01/settings-panel.js +15 -10
  84. package/dist/blocks/site-footer/index.d.ts +2 -2
  85. package/dist/blocks/site-footer/index.js +1 -1
  86. package/dist/blocks/site-footer/site-footer.d.ts +1 -1
  87. package/dist/blocks/site-footer/site-footer.js +2 -2
  88. package/dist/blocks/site-header/index.d.ts +2 -2
  89. package/dist/blocks/site-header/index.js +1 -1
  90. package/dist/blocks/site-header/site-header.d.ts +2 -2
  91. package/dist/blocks/site-header/site-header.js +3 -3
  92. package/dist/blocks/site-navbar/index.d.ts +3 -0
  93. package/dist/blocks/site-navbar/index.d.ts.map +1 -0
  94. package/dist/blocks/site-navbar/index.js +1 -0
  95. package/dist/blocks/site-navbar/mobile-nav.d.ts +48 -0
  96. package/dist/blocks/site-navbar/mobile-nav.d.ts.map +1 -0
  97. package/dist/blocks/site-navbar/mobile-nav.js +35 -0
  98. package/dist/blocks/site-navbar/mode-toggle.d.ts +39 -0
  99. package/dist/blocks/site-navbar/mode-toggle.d.ts.map +1 -0
  100. package/dist/blocks/site-navbar/mode-toggle.js +39 -0
  101. package/dist/blocks/site-navbar/navbar-controls.d.ts +79 -0
  102. package/dist/blocks/site-navbar/navbar-controls.d.ts.map +1 -0
  103. package/dist/blocks/site-navbar/navbar-controls.js +40 -0
  104. package/dist/blocks/site-navbar/search-entry.d.ts +29 -0
  105. package/dist/blocks/site-navbar/search-entry.d.ts.map +1 -0
  106. package/dist/blocks/site-navbar/search-entry.js +52 -0
  107. package/dist/blocks/site-navbar/site-navbar.d.ts +212 -0
  108. package/dist/blocks/site-navbar/site-navbar.d.ts.map +1 -0
  109. package/dist/blocks/site-navbar/site-navbar.js +61 -0
  110. package/dist/blocks/site-navbar/sites-menu.d.ts +40 -0
  111. package/dist/blocks/site-navbar/sites-menu.d.ts.map +1 -0
  112. package/dist/blocks/site-navbar/sites-menu.js +48 -0
  113. package/dist/blocks/site-navbar/theme-menu.d.ts +47 -0
  114. package/dist/blocks/site-navbar/theme-menu.d.ts.map +1 -0
  115. package/dist/blocks/site-navbar/theme-menu.js +55 -0
  116. package/dist/blocks/stack-grid-01/index.d.ts +2 -2
  117. package/dist/blocks/stack-grid-01/index.js +1 -1
  118. package/dist/blocks/stack-grid-01/stack-grid.d.ts +42 -11
  119. package/dist/blocks/stack-grid-01/stack-grid.d.ts.map +1 -1
  120. package/dist/blocks/stack-grid-01/stack-grid.js +15 -7
  121. package/dist/blocks/stats-01/index.d.ts +9 -1
  122. package/dist/blocks/stats-01/index.d.ts.map +1 -1
  123. package/dist/blocks/stats-01/index.js +8 -1
  124. package/dist/blocks/stats-01/stats.d.ts +1 -1
  125. package/dist/blocks/stats-01/stats.js +2 -2
  126. package/dist/blocks/status-ledger-01/index.d.ts +2 -2
  127. package/dist/blocks/status-ledger-01/index.js +1 -1
  128. package/dist/blocks/status-ledger-01/status-ledger.d.ts +1 -1
  129. package/dist/blocks/status-ledger-01/status-ledger.js +2 -2
  130. package/dist/catalog.d.ts +1 -1
  131. package/dist/catalog.d.ts.map +1 -1
  132. package/dist/catalog.js +473 -1
  133. package/dist/components/index.d.ts +104 -66
  134. package/dist/components/index.d.ts.map +1 -1
  135. package/dist/components/index.js +74 -40
  136. package/dist/components/ui/accordion.js +1 -1
  137. package/dist/components/ui/alert-dialog.d.ts +119 -0
  138. package/dist/components/ui/alert-dialog.d.ts.map +1 -0
  139. package/dist/components/ui/alert-dialog.js +82 -0
  140. package/dist/components/ui/alert.js +1 -1
  141. package/dist/components/ui/aspect-ratio.d.ts +60 -0
  142. package/dist/components/ui/aspect-ratio.d.ts.map +1 -0
  143. package/dist/components/ui/aspect-ratio.js +61 -0
  144. package/dist/components/ui/avatar.js +1 -1
  145. package/dist/components/ui/badge.js +1 -1
  146. package/dist/components/ui/breadcrumb.js +1 -1
  147. package/dist/components/ui/button-group.d.ts +68 -0
  148. package/dist/components/ui/button-group.d.ts.map +1 -0
  149. package/dist/components/ui/button-group.js +54 -0
  150. package/dist/components/ui/button.js +1 -1
  151. package/dist/components/ui/calendar.d.ts +158 -0
  152. package/dist/components/ui/calendar.d.ts.map +1 -0
  153. package/dist/components/ui/calendar.js +216 -0
  154. package/dist/components/ui/card.d.ts +18 -6
  155. package/dist/components/ui/card.d.ts.map +1 -1
  156. package/dist/components/ui/card.js +23 -8
  157. package/dist/components/ui/carousel.d.ts +118 -0
  158. package/dist/components/ui/carousel.d.ts.map +1 -0
  159. package/dist/components/ui/carousel.js +152 -0
  160. package/dist/components/ui/chart-frame.d.ts +216 -0
  161. package/dist/components/ui/chart-frame.d.ts.map +1 -0
  162. package/dist/components/ui/chart-frame.js +286 -0
  163. package/dist/components/ui/checkbox.js +1 -1
  164. package/dist/components/ui/collapsible.d.ts +97 -0
  165. package/dist/components/ui/collapsible.d.ts.map +1 -0
  166. package/dist/components/ui/collapsible.js +54 -0
  167. package/dist/components/ui/combobox.d.ts +141 -0
  168. package/dist/components/ui/combobox.d.ts.map +1 -0
  169. package/dist/components/ui/combobox.js +243 -0
  170. package/dist/components/ui/command-palette.d.ts +106 -0
  171. package/dist/components/ui/command-palette.d.ts.map +1 -0
  172. package/dist/components/ui/command-palette.js +176 -0
  173. package/dist/components/ui/command.d.ts +82 -0
  174. package/dist/components/ui/command.d.ts.map +1 -0
  175. package/dist/components/ui/command.js +37 -0
  176. package/dist/components/ui/context-menu.d.ts +151 -0
  177. package/dist/components/ui/context-menu.d.ts.map +1 -0
  178. package/dist/components/ui/context-menu.js +97 -0
  179. package/dist/components/ui/cta-link.js +1 -1
  180. package/dist/components/ui/date-picker.d.ts +111 -0
  181. package/dist/components/ui/date-picker.d.ts.map +1 -0
  182. package/dist/components/ui/date-picker.js +74 -0
  183. package/dist/components/ui/diagram.d.ts +84 -15
  184. package/dist/components/ui/diagram.d.ts.map +1 -1
  185. package/dist/components/ui/diagram.js +77 -7
  186. package/dist/components/ui/dialog.js +1 -1
  187. package/dist/components/ui/diff.d.ts +105 -0
  188. package/dist/components/ui/diff.d.ts.map +1 -0
  189. package/dist/components/ui/diff.js +113 -0
  190. package/dist/components/ui/dropdown-menu.d.ts +18 -0
  191. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  192. package/dist/components/ui/dropdown-menu.js +1 -1
  193. package/dist/components/ui/fact-list.js +1 -1
  194. package/dist/components/ui/field.js +1 -1
  195. package/dist/components/ui/form.d.ts +151 -0
  196. package/dist/components/ui/form.d.ts.map +1 -0
  197. package/dist/components/ui/form.js +73 -0
  198. package/dist/components/ui/hover-card.d.ts +105 -0
  199. package/dist/components/ui/hover-card.d.ts.map +1 -0
  200. package/dist/components/ui/hover-card.js +49 -0
  201. package/dist/components/ui/input-group.d.ts +99 -0
  202. package/dist/components/ui/input-group.d.ts.map +1 -0
  203. package/dist/components/ui/input-group.js +83 -0
  204. package/dist/components/ui/input.js +1 -1
  205. package/dist/components/ui/item.d.ts +113 -0
  206. package/dist/components/ui/item.d.ts.map +1 -0
  207. package/dist/components/ui/item.js +61 -0
  208. package/dist/components/ui/kbd.js +1 -1
  209. package/dist/components/ui/label.d.ts +58 -0
  210. package/dist/components/ui/label.d.ts.map +1 -0
  211. package/dist/components/ui/label.js +29 -0
  212. package/dist/components/ui/live-region.d.ts +9 -0
  213. package/dist/components/ui/live-region.d.ts.map +1 -1
  214. package/dist/components/ui/live-region.js +10 -2
  215. package/dist/components/ui/mark.js +1 -1
  216. package/dist/components/ui/menubar.d.ts +197 -0
  217. package/dist/components/ui/menubar.d.ts.map +1 -0
  218. package/dist/components/ui/menubar.js +109 -0
  219. package/dist/components/ui/meter.d.ts +121 -0
  220. package/dist/components/ui/meter.d.ts.map +1 -0
  221. package/dist/components/ui/meter.js +82 -0
  222. package/dist/components/ui/native-select.d.ts +72 -0
  223. package/dist/components/ui/native-select.d.ts.map +1 -0
  224. package/dist/components/ui/native-select.js +55 -0
  225. package/dist/components/ui/navigation-menu.d.ts +186 -0
  226. package/dist/components/ui/navigation-menu.d.ts.map +1 -0
  227. package/dist/components/ui/navigation-menu.js +94 -0
  228. package/dist/components/ui/number-field.d.ts +111 -0
  229. package/dist/components/ui/number-field.d.ts.map +1 -0
  230. package/dist/components/ui/number-field.js +84 -0
  231. package/dist/components/ui/one-time-code.d.ts +127 -0
  232. package/dist/components/ui/one-time-code.d.ts.map +1 -0
  233. package/dist/components/ui/one-time-code.js +57 -0
  234. package/dist/components/ui/pagination.js +1 -1
  235. package/dist/components/ui/popover.js +1 -1
  236. package/dist/components/ui/product-mark.d.ts +1 -1
  237. package/dist/components/ui/product-mark.js +1 -1
  238. package/dist/components/ui/product-switcher.d.ts +1 -1
  239. package/dist/components/ui/product-switcher.js +2 -2
  240. package/dist/components/ui/progress.js +1 -1
  241. package/dist/components/ui/prose.js +1 -1
  242. package/dist/components/ui/pulse-graph.js +1 -1
  243. package/dist/components/ui/pulse-series.js +1 -1
  244. package/dist/components/ui/radio-group.js +1 -1
  245. package/dist/components/ui/resizable.d.ts +129 -0
  246. package/dist/components/ui/resizable.d.ts.map +1 -0
  247. package/dist/components/ui/resizable.js +205 -0
  248. package/dist/components/ui/scroll-area.d.ts +97 -0
  249. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  250. package/dist/components/ui/scroll-area.js +68 -0
  251. package/dist/components/ui/search-dialog.d.ts +139 -0
  252. package/dist/components/ui/search-dialog.d.ts.map +1 -0
  253. package/dist/components/ui/search-dialog.js +178 -0
  254. package/dist/components/ui/section.d.ts +26 -0
  255. package/dist/components/ui/section.d.ts.map +1 -1
  256. package/dist/components/ui/section.js +32 -1
  257. package/dist/components/ui/select.js +1 -1
  258. package/dist/components/ui/separator.js +1 -1
  259. package/dist/components/ui/sheet.d.ts +103 -0
  260. package/dist/components/ui/sheet.d.ts.map +1 -0
  261. package/dist/components/ui/sheet.js +58 -0
  262. package/dist/components/ui/sidebar.d.ts +228 -0
  263. package/dist/components/ui/sidebar.d.ts.map +1 -0
  264. package/dist/components/ui/sidebar.js +172 -0
  265. package/dist/components/ui/signal-field.js +1 -1
  266. package/dist/components/ui/skeleton.js +1 -1
  267. package/dist/components/ui/slider.js +1 -1
  268. package/dist/components/ui/spinner.d.ts +79 -0
  269. package/dist/components/ui/spinner.d.ts.map +1 -0
  270. package/dist/components/ui/spinner.js +79 -0
  271. package/dist/components/ui/switch.js +1 -1
  272. package/dist/components/ui/table-sort.d.ts +97 -0
  273. package/dist/components/ui/table-sort.d.ts.map +1 -0
  274. package/dist/components/ui/table-sort.js +102 -0
  275. package/dist/components/ui/table.js +1 -1
  276. package/dist/components/ui/tabs.js +1 -1
  277. package/dist/components/ui/textarea.js +1 -1
  278. package/dist/components/ui/timeline.d.ts +106 -0
  279. package/dist/components/ui/timeline.d.ts.map +1 -0
  280. package/dist/components/ui/timeline.js +111 -0
  281. package/dist/components/ui/toast.d.ts +219 -0
  282. package/dist/components/ui/toast.d.ts.map +1 -0
  283. package/dist/components/ui/toast.js +270 -0
  284. package/dist/components/ui/toggle-group.d.ts +118 -0
  285. package/dist/components/ui/toggle-group.d.ts.map +1 -0
  286. package/dist/components/ui/toggle-group.js +194 -0
  287. package/dist/components/ui/toggle.d.ts +73 -0
  288. package/dist/components/ui/toggle.d.ts.map +1 -0
  289. package/dist/components/ui/toggle.js +44 -0
  290. package/dist/components/ui/tooltip.js +1 -1
  291. package/dist/components/ui/tree.js +1 -1
  292. package/dist/components/ui/typography.js +1 -1
  293. package/dist/fonts/Inter-OFL.txt +93 -93
  294. package/dist/index.d.ts +72 -70
  295. package/dist/index.d.ts.map +1 -1
  296. package/dist/index.js +61 -60
  297. package/dist/lib/rank.d.ts +69 -0
  298. package/dist/lib/rank.d.ts.map +1 -0
  299. package/dist/lib/rank.js +77 -0
  300. package/dist/live/index.d.ts +22 -0
  301. package/dist/live/index.d.ts.map +1 -0
  302. package/dist/live/index.js +20 -0
  303. package/dist/live/run-stream-01/index.d.ts +3 -0
  304. package/dist/live/run-stream-01/index.d.ts.map +1 -0
  305. package/dist/live/run-stream-01/index.js +1 -0
  306. package/dist/live/run-stream-01/run-stream.d.ts +125 -0
  307. package/dist/live/run-stream-01/run-stream.d.ts.map +1 -0
  308. package/dist/live/run-stream-01/run-stream.js +111 -0
  309. package/dist/pages/auth-page/index.d.ts +15 -2
  310. package/dist/pages/auth-page/index.d.ts.map +1 -1
  311. package/dist/pages/auth-page/index.js +8 -4
  312. package/dist/pages/blog-post-page/index.js +5 -5
  313. package/dist/pages/dashboard-page/index.d.ts +4 -4
  314. package/dist/pages/dashboard-page/index.js +4 -4
  315. package/dist/pages/docs-shell/docs-shell.d.ts +37 -4
  316. package/dist/pages/docs-shell/docs-shell.d.ts.map +1 -1
  317. package/dist/pages/docs-shell/docs-shell.js +107 -16
  318. package/dist/pages/docs-shell/index.d.ts +2 -2
  319. package/dist/pages/docs-shell/index.js +1 -1
  320. package/dist/pages/index.d.ts +14 -14
  321. package/dist/pages/index.js +7 -7
  322. package/dist/pages/marketing-page/index.d.ts +6 -6
  323. package/dist/pages/marketing-page/index.js +5 -5
  324. package/dist/pages/not-found-page/index.js +4 -4
  325. package/dist/pages/settings-page/index.d.ts +3 -3
  326. package/dist/pages/settings-page/index.js +4 -4
  327. package/dist/provider/index.d.ts +4 -4
  328. package/dist/provider/index.js +2 -2
  329. package/dist/provider/provider.d.ts +1 -1
  330. package/dist/provider/provider.js +1 -1
  331. package/dist/provider/theme-script.d.ts +1 -1
  332. package/dist/provider/theme-script.js +1 -1
  333. package/dist/styles.css +689 -0
  334. package/package.json +9 -1
@@ -0,0 +1,79 @@
1
+ import type { ComponentProps } from 'react';
2
+ /**
3
+ * The props a Spinner accepts.
4
+ *
5
+ * `label` is required and there is no default, which is the Component's one
6
+ * design decision and the reason it is not simply "a div with a border on it".
7
+ */
8
+ export interface SpinnerProps extends Omit<ComponentProps<'span'>, 'children'> {
9
+ /**
10
+ * The words a screen reader hears while the work is happening.
11
+ *
12
+ * Required, and required to be a real sentence rather than a verb. A busy
13
+ * indicator with no name is an animation, and an animation is nothing to a
14
+ * reader who cannot see it; the whole reason this Component exists rather
15
+ * than a border on a `div` is that it has to be able to say what is busy in
16
+ * the consumer's own words, because a shared library cannot know whether the
17
+ * answer is "Loading invoices" or "Carregando faturas".
18
+ *
19
+ * The words name the work, not the Component. "Loading" alone is a weaker
20
+ * name than "Loading invoices", and a caller who has two spinners on a page
21
+ * needs to tell them apart in the same way a sighted reader does.
22
+ */
23
+ label: string;
24
+ /**
25
+ * The diameter of the ring, in one of three authored steps.
26
+ *
27
+ * A prop rather than something a caller sets through `className`, because a
28
+ * ring whose size is a layout decision is a ring that can be 2px and become
29
+ * invisible, and `className` is layout only on every Component in this
30
+ * package. Three steps is what a spinner is ever drawn at: beside body text,
31
+ * beside a control's label, and alone in a region of its own.
32
+ *
33
+ * @defaultValue 'default'
34
+ */
35
+ size?: 'sm' | 'default' | 'lg';
36
+ }
37
+ /**
38
+ * A mark that says work is happening and nothing about how much of it is left.
39
+ *
40
+ * **It is not a Progress and it is not a Skeleton, and the three are confused
41
+ * often enough to be worth stating.** A Skeleton stands in for content: it has
42
+ * the shape of the thing that is arriving, so the page does not move when the
43
+ * content lands. A Progress reports a position: it carries a value, a minimum
44
+ * and a maximum, and a screen reader is told "68 percent" rather than the width
45
+ * of a coloured bar. A Spinner reports exactly one fact, that something is
46
+ * happening, and carries neither a shape nor a value. Pick by the question:
47
+ * *what will arrive* is a Skeleton, *how far along* is a Progress, and *is
48
+ * anything happening at all* is a Spinner.
49
+ *
50
+ * **The honest reason a Spinner is often the wrong answer is that it causes a
51
+ * layout shift.** The ring is a fixed square, so the content it stands in for
52
+ * is not, and everything below it moves twice: once when the content is
53
+ * replaced by the ring, and again when the ring is replaced by the content.
54
+ * A reader's eye has to find the thing twice. That is the whole cost of this
55
+ * Component and it is not paid when the shape of what is coming is already
56
+ * known, which is why:
57
+ *
58
+ * - **Do not use a Spinner as a page-level loading state where the layout is
59
+ * known.** A first paint of Skeletons is a better answer, and
60
+ * `Skeleton` is the Component for it. A Spinner alone in the middle of an
61
+ * empty page tells the reader nothing about the page they are about to get.
62
+ * - **Do not use a Spinner for a task of known length.** A countable upload
63
+ * is a `Progress`, and a bar that shows real information beats a ring that
64
+ * shows anxiety.
65
+ * - **Do not use a Spinner for a wait under about a second.** A control that
66
+ * disables itself for 400ms and then re-enables is a control that flickers.
67
+ * A `Skeleton` that never gets to paint, or nothing at all, is quieter.
68
+ * - **Do not use a Spinner inside a Button next to a label that already says
69
+ * what is happening.** The label is the announcement; the ring is the
70
+ * confirmation, and beside a label that already reads "Saving..." it is a
71
+ * second copy of a sentence the reader can already see.
72
+ *
73
+ * **It is a server Component.** It reads its props and renders, so the client
74
+ * directive would be a claim about work this Component does not do. A hundred
75
+ * spinners on a page cost no JavaScript.
76
+ */
77
+ declare function Spinner({ className, label, size, ...props }: SpinnerProps): import("react").JSX.Element;
78
+ export { Spinner };
79
+ //# sourceMappingURL=spinner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../../src/components/ui/spinner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAI3C;;;;;GAKG;AACH,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAC5E;;;;;;;;;;;;;OAaG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,GAAG,IAAI,CAAA;CAC/B;AA0BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,iBAAS,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,IAAgB,EAAE,GAAG,KAAK,EAAE,EAAE,YAAY,+BA+B9E;AAED,OAAO,EAAE,OAAO,EAAE,CAAA"}
@@ -0,0 +1,79 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from '../../lib/utils.js';
3
+ /**
4
+ * The ring, at each authored size.
5
+ *
6
+ * `size-*` on the box and `border-2` on the arc. A spinner is drawn as a
7
+ * rounded box with one transparent edge, which is the shape that reads as
8
+ * "turning" without a keyframe: the gap in the border is what moves, and the
9
+ * box is a fixed frame around it.
10
+ *
11
+ * The one animation is `animate-spin`, and it is the only motion in this
12
+ * package that is a *rotation* rather than a transition between two states.
13
+ * A keyframe cannot be authored here - the design system emits no keyframes
14
+ * and a Component cannot add one - and `animate-spin` is a Tailwind utility
15
+ * that already carries its own, so this is the honest ceiling: a spinner that
16
+ * cannot be built from a transition between two states needs a loop, and a
17
+ * loop is what `animate-spin` is. It is state feedback rather than decoration
18
+ * for the reason `Timeline`'s pulsing mark is: it is running because work is
19
+ * running, and it stops when the work does.
20
+ */
21
+ const RING = {
22
+ sm: 'size-4 border-2',
23
+ default: 'size-5 border-2',
24
+ lg: 'size-8 border-[3px]',
25
+ };
26
+ /**
27
+ * A mark that says work is happening and nothing about how much of it is left.
28
+ *
29
+ * **It is not a Progress and it is not a Skeleton, and the three are confused
30
+ * often enough to be worth stating.** A Skeleton stands in for content: it has
31
+ * the shape of the thing that is arriving, so the page does not move when the
32
+ * content lands. A Progress reports a position: it carries a value, a minimum
33
+ * and a maximum, and a screen reader is told "68 percent" rather than the width
34
+ * of a coloured bar. A Spinner reports exactly one fact, that something is
35
+ * happening, and carries neither a shape nor a value. Pick by the question:
36
+ * *what will arrive* is a Skeleton, *how far along* is a Progress, and *is
37
+ * anything happening at all* is a Spinner.
38
+ *
39
+ * **The honest reason a Spinner is often the wrong answer is that it causes a
40
+ * layout shift.** The ring is a fixed square, so the content it stands in for
41
+ * is not, and everything below it moves twice: once when the content is
42
+ * replaced by the ring, and again when the ring is replaced by the content.
43
+ * A reader's eye has to find the thing twice. That is the whole cost of this
44
+ * Component and it is not paid when the shape of what is coming is already
45
+ * known, which is why:
46
+ *
47
+ * - **Do not use a Spinner as a page-level loading state where the layout is
48
+ * known.** A first paint of Skeletons is a better answer, and
49
+ * `Skeleton` is the Component for it. A Spinner alone in the middle of an
50
+ * empty page tells the reader nothing about the page they are about to get.
51
+ * - **Do not use a Spinner for a task of known length.** A countable upload
52
+ * is a `Progress`, and a bar that shows real information beats a ring that
53
+ * shows anxiety.
54
+ * - **Do not use a Spinner for a wait under about a second.** A control that
55
+ * disables itself for 400ms and then re-enables is a control that flickers.
56
+ * A `Skeleton` that never gets to paint, or nothing at all, is quieter.
57
+ * - **Do not use a Spinner inside a Button next to a label that already says
58
+ * what is happening.** The label is the announcement; the ring is the
59
+ * confirmation, and beside a label that already reads "Saving..." it is a
60
+ * second copy of a sentence the reader can already see.
61
+ *
62
+ * **It is a server Component.** It reads its props and renders, so the client
63
+ * directive would be a claim about work this Component does not do. A hundred
64
+ * spinners on a page cost no JavaScript.
65
+ */
66
+ function Spinner({ className, label, size = 'default', ...props }) {
67
+ return (_jsx("span", { "data-slot": "spinner",
68
+ // The role is what makes the label legal, and the reason is the same one
69
+ // `LiveRegion` gives: a `span` with no role has no accessible name, so
70
+ // `aria-label` on one is prohibited rather than merely ignored. `status`
71
+ // is also the honest role, because a spinner is content that reports a
72
+ // state and is not waiting for the reader. It is deliberately NOT
73
+ // `aria-busy`: a busy *region* is still readable, and which regions are
74
+ // busy is the caller's knowledge rather than this Component's, so a
75
+ // consumer that wants it wraps the region in a `LiveRegion` and sets
76
+ // `busy` there.
77
+ role: "status", "aria-label": label, className: cn('inline-flex shrink-0', className), ...props, children: _jsx("span", { "data-slot": "spinner-ring", "aria-hidden": "true", className: cn('border-current animate-spin rounded-full border-transparent border-t-current', RING[size]) }) }));
78
+ }
79
+ export { Spinner };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Switch as SwitchPrimitive } from '@base-ui/react/switch';
4
- import { cn } from '../../lib/utils';
4
+ import { cn } from '../../lib/utils.js';
5
5
  /**
6
6
  * A binary control that takes effect immediately.
7
7
  *
@@ -0,0 +1,97 @@
1
+ import { type ComponentProps, type ReactNode } from 'react';
2
+ /** Which way a column is sorted, and `none` for a column that is not. */
3
+ export type TableSortDirection = 'none' | 'ascending' | 'descending';
4
+ /** What activating a header will do, given where the column is now. */
5
+ export type TableSortAnnouncement = (direction: TableSortDirection) => string;
6
+ /** The props the TableSort accepts. */
7
+ export interface TableSortProps extends Omit<ComponentProps<'th'>, 'children'> {
8
+ /**
9
+ * The column's name, and the visible text of the header.
10
+ *
11
+ * It is a `th` with `scope="col"` rather than a `button` dressed as a header,
12
+ * because the header is what a screen reader announces while a reader is moving
13
+ * across the table, and a header that is not a header is a row of buttons above a
14
+ * grid with no column names in it.
15
+ */
16
+ children?: ReactNode;
17
+ /** The column this header sorts. */
18
+ column: string;
19
+ /** The direction the column is currently sorted in. @defaultValue 'none' */
20
+ direction?: TableSortDirection;
21
+ /**
22
+ * Called with the direction the column will be in after this activation.
23
+ *
24
+ * The caller owns the sort and the rows, because a Component cannot sort an
25
+ * arbitrary set of records: it does not know what the column means, whether it
26
+ * is a number or a date or a name, or what the original order was. So the
27
+ * header reports the direction and the caller's table reorders.
28
+ */
29
+ onDirectionChange?: (direction: TableSortDirection) => void;
30
+ /**
31
+ * The words read for what activating the header will do.
32
+ *
33
+ * Required, and required as a **function of the next direction** rather than as
34
+ * a string, because the announcement has to say what *will* happen, not what is.
35
+ * A header that announces "sorted ascending" when it is already ascending tells
36
+ * a reader nothing about the press they are about to make, and one that
37
+ * announces "sort" says nothing about which way. The words are the caller's for
38
+ * the reason every other reader-facing string in this package is: a shared
39
+ * library cannot know whether the word for this is "sort", "trier" or
40
+ * "sortieren", and a sentence it chose would be inherited by every consumer in
41
+ * a language they did not write.
42
+ */
43
+ announce: TableSortAnnouncement;
44
+ /** Whether this header ignores user interaction. */
45
+ disabled?: boolean;
46
+ /** Layout only. */
47
+ className?: string;
48
+ }
49
+ /**
50
+ * The sortable header affordance for a `Table`.
51
+ *
52
+ * **`aria-sort` belongs on the `th`, and this is a `th`.** Not a `button` inside a
53
+ * `th`, and not a `th` with a `role="button"` on it. A column header is what a
54
+ * screen reader announces while a reader is moving across a table, and the sort
55
+ * state is a property of that header rather than of a control inside it, so
56
+ * `aria-sort` sits on the cell where the reader is told about it. It is set on
57
+ * exactly one header at a time, which is the rule: two columns both reporting
58
+ * `aria-sort` is a table claiming to be ordered two ways at once, and a reader
59
+ * comparing the two hears a contradiction.
60
+ *
61
+ * **The announcement says what will happen, not what is.** That is the decision
62
+ * this Component is built around, and it is why `announce` is a function of the
63
+ * next direction rather than a string. A reader who is about to press a header
64
+ * needs to know what the press will do, and the honest way to say it depends on
65
+ * the column's current state: on an unsorted column the press sorts ascending, on
66
+ * an ascending one it reverses, on a descending one it returns the original
67
+ * order. One string cannot be right for all three, and a string chosen for one of
68
+ * them is wrong for the other two in a way the reader cannot detect.
69
+ *
70
+ * **Two clicks to descending, three back to unsorted, and the third is the point.**
71
+ * The cycle is `none`, `ascending`, `descending`, and back to `none`, and the last
72
+ * step is a decision rather than an omission. A reader who has sorted to find the
73
+ * largest value and no longer cares has to be able to get the original order back
74
+ * from the control they already know, because any other route is a second
75
+ * affordance this package would then have to document. The announcement prop
76
+ * exists so a caller can say what that third press does in their own words, which
77
+ * is the one sentence in the whole interaction Prism cannot write for them.
78
+ *
79
+ * **The sort is the caller's.** A Component cannot sort an arbitrary set of
80
+ * records: it does not know whether the column is a number, a date or a name, it
81
+ * does not know the locale a name sorts in, and it does not know the original
82
+ * order it would have to restore. So this header owns the state it displays and
83
+ * reports the direction, and the caller's table reorders the rows.
84
+ *
85
+ * **The mark is a shape as well as a colour.** An unsorted column carries the
86
+ * two-headed mark, an ascending one an up arrow and a descending one a down
87
+ * arrow, and the mark is `aria-hidden` because the state is already on the cell in
88
+ * `aria-sort` and a reader is not told twice. A reader who cannot separate the
89
+ * three marks still gets the state from the attribute, which is the same order the
90
+ * whole package keeps: the attribute is the fact and the drawing is the reminder.
91
+ *
92
+ * It is a client Component, because a header that reports a direction and holds
93
+ * the announcement is holding state and attaching a handler.
94
+ */
95
+ declare function TableSort({ children, column, direction, onDirectionChange, announce, disabled, className, ...props }: TableSortProps): import("react").JSX.Element;
96
+ export { TableSort };
97
+ //# sourceMappingURL=table-sort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-sort.d.ts","sourceRoot":"","sources":["../../../src/components/ui/table-sort.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAI5E,yEAAyE;AACzE,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,WAAW,GAAG,YAAY,CAAA;AAEpE,uEAAuE;AACvE,MAAM,MAAM,qBAAqB,GAAG,CAAC,SAAS,EAAE,kBAAkB,KAAK,MAAM,CAAA;AAE7E,uCAAuC;AACvC,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC5E;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,4EAA4E;IAC5E,SAAS,CAAC,EAAE,kBAAkB,CAAA;IAC9B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC3D;;;;;;;;;;;;OAYG;IACH,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,oDAAoD;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,mBAAmB;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA4BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,iBAAS,SAAS,CAAC,EACjB,QAAQ,EACR,MAAM,EACN,SAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,QAAgB,EAChB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,cAAc,+BAmFhB;AAED,OAAO,EAAE,SAAS,EAAE,CAAA"}
@@ -0,0 +1,102 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ArrowDownIcon, ArrowUpIcon, ChevronsUpDownIcon } from 'lucide-react';
4
+ import { useId, useState } from 'react';
5
+ import { cn } from '../../lib/utils.js';
6
+ /** The direction a click on a header in `from` produces. */
7
+ function nextDirection(from) {
8
+ // Unsorted, then ascending, then descending, then unsorted again.
9
+ //
10
+ // Returning to `none` is the decision worth making, and it is why the cycle has
11
+ // three states rather than two. A reader who has sorted a column to find the
12
+ // extreme and no longer cares has to get the original order back, and the only
13
+ // route to it is a third click on the header they already know. A two-state
14
+ // control makes that order unreachable from the control, which means the caller
15
+ // has to invent a second affordance for it, and a second affordance is a second
16
+ // thing a reader has to find. So the cycle closes.
17
+ if (from === 'none')
18
+ return 'ascending';
19
+ if (from === 'ascending')
20
+ return 'descending';
21
+ return 'none';
22
+ }
23
+ /** The mark each state draws, which is a shape and not only a colour. */
24
+ const MARK = {
25
+ // The unsorted mark is the two-headed one, because it says "this column can be
26
+ // ordered either way" rather than "this column is ordered". A single upward
27
+ // arrow on an unsorted column claims an order that is not in force.
28
+ none: _jsx(ChevronsUpDownIcon, { className: "size-3.5" }),
29
+ ascending: _jsx(ArrowUpIcon, { className: "size-3.5" }),
30
+ descending: _jsx(ArrowDownIcon, { className: "size-3.5" }),
31
+ };
32
+ /**
33
+ * The sortable header affordance for a `Table`.
34
+ *
35
+ * **`aria-sort` belongs on the `th`, and this is a `th`.** Not a `button` inside a
36
+ * `th`, and not a `th` with a `role="button"` on it. A column header is what a
37
+ * screen reader announces while a reader is moving across a table, and the sort
38
+ * state is a property of that header rather than of a control inside it, so
39
+ * `aria-sort` sits on the cell where the reader is told about it. It is set on
40
+ * exactly one header at a time, which is the rule: two columns both reporting
41
+ * `aria-sort` is a table claiming to be ordered two ways at once, and a reader
42
+ * comparing the two hears a contradiction.
43
+ *
44
+ * **The announcement says what will happen, not what is.** That is the decision
45
+ * this Component is built around, and it is why `announce` is a function of the
46
+ * next direction rather than a string. A reader who is about to press a header
47
+ * needs to know what the press will do, and the honest way to say it depends on
48
+ * the column's current state: on an unsorted column the press sorts ascending, on
49
+ * an ascending one it reverses, on a descending one it returns the original
50
+ * order. One string cannot be right for all three, and a string chosen for one of
51
+ * them is wrong for the other two in a way the reader cannot detect.
52
+ *
53
+ * **Two clicks to descending, three back to unsorted, and the third is the point.**
54
+ * The cycle is `none`, `ascending`, `descending`, and back to `none`, and the last
55
+ * step is a decision rather than an omission. A reader who has sorted to find the
56
+ * largest value and no longer cares has to be able to get the original order back
57
+ * from the control they already know, because any other route is a second
58
+ * affordance this package would then have to document. The announcement prop
59
+ * exists so a caller can say what that third press does in their own words, which
60
+ * is the one sentence in the whole interaction Prism cannot write for them.
61
+ *
62
+ * **The sort is the caller's.** A Component cannot sort an arbitrary set of
63
+ * records: it does not know whether the column is a number, a date or a name, it
64
+ * does not know the locale a name sorts in, and it does not know the original
65
+ * order it would have to restore. So this header owns the state it displays and
66
+ * reports the direction, and the caller's table reorders the rows.
67
+ *
68
+ * **The mark is a shape as well as a colour.** An unsorted column carries the
69
+ * two-headed mark, an ascending one an up arrow and a descending one a down
70
+ * arrow, and the mark is `aria-hidden` because the state is already on the cell in
71
+ * `aria-sort` and a reader is not told twice. A reader who cannot separate the
72
+ * three marks still gets the state from the attribute, which is the same order the
73
+ * whole package keeps: the attribute is the fact and the drawing is the reminder.
74
+ *
75
+ * It is a client Component, because a header that reports a direction and holds
76
+ * the announcement is holding state and attaching a handler.
77
+ */
78
+ function TableSort({ children, column, direction = 'none', onDirectionChange, announce, disabled = false, className, ...props }) {
79
+ // Uncontrolled use is a state the header keeps for itself, so a caller who wants
80
+ // to read the sort back has a `direction` to pass. The two together are the
81
+ // usual React shape, and the state is a convenience rather than the contract:
82
+ // `onDirectionChange` is the contract.
83
+ const [uncontrolled, setUncontrolled] = useState('none');
84
+ const active = direction === 'none' && onDirectionChange === undefined ? uncontrolled : direction;
85
+ const next = nextDirection(active);
86
+ // The id that ties the announcement to the button. Generated rather than
87
+ // derived from the column, because two tables on a page may sort the same column
88
+ // and an id built from its name would collide, leaving both headers describing
89
+ // each other.
90
+ const descriptionId = useId();
91
+ return (_jsxs("th", { "data-slot": "table-sort", "data-column": column, "data-direction": active, scope: "col", "aria-sort": active === 'none' ? undefined : active, className: cn('p-0', className), ...props, children: [_jsx("span", { id: descriptionId, "data-slot": "table-sort-announcement", className: "sr-only", children: announce(next) }), _jsxs("button", { type: "button", "data-slot": "table-sort-button", "aria-describedby": descriptionId, disabled: disabled, onClick: () => {
92
+ if (onDirectionChange === undefined)
93
+ setUncontrolled(next);
94
+ onDirectionChange?.(next);
95
+ }, className: cn('text-muted-foreground hover:text-foreground flex h-10 w-full items-center gap-1.5 px-2 text-left align-middle text-sm font-medium whitespace-nowrap outline-none', 'transition-colors duration-fast ease-out', 'focus-visible:ring-ring focus-visible:ring-[3px]', 'disabled:pointer-events-none disabled:opacity-50',
96
+ // The sorted column says so in the foreground ink rather than the muted
97
+ // one, which is a second channel on top of the arrow and the attribute:
98
+ // a reader who can see neither the arrow nor the attribute still sees
99
+ // which column is ordered.
100
+ active === 'none' ? '' : 'text-foreground'), children: [_jsx("span", { "data-slot": "table-sort-label", className: "truncate", children: children }), _jsx("span", { "data-slot": "table-sort-mark", "aria-hidden": "true", className: "ml-auto inline-flex shrink-0 items-center", children: MARK[active] })] })] }));
101
+ }
102
+ export { TableSort };
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from '../../lib/utils';
2
+ import { cn } from '../../lib/utils.js';
3
3
  /**
4
4
  * The semantic table components for tabular data.
5
5
  *
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Tabs as TabsPrimitive } from '@base-ui/react/tabs';
4
- import { cn } from '../../lib/utils';
4
+ import { cn } from '../../lib/utils.js';
5
5
  /**
6
6
  * A set of layered panels shown one at a time.
7
7
  *
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from '../../lib/utils';
2
+ import { cn } from '../../lib/utils.js';
3
3
  /**
4
4
  * The multi-line text control.
5
5
  *
@@ -0,0 +1,106 @@
1
+ import type { ComponentProps, ReactNode } from 'react';
2
+ /**
3
+ * How far along an event is, where the run is still going.
4
+ *
5
+ * Four states, and this is a different vocabulary from the four product-capability
6
+ * tiers a StatusLedger01 row carries. Those answer "what can this product do
7
+ * today" and these answer "what happened to this run", so they are two lists
8
+ * about two different subjects rather than two lists about one. A `planned` agent
9
+ * run is a category error, and a `failed` capability is a category error, which is
10
+ * the test that tells them apart.
11
+ *
12
+ * `running` is the only one that moves. It is the state a run is in while it is
13
+ * still arriving events, and it is the reason a Timeline is normally wrapped in a
14
+ * LiveRegion rather than being one.
15
+ */
16
+ export type TimelineState = 'pending' | 'running' | 'done' | 'failed';
17
+ /** One event in a run. */
18
+ export type TimelineEntry = {
19
+ /**
20
+ * A stable key for the entry.
21
+ *
22
+ * Required, because a run's entries arrive and are reordered as it goes, and a
23
+ * list keyed by index would have a reader's focus jump to a different step when
24
+ * an earlier one is inserted.
25
+ */
26
+ id: string;
27
+ /** The words naming what happened. */
28
+ title: string;
29
+ /**
30
+ * How long the step took, in milliseconds.
31
+ *
32
+ * Omit it for a step whose duration is meaningless: a run's opening event has no
33
+ * length, and a bar drawn for it would be a bar comparing nothing.
34
+ */
35
+ duration?: number;
36
+ /** Where the step is. @defaultValue 'done' */
37
+ state?: TimelineState;
38
+ /** Anything the caller wants under the step: a command, a diff, a payload. */
39
+ children?: ReactNode;
40
+ };
41
+ /** The props the Timeline accepts. */
42
+ export interface TimelineProps extends Omit<ComponentProps<'ol'>, 'children'> {
43
+ /**
44
+ * The events, in the order they happened.
45
+ *
46
+ * Order is the caller's, not this Component's: a run log arrives in the order
47
+ * the transport delivered it, and a Timeline that sorted by duration or by state
48
+ * would be a different component that answers a different question.
49
+ */
50
+ entries: readonly TimelineEntry[];
51
+ /**
52
+ * The accessible name of the list, read before its entries.
53
+ *
54
+ * Required rather than defaulted, because two timelines on a page are two lists a
55
+ * reader cannot tell apart, and the name is the caller's word.
56
+ */
57
+ label: string;
58
+ /**
59
+ * The words for a duration, given the number of milliseconds.
60
+ *
61
+ * The default renders seconds with at most one decimal, which is a measurement
62
+ * rather than a sentence. A caller whose steps are measured in minutes or whose
63
+ * readers want a different precision passes its own.
64
+ */
65
+ format?: (ms: number) => string;
66
+ /** Layout only. */
67
+ className?: string;
68
+ /**
69
+ * What a reader is told when the run has no events yet.
70
+ *
71
+ * An empty list is the state a run spends its first moments in, so it is
72
+ * authored rather than left to a caller to notice.
73
+ */
74
+ empty?: ReactNode;
75
+ }
76
+ /**
77
+ * A run's events in order, with the time each one took drawn to scale.
78
+ *
79
+ * The second half of an agent console is always the same question: not what
80
+ * happened, but where the time went. A list of events in order answers the first
81
+ * and is silent on the second, so a reader who wants to know why a run took forty
82
+ * seconds has to read every number and do the arithmetic themselves.
83
+ *
84
+ * **The bars share a left edge and are drawn to scale against the longest step in
85
+ * the run.** That is the whole idea. The scale is relative rather than absolute,
86
+ * which is the right choice here: the shape of the run is the question, and a
87
+ * shared zero baseline would be a second axis nobody reads. The consequence worth
88
+ * stating is that the bars answer "which step was slow" and deliberately do not
89
+ * answer "how long did the run take", because only the caller knows whether its
90
+ * steps were sequential or overlapping, and a bar that implied otherwise would be
91
+ * claiming something about the run it cannot see.
92
+ *
93
+ * **A step with no duration draws no bar.** A run's opening event has no length,
94
+ * and a zero-width bar beside it would read as "this was instant" rather than
95
+ * "this has no duration", which are different claims and only one is true.
96
+ *
97
+ * **It is a list, not a live region.** The order is the accessibility tree: an
98
+ * ordered list, so a reader hears the sequence and the count. A run that is still
99
+ * arriving is normally wrapped in a LiveRegion by the caller, because the arrival
100
+ * is the event worth announcing and the list is the thing worth reading
101
+ * afterwards. Making the list itself live would announce the whole run again on
102
+ * every append.
103
+ */
104
+ declare function Timeline({ entries, label, format, className, empty, ...props }: TimelineProps): import("react").JSX.Element;
105
+ export { Timeline };
106
+ //# sourceMappingURL=timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../../src/components/ui/timeline.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAA;AAErE,0BAA0B;AAC1B,MAAM,MAAM,aAAa,GAAG;IAC1B;;;;;;OAMG;IACH,EAAE,EAAE,MAAM,CAAA;IACV,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAED,sCAAsC;AACtC,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3E;;;;;;OAMG;IACH,OAAO,EAAE,SAAS,aAAa,EAAE,CAAA;IACjC;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAA;IAC/B,mBAAmB;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB;AAgDD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,iBAAS,QAAQ,CAAC,EAChB,OAAO,EACP,KAAK,EACL,MAAsB,EACtB,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,aAAa,+BAkIf;AAED,OAAO,EAAE,QAAQ,EAAE,CAAA"}
@@ -0,0 +1,111 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from '../../lib/utils.js';
3
+ /**
4
+ * The border each state draws its mark in.
5
+ *
6
+ * Every mark is a ring and none of them is filled, for two reasons that point the
7
+ * same way. The first is the measurement the StatusLedger01 mark gives: a filled
8
+ * pastel dot fails contrast against a light card, and a state a reader cannot see
9
+ * is not a state. The second is uniformity: one geometry for all four states means
10
+ * a reader scanning the column is comparing colours rather than shapes, and a
11
+ * column where some marks are solid and some are hollow is a column to decode.
12
+ *
13
+ * A ring also needs no background of its own. An unfilled mark shows the surface
14
+ * behind it, which is the page ground or the card it is on, and a ground-coloured
15
+ * interior inside a coloured ring is exactly what a ring looks like. A filled
16
+ * variant had to paint `bg-background` into the middle to avoid rendering as a
17
+ * transparent hole, and that is a fill declaring a surface while carrying nothing
18
+ * on it.
19
+ *
20
+ * The state is not carried by the colour alone. The mark is `aria-hidden` and the
21
+ * entry's own words carry it, so a reader who cannot separate the four borders
22
+ * still reads "Failed to fetch" on the entry itself.
23
+ */
24
+ const STATE_MARK = {
25
+ pending: 'border-border',
26
+ running: 'border-primary',
27
+ done: 'border-brand-ink',
28
+ failed: 'border-destructive',
29
+ };
30
+ const STATE_BAR = {
31
+ // A pending step's bar is the border colour rather than a muted fill, because a
32
+ // bar that is drawn but empty says "this took no time" rather than "this has not
33
+ // run yet". Those are different claims and only one of them is true.
34
+ pending: 'bg-border',
35
+ running: 'bg-primary',
36
+ done: 'bg-brand-ink',
37
+ failed: 'bg-destructive',
38
+ };
39
+ /** A duration in the compact form the default uses, and the guard against a non-number. */
40
+ function defaultFormat(ms) {
41
+ if (!Number.isFinite(ms) || ms < 0)
42
+ return '';
43
+ if (ms < 1000)
44
+ return `${Math.round(ms)}ms`;
45
+ const seconds = ms / 1000;
46
+ return `${seconds >= 10 ? Math.round(seconds) : Math.round(seconds * 10) / 10}s`;
47
+ }
48
+ /**
49
+ * A run's events in order, with the time each one took drawn to scale.
50
+ *
51
+ * The second half of an agent console is always the same question: not what
52
+ * happened, but where the time went. A list of events in order answers the first
53
+ * and is silent on the second, so a reader who wants to know why a run took forty
54
+ * seconds has to read every number and do the arithmetic themselves.
55
+ *
56
+ * **The bars share a left edge and are drawn to scale against the longest step in
57
+ * the run.** That is the whole idea. The scale is relative rather than absolute,
58
+ * which is the right choice here: the shape of the run is the question, and a
59
+ * shared zero baseline would be a second axis nobody reads. The consequence worth
60
+ * stating is that the bars answer "which step was slow" and deliberately do not
61
+ * answer "how long did the run take", because only the caller knows whether its
62
+ * steps were sequential or overlapping, and a bar that implied otherwise would be
63
+ * claiming something about the run it cannot see.
64
+ *
65
+ * **A step with no duration draws no bar.** A run's opening event has no length,
66
+ * and a zero-width bar beside it would read as "this was instant" rather than
67
+ * "this has no duration", which are different claims and only one is true.
68
+ *
69
+ * **It is a list, not a live region.** The order is the accessibility tree: an
70
+ * ordered list, so a reader hears the sequence and the count. A run that is still
71
+ * arriving is normally wrapped in a LiveRegion by the caller, because the arrival
72
+ * is the event worth announcing and the list is the thing worth reading
73
+ * afterwards. Making the list itself live would announce the whole run again on
74
+ * every append.
75
+ */
76
+ function Timeline({ entries, label, format = defaultFormat, className, empty, ...props }) {
77
+ if (entries.length === 0) {
78
+ // A run spends its first moments with nothing in it, and an empty list with no
79
+ // words in it is a gap rather than a state.
80
+ return (_jsx("div", { "data-slot": "timeline-empty", className: cn('text-muted-foreground text-sm', className), children: empty ?? null }));
81
+ }
82
+ // The longest step with a duration sets the scale. A run whose steps all report
83
+ // no duration has no scale, and every bar is then omitted rather than drawn at
84
+ // an arbitrary width.
85
+ const longest = Math.max(0, ...entries
86
+ .map((entry) => entry.duration)
87
+ .filter((duration) => typeof duration === 'number' && duration > 0));
88
+ return (_jsx("ol", { "data-slot": "timeline", "aria-label": label, className: cn('flex flex-col', className), ...props, children: entries.map((entry, index) => {
89
+ const state = entry.state ?? 'done';
90
+ // Bound to a local before the test, because a boolean derived from a
91
+ // narrowing check does not carry the narrowing to its own use: reading
92
+ // `entry.duration` after `hasDuration` is still `number | undefined` to
93
+ // the checker, which is the sort of thing a boolean is a poor carrier for.
94
+ const duration = entry.duration;
95
+ const hasDuration = typeof duration === 'number' && duration > 0 && longest > 0;
96
+ const width = hasDuration ? (duration / longest) * 100 : 0;
97
+ const isLast = index === entries.length - 1;
98
+ return (_jsxs("li", { "data-slot": "timeline-entry", "data-state": state, className: "grid grid-cols-[auto_1fr] gap-x-3", children: [_jsxs("div", { "data-slot": "timeline-rail", "aria-hidden": "true", className: "flex flex-col items-center", children: [_jsx("span", { "data-slot": "timeline-mark", className: cn('mt-1.5 size-2 shrink-0 rounded-full border-2', STATE_MARK[state],
99
+ // A running step is the one that is still moving, and a ring that
100
+ // pulses is the only motion in the Component. It is state
101
+ // feedback, not decoration: it stops when the step stops.
102
+ state === 'running' && 'animate-pulse') }), isLast ? null : _jsx("span", { "data-slot": "timeline-spine", className: "bg-border w-px flex-1" })] }), _jsxs("div", { className: cn(
103
+ // The last entry has nothing under it, so its bottom padding is
104
+ // dropped rather than leaving a gap the reader reads as a
105
+ // missing step.
106
+ 'flex min-w-0 flex-col gap-1.5', isLast ? 'pb-0' : 'pb-4'), children: [_jsxs("div", { className: "flex items-baseline justify-between gap-3", children: [_jsx("span", { "data-slot": "timeline-title", className: cn('min-w-0 truncate text-sm', state === 'pending' && 'text-muted-foreground', state === 'failed' && 'text-destructive', state === 'done' || state === 'running'
107
+ ? 'text-foreground'
108
+ : 'text-muted-foreground'), children: entry.title }), typeof entry.duration === 'number' && entry.duration > 0 ? (_jsx("span", { "data-slot": "timeline-duration", className: "text-muted-foreground shrink-0 text-xs tabular-nums", children: format(entry.duration) })) : null] }), hasDuration ? (_jsx("span", { "data-slot": "timeline-bar", className: cn('block h-1 w-full overflow-hidden rounded-full', STATE_BAR[state]), style: { width: `${width}%` } })) : null, entry.children === undefined ? null : (_jsx("div", { "data-slot": "timeline-detail", className: "text-muted-foreground min-w-0 text-sm", children: entry.children }))] })] }, entry.id));
109
+ }) }));
110
+ }
111
+ export { Timeline };