@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,163 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
5
+ import { cn } from "../../../../lib/utils"
6
+
7
+ import { XIcon } from "lucide-react"
8
+
9
+ import { Button } from "./button"
10
+ import { stockPortalProps, useStockShape } from "../stock-shape"
11
+
12
+ function Dialog({ ...props }: DialogPrimitive.Root.Props) {
13
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
14
+ }
15
+
16
+ function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
17
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
18
+ }
19
+
20
+ function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
21
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
22
+ }
23
+
24
+ function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
25
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
26
+ }
27
+
28
+ function DialogOverlay({
29
+ className,
30
+ ...props
31
+ }: DialogPrimitive.Backdrop.Props) {
32
+ return (
33
+ <DialogPrimitive.Backdrop
34
+ data-slot="dialog-overlay"
35
+ className={cn(
36
+ "fixed inset-0 isolate z-50 bg-black/10 duration-(--dur-fast) supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
37
+ className
38
+ )}
39
+ {...props}
40
+ />
41
+ )
42
+ }
43
+
44
+ function DialogContent({
45
+ className,
46
+ children,
47
+ showCloseButton = true,
48
+ ...props
49
+ }: DialogPrimitive.Popup.Props & {
50
+ showCloseButton?: boolean
51
+ }) {
52
+ const stock = useStockShape()
53
+ return (
54
+ <DialogPortal>
55
+ <DialogOverlay />
56
+ <DialogPrimitive.Popup
57
+ data-slot="dialog-content"
58
+ {...stockPortalProps(stock)}
59
+ className={cn(
60
+ "fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-surface-2 p-4 text-sm text-ink ring-1 ring-foreground/10 duration-(--dur-fast) outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
61
+ className
62
+ )}
63
+ {...props}
64
+ >
65
+ {children}
66
+ {showCloseButton && (
67
+ <DialogPrimitive.Close
68
+ data-slot="dialog-close"
69
+ render={
70
+ <Button
71
+ variant="ghost"
72
+ className="absolute top-2 right-2"
73
+ size="icon-sm"
74
+ />
75
+ }
76
+ >
77
+ <XIcon />
78
+ <span className="sr-only">Close</span>
79
+ </DialogPrimitive.Close>
80
+ )}
81
+ </DialogPrimitive.Popup>
82
+ </DialogPortal>
83
+ )
84
+ }
85
+
86
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
87
+ return (
88
+ <div
89
+ data-slot="dialog-header"
90
+ className={cn("flex flex-col gap-2", className)}
91
+ {...props}
92
+ />
93
+ )
94
+ }
95
+
96
+ function DialogFooter({
97
+ className,
98
+ showCloseButton = false,
99
+ children,
100
+ ...props
101
+ }: React.ComponentProps<"div"> & {
102
+ showCloseButton?: boolean
103
+ }) {
104
+ return (
105
+ <div
106
+ data-slot="dialog-footer"
107
+ className={cn(
108
+ "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-surface-2/50 p-4 sm:flex-row sm:justify-end",
109
+ className
110
+ )}
111
+ {...props}
112
+ >
113
+ {children}
114
+ {showCloseButton && (
115
+ <DialogPrimitive.Close render={<Button variant="outline" />}>
116
+ Close
117
+ </DialogPrimitive.Close>
118
+ )}
119
+ </div>
120
+ )
121
+ }
122
+
123
+ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
124
+ return (
125
+ <DialogPrimitive.Title
126
+ data-slot="dialog-title"
127
+ className={cn(
128
+ "cn-font-heading text-base leading-none font-medium",
129
+ className
130
+ )}
131
+ {...props}
132
+ />
133
+ )
134
+ }
135
+
136
+ function DialogDescription({
137
+ className,
138
+ ...props
139
+ }: DialogPrimitive.Description.Props) {
140
+ return (
141
+ <DialogPrimitive.Description
142
+ data-slot="dialog-description"
143
+ className={cn(
144
+ "text-sm text-ink-mute *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-ink",
145
+ className
146
+ )}
147
+ {...props}
148
+ />
149
+ )
150
+ }
151
+
152
+ export {
153
+ Dialog,
154
+ DialogClose,
155
+ DialogContent,
156
+ DialogDescription,
157
+ DialogFooter,
158
+ DialogHeader,
159
+ DialogOverlay,
160
+ DialogPortal,
161
+ DialogTitle,
162
+ DialogTrigger,
163
+ }
@@ -0,0 +1,13 @@
1
+ import { cn } from "../../../../lib/utils"
2
+
3
+ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4
+ return (
5
+ <div
6
+ data-slot="skeleton"
7
+ className={cn("animate-pulse rounded-md bg-surface-2", className)}
8
+ {...props}
9
+ />
10
+ )
11
+ }
12
+
13
+ export { Skeleton }
@@ -0,0 +1,20 @@
1
+ import * as React from "react"
2
+ import { cn } from "../../../../lib/utils"
3
+ import { useStockShape } from "../stock-shape"
4
+
5
+ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
6
+ const stock = useStockShape()
7
+ return (
8
+ <textarea
9
+ data-slot="textarea"
10
+ className={cn(
11
+ "flex field-sizing-content min-h-16 w-full rounded-none border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-ink-mute focus-visible:border-accent focus-visible:ring-3 focus-visible:ring-accent/50 disabled:cursor-not-allowed disabled:bg-surface-2/50 disabled:opacity-50 aria-invalid:border-danger aria-invalid:ring-3 aria-invalid:ring-danger/20 md:text-sm dark:bg-surface-2/30 dark:disabled:bg-surface-2/80 dark:aria-invalid:border-danger/50 dark:aria-invalid:ring-danger/40",
12
+ stock && "rounded-lg",
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+
20
+ export { Textarea }
@@ -0,0 +1,68 @@
1
+ "use client"
2
+
3
+ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
4
+ import { cn } from "../../../../lib/utils"
5
+ import { useStockShape } from "../stock-shape"
6
+
7
+ function TooltipProvider({
8
+ delay = 0,
9
+ ...props
10
+ }: TooltipPrimitive.Provider.Props) {
11
+ return (
12
+ <TooltipPrimitive.Provider
13
+ data-slot="tooltip-provider"
14
+ delay={delay}
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+
20
+ function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
21
+ return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
22
+ }
23
+
24
+ function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
25
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
26
+ }
27
+
28
+ function TooltipContent({
29
+ className,
30
+ side = "top",
31
+ sideOffset = 4,
32
+ align = "center",
33
+ alignOffset = 0,
34
+ children,
35
+ ...props
36
+ }: TooltipPrimitive.Popup.Props &
37
+ Pick<
38
+ TooltipPrimitive.Positioner.Props,
39
+ "align" | "alignOffset" | "side" | "sideOffset"
40
+ >) {
41
+ const stock = useStockShape()
42
+ return (
43
+ <TooltipPrimitive.Portal>
44
+ <TooltipPrimitive.Positioner
45
+ align={align}
46
+ alignOffset={alignOffset}
47
+ side={side}
48
+ sideOffset={sideOffset}
49
+ className="isolate z-50"
50
+ >
51
+ <TooltipPrimitive.Popup
52
+ data-slot="tooltip-content"
53
+ className={cn(
54
+ "z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-none bg-ink px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-none data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
55
+ stock && "rounded-md **:data-[slot=kbd]:rounded-sm",
56
+ className
57
+ )}
58
+ {...props}
59
+ >
60
+ {children}
61
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-ink fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
62
+ </TooltipPrimitive.Popup>
63
+ </TooltipPrimitive.Positioner>
64
+ </TooltipPrimitive.Portal>
65
+ )
66
+ }
67
+
68
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
@@ -0,0 +1,42 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { useAuiState } from "@assistant-ui/react";
5
+ import { useShallow } from "zustand/react/shallow";
6
+
7
+ const useFileSrc = (file: File | undefined) => {
8
+ const [entry, setEntry] = useState<{ file: File; url: string } | undefined>(
9
+ undefined,
10
+ );
11
+
12
+ useEffect(() => {
13
+ if (!file) {
14
+ setEntry(undefined);
15
+ return;
16
+ }
17
+
18
+ const objectUrl = URL.createObjectURL(file);
19
+ setEntry({ file, url: objectUrl });
20
+
21
+ return () => {
22
+ URL.revokeObjectURL(objectUrl);
23
+ };
24
+ }, [file]);
25
+
26
+ return entry !== undefined && entry.file === file ? entry.url : undefined;
27
+ };
28
+
29
+ export const useAttachmentSrc = () => {
30
+ const { file, src } = useAuiState(
31
+ useShallow((s): { file?: File; src?: string } => {
32
+ if (s.attachment.type !== "image") return {};
33
+ if (s.attachment.file) return { file: s.attachment.file };
34
+ const src = s.attachment.content?.filter((c) => c.type === "image")[0]
35
+ ?.image;
36
+ if (!src) return {};
37
+ return { src };
38
+ }),
39
+ );
40
+
41
+ return useFileSrc(file) ?? src;
42
+ };
@@ -0,0 +1,29 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+
5
+ export type UseCopyToClipboardOptions = {
6
+ copiedDuration?: number;
7
+ };
8
+
9
+ export const useCopyToClipboard = ({
10
+ copiedDuration = 3000,
11
+ }: UseCopyToClipboardOptions = {}) => {
12
+ const [isCopied, setIsCopied] = useState<boolean>(false);
13
+
14
+ const copyToClipboard = (value: string) => {
15
+ if (!value || typeof navigator === "undefined" || !navigator.clipboard) {
16
+ return;
17
+ }
18
+
19
+ navigator.clipboard.writeText(value).then(
20
+ () => {
21
+ setIsCopied(true);
22
+ setTimeout(() => setIsCopied(false), copiedDuration);
23
+ },
24
+ () => {},
25
+ );
26
+ };
27
+
28
+ return { isCopied, copyToClipboard };
29
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * DeepSeek models sometimes echo their own search-template instruction into
3
+ * the visible answer ("Use the results below to formulate an answer to the
4
+ * user question unless additional information is needed"), often twice in a
5
+ * row. It is never content, so drop it from rendered markdown (DIG-1210).
6
+ */
7
+ const PHRASE =
8
+ "Use the results below to formulate an answer to the user question unless additional information is needed";
9
+
10
+ const esc = (t: string) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11
+ // Words joined by flexible whitespace, so line-wrapped copies still match.
12
+ const FULL = PHRASE.split(" ").map(esc).join("\\s+");
13
+ /** One or more back-to-back full copies, with or without separators. */
14
+ const REPEATED = new RegExp(`\\s*(?:${FULL}\\s*[.,;:]?\\s*)+`, "gi");
15
+
16
+ /** Drop a copy cut off mid-phrase at the very end (streaming or truncated). */
17
+ function stripTrailingPartial(text: string): string {
18
+ const lower = text.toLowerCase();
19
+ const at = lower.lastIndexOf("use the");
20
+ if (at < 0) return text;
21
+ const tail = lower.slice(at).replace(/\s+/g, " ").trimEnd();
22
+ return PHRASE.toLowerCase().startsWith(tail) ? text.slice(0, at).trimEnd() : text;
23
+ }
24
+
25
+ export function stripModelBoilerplate(text: string): string {
26
+ const out = text.replace(REPEATED, (_m, offset: number) => (offset === 0 ? "" : " "));
27
+ return stripTrailingPartial(out).replace(/ {2,}/g, " ");
28
+ }
29
+
30
+ type TextNode = { type: string; value?: string; children?: TextNode[] };
31
+
32
+ function walk(node: TextNode): void {
33
+ if (node.type === "text" && typeof node.value === "string") {
34
+ node.value = stripModelBoilerplate(node.value);
35
+ }
36
+ for (const child of node.children ?? []) walk(child);
37
+ }
38
+
39
+ /** remark plugin: strip the boilerplate from every text node. */
40
+ export function remarkStripModelBoilerplate() {
41
+ return (tree: TextNode) => walk(tree);
42
+ }
@@ -0,0 +1,230 @@
1
+ "use client";
2
+
3
+ /**
4
+ * The empty-state starter chips, capped and cycled.
5
+ *
6
+ * A tenant defines its own starter list (eight for the house tenant, five for
7
+ * the trial) and the welcome used to print all of them, turning the empty state into
8
+ * a wall of prompts. Four is what fits on one line of reading; the rest are
9
+ * reached by cycling, so every defined starter is still seen.
10
+ *
11
+ * The cycle is deliberately polite:
12
+ * - it pauses while the pointer is over the chips or focus is inside them,
13
+ * because a list that changes under the cursor is a list you cannot click;
14
+ * - it stops for good once the conversation has started or the composer holds
15
+ * text, so the prompts never move under someone who is reading them while
16
+ * composing;
17
+ * - it never starts under `prefers-reduced-motion: reduce`, which pins the
18
+ * first group;
19
+ * - the timer is cleared on unmount and whenever those conditions stop it, so
20
+ * nothing ticks in the background.
21
+ *
22
+ * The group size is constant, so a swap never reflows the row: the wrapper is
23
+ * keyed by the group offset and the chips re-run their existing entrance fade
24
+ * (house duration tokens) instead of the row growing or shrinking.
25
+ */
26
+
27
+ import {
28
+ createContext,
29
+ useContext,
30
+ useEffect,
31
+ useMemo,
32
+ useState,
33
+ type FocusEventHandler,
34
+ type PointerEventHandler,
35
+ type ReactNode,
36
+ } from "react";
37
+
38
+ /** How many starter chips are on screen at once. */
39
+ export const VISIBLE_SUGGESTIONS = 4;
40
+
41
+ /** How long a group stays before the next one fades in. */
42
+ export const SUGGESTION_CYCLE_MS = 8_000;
43
+
44
+ /**
45
+ * The window of `all` starting at `offset`, wrapping. Returns the whole list
46
+ * when it already fits, so a tenant with 3 starters is never padded.
47
+ */
48
+ export function suggestionWindow(
49
+ all: readonly string[],
50
+ offset: number,
51
+ ): readonly string[] {
52
+ if (all.length <= VISIBLE_SUGGESTIONS) return all;
53
+ const start = ((offset % all.length) + all.length) % all.length;
54
+ return Array.from(
55
+ { length: VISIBLE_SUGGESTIONS },
56
+ (_, index) => all[(start + index) % all.length]!,
57
+ );
58
+ }
59
+
60
+ /** Reduced-motion, read directly: the cycle is a JS timer, so CSS cannot stop it. */
61
+ function usePrefersReducedMotion(): boolean {
62
+ // Lazy read: the group on screen is the same either way (offset 0), so the
63
+ // client's real preference cannot desync the server markup.
64
+ const [reduced, setReduced] = useState(
65
+ () =>
66
+ typeof window !== "undefined" &&
67
+ Boolean(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches),
68
+ );
69
+ useEffect(() => {
70
+ if (typeof window === "undefined" || !window.matchMedia) return;
71
+ const query = window.matchMedia("(prefers-reduced-motion: reduce)");
72
+ const onChange = (event: MediaQueryListEvent) => setReduced(event.matches);
73
+ query.addEventListener?.("change", onChange);
74
+ return () => query.removeEventListener?.("change", onChange);
75
+ }, []);
76
+ return reduced;
77
+ }
78
+
79
+ /** Handlers a chip row spreads so the cycle pauses while it is being read. */
80
+ export type SuggestionCycleHandlers = {
81
+ onPointerEnter: PointerEventHandler;
82
+ onPointerLeave: PointerEventHandler;
83
+ onFocus: FocusEventHandler;
84
+ onBlur: FocusEventHandler;
85
+ };
86
+
87
+ export type SuggestionCycle = {
88
+ /** The group currently on screen. */
89
+ visible: readonly string[];
90
+ /** Index of the group's first chip — also the row's remount key. */
91
+ offset: number;
92
+ /** True while hovered or focused: the cycle is held, not stopped. */
93
+ paused: boolean;
94
+ handlers: SuggestionCycleHandlers;
95
+ };
96
+
97
+ /**
98
+ * Cap `all` to {@link VISIBLE_SUGGESTIONS} and advance through it every
99
+ * {@link SUGGESTION_CYCLE_MS}.
100
+ *
101
+ * `stopped` is the caller's "the user is no longer looking at a fresh empty
102
+ * state" signal (thread has messages, composer has text).
103
+ */
104
+ export function useSuggestionCycle(
105
+ all: readonly string[],
106
+ { stopped = false }: { stopped?: boolean } = {},
107
+ ): SuggestionCycle {
108
+ const [offset, setOffset] = useState(0);
109
+ const [hovered, setHovered] = useState(false);
110
+ const [focused, setFocused] = useState(false);
111
+ const reduced = usePrefersReducedMotion();
112
+
113
+ const paused = hovered || focused;
114
+ const cycles = all.length > VISIBLE_SUGGESTIONS;
115
+ const active = cycles && !stopped && !paused && !reduced;
116
+
117
+ useEffect(() => {
118
+ if (!active) return;
119
+ const timer = setInterval(
120
+ () => setOffset((current) => current + VISIBLE_SUGGESTIONS),
121
+ SUGGESTION_CYCLE_MS,
122
+ );
123
+ return () => clearInterval(timer);
124
+ }, [active]);
125
+
126
+ const handlers = useMemo<SuggestionCycleHandlers>(
127
+ () => ({
128
+ onPointerEnter: () => setHovered(true),
129
+ onPointerLeave: () => setHovered(false),
130
+ onFocus: () => setFocused(true),
131
+ onBlur: () => setFocused(false),
132
+ }),
133
+ [],
134
+ );
135
+
136
+ const visible = useMemo(
137
+ () => suggestionWindow(all, offset),
138
+ [all, offset],
139
+ );
140
+
141
+ return { visible, offset, paused, handlers };
142
+ }
143
+
144
+ const SuggestionCycleContext = createContext<SuggestionCycle | null>(null);
145
+
146
+ /** Mounts the cycle above the skins so every renderer shares one timer. */
147
+ export function SuggestionCycleProvider({
148
+ value,
149
+ children,
150
+ }: {
151
+ value: SuggestionCycle;
152
+ children: ReactNode;
153
+ }) {
154
+ return (
155
+ <SuggestionCycleContext.Provider value={value}>
156
+ {children}
157
+ </SuggestionCycleContext.Provider>
158
+ );
159
+ }
160
+
161
+ /**
162
+ * What a skin's chip row needs: the remount key (so the group fades in) and
163
+ * the pause handlers. Surfaces mounted without the provider still render —
164
+ * they simply never cycle.
165
+ */
166
+ export function useSuggestionGroup(): {
167
+ groupKey: number;
168
+ handlers: SuggestionCycleHandlers;
169
+ } {
170
+ const cycle = useContext(SuggestionCycleContext);
171
+ return useMemo(
172
+ () => ({
173
+ groupKey: cycle?.offset ?? 0,
174
+ handlers: cycle?.handlers ?? NO_HANDLERS,
175
+ }),
176
+ [cycle],
177
+ );
178
+ }
179
+
180
+ const NO_HANDLERS: SuggestionCycleHandlers = {
181
+ onPointerEnter: () => {},
182
+ onPointerLeave: () => {},
183
+ onFocus: () => {},
184
+ onBlur: () => {},
185
+ };
186
+
187
+ /**
188
+ * Has the visitor left the fresh empty state? Reads the assistant-ui runtime
189
+ * directly so the shell that owns the starter list can stop the cycle without
190
+ * sitting inside the runtime provider. Defensive on purpose: a runtime double
191
+ * or an SSR pass without a live thread simply reports "not started".
192
+ */
193
+ export function useThreadActivity(
194
+ runtime: { thread?: unknown } | null | undefined,
195
+ ): { started: boolean; typed: boolean } {
196
+ const thread = runtime?.thread as
197
+ | {
198
+ getState?: () => { messages?: readonly unknown[] } | undefined;
199
+ subscribe?: (callback: () => void) => (() => void) | void;
200
+ composer?: { getState?: () => { text?: string } | undefined };
201
+ }
202
+ | undefined;
203
+ const [started, setStarted] = useState(false);
204
+ const [typed, setTyped] = useState(false);
205
+
206
+ useEffect(() => {
207
+ if (typeof thread?.getState !== "function") return;
208
+ const read = () => {
209
+ let messages: readonly unknown[] | undefined;
210
+ let text: string | undefined;
211
+ try {
212
+ messages = thread.getState?.()?.messages;
213
+ text = thread.composer?.getState?.()?.text;
214
+ } catch {
215
+ return;
216
+ }
217
+ setStarted((messages?.length ?? 0) > 0);
218
+ setTyped((text?.trim().length ?? 0) > 0);
219
+ };
220
+ // Deferred out of the synchronous effect body —
221
+ // react-hooks/set-state-in-effect.
222
+ queueMicrotask(read);
223
+ const unsubscribe = thread.subscribe?.(read);
224
+ return () => {
225
+ if (typeof unsubscribe === "function") unsubscribe();
226
+ };
227
+ }, [thread]);
228
+
229
+ return useMemo(() => ({ started, typed }), [started, typed]);
230
+ }
@@ -0,0 +1,81 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Horizontal row for example questions. Its scrollbar is thin and stays
5
+ * hidden until the row is hovered, focused or scrolled (styles in
6
+ * chat-aui.css, `.aui-suggestion-scroll`). Chrome does not repaint a
7
+ * `::-webkit-scrollbar-thumb` when only `:hover` changes, so the row sets
8
+ * `data-hover` / `data-focus` itself, and `data-scrolling` for a moment after
9
+ * each scroll so touch and trackpad users see where they are.
10
+ */
11
+
12
+ import {
13
+ forwardRef,
14
+ useCallback,
15
+ useEffect,
16
+ useRef,
17
+ type FocusEvent,
18
+ type HTMLAttributes,
19
+ type PointerEvent,
20
+ type UIEvent,
21
+ } from "react";
22
+
23
+ import { cn } from "../../lib/utils";
24
+
25
+ const REVEAL_MS = 900;
26
+
27
+ export const SuggestionScrollRow = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
28
+ function SuggestionScrollRow(
29
+ { className, onScroll, onPointerEnter, onPointerLeave, onFocus, onBlur, ...props },
30
+ ref,
31
+ ) {
32
+ const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
33
+
34
+ useEffect(
35
+ () => () => {
36
+ if (timer.current) clearTimeout(timer.current);
37
+ },
38
+ [],
39
+ );
40
+
41
+ const handleScroll = useCallback(
42
+ (event: UIEvent<HTMLDivElement>) => {
43
+ const el = event.currentTarget;
44
+ el.dataset.scrolling = "true";
45
+ if (timer.current) clearTimeout(timer.current);
46
+ timer.current = setTimeout(() => {
47
+ delete el.dataset.scrolling;
48
+ }, REVEAL_MS);
49
+ onScroll?.(event);
50
+ },
51
+ [onScroll],
52
+ );
53
+
54
+ return (
55
+ <div
56
+ ref={ref}
57
+ className={cn("aui-suggestion-scroll overflow-x-auto", className)}
58
+ onScroll={handleScroll}
59
+ onPointerEnter={(event: PointerEvent<HTMLDivElement>) => {
60
+ if (event.pointerType === "mouse") event.currentTarget.dataset.hover = "true";
61
+ onPointerEnter?.(event);
62
+ }}
63
+ onPointerLeave={(event: PointerEvent<HTMLDivElement>) => {
64
+ delete event.currentTarget.dataset.hover;
65
+ onPointerLeave?.(event);
66
+ }}
67
+ onFocus={(event: FocusEvent<HTMLDivElement>) => {
68
+ event.currentTarget.dataset.focus = "true";
69
+ onFocus?.(event);
70
+ }}
71
+ onBlur={(event: FocusEvent<HTMLDivElement>) => {
72
+ if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {
73
+ delete event.currentTarget.dataset.focus;
74
+ }
75
+ onBlur?.(event);
76
+ }}
77
+ {...props}
78
+ />
79
+ );
80
+ },
81
+ );