@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,1050 @@
1
+ "use client";
2
+
3
+ import {
4
+ ComposerAddAttachment,
5
+ ComposerAttachments,
6
+ UserMessageAttachments,
7
+ } from "./attachment.aui";
8
+ import { MarkdownText } from "./markdown-text";
9
+ import { useDigichatWarmupPhase } from "../../use-warmup-phase";
10
+ import { useSuggestionGroup } from "../../suggestion-cycle";
11
+ import { SuggestionScrollRow } from "../../suggestion-scroll-row";
12
+ import { connectionStatusText } from "../../connection-status";
13
+ import { MessageTiming } from "./elements/message-timing.aui";
14
+ import { backendNoticeOf } from "../../stock/chat-notice";
15
+ import { useSkinRuntime } from "../../stock/skin-runtime";
16
+ // assistant-ui's own trace parts (see ./aui-default/README.md), shared with
17
+ // the stock thread: the base skins render reasoning and tool calls as
18
+ // assistant-ui ships them.
19
+ import { AssistantTrace } from "./elements/assistant-trace.aui";
20
+
21
+ import { MessageExportMenu } from "../../stock/message-export-menu";
22
+ import { TooltipIconButton } from "./tooltip-icon-button";
23
+ import { messageHasContent } from "../../trace-grouping";
24
+ import { Button } from "./ui/button";
25
+ import { Skeleton } from "./ui/skeleton";
26
+ import { cn } from "../../../../lib/utils";
27
+ import {
28
+ SkinCredit,
29
+ useComposerCopy,
30
+ useSkinChrome,
31
+ } from "../../stock";
32
+ import {
33
+ ComposerQuotePreview,
34
+ QuoteBlock,
35
+ SelectionToolbar,
36
+ } from "./elements/quote.aui";
37
+ import { ComposerTriggerPopover } from "./elements/composer-trigger-popover.aui";
38
+ import { useSkinPaletteOptional } from "../../stock/skin-palette-context";
39
+ import { useEmbedChatPrefsOptional } from "../../stock/embed-chat-prefs";
40
+ import { DirectiveText } from "./elements/directive-text.aui";
41
+ import {
42
+ ActionBarPrimitive,
43
+ AuiIf,
44
+ type AssistantState,
45
+ BranchPickerPrimitive,
46
+ ComposerPrimitive,
47
+ ErrorPrimitive,
48
+ MessagePrimitive,
49
+ ThreadPrimitive,
50
+ unstable_useMentionAdapter,
51
+ unstable_useSlashCommandAdapter,
52
+ useAui,
53
+ useAuiState,
54
+ type Unstable_SlashCommand,
55
+ } from "@assistant-ui/react";
56
+ import {
57
+ ArrowDownIcon,
58
+ ArrowUpIcon,
59
+ ChartColumnIcon,
60
+ CheckIcon,
61
+ ChevronDownIcon,
62
+ ChevronLeftIcon,
63
+ ChevronRightIcon,
64
+ CloudSunIcon,
65
+ CodeXmlIcon,
66
+ CopyIcon,
67
+ DownloadIcon,
68
+ FileTextIcon,
69
+ GlobeIcon,
70
+ HelpCircleIcon,
71
+ LanguagesIcon,
72
+ LightbulbIcon,
73
+ MicIcon,
74
+ MoreHorizontalIcon,
75
+ PencilIcon,
76
+ PencilLineIcon,
77
+ RefreshCwIcon,
78
+ SlashIcon,
79
+ SquareIcon,
80
+ WrenchIcon,
81
+ } from "lucide-react";
82
+ import {
83
+ LexicalComposerInput,
84
+ type DirectiveChipProps,
85
+ } from "@assistant-ui/react-lexical";
86
+ import { useEffect, useRef, useState, type FC, type ReactNode } from "react";
87
+ import { ModelSelector } from "./elements/model-selector.aui";
88
+ import { docsModelOptions } from "./docs-model-options";
89
+ import { DEFAULT_MODEL_ID } from "./model";
90
+
91
+ const models = docsModelOptions();
92
+
93
+ /** Display label for a model id: the part after the vendor prefix. */
94
+ export function modelChipLabel(model: string): string {
95
+ const id = model.trim();
96
+ if (!id) return "auto";
97
+ return id.slice(id.lastIndexOf("/") + 1);
98
+ }
99
+
100
+ /**
101
+ * Inside a host with chat prefs the chip is the real picker: it shows the
102
+ * session model and opens the host's models menu (the same list `/models`
103
+ * shows). The demo selector only renders without a host (the catalog).
104
+ */
105
+ const ModelPicker: FC = () => {
106
+ const prefs = useEmbedChatPrefsOptional();
107
+ if (prefs) {
108
+ if (!prefs.showModels) return null;
109
+ return (
110
+ <div data-deploy-model-picker>
111
+ <Button
112
+ type="button"
113
+ variant="ghost"
114
+ size="sm"
115
+ className="aui-model-chip text-muted-foreground hover:text-foreground h-7 max-w-48 gap-1 rounded-full px-2.5 text-xs"
116
+ onClick={prefs.openModels}
117
+ aria-label="Open models"
118
+ title={prefs.prefs.model.trim() || "auto"}
119
+ >
120
+ <span className="truncate">{modelChipLabel(prefs.prefs.model)}</span>
121
+ <ChevronDownIcon className="size-3.5 shrink-0 opacity-60" />
122
+ </Button>
123
+ </div>
124
+ );
125
+ }
126
+ return (
127
+ <div data-deploy-model-picker>
128
+ <ModelSelector
129
+ models={models}
130
+ defaultValue={DEFAULT_MODEL_ID}
131
+ variant="ghost"
132
+ size="sm"
133
+ className="h-7 rounded-full"
134
+ />
135
+ </div>
136
+ );
137
+ };
138
+
139
+ /**
140
+ * The tenant's chat languages, as a chip beside the model chip. It drives the
141
+ * same session language as `/language`, so the two always agree. Renders only
142
+ * when the host lists two or more languages.
143
+ */
144
+ const LanguageChip: FC = () => {
145
+ const prefs = useEmbedChatPrefsOptional();
146
+ const [open, setOpen] = useState(false);
147
+ const rootRef = useRef<HTMLDivElement>(null);
148
+ useEffect(() => {
149
+ if (!open) return;
150
+ const onDown = (e: PointerEvent) => {
151
+ if (!rootRef.current?.contains(e.target as Node)) setOpen(false);
152
+ };
153
+ const onKey = (e: KeyboardEvent) => {
154
+ if (e.key === "Escape") setOpen(false);
155
+ };
156
+ document.addEventListener("pointerdown", onDown);
157
+ document.addEventListener("keydown", onKey);
158
+ return () => {
159
+ document.removeEventListener("pointerdown", onDown);
160
+ document.removeEventListener("keydown", onKey);
161
+ };
162
+ }, [open]);
163
+ const options = prefs?.languageOptions;
164
+ if (!prefs || !options || options.length < 2) return null;
165
+ const current = prefs.prefs.language;
166
+ const active = options.find((o) => o.code === current);
167
+ return (
168
+ <div ref={rootRef} data-deploy-language-picker className="relative">
169
+ <Button
170
+ type="button"
171
+ variant="ghost"
172
+ size="sm"
173
+ className="aui-language-chip text-muted-foreground hover:text-foreground h-7 gap-1 rounded-full px-2.5 text-xs"
174
+ onClick={() => setOpen((v) => !v)}
175
+ aria-label="Chat language"
176
+ aria-haspopup="listbox"
177
+ aria-expanded={open}
178
+ title={active?.label ?? current}
179
+ >
180
+ <span>{active?.short ?? current.toUpperCase()}</span>
181
+ <ChevronDownIcon className="size-3.5 shrink-0 opacity-60" />
182
+ </Button>
183
+ {open ? (
184
+ <div
185
+ role="listbox"
186
+ aria-label="Chat language"
187
+ className="aui-language-menu bg-popover text-popover-foreground absolute bottom-full left-0 z-50 mb-1 min-w-36 rounded-xl border p-1 shadow-md"
188
+ >
189
+ {options.map((o) => (
190
+ <button
191
+ key={o.code}
192
+ type="button"
193
+ role="option"
194
+ aria-selected={o.code === current}
195
+ className="hover:bg-muted focus-visible:bg-muted focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset flex w-full items-center justify-between gap-3 rounded-lg px-2.5 py-1.5 text-left text-xs outline-none"
196
+ onClick={() => {
197
+ prefs.setLanguage(o.code);
198
+ setOpen(false);
199
+ }}
200
+ >
201
+ <span>{o.label}</span>
202
+ <span className="text-muted-foreground">{o.short}</span>
203
+ </button>
204
+ ))}
205
+ </div>
206
+ ) : null}
207
+ </div>
208
+ );
209
+ };
210
+
211
+ // Startup exposes a loading placeholder thread; treat it as a new chat so
212
+ // the composer mounts centered. Loads after startup keep the docked layout.
213
+ const isNewChatView = (s: AssistantState) =>
214
+ s.thread.messages.length === 0 &&
215
+ (!s.thread.isLoading || s.threads.isLoading);
216
+
217
+ // A switched thread that is still fetching its history: skeleton, not welcome.
218
+ const isHistoryLoadingView = (s: AssistantState) =>
219
+ s.thread.messages.length === 0 &&
220
+ s.thread.isLoading &&
221
+ !s.thread.isDisabled &&
222
+ !s.threads.isLoading;
223
+
224
+ const ThreadHistorySkeleton: FC = () => (
225
+ <div
226
+ data-slot="aui_thread-history-skeleton"
227
+ role="status"
228
+ className="animate-in fade-in fill-mode-both mx-auto flex w-full max-w-(--thread-max-width) flex-col gap-y-6 [animation-delay:150ms] [animation-duration:200ms]"
229
+ >
230
+ <span className="sr-only">Loading conversation</span>
231
+ <Skeleton className="ml-auto h-9 w-2/5 rounded-xl motion-reduce:animate-none" />
232
+ <div className="flex flex-col gap-y-2">
233
+ <Skeleton className="h-4 w-11/12 motion-reduce:animate-none" />
234
+ <Skeleton className="h-4 w-4/5 motion-reduce:animate-none" />
235
+ <Skeleton className="h-4 w-3/5 motion-reduce:animate-none" />
236
+ </div>
237
+ <Skeleton className="ml-auto h-9 w-1/3 rounded-xl motion-reduce:animate-none" />
238
+ <div className="flex flex-col gap-y-2">
239
+ <Skeleton className="h-4 w-10/12 motion-reduce:animate-none" />
240
+ <Skeleton className="h-4 w-2/3 motion-reduce:animate-none" />
241
+ </div>
242
+ </div>
243
+ );
244
+
245
+ const Thread: FC = () => {
246
+ const isEmpty = useAuiState(isNewChatView);
247
+
248
+ return (
249
+ <ThreadPrimitive.Root
250
+ className="aui-root aui-thread-root bg-background @container flex h-full flex-col"
251
+ style={{
252
+ ["--thread-max-width" as string]: "44rem",
253
+ // `--card`, not `--color-card`: the bridge alias resolves at :root,
254
+ // so it would keep the page card colour instead of the brand palette
255
+ // the skin injects on its wrapper.
256
+ ["--composer-bg" as string]: "var(--card)",
257
+ ["--composer-radius" as string]: "var(--radius-thread)",
258
+ ["--composer-padding" as string]: "8px",
259
+ }}
260
+ >
261
+ <ThreadPrimitive.Viewport
262
+ turnAnchor="top"
263
+ data-slot="aui_thread-viewport"
264
+ className={cn(
265
+ "relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4",
266
+ isEmpty && "justify-center",
267
+ )}
268
+ >
269
+ <AuiIf condition={isNewChatView}>
270
+ <ThreadWelcome />
271
+ </AuiIf>
272
+ <AuiIf condition={isHistoryLoadingView}>
273
+ <ThreadHistorySkeleton />
274
+ </AuiIf>
275
+
276
+ <div
277
+ data-slot="aui_message-group"
278
+ className="mb-14 flex flex-col gap-y-6 empty:hidden"
279
+ >
280
+ <ThreadPrimitive.Messages>
281
+ {({ message }) => {
282
+ if (message.composer.isEditing) return <EditComposer />;
283
+ if (message.role === "user") return <UserMessage />;
284
+ return <AssistantMessage />;
285
+ }}
286
+ </ThreadPrimitive.Messages>
287
+ </div>
288
+
289
+ <ThreadPrimitive.ViewportFooter
290
+ className={cn(
291
+ "aui-thread-viewport-footer bg-background mx-auto flex w-full max-w-(--thread-max-width) flex-col gap-4 overflow-visible pb-2 md:pb-3",
292
+ !isEmpty &&
293
+ "sticky bottom-0 mt-auto rounded-t-(--composer-radius)",
294
+ )}
295
+ >
296
+ <ThreadScrollToBottom />
297
+ <Composer />
298
+ <AuiIf condition={(s) => !s.thread.isEmpty}>
299
+ <SkinCredit />
300
+ </AuiIf>
301
+
302
+ <AuiIf condition={isNewChatView}>
303
+ <div className="aui-thread-welcome-suggestions-shell min-h-19">
304
+ <AuiIf condition={(s) => s.composer.isEmpty}>
305
+ <ThreadSuggestions />
306
+ </AuiIf>
307
+ </div>
308
+ </AuiIf>
309
+ </ThreadPrimitive.ViewportFooter>
310
+ <AuiIf condition={isNewChatView}>
311
+ <div className="pointer-events-none absolute inset-x-0 bottom-0 px-4 pb-2">
312
+ <SkinCredit />
313
+ </div>
314
+ </AuiIf>
315
+ </ThreadPrimitive.Viewport>
316
+
317
+ <SelectionToolbar />
318
+ </ThreadPrimitive.Root>
319
+ );
320
+ };
321
+
322
+ const ThreadScrollToBottom: FC = () => {
323
+ return (
324
+ <ThreadPrimitive.ScrollToBottom asChild>
325
+ <TooltipIconButton
326
+ tooltip="Scroll to bottom"
327
+ variant="outline"
328
+ className="aui-thread-scroll-to-bottom dark:border-border dark:bg-background dark:hover:bg-muted absolute -top-12 z-10 self-center rounded-full p-4 disabled:invisible"
329
+ >
330
+ <ArrowDownIcon />
331
+ </TooltipIconButton>
332
+ </ThreadPrimitive.ScrollToBottom>
333
+ );
334
+ };
335
+
336
+ const ThreadWelcome: FC = () => {
337
+ const chrome = useSkinChrome();
338
+ const { welcome } = useComposerCopy(
339
+ "How can I help you today?",
340
+ "Send a message... (@ to mention, / for commands)",
341
+ );
342
+ const headline = chrome.welcome?.trim() || welcome;
343
+ return (
344
+ <div className="aui-thread-welcome-root mx-auto mb-6 flex w-full max-w-(--thread-max-width) flex-col items-center px-4 text-center">
345
+ <p className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both text-2xl font-medium tracking-tight duration-200">
346
+ {headline}
347
+ </p>
348
+ </div>
349
+ );
350
+ };
351
+
352
+ type SuggestionGroup = {
353
+ label: string;
354
+ icon: ReactNode;
355
+ options: { label: string; prompt: string }[];
356
+ };
357
+
358
+ const SUGGESTION_GROUPS: SuggestionGroup[] = [
359
+ {
360
+ label: "Weather",
361
+ icon: <CloudSunIcon />,
362
+ options: [
363
+ {
364
+ label: "in San Francisco",
365
+ prompt: "What's the weather in San Francisco?",
366
+ },
367
+ { label: "in Singapore", prompt: "What's the weather in Singapore?" },
368
+ { label: "in Tokyo", prompt: "What's the weather in Tokyo?" },
369
+ { label: "in London", prompt: "What's the weather in London?" },
370
+ ],
371
+ },
372
+ {
373
+ label: "Code",
374
+ icon: <CodeXmlIcon />,
375
+ options: [
376
+ {
377
+ label: "explain React hooks",
378
+ prompt: "Explain React hooks like useState and useEffect",
379
+ },
380
+ {
381
+ label: "write a debounce function",
382
+ prompt: "Write a debounce function in TypeScript",
383
+ },
384
+ {
385
+ label: "review a useEffect cleanup",
386
+ prompt: "Show me the right way to clean up a subscription in useEffect",
387
+ },
388
+ ],
389
+ },
390
+ {
391
+ label: "Write",
392
+ icon: <PencilLineIcon />,
393
+ options: [
394
+ {
395
+ label: "a birthday card message",
396
+ prompt:
397
+ "Help me write a birthday card message for a friend in the notepad",
398
+ },
399
+ {
400
+ label: "a product announcement",
401
+ prompt: "Draft a short product announcement for a new dark mode",
402
+ },
403
+ {
404
+ label: "release notes",
405
+ prompt:
406
+ "Write release notes for a bugfix release of a React component library",
407
+ },
408
+ {
409
+ label: "a PR description",
410
+ prompt:
411
+ "Write a pull request description for a change that adds keyboard shortcuts",
412
+ },
413
+ ],
414
+ },
415
+ {
416
+ label: "Analyze",
417
+ icon: <ChartColumnIcon />,
418
+ options: [
419
+ {
420
+ label: "React vs Vue vs Svelte",
421
+ prompt: "Compare React, Vue, and Svelte in a table",
422
+ },
423
+ {
424
+ label: "GDP of US, China, Japan",
425
+ prompt:
426
+ "Compare the GDP of the United States, China, and Japan in a table",
427
+ },
428
+ {
429
+ label: "pros and cons of SSR",
430
+ prompt: "What are the pros and cons of server-side rendering?",
431
+ },
432
+ ],
433
+ },
434
+ {
435
+ label: "Brainstorm",
436
+ icon: <LightbulbIcon />,
437
+ options: [
438
+ {
439
+ label: "side project ideas",
440
+ prompt: "Brainstorm five side project ideas for a React developer",
441
+ },
442
+ {
443
+ label: "names for a dev tool",
444
+ prompt: "Brainstorm names for a developer tools startup",
445
+ },
446
+ {
447
+ label: "talk topics",
448
+ prompt: "Brainstorm talk topics for a React meetup",
449
+ },
450
+ ],
451
+ },
452
+ ];
453
+
454
+ const suggestionChipClass =
455
+ "aui-thread-welcome-suggestion fade-in animate-in fill-mode-both duration-(--dur) text-foreground hover:bg-muted border-border/60 h-auto gap-1.5 rounded-full border px-3.5 py-1.5 text-sm font-normal whitespace-nowrap transition-colors [&_svg]:size-4";
456
+
457
+ const ThreadSuggestions: FC = () => {
458
+ const aui = useAui();
459
+ const { suggestions } = useComposerCopy(
460
+ "How can I help you today?",
461
+ "Send a message...",
462
+ );
463
+ // Keyed by the cycle's group offset so a new group remounts and re-runs the
464
+ // chips' entrance fade; hover/focus handlers hold the cycle while it is read.
465
+ const { groupKey, handlers } = useSuggestionGroup();
466
+ const [expandedLabel, setExpandedLabel] = useState<string | null>(null);
467
+ const expandedGroup = SUGGESTION_GROUPS.find(
468
+ (group) => group.label === expandedLabel,
469
+ );
470
+
471
+ const sendPrompt = (prompt: string) => {
472
+ if (aui.thread.getState().isRunning) return;
473
+ aui.thread.append({
474
+ content: [{ type: "text", text: prompt }],
475
+ runConfig: aui.composer.getState().runConfig,
476
+ });
477
+ };
478
+
479
+ if (suggestions.length > 0) {
480
+ return (
481
+ <div
482
+ key={groupKey}
483
+ className="aui-thread-welcome-suggestions flex w-full flex-col gap-2 px-4"
484
+ {...handlers}
485
+ >
486
+ <SuggestionScrollRow className="w-full">
487
+ <div className="mx-auto flex w-max items-center gap-2">
488
+ {suggestions.map((prompt) => (
489
+ <Button
490
+ key={prompt}
491
+ variant="ghost"
492
+ className={suggestionChipClass}
493
+ onClick={() => sendPrompt(prompt)}
494
+ >
495
+ {prompt}
496
+ </Button>
497
+ ))}
498
+ </div>
499
+ </SuggestionScrollRow>
500
+ </div>
501
+ );
502
+ }
503
+
504
+ return (
505
+ <div
506
+ key={groupKey}
507
+ className="aui-thread-welcome-suggestions flex w-full flex-col gap-2 px-4"
508
+ {...handlers}
509
+ >
510
+ <SuggestionScrollRow className="w-full">
511
+ <div className="mx-auto flex w-max items-center gap-2">
512
+ {SUGGESTION_GROUPS.map((group) => (
513
+ <Button
514
+ key={group.label}
515
+ variant="ghost"
516
+ className={cn(
517
+ suggestionChipClass,
518
+ group.label === expandedLabel && "bg-muted",
519
+ )}
520
+ onClick={() =>
521
+ setExpandedLabel(
522
+ group.label === expandedLabel ? null : group.label,
523
+ )
524
+ }
525
+ >
526
+ {group.icon}
527
+ {group.label}
528
+ </Button>
529
+ ))}
530
+ </div>
531
+ </SuggestionScrollRow>
532
+ {expandedGroup && (
533
+ <SuggestionScrollRow
534
+ key={expandedGroup.label}
535
+ className="fade-in slide-in-from-top-1 animate-in w-full duration-200"
536
+ >
537
+ <div className="mx-auto flex w-max items-center gap-2">
538
+ {expandedGroup.options.map((option) => (
539
+ <Button
540
+ key={option.label}
541
+ variant="ghost"
542
+ className={suggestionChipClass}
543
+ onClick={() => sendPrompt(option.prompt)}
544
+ >
545
+ {option.label}
546
+ </Button>
547
+ ))}
548
+ </div>
549
+ </SuggestionScrollRow>
550
+ )}
551
+ </div>
552
+ );
553
+ };
554
+
555
+ const slashCommands: readonly Unstable_SlashCommand[] = [
556
+ {
557
+ id: "summarize",
558
+ description: "Summarize the conversation",
559
+ icon: "FileText",
560
+ // Example only — no-op.
561
+ execute: () => undefined,
562
+ },
563
+ {
564
+ id: "translate",
565
+ description: "Translate text to another language",
566
+ icon: "Languages",
567
+ // Example only — no-op.
568
+ execute: () => undefined,
569
+ },
570
+ {
571
+ id: "search",
572
+ description: "Search the web for information",
573
+ icon: "Globe",
574
+ // Example only — no-op.
575
+ execute: () => undefined,
576
+ },
577
+ {
578
+ id: "help",
579
+ description: "List available commands",
580
+ icon: "HelpCircle",
581
+ // Example only — no-op.
582
+ execute: () => undefined,
583
+ },
584
+ ];
585
+
586
+ const slashIconMap: Record<string, FC<{ className?: string }>> = {
587
+ FileText: FileTextIcon,
588
+ Languages: LanguagesIcon,
589
+ Globe: GlobeIcon,
590
+ HelpCircle: HelpCircleIcon,
591
+ };
592
+
593
+ function DirectiveChip(props: DirectiveChipProps) {
594
+ const { directiveId, directiveType, label } = props;
595
+ const showWrench = directiveType !== "command";
596
+ return (
597
+ <span
598
+ className="aui-directive-chip"
599
+ data-directive-type={directiveType}
600
+ data-directive-id={directiveId}
601
+ >
602
+ {showWrench && (
603
+ <span className="aui-directive-chip-icon">
604
+ <WrenchIcon className="size-3" />
605
+ </span>
606
+ )}
607
+ <span className="aui-directive-chip-label">{label}</span>
608
+ </span>
609
+ );
610
+ }
611
+
612
+ const Composer: FC = () => {
613
+ const mention = unstable_useMentionAdapter({ fallbackIcon: WrenchIcon });
614
+ const slash = unstable_useSlashCommandAdapter({
615
+ commands: slashCommands,
616
+ iconMap: slashIconMap,
617
+ fallbackIcon: SlashIcon,
618
+ });
619
+ // Host-wired product palette (SkinPaletteBridge): it owns the trigger root
620
+ // and the `/` and `@` menus, so the demo commands step aside. A nested root
621
+ // here would shadow it.
622
+ const productPalette = useSkinPaletteOptional();
623
+ const { placeholder } = useComposerCopy(
624
+ "How can I help you today?",
625
+ "Send a message... (@ to mention, / for commands)",
626
+ );
627
+
628
+ return (
629
+ <DemoTriggerRoot enabled={!productPalette}>
630
+ <ComposerPrimitive.Root className="aui-composer-root relative flex w-full flex-col">
631
+ <ComposerPrimitive.AttachmentDropzone asChild>
632
+ <div
633
+ data-slot="aui_composer-shell"
634
+ className="border-border/60 data-[dragging=true]:border-ring focus-within:border-border dark:border-muted-foreground/15 dark:focus-within:border-muted-foreground/30 flex w-full cursor-text flex-col gap-2 rounded-(--composer-radius) border bg-(--composer-bg) p-(--composer-padding) transition-[border-color] data-[dragging=true]:border-dashed data-[dragging=true]:bg-[color-mix(in_oklab,var(--color-accent)_50%,var(--color-background))]"
635
+ >
636
+ <ComposerQuotePreview />
637
+ <ComposerAttachments />
638
+ <LexicalComposerInput
639
+ directiveChip={DirectiveChip}
640
+ placeholder={placeholder}
641
+ className="aui-composer-input [&_.aui-lexical-placeholder]:text-muted-foreground/60 relative max-h-48 min-h-10 w-full resize-none bg-transparent px-2.5 py-1 text-base leading-6 outline-none [&_.aui-directive-chip]:inline-flex [&_.aui-directive-chip]:items-baseline [&_.aui-directive-chip]:gap-1 [&_.aui-directive-chip]:rounded-md [&_.aui-directive-chip]:bg-blue-100 [&_.aui-directive-chip]:px-1.5 [&_.aui-directive-chip]:py-0.5 [&_.aui-directive-chip]:text-[13px] [&_.aui-directive-chip]:leading-none [&_.aui-directive-chip]:font-medium [&_.aui-directive-chip]:text-blue-700 dark:[&_.aui-directive-chip]:bg-blue-900/50 dark:[&_.aui-directive-chip]:text-blue-300 [&_.aui-directive-chip-icon]:self-center [&_.aui-lexical-input]:min-h-lh [&_.aui-lexical-input]:outline-none [&_.aui-lexical-placeholder]:pointer-events-none [&_.aui-lexical-placeholder]:absolute [&_.aui-lexical-placeholder]:top-0 [&_.aui-lexical-placeholder]:right-0 [&_.aui-lexical-placeholder]:left-0 [&_.aui-lexical-placeholder]:truncate [&_.aui-lexical-placeholder]:px-2.5 [&_.aui-lexical-placeholder]:py-1"
642
+ />
643
+ <ComposerAction />
644
+ </div>
645
+ </ComposerPrimitive.AttachmentDropzone>
646
+
647
+ {productPalette ? null : (
648
+ <ComposerTriggerPopover char="@" {...mention} />
649
+ )}
650
+
651
+ {productPalette ? null : (
652
+ <ComposerTriggerPopover
653
+ char="/"
654
+ {...slash}
655
+ emptyItemsLabel="No matching commands"
656
+ />
657
+ )}
658
+ </ComposerPrimitive.Root>
659
+ </DemoTriggerRoot>
660
+ );
661
+ };
662
+
663
+ /** The demo palette's own trigger root; skipped under the product palette. */
664
+ const DemoTriggerRoot: FC<{ enabled: boolean; children: ReactNode }> = ({
665
+ enabled,
666
+ children,
667
+ }) =>
668
+ enabled ? (
669
+ <ComposerPrimitive.Unstable_TriggerPopoverRoot>
670
+ {children}
671
+ </ComposerPrimitive.Unstable_TriggerPopoverRoot>
672
+ ) : (
673
+ <>{children}</>
674
+ );
675
+
676
+
677
+ const ComposerAction: FC = () => {
678
+ return (
679
+ <div className="aui-composer-action-wrapper relative flex items-center justify-between">
680
+ <div className="flex items-center gap-1">
681
+ <ComposerAddAttachment />
682
+ <ModelPicker />
683
+ <LanguageChip />
684
+ </div>
685
+ <div className="flex items-center gap-1.5">
686
+ <AuiIf condition={(s) => s.thread.capabilities.dictation}>
687
+ <AuiIf condition={(s) => s.composer.dictation == null}>
688
+ <ComposerPrimitive.Dictate asChild>
689
+ <TooltipIconButton
690
+ tooltip="Voice input"
691
+ side="bottom"
692
+ type="button"
693
+ variant="ghost"
694
+ size="icon"
695
+ className="aui-composer-dictate text-muted-foreground hover:text-foreground size-7 rounded-full"
696
+ aria-label="Start voice input"
697
+ >
698
+ <MicIcon className="aui-composer-dictate-icon size-4" />
699
+ </TooltipIconButton>
700
+ </ComposerPrimitive.Dictate>
701
+ </AuiIf>
702
+ <AuiIf condition={(s) => s.composer.dictation != null}>
703
+ <ComposerPrimitive.StopDictation asChild>
704
+ <TooltipIconButton
705
+ tooltip="Stop dictation"
706
+ side="bottom"
707
+ type="button"
708
+ variant="ghost"
709
+ size="icon"
710
+ className="aui-composer-stop-dictation text-destructive size-7 rounded-full"
711
+ aria-label="Stop voice input"
712
+ >
713
+ <SquareIcon className="aui-composer-stop-dictation-icon size-3.5 animate-pulse fill-current" />
714
+ </TooltipIconButton>
715
+ </ComposerPrimitive.StopDictation>
716
+ </AuiIf>
717
+ </AuiIf>
718
+ <AuiIf condition={(s) => !s.thread.isRunning}>
719
+ <ComposerPrimitive.Send asChild>
720
+ <TooltipIconButton
721
+ tooltip="Send message"
722
+ side="bottom"
723
+ type="button"
724
+ variant="default"
725
+ size="icon"
726
+ className="aui-composer-send size-7 rounded-full"
727
+ aria-label="Send message"
728
+ >
729
+ <ArrowUpIcon className="aui-composer-send-icon size-4" />
730
+ </TooltipIconButton>
731
+ </ComposerPrimitive.Send>
732
+ </AuiIf>
733
+ <AuiIf condition={(s) => s.thread.isRunning}>
734
+ <ComposerPrimitive.Cancel asChild>
735
+ <Button
736
+ type="button"
737
+ variant="default"
738
+ size="icon"
739
+ className="aui-composer-cancel size-7 rounded-full"
740
+ aria-label="Stop generating"
741
+ >
742
+ <SquareIcon className="aui-composer-cancel-icon size-3.5 fill-current" />
743
+ </Button>
744
+ </ComposerPrimitive.Cancel>
745
+ </AuiIf>
746
+ </div>
747
+ </div>
748
+ );
749
+ };
750
+
751
+ /**
752
+ * assistant-ui's own working indicator (base thread.aui.tsx, `indicator`
753
+ * part), verbatim. The only addition: a status line the server sent (a boot
754
+ * retry, a rate-limit wait, waking services) shows beside it as plain text.
755
+ */
756
+ const AssistantWorkingIndicator: FC = () => {
757
+ const statusText = useAuiState((s) => connectionStatusText(s.message.parts));
758
+ const wakePhase = useDigichatWarmupPhase();
759
+ const line = statusText ?? (wakePhase === "waking" ? "Waking up services…" : null);
760
+ return (
761
+ <>
762
+ <span
763
+ data-slot="aui_assistant-message-indicator"
764
+ className="animate-pulse font-sans"
765
+ aria-label="Assistant is working"
766
+ >
767
+ {"●"}
768
+ </span>
769
+ {line ? (
770
+ <span
771
+ data-slot="aui_assistant-message-status"
772
+ role="status"
773
+ className="text-muted-foreground ms-2 text-sm"
774
+ >
775
+ {line}
776
+ </span>
777
+ ) : null}
778
+ </>
779
+ );
780
+ };
781
+
782
+ /**
783
+ * assistant-ui's own MessageError (base thread.aui.tsx), verbatim markup. The
784
+ * message text goes through the host's error formatter when it has one, so a
785
+ * raw API payload never reaches the visitor.
786
+ */
787
+ const MessageError: FC = () => {
788
+ const runtime = useSkinRuntime();
789
+ const raw = useAuiState((s) => {
790
+ const status = s.message.status as { type?: string; reason?: string; error?: unknown } | undefined;
791
+ if (status?.type !== "incomplete" || status.reason !== "error") return undefined;
792
+ const error = status.error;
793
+ if (typeof error === "string") return error;
794
+ if (error && typeof error === "object" && "message" in error) {
795
+ const message = (error as { message: unknown }).message;
796
+ if (typeof message === "string") return message;
797
+ }
798
+ return error != null ? String(error) : undefined;
799
+ });
800
+ const err = raw ? new Error(raw) : undefined;
801
+ const friendly =
802
+ runtime.errorParsers?.formatError?.(err) ?? runtime.errorParsers?.parseError?.(err)?.message ?? undefined;
803
+ return (
804
+ <MessagePrimitive.Error>
805
+ <ErrorPrimitive.Root className="aui-message-error-root border-destructive bg-destructive/10 text-destructive dark:bg-destructive/5 mt-2 rounded-md border p-3 text-sm dark:text-red-200">
806
+ <ErrorPrimitive.Message className="aui-message-error-message line-clamp-2">
807
+ {friendly ?? undefined}
808
+ </ErrorPrimitive.Message>
809
+ </ErrorPrimitive.Root>
810
+ </MessagePrimitive.Error>
811
+ );
812
+ };
813
+
814
+ /**
815
+ * A backend notice (data-notice, or a data-status with a message). Stock
816
+ * assistant-ui renders only `dataRendererUI` for data parts, so these use the
817
+ * MessageError box above for errors and plain muted text for status lines.
818
+ */
819
+ const DataNotice: FC<{ part: { readonly type: string; readonly name?: string; readonly data?: unknown } }> = ({
820
+ part,
821
+ }) => {
822
+ const notice = backendNoticeOf(part);
823
+ if (!notice) return null;
824
+ if (notice.tone === "status") {
825
+ return (
826
+ <p data-slot="chat-notice" data-tone="status" role="status" className="text-muted-foreground my-1 text-sm">
827
+ {notice.message}
828
+ </p>
829
+ );
830
+ }
831
+ return (
832
+ <div
833
+ data-slot="chat-notice"
834
+ data-tone="error"
835
+ role="alert"
836
+ className="aui-message-error-root border-destructive bg-destructive/10 text-destructive dark:bg-destructive/5 my-1 rounded-md border p-3 text-sm dark:text-red-200"
837
+ >
838
+ {notice.message}
839
+ </div>
840
+ );
841
+ };
842
+
843
+ const AssistantMessage: FC = () => {
844
+ const messageParts = useAuiState((s) => s.message.parts);
845
+ const hasContent = messageHasContent(messageParts);
846
+ // reserves space for action bar and compensates with `-mb` for consistent msg spacing
847
+ // keeps hovered action bar from shifting layout (autohide doesn't support absolute positioning well)
848
+ // for pt-[n] use -mb-[n + 6] & min-h-[n + 6] to preserve compensation
849
+ const ACTION_BAR_PT = "pt-1.5";
850
+ const ACTION_BAR_HEIGHT = `-mb-7.5 min-h-7.5 ${ACTION_BAR_PT}`;
851
+
852
+ return (
853
+ <MessagePrimitive.Root
854
+ data-slot="aui_assistant-message-root"
855
+ data-role="assistant"
856
+ className="fade-in slide-in-from-bottom-1 animate-in relative mx-auto w-full max-w-(--thread-max-width) duration-150"
857
+ >
858
+ <div
859
+ data-slot="aui_assistant-message-content"
860
+ className="text-foreground px-2 leading-relaxed wrap-break-word"
861
+ >
862
+ {/* The digichat skin's trace grouping and fold rules
863
+ (`trace-grouping`); only the look is the base skin's. */}
864
+ <AssistantTrace
865
+ renderPart={({ part }) => {
866
+ switch (part.type) {
867
+ case "text":
868
+ return <MarkdownText />;
869
+ case "indicator":
870
+ // One loading affordance per turn: once anything real has
871
+ // arrived the work chain takes over.
872
+ return hasContent ? null : <AssistantWorkingIndicator />;
873
+ case "data":
874
+ return part.dataRendererUI ?? <DataNotice part={part} />;
875
+ case "source":
876
+ // Citations are inline links in the answer.
877
+ return null;
878
+ default:
879
+ return null;
880
+ }
881
+ }}
882
+ />
883
+ <MessageError />
884
+ </div>
885
+
886
+ <div
887
+ data-slot="aui_assistant-message-footer"
888
+ className={cn("ml-2 flex items-center", ACTION_BAR_HEIGHT)}
889
+ >
890
+ <BranchPicker />
891
+ <AssistantActionBar />
892
+ </div>
893
+ </MessagePrimitive.Root>
894
+ );
895
+ };
896
+
897
+ const AssistantActionBar: FC = () => {
898
+ return (
899
+ <ActionBarPrimitive.Root
900
+ hideWhenRunning
901
+ autohide="not-last"
902
+ className="aui-assistant-action-bar-root text-muted-foreground animate-in fade-in col-start-3 row-start-2 -ml-1 flex gap-1 duration-200"
903
+ >
904
+ <ActionBarPrimitive.Copy asChild>
905
+ <TooltipIconButton tooltip="Copy">
906
+ <AuiIf condition={(s) => s.message.isCopied}>
907
+ <CheckIcon className="animate-in zoom-in-50 fade-in duration-200 ease-out" />
908
+ </AuiIf>
909
+ <AuiIf condition={(s) => !s.message.isCopied}>
910
+ <CopyIcon className="animate-in zoom-in-75 fade-in duration-150" />
911
+ </AuiIf>
912
+ </TooltipIconButton>
913
+ </ActionBarPrimitive.Copy>
914
+ <ActionBarPrimitive.Reload asChild>
915
+ <TooltipIconButton tooltip="Refresh">
916
+ <RefreshCwIcon />
917
+ </TooltipIconButton>
918
+ </ActionBarPrimitive.Reload>
919
+ <MessageExportMenu
920
+ trigger={
921
+ <TooltipIconButton tooltip="More" className="data-[state=open]:bg-muted">
922
+ <MoreHorizontalIcon />
923
+ </TooltipIconButton>
924
+ }
925
+ icon={<DownloadIcon className="size-4" />}
926
+ contentClassName="aui-action-bar-more-content bg-popover text-popover-foreground data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:animate-out data-[side=bottom]:slide-in-from-top-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 z-50 min-w-[8rem] overflow-hidden rounded-xl border p-1.5 shadow-md"
927
+ itemClassName="aui-action-bar-more-item hover:bg-muted hover:text-foreground focus:bg-muted focus:text-foreground data-[state=open]:bg-muted data-[state=open]:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset flex cursor-pointer items-center gap-2 rounded-lg px-2.5 py-1.5 text-sm outline-none select-none"
928
+ />
929
+ <MessageTiming />
930
+ </ActionBarPrimitive.Root>
931
+ );
932
+ };
933
+
934
+ const UserMessage: FC = () => {
935
+ return (
936
+ <MessagePrimitive.Root
937
+ data-slot="aui_user-message-root"
938
+ data-role="user"
939
+ className="fade-in slide-in-from-bottom-1 animate-in mx-auto grid w-full max-w-(--thread-max-width) auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] content-start gap-y-2 px-2 duration-150 [&:where(>*)]:col-start-2"
940
+ >
941
+ <UserMessageAttachments />
942
+
943
+ <div className="aui-user-message-content-wrapper relative col-start-2 min-w-0">
944
+ <div className="aui-user-message-content peer bg-muted text-foreground rounded-thread px-4 py-2 wrap-break-word empty:hidden">
945
+ <MessagePrimitive.Quote>
946
+ {(quote) => <QuoteBlock {...quote} />}
947
+ </MessagePrimitive.Quote>
948
+ <MessagePrimitive.Parts components={{ Text: DirectiveText }} />
949
+ </div>
950
+ <div className="aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2 peer-empty:hidden">
951
+ <UserActionBar />
952
+ </div>
953
+ </div>
954
+
955
+ <BranchPicker
956
+ data-slot="aui_user-branch-picker"
957
+ className="col-span-full col-start-1 row-start-3 -mr-1 justify-end"
958
+ />
959
+ </MessagePrimitive.Root>
960
+ );
961
+ };
962
+
963
+ const UserActionBar: FC = () => {
964
+ return (
965
+ <ActionBarPrimitive.Root
966
+ hideWhenRunning
967
+ autohide="not-last"
968
+ className="aui-user-action-bar-root flex flex-col items-end"
969
+ >
970
+ <ActionBarPrimitive.Edit asChild>
971
+ <TooltipIconButton tooltip="Edit" className="aui-user-action-edit">
972
+ <PencilIcon />
973
+ </TooltipIconButton>
974
+ </ActionBarPrimitive.Edit>
975
+ </ActionBarPrimitive.Root>
976
+ );
977
+ };
978
+
979
+ const EditComposer: FC = () => {
980
+ return (
981
+ <MessagePrimitive.Root
982
+ data-slot="aui_edit-composer-wrapper"
983
+ className="mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2"
984
+ >
985
+ <ComposerPrimitive.Unstable_TriggerPopoverRoot>
986
+ <ComposerPrimitive.Root className="aui-edit-composer-root border-border/60 dark:border-muted-foreground/15 ml-auto flex w-full max-w-[85%] cursor-text flex-col rounded-(--composer-radius) border bg-(--composer-bg)">
987
+ <LexicalComposerInput
988
+ directiveChip={DirectiveChip}
989
+ autoFocus
990
+ className="aui-edit-composer-input text-foreground min-h-14 w-full resize-none bg-transparent px-4 pt-3 pb-1 text-base outline-none [&_.aui-directive-chip]:inline-flex [&_.aui-directive-chip]:items-baseline [&_.aui-directive-chip]:gap-1 [&_.aui-directive-chip]:rounded-md [&_.aui-directive-chip]:bg-blue-100 [&_.aui-directive-chip]:px-1.5 [&_.aui-directive-chip]:py-0.5 [&_.aui-directive-chip]:text-[13px] [&_.aui-directive-chip]:leading-none [&_.aui-directive-chip]:font-medium [&_.aui-directive-chip]:text-blue-700 dark:[&_.aui-directive-chip]:bg-blue-900/50 dark:[&_.aui-directive-chip]:text-blue-300 [&_.aui-directive-chip-icon]:self-center [&_.aui-lexical-input]:min-h-lh [&_.aui-lexical-input]:outline-none"
991
+ />
992
+ <div className="aui-edit-composer-footer mx-2.5 mb-2.5 flex items-center gap-1.5 self-end">
993
+ <ComposerPrimitive.Cancel asChild>
994
+ <Button
995
+ variant="ghost"
996
+ size="sm"
997
+ className="h-8 rounded-full px-3.5"
998
+ >
999
+ Cancel
1000
+ </Button>
1001
+ </ComposerPrimitive.Cancel>
1002
+ <ComposerPrimitive.Send asChild>
1003
+ <Button size="sm" className="h-8 rounded-full px-3.5">
1004
+ Update
1005
+ </Button>
1006
+ </ComposerPrimitive.Send>
1007
+ </div>
1008
+ </ComposerPrimitive.Root>
1009
+ </ComposerPrimitive.Unstable_TriggerPopoverRoot>
1010
+ </MessagePrimitive.Root>
1011
+ );
1012
+ };
1013
+
1014
+ const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
1015
+ className,
1016
+ ...rest
1017
+ }) => {
1018
+ return (
1019
+ <BranchPickerPrimitive.Root
1020
+ hideWhenSingleBranch
1021
+ className={cn(
1022
+ "aui-branch-picker-root text-muted-foreground mr-2 -ml-2 inline-flex items-center text-xs",
1023
+ className,
1024
+ )}
1025
+ {...rest}
1026
+ >
1027
+ <BranchPickerPrimitive.Previous asChild>
1028
+ <TooltipIconButton tooltip="Previous">
1029
+ <ChevronLeftIcon />
1030
+ </TooltipIconButton>
1031
+ </BranchPickerPrimitive.Previous>
1032
+ <span className="aui-branch-picker-state font-medium">
1033
+ <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
1034
+ </span>
1035
+ <BranchPickerPrimitive.Next asChild>
1036
+ <TooltipIconButton tooltip="Next">
1037
+ <ChevronRightIcon />
1038
+ </TooltipIconButton>
1039
+ </BranchPickerPrimitive.Next>
1040
+ </BranchPickerPrimitive.Root>
1041
+ );
1042
+ };
1043
+
1044
+ export function BaseThread() {
1045
+ return <Thread />;
1046
+ }
1047
+
1048
+ export const Base: FC = () => {
1049
+ return <Thread />;
1050
+ };