@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,720 @@
1
+ "use client";
2
+
3
+ import {
4
+ useCallback,
5
+ useEffect,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ createContext,
10
+ useContext,
11
+ type ComponentPropsWithoutRef,
12
+ type ReactNode,
13
+ } from "react";
14
+ import { cva, type VariantProps } from "class-variance-authority";
15
+ import { CheckIcon, ChevronDownIcon } from "lucide-react";
16
+ import { cn } from "../../../../../lib/utils";
17
+ import {
18
+ Popover,
19
+ PopoverContent,
20
+ PopoverTrigger,
21
+ } from "../ui/popover";
22
+ import {
23
+ Command,
24
+ CommandEmpty,
25
+ CommandGroup,
26
+ CommandInput,
27
+ CommandItem,
28
+ CommandList,
29
+ CommandSeparator,
30
+ } from "../ui/command";
31
+ import { RadioGroup } from "@base-ui/react/radio-group";
32
+ import { Radio } from "@base-ui/react/radio";
33
+
34
+ export type ModelSelectorEffortOption = {
35
+ id: string;
36
+ name: string;
37
+ };
38
+
39
+ export const DEFAULT_EFFORT_OPTIONS: readonly ModelSelectorEffortOption[] = [
40
+ { id: "low", name: "Low" },
41
+ { id: "medium", name: "Med" },
42
+ { id: "high", name: "High" },
43
+ ];
44
+
45
+ export type ModelOption = {
46
+ id: string;
47
+ name: string;
48
+ description?: string;
49
+ icon?: ReactNode;
50
+ disabled?: boolean;
51
+ /** Extra terms matched by ModelSelector.Search, in addition to id and name. */
52
+ keywords?: readonly string[];
53
+ /**
54
+ * Reasoning effort levels the model supports. Pass `true` for the default
55
+ * low/medium/high levels, or a custom list. Omit for models without
56
+ * configurable reasoning.
57
+ */
58
+ efforts?: boolean | readonly ModelSelectorEffortOption[];
59
+ };
60
+
61
+ function getModelEfforts(
62
+ model: ModelOption | undefined,
63
+ ): readonly ModelSelectorEffortOption[] | undefined {
64
+ if (!model?.efforts) return undefined;
65
+ return model.efforts === true ? DEFAULT_EFFORT_OPTIONS : model.efforts;
66
+ }
67
+
68
+ function resolveEffort(
69
+ efforts: readonly ModelSelectorEffortOption[] | undefined,
70
+ effort: string | undefined,
71
+ ): string | undefined {
72
+ if (effort === undefined) return undefined;
73
+ return efforts?.some((e) => e.id === effort) ? effort : undefined;
74
+ }
75
+
76
+ /**
77
+ * Returns the effort id if the given model supports it, otherwise undefined.
78
+ * Effort selection is kept sticky across model switches; this resolves what
79
+ * actually applies to the current model.
80
+ */
81
+ export function resolveModelEffort(
82
+ models: readonly ModelOption[],
83
+ modelId: string | undefined,
84
+ effort: string | undefined,
85
+ ): string | undefined {
86
+ return resolveEffort(
87
+ getModelEfforts(models.find((m) => m.id === modelId)),
88
+ effort,
89
+ );
90
+ }
91
+
92
+ function useControllableState<T>({
93
+ prop,
94
+ defaultProp,
95
+ onChange,
96
+ }: {
97
+ prop: T | undefined;
98
+ defaultProp: T | undefined;
99
+ onChange: ((next: T) => void) | undefined;
100
+ }) {
101
+ const [internal, setInternal] = useState(defaultProp);
102
+ const isControlled = prop !== undefined;
103
+ const value = isControlled ? prop : internal;
104
+ // Read onChange through a ref so inline callbacks don't recreate the setter
105
+ // (and with it the memoized context value) every render.
106
+ const onChangeRef = useRef(onChange);
107
+ useEffect(() => {
108
+ onChangeRef.current = onChange;
109
+ });
110
+ const setValue = useCallback(
111
+ (next: T) => {
112
+ if (!isControlled) setInternal(next);
113
+ onChangeRef.current?.(next);
114
+ },
115
+ [isControlled],
116
+ );
117
+ return [value, setValue] as const;
118
+ }
119
+
120
+ type ModelSelectorContextValue = {
121
+ models: readonly ModelOption[];
122
+ value: string | undefined;
123
+ setValue: (value: string) => void;
124
+ /** The model matching `value`, derived once for all sub-components. */
125
+ selectedModel: ModelOption | undefined;
126
+ /** The selected model's effort levels, undefined when not configurable. */
127
+ efforts: readonly ModelSelectorEffortOption[] | undefined;
128
+ /** Effort resolved against the selected model's supported levels. */
129
+ effort: string | undefined;
130
+ setEffort: (effort: string) => void;
131
+ setOpen: (open: boolean) => void;
132
+ };
133
+
134
+ const ModelSelectorContext = createContext<ModelSelectorContextValue | null>(
135
+ null,
136
+ );
137
+
138
+ export function useModelSelectorContext() {
139
+ const ctx = useContext(ModelSelectorContext);
140
+ if (!ctx) {
141
+ throw new Error(
142
+ "ModelSelector sub-components must be used within ModelSelector.Root",
143
+ );
144
+ }
145
+ return ctx;
146
+ }
147
+
148
+ /**
149
+ * The selected model's effort levels and the active selection. Use it to build
150
+ * a custom effort UI inside ModelSelector.Content (e.g. a slider or a shadcn
151
+ * DropdownMenu) when the built-in ModelSelector.Effort layout doesn't fit.
152
+ * `efforts` is undefined for models without configurable reasoning.
153
+ */
154
+ export function useModelSelectorEfforts(): {
155
+ efforts: readonly ModelSelectorEffortOption[] | undefined;
156
+ effort: string | undefined;
157
+ setEffort: (effort: string) => void;
158
+ } {
159
+ const { efforts, effort, setEffort } = useModelSelectorContext();
160
+ return { efforts, effort, setEffort };
161
+ }
162
+
163
+ export type ModelSelectorRootProps = {
164
+ models: readonly ModelOption[];
165
+ value?: string;
166
+ defaultValue?: string;
167
+ onValueChange?: (value: string) => void;
168
+ effort?: string;
169
+ defaultEffort?: string;
170
+ onEffortChange?: (effort: string) => void;
171
+ open?: boolean;
172
+ defaultOpen?: boolean;
173
+ onOpenChange?: (open: boolean) => void;
174
+ children: ReactNode;
175
+ };
176
+
177
+ function ModelSelectorRoot({
178
+ models,
179
+ value: valueProp,
180
+ defaultValue,
181
+ onValueChange,
182
+ effort: effortProp,
183
+ defaultEffort,
184
+ onEffortChange,
185
+ open: openProp,
186
+ defaultOpen,
187
+ onOpenChange,
188
+ children,
189
+ }: ModelSelectorRootProps) {
190
+ const [value, setValue] = useControllableState({
191
+ prop: valueProp,
192
+ defaultProp: defaultValue ?? models[0]?.id,
193
+ onChange: onValueChange,
194
+ });
195
+ const [effort, setEffort] = useControllableState({
196
+ prop: effortProp,
197
+ defaultProp: defaultEffort,
198
+ onChange: onEffortChange,
199
+ });
200
+ const [open, setOpen] = useControllableState({
201
+ prop: openProp,
202
+ defaultProp: defaultOpen ?? false,
203
+ onChange: onOpenChange,
204
+ });
205
+
206
+ const selectedModel = models.find((m) => m.id === value);
207
+ const efforts = getModelEfforts(selectedModel);
208
+ const activeEffort = resolveEffort(efforts, effort);
209
+ const contextValue = useMemo(
210
+ () => ({
211
+ models,
212
+ value,
213
+ setValue,
214
+ selectedModel,
215
+ efforts,
216
+ effort: activeEffort,
217
+ setEffort,
218
+ setOpen,
219
+ }),
220
+ [
221
+ models,
222
+ value,
223
+ setValue,
224
+ selectedModel,
225
+ efforts,
226
+ activeEffort,
227
+ setEffort,
228
+ setOpen,
229
+ ],
230
+ );
231
+
232
+ return (
233
+ <ModelSelectorContext.Provider value={contextValue}>
234
+ <Popover open={open ?? false} onOpenChange={setOpen}>
235
+ {children}
236
+ </Popover>
237
+ </ModelSelectorContext.Provider>
238
+ );
239
+ }
240
+
241
+ export const modelSelectorTriggerVariants = cva(
242
+ "focus-visible:ring-ring/50 flex w-fit items-center justify-between gap-2 overflow-hidden rounded-md text-sm whitespace-nowrap transition-colors outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
243
+ {
244
+ variants: {
245
+ variant: {
246
+ outline:
247
+ "border-input hover:bg-muted hover:text-foreground border bg-transparent",
248
+ ghost: "hover:bg-muted hover:text-foreground",
249
+ muted: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
250
+ },
251
+ size: {
252
+ default: "h-9 px-3 py-2",
253
+ sm: "h-8 px-2.5 py-1.5 text-xs",
254
+ lg: "h-10 px-4 py-2.5",
255
+ },
256
+ },
257
+ defaultVariants: {
258
+ variant: "outline",
259
+ size: "default",
260
+ },
261
+ },
262
+ );
263
+
264
+ export type ModelSelectorTriggerProps = ComponentPropsWithoutRef<
265
+ typeof PopoverTrigger
266
+ > &
267
+ VariantProps<typeof modelSelectorTriggerVariants>;
268
+
269
+ function ModelSelectorTrigger({
270
+ className,
271
+ variant,
272
+ size,
273
+ children,
274
+ onKeyDown,
275
+ ...props
276
+ }: ModelSelectorTriggerProps) {
277
+ const { setOpen } = useModelSelectorContext();
278
+
279
+ return (
280
+ <PopoverTrigger
281
+ data-slot="model-selector-trigger"
282
+ data-variant={variant ?? "outline"}
283
+ data-size={size ?? "default"}
284
+ role="combobox"
285
+ aria-haspopup="listbox"
286
+ className={cn(modelSelectorTriggerVariants({ variant, size }), className)}
287
+ onKeyDown={(e) => {
288
+ onKeyDown?.(e);
289
+ if (e.defaultPrevented) return;
290
+ // ARIA combobox: arrows open the listbox from a focused trigger.
291
+ // Popover leaves this to the consumer.
292
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
293
+ e.preventDefault();
294
+ setOpen(true);
295
+ }
296
+ }}
297
+ {...props}
298
+ >
299
+ {children ?? <ModelSelectorValue />}
300
+ <ChevronDownIcon className="size-4 opacity-50" />
301
+ </PopoverTrigger>
302
+ );
303
+ }
304
+
305
+ export type ModelSelectorValueProps = {
306
+ placeholder?: ReactNode;
307
+ /** Show the active effort level next to the model name. */
308
+ showEffort?: boolean;
309
+ className?: string;
310
+ };
311
+
312
+ function ModelIcon({
313
+ children,
314
+ className,
315
+ }: {
316
+ children: ReactNode;
317
+ className?: string;
318
+ }) {
319
+ return (
320
+ <span
321
+ className={cn(
322
+ "flex size-3.5 shrink-0 items-center justify-center [&_svg]:size-3.5",
323
+ className,
324
+ )}
325
+ >
326
+ {children}
327
+ </span>
328
+ );
329
+ }
330
+
331
+ function ModelSelectorValue({
332
+ placeholder = "Select model",
333
+ showEffort = true,
334
+ className,
335
+ }: ModelSelectorValueProps) {
336
+ const { selectedModel, efforts, effort } = useModelSelectorContext();
337
+
338
+ if (!selectedModel) {
339
+ return (
340
+ <span
341
+ data-slot="model-selector-value"
342
+ className={cn("text-muted-foreground", className)}
343
+ >
344
+ {placeholder}
345
+ </span>
346
+ );
347
+ }
348
+
349
+ const effortName =
350
+ showEffort && effort !== undefined
351
+ ? efforts?.find((e) => e.id === effort)?.name
352
+ : undefined;
353
+
354
+ return (
355
+ <span
356
+ data-slot="model-selector-value"
357
+ className={cn("flex min-w-0 items-center gap-2", className)}
358
+ >
359
+ {selectedModel.icon && <ModelIcon>{selectedModel.icon}</ModelIcon>}
360
+ <span className="truncate font-medium">{selectedModel.name}</span>
361
+ {effortName && (
362
+ <span className="text-muted-foreground min-w-7.5 truncate text-center">
363
+ {effortName}
364
+ </span>
365
+ )}
366
+ </span>
367
+ );
368
+ }
369
+
370
+ export type ModelSelectorContentProps = Omit<
371
+ ComponentPropsWithoutRef<typeof PopoverContent>,
372
+ "side"
373
+ > & {
374
+ /**
375
+ * Preferred side for the initial placement. Once the popover is open, the
376
+ * rendered side takes over until it closes, so the popup does not jump
377
+ * between sides while filtering resizes the list.
378
+ */
379
+ side?: ComponentPropsWithoutRef<typeof PopoverContent>["side"];
380
+ searchable?: boolean;
381
+ };
382
+
383
+ // Base UI's Popover re-evaluates collision flipping whenever the popup
384
+ // resizes, so filtering the list down flips the popup back to the preferred
385
+ // side mid-interaction. Base UI only exposes its lazy-flip behavior on the
386
+ // Combobox positioner, so mirror it here: feed the rendered side back as the
387
+ // preferred side, making the popup keep its side until it no longer fits.
388
+ function useLazyFlipSide(): {
389
+ side: ModelSelectorContentProps["side"];
390
+ popupRef: (node: HTMLDivElement | null) => void;
391
+ } {
392
+ const [side, setSide] = useState<ModelSelectorContentProps["side"]>();
393
+ const observerRef = useRef<MutationObserver | null>(null);
394
+ const popupRef = useCallback((node: HTMLDivElement | null) => {
395
+ observerRef.current?.disconnect();
396
+ observerRef.current = null;
397
+ if (!node) {
398
+ setSide(undefined);
399
+ return;
400
+ }
401
+ const sync = () => {
402
+ const rendered = node.getAttribute("data-side");
403
+ if (rendered) setSide(rendered as ModelSelectorContentProps["side"]);
404
+ };
405
+ sync();
406
+ const observer = new MutationObserver(sync);
407
+ observer.observe(node, {
408
+ attributes: true,
409
+ attributeFilter: ["data-side"],
410
+ });
411
+ observerRef.current = observer;
412
+ }, []);
413
+ return { side, popupRef };
414
+ }
415
+
416
+ /**
417
+ * Hidden input that anchors cmdk's keyboard navigation, keeping the list
418
+ * keyboard-operable without a visible search box. ModelSelectorContent renders
419
+ * one automatically when unfiltered.
420
+ */
421
+ function ModelSelectorFocusAnchor() {
422
+ return (
423
+ <div className="sr-only">
424
+ <CommandInput readOnly aria-label="Model" />
425
+ </div>
426
+ );
427
+ }
428
+
429
+ function ModelSelectorContent({
430
+ className,
431
+ align = "start",
432
+ side,
433
+ sideOffset = 6,
434
+ searchable,
435
+ children,
436
+ ...props
437
+ }: ModelSelectorContentProps) {
438
+ const { value } = useModelSelectorContext();
439
+ const { side: renderedSide, popupRef } = useLazyFlipSide();
440
+ const unfiltered =
441
+ searchable === false || (!searchable && children === undefined);
442
+
443
+ return (
444
+ <PopoverContent
445
+ ref={popupRef}
446
+ data-slot="model-selector-content"
447
+ align={align}
448
+ side={renderedSide ?? side ?? "bottom"}
449
+ sideOffset={sideOffset}
450
+ className={cn(
451
+ "bg-popover w-72 min-w-(--anchor-width) overflow-hidden rounded-xl p-0",
452
+ className,
453
+ )}
454
+ {...props}
455
+ >
456
+ <Command
457
+ className="bg-transparent"
458
+ shouldFilter={!unfiltered}
459
+ {...(value !== undefined ? { defaultValue: value } : {})}
460
+ >
461
+ {unfiltered && <ModelSelectorFocusAnchor />}
462
+ {children ?? (
463
+ <>
464
+ {searchable && <ModelSelectorSearch />}
465
+ <ModelSelectorList />
466
+ <ModelSelectorEffort />
467
+ </>
468
+ )}
469
+ </Command>
470
+ </PopoverContent>
471
+ );
472
+ }
473
+
474
+ export type ModelSelectorSearchProps = ComponentPropsWithoutRef<
475
+ typeof CommandInput
476
+ >;
477
+
478
+ function ModelSelectorSearch({
479
+ placeholder = "Search models...",
480
+ ...props
481
+ }: ModelSelectorSearchProps) {
482
+ return (
483
+ <CommandInput
484
+ data-slot="model-selector-search"
485
+ placeholder={placeholder}
486
+ {...props}
487
+ />
488
+ );
489
+ }
490
+
491
+ export type ModelSelectorListProps = ComponentPropsWithoutRef<
492
+ typeof CommandList
493
+ >;
494
+
495
+ function ModelSelectorList({
496
+ className,
497
+ children,
498
+ ...props
499
+ }: ModelSelectorListProps) {
500
+ const { models } = useModelSelectorContext();
501
+
502
+ return (
503
+ <CommandList
504
+ data-slot="model-selector-list"
505
+ className={cn(
506
+ "[-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
507
+ className,
508
+ )}
509
+ {...props}
510
+ >
511
+ {children ?? (
512
+ <>
513
+ <ModelSelectorEmpty />
514
+ <CommandGroup>
515
+ {models.map((model) => (
516
+ <ModelSelectorItem key={model.id} model={model} />
517
+ ))}
518
+ </CommandGroup>
519
+ </>
520
+ )}
521
+ </CommandList>
522
+ );
523
+ }
524
+
525
+ export type ModelSelectorEmptyProps = ComponentPropsWithoutRef<
526
+ typeof CommandEmpty
527
+ >;
528
+
529
+ function ModelSelectorEmpty({ children, ...props }: ModelSelectorEmptyProps) {
530
+ return (
531
+ <CommandEmpty data-slot="model-selector-empty" {...props}>
532
+ {children ?? "No models found."}
533
+ </CommandEmpty>
534
+ );
535
+ }
536
+
537
+ export type ModelSelectorGroupProps = ComponentPropsWithoutRef<
538
+ typeof CommandGroup
539
+ >;
540
+
541
+ function ModelSelectorGroup(props: ModelSelectorGroupProps) {
542
+ return <CommandGroup data-slot="model-selector-group" {...props} />;
543
+ }
544
+
545
+ export type ModelSelectorSeparatorProps = ComponentPropsWithoutRef<
546
+ typeof CommandSeparator
547
+ >;
548
+
549
+ function ModelSelectorSeparator(props: ModelSelectorSeparatorProps) {
550
+ return <CommandSeparator data-slot="model-selector-separator" {...props} />;
551
+ }
552
+
553
+ export type ModelSelectorItemProps = Omit<
554
+ ComponentPropsWithoutRef<typeof CommandItem>,
555
+ "value"
556
+ > & {
557
+ model: ModelOption;
558
+ };
559
+
560
+ function ModelSelectorItem({
561
+ model,
562
+ className,
563
+ children,
564
+ onSelect,
565
+ ...props
566
+ }: ModelSelectorItemProps) {
567
+ const { value, setValue, setOpen } = useModelSelectorContext();
568
+ const isSelected = value === model.id;
569
+
570
+ return (
571
+ <CommandItem
572
+ data-slot="model-selector-item"
573
+ value={model.id}
574
+ keywords={[model.name, ...(model.keywords ?? [])]}
575
+ {...(model.disabled ? { disabled: true } : undefined)}
576
+ onSelect={(selectedValue) => {
577
+ setValue(model.id);
578
+ setOpen(false);
579
+ onSelect?.(selectedValue);
580
+ }}
581
+ className={cn(
582
+ "relative items-start gap-2 rounded-lg py-2 ps-3 pe-9 [&_svg:not([class*='size-'])]:size-3.5",
583
+ className,
584
+ )}
585
+ {...props}
586
+ >
587
+ {children ?? (
588
+ <>
589
+ {model.icon && (
590
+ <ModelIcon className="mt-[3px]">{model.icon}</ModelIcon>
591
+ )}
592
+ <span className="flex min-w-0 flex-col">
593
+ <span className="truncate font-medium">{model.name}</span>
594
+ {model.description && (
595
+ <span className="text-muted-foreground truncate text-xs">
596
+ {model.description}
597
+ </span>
598
+ )}
599
+ </span>
600
+ </>
601
+ )}
602
+ {isSelected && (
603
+ <span className="absolute end-3 top-2.5 flex size-4 items-center justify-center">
604
+ <CheckIcon className="size-4" />
605
+ </span>
606
+ )}
607
+ </CommandItem>
608
+ );
609
+ }
610
+
611
+ export type ModelSelectorEffortProps = ComponentPropsWithoutRef<"div"> & {
612
+ label?: ReactNode;
613
+ };
614
+
615
+ function ModelSelectorEffort({
616
+ label = "Thinking",
617
+ className,
618
+ onKeyDown,
619
+ onKeyDownCapture,
620
+ ...props
621
+ }: ModelSelectorEffortProps) {
622
+ const { efforts, effort, setEffort } = useModelSelectorEfforts();
623
+
624
+ if (!efforts?.length) return null;
625
+
626
+ return (
627
+ <div
628
+ data-slot="model-selector-effort"
629
+ className={cn(
630
+ "flex cursor-default items-center justify-between gap-3 border-t px-3 py-2",
631
+ className,
632
+ )}
633
+ onKeyDownCapture={(e) => {
634
+ onKeyDownCapture?.(e);
635
+ if (e.defaultPrevented) return;
636
+ if (e.key !== "ArrowUp" && e.key !== "ArrowDown") return;
637
+ // Base UI's RadioGroup composite claims vertical arrows for roving
638
+ // focus (orientation "both", not configurable), so intercept them in
639
+ // capture and hand the keypress to cmdk: the model list owns vertical
640
+ // navigation, and cmdk's Enter is inert while a radio has focus.
641
+ onKeyDown?.(e);
642
+ if (e.defaultPrevented) return;
643
+ const input = e.currentTarget
644
+ .closest("[cmdk-root]")
645
+ ?.querySelector<HTMLInputElement>("[cmdk-input]");
646
+ if (!input) return;
647
+ e.preventDefault();
648
+ e.stopPropagation();
649
+ input.focus();
650
+ input.dispatchEvent(new KeyboardEvent("keydown", e.nativeEvent));
651
+ }}
652
+ onKeyDown={(e) => {
653
+ if (e.key === "ArrowUp" || e.key === "ArrowDown") return;
654
+ onKeyDown?.(e);
655
+ if (e.defaultPrevented) return;
656
+ // Base UI's radio composite ignores Home/End and cmdk's Command
657
+ // root would claim them to jump the model list; move radio focus
658
+ // here so only the radiogroup reacts.
659
+ if (e.key === "Home" || e.key === "End") {
660
+ e.preventDefault();
661
+ e.stopPropagation();
662
+ const radios = Array.from(
663
+ e.currentTarget.querySelectorAll<HTMLElement>(
664
+ '[role="radio"]:not([data-disabled])',
665
+ ),
666
+ );
667
+ (e.key === "Home" ? radios[0] : radios[radios.length - 1])?.focus();
668
+ }
669
+ }}
670
+ {...props}
671
+ >
672
+ <span className="text-muted-foreground text-xs">{label}</span>
673
+ <RadioGroup
674
+ value={effort ?? ""}
675
+ onValueChange={setEffort}
676
+ aria-label={typeof label === "string" ? label : "Reasoning effort"}
677
+ className="flex items-center gap-0.5"
678
+ >
679
+ {efforts.map((option) => (
680
+ <Radio.Root
681
+ key={option.id}
682
+ value={option.id}
683
+ className={cn(
684
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded-md px-2 py-1 text-xs transition-colors outline-none",
685
+ "data-checked:bg-muted data-checked:text-foreground data-checked:font-medium",
686
+ )}
687
+ >
688
+ {option.name}
689
+ </Radio.Root>
690
+ ))}
691
+ </RadioGroup>
692
+ </div>
693
+ );
694
+ }
695
+
696
+ export type ModelSelectorProps = Omit<ModelSelectorRootProps, "children"> &
697
+ VariantProps<typeof modelSelectorTriggerVariants> & {
698
+ /** Render a search input above the model list. */
699
+ searchable?: boolean;
700
+ /** Alignment of the dropdown relative to the trigger. Use `"end"` when the
701
+ * trigger sits at the right edge of its container. */
702
+ align?: ModelSelectorContentProps["align"];
703
+ className?: string;
704
+ contentClassName?: string;
705
+ };
706
+
707
+ export {
708
+ ModelSelectorRoot,
709
+ ModelSelectorTrigger,
710
+ ModelSelectorValue,
711
+ ModelSelectorContent,
712
+ ModelSelectorSearch,
713
+ ModelSelectorFocusAnchor,
714
+ ModelSelectorList,
715
+ ModelSelectorEmpty,
716
+ ModelSelectorGroup,
717
+ ModelSelectorSeparator,
718
+ ModelSelectorItem,
719
+ ModelSelectorEffort,
720
+ };