@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,123 @@
1
+ import type { RepoPullItem } from "./types";
2
+
3
+ /**
4
+ * One UTC day of contribution history. `count` is the total; the three source
5
+ * fields are the split behind it, present only when the caller bucketed them
6
+ * (`bucketContributions`). The heat cell's `title` reads the split so hovering a
7
+ * square says where the day's work came from, not just how much there was.
8
+ */
9
+ export type HeatDay = {
10
+ date: string;
11
+ count: number;
12
+ pulls?: number;
13
+ commits?: number;
14
+ issues?: number;
15
+ };
16
+
17
+ const DAY_MS = 86_400_000;
18
+
19
+ /** Stamp → UTC calendar day (`2026-08-21T00:30:00+02:00` → `2026-08-20`); "" when absent. */
20
+ function dayOf(stamp: string | null | undefined): string {
21
+ if (!stamp) return "";
22
+ const ms = Date.parse(stamp);
23
+ return Number.isNaN(ms) ? "" : isoOf(ms);
24
+ }
25
+
26
+ function toUtcMidnight(d: Date): number {
27
+ return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
28
+ }
29
+
30
+ function isoOf(ms: number): string {
31
+ return new Date(ms).toISOString().slice(0, 10);
32
+ }
33
+
34
+ /**
35
+ * Bucket merged-PR merge dates into consecutive UTC days, oldest → newest.
36
+ * `weeks * 7` cells ending at `end` (default today). Pulls outside the window
37
+ * or without a date are ignored. Pure — safe for SSR and tests.
38
+ */
39
+ export function bucketDaily(
40
+ pulls: RepoPullItem[],
41
+ weeks = 16,
42
+ end: Date = new Date(),
43
+ ): HeatDay[] {
44
+ const safeWeeks = Math.max(1, Math.floor(weeks));
45
+ const endMs = toUtcMidnight(end);
46
+ const total = safeWeeks * 7;
47
+ const startMs = endMs - (total - 1) * DAY_MS;
48
+ const counts = new Map<string, number>();
49
+ for (const p of pulls) {
50
+ const day = dayOf(p.mergedAt);
51
+ if (!day) continue;
52
+ const ms = Date.parse(`${day}T00:00:00Z`);
53
+ if (Number.isNaN(ms) || ms < startMs || ms > endMs) continue;
54
+ counts.set(day, (counts.get(day) ?? 0) + 1);
55
+ }
56
+ const days: HeatDay[] = [];
57
+ for (let i = 0; i < total; i++) {
58
+ const date = isoOf(startMs + i * DAY_MS);
59
+ days.push({ date, count: counts.get(date) ?? 0 });
60
+ }
61
+ return days;
62
+ }
63
+
64
+ /**
65
+ * Bucket repo-level contributions (GitHub profile-graph style) into
66
+ * consecutive UTC days, oldest → newest. Sums merged PRs + commits +
67
+ * closed issues per day so the heat tracks contributions, not merges alone.
68
+ * Each source is an ISO timestamp (or null); undated or out-of-window
69
+ * entries are ignored. Pure — safe for SSR and tests.
70
+ */
71
+ export function bucketContributions(
72
+ pulls: RepoPullItem[],
73
+ commits: Array<string | null | undefined> = [],
74
+ closedIssues: Array<string | null | undefined> = [],
75
+ weeks = 53,
76
+ end: Date = new Date(),
77
+ ): HeatDay[] {
78
+ const safeWeeks = Math.max(1, Math.floor(weeks));
79
+ const endMs = toUtcMidnight(end);
80
+ const total = safeWeeks * 7;
81
+ const startMs = endMs - (total - 1) * DAY_MS;
82
+ const counts = new Map<string, number>();
83
+ const split = {
84
+ pulls: new Map<string, number>(),
85
+ commits: new Map<string, number>(),
86
+ issues: new Map<string, number>(),
87
+ };
88
+ const bump = (bucket: Map<string, number>, day: string) =>
89
+ bucket.set(day, (bucket.get(day) ?? 0) + 1);
90
+ const add = (bucket: Map<string, number>, stamp: string | null | undefined) => {
91
+ const day = dayOf(stamp);
92
+ if (!day) return;
93
+ const ms = Date.parse(`${day}T00:00:00Z`);
94
+ if (Number.isNaN(ms) || ms < startMs || ms > endMs) return;
95
+ counts.set(day, (counts.get(day) ?? 0) + 1);
96
+ bump(bucket, day);
97
+ };
98
+ for (const p of pulls) add(split.pulls, p.mergedAt);
99
+ for (const c of commits) add(split.commits, c);
100
+ for (const i of closedIssues) add(split.issues, i);
101
+ const days: HeatDay[] = [];
102
+ for (let i = 0; i < total; i++) {
103
+ const date = isoOf(startMs + i * DAY_MS);
104
+ days.push({
105
+ date,
106
+ count: counts.get(date) ?? 0,
107
+ pulls: split.pulls.get(date) ?? 0,
108
+ commits: split.commits.get(date) ?? 0,
109
+ issues: split.issues.get(date) ?? 0,
110
+ });
111
+ }
112
+ return days;
113
+ }
114
+
115
+ /**
116
+ * Accent step for a cell: 0 when empty, otherwise 1–4. Small maxima map
117
+ * 1:1; larger ones use quartiles so a single busy day cannot flatten the rest.
118
+ */
119
+ export function levelFor(count: number, max: number): 0 | 1 | 2 | 3 | 4 {
120
+ if (count <= 0) return 0;
121
+ if (max <= 4) return Math.min(4, count) as 1 | 2 | 3 | 4;
122
+ return Math.min(4, Math.max(1, Math.ceil((count / max) * 4))) as 1 | 2 | 3 | 4;
123
+ }
@@ -0,0 +1,28 @@
1
+ export {
2
+ RepoActivity,
3
+ type RepoActivityProps,
4
+ type RepoContributor,
5
+ type RepoModuleRelease,
6
+ } from "./RepoActivity";
7
+ export { RepoHeatmap, type RepoHeatmapProps } from "./RepoHeatmap";
8
+ export { bucketContributions, bucketDaily, levelFor, type HeatDay } from "./heatmap";
9
+ export { fetchRepoActivityLive, type FetchRepoActivityLiveOptions } from "./fetch";
10
+ export {
11
+ REPO_ACTIVITY_DEMO,
12
+ REPO_ACTIVITY_DEMO_CLONE,
13
+ REPO_ACTIVITY_DEMO_CONTRIBUTING,
14
+ REPO_ACTIVITY_DEMO_URL,
15
+ } from "./demo";
16
+ export {
17
+ cloneParts,
18
+ DEFAULT_LIVE_TIMEOUT_MS,
19
+ grouped,
20
+ isoDay,
21
+ type RepoActivityLiveConfig,
22
+ type RepoActivitySnapshot,
23
+ type RepoFeature,
24
+ type RepoIssueItem,
25
+ type RepoModuleActivity,
26
+ type RepoPullItem,
27
+ type RepoRelease,
28
+ } from "./types";
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Snapshot-first repository activity — the shared data contract for compact
3
+ * and detailed views. Velocity figures are windowed; backlog figures are not.
4
+ * Stars, forks and watchers are deliberately absent.
5
+ */
6
+
7
+ import type { HeatDay } from "./heatmap";
8
+
9
+ export type RepoRelease = {
10
+ tag: string;
11
+ name: string | null;
12
+ publishedAt: string;
13
+ url: string;
14
+ };
15
+
16
+ export type RepoPullItem = {
17
+ number: number;
18
+ title: string;
19
+ url: string;
20
+ /** Merge time when known; closed_at is an acceptable proxy from Search. */
21
+ mergedAt: string | null;
22
+ };
23
+
24
+ export type RepoIssueItem = {
25
+ number: number;
26
+ title: string;
27
+ url: string;
28
+ updatedAt: string | null;
29
+ };
30
+
31
+ export type RepoFeature = {
32
+ scope: string | null;
33
+ summary: string;
34
+ pr: number;
35
+ date: string | null;
36
+ };
37
+
38
+ export type RepoModuleActivity = {
39
+ path: string;
40
+ lastCommit: string | null;
41
+ files: number;
42
+ lines: number;
43
+ };
44
+
45
+ /**
46
+ * One snapshot object both variants render. `commits` / `pullsMerged` /
47
+ * `issuesClosed` cover `windowDays` on `branch`. `pullsOpen` / `issuesOpen`
48
+ * are the current backlog (unwindowed). `modules` and `features` are optional
49
+ * extras for hosts that already generate them; the views do not require them.
50
+ */
51
+ export type RepoActivitySnapshot = {
52
+ generatedAt: string;
53
+ windowDays: number;
54
+ branch: string;
55
+ commits: number;
56
+ pullsMerged: number;
57
+ issuesClosed: number;
58
+ pullsOpen: number;
59
+ issuesOpen: number;
60
+ latestRelease: RepoRelease | null;
61
+ mergedPulls: RepoPullItem[];
62
+ openIssues: RepoIssueItem[];
63
+ /** Pre-bucketed per-day contributions for the heatmap (oldest → newest).
64
+ * Emitted by scripts/fetch_repo_activity.py; absent in older snapshots. */
65
+ dailyContributions?: HeatDay[];
66
+ features?: RepoFeature[];
67
+ modules?: Record<string, RepoModuleActivity | undefined>;
68
+ };
69
+
70
+ /** Live GitHub refresh — all-or-nothing; the component keeps the snapshot on any failure. */
71
+ export type RepoActivityLiveConfig = {
72
+ owner: string;
73
+ repo: string;
74
+ branch?: string;
75
+ windowDays?: number;
76
+ timeoutMs?: number;
77
+ };
78
+
79
+ export const DEFAULT_LIVE_TIMEOUT_MS = 8_000;
80
+
81
+ /** `2026-08-06T12:34:56Z` → `2026-08-06`; empty string when absent. */
82
+ export function isoDay(stamp: string | null | undefined): string {
83
+ return stamp ? stamp.slice(0, 10) : "";
84
+ }
85
+
86
+ /** Thousands-separated, locale-stable so SSR and the browser cannot drift. */
87
+ export function grouped(n: number): string {
88
+ return n.toLocaleString("en-US");
89
+ }
90
+
91
+ /**
92
+ * Split a clone command at its last slash so a `<wbr/>` can sit after the org.
93
+ * Rejoining the halves must equal the copied payload.
94
+ */
95
+ export function cloneParts(command: string): [string, string] {
96
+ const cut = command.lastIndexOf("/") + 1;
97
+ if (cut <= 0) return [command, ""];
98
+ return [command.slice(0, cut), command.slice(cut)];
99
+ }
@@ -0,0 +1,116 @@
1
+ "use client";
2
+ import { m, useMotionSafe, EASE } from "../../motion/primitives";
3
+
4
+ /**
5
+ * NumberedStages — the sequential-stage grammar promoted from the design
6
+ * reference (layout-patterns/numbered-stages): a numbered spine where each
7
+ * step is one move in the flow — big mono index badge, an optional mono
8
+ * micro-caps tag, a serif title, one sentence of mechanism. A hairline spine
9
+ * (`.ns-stage::before`, styles/stages.css) runs through the badge column and
10
+ * stops at the last stage; each stage may scope its own module livery via
11
+ * `livery` (`accent-<module>` — declared in @digithings-ai/design/tokens.css),
12
+ * which re-tints the badge's two-color color-mix and the tag/index text.
13
+ *
14
+ * Scroll cadence (#1450): stages fill the pipeline one at a time — each row
15
+ * rises in on its own whileInView with an index-staggered delay, so on a
16
+ * normal scroll the spine reads 01 → 02 → 03 → 04 in order. `animated={false}`
17
+ * opts a consumer out; reduced motion (useMotionSafe) and no-JS
18
+ * (html.no-js [data-motion]) render every stage visible with no animation.
19
+ *
20
+ * Alignment: the badge pill is 2rem tall; an untagged title's first line is
21
+ * flex-centered against that height so pill and title read as one row. With a
22
+ * `tag`, the tag takes the pill-aligned slot and the title stacks under it.
23
+ *
24
+ * Client component (Motion m.li under the app's MotionProvider). Wiring (in
25
+ * the consuming app):
26
+ * globals.css @import "@digithings-ai/ui/styles/stages.css";
27
+ * @source "<path-to>/packages/ui/src/components/stages";
28
+ */
29
+ export type NumberedStage = {
30
+ /** Big mono index rendered in the spine badge — "1.0", "01", "a" … */
31
+ num: string;
32
+ /** Serif stage title. */
33
+ title: string;
34
+ /** One sentence of mechanism under the title. */
35
+ mech: string;
36
+ /** Optional mono micro-caps tag above the title (module name, phase …). */
37
+ tag?: string;
38
+ /** Optional module livery scope — suffix of an `accent-<module>` class. */
39
+ livery?: string;
40
+ };
41
+
42
+ export function NumberedStages({
43
+ stages,
44
+ className,
45
+ animated = true,
46
+ }: {
47
+ stages: NumberedStage[];
48
+ className?: string;
49
+ /** Sequential scroll fill (default). Pass false for a static spine. */
50
+ animated?: boolean;
51
+ }) {
52
+ const safe = useMotionSafe();
53
+ const fill = animated && safe;
54
+
55
+ return (
56
+ <ol className={["grid list-none gap-0 p-0", className].filter(Boolean).join(" ")}>
57
+ {stages.map((s, i) => {
58
+ // Utilities live in clean string literals on purpose: a template hole
59
+ // butted against a candidate (`pb-[…]${`) hides it from Tailwind's
60
+ // scanner, so the utility is silently never generated — the original
61
+ // pb-[1.8rem] shipped un-compiled and the stages rendered with no
62
+ // inter-stage padding at all (#1450 round 3). 2.8rem is the ruled
63
+ // breathing room between a stage's blurb and the next stage's title;
64
+ // the spine ::before spans the padded box (styles/stages.css), so the
65
+ // connector stays continuous, and :last-child zeroes the trailing pad.
66
+ const liClass = [
67
+ "ns-stage relative grid grid-cols-[3.4rem_1fr] gap-[1.1rem] pb-[2.8rem]",
68
+ s.livery ? `accent-${s.livery}` : "",
69
+ ]
70
+ .filter(Boolean)
71
+ .join(" ");
72
+ const content = (
73
+ <>
74
+ <span className="ns-node" aria-hidden="true">
75
+ {s.num}
76
+ </span>
77
+ <div className={s.tag ? "pt-[0.4rem]" : undefined}>
78
+ {s.tag ? (
79
+ <p className="font-mono text-[0.6rem] uppercase tracking-[0.12em] text-accent">
80
+ {s.tag}
81
+ </p>
82
+ ) : null}
83
+ <h3
84
+ className={`${
85
+ s.tag ? "mt-[0.25rem]" : "flex min-h-[2rem] items-center"
86
+ } font-display font-normal text-[clamp(1.2rem,2.4vw,1.5rem)] leading-[1.25] tracking-[-0.013em] text-ink`}
87
+ >
88
+ {s.title}
89
+ </h3>
90
+ <p className="mt-[0.35rem] max-w-[52ch] text-[0.9rem] leading-[1.55] text-ink-soft">
91
+ {s.mech}
92
+ </p>
93
+ </div>
94
+ </>
95
+ );
96
+ return fill ? (
97
+ <m.li
98
+ key={s.num}
99
+ className={liClass}
100
+ data-motion=""
101
+ initial={{ opacity: 0, y: 14 }}
102
+ whileInView={{ opacity: 1, y: 0 }}
103
+ viewport={{ once: true, margin: "0px 0px -12% 0px" }}
104
+ transition={{ duration: 0.55, ease: EASE, delay: i * 0.28 }}
105
+ >
106
+ {content}
107
+ </m.li>
108
+ ) : (
109
+ <li key={s.num} className={liClass}>
110
+ {content}
111
+ </li>
112
+ );
113
+ })}
114
+ </ol>
115
+ );
116
+ }
@@ -0,0 +1,116 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ /**
4
+ * Brand marks — promoted from the design reference (symbols/marks) as pure,
5
+ * props-driven components. The four-stroke digiquant signature was previously
6
+ * copied verbatim in four places (dashboard-mark.tsx + DashboardLoader.tsx,
7
+ * digiquant-web's DigiquantMark, the reference specimen) — this file is the
8
+ * canonical copy. Everything draws in currentColor so a mark inherits the
9
+ * ink/accent of its livery scope. No CSS ships with this family: the marks
10
+ * are pure SVG/text; stroke-draw loader animations stay in the consuming
11
+ * app's CSS, targeted through `strokeClassPrefix`. Default hooks are
12
+ * `dq-mark` / `dq-stroke`.
13
+ */
14
+
15
+ export type DigiquantMarkProps = {
16
+ size?: number;
17
+ className?: string;
18
+ /** Accessible name (renders a <title> + role="img"); omitted → decorative. */
19
+ title?: string;
20
+ /**
21
+ * Per-path class hook for stroke-draw animations: each stroke gets
22
+ * `${prefix} ${prefix}-N` (N = 1..4, outer arc last). The dashboard
23
+ * loader keys its draw keyframes off "research-loader-stroke";
24
+ * the reference / landing hover replay uses the default
25
+ * "dq-stroke".
26
+ */
27
+ strokeClassPrefix?: string;
28
+ };
29
+
30
+ export function DigiquantMark({
31
+ size = 22,
32
+ className,
33
+ title,
34
+ strokeClassPrefix = "dq-stroke",
35
+ }: DigiquantMarkProps) {
36
+ const strokeClass = (n: number) => `${strokeClassPrefix} ${strokeClassPrefix}-${n}`;
37
+ return (
38
+ <svg
39
+ width={size}
40
+ height={size}
41
+ viewBox="0 0 48 48"
42
+ fill="none"
43
+ role={title ? "img" : undefined}
44
+ aria-hidden={title ? undefined : "true"}
45
+ focusable="false"
46
+ className={["dq-mark", className].filter(Boolean).join(" ")}
47
+ >
48
+ {title ? <title>{title}</title> : null}
49
+ <path
50
+ className={strokeClass(1)}
51
+ stroke="currentColor"
52
+ strokeWidth="2.2"
53
+ strokeLinecap="round"
54
+ strokeLinejoin="round"
55
+ d="M4.2774,32.5293a11.6485,11.6485,0,0,1,23.2219,1.32h0c0,3.2166.0022,11.6479.0022,11.6479"
56
+ />
57
+ <path
58
+ className={strokeClass(2)}
59
+ stroke="currentColor"
60
+ strokeWidth="2.2"
61
+ strokeLinecap="round"
62
+ strokeLinejoin="round"
63
+ d="M3.3047,29.8574q-.0277-.4816-.0279-.97a16.61,16.61,0,1,1,33.2209,0v0c0,4.5869.0031,16.6095.0031,16.6095"
64
+ />
65
+ <circle
66
+ className={strokeClass(3)}
67
+ stroke="currentColor"
68
+ strokeWidth="2"
69
+ cx="16.5007"
70
+ cy="33.4992"
71
+ r="5.0328"
72
+ />
73
+ <path
74
+ className={strokeClass(4)}
75
+ stroke="currentColor"
76
+ strokeWidth="2.2"
77
+ strokeLinecap="round"
78
+ strokeLinejoin="round"
79
+ d="M45.5,24A21.5,21.5,0,1,0,24,45.5H45.5Z"
80
+ />
81
+ </svg>
82
+ );
83
+ }
84
+
85
+ export type WordmarkProps = {
86
+ /**
87
+ * Suffix after the prefix — "things", "quant". Wears var(--accent), so a
88
+ * livery scope (e.g. `.accent-digiquant`) dresses it automatically.
89
+ */
90
+ suffix: ReactNode;
91
+ /** Lockup prefix, in ink (default "digi"). */
92
+ prefix?: ReactNode;
93
+ className?: string;
94
+ };
95
+
96
+ /**
97
+ * Wordmark — the text lockup grammar shared with the footer colophon:
98
+ * `digi` in ink, the suffix wearing var(--accent). Mono, weight 500.
99
+ * Carries token-backed utilities — consumers need an
100
+ * `@source "<path-to>/packages/ui/src/components/symbols"` line.
101
+ */
102
+ export function Wordmark({ suffix, prefix = "digi", className }: WordmarkProps) {
103
+ return (
104
+ <span
105
+ className={[
106
+ "font-mono text-[1.25rem] font-medium tracking-[-0.01em] text-ink",
107
+ className,
108
+ ]
109
+ .filter(Boolean)
110
+ .join(" ")}
111
+ >
112
+ {prefix}
113
+ <em className="not-italic text-accent">{suffix}</em>
114
+ </span>
115
+ );
116
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Animated brand lockup — `digi` and a cursor that types each module in turn.
3
+ *
4
+ * The identity as a live terminal line: `digi` holds, the cursor blinks, then a
5
+ * module suffix types in character by character, holds long enough to read,
6
+ * deletes, and the next one follows. Pure CSS, no JavaScript and no state — so it
7
+ * renders identically on the server and degrades correctly.
8
+ *
9
+ * Suffixes default to the real module registry (`data/modules`) with the shared
10
+ * `digi` prefix stripped, so the animation cannot drift out of sync with the
11
+ * stack the way a hardcoded list would.
12
+ *
13
+ * Two implementation details that are load-bearing:
14
+ *
15
+ * - **Keyframe offsets must be unique and strictly increasing, and every segment
16
+ * needs its own `animation-timing-function`.** Browsers merge duplicate-offset
17
+ * keyframes and silently drop the timing function — which turns the
18
+ * character-by-character type-in into a smooth width slide with no error
19
+ * anywhere. That is why the offsets are computed rather than hand-written.
20
+ * - **Typing is priced per character, not per slot.** A fixed slot makes a
21
+ * 3-letter module type at 2x the speed of a 6-letter one and the rhythm limps.
22
+ * Constant cadence means unequal slots, so the shared cycle is built from
23
+ * cumulative times.
24
+ *
25
+ * The keyframes are emitted as a scoped `<style>` rather than shipped in a family
26
+ * sheet because they are derived from the word list: a static sheet would have to
27
+ * hardcode a module count, and would go stale the moment the registry changes.
28
+ *
29
+ * `prefers-reduced-motion` renders the final state — `digi` + the first module,
30
+ * cursor still — per the canon's one-motion-moment rule.
31
+ */
32
+ "use client";
33
+
34
+ import { useId } from "react";
35
+ import { modules } from "../../data/modules";
36
+
37
+ /** Module suffixes, `digi` stripped: ["graph", "quant", …]. */
38
+ export const MODULE_SUFFIXES: string[] = modules
39
+ .map((m) => m.id)
40
+ .filter((id) => id.startsWith("digi"))
41
+ .map((id) => id.slice(4));
42
+
43
+ /** Seconds. Typing is per character so every module types at one cadence. */
44
+ const TYPE_S = 0.14;
45
+ const ERASE_S = 0.09;
46
+ const HOLD_S = 2.4;
47
+ const REST_S = 1.2;
48
+
49
+ type Phase = { word: string; type: number; hold: number; erase: number; rest: number };
50
+
51
+ function plan(words: string[]): { phases: Phase[]; total: number } {
52
+ let t = 0;
53
+ const phases: Phase[] = [];
54
+ for (const word of words) {
55
+ const n = word.length;
56
+ const type = t;
57
+ const hold = type + n * TYPE_S;
58
+ const erase = hold + HOLD_S;
59
+ const rest = erase + n * ERASE_S;
60
+ phases.push({ word, type, hold, erase, rest });
61
+ t = rest + REST_S;
62
+ }
63
+ return { phases, total: t };
64
+ }
65
+
66
+ function keyframes(scope: string, words: string[]): string {
67
+ const { phases, total } = plan(words);
68
+ const pct = (s: number) => ((s / total) * 100).toFixed(4);
69
+ return phases
70
+ .map(({ word, type, hold, erase, rest }, i) => {
71
+ const n = word.length;
72
+ const rows: string[] = [];
73
+ if (type > 0) {
74
+ rows.push(`0%{width:0;animation-timing-function:linear}`);
75
+ rows.push(`${pct(type)}%{width:0;animation-timing-function:steps(${n},end)}`);
76
+ } else {
77
+ rows.push(`0%{width:0;animation-timing-function:steps(${n},end)}`);
78
+ }
79
+ rows.push(`${pct(hold)}%{width:${n}ch;animation-timing-function:linear}`);
80
+ rows.push(`${pct(erase)}%{width:${n}ch;animation-timing-function:steps(${n},end)}`);
81
+ rows.push(`${pct(rest)}%{width:0;animation-timing-function:linear}`);
82
+ if (rest < total) rows.push(`100%{width:0}`);
83
+ return `@keyframes ${scope}-t${i}{${rows.join("")}}`;
84
+ })
85
+ .join("");
86
+ }
87
+
88
+ export interface AnimatedLockupProps {
89
+ /** Suffixes to cycle. Defaults to the module registry. */
90
+ suffixes?: string[];
91
+ /** Text before the typed suffix. */
92
+ prefix?: string;
93
+ className?: string;
94
+ /** Accessible name. The typed suffixes are decorative — one name for the whole. */
95
+ title?: string;
96
+ }
97
+
98
+ export function AnimatedLockup({
99
+ suffixes = MODULE_SUFFIXES,
100
+ prefix = "digi",
101
+ className,
102
+ title = "digithings",
103
+ }: AnimatedLockupProps) {
104
+ // useId keeps the keyframe names unique if the lockup is ever mounted twice.
105
+ const scope = `dtl${useId().replace(/[^a-zA-Z0-9]/g, "")}`;
106
+ const { total } = plan(suffixes);
107
+ const css = [
108
+ `.${scope}{font-family:var(--font-mono);font-weight:400;line-height:1.05;`,
109
+ `letter-spacing:0;white-space:nowrap;display:inline-block;user-select:none}`,
110
+ `.${scope} i{font-style:normal;display:inline-block;overflow:hidden;width:0;`,
111
+ `vertical-align:bottom;animation-duration:${total.toFixed(2)}s;`,
112
+ `animation-iteration-count:infinite;animation-fill-mode:both}`,
113
+ suffixes.map((_, i) => `.${scope} i:nth-child(${i + 1}){animation-name:${scope}-t${i}}`).join(""),
114
+ `.${scope} u{display:inline-block;width:.6em;height:.71em;background:currentColor;`,
115
+ `text-decoration:none;animation:${scope}-blink 1.05s steps(1) infinite}`,
116
+ `@keyframes ${scope}-blink{50%{opacity:0}}`,
117
+ keyframes(scope, suffixes),
118
+ `@media (prefers-reduced-motion:reduce){`,
119
+ `.${scope} u{animation:none}`,
120
+ `.${scope} i{animation:none;width:0}`,
121
+ `.${scope} i:nth-child(1){width:${suffixes[0]?.length ?? 0}ch}}`,
122
+ ].join("");
123
+
124
+ return (
125
+ <span className={[scope, className].filter(Boolean).join(" ")} role="img" aria-label={title}>
126
+ <style>{css}</style>
127
+ {prefix}
128
+ <span aria-hidden="true">
129
+ {suffixes.map((w) => (
130
+ <i key={w}>{w}</i>
131
+ ))}
132
+ </span>
133
+ <u aria-hidden="true" />
134
+ </span>
135
+ );
136
+ }