@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,203 @@
1
+ "use client";
2
+ /**
3
+ * The parse half of <ChatMarkdown>: a markdown *string* → the `.chat-md`
4
+ * element grammar. <ChatMarkdown> stays a directive-free, server-safe frame
5
+ * around pre-built children; this file is where the renderer lives, split out
6
+ * purely for the RSC boundary — react-markdown imports React hooks, so it
7
+ * cannot be pulled into a server component.
8
+ *
9
+ * Pipeline (remark → rehype → React, no raw HTML anywhere — React escapes text
10
+ * nodes and no rehype-raw is in the chain):
11
+ * remark-gfm tables, task lists, strikethrough, autolinks
12
+ * remark-math `$inline$` and `$$block$$` → math nodes
13
+ * rehype-katex math nodes → KaTeX HTML + MathML, *at parse time*
14
+ *
15
+ * Math is deliberately NOT a client-side pass: rehype-katex runs inside the
16
+ * unified pipeline, so the server markup already contains laid-out KaTeX and
17
+ * math reads correctly with JS disabled. Only mermaid — which needs a live DOM
18
+ * to measure text — defers to the client, and <ChatMermaidBlock> ships the
19
+ * diagram source as its own fallback for that window.
20
+ *
21
+ * Emits bare tags wherever chat-core.css already has a combinator, and reaches
22
+ * for a component only where an affordance is needed: fenced code gets
23
+ * <ChatCodeBlock>'s copy caption, a ```mermaid fence gets <ChatMermaidBlock>.
24
+ */
25
+ import { isValidElement, type ReactNode } from "react";
26
+ import type { Components, Options } from "react-markdown";
27
+ import ReactMarkdown from "react-markdown";
28
+ import rehypeKatex from "rehype-katex";
29
+ import remarkGfm from "remark-gfm";
30
+ import remarkMath from "remark-math";
31
+
32
+ import { ChatCodeBlock } from "./ChatCodeBlock";
33
+ import { ChatMermaidBlock } from "./ChatMermaidBlock";
34
+
35
+ /** Minimal shape of the mdast nodes remarkDisplayDollars touches. */
36
+ type MathNode = {
37
+ type: string;
38
+ children?: MathNode[];
39
+ position?: { start?: { offset?: number } };
40
+ data?: { hProperties?: { className?: string[] } };
41
+ };
42
+
43
+ /**
44
+ * remark-math's *flow* (display) construct needs the `$$` fences on lines of
45
+ * their own; `$$x$$` written on a single line therefore comes out as an
46
+ * `inlineMath` node and renders squeezed inline. Models write display math on
47
+ * one line constantly, so promote the double-dollar case: rehype-katex keys
48
+ * displayMode off the `math-display` class alone, so swapping the class is the
49
+ * whole fix. Single-dollar `$x$` is untouched and stays inline.
50
+ */
51
+ function remarkDisplayDollars() {
52
+ return (tree: unknown, file: { value?: unknown }) => {
53
+ const src = String(file.value ?? "");
54
+ const walk = (node: MathNode): void => {
55
+ if (node.type === "inlineMath" && node.data?.hProperties) {
56
+ const at = node.position?.start?.offset;
57
+ if (typeof at === "number" && src.startsWith("$$", at)) {
58
+ node.data.hProperties.className = ["language-math", "math-display"];
59
+ }
60
+ }
61
+ for (const child of node.children ?? []) walk(child);
62
+ };
63
+ walk(tree as MathNode);
64
+ };
65
+ }
66
+
67
+ /**
68
+ * `singleDollarTextMath: false` is load-bearing, not a preference. remark-math
69
+ * defaults it to true, which makes any paragraph containing two `$` signs parse
70
+ * as inline math: "the starter plan is $29 and the team plan is $99" re-sets the
71
+ * prose between them as italic math with the spaces collapsed, and eats the
72
+ * second `$`. Currency is far commoner than inline math in a docs answer — and
73
+ * this renders for a quant-finance stack — so `$…$` stays literal.
74
+ *
75
+ * The cost, stated plainly: there is then NO inline math syntax. remark-math
76
+ * understands only `$`/`$$`, not `\(…\)`, so turning single-dollar off leaves
77
+ * block math (`$$…$$`, plus fenced math promoted by remarkDisplayDollars below)
78
+ * as the only route. That is the right trade for a docs chat — a mangled price
79
+ * is a wrong answer, a formula that needs its own line is a formatting nit — but
80
+ * it is a trade, not a free win. Revisit if inline notation ever outweighs
81
+ * currency on these surfaces.
82
+ */
83
+ const remarkPlugins: Options["remarkPlugins"] = [
84
+ remarkGfm,
85
+ [remarkMath, { singleDollarTextMath: false }],
86
+ remarkDisplayDollars,
87
+ ];
88
+
89
+ /**
90
+ * KaTeX options. `strict: "ignore"` keeps half-typed model output from filling
91
+ * the console with parse complaints. rehype-katex forces `throwOnError` itself
92
+ * (first strict, then a lenient retry, then a `.katex-error` span), and that
93
+ * last-resort span is inline-styled from `errorColor` — so it is handed a
94
+ * var() and the failed expression reads on the token palette in both themes
95
+ * instead of KaTeX's hard-coded red.
96
+ */
97
+ const rehypePlugins: Options["rehypePlugins"] = [
98
+ // maxSize bounds \rule and friends. KaTeX names maxExpand and maxSize as the two
99
+ // knobs for untrusted input; maxExpand defaults to 1000 and holds, but maxSize
100
+ // defaults to Infinity, so `$$\rule{99999em}{99999em}$$` in a model answer
101
+ // inserts a ~1.6-million-pixel box and blows out the page scroll — inline math
102
+ // has no overflow container at all.
103
+ [rehypeKatex, { strict: "ignore", errorColor: "var(--down)", maxSize: 10 }],
104
+ ];
105
+
106
+ /** Only http(s) links survive; anything else (javascript:, data:) renders as text. */
107
+ function safeHref(href: string | undefined): string | undefined {
108
+ return href && (href.startsWith("https://") || href.startsWith("http://")) ? href : undefined;
109
+ }
110
+
111
+ /** Per-fence-language block-code hook. Called with the fence's language tag
112
+ * (lowercased, no `language-` prefix — empty string for a bare fence) and its
113
+ * raw code. Return a node to render instead of the default fenced-code/mermaid
114
+ * handling below, or `undefined` to fall through to it. Not consulted for
115
+ * inline code (a single backtick span) — only ever for a block fence, since
116
+ * that is the only shape a caller has needed an override for so far. */
117
+ export type CodeBlockOverride = (lang: string, code: string) => ReactNode | undefined;
118
+
119
+ function buildComponents(renderCodeBlock?: CodeBlockOverride): Components {
120
+ return {
121
+ a: ({ href, children }) => {
122
+ const safe = safeHref(href);
123
+ if (!safe) return <span>{children}</span>;
124
+ return (
125
+ <a href={safe} target="_blank" rel="noopener noreferrer">
126
+ {children}
127
+ </a>
128
+ );
129
+ },
130
+ // Headings are downshifted two levels: a model that opens an answer with `#`
131
+ // must not take the page's <h1>. Neither digichat /embed nor digithings.ai/chat
132
+ // has an <h1> of its own on the transcript route, so an un-downshifted h1 from
133
+ // streamed text becomes the document's top-level heading — a real outline and
134
+ // screen-reader defect, not a cosmetic one. The renderer this replaced
135
+ // (digichat-ui's MiniMarkdown) mapped h1→h3, h2→h4, h3→h5, h4→h6; that
136
+ // behaviour is preserved here rather than dropped.
137
+ h1: ({ children }) => <h3>{children}</h3>,
138
+ h2: ({ children }) => <h4>{children}</h4>,
139
+ h3: ({ children }) => <h5>{children}</h5>,
140
+ h4: ({ children }) => <h6>{children}</h6>,
141
+ // Tables scroll inside their own box rather than widening the turn.
142
+ table: ({ children }) => (
143
+ <div className="chat-md-table">
144
+ <table>{children}</table>
145
+ </div>
146
+ ),
147
+ // <ChatCodeBlock>/<ChatMermaidBlock> are <figure>s that bring their own
148
+ // <pre>, so the fence's wrapper is dropped and `code` decides the shape.
149
+ // Block code is decided HERE, at the <pre>, because that node is the only one
150
+ // that actually says "this is a block". Guessing from the child string instead
151
+ // — react-markdown >=9 dropped the `inline` prop — misread the commonest shape
152
+ // of model output: a bare one-line fence holding a single shell command has no
153
+ // language and no newline, so it rendered as inline <code>, losing its block
154
+ // styling and its copy button. Indented blocks lost their leading whitespace
155
+ // for the same reason.
156
+ pre: ({ children }) => {
157
+ const child = Array.isArray(children) ? children[0] : children;
158
+ if (isValidElement<{ className?: string; children?: ReactNode }>(child)) {
159
+ const raw = String(child.props.children ?? "").replace(/\n$/, "");
160
+ const lang = child.props.className?.replace(/^language-/, "").toLowerCase() ?? "";
161
+ if (renderCodeBlock) {
162
+ const overridden = renderCodeBlock(lang, raw);
163
+ if (overridden !== undefined) return overridden;
164
+ }
165
+ if (lang === "mermaid") return <ChatMermaidBlock code={raw} />;
166
+ return <ChatCodeBlock code={raw} lang={lang || undefined} />;
167
+ }
168
+ return <>{children}</>;
169
+ },
170
+ // Reached for inline code only; the `pre` branch above consumes block code
171
+ // without rendering its child. renderCodeBlock is deliberately NOT consulted
172
+ // here — every caller that has needed it so far (a fenced ```json chart
173
+ // envelope) only ever appears as a block, and applying it to inline spans
174
+ // too would risk misfiring on a short inline snippet that happens to share a
175
+ // language tag.
176
+ code: ({ className, children }) => {
177
+ const raw = String(children).replace(/\n$/, "");
178
+ const lang = className?.replace(/^language-/, "").toLowerCase() ?? "";
179
+ if (lang === "mermaid") return <ChatMermaidBlock code={raw} />;
180
+ if (lang) return <ChatCodeBlock code={raw} lang={lang} />;
181
+ return <code>{raw}</code>;
182
+ },
183
+ };
184
+ }
185
+
186
+ export type ChatMarkdownSourceProps = {
187
+ /** Markdown source. */
188
+ source: string;
189
+ /** See {@link CodeBlockOverride}. */
190
+ renderCodeBlock?: CodeBlockOverride;
191
+ };
192
+
193
+ export function ChatMarkdownSource({ source, renderCodeBlock }: ChatMarkdownSourceProps) {
194
+ return (
195
+ <ReactMarkdown
196
+ remarkPlugins={remarkPlugins}
197
+ rehypePlugins={rehypePlugins}
198
+ components={buildComponents(renderCodeBlock)}
199
+ >
200
+ {source}
201
+ </ReactMarkdown>
202
+ );
203
+ }
@@ -0,0 +1,202 @@
1
+ "use client";
2
+ /**
3
+ * Mermaid diagram block for <ChatMarkdown> — a ```mermaid fence in streamed
4
+ * model output, drawn as an SVG in the token palette. Promoted up from
5
+ * digichat-ui's MermaidBlock so both chat surfaces draw the same diagram;
6
+ * this is the canon copy.
7
+ *
8
+ * Four things it owes the canon:
9
+ *
10
+ * 1. TOKENS, NEVER MERMAID'S PALETTE. Every built-in mermaid theme hard-codes
11
+ * its own hues, so we run `theme: "base"` — the one palette that lets every
12
+ * themeVariable through — and fill it from the *computed* value of
13
+ * --ink / --surface / --hair / --accent read off this figure. Reading off
14
+ * the figure rather than :root means a livery scope (`.accent-digichat`)
15
+ * or a nested [data-theme] resolves exactly the colours the surrounding
16
+ * turn resolves.
17
+ * 2. BOTH THEMES, LIVE. A MutationObserver on <html data-theme> re-reads the
18
+ * tokens and re-renders, so flipping the theme toggle repaints the diagram
19
+ * instead of leaving a dark graph on a paper surface.
20
+ * 3. NO JS, NO PROBLEM. The source ships inside a <pre> in the server markup
21
+ * and is only replaced once mermaid has actually drawn something. A reader
22
+ * without JS (or before hydration) sees the diagram source, never an empty
23
+ * box; a parse failure keeps that same <pre> and never throws into the
24
+ * transcript.
25
+ * 4. LAZY. mermaid is ~2.9 MB raw (parser + d3 + dagre + cytoscape), so it is
26
+ * imported *inside* the effect — `await import("mermaid")` — and lands in
27
+ * its own async chunk that no page pays for until a diagram appears.
28
+ *
29
+ * Safety: the source is attacker-influenceable model text, so it runs under
30
+ * `securityLevel: "strict"` — mermaid disables HTML labels, drops click
31
+ * handlers/`script` directives, and passes its own output through the DOMPurify
32
+ * copy it bundles before returning it. `suppressErrorRendering` additionally
33
+ * stops a malformed graph injecting mermaid's error-bomb SVG into the
34
+ * transcript. `mermaid.render()` therefore hands back an already-sanitized SVG
35
+ * *string*, which is why this is the one justified innerHTML on the chat
36
+ * surfaces (`htmlLabels: false` is set again below so a config merge cannot
37
+ * quietly re-enable raw label HTML). Motion lives in styles/chat-core.css
38
+ * behind a prefers-reduced-motion guard.
39
+ */
40
+ import { useEffect, useId, useRef, useState } from "react";
41
+
42
+ import { tokenThemeVariables } from "../diagrams/mermaid-theme";
43
+ import { ChatCopyButton } from "./ChatCodeBlock";
44
+
45
+ /**
46
+ * Quotes unquoted `[...]` flowchart node labels that contain characters the
47
+ * classic flowchart grammar cannot parse unquoted — `(`, `)`, `{`, `}` — so a
48
+ * model-written label like `C[List locations (Exchange, OneDrive, Blob)]`
49
+ * (a real observed failure: `Parse error ... got 'PS'` on the `(`) renders
50
+ * instead of falling back to source. Mermaid's own fix for this is quoting
51
+ * the label (`C["List locations (Exchange, OneDrive, Blob)"]`); this applies
52
+ * that mechanically rather than relying on the model to always remember to.
53
+ *
54
+ * Deliberately narrow: only touches `ID[label]` shapes (the commonest node
55
+ * form in model output), only when the label is not already quoted, and only
56
+ * when it contains one of the four unparseable characters — anything else
57
+ * (round `(...)`, diamond `{...}`, decorated shapes) is left alone rather than
58
+ * guessed at. The `[^\]"]*` capture excludes any label already containing a
59
+ * literal `"` from the match entirely — such a label is already ambiguous
60
+ * (unquoted-but-quoted), so this leaves it for mermaid's own parser to reject
61
+ * rather than guessing at an escape.
62
+ *
63
+ * A label starting with `[`, `(`, `/`, or `\` right after the node's opening
64
+ * `[` is bailed out on rather than sanitized: that is the four OTHER `[...]`
65
+ * bracket shapes mermaid overloads the same opening character for — subroutine
66
+ * `id[[text]]`, cylinder `id[(text)]`, parallelogram `id[/text/]`, trapezoid
67
+ * `id[\text\]`. The regex only looks for the FIRST `]`, so on a subroutine it
68
+ * stops at the shape's own inner `]]` instead of the outer one, and on a
69
+ * cylinder/parallelogram/trapezoid it captures the whole shape delimiter as
70
+ * part of the "label" and silently downgrades the node to a plain rectangle
71
+ * when it quotes it. Left untouched, these still fail the same way they did
72
+ * before this function existed (a genuinely paren-bearing cylinder label was
73
+ * already unparseable) — worse than not fixing it would be corrupting a shape
74
+ * that was rendering fine.
75
+ *
76
+ * Applied only to the copy handed to `mermaid.parse`/`render` — the `code`
77
+ * prop keeps the model's exact text for the copy button and the source
78
+ * fallback, so a reader never sees text the model didn't write.
79
+ */
80
+ export function sanitizeMermaidLabels(code: string): string {
81
+ return code.replace(/(\b[\w-]+)\[([^\]"]*)\]/g, (whole, id: string, label: string) => {
82
+ if (/^[[(/\\]/.test(label)) return whole;
83
+ if (!/[(){}]/.test(label)) return whole;
84
+ return `${id}["${label}"]`;
85
+ });
86
+ }
87
+
88
+ /**
89
+ * The palette table moved to `../diagrams/mermaid-theme` when the
90
+ * architecture diagram became a second mermaid surface — one table, both
91
+ * diagrams, so a token added for one is a token added for both.
92
+ */
93
+
94
+ export type ChatMermaidBlockProps = {
95
+ /** Diagram source — rendered verbatim as the no-JS / parse-failure fallback. */
96
+ code: string;
97
+ /** Caption language label. */
98
+ label?: string;
99
+ className?: string;
100
+ };
101
+
102
+ export function ChatMermaidBlock({ code, label = "mermaid", className }: ChatMermaidBlockProps) {
103
+ const [svg, setSvg] = useState("");
104
+ const [failed, setFailed] = useState(false);
105
+ const [showSource, setShowSource] = useState(false);
106
+ // Bumped by the [data-theme] observer to re-run the render effect.
107
+ const [themeTick, setThemeTick] = useState(0);
108
+ const hostRef = useRef<HTMLElement>(null);
109
+ // React 19's useId is «r0»-shaped; mermaid uses this verbatim as an SVG id
110
+ // AND inside generated CSS selectors, so strip everything non-alphanumeric.
111
+ const id = `chat-mermaid-${useId().replace(/[^a-zA-Z0-9]/g, "")}`;
112
+
113
+ useEffect(() => {
114
+ const observer = new MutationObserver(() => setThemeTick((n) => n + 1));
115
+ observer.observe(document.documentElement, {
116
+ attributes: true,
117
+ attributeFilter: ["data-theme"],
118
+ });
119
+ return () => observer.disconnect();
120
+ }, []);
121
+
122
+ useEffect(() => {
123
+ const host = hostRef.current;
124
+ if (!host) return;
125
+ let cancelled = false;
126
+
127
+ void (async () => {
128
+ try {
129
+ const mermaid = (await import("mermaid")).default;
130
+ mermaid.initialize({
131
+ startOnLoad: false,
132
+ securityLevel: "strict",
133
+ suppressErrorRendering: true,
134
+ theme: "base",
135
+ themeVariables: tokenThemeVariables(host),
136
+ flowchart: { htmlLabels: false, useMaxWidth: true },
137
+ sequence: { useMaxWidth: true },
138
+ });
139
+ // Validate first: parse throws on a malformed graph without touching
140
+ // the document, so the failure path never leaves stray nodes behind.
141
+ const sanitized = sanitizeMermaidLabels(code);
142
+ await mermaid.parse(sanitized);
143
+ const rendered = await mermaid.render(id, sanitized);
144
+ if (cancelled) return;
145
+ setSvg(rendered.svg);
146
+ setFailed(false);
147
+ } catch {
148
+ if (cancelled) return;
149
+ setSvg("");
150
+ setFailed(true);
151
+ }
152
+ })();
153
+
154
+ return () => {
155
+ cancelled = true;
156
+ };
157
+ }, [code, id, themeTick]);
158
+
159
+ const drawn = Boolean(svg) && !failed;
160
+ const cls = ["chat-md-mermaid", className ?? ""].filter(Boolean).join(" ");
161
+
162
+ return (
163
+ <figure
164
+ ref={hostRef}
165
+ className={cls}
166
+ data-state={failed ? "source" : drawn ? "diagram" : "pending"}
167
+ >
168
+ <figcaption>
169
+ <span>{failed ? `${label} · source` : label}</span>
170
+ <span className="chat-md-mermaid-actions">
171
+ {drawn ? (
172
+ <button
173
+ type="button"
174
+ className="chat-md-copy"
175
+ aria-expanded={showSource}
176
+ aria-controls={`${id}-source`}
177
+ onClick={() => setShowSource((v) => !v)}
178
+ >
179
+ {showSource ? "hide source" : "view source"}
180
+ </button>
181
+ ) : null}
182
+ <ChatCopyButton text={code} ariaLabel="Copy diagram source" />
183
+ </span>
184
+ </figcaption>
185
+ {drawn ? (
186
+ <div
187
+ className="chat-md-mermaid-fig"
188
+ role="img"
189
+ aria-label={`${label} diagram`}
190
+ // mermaid.render returns a sanitized SVG string under
191
+ // securityLevel: "strict" — see the module docblock.
192
+ dangerouslySetInnerHTML={{ __html: svg }}
193
+ />
194
+ ) : null}
195
+ {!drawn || showSource ? (
196
+ <pre id={`${id}-source`}>
197
+ <code>{code}</code>
198
+ </pre>
199
+ ) : null}
200
+ </figure>
201
+ );
202
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Chat message — one turn of the digichat grammar (canon §16): a marker
3
+ * column and a body on a `1.25rem minmax(0,1fr)` grid — the digichat
4
+ * .dc-term-row geometry, and the convergence target for digichat-ui's
5
+ * .dc-msg (marker ⇢ .dc-who, body ⇢ .dc-body). `>` marks the user, `▸` the
6
+ * assistant, `·` system asides; markers carry the accent and stay out of
7
+ * the a11y tree. `tone` picks the body ink explicitly (user turns default
8
+ * to full ink, the rest to soft; an emphasized answer passes tone="ink") so
9
+ * consumers never fight utility order. `streaming` appends the blink caret.
10
+ * The row is position:relative (chat-core.css) so hover affordances — a
11
+ * per-turn <ChatCopyButton>, digichat-ui's .dc-msg-copy — can pin to it.
12
+ */
13
+ import type { ReactNode } from "react";
14
+ import { ChatStreamCursor } from "./ChatStreamCursor";
15
+
16
+ export type ChatRole = "user" | "assistant" | "system";
17
+
18
+ export type ChatTone = "ink" | "soft" | "mute";
19
+
20
+ const MARKERS: Record<ChatRole, string> = { user: ">", assistant: "▸", system: "·" };
21
+
22
+ const TONES: Record<ChatTone, string> = {
23
+ ink: "text-ink",
24
+ soft: "text-ink-soft",
25
+ mute: "text-ink-mute",
26
+ };
27
+
28
+ export type ChatMessageProps = {
29
+ role: ChatRole;
30
+ /** Marker glyph override (digichat-ui marks assistant turns `·`). */
31
+ marker?: ReactNode;
32
+ /** Body ink; defaults to `ink` for user turns, `soft` otherwise. */
33
+ tone?: ChatTone;
34
+ /** Append the blinking stream caret after the body. */
35
+ streaming?: boolean;
36
+ children?: ReactNode;
37
+ className?: string;
38
+ bodyClassName?: string;
39
+ };
40
+
41
+ export function ChatMessage({
42
+ role,
43
+ marker,
44
+ tone,
45
+ streaming,
46
+ children,
47
+ className,
48
+ bodyClassName,
49
+ }: ChatMessageProps) {
50
+ const bodyTone = TONES[tone ?? (role === "user" ? "ink" : "soft")];
51
+ const cls = [
52
+ `chat-msg chat-msg--${role}`,
53
+ "grid grid-cols-[1.25rem_minmax(0,1fr)] items-baseline gap-[0.55rem]",
54
+ className ?? "",
55
+ ]
56
+ .filter(Boolean)
57
+ .join(" ");
58
+ const bodyCls = [
59
+ "chat-msg-body min-w-0",
60
+ bodyTone,
61
+ role === "user" ? "whitespace-pre-wrap" : "",
62
+ bodyClassName ?? "",
63
+ ]
64
+ .filter(Boolean)
65
+ .join(" ");
66
+ return (
67
+ <div className={cls}>
68
+ <span className="chat-msg-marker aui-msg-marker text-ink" aria-hidden="true">
69
+ {marker ?? MARKERS[role]}
70
+ </span>
71
+ <div className={bodyCls}>
72
+ {children}
73
+ {streaming ? <ChatStreamCursor /> : null}
74
+ </div>
75
+ </div>
76
+ );
77
+ }