@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
@@ -0,0 +1,112 @@
1
+ /**
2
+ * ContactMailto — the shared Cloudflare-safe `mailto:` link, promoted from
3
+ * the three forks that grew one per app (`apps/digithings-web`,
4
+ * `apps/digiquant-web`, `apps/digichat`, issues #2220/#2226).
5
+ *
6
+ * Cloudflare's Email Address Obfuscation (Scrape Shield) rewrites any
7
+ * literal `mailto:` href — and any bare visible address — in the HTML it
8
+ * actually serves, so React's hydration payload disagrees with the real DOM.
9
+ * This component renders with no literal `mailto:` string or bare address in
10
+ * the initial HTML at all (no href server-side; the real href — and, for
11
+ * `showAddress`, the address text — assigned after the hydration-safe client
12
+ * mount flag flips), giving the edge rewriter nothing to rewrite.
13
+ *
14
+ * Real users with JS get a working mailto: link; without JS the link is
15
+ * inert (dimmed, aria-disabled). `showAddress` callers must still pass
16
+ * `children` — the server-rendered fallback text and accessible name until
17
+ * the mount swap. Omitting `children` entirely leaves the link empty until
18
+ * JS runs: acceptable only on JS-required surfaces (the digichat embed),
19
+ * never on static marketing pages.
20
+ *
21
+ * Utility-classed (pending dim only); consuming apps need an `@source` line
22
+ * for this directory (MIGRATION.md rule 3).
23
+ */
24
+ "use client";
25
+
26
+ import { useCallback, useSyncExternalStore } from "react";
27
+ import type { CSSProperties, MouseEvent, ReactNode } from "react";
28
+
29
+ import { cx } from "../../lib/cx";
30
+
31
+ /** Pure so the query-string assembly is unit-testable without rendering. */
32
+ export function buildMailtoHref(email: string, subject?: string): string {
33
+ const query = subject ? `?subject=${subject}` : "";
34
+ return `mailto:${email}${query}`;
35
+ }
36
+
37
+ // Baked into the JSX unconditionally rather than a new app-local CSS class —
38
+ // the frontend canon guard's family census rejects new classes in census
39
+ // app stylesheets; token-backed Tailwind utilities are not scanned.
40
+ const PENDING_CLASSES = ["opacity-50", "cursor-default"] as const;
41
+
42
+ const emptySubscribe = () => () => {};
43
+ const getMountedSnapshot = () => true;
44
+ const getServerSnapshot = () => false;
45
+
46
+ export function ContactMailto({
47
+ email,
48
+ subject,
49
+ showAddress = false,
50
+ className,
51
+ style,
52
+ ariaLabel,
53
+ children,
54
+ }: {
55
+ /** Appended as `?subject=<subject>` verbatim — callers pass it already
56
+ * URL-encoded. */
57
+ email: string;
58
+ subject?: string;
59
+ /** Swap `children` for the address itself once mounted, rather than
60
+ * passing the address as `children` directly (which would bake the bare
61
+ * address into the server-rendered text node). `children` is still
62
+ * required on static pages: the fallback text and accessible name. */
63
+ showAddress?: boolean;
64
+ className?: string;
65
+ style?: CSSProperties;
66
+ ariaLabel?: string;
67
+ children?: ReactNode;
68
+ }) {
69
+ // Server + first client hydration pass: false. After hydration: true.
70
+ // useSyncExternalStore, not setState-in-effect.
71
+ const mounted = useSyncExternalStore(
72
+ emptySubscribe,
73
+ getMountedSnapshot,
74
+ getServerSnapshot,
75
+ );
76
+
77
+ if (
78
+ process.env.NODE_ENV !== "production" &&
79
+ showAddress &&
80
+ children == null
81
+ ) {
82
+ // eslint-disable-next-line no-console
83
+ console.warn(
84
+ "ContactMailto with showAddress and no children renders an empty, " +
85
+ "unlabeled link until JS runs — pass fallback children on static pages.",
86
+ );
87
+ }
88
+
89
+ const readyHref = mounted ? buildMailtoHref(email, subject) : null;
90
+ const addressText = mounted && showAddress ? email : null;
91
+ const pending = !mounted;
92
+
93
+ const onClick = useCallback(
94
+ (e: MouseEvent<HTMLAnchorElement>) => {
95
+ if (pending) e.preventDefault();
96
+ },
97
+ [pending],
98
+ );
99
+
100
+ return (
101
+ <a
102
+ {...(readyHref ? { href: readyHref } : {})}
103
+ className={cx(className, pending && PENDING_CLASSES.join(" "))}
104
+ style={style}
105
+ aria-label={ariaLabel}
106
+ aria-disabled={pending ? true : undefined}
107
+ onClick={onClick}
108
+ >
109
+ {addressText ?? children}
110
+ </a>
111
+ );
112
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Contact family barrel — the Cloudflare-safe mailto link. Re-exported
3
+ * from the package barrel (src/index.ts).
4
+ */
5
+ export { ContactMailto, buildMailtoHref } from "./ContactMailto";
@@ -0,0 +1,40 @@
1
+ /**
2
+ * ConvictionMeter — the single canonical UNSIGNED conviction encoding,
3
+ * promoted from `apps/dashboard/components/shared/conviction-meter.tsx`
4
+ * (dashboard F6 ruling, preserved verbatim): integer pips (max 3 for
5
+ * `positions.conviction`, max 4 for pre-scaled `theses.confidence`), filled
6
+ * pips `--accent`, empty pips `--hair`, accent the only color on its row
7
+ * (F5). Value is clamped and rounded; the redundant `aria-label` + `sr-only`
8
+ * pair and `data-filled` attributes are preserved for testability.
9
+ *
10
+ * Utility-classed (no family CSS); consuming apps need an `@source` line
11
+ * for this directory (MIGRATION.md rule 3).
12
+ */
13
+ export function ConvictionMeter({
14
+ value,
15
+ max = 3,
16
+ srLabel,
17
+ }: {
18
+ value: number;
19
+ max?: number;
20
+ srLabel: string;
21
+ }) {
22
+ const filled = Math.max(0, Math.min(max, Math.round(value)));
23
+ return (
24
+ <span className="inline-flex items-center gap-1" role="img" aria-label={srLabel}>
25
+ {Array.from({ length: max }).map((_, i) => {
26
+ const isFilled = i < filled;
27
+ return (
28
+ <span
29
+ key={i}
30
+ data-filled={isFilled ? "true" : "false"}
31
+ className={`h-1.5 w-1.5 rounded-full ${
32
+ isFilled ? "bg-accent" : "bg-hair"
33
+ }`}
34
+ />
35
+ );
36
+ })}
37
+ <span className="sr-only">{srLabel}</span>
38
+ </span>
39
+ );
40
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * SignedConvictionBadge — the SIGNED stance badge, promoted from
3
+ * `apps/dashboard/components/shared/signed-conviction-badge.tsx`
4
+ * (dashboard F6 ruling, logic verbatim): `decision_log.conviction`
5
+ * (−5..+5; maps to `AnalystPayload.conviction_score`, ge=-5 le=5 — render
6
+ * any signed int, do NOT clamp). Up-toned at zero and above, down-toned
7
+ * below zero: the strict signed-financial-value semantic (F5), never to be
8
+ * merged with the unsigned ConvictionMeter even when the two share a row.
9
+ * One intentional deviation from the dashboard source: `rounded-none` pins
10
+ * the utilitarian radius rule (visually no-op under preflight).
11
+ *
12
+ * Utility-classed (no family CSS); consuming apps need an `@source` line
13
+ * for this directory (MIGRATION.md rule 3).
14
+ */
15
+ export function SignedConvictionBadge({ value }: { value: number }) {
16
+ const sign = value < 0 ? "−" : "+";
17
+ const tone = value < 0 ? "text-down border-down/35" : "text-up border-up/35";
18
+ return (
19
+ <span
20
+ className={`inline-flex items-center rounded-none border px-1.5 py-0.5 font-mono text-[11px] font-semibold tabular-nums ${tone}`}
21
+ >
22
+ {sign}
23
+ {Math.abs(value)}
24
+ </span>
25
+ );
26
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Conviction family barrel — the dashboard F6 vocabulary (unsigned meter +
3
+ * signed badge), promoted verbatim. Re-exported from the package barrel
4
+ * (src/index.ts).
5
+ */
6
+ export { ConvictionMeter } from "./ConvictionMeter";
7
+ export { SignedConvictionBadge } from "./SignedConvictionBadge";
@@ -0,0 +1,52 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ /**
4
+ * BentoGrid + BentoCell — the asymmetric bento layout promoted from the design
5
+ * reference (layout-patterns/bento-grid): a 4-column grid where cell weight
6
+ * signals hierarchy — `hero` anchors 2×2, `wide` runs two columns, `tall` two
7
+ * rows, `unit` takes one. Each cell may scope a module livery (`accent-<id>`,
8
+ * declared in @digithings-ai/design/tokens.css) that tints the ::before hover
9
+ * hairline and the hero wash (styles/data-layout.css). Content is entirely
10
+ * children-driven — the cell only owns the frame; a heading inside a hero cell
11
+ * should carry the `bento-name` class to pick up the hero's larger type.
12
+ * Collapses to one column below 760px. Server components — no state.
13
+ *
14
+ * Wiring (in the consuming app):
15
+ * globals.css @import "@digithings-ai/ui/styles/data-layout.css";
16
+ * @source "<path-to>/packages/ui/src/components/data-layout";
17
+ */
18
+ export type BentoSpan = "hero" | "wide" | "tall" | "unit";
19
+
20
+ export function BentoGrid({
21
+ children,
22
+ className,
23
+ }: {
24
+ children: ReactNode;
25
+ className?: string;
26
+ }) {
27
+ return <div className={`bento-grid${className ? ` ${className}` : ""}`}>{children}</div>;
28
+ }
29
+
30
+ export function BentoCell({
31
+ span = "unit",
32
+ livery,
33
+ className,
34
+ children,
35
+ }: {
36
+ /** Cell weight — hero 2×2, wide 2×1, tall 1×2, unit 1×1. */
37
+ span?: BentoSpan;
38
+ /** Optional module livery scope — suffix of an `accent-<module>` class. */
39
+ livery?: string;
40
+ className?: string;
41
+ children: ReactNode;
42
+ }) {
43
+ return (
44
+ <article
45
+ className={`bento-cell flex flex-col justify-between rounded-none border border-hair p-[1.1rem] bento-${span}${
46
+ livery ? ` accent-${livery}` : ""
47
+ }${className ? ` ${className}` : ""}`}
48
+ >
49
+ {children}
50
+ </article>
51
+ );
52
+ }
@@ -0,0 +1,150 @@
1
+ "use client";
2
+
3
+ import { Children, useCallback, useEffect, useRef, useState, type ReactNode } from "react";
4
+ import { IconButton } from "../../ui";
5
+ import { cn } from "../../lib/utils";
6
+ import { clamp, nearestIndex } from "../effects-chrome/horizontal-track-core";
7
+
8
+ /**
9
+ * CardRail — a horizontal rail of cards on NATIVE scroll-snap: swipe, wheel,
10
+ * arrow keys, or the prev / next buttons (one card per press). Edge fades read
11
+ * the strip as continuing past the frame. No pin, no transform, no JS needed to
12
+ * scroll — the buttons are an enhancement over a fully working native rail.
13
+ *
14
+ * Re-authored from the design reference's changelog rail (and the refactor
15
+ * branch's rail) as a utilities-only part: no CSS sheet, no `.cr-*` classes.
16
+ * The edge fade is a mask over token stops (alpha only, no raw colour).
17
+ *
18
+ * Each child is wrapped in a `role="listitem"` snap target. The wrapper carries
19
+ * `data-active="true"` on the card at the leading edge (a `group/card`, so a
20
+ * child can style `group-data-[active=true]/card:border-accent` without the rail
21
+ * knowing anything about it). The wrapper is not a tab stop — put links and
22
+ * buttons inside the cards; focusing one scrolls the rail natively. The track
23
+ * itself is focusable so a keyboard user can scroll it with the arrow keys.
24
+ *
25
+ * Reveal-safe: the track carries vertical padding so a `Reveal` child's rise
26
+ * (translateY) is not clipped by the rail's overflow, and wrappers never
27
+ * transform. With no JS and under reduced motion every card is reachable.
28
+ *
29
+ * Stepping is instant under `prefers-reduced-motion` (CSS `scroll-behavior`
30
+ * smooth is applied only when motion is allowed).
31
+ *
32
+ * Wiring (in the consuming app):
33
+ * globals.css @source "<path-to>/packages/ui/src/components/data-layout";
34
+ */
35
+ export type CardRailProps = {
36
+ /** The cards. Each direct child becomes one snap item. */
37
+ children: ReactNode;
38
+ /** Accessible name for the scroll region, e.g. "Strategy tearsheets". */
39
+ ariaLabel: string;
40
+ /** Left of the prev / next buttons (a title, a count). */
41
+ header?: ReactNode;
42
+ /** Classes on each snap-item wrapper (sets the card width). */
43
+ itemClassName?: string;
44
+ /** Classes on the outermost element. */
45
+ className?: string;
46
+ /** Accessible names for the two buttons. */
47
+ prevLabel?: string;
48
+ nextLabel?: string;
49
+ };
50
+
51
+ // Alpha stops through a token — a mask reads alpha only, so no raw colour.
52
+ const FADE =
53
+ "[mask-image:linear-gradient(90deg,transparent,var(--ink)_1.5rem,var(--ink)_calc(100%_-_1.5rem),transparent)]";
54
+
55
+ function offsetsOf(track: HTMLElement): number[] {
56
+ const kids = Array.from(track.children).filter((c): c is HTMLElement => c instanceof HTMLElement);
57
+ const base = kids[0]?.offsetLeft ?? 0;
58
+ return kids.map((k) => k.offsetLeft - base);
59
+ }
60
+
61
+ export function CardRail({
62
+ children,
63
+ ariaLabel,
64
+ header,
65
+ itemClassName,
66
+ className,
67
+ prevLabel = "Previous card",
68
+ nextLabel = "Next card",
69
+ }: CardRailProps) {
70
+ const trackRef = useRef<HTMLDivElement | null>(null);
71
+ const items = Children.toArray(children);
72
+ const [active, setActive] = useState(0);
73
+ const [atStart, setAtStart] = useState(true);
74
+ const [atEnd, setAtEnd] = useState(false);
75
+
76
+ const sync = useCallback(() => {
77
+ const el = trackRef.current;
78
+ if (!el) return;
79
+ setAtStart(el.scrollLeft <= 1);
80
+ setAtEnd(el.scrollLeft + el.clientWidth >= el.scrollWidth - 1);
81
+ setActive(nearestIndex(offsetsOf(el), el.scrollLeft));
82
+ }, []);
83
+
84
+ useEffect(() => {
85
+ const el = trackRef.current;
86
+ if (!el) return;
87
+ sync();
88
+ el.addEventListener("scroll", sync, { passive: true });
89
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(sync);
90
+ observer?.observe(el);
91
+ return () => {
92
+ el.removeEventListener("scroll", sync);
93
+ observer?.disconnect();
94
+ };
95
+ }, [sync, items.length]);
96
+
97
+ const step = (direction: 1 | -1) => {
98
+ const el = trackRef.current;
99
+ if (!el) return;
100
+ const offsets = offsetsOf(el);
101
+ const next = clamp(active + direction, 0, offsets.length - 1);
102
+ el.scrollTo({ left: offsets[next] ?? 0 });
103
+ };
104
+
105
+ return (
106
+ <div className={cn("grid min-w-0 gap-3", className)} data-card-rail="">
107
+ <div className="flex items-end justify-between gap-4">
108
+ <div className="min-w-0">{header}</div>
109
+ <div className="flex shrink-0 gap-[0.4rem]">
110
+ <IconButton
111
+ aria-label={prevLabel}
112
+ onClick={() => step(-1)}
113
+ disabled={atStart}
114
+ className="border border-hair"
115
+ >
116
+ <span aria-hidden="true">{"←"}</span>
117
+ </IconButton>
118
+ <IconButton
119
+ aria-label={nextLabel}
120
+ onClick={() => step(1)}
121
+ disabled={atEnd}
122
+ className="border border-hair"
123
+ >
124
+ <span aria-hidden="true">{"→"}</span>
125
+ </IconButton>
126
+ </div>
127
+ </div>
128
+ <div className={cn("min-w-0", FADE)}>
129
+ <div
130
+ ref={trackRef}
131
+ role="list"
132
+ aria-label={ariaLabel}
133
+ tabIndex={0}
134
+ className="flex snap-x snap-mandatory gap-4 overflow-x-auto scroll-ps-6 px-6 py-4 outline-none [scrollbar-width:none] motion-safe:scroll-smooth focus-visible:outline focus-visible:outline-1 focus-visible:-outline-offset-1 focus-visible:outline-accent [&::-webkit-scrollbar]:hidden"
135
+ >
136
+ {items.map((child, i) => (
137
+ <div
138
+ key={i}
139
+ role="listitem"
140
+ data-active={i === active ? "true" : undefined}
141
+ className={cn("group/card relative flex-none snap-start w-[min(82vw,17rem)]", itemClassName)}
142
+ >
143
+ {child}
144
+ </div>
145
+ ))}
146
+ </div>
147
+ </div>
148
+ </div>
149
+ );
150
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * DotMatrixStat — a single metric rendered as a hardware-style LED grid,
3
+ * promoted from the design reference (data/dot-matrix-stat). A cols×rows
4
+ * field of dots lights the first N to encode a percentage, paired with a
5
+ * mono label / value / note column. The lit dots wear the module accent
6
+ * (scope with an `accent-<module>` livery class via className) and glow.
7
+ * The dot field is pure CSS/DOM — no canvas — and decorative (aria-hidden);
8
+ * the value is real text. Server component — no state, no effects.
9
+ *
10
+ * Wiring (in the consuming app):
11
+ * globals.css @import "@digithings-ai/ui/styles/data-layout.css";
12
+ * @source "<path-to>/packages/ui/src/components/data-layout";
13
+ */
14
+ export type DotMatrixStatProps = {
15
+ /** Mono micro-caps label above the value — "Signal confidence". */
16
+ label: string;
17
+ /** The figure itself, preformatted — "67%". */
18
+ value: string;
19
+ /** Percentage of the field to light, 0–100. */
20
+ percent: number;
21
+ /** One quiet sentence under the value. */
22
+ note?: string;
23
+ /** Dot columns in the LED field. */
24
+ cols?: number;
25
+ /** Dot rows in the LED field. */
26
+ rows?: number;
27
+ className?: string;
28
+ };
29
+
30
+ export function DotMatrixStat({
31
+ label,
32
+ value,
33
+ percent,
34
+ note,
35
+ cols = 12,
36
+ rows = 8,
37
+ className,
38
+ }: DotMatrixStatProps) {
39
+ const total = cols * rows;
40
+ const lit = Math.min(total, Math.max(0, Math.round((percent / 100) * total)));
41
+ return (
42
+ <div
43
+ className={`grid grid-cols-[180px_1fr] gap-[1.1rem] rounded-none border border-hair bg-surface p-[1.1rem] max-[900px]:grid-cols-1${
44
+ className ? ` ${className}` : ""
45
+ }`}
46
+ >
47
+ <div
48
+ className="dms-grid"
49
+ style={{ gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` }}
50
+ aria-hidden="true"
51
+ >
52
+ {Array.from({ length: total }, (_, i) => (
53
+ <span key={i} className={i < lit ? "dms-dot on" : "dms-dot"} />
54
+ ))}
55
+ </div>
56
+ <div>
57
+ <p className="font-mono text-[0.72rem] uppercase tracking-[0.08em] text-ink-mute">
58
+ {label}
59
+ </p>
60
+ <p className="mt-[0.2rem] font-mono text-[clamp(2rem,5vw,2.8rem)]">{value}</p>
61
+ {note ? <p className="mt-[0.55rem] max-w-[50ch] text-ink-soft">{note}</p> : null}
62
+ </div>
63
+ </div>
64
+ );
65
+ }
@@ -0,0 +1,80 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ /**
4
+ * FeatureCell — the universal feature block promoted from the design reference
5
+ * (layout-patterns/feature-cell), the structure that repeats across graphite /
6
+ * cursor / x.ai and every band we ship: eyebrow → a short outcome headline →
7
+ * one sentence of mechanism → a quiet link → the product visual (children —
8
+ * typically a <ProductFrame/>). Each cell may scope a module livery
9
+ * (`accent-<module>`) that tints the eyebrow and link. Stack cells as direct
10
+ * siblings and even-numbered ones swap copy/visual columns automatically
11
+ * (`:nth-child(even)` in styles/data-layout.css); everything collapses to one
12
+ * column below 760px. Server component — no state, no effects.
13
+ *
14
+ * Wiring (in the consuming app):
15
+ * globals.css @import "@digithings-ai/ui/styles/data-layout.css";
16
+ * @source "<path-to>/packages/ui/src/components/data-layout";
17
+ */
18
+ export type FeatureCellProps = {
19
+ /** Mono micro-caps eyebrow above the headline — "research". */
20
+ eyebrow: string;
21
+ /** The outcome headline, 4–8 words. */
22
+ outcome: string;
23
+ /** One sentence of mechanism under the headline. */
24
+ mechanism: string;
25
+ /** Destination of the quiet link; omit to drop the link. */
26
+ href?: string;
27
+ /** Link copy. */
28
+ linkLabel?: string;
29
+ /** Overrides the link's accessible name — set this when two cells on the
30
+ * same page would otherwise share identical link text (and, as in the
31
+ * reference demo, an identical href): screen readers announce links by
32
+ * their accessible name, so two indistinguishable "Learn more" links
33
+ * read as one ambiguous link repeated. */
34
+ linkAriaLabel?: string;
35
+ /** Optional module livery scope — suffix of an `accent-<module>` class. */
36
+ livery?: string;
37
+ className?: string;
38
+ /** The product visual — typically a <ProductFrame/> crop. */
39
+ children?: ReactNode;
40
+ };
41
+
42
+ export function FeatureCell({
43
+ eyebrow,
44
+ outcome,
45
+ mechanism,
46
+ href,
47
+ linkLabel = "Learn more",
48
+ linkAriaLabel,
49
+ livery,
50
+ className,
51
+ children,
52
+ }: FeatureCellProps) {
53
+ return (
54
+ <article
55
+ className={`fc-cell grid grid-cols-[minmax(0,1fr)_minmax(0,1.1fr)] items-center gap-[1.6rem] rounded-none border border-hair bg-surface p-[1.6rem] max-[760px]:grid-cols-1${
56
+ livery ? ` accent-${livery}` : ""
57
+ }${className ? ` ${className}` : ""}`}
58
+ >
59
+ <div className="fc-copy">
60
+ <p className="font-mono text-[0.62rem] uppercase tracking-[0.12em] text-accent">
61
+ {eyebrow}
62
+ </p>
63
+ <h3 className="mt-[0.5rem] font-display font-normal text-[clamp(1.3rem,2.6vw,1.7rem)] tracking-[-0.013em] leading-[1.12] text-ink">
64
+ {outcome}
65
+ </h3>
66
+ <p className="mt-[0.6rem] max-w-[42ch] text-[0.92rem] text-ink-soft">{mechanism}</p>
67
+ {href ? (
68
+ <a
69
+ className="fc-link mt-[0.9rem] inline-block font-mono text-[0.72rem] text-accent no-underline"
70
+ href={href}
71
+ aria-label={linkAriaLabel}
72
+ >
73
+ {linkLabel} <span aria-hidden="true">→</span>
74
+ </a>
75
+ ) : null}
76
+ </div>
77
+ {children}
78
+ </article>
79
+ );
80
+ }
@@ -0,0 +1,125 @@
1
+ import { cn } from "../../lib/utils";
2
+
3
+ /**
4
+ * MediaFrame — a 16:9 framed artboard for a recording (video) or a still
5
+ * (image), wearing the terminal chrome: a title row of bracketed micro-labels,
6
+ * a REQUIRED honesty `badge`, the media, and a caption row. Props-driven and
7
+ * honest: it shows only the `src` it is given.
8
+ *
9
+ * Without `src` it renders a designed placeholder, not a bare dashed box: the
10
+ * title bar, a faint hairline grid, viewfinder corner ticks, a disabled play
11
+ * glyph, and the caller's `placeholderLabel` ("recording to come") centred. A
12
+ * `--:--:--` timecode stands in for a length it does not know.
13
+ *
14
+ * With `src` (kind "video", the default) it renders a native `<video>` with
15
+ * controls, `muted`, `playsInline`, `preload="none"` and the optional `poster`;
16
+ * it NEVER autoplays, so reduced motion and data-saver users get a still until
17
+ * they press play. kind "image" renders an `<img>` (alt = caption) instead.
18
+ *
19
+ * Utilities-only server component: no CSS sheet, no keyframes, no state, no
20
+ * motion. Tokens only (grid lines are `--hair`, wash is `--surface`).
21
+ *
22
+ * Wiring (in the consuming app):
23
+ * globals.css @source "<path-to>/packages/ui/src/components/data-layout";
24
+ */
25
+ export type MediaFrameProps = {
26
+ /** Video (or image) URL. Omit for the placeholder state. */
27
+ src?: string;
28
+ /** Poster still for a video. */
29
+ poster?: string;
30
+ /** What the media shows — caption row, accessible name, image alt. */
31
+ caption: string;
32
+ /** Centred label of the placeholder state, e.g. "recording to come". */
33
+ placeholderLabel: string;
34
+ /** Title-row label, e.g. "dashboard walkthrough". */
35
+ title: string;
36
+ /** REQUIRED honesty badge in the chrome, e.g. "Placeholder · no recording yet". */
37
+ badge: string;
38
+ /** "video" (default) or "image". */
39
+ kind?: "video" | "image";
40
+ className?: string;
41
+ };
42
+
43
+ const CORNER = "pointer-events-none absolute size-3 border-ink-mute";
44
+
45
+ export function MediaFrame({
46
+ src,
47
+ poster,
48
+ caption,
49
+ placeholderLabel,
50
+ title,
51
+ badge,
52
+ kind = "video",
53
+ className,
54
+ }: MediaFrameProps) {
55
+ return (
56
+ <figure
57
+ data-slot="media-frame"
58
+ data-state={src ? "media" : "placeholder"}
59
+ className={cn("m-0 min-w-0 border border-hair bg-surface font-mono", className)}
60
+ >
61
+ <div className="flex items-center gap-3 border-b border-hair px-3 py-2 text-[0.68rem] tracking-[0.04em] text-ink-mute">
62
+ <span aria-hidden="true">{kind === "image" ? "[image]" : "[video]"}</span>
63
+ <span className="min-w-0 flex-1 truncate text-ink-soft">{title}</span>
64
+ <span className="shrink-0" aria-hidden="true">
65
+ {src ? "[ready]" : "[empty]"}
66
+ </span>
67
+ </div>
68
+ <div className="border-b border-hair px-3 py-1.5 text-[0.66rem] text-ink-soft" data-slot="media-frame-badge">
69
+ {`◇ ${badge}`}
70
+ </div>
71
+
72
+ <div className="relative aspect-video w-full overflow-hidden bg-term-bg">
73
+ {src ? (
74
+ kind === "image" ? (
75
+ // Note: plain <img> is intentional — package part, no next/image dependency.
76
+ // (No next lint plugin runs on packages, so no disable directive here.)
77
+ <img src={src} alt={caption} className="absolute inset-0 size-full object-cover" />
78
+ ) : (
79
+ <video
80
+ className="absolute inset-0 size-full object-cover"
81
+ src={src}
82
+ poster={poster}
83
+ controls
84
+ muted
85
+ playsInline
86
+ preload="none"
87
+ aria-label={caption}
88
+ />
89
+ )
90
+ ) : (
91
+ <div role="img" aria-label={`${placeholderLabel}: ${caption}`} className="absolute inset-0">
92
+ <div
93
+ aria-hidden="true"
94
+ className="absolute inset-0 opacity-50 [background-image:linear-gradient(var(--hair)_1px,transparent_1px),linear-gradient(90deg,var(--hair)_1px,transparent_1px)] [background-size:2.5rem_2.5rem]"
95
+ />
96
+ <span aria-hidden="true" className={cn(CORNER, "start-3 top-3 border-s border-t")} />
97
+ <span aria-hidden="true" className={cn(CORNER, "end-3 top-3 border-e border-t")} />
98
+ <span aria-hidden="true" className={cn(CORNER, "bottom-3 start-3 border-b border-s")} />
99
+ <span aria-hidden="true" className={cn(CORNER, "bottom-3 end-3 border-b border-e")} />
100
+ <div aria-hidden="true" className="absolute inset-0 grid place-content-center justify-items-center gap-3">
101
+ <button
102
+ type="button"
103
+ disabled
104
+ tabIndex={-1}
105
+ className="grid size-12 cursor-not-allowed place-items-center border border-hair bg-surface text-[1rem] text-ink-mute opacity-80"
106
+ >
107
+ ▶
108
+ </button>
109
+ <span className="border border-hair bg-surface px-3 py-1 text-[0.72rem] tracking-[0.06em] text-ink-soft">
110
+ {`[ ${placeholderLabel} ]`}
111
+ </span>
112
+ </div>
113
+ <span aria-hidden="true" className="absolute bottom-3 start-8 text-[0.62rem] tracking-[0.08em] text-ink-mute">
114
+ --:--:--
115
+ </span>
116
+ </div>
117
+ )}
118
+ </div>
119
+
120
+ <figcaption className="border-t border-hair px-3 py-2 text-[0.72rem] leading-[1.5] text-ink-soft">
121
+ {caption}
122
+ </figcaption>
123
+ </figure>
124
+ );
125
+ }