@digithings-ai/ui 0.0.0-stage → 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 (424) hide show
  1. package/README.md +48 -2
  2. package/package.json +143 -4
  3. package/src/components/NavShell.tsx +776 -0
  4. package/src/components/SocialRow.tsx +87 -0
  5. package/src/components/Spinner.tsx +27 -0
  6. package/src/components/StackLogo.tsx +40 -0
  7. package/src/components/Terminal.tsx +110 -0
  8. package/src/components/TerminalManifest.tsx +212 -0
  9. package/src/components/ThemeProvider.tsx +271 -0
  10. package/src/components/account/AuthCard.tsx +309 -0
  11. package/src/components/assistant-ui/elements/mermaid-diagram.aui.tsx +63 -0
  12. package/src/components/assistant-ui/elements/mermaid-diagram.tsx +340 -0
  13. package/src/components/brand/BrandWordmark.tsx +32 -0
  14. package/src/components/changelog/ReleaseRail.tsx +81 -0
  15. package/src/components/changelog/index.ts +1 -0
  16. package/src/components/chat/ChatCodeBlock.tsx +67 -0
  17. package/src/components/chat/ChatMarkdown.tsx +66 -0
  18. package/src/components/chat/ChatMarkdownSource.tsx +203 -0
  19. package/src/components/chat/ChatMermaidBlock.tsx +202 -0
  20. package/src/components/chat/ChatMessage.tsx +77 -0
  21. package/src/components/chat/ChatPlayback.tsx +422 -0
  22. package/src/components/chat/ChatStreamCursor.tsx +25 -0
  23. package/src/components/chat/ChatThinking.tsx +113 -0
  24. package/src/components/chat/ChatToolCall.tsx +190 -0
  25. package/src/components/chat/ChatTranscript.tsx +60 -0
  26. package/src/components/chat/ChatWidgetFrame.tsx +107 -0
  27. package/src/components/chat/DigichatBootLoader.tsx +683 -0
  28. package/src/components/chat/DigichatLauncher.tsx +361 -0
  29. package/src/components/chat/DigichatThread.tsx +118 -0
  30. package/src/components/chat/DigichatThreadList.tsx +54 -0
  31. package/src/components/chat/DigichatWordmark.tsx +24 -0
  32. package/src/components/chat/DotMatrix.tsx +650 -0
  33. package/src/components/chat/boot-lab.tsx +766 -0
  34. package/src/components/chat/boot-signal.ts +144 -0
  35. package/src/components/chat/chat-surfaces.ts +52 -0
  36. package/src/components/chat/connection-status.ts +40 -0
  37. package/src/components/chat/digichat-wordmark.ts +355 -0
  38. package/src/components/chat/disclosure-chevron.tsx +49 -0
  39. package/src/components/chat/followable-href.ts +9 -0
  40. package/src/components/chat/gallery-thread/action-icons.tsx +25 -0
  41. package/src/components/chat/gallery-thread/attachment.aui.tsx +262 -0
  42. package/src/components/chat/gallery-thread/block-caret.tsx +439 -0
  43. package/src/components/chat/gallery-thread/cn.ts +1 -0
  44. package/src/components/chat/gallery-thread/composer-keyboard.ts +106 -0
  45. package/src/components/chat/gallery-thread/composer-trigger-popover.aui.tsx +235 -0
  46. package/src/components/chat/gallery-thread/css.d.ts +6 -0
  47. package/src/components/chat/gallery-thread/file.tsx +238 -0
  48. package/src/components/chat/gallery-thread/follow-up-suggestions.aui.tsx +92 -0
  49. package/src/components/chat/gallery-thread/format-json-dump.ts +112 -0
  50. package/src/components/chat/gallery-thread/hooks/use-attachment-src.ts +46 -0
  51. package/src/components/chat/gallery-thread/hooks/use-copy-to-clipboard.ts +9 -0
  52. package/src/components/chat/gallery-thread/image.tsx +519 -0
  53. package/src/components/chat/gallery-thread/markdown-text.tsx +278 -0
  54. package/src/components/chat/gallery-thread/message-error.aui.tsx +105 -0
  55. package/src/components/chat/gallery-thread/message-timing.aui.tsx +143 -0
  56. package/src/components/chat/gallery-thread/private-work-step.tsx +188 -0
  57. package/src/components/chat/gallery-thread/reasoning.tsx +332 -0
  58. package/src/components/chat/gallery-thread/scroll-mode.ts +174 -0
  59. package/src/components/chat/gallery-thread/source.tsx +76 -0
  60. package/src/components/chat/gallery-thread/thread.aui.tsx +1619 -0
  61. package/src/components/chat/gallery-thread/tooltip-icon-button.tsx +53 -0
  62. package/src/components/chat/gallery-thread/typed-welcome-copy.tsx +18 -0
  63. package/src/components/chat/gallery-thread/ui/tooltip.tsx +22 -0
  64. package/src/components/chat/gallery-thread/work-chain-summary.tsx +48 -0
  65. package/src/components/chat/gallery-thread/work-chain.tsx +110 -0
  66. package/src/components/chat/gallery-thread/work-status.ts +66 -0
  67. package/src/components/chat/overlay-scrollbar.tsx +86 -0
  68. package/src/components/chat/sanitize-mermaid-svg.ts +38 -0
  69. package/src/components/chat/skins/SOURCE.md +36 -0
  70. package/src/components/chat/skins/base/attachment.aui.tsx +287 -0
  71. package/src/components/chat/skins/base/aui-default/README.md +45 -0
  72. package/src/components/chat/skins/base/aui-default/collapsible-root.tsx +19 -0
  73. package/src/components/chat/skins/base/aui-default/collapsible.tsx +21 -0
  74. package/src/components/chat/skins/base/aui-default/reasoning.aui.tsx +120 -0
  75. package/src/components/chat/skins/base/aui-default/reasoning.tsx +331 -0
  76. package/src/components/chat/skins/base/aui-default/surfaces.ts +7 -0
  77. package/src/components/chat/skins/base/aui-default/tool-fallback.aui.tsx +772 -0
  78. package/src/components/chat/skins/base/aui-default/tool-group.aui.tsx +241 -0
  79. package/src/components/chat/skins/base/docs-model-options.ts +6 -0
  80. package/src/components/chat/skins/base/elements/assistant-trace.aui.tsx +169 -0
  81. package/src/components/chat/skins/base/elements/composer-trigger-popover.aui.tsx +271 -0
  82. package/src/components/chat/skins/base/elements/directive-text.aui.tsx +29 -0
  83. package/src/components/chat/skins/base/elements/directive-text.tsx +76 -0
  84. package/src/components/chat/skins/base/elements/message-timing.aui.tsx +104 -0
  85. package/src/components/chat/skins/base/elements/model-selector.aui.tsx +140 -0
  86. package/src/components/chat/skins/base/elements/model-selector.tsx +720 -0
  87. package/src/components/chat/skins/base/elements/quote.aui.tsx +290 -0
  88. package/src/components/chat/skins/base/elements/work-chain.tsx +100 -0
  89. package/src/components/chat/skins/base/markdown-text.tsx +294 -0
  90. package/src/components/chat/skins/base/model.ts +1 -0
  91. package/src/components/chat/skins/base/thread.tsx +1050 -0
  92. package/src/components/chat/skins/base/tooltip-icon-button.tsx +56 -0
  93. package/src/components/chat/skins/base/ui/avatar.tsx +115 -0
  94. package/src/components/chat/skins/base/ui/badge.tsx +51 -0
  95. package/src/components/chat/skins/base/ui/button.tsx +67 -0
  96. package/src/components/chat/skins/base/ui/command.tsx +185 -0
  97. package/src/components/chat/skins/base/ui/dialog.tsx +168 -0
  98. package/src/components/chat/skins/base/ui/dot-matrix.tsx +267 -0
  99. package/src/components/chat/skins/base/ui/popover.tsx +97 -0
  100. package/src/components/chat/skins/base/ui/skeleton.tsx +20 -0
  101. package/src/components/chat/skins/base/ui/textarea.tsx +24 -0
  102. package/src/components/chat/skins/base/ui/tooltip.tsx +73 -0
  103. package/src/components/chat/skins/base-assistant-ui/config-provider.tsx +90 -0
  104. package/src/components/chat/skins/base-assistant-ui/defaults.ts +419 -0
  105. package/src/components/chat/skins/base-assistant-ui/index.tsx +44 -0
  106. package/src/components/chat/skins/chatgpt.tsx +448 -0
  107. package/src/components/chat/skins/claude.tsx +380 -0
  108. package/src/components/chat/skins/digichat-composer-toolbar.tsx +151 -0
  109. package/src/components/chat/skins/digichat-context-meter-shares.ts +158 -0
  110. package/src/components/chat/skins/digichat-context-meter.tsx +243 -0
  111. package/src/components/chat/skins/digichat-meter-tone.ts +26 -0
  112. package/src/components/chat/skins/digichat-usage-meter.tsx +103 -0
  113. package/src/components/chat/skins/digichat.tsx +144 -0
  114. package/src/components/chat/skins/expo-react-native.tsx +29 -0
  115. package/src/components/chat/skins/gemini.tsx +322 -0
  116. package/src/components/chat/skins/grok-icon.tsx +34 -0
  117. package/src/components/chat/skins/grok.tsx +369 -0
  118. package/src/components/chat/skins/index.ts +46 -0
  119. package/src/components/chat/skins/perplexity.tsx +462 -0
  120. package/src/components/chat/skins/product-page-assistant/assistant-modal.tsx +73 -0
  121. package/src/components/chat/skins/product-page-assistant/index.tsx +19 -0
  122. package/src/components/chat/skins/product-page-assistant/product-dashboard.tsx +164 -0
  123. package/src/components/chat/skins/react-ink.tsx +97 -0
  124. package/src/components/chat/skins/thread-skin-host-contract.ts +102 -0
  125. package/src/components/chat/skins/thread-skin-view.tsx +156 -0
  126. package/src/components/chat/skins/thread-skins.ts +143 -0
  127. package/src/components/chat/skins/tooltip-icon-button.tsx +35 -0
  128. package/src/components/chat/skins/ui/dropdown-menu.tsx +272 -0
  129. package/src/components/chat/skins/webpage-assistant/assistant-config.ts +238 -0
  130. package/src/components/chat/skins/webpage-assistant/assistant-modal.tsx +78 -0
  131. package/src/components/chat/skins/webpage-assistant/config-provider.tsx +132 -0
  132. package/src/components/chat/skins/webpage-assistant/docs-app.tsx +106 -0
  133. package/src/components/chat/skins/webpage-assistant/docs-article.tsx +109 -0
  134. package/src/components/chat/skins/webpage-assistant/docs-assistant.tsx +73 -0
  135. package/src/components/chat/skins/webpage-assistant/docs-nav.tsx +81 -0
  136. package/src/components/chat/skins/webpage-assistant/host-ui.ts +79 -0
  137. package/src/components/chat/skins/webpage-assistant/index.tsx +12 -0
  138. package/src/components/chat/skins/webpage-assistant/markdown-page.tsx +11 -0
  139. package/src/components/chat/skins/webpage-assistant/pages.ts +9 -0
  140. package/src/components/chat/skins/webpage-assistant/types.ts +178 -0
  141. package/src/components/chat/skins/webpage-assistant/use-mobile.ts +19 -0
  142. package/src/components/chat/stock/SOURCE.md +90 -0
  143. package/src/components/chat/stock/artifacts-panel.tsx +305 -0
  144. package/src/components/chat/stock/attachment.aui.tsx +280 -0
  145. package/src/components/chat/stock/chat-notice.tsx +143 -0
  146. package/src/components/chat/stock/chat-private-mode.tsx +64 -0
  147. package/src/components/chat/stock/deploy-ui-context.tsx +89 -0
  148. package/src/components/chat/stock/embed-chat-prefs.tsx +471 -0
  149. package/src/components/chat/stock/file.tsx +265 -0
  150. package/src/components/chat/stock/follow-up-suggestions.aui.tsx +83 -0
  151. package/src/components/chat/stock/image.tsx +531 -0
  152. package/src/components/chat/stock/index.ts +129 -0
  153. package/src/components/chat/stock/json-view.tsx +140 -0
  154. package/src/components/chat/stock/markdown-text.tsx +319 -0
  155. package/src/components/chat/stock/message-error.aui.tsx +96 -0
  156. package/src/components/chat/stock/message-export-menu.tsx +156 -0
  157. package/src/components/chat/stock/palette-row-preview.tsx +26 -0
  158. package/src/components/chat/stock/reasoning.aui.tsx +120 -0
  159. package/src/components/chat/stock/reasoning.tsx +335 -0
  160. package/src/components/chat/stock/skin-chrome.tsx +81 -0
  161. package/src/components/chat/stock/skin-credit.tsx +63 -0
  162. package/src/components/chat/stock/skin-palette-bridge.tsx +235 -0
  163. package/src/components/chat/stock/skin-palette-context.ts +16 -0
  164. package/src/components/chat/stock/skin-runtime.tsx +52 -0
  165. package/src/components/chat/stock/skin-slash-palette.tsx +393 -0
  166. package/src/components/chat/stock/skin-types.ts +217 -0
  167. package/src/components/chat/stock/stock-chat-prefs-host.tsx +501 -0
  168. package/src/components/chat/stock/stock-send-gate.tsx +94 -0
  169. package/src/components/chat/stock/stock-shape.tsx +39 -0
  170. package/src/components/chat/stock/thread.aui.tsx +645 -0
  171. package/src/components/chat/stock/tool-display.ts +6 -0
  172. package/src/components/chat/stock/tool-fallback.aui.tsx +866 -0
  173. package/src/components/chat/stock/tool-group.aui.tsx +244 -0
  174. package/src/components/chat/stock/tool-surface.ts +120 -0
  175. package/src/components/chat/stock/tooltip-icon-button.tsx +49 -0
  176. package/src/components/chat/stock/ui/avatar.tsx +108 -0
  177. package/src/components/chat/stock/ui/button.tsx +81 -0
  178. package/src/components/chat/stock/ui/collapsible.tsx +21 -0
  179. package/src/components/chat/stock/ui/dialog.tsx +163 -0
  180. package/src/components/chat/stock/ui/skeleton.tsx +13 -0
  181. package/src/components/chat/stock/ui/textarea.tsx +20 -0
  182. package/src/components/chat/stock/ui/tooltip.tsx +68 -0
  183. package/src/components/chat/stock/use-attachment-src.ts +42 -0
  184. package/src/components/chat/stock/use-copy-to-clipboard.ts +29 -0
  185. package/src/components/chat/strip-model-boilerplate.ts +42 -0
  186. package/src/components/chat/suggestion-cycle.tsx +230 -0
  187. package/src/components/chat/suggestion-scroll-row.tsx +81 -0
  188. package/src/components/chat/trace-grouping/chain-group-by.ts +44 -0
  189. package/src/components/chat/trace-grouping/chain-state.ts +141 -0
  190. package/src/components/chat/trace-grouping/index.ts +32 -0
  191. package/src/components/chat/trace-grouping/tool-row.ts +74 -0
  192. package/src/components/chat/trace-grouping/use-deploy-work-disclosure.ts +35 -0
  193. package/src/components/chat/trace-grouping/use-work-chain.ts +103 -0
  194. package/src/components/chat/trace-i18n/core.ts +48 -0
  195. package/src/components/chat/trace-i18n/index.tsx +23 -0
  196. package/src/components/chat/trace-i18n/locales/en.ts +24 -0
  197. package/src/components/chat/trace-i18n/locales/fr.ts +28 -0
  198. package/src/components/chat/trace-i18n/types.ts +35 -0
  199. package/src/components/chat/trace-spacing.ts +249 -0
  200. package/src/components/chat/transcript/activity-view.ts +375 -0
  201. package/src/components/chat/transcript/chat-export.ts +133 -0
  202. package/src/components/chat/transcript/index.ts +10 -0
  203. package/src/components/chat/transcript/save-file.ts +209 -0
  204. package/src/components/chat/transcript/transcript-markdown.ts +428 -0
  205. package/src/components/chat/transcript/types.ts +81 -0
  206. package/src/components/chat/turn-outcome.ts +52 -0
  207. package/src/components/chat/use-warmup-phase.ts +18 -0
  208. package/src/components/chat/work-status-lead.tsx +49 -0
  209. package/src/components/chrome.tsx +264 -0
  210. package/src/components/command-palette/CommandPalette.tsx +404 -0
  211. package/src/components/command-palette/index.ts +6 -0
  212. package/src/components/contact/ContactMailto.tsx +112 -0
  213. package/src/components/contact/index.ts +5 -0
  214. package/src/components/conviction/ConvictionMeter.tsx +40 -0
  215. package/src/components/conviction/SignedConvictionBadge.tsx +26 -0
  216. package/src/components/conviction/index.ts +7 -0
  217. package/src/components/data-layout/BentoGrid.tsx +52 -0
  218. package/src/components/data-layout/CardRail.tsx +150 -0
  219. package/src/components/data-layout/DotMatrixStat.tsx +65 -0
  220. package/src/components/data-layout/FeatureCell.tsx +80 -0
  221. package/src/components/data-layout/MediaFrame.tsx +125 -0
  222. package/src/components/data-layout/ModuleGrid.tsx +735 -0
  223. package/src/components/data-layout/Odometer.tsx +149 -0
  224. package/src/components/data-layout/ProductFrame.tsx +79 -0
  225. package/src/components/data-layout/TestimonialWall.tsx +139 -0
  226. package/src/components/data-layout/index.ts +19 -0
  227. package/src/components/data-layout/treemap.ts +368 -0
  228. package/src/components/deck/DeckStack.tsx +243 -0
  229. package/src/components/desk/DataTable.tsx +373 -0
  230. package/src/components/desk/KpiGrid.tsx +80 -0
  231. package/src/components/desk/Sparkline.tsx +63 -0
  232. package/src/components/desk/StateBlock.tsx +132 -0
  233. package/src/components/desk/index.ts +18 -0
  234. package/src/components/diagrams/ArchitectureDiagram.tsx +350 -0
  235. package/src/components/diagrams/ArchitectureTour.tsx +912 -0
  236. package/src/components/diagrams/TerminalSchematic.tsx +192 -0
  237. package/src/components/diagrams/architecture-svg.tsx +384 -0
  238. package/src/components/diagrams/index.ts +30 -0
  239. package/src/components/diagrams/mermaid-theme.ts +115 -0
  240. package/src/components/diagrams/tour-camera.ts +171 -0
  241. package/src/components/docs/CodeTabs.tsx +90 -0
  242. package/src/components/docs/CopyCommand.tsx +151 -0
  243. package/src/components/docs/DocsLayout.tsx +197 -0
  244. package/src/components/docs/DocsSearch.tsx +97 -0
  245. package/src/components/docs/Endpoint.tsx +135 -0
  246. package/src/components/document/DocumentFrame.tsx +43 -0
  247. package/src/components/document/GlyphList.tsx +51 -0
  248. package/src/components/document/PageTitle.tsx +44 -0
  249. package/src/components/document/Prose.tsx +38 -0
  250. package/src/components/document/Section.tsx +55 -0
  251. package/src/components/document/index.ts +13 -0
  252. package/src/components/effects-chrome/AnnouncementBar.tsx +88 -0
  253. package/src/components/effects-chrome/HorizontalScrollTrack.tsx +461 -0
  254. package/src/components/effects-chrome/Pipeline.tsx +226 -0
  255. package/src/components/effects-chrome/RotatingPrompts.tsx +116 -0
  256. package/src/components/effects-chrome/StackingPanels.tsx +101 -0
  257. package/src/components/effects-chrome/TabStrip.tsx +232 -0
  258. package/src/components/effects-chrome/ToastStack.tsx +201 -0
  259. package/src/components/effects-chrome/horizontal-track-core.ts +70 -0
  260. package/src/components/effects-chrome/index.ts +35 -0
  261. package/src/components/emblems.tsx +188 -0
  262. package/src/components/faviconTheme.ts +23 -0
  263. package/src/components/figure/Figure.tsx +38 -0
  264. package/src/components/figure/index.ts +2 -0
  265. package/src/components/finance-charts/DrawdownPlot.tsx +76 -0
  266. package/src/components/finance-charts/EquityCurve.tsx +71 -0
  267. package/src/components/finance-charts/PriceChart.tsx +119 -0
  268. package/src/components/finance-charts/chart-host.tsx +236 -0
  269. package/src/components/finance-charts/chart-scale.ts +93 -0
  270. package/src/components/finance-charts/demo-data.ts +88 -0
  271. package/src/components/finance-charts/index.ts +46 -0
  272. package/src/components/finance-charts/lw-chart.tsx +292 -0
  273. package/src/components/finance-composites/MarketBar.tsx +243 -0
  274. package/src/components/finance-composites/OrderBook.tsx +133 -0
  275. package/src/components/finance-composites/PerformanceDashboard.tsx +185 -0
  276. package/src/components/finance-composites/SortableTable.tsx +144 -0
  277. package/src/components/finance-composites/StockTicker.tsx +65 -0
  278. package/src/components/finance-composites/SyncedTearsheet.tsx +226 -0
  279. package/src/components/finance-composites/index.ts +28 -0
  280. package/src/components/finance-tearsheet/KpiStrip.tsx +49 -0
  281. package/src/components/finance-tearsheet/LiveBadge.tsx +29 -0
  282. package/src/components/finance-tearsheet/ReturnsMatrix.tsx +321 -0
  283. package/src/components/finance-tearsheet/StrategyTearsheet.tsx +99 -0
  284. package/src/components/finance-tearsheet/TearsheetCard.tsx +54 -0
  285. package/src/components/finance-tearsheet/TradeLogTable.tsx +73 -0
  286. package/src/components/finance-tearsheet/charts.tsx +2624 -0
  287. package/src/components/finance-tearsheet/demo-data.ts +177 -0
  288. package/src/components/finance-tearsheet/format.ts +41 -0
  289. package/src/components/finance-tearsheet/index.ts +120 -0
  290. package/src/components/finance-tearsheet/print.ts +52 -0
  291. package/src/components/finance-tearsheet/risk-bands.ts +48 -0
  292. package/src/components/finance-tearsheet/stats.ts +35 -0
  293. package/src/components/finance-tearsheet/strategy-sheet-model.ts +238 -0
  294. package/src/components/finance-tearsheet/types.ts +58 -0
  295. package/src/components/footer/FooterCells.tsx +109 -0
  296. package/src/components/footer/index.ts +6 -0
  297. package/src/components/graph.tsx +158 -0
  298. package/src/components/icons.tsx +98 -0
  299. package/src/components/landing/PixelWordmark.tsx +36 -0
  300. package/src/components/logos.ts +39 -0
  301. package/src/components/marquee/Marquee.tsx +204 -0
  302. package/src/components/metrics/PerfMetrics.tsx +68 -0
  303. package/src/components/metrics/StatCounter.tsx +104 -0
  304. package/src/components/page-geometry.tsx +177 -0
  305. package/src/components/pricing/Pricing.tsx +174 -0
  306. package/src/components/pricing/PricingMatrix.tsx +219 -0
  307. package/src/components/prose/index.ts +2 -0
  308. package/src/components/prose/prose.tsx +86 -0
  309. package/src/components/repo-activity/RepoActivity.tsx +599 -0
  310. package/src/components/repo-activity/RepoHeatmap.tsx +225 -0
  311. package/src/components/repo-activity/demo.ts +201 -0
  312. package/src/components/repo-activity/fetch.ts +211 -0
  313. package/src/components/repo-activity/heatmap.ts +123 -0
  314. package/src/components/repo-activity/index.ts +28 -0
  315. package/src/components/repo-activity/types.ts +99 -0
  316. package/src/components/stages/NumberedStages.tsx +116 -0
  317. package/src/components/symbols/marks.tsx +116 -0
  318. package/src/components/symbols/terminal-lockup.tsx +136 -0
  319. package/src/components/symbols/terminal-marks.tsx +226 -0
  320. package/src/components/terminal/ChatResponseLoader.tsx +67 -0
  321. package/src/components/terminal/ContainerBootLoader.tsx +82 -0
  322. package/src/components/terminal/TerminalStepCaret.tsx +202 -0
  323. package/src/components/themeInit.ts +24 -0
  324. package/src/components/typography/MotionReveal.tsx +108 -0
  325. package/src/components/typography/TypewriterReveal.tsx +101 -0
  326. package/src/components/typography/WordReveal.tsx +143 -0
  327. package/src/data/modules.ts +378 -0
  328. package/src/data/subsystems.ts +101 -0
  329. package/src/index.ts +587 -0
  330. package/src/lib/base-path.ts +60 -0
  331. package/src/lib/cx.ts +25 -0
  332. package/src/lib/gloomberb.ts +69 -0
  333. package/src/lib/record.ts +19 -0
  334. package/src/lib/stub-payload.ts +183 -0
  335. package/src/lib/useBodyScrollLock.ts +44 -0
  336. package/src/lib/utils.ts +6 -0
  337. package/src/motion/primitives.tsx +105 -0
  338. package/src/motion/scroll-glide.ts +62 -0
  339. package/src/motion/scrolly-core.ts +38 -0
  340. package/src/motion/scrolly.tsx +95 -0
  341. package/src/navigation/HashScrollManager.tsx +167 -0
  342. package/src/navigation/hashScroll.ts +78 -0
  343. package/src/replay/browser.ts +280 -0
  344. package/src/replay/chat-export-run.ts +121 -0
  345. package/src/replay/export-format.ts +25 -0
  346. package/src/replay/export-mount.ts +32 -0
  347. package/src/replay/filename.ts +54 -0
  348. package/src/replay/index.ts +21 -0
  349. package/src/replay/redact-dom.ts +178 -0
  350. package/src/replay/redact.ts +113 -0
  351. package/src/replay/snapshot.ts +659 -0
  352. package/src/styles/account-auth.css +339 -0
  353. package/src/styles/brand-wordmark.css +11 -0
  354. package/src/styles/card-rail.css +120 -0
  355. package/src/styles/chat-aui.css +1143 -0
  356. package/src/styles/chat-core.css +469 -0
  357. package/src/styles/chat-digichat.css +2531 -0
  358. package/src/styles/chat-math.css +70 -0
  359. package/src/styles/chat-skin-palette.css +138 -0
  360. package/src/styles/chat-stock-menu.css +37 -0
  361. package/src/styles/chat-widgets.css +190 -0
  362. package/src/styles/command-palette.css +372 -0
  363. package/src/styles/controls-core.css +2261 -0
  364. package/src/styles/controls-overlay.css +603 -0
  365. package/src/styles/data-layout.css +288 -0
  366. package/src/styles/deck.css +141 -0
  367. package/src/styles/diagrams.css +1860 -0
  368. package/src/styles/digichat-app-theme.css +250 -0
  369. package/src/styles/digichat-boot-loader.css +487 -0
  370. package/src/styles/digichat-launcher.css +377 -0
  371. package/src/styles/docs.css +165 -0
  372. package/src/styles/effects-chrome.css +888 -0
  373. package/src/styles/finance-composites.css +214 -0
  374. package/src/styles/finance-tearsheet-overlay.css +90 -0
  375. package/src/styles/finance-tearsheet.css +945 -0
  376. package/src/styles/marquee.css +168 -0
  377. package/src/styles/metrics.css +82 -0
  378. package/src/styles/nav-shell.css +551 -0
  379. package/src/styles/pricing.css +79 -0
  380. package/src/styles/repo-activity.css +575 -0
  381. package/src/styles/stages.css +69 -0
  382. package/src/styles/terminal-loaders.css +122 -0
  383. package/src/styles/terminal-manifest.css +228 -0
  384. package/src/styles/web-theme.css +1007 -0
  385. package/src/styles/word-reveal.css +204 -0
  386. package/src/theme/apply-theme.ts +214 -0
  387. package/src/theme/index.ts +27 -0
  388. package/src/theme/palette-registry.ts +855 -0
  389. package/src/theme/tui-prefs.ts +64 -0
  390. package/src/ui/alert.tsx +78 -0
  391. package/src/ui/avatar.tsx +104 -0
  392. package/src/ui/badge.tsx +75 -0
  393. package/src/ui/breadcrumb.tsx +61 -0
  394. package/src/ui/button.tsx +67 -0
  395. package/src/ui/card.tsx +139 -0
  396. package/src/ui/checkbox.tsx +27 -0
  397. package/src/ui/collapsible.tsx +32 -0
  398. package/src/ui/date-pager.tsx +325 -0
  399. package/src/ui/dialog.tsx +157 -0
  400. package/src/ui/dropdown-menu.tsx +267 -0
  401. package/src/ui/empty-state.tsx +165 -0
  402. package/src/ui/field.tsx +104 -0
  403. package/src/ui/form.tsx +36 -0
  404. package/src/ui/icon-button.tsx +36 -0
  405. package/src/ui/index.ts +34 -0
  406. package/src/ui/input.tsx +33 -0
  407. package/src/ui/kbd.tsx +30 -0
  408. package/src/ui/label.tsx +32 -0
  409. package/src/ui/pager.tsx +119 -0
  410. package/src/ui/pagination.tsx +126 -0
  411. package/src/ui/radio-group.tsx +51 -0
  412. package/src/ui/search-bar.tsx +85 -0
  413. package/src/ui/segmented-control.tsx +76 -0
  414. package/src/ui/select.tsx +257 -0
  415. package/src/ui/separator.tsx +24 -0
  416. package/src/ui/sheet.tsx +141 -0
  417. package/src/ui/skeleton.tsx +76 -0
  418. package/src/ui/slider.tsx +76 -0
  419. package/src/ui/switch.tsx +31 -0
  420. package/src/ui/table.tsx +179 -0
  421. package/src/ui/tabs.tsx +89 -0
  422. package/src/ui/tags-input.tsx +168 -0
  423. package/src/ui/textarea.tsx +17 -0
  424. package/src/ui/tooltip.tsx +71 -0
