@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,340 @@
1
+ "use client";
2
+
3
+ import { renderMermaidSVG } from "beautiful-mermaid";
4
+ import { Maximize2, Minus, Plus, RotateCcw, X } from "lucide-react";
5
+ import {
6
+ type FC,
7
+ type ReactNode,
8
+ memo,
9
+ useCallback,
10
+ useEffect,
11
+ useMemo,
12
+ useRef,
13
+ useState,
14
+ } from "react";
15
+ import { createPortal } from "react-dom";
16
+ import { sanitizeMermaidSvg } from "../../chat/sanitize-mermaid-svg";
17
+ import { cn } from "../../chat/gallery-thread/cn";
18
+
19
+ export type MermaidDiagramProps = {
20
+ code: string;
21
+ className?: string;
22
+ /** Renders a skeleton instead of the diagram while `true`. */
23
+ streaming?: boolean;
24
+ };
25
+
26
+ const MIN_SCALE = 0.5;
27
+ const MAX_SCALE = 4;
28
+
29
+ type MermaidZoomProps = {
30
+ svg: string;
31
+ children: ReactNode;
32
+ };
33
+
34
+ function MermaidZoom({ svg, children }: MermaidZoomProps) {
35
+ const [isOpen, setIsOpen] = useState(false);
36
+ const [transform, setTransform] = useState({ x: 0, y: 0, scale: 1 });
37
+ const triggerRef = useRef<HTMLButtonElement>(null);
38
+ const closeRef = useRef<HTMLButtonElement>(null);
39
+ const overlayRef = useRef<HTMLDivElement>(null);
40
+ const viewportRef = useRef<HTMLDivElement>(null);
41
+ const drag = useRef<{
42
+ startX: number;
43
+ startY: number;
44
+ originX: number;
45
+ originY: number;
46
+ } | null>(null);
47
+ const transformRef = useRef(transform);
48
+ transformRef.current = transform;
49
+
50
+ const zoomSvg = useMemo(
51
+ () =>
52
+ sanitizeMermaidSvg(
53
+ svg
54
+ .replace(/id="([^"]+)"/g, 'id="$1-zoom"')
55
+ .replace(/url\(#([^)]+)\)/g, "url(#$1-zoom)")
56
+ .replace(/(href|xlink:href)="#([^"]+)"/g, '$1="#$2-zoom"'),
57
+ ),
58
+ [svg],
59
+ );
60
+
61
+ const handleClose = useCallback(() => {
62
+ setIsOpen(false);
63
+ setTransform({ x: 0, y: 0, scale: 1 });
64
+ triggerRef.current?.focus();
65
+ }, []);
66
+
67
+ useEffect(() => {
68
+ if (!isOpen) return;
69
+ const handleKeyDown = (e: KeyboardEvent) => {
70
+ if (e.key === "Escape") {
71
+ handleClose();
72
+ return;
73
+ }
74
+ if (e.key !== "Tab") return;
75
+ const focusables = overlayRef.current?.querySelectorAll<HTMLElement>(
76
+ 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])',
77
+ );
78
+ const first = focusables?.[0];
79
+ const last = focusables?.[focusables.length - 1];
80
+ if (!first || !last) return;
81
+ if (e.shiftKey && document.activeElement === first) {
82
+ e.preventDefault();
83
+ last.focus();
84
+ } else if (!e.shiftKey && document.activeElement === last) {
85
+ e.preventDefault();
86
+ first.focus();
87
+ }
88
+ };
89
+ document.addEventListener("keydown", handleKeyDown);
90
+ return () => document.removeEventListener("keydown", handleKeyDown);
91
+ }, [isOpen, handleClose]);
92
+
93
+ useEffect(() => {
94
+ if (!isOpen) return;
95
+ const originalOverflow = document.body.style.overflow;
96
+ document.body.style.overflow = "hidden";
97
+ return () => {
98
+ document.body.style.overflow = originalOverflow;
99
+ };
100
+ }, [isOpen]);
101
+
102
+ useEffect(() => {
103
+ if (isOpen) closeRef.current?.focus();
104
+ }, [isOpen]);
105
+
106
+ const zoomBy = useCallback((factor: number, cx?: number, cy?: number) => {
107
+ setTransform((t) => {
108
+ const scale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, t.scale * factor));
109
+ const ratio = scale / t.scale;
110
+ if (cx === undefined || cy === undefined) {
111
+ const viewport = viewportRef.current;
112
+ cx = (viewport?.clientWidth ?? 0) / 2;
113
+ cy = (viewport?.clientHeight ?? 0) / 2;
114
+ }
115
+ return {
116
+ scale,
117
+ x: cx - (cx - t.x) * ratio,
118
+ y: cy - (cy - t.y) * ratio,
119
+ };
120
+ });
121
+ }, []);
122
+
123
+ const onWheel = useCallback(
124
+ (e: React.WheelEvent) => {
125
+ const viewport = viewportRef.current;
126
+ if (!viewport) return;
127
+ const rect = viewport.getBoundingClientRect();
128
+ zoomBy(
129
+ Math.exp(-e.deltaY * 0.0015),
130
+ e.clientX - rect.left,
131
+ e.clientY - rect.top,
132
+ );
133
+ },
134
+ [zoomBy],
135
+ );
136
+
137
+ const onPointerDown = useCallback((e: React.PointerEvent) => {
138
+ e.currentTarget.setPointerCapture(e.pointerId);
139
+ const t = transformRef.current;
140
+ drag.current = {
141
+ startX: e.clientX,
142
+ startY: e.clientY,
143
+ originX: t.x,
144
+ originY: t.y,
145
+ };
146
+ }, []);
147
+
148
+ const onPointerMove = useCallback((e: React.PointerEvent) => {
149
+ const d = drag.current;
150
+ if (!d) return;
151
+ setTransform((t) => ({
152
+ ...t,
153
+ x: d.originX + e.clientX - d.startX,
154
+ y: d.originY + e.clientY - d.startY,
155
+ }));
156
+ }, []);
157
+
158
+ const onPointerUp = useCallback(() => {
159
+ drag.current = null;
160
+ }, []);
161
+
162
+ return (
163
+ <div
164
+ data-slot="mermaid-zoom-wrap"
165
+ className="aui-mermaid-zoom-wrap group/mermaid relative"
166
+ >
167
+ {children}
168
+ <button
169
+ ref={triggerRef}
170
+ type="button"
171
+ data-slot="mermaid-zoom-trigger"
172
+ aria-label="Expand diagram"
173
+ onClick={() => setIsOpen(true)}
174
+ className="aui-mermaid-zoom-trigger text-muted-foreground hover:text-foreground hover:border-muted-foreground/70 border-border bg-background absolute top-2 end-2 cursor-pointer rounded-md border p-1.5 opacity-0 transition group-hover/mermaid:opacity-100 focus-visible:opacity-100"
175
+ >
176
+ <Maximize2 className="size-3.5" />
177
+ </button>
178
+ {isOpen &&
179
+ createPortal(
180
+ <div
181
+ ref={overlayRef}
182
+ data-slot="mermaid-zoom-overlay"
183
+ role="dialog"
184
+ aria-modal="true"
185
+ aria-label="Diagram"
186
+ className="aui-mermaid-zoom-overlay fade-in animate-in bg-background fixed inset-0 z-50 duration-200"
187
+ >
188
+ <div
189
+ ref={viewportRef}
190
+ className="aui-mermaid-zoom-viewport h-full w-full cursor-grab touch-none overflow-hidden active:cursor-grabbing"
191
+ onWheel={onWheel}
192
+ onPointerDown={onPointerDown}
193
+ onPointerMove={onPointerMove}
194
+ onPointerUp={onPointerUp}
195
+ onPointerCancel={onPointerUp}
196
+ >
197
+ <div
198
+ data-slot="mermaid-zoom-content"
199
+ className="aui-mermaid-zoom-content flex h-full w-full items-center justify-center [&_svg]:max-h-[80vh] [&_svg]:max-w-[90vw]"
200
+ style={{
201
+ transform: `translate(${transform.x}px, ${transform.y}px) scale(${transform.scale})`,
202
+ transformOrigin: "0 0",
203
+ }}
204
+ dangerouslySetInnerHTML={{ __html: zoomSvg }}
205
+ />
206
+ </div>
207
+ <div
208
+ data-slot="mermaid-zoom-toolbar"
209
+ className="aui-mermaid-zoom-toolbar border-border bg-background absolute top-4 end-4 flex items-center gap-1 rounded-lg border p-1"
210
+ >
211
+ <button
212
+ type="button"
213
+ aria-label="Zoom in"
214
+ onClick={() => zoomBy(1.25)}
215
+ className="text-muted-foreground hover:text-foreground cursor-pointer rounded-sm p-1.5"
216
+ >
217
+ <Plus className="size-4" />
218
+ </button>
219
+ <button
220
+ type="button"
221
+ aria-label="Zoom out"
222
+ onClick={() => zoomBy(0.8)}
223
+ className="text-muted-foreground hover:text-foreground cursor-pointer rounded-sm p-1.5"
224
+ >
225
+ <Minus className="size-4" />
226
+ </button>
227
+ <button
228
+ type="button"
229
+ aria-label="Reset zoom"
230
+ onClick={() => setTransform({ x: 0, y: 0, scale: 1 })}
231
+ className="text-muted-foreground hover:text-foreground cursor-pointer rounded-sm p-1.5"
232
+ >
233
+ <RotateCcw className="size-4" />
234
+ </button>
235
+ <button
236
+ ref={closeRef}
237
+ type="button"
238
+ aria-label="Close"
239
+ onClick={handleClose}
240
+ className="text-muted-foreground hover:text-foreground cursor-pointer rounded-sm p-1.5"
241
+ >
242
+ <X className="size-4" />
243
+ </button>
244
+ </div>
245
+ </div>,
246
+ document.body,
247
+ )}
248
+ </div>
249
+ );
250
+ }
251
+
252
+ const MermaidDiagramImpl: FC<MermaidDiagramProps> = ({
253
+ code,
254
+ className,
255
+ streaming = false,
256
+ }) => {
257
+ const result = useMemo(() => {
258
+ if (streaming) return null;
259
+ try {
260
+ return {
261
+ svg: sanitizeMermaidSvg(
262
+ renderMermaidSVG(code, {
263
+ bg: "var(--background)",
264
+ fg: "var(--foreground)",
265
+ muted: "var(--muted-foreground)",
266
+ border: "var(--border)",
267
+ accent: "var(--foreground)",
268
+ transparent: true,
269
+ }),
270
+ ),
271
+ error: null,
272
+ };
273
+ } catch (err) {
274
+ return {
275
+ svg: null,
276
+ error: err instanceof Error ? err : new Error(String(err)),
277
+ };
278
+ }
279
+ }, [streaming, code]);
280
+
281
+ if (!result) {
282
+ return (
283
+ <div
284
+ data-slot="mermaid-skeleton"
285
+ aria-label="Rendering diagram"
286
+ className={cn(
287
+ "aui-mermaid-skeleton bg-muted flex h-32 animate-pulse items-center justify-center gap-3 rounded-b-lg p-4",
288
+ className,
289
+ )}
290
+ >
291
+ <div className="bg-muted-foreground/20 h-8 w-20 rounded-md" />
292
+ <div className="bg-muted-foreground/20 h-px w-10" />
293
+ <div className="bg-muted-foreground/20 h-8 w-20 rounded-md" />
294
+ <div className="bg-muted-foreground/20 h-px w-10" />
295
+ <div className="bg-muted-foreground/20 h-8 w-20 rounded-md" />
296
+ </div>
297
+ );
298
+ }
299
+
300
+ if (result.error) {
301
+ return (
302
+ <div
303
+ data-slot="mermaid-fallback"
304
+ className={cn(
305
+ "aui-mermaid-fallback bg-muted/75 rounded-b-lg",
306
+ className,
307
+ )}
308
+ >
309
+ <pre className="overflow-x-auto p-4 text-sm">{code.trim()}</pre>
310
+ <p className="text-muted-foreground border-border border-t px-4 py-1.5 text-xs">
311
+ diagram could not be rendered
312
+ </p>
313
+ </div>
314
+ );
315
+ }
316
+
317
+ return (
318
+ <MermaidZoom svg={result.svg}>
319
+ <div
320
+ data-slot="mermaid-diagram"
321
+ className={cn(
322
+ "aui-mermaid-diagram bg-muted overflow-x-auto rounded-b-lg p-2 [&_svg]:mx-auto [&_svg]:h-auto [&_svg]:max-w-full",
323
+ className,
324
+ )}
325
+ dangerouslySetInnerHTML={{ __html: result.svg }}
326
+ />
327
+ </MermaidZoom>
328
+ );
329
+ };
330
+
331
+ const MermaidDiagram = memo(
332
+ MermaidDiagramImpl,
333
+ ) as unknown as FC<MermaidDiagramProps> & {
334
+ Zoom: typeof MermaidZoom;
335
+ };
336
+
337
+ MermaidDiagram.displayName = "MermaidDiagram";
338
+ MermaidDiagram.Zoom = MermaidZoom;
339
+
340
+ export { MermaidDiagram, MermaidZoom };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The digithings wordmark.
3
+ *
4
+ * Same 4×4 mask the brand studio composes
5
+ * (`packages/brand/studio/wordmark-mask.svg`), drawn in `currentColor`.
6
+ * Each app mirrors that file to `/wordmark-mask.svg` (see
7
+ * `packages/brand/build-studio.py`). Static on purpose: the animated build
8
+ * belongs to the studio, not to a page header.
9
+ */
10
+ export function BrandWordmark({
11
+ className,
12
+ label = "digithings",
13
+ mask = "/wordmark-mask.svg",
14
+ }: {
15
+ className?: string;
16
+ /** Accessible name. The mark is ink, not text. */
17
+ label?: string;
18
+ /**
19
+ * Mask URL. Apps served under a base path pass the prefixed path; the
20
+ * default is the site root.
21
+ */
22
+ mask?: string;
23
+ }) {
24
+ return (
25
+ <span
26
+ className={className ? `brand-wordmark ${className}` : "brand-wordmark"}
27
+ role="img"
28
+ aria-label={label}
29
+ style={{ ["--brand-wordmark" as string]: `url("${mask}")` }}
30
+ />
31
+ );
32
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * The release rail — the changelog's row grammar, OpenCode-informed: a two-column
3
+ * grid per release with the version, date and product pinned in a sticky aside,
4
+ * and the shipped title with its highlights in the wide column under a hairline.
5
+ * Static, so the whole changelog renders without JavaScript (D1, #4429). Generic:
6
+ * rows arrive as data and the caller owns the order, so a site can fold its own
7
+ * release source in without this part knowing where releases come from.
8
+ */
9
+ import type { ReactNode } from "react";
10
+ import { cn } from "../../lib/utils";
11
+
12
+ export interface ReleaseRailItem {
13
+ /** The product or package the release belongs to, e.g. `digichat`. */
14
+ product: string;
15
+ /** The version as published, e.g. `v2.3.1`. */
16
+ version: string;
17
+ /** The release date, ISO `YYYY-MM-DD`. */
18
+ date: string;
19
+ /** The published title. */
20
+ title: string;
21
+ /** The release URL, so the version stays the link. */
22
+ href: string;
23
+ /** The release kind as tagged upstream, e.g. `fix` or `release`. */
24
+ tag?: string;
25
+ /** Optional highlights, one plain clause each. */
26
+ entries?: ReactNode[];
27
+ }
28
+
29
+ export interface ReleaseRailProps {
30
+ items: ReleaseRailItem[];
31
+ className?: string;
32
+ }
33
+
34
+ export function ReleaseRail({ items, className }: ReleaseRailProps) {
35
+ return (
36
+ <ol className={cn("m-0 grid list-none gap-0 p-0", className)}>
37
+ {items.map((item) => (
38
+ <li
39
+ key={`${item.product}-${item.version}`}
40
+ className="grid gap-[0.9rem] border-t border-hair py-[1.6rem] first:border-t-0 first:pt-0 last:pb-0 sm:grid-cols-[180px_1fr] sm:gap-[3rem]"
41
+ >
42
+ <div className="flex flex-col gap-[0.3rem] sm:sticky sm:top-[5rem] sm:self-start">
43
+ <a
44
+ href={item.href}
45
+ className="font-mono text-[0.9rem] font-medium text-ink underline-offset-[3px] hover:underline"
46
+ >
47
+ {item.version}
48
+ </a>
49
+ <span className="font-mono text-[length:var(--type-meta)] tracking-[0.02em] text-ink-mute">
50
+ <time dateTime={item.date}>{item.date}</time>
51
+ {item.tag ? ` · ${item.tag}` : ""}
52
+ </span>
53
+ <span className="font-mono text-[length:var(--type-meta)] tracking-[0.02em] text-ink-mute">
54
+ {item.product}
55
+ </span>
56
+ </div>
57
+ <div className="min-w-0">
58
+ <p className="m-0 text-[length:var(--type-body)] font-medium leading-[1.5] text-ink">
59
+ {item.title}
60
+ </p>
61
+ {item.entries && item.entries.length > 0 ? (
62
+ <ul className="m-0 mt-[0.8rem] grid list-none gap-[0.6rem] p-0">
63
+ {item.entries.map((entry, index) => (
64
+ <li
65
+ key={index}
66
+ className="flex gap-[0.75rem] text-[0.9rem] leading-[1.7] text-ink-soft"
67
+ >
68
+ <span aria-hidden="true" className="shrink-0 font-mono text-ink-mute">
69
+ [*]
70
+ </span>
71
+ <span>{entry}</span>
72
+ </li>
73
+ ))}
74
+ </ul>
75
+ ) : null}
76
+ </div>
77
+ </li>
78
+ ))}
79
+ </ol>
80
+ );
81
+ }
@@ -0,0 +1 @@
1
+ export { ReleaseRail, type ReleaseRailItem, type ReleaseRailProps } from "./ReleaseRail";
@@ -0,0 +1,67 @@
1
+ "use client";
2
+ /**
3
+ * Fenced code + copy affordance for <ChatMarkdown> (#1418): a hairline
4
+ * figure with a microtype caption (language label · copy) over verbatim
5
+ * code. The copy button is the only stateful piece — it flips to `copied`
6
+ * for a beat via navigator.clipboard and silently no-ops where the API is
7
+ * unavailable (e.g. cross-origin iframes — the digichat embed rule).
8
+ * <ChatCopyButton> is exported solo so transcripts can pin the same
9
+ * affordance to whole turns (digichat-ui's .dc-msg-copy / .dc-code-copy
10
+ * rebuild on it). Box surface, caption row, and button mechanics live in
11
+ * styles/chat-core.css.
12
+ */
13
+ import { useState } from "react";
14
+
15
+ export type ChatCopyButtonProps = {
16
+ /** Payload written to the clipboard. */
17
+ text: string;
18
+ ariaLabel?: string;
19
+ className?: string;
20
+ };
21
+
22
+ export function ChatCopyButton({ text, ariaLabel = "Copy", className }: ChatCopyButtonProps) {
23
+ const [copied, setCopied] = useState(false);
24
+ const cls = ["chat-md-copy", copied ? "is-copied" : "", className ?? ""]
25
+ .filter(Boolean)
26
+ .join(" ");
27
+ return (
28
+ <button
29
+ type="button"
30
+ className={cls}
31
+ aria-label={ariaLabel}
32
+ onClick={() =>
33
+ navigator.clipboard?.writeText(text).then(
34
+ () => {
35
+ setCopied(true);
36
+ window.setTimeout(() => setCopied(false), 1200);
37
+ },
38
+ () => {},
39
+ )
40
+ }
41
+ >
42
+ {copied ? "copied" : "copy"}
43
+ </button>
44
+ );
45
+ }
46
+
47
+ export type ChatCodeBlockProps = {
48
+ /** Raw source — rendered verbatim and used as the copy payload. */
49
+ code: string;
50
+ /** Caption language label, e.g. `python`. */
51
+ lang?: string;
52
+ className?: string;
53
+ };
54
+
55
+ export function ChatCodeBlock({ code, lang, className }: ChatCodeBlockProps) {
56
+ return (
57
+ <figure className={["chat-md-code", className ?? ""].filter(Boolean).join(" ")}>
58
+ <figcaption>
59
+ <span>{lang}</span>
60
+ <ChatCopyButton text={code} ariaLabel="Copy code" />
61
+ </figcaption>
62
+ <pre>
63
+ <code>{code}</code>
64
+ </pre>
65
+ </figure>
66
+ );
67
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Chat markdown — the rich-text frame for an assistant turn (#1418):
3
+ * headings, emphasis, lists, quotes, links, tables, math and diagrams styled
4
+ * on the token palette so rendered markdown reads as one surface with the
5
+ * transcript. Renders a `.chat-md` scope whose ELEMENT combinators (with their
6
+ * color-mix washes and ::marker accents) live in styles/chat-core.css —
7
+ * markdown renderers need no per-node classes; bare tags pick up the
8
+ * grammar, which is exactly how digichat-ui's .dc-md-* map onto it. Fenced
9
+ * code with the copy affordance is <ChatCodeBlock>; a bare <pre> still
10
+ * reads as the same hairline box.
11
+ *
12
+ * Two ways in:
13
+ *
14
+ * <ChatMarkdown>{nodes}</ChatMarkdown> frame only — static, server-safe
15
+ * <ChatMarkdown source={md} /> frame + renderer
16
+ *
17
+ * `source` runs the shared pipeline in <ChatMarkdownSource>: GFM, `$inline$` /
18
+ * `$$block$$` math via remark-math + rehype-katex (rendered at parse time, so
19
+ * it survives with JS off), and ```mermaid fences via <ChatMermaidBlock>
20
+ * (client-only, lazily imported, source shown until it draws). Both may be
21
+ * combined — `source` renders first, then children, which is how a streaming
22
+ * turn pins <ChatStreamCursor> to the end of the text.
23
+ *
24
+ * The children-only path stays directive-free and server-safe.
25
+ *
26
+ * It does NOT, however, avoid the cost: `ChatMarkdownSource` is a **static**
27
+ * import below, so react-markdown, remark-gfm, remark-math, rehype-katex (→ katex,
28
+ * 266 KB raw / 76 KB gzipped) and <ChatMermaidBlock> are in the module graph of
29
+ * every consumer, including one that only ever passes children. An earlier version
30
+ * of this comment claimed "passing `source` pulls in the client renderer", implying
31
+ * the children path escaped it; that was wrong. Only `mermaid` itself (~2.9 MB) is
32
+ * genuinely deferred, via `await import("mermaid")` inside the effect.
33
+ *
34
+ * Making the whole renderer dynamic would be the real fix and is not done here: it
35
+ * would turn every `source` render into a suspense boundary and change the
36
+ * streaming behaviour, which needs its own change with its own review.
37
+ */
38
+ import type { ReactNode } from "react";
39
+
40
+ import { ChatMarkdownSource, type CodeBlockOverride } from "./ChatMarkdownSource";
41
+
42
+ export type { CodeBlockOverride } from "./ChatMarkdownSource";
43
+
44
+ export type ChatMarkdownProps = {
45
+ children?: ReactNode;
46
+ /** Markdown source — GFM + `$math$` + ```mermaid, rendered by the shared pipeline. */
47
+ source?: string;
48
+ className?: string;
49
+ /** Per-fence-language block-code hook — see {@link CodeBlockOverride}. Only
50
+ * consulted when `source` is passed; the children-only frame has nothing to
51
+ * parse. Optional, and additive: omit it and every fence renders exactly as
52
+ * before (mermaid diagram, or the copy-caption code block). */
53
+ renderCodeBlock?: CodeBlockOverride;
54
+ };
55
+
56
+ export function ChatMarkdown({ children, source, className, renderCodeBlock }: ChatMarkdownProps) {
57
+ const cls = ["chat-md min-w-0 text-[0.88rem] leading-[1.6]", className ?? ""]
58
+ .filter(Boolean)
59
+ .join(" ");
60
+ return (
61
+ <div className={cls}>
62
+ {source ? <ChatMarkdownSource source={source} renderCodeBlock={renderCodeBlock} /> : null}
63
+ {children}
64
+ </div>
65
+ );
66
+ }