package/src/ui/kbd.tsx ADDED
@@ -0,0 +1,30 @@
1
+ import type { HTMLAttributes } from "react"
2
+ import { cn } from "../lib/utils"
3
+
4
+ /**
5
+ * Kbd — the keycap chip. Promoted from the old reference gallery's global
6
+ * `.kbd`, which was reference-only: an app that wanted a keycap had no
7
+ * canonical part to reach for and would have tripped the family census.
8
+ *
9
+ * The keycap look: hairline box, a heavier bottom border so it reads as a
10
+ * physical key, the ink-wash fill, mono at the micro size. Token utilities
11
+ * only — no app-local class family. Renders a real `<kbd>`, so the phase-id /
12
+ * shortcut chips it dresses keep their semantic.
13
+ */
14
+ export type KbdProps = HTMLAttributes<HTMLElement>
15
+
16
+ function Kbd({ className, children, ...props }: KbdProps) {
17
+ return (
18
+ <kbd
19
+ className={cn(
20
+ "inline-flex h-[1.15rem] min-w-[1.15rem] items-center justify-center rounded-none border border-hair border-b-2 bg-ink/5 px-[0.3rem] align-middle font-mono text-[0.62rem] leading-none text-ink-soft",
21
+ className,
22
+ )}
23
+ {...props}
24
+ >
25
+ {children}
26
+ </kbd>
27
+ )
28
+ }
29
+
30
+ export { Kbd }
@@ -0,0 +1,32 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../lib/utils"
5
+
6
+ export type LabelDress = "default" | "chat";
7
+
8
+ function Label({
9
+ className,
10
+ dress = "default",
11
+ ...props
12
+ }: React.ComponentProps<"label"> & { dress?: LabelDress }) {
13
+ // dress="chat" emits the digichat chat-dress class (styles/controls-core.css)
14
+ // instead of the kit utilities, keeping digichat's rendered look exact.
15
+ const classes =
16
+ dress === "chat"
17
+ ? cn("ctl-label-chat", className)
18
+ : cn(
19
+ "flex items-center gap-2 text-xs leading-none select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
20
+ className
21
+ );
22
+
23
+ return (
24
+ <label
25
+ data-slot="label"
26
+ className={classes}
27
+ {...props}
28
+ />
29
+ )
30
+ }
31
+
32
+ export { Label }
@@ -0,0 +1,119 @@
1
+ "use client"
2
+
3
+ /**
4
+ * Pager — the prev/next wayfinding control with a fixed middle slot
5
+ * (`PagerPage` cells, a page count, a date), promoted from the controls layer
6
+ * into the kit (#4306, batch K3). Disabled edges around a middle column that
7
+ * stays put while its label width changes. Two dresses: "reference" (default,
8
+ * per-cell hairline) and "capsule" (dashboard's shipped one-capsule look —
9
+ * borderless chevrons pinned at the ends). The `.nb-pager*` / `.nb-page*`
10
+ * dress is translated into token utilities here, so the controls CSS can
11
+ * retire. This is distinct from {@link Pagination}, the numbered page trail.
12
+ */
13
+
14
+ import type { ComponentPropsWithoutRef, HTMLAttributes, ReactNode } from "react"
15
+
16
+ import { cn } from "../lib/utils"
17
+
18
+ const BASE_ITEM =
19
+ "min-w-[2rem] h-[2rem] px-[0.6rem] border border-hair bg-surface font-mono text-[0.72rem] text-ink-soft transition-colors"
20
+
21
+ const BASE_EDGE = cn(
22
+ BASE_ITEM,
23
+ "cursor-pointer enabled:hover:text-ink enabled:hover:border-[color-mix(in_srgb,var(--accent)_45%,var(--hair))] disabled:cursor-not-allowed disabled:opacity-40",
24
+ )
25
+
26
+ const CAPSULE_EDGE =
27
+ "min-w-0 cursor-pointer border-0 bg-transparent p-0 text-ink-mute transition-colors duration-150 enabled:hover:text-ink disabled:cursor-not-allowed disabled:opacity-30"
28
+
29
+ export type PagerProps = HTMLAttributes<HTMLDivElement> & {
30
+ onPrev?: () => void
31
+ onNext?: () => void
32
+ prevDisabled?: boolean
33
+ nextDisabled?: boolean
34
+ prevLabel?: ReactNode
35
+ nextLabel?: ReactNode
36
+ /** Set these when the edge labels are bare glyphs (chevrons). */
37
+ prevAriaLabel?: string
38
+ nextAriaLabel?: string
39
+ /** Middle slot — PagerPage cells, or any label (a date, "3 / 12", …). */
40
+ children?: ReactNode
41
+ /** Visual dress — "reference" (default) or dashboard's "capsule". */
42
+ dress?: "reference" | "capsule"
43
+ }
44
+
45
+ export function Pager({
46
+ onPrev,
47
+ onNext,
48
+ prevDisabled,
49
+ nextDisabled,
50
+ prevLabel = "‹ prev",
51
+ nextLabel = "next ›",
52
+ prevAriaLabel,
53
+ nextAriaLabel,
54
+ children,
55
+ className,
56
+ dress = "reference",
57
+ ...props
58
+ }: PagerProps) {
59
+ const edge = dress === "capsule" ? CAPSULE_EDGE : BASE_EDGE
60
+ return (
61
+ <div
62
+ data-slot="pager"
63
+ data-dress={dress}
64
+ className={cn(
65
+ "inline-grid grid-cols-[auto_minmax(0,max-content)_auto] items-center gap-[0.3rem]",
66
+ dress === "capsule" &&
67
+ "grid-cols-[auto_1fr_auto] gap-[0.5rem] border border-hair bg-term-bg px-[0.625rem] py-[0.375rem] font-mono text-[12.5px] tabular-nums",
68
+ className,
69
+ )}
70
+ {...props}
71
+ >
72
+ <button
73
+ type="button"
74
+ className={edge}
75
+ disabled={prevDisabled}
76
+ aria-label={prevAriaLabel}
77
+ onClick={onPrev}
78
+ >
79
+ {prevLabel}
80
+ </button>
81
+ {/* Fixed middle column so capsule arrows stay pinned at the ends while
82
+ the label width varies (date strings, page counts, …). */}
83
+ <div className="flex min-w-0 items-center justify-center">{children}</div>
84
+ <button
85
+ type="button"
86
+ className={edge}
87
+ disabled={nextDisabled}
88
+ aria-label={nextAriaLabel}
89
+ onClick={onNext}
90
+ >
91
+ {nextLabel}
92
+ </button>
93
+ </div>
94
+ )
95
+ }
96
+
97
+ export type PagerPageProps = ComponentPropsWithoutRef<"button"> & {
98
+ /** The current page — wears the accent fill and aria-current="page". */
99
+ current?: boolean
100
+ }
101
+
102
+ export function PagerPage({ current, className, ...props }: PagerPageProps) {
103
+ return (
104
+ <button
105
+ type="button"
106
+ data-slot="pager-page"
107
+ aria-current={current ? "page" : undefined}
108
+ className={cn(
109
+ BASE_ITEM,
110
+ "cursor-pointer",
111
+ current
112
+ ? "text-on-accent bg-accent border-accent"
113
+ : "hover:text-ink hover:border-[color-mix(in_srgb,var(--accent)_45%,var(--hair))]",
114
+ className,
115
+ )}
116
+ {...props}
117
+ />
118
+ )
119
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Pagination — the numbered page trail (`nav[aria-label]`), promoted from the
3
+ * controls layer into the kit (#4306, batch K2). Controlled: `page` (1-based)
4
+ * + `pageCount`, with either `hrefForPage` (links) or `onPageChange` (buttons).
5
+ * The window shows first / last with an ellipsis around the current page;
6
+ * prev/next disable at the edges. The current page is the one loud control
7
+ * (ink fill); siblings are hairline ghosts.
8
+ *
9
+ * The `.ctl-pagination*` dress is translated into token utilities here, so the
10
+ * controls CSS can retire. Direction glyphs mirror under RTL via `rtl:`.
11
+ */
12
+
13
+ import { cn } from "../lib/utils"
14
+
15
+ export type PaginationProps = {
16
+ page: number
17
+ pageCount: number
18
+ onPageChange?: (page: number) => void
19
+ hrefForPage?: (page: number) => string
20
+ ariaLabel?: string
21
+ className?: string
22
+ }
23
+
24
+ /** 1-based page window: first, last, and ±1 around current, ellipsis-filled. */
25
+ export function paginationWindow(page: number, pageCount: number): (number | "…")[] {
26
+ const pages = new Set<number>([1, pageCount, page - 1, page, page + 1])
27
+ const sorted = [...pages].filter((p) => p >= 1 && p <= pageCount).sort((a, b) => a - b)
28
+ const out: (number | "…")[] = []
29
+ for (const p of sorted) {
30
+ if (out.length > 0 && p - (out[out.length - 1] as number) > 1) out.push("…")
31
+ out.push(p)
32
+ }
33
+ return out
34
+ }
35
+
36
+ const ITEM =
37
+ "inline-flex h-[2rem] min-w-[2rem] items-center justify-center border border-hair bg-transparent px-[0.5rem] text-ink-soft no-underline transition-colors [font:inherit] hover:border-hair-2 hover:text-ink focus-visible:border-hair-2 focus-visible:ring-[3px] focus-visible:ring-accent/30 focus-visible:outline-none"
38
+
39
+ export function Pagination({
40
+ page,
41
+ pageCount,
42
+ onPageChange,
43
+ hrefForPage,
44
+ ariaLabel = "Pagination",
45
+ className,
46
+ }: PaginationProps) {
47
+ if (pageCount < 1) return null
48
+ const current = Math.max(1, Math.min(pageCount, Math.floor(page)))
49
+ const go = (p: number) => onPageChange?.(Math.max(1, Math.min(pageCount, p)))
50
+
51
+ const item = (p: number) => {
52
+ const isCurrent = p === current
53
+ const cls = cn(ITEM, isCurrent && "cursor-default border-ink bg-ink font-semibold text-bg")
54
+ const inner = String(p)
55
+ if (isCurrent) {
56
+ return (
57
+ <span key={p} aria-current="page" className={cls}>
58
+ {inner}
59
+ </span>
60
+ )
61
+ }
62
+ if (hrefForPage) {
63
+ return (
64
+ <a key={p} href={hrefForPage(p)} className={cn(ITEM, "cursor-pointer")}>
65
+ {inner}
66
+ </a>
67
+ )
68
+ }
69
+ return (
70
+ <button key={p} type="button" className={cn(ITEM, "cursor-pointer")} onClick={() => go(p)}>
71
+ {inner}
72
+ </button>
73
+ )
74
+ }
75
+
76
+ const step = (dir: -1 | 1) => {
77
+ const target = current + dir
78
+ const disabled = dir === -1 ? current <= 1 : current >= pageCount
79
+ const label = dir === -1 ? "Previous page" : "Next page"
80
+ const glyph = dir === -1 ? "←" : "→"
81
+ const cls = cn(ITEM, "cursor-pointer disabled:cursor-not-allowed disabled:opacity-40")
82
+ if (hrefForPage && !disabled) {
83
+ return (
84
+ <a key={dir} href={hrefForPage(target)} aria-label={label} className={cls}>
85
+ <span aria-hidden="true" className="inline-block rtl:scale-x-[-1]">
86
+ {glyph}
87
+ </span>
88
+ </a>
89
+ )
90
+ }
91
+ return (
92
+ <button
93
+ key={dir}
94
+ type="button"
95
+ aria-label={label}
96
+ className={cls}
97
+ disabled={disabled}
98
+ onClick={() => go(target)}
99
+ >
100
+ <span aria-hidden="true" className="inline-block rtl:scale-x-[-1]">
101
+ {glyph}
102
+ </span>
103
+ </button>
104
+ )
105
+ }
106
+
107
+ return (
108
+ <nav
109
+ aria-label={ariaLabel}
110
+ data-slot="pagination"
111
+ className={cn("flex items-center gap-[0.4rem] font-mono text-[0.8rem]", className)}
112
+ >
113
+ {step(-1)}
114
+ {paginationWindow(current, pageCount).map((p, i) =>
115
+ p === "…" ? (
116
+ <span key={`gap-${i}`} aria-hidden="true" className="px-[0.1rem] text-ink-mute">
117
+ …
118
+ </span>
119
+ ) : (
120
+ item(p)
121
+ ),
122
+ )}
123
+ {step(1)}
124
+ </nav>
125
+ )
126
+ }
@@ -0,0 +1,51 @@
1
+ "use client"
2
+
3
+ /**
4
+ * RadioGroup / Radio — the shared selection control (#1548), promoted from the
5
+ * controls layer into the kit (#4306, batch K1). Behavior comes from
6
+ * `@base-ui/react`'s Radio / RadioGroup primitives (roving focus, keyboard
7
+ * toggle, `data-checked` / `data-disabled` state); this file skins them to the
8
+ * form-fields grammar — a 16px circle, hairline, accent dot, accent focus ring
9
+ * — re-expressed in kit token utilities so `.ctl-radio*` can retire.
10
+ *
11
+ * Pair with a plain `<label>` (wrapping or `htmlFor`) — the control owns no
12
+ * label of its own, same as the native inputs it replaces. `Checkbox` and
13
+ * `Switch` remain on the controls layer for now; the kit already owns both, so
14
+ * they have no live main-barrel consumer to repoint.
15
+ */
16
+
17
+ import { Radio as RadioPrimitive } from "@base-ui/react/radio"
18
+ import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group"
19
+
20
+ import { cn } from "../lib/utils"
21
+
22
+ export type RadioGroupProps = RadioGroupPrimitive.Props
23
+ export type RadioProps = RadioPrimitive.Root.Props
24
+
25
+ export function RadioGroup({ className, ...props }: RadioGroupProps) {
26
+ return (
27
+ <RadioGroupPrimitive
28
+ data-slot="radio-group"
29
+ className={cn("flex flex-col gap-2", className)}
30
+ {...props}
31
+ />
32
+ )
33
+ }
34
+
35
+ export function Radio({ className, ...props }: RadioProps) {
36
+ return (
37
+ <RadioPrimitive.Root
38
+ data-slot="radio"
39
+ className={cn(
40
+ "relative inline-flex size-4 shrink-0 cursor-pointer appearance-none items-center justify-center rounded-full border border-hair bg-surface p-0 transition-colors outline-none after:absolute after:-inset-3 focus-visible:ring-[3px] focus-visible:ring-ring/30 data-checked:border-accent data-disabled:cursor-not-allowed data-disabled:opacity-50",
41
+ className,
42
+ )}
43
+ {...props}
44
+ >
45
+ <RadioPrimitive.Indicator
46
+ data-slot="radio-indicator"
47
+ className="absolute inset-[3px] rounded-full bg-accent"
48
+ />
49
+ </RadioPrimitive.Root>
50
+ )
51
+ }
@@ -0,0 +1,85 @@
1
+ "use client"
2
+ /**
3
+ * SearchBar — a search field with a leading glyph, a clear affordance that
4
+ * appears once there's input, and an optional trailing `hint` slot shown
5
+ * while empty (the reference wears a `/` keycap), promoted from the controls
6
+ * layer into the kit (#4306, batch K2). Controlled — the results pane stays
7
+ * with the caller; the bar only owns the query.
8
+ *
9
+ * The `.ctl-search` / `.sb-*` dress is translated into token utilities here;
10
+ * focus-within lights the accent ring, and the native WebKit search-cancel
11
+ * affordance is suppressed in favour of the clear button.
12
+ */
13
+ import type { InputHTMLAttributes, ReactNode } from "react"
14
+
15
+ import { cn } from "../lib/utils"
16
+
17
+ export type SearchBarProps = Omit<
18
+ InputHTMLAttributes<HTMLInputElement>,
19
+ "value" | "onChange" | "type" | "className"
20
+ > & {
21
+ value: string
22
+ onChange: (query: string) => void
23
+ /** Clears the query — defaults to onChange(""). */
24
+ onClear?: () => void
25
+ clearAriaLabel?: string
26
+ /** Trailing slot shown while the query is empty (keycap hint etc.). */
27
+ hint?: ReactNode
28
+ /** Replaces the leading magnifier glyph. */
29
+ glyph?: ReactNode
30
+ /** Lands on the field wrapper, not the input. */
31
+ className?: string
32
+ }
33
+
34
+ const MAGNIFIER = (
35
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="1.6">
36
+ <circle cx="11" cy="11" r="7" />
37
+ <path d="M20 20l-3.5-3.5" strokeLinecap="round" />
38
+ </svg>
39
+ )
40
+
41
+ export function SearchBar({
42
+ value,
43
+ onChange,
44
+ onClear,
45
+ clearAriaLabel = "Clear search",
46
+ hint,
47
+ glyph,
48
+ className,
49
+ "aria-label": ariaLabel = "Search",
50
+ ...inputProps
51
+ }: SearchBarProps) {
52
+ return (
53
+ <div
54
+ data-slot="search-bar"
55
+ className={cn(
56
+ "flex w-[min(100%,26rem)] items-center gap-[0.55rem] border border-hair bg-surface px-[0.7rem] py-[0.55rem] transition-[border-color,box-shadow] duration-200 focus-within:border-[color-mix(in_srgb,var(--accent)_55%,var(--hair))] focus-within:shadow-[0_0_0_3px_color-mix(in_srgb,var(--accent)_22%,transparent)]",
57
+ className,
58
+ )}
59
+ >
60
+ <span className="inline-flex text-ink-mute" aria-hidden="true">
61
+ {glyph ?? MAGNIFIER}
62
+ </span>
63
+ <input
64
+ className="flex-1 border-none bg-transparent font-mono text-[0.82rem] text-ink outline-none placeholder:text-ink-mute [&::-webkit-search-cancel-button]:hidden"
65
+ type="search"
66
+ value={value}
67
+ aria-label={ariaLabel}
68
+ onChange={(e) => onChange(e.target.value)}
69
+ {...inputProps}
70
+ />
71
+ {value ? (
72
+ <button
73
+ type="button"
74
+ className="cursor-pointer border-none bg-transparent px-[0.2rem] py-[0.1rem] text-[0.7rem] text-ink-mute transition-colors hover:text-ink"
75
+ aria-label={clearAriaLabel}
76
+ onClick={onClear ?? (() => onChange(""))}
77
+ >
78
+ ✕
79
+ </button>
80
+ ) : (
81
+ (hint ?? null)
82
+ )}
83
+ </div>
84
+ )
85
+ }
@@ -0,0 +1,76 @@
1
+ "use client"
2
+
3
+ /**
4
+ * SegmentedControl — the range switch (#1548), promoted from the controls
5
+ * layer into the kit (#4306, batch K1). Plain buttons wearing `aria-pressed`
6
+ * inside a `role="group"` — deliberately NOT a tablist (the segments switch a
7
+ * data range on the same view, they don't own tab panels). Two dresses:
8
+ * "reference" (default, mono cells on surface / accent-weak selection) and
9
+ * "accent" (dashboard's shipped look — sans font-medium cells, transparent
10
+ * track, accent/20 wash + accent text on the selection). Token utilities only,
11
+ * so `.nb-seg*` can retire.
12
+ */
13
+
14
+ import type { HTMLAttributes, ReactNode } from "react"
15
+
16
+ import { cn } from "../lib/utils"
17
+
18
+ export type SegmentedOption<T extends string = string> = {
19
+ value: T
20
+ /** Defaults to the value itself. */
21
+ label?: ReactNode
22
+ disabled?: boolean
23
+ }
24
+
25
+ export type SegmentedControlProps<T extends string = string> = Omit<
26
+ HTMLAttributes<HTMLDivElement>,
27
+ "onChange"
28
+ > & {
29
+ options: ReadonlyArray<T | SegmentedOption<T>>
30
+ value: T
31
+ onChange?: (value: T) => void
32
+ /** Visual dress — "reference" (default) or dashboard's "accent". */
33
+ dress?: "reference" | "accent"
34
+ }
35
+
36
+ export function SegmentedControl<T extends string = string>({
37
+ options,
38
+ value,
39
+ onChange,
40
+ className,
41
+ dress = "reference",
42
+ ...props
43
+ }: SegmentedControlProps<T>) {
44
+ const accent = dress === "accent"
45
+ return (
46
+ <div
47
+ data-slot="segmented"
48
+ role="group"
49
+ className={cn(
50
+ "inline-flex overflow-hidden rounded-none border border-hair",
51
+ className,
52
+ )}
53
+ {...props}
54
+ >
55
+ {options.map((option) => {
56
+ const opt = typeof option === "string" ? { value: option } : option
57
+ return (
58
+ <button
59
+ key={opt.value}
60
+ type="button"
61
+ className={cn(
62
+ "inline-flex cursor-pointer items-center justify-center rounded-none bg-surface px-[0.85rem] py-[0.4rem] font-mono text-[0.72rem] text-ink-mute transition-colors [border-inline-start:1px_solid_var(--hair)] first:[border-inline-start:0] hover:text-ink-soft aria-pressed:bg-accent-weak aria-pressed:text-ink disabled:cursor-not-allowed",
63
+ accent &&
64
+ "bg-transparent px-3 py-[0.375rem] font-sans text-xs font-medium text-ink-mute hover:bg-ink/4 hover:text-ink aria-pressed:bg-accent/20 aria-pressed:text-accent",
65
+ )}
66
+ aria-pressed={opt.value === value}
67
+ disabled={opt.disabled}
68
+ onClick={() => onChange?.(opt.value)}
69
+ >
70
+ {opt.label ?? opt.value}
71
+ </button>
72
+ )
73
+ })}
74
+ </div>
75
+ )
76
+ }