@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,380 @@
1
+ "use client";
2
+
3
+ import {
4
+ ActionBarPrimitive,
5
+ AuiIf,
6
+ AttachmentPrimitive,
7
+ ComposerPrimitive,
8
+ MessagePrimitive,
9
+ ThreadPrimitive,
10
+ useAuiState,
11
+ } from "@assistant-ui/react";
12
+ import {
13
+ ArrowUpIcon,
14
+ AudioLines,
15
+ Calendar as CalendarIcon,
16
+ CheckIcon,
17
+ ChevronDownIcon,
18
+ ClipboardIcon,
19
+ Code as CodeIcon,
20
+ Download,
21
+ FolderOpen,
22
+ GraduationCap,
23
+ MoreHorizontal,
24
+ PencilIcon,
25
+ PenLine,
26
+ PlusIcon,
27
+ RefreshCwIcon,
28
+ Sparkle,
29
+ ThumbsDown,
30
+ ThumbsUp,
31
+ XIcon,
32
+ } from "lucide-react";
33
+ import { useState, type FC } from "react";
34
+ import { useAttachmentSrc } from "../stock/use-attachment-src";
35
+ import { MarkdownText } from "../stock/markdown-text";
36
+ import { MessageExportMenu } from "../stock/message-export-menu";
37
+ import { SkinCredit, useComposerCopy } from "../stock";
38
+ import {
39
+ DropdownMenu,
40
+ DropdownMenuContent,
41
+ DropdownMenuItem,
42
+ DropdownMenuSeparator,
43
+ DropdownMenuTrigger,
44
+ } from "./ui/dropdown-menu";
45
+
46
+
47
+ const messageActionButtonClassName =
48
+ "flex size-8 items-center justify-center rounded-md text-[#5b5950] transition-colors hover:bg-[#1a1a18]/5 hover:text-[#1a1a18] dark:text-[#a3a098] dark:hover:bg-white/5 dark:hover:text-[#eee]";
49
+
50
+ export const Claude: FC = () => {
51
+ return (
52
+ <ThreadPrimitive.Root data-export-thread="" className="flex h-full flex-col items-stretch bg-[#F0ECE0] font-serif text-[#1a1a18] dark:bg-[#2b2a27] dark:text-[#eee]">
53
+ <AuiIf condition={(s) => s.thread.isEmpty}>
54
+ <EmptyState />
55
+ </AuiIf>
56
+
57
+ <AuiIf condition={(s) => !s.thread.isEmpty}>
58
+ <ThreadPrimitive.Viewport className="flex grow flex-col overflow-y-auto px-4 pt-12">
59
+ <ThreadPrimitive.Messages>
60
+ {() => <ChatMessage />}
61
+ </ThreadPrimitive.Messages>
62
+
63
+ <ThreadPrimitive.ViewportFooter className="sticky bottom-0 mx-auto mt-auto w-full max-w-3xl bg-linear-to-b from-transparent via-[#F0ECE0]/85 to-[#F0ECE0] pt-4 pb-1 dark:via-[#2b2a27]/85 dark:to-[#2b2a27]">
64
+ <Composer />
65
+ <SkinCredit />
66
+ </ThreadPrimitive.ViewportFooter>
67
+ </ThreadPrimitive.Viewport>
68
+ </AuiIf>
69
+ </ThreadPrimitive.Root>
70
+ );
71
+ };
72
+
73
+ const EmptyState: FC = () => {
74
+ const { welcome } = useComposerCopy(
75
+ "How can I help you today?",
76
+ "How can I help you today?",
77
+ );
78
+ return (
79
+ <div className="relative flex grow flex-col items-center justify-center px-4">
80
+ <div className="mx-auto flex w-full max-w-2xl flex-col items-stretch gap-5">
81
+ <h1 className="flex items-center justify-center gap-3 font-serif text-3xl text-[#1a1a18] sm:text-4xl dark:text-[#eee]">
82
+ <Sparkle className="size-7 fill-[#c96442] text-[#c96442]" />
83
+ <span>{welcome}</span>
84
+ </h1>
85
+ <Composer />
86
+ <ModeTabs />
87
+ </div>
88
+ <div className="pointer-events-none absolute inset-x-0 bottom-0 px-4 pb-2">
89
+ <SkinCredit />
90
+ </div>
91
+ </div>
92
+ );
93
+ };
94
+
95
+ const Composer: FC = () => {
96
+ const { placeholder } = useComposerCopy(
97
+ "How can I help you today?",
98
+ "How can I help you today?",
99
+ );
100
+ return (
101
+ <ComposerPrimitive.Root data-export-chrome="" className="flex w-full flex-col gap-2 rounded-2xl border border-[#E5E0D6] bg-white px-3.5 pt-3 pb-2.5 dark:border-[#3d3a35] dark:bg-[#1f1e1b]">
102
+ <ComposerPrimitive.Input
103
+ placeholder={placeholder}
104
+ rows={1}
105
+ className="block max-h-72 min-h-6 w-full resize-none bg-transparent text-[#1a1a18] outline-none placeholder:text-[#9a9893] dark:text-[#eee] dark:placeholder:text-[#9a9893]"
106
+ />
107
+
108
+ <div className="flex w-full items-center gap-2">
109
+ <ComposerPrimitive.AddAttachment
110
+ aria-label="Add attachment"
111
+ className="flex size-8 shrink-0 items-center justify-center rounded-md text-[#5b5950] transition-colors hover:bg-[#1a1a18]/5 hover:text-[#1a1a18] dark:text-[#a3a098] dark:hover:bg-white/5 dark:hover:text-[#eee]"
112
+ >
113
+ <PlusIcon width={16} height={16} />
114
+ </ComposerPrimitive.AddAttachment>
115
+
116
+ <div className="ml-auto flex items-center gap-1">
117
+ <ClaudeModelPicker />
118
+ <ComposerPrimaryAction />
119
+ </div>
120
+ </div>
121
+
122
+ <AuiIf condition={(s) => s.composer.attachments.length > 0}>
123
+ <div className="-mx-1 -mb-1 flex flex-row gap-2 overflow-x-auto pt-1">
124
+ <ComposerPrimitive.Attachments>
125
+ {() => <ClaudeAttachment />}
126
+ </ComposerPrimitive.Attachments>
127
+ </div>
128
+ </AuiIf>
129
+ </ComposerPrimitive.Root>
130
+ );
131
+ };
132
+
133
+ const ComposerPrimaryAction: FC = () => {
134
+ return (
135
+ <>
136
+ <AuiIf condition={(s) => s.thread.isRunning}>
137
+ <ComposerPrimitive.Cancel className="flex size-8 items-center justify-center rounded-md bg-[#c96442] text-white transition-colors hover:bg-[#b1573a]">
138
+ <div className="size-2.5 rounded-[2px] bg-current" />
139
+ </ComposerPrimitive.Cancel>
140
+ </AuiIf>
141
+
142
+ <AuiIf
143
+ condition={(s) => !s.thread.isRunning && s.composer.dictation != null}
144
+ >
145
+ <ComposerPrimitive.StopDictation
146
+ className="flex size-8 items-center justify-center rounded-md bg-[#c96442] text-white transition-colors hover:bg-[#b1573a]"
147
+ aria-label="Stop dictation"
148
+ >
149
+ <div className="size-2.5 animate-pulse rounded-[2px] bg-current" />
150
+ </ComposerPrimitive.StopDictation>
151
+ </AuiIf>
152
+
153
+ <AuiIf
154
+ condition={(s) =>
155
+ !s.thread.isRunning &&
156
+ s.composer.dictation == null &&
157
+ !s.composer.isEmpty
158
+ }
159
+ >
160
+ <ComposerPrimitive.Send className="flex size-8 items-center justify-center rounded-md bg-[#c96442] text-white transition-colors hover:bg-[#b1573a] disabled:pointer-events-none disabled:opacity-50">
161
+ <ArrowUpIcon width={16} height={16} />
162
+ </ComposerPrimitive.Send>
163
+ </AuiIf>
164
+
165
+ <AuiIf
166
+ condition={(s) =>
167
+ !s.thread.isRunning &&
168
+ s.composer.dictation == null &&
169
+ s.composer.isEmpty
170
+ }
171
+ >
172
+ <ComposerPrimitive.Dictate
173
+ className="flex size-8 items-center justify-center rounded-md text-[#5b5950] transition-colors hover:bg-[#1a1a18]/5 hover:text-[#1a1a18] dark:text-[#a3a098] dark:hover:bg-white/5 dark:hover:text-[#eee]"
174
+ aria-label="Use voice mode"
175
+ >
176
+ <AudioLines className="size-4" />
177
+ </ComposerPrimitive.Dictate>
178
+ </AuiIf>
179
+ </>
180
+ );
181
+ };
182
+
183
+ const CLAUDE_MODELS = [
184
+ {
185
+ id: "sonnet-4.5",
186
+ name: "Sonnet 4.5",
187
+ description: "Smart, fast, everyday tasks",
188
+ },
189
+ {
190
+ id: "opus-4.7",
191
+ name: "Opus 4.7",
192
+ description: "Anthropic's most capable model",
193
+ },
194
+ {
195
+ id: "haiku-4.5",
196
+ name: "Haiku 4.5",
197
+ description: "Fastest, most affordable",
198
+ },
199
+ ];
200
+
201
+ const ClaudeModelPicker: FC = () => {
202
+ const [model, setModel] = useState(CLAUDE_MODELS[0]!.id);
203
+
204
+ return (
205
+ <div data-deploy-model-picker className="contents">
206
+ <DropdownMenu>
207
+ <DropdownMenuTrigger className="flex h-8 items-center gap-1 rounded-md px-2.5 text-sm whitespace-nowrap text-[#1a1a18] transition hover:bg-[#1a1a18]/5 dark:text-[#eee] dark:hover:bg-white/5">
208
+ <span className="font-serif">
209
+ {CLAUDE_MODELS.find((m) => m.id === model)?.name}
210
+ </span>
211
+ <ChevronDownIcon width={16} height={16} className="opacity-60" />
212
+ </DropdownMenuTrigger>
213
+ <DropdownMenuContent align="end" className="min-w-64">
214
+ {CLAUDE_MODELS.map((m) => (
215
+ <DropdownMenuItem
216
+ key={m.id}
217
+ onClick={() => setModel(m.id)}
218
+ className="flex items-start gap-3"
219
+ >
220
+ <span className="mt-0.5 flex size-4 items-center justify-center text-[#c96442]">
221
+ {m.id === model ? <CheckIcon /> : null}
222
+ </span>
223
+ <span className="flex flex-1 flex-col">
224
+ <span className="text-foreground font-serif text-sm">
225
+ {m.name}
226
+ </span>
227
+ <span className="text-muted-foreground text-xs">
228
+ {m.description}
229
+ </span>
230
+ </span>
231
+ </DropdownMenuItem>
232
+ ))}
233
+ <DropdownMenuSeparator />
234
+ <DropdownMenuItem className="text-muted-foreground text-sm">
235
+ More options
236
+ </DropdownMenuItem>
237
+ </DropdownMenuContent>
238
+ </DropdownMenu>
239
+ </div>
240
+ );
241
+ };
242
+
243
+ const ModeTabs: FC = () => {
244
+ const tabs = [
245
+ { label: "Write", Icon: PenLine },
246
+ { label: "Learn", Icon: GraduationCap },
247
+ { label: "Code", Icon: CodeIcon },
248
+ { label: "From Drive", Icon: FolderOpen },
249
+ { label: "From Calendar", Icon: CalendarIcon },
250
+ ];
251
+
252
+ return (
253
+ <div className="flex flex-wrap items-center justify-center gap-2">
254
+ {tabs.map(({ label, Icon }) => (
255
+ <button
256
+ key={label}
257
+ type="button"
258
+ aria-hidden="true"
259
+ tabIndex={-1}
260
+ className="flex h-8 items-center gap-1.5 rounded-lg border border-[#E5E0D6] bg-transparent px-3 text-sm whitespace-nowrap text-[#3d3a35] transition-colors hover:bg-white/60 dark:border-[#3d3a35] dark:text-[#cdc9be] dark:hover:bg-[#1f1e1b]/60"
261
+ >
262
+ <Icon className="size-3.5 text-[#8a8780] dark:text-[#a3a098]" />
263
+ <span className="font-serif">{label}</span>
264
+ </button>
265
+ ))}
266
+ </div>
267
+ );
268
+ };
269
+
270
+ const ChatMessage: FC = () => {
271
+ const role = useAuiState((s) => s.message.role);
272
+ return (
273
+ <MessagePrimitive.Root data-role={role} className="group/message relative mx-auto flex w-full max-w-3xl flex-col py-2">
274
+ <AuiIf condition={(s) => s.message.role === "user"}>
275
+ <div className="flex flex-col items-end gap-1">
276
+ <div className="max-w-[80%] rounded-2xl bg-[#E5E0D6] px-4 py-2.5 wrap-break-word whitespace-pre-wrap text-[#1a1a18] dark:bg-[#393937] dark:text-[#eee]">
277
+ <MessagePrimitive.Parts>
278
+ {({ part }) => {
279
+ if (part.type === "text") return <MarkdownText />;
280
+ return null;
281
+ }}
282
+ </MessagePrimitive.Parts>
283
+ </div>
284
+ <ActionBarPrimitive.Root data-export-chrome="" className="-mt-px flex items-center gap-0.5 opacity-0 transition-opacity group-focus-within/message:opacity-100 group-hover/message:opacity-100">
285
+ <ActionBarPrimitive.Edit className={messageActionButtonClassName}>
286
+ <PencilIcon width={16} height={16} />
287
+ </ActionBarPrimitive.Edit>
288
+ <ActionBarPrimitive.Copy className={messageActionButtonClassName}>
289
+ <AuiIf condition={(s) => s.message.isCopied}>
290
+ <CheckIcon />
291
+ </AuiIf>
292
+ <AuiIf condition={(s) => !s.message.isCopied}>
293
+ <ClipboardIcon width={16} height={16} />
294
+ </AuiIf>
295
+ </ActionBarPrimitive.Copy>
296
+ </ActionBarPrimitive.Root>
297
+ </div>
298
+ </AuiIf>
299
+
300
+ <AuiIf condition={(s) => s.message.role === "assistant"}>
301
+ <div className="flex flex-col">
302
+ <div className="prose prose-claude font-serif leading-[1.65rem] wrap-break-word text-[#1a1a18] dark:text-[#eee]">
303
+ <MessagePrimitive.Parts>
304
+ {({ part }) => {
305
+ if (part.type === "text") return <MarkdownText />;
306
+ return null;
307
+ }}
308
+ </MessagePrimitive.Parts>
309
+ </div>
310
+ <ActionBarPrimitive.Root data-export-chrome="" className="mt-2 flex items-center gap-0.5 opacity-0 transition-opacity group-focus-within/message:opacity-100 group-hover/message:opacity-100">
311
+ <ActionBarPrimitive.Copy className={messageActionButtonClassName}>
312
+ <AuiIf condition={(s) => s.message.isCopied}>
313
+ <CheckIcon />
314
+ </AuiIf>
315
+ <AuiIf condition={(s) => !s.message.isCopied}>
316
+ <ClipboardIcon width={16} height={16} />
317
+ </AuiIf>
318
+ </ActionBarPrimitive.Copy>
319
+ <ActionBarPrimitive.FeedbackPositive
320
+ className={messageActionButtonClassName}
321
+ >
322
+ <ThumbsUp className="size-4" />
323
+ </ActionBarPrimitive.FeedbackPositive>
324
+ <ActionBarPrimitive.FeedbackNegative
325
+ className={messageActionButtonClassName}
326
+ >
327
+ <ThumbsDown className="size-4" />
328
+ </ActionBarPrimitive.FeedbackNegative>
329
+ <ActionBarPrimitive.Reload className={messageActionButtonClassName}>
330
+ <RefreshCwIcon width={16} height={16} />
331
+ </ActionBarPrimitive.Reload>
332
+ <MessageExportMenu
333
+ trigger={
334
+ <button type="button" aria-label="More" className={messageActionButtonClassName}>
335
+ <MoreHorizontal width={16} height={16} />
336
+ </button>
337
+ }
338
+ icon={<Download className="size-4" />}
339
+ align="start"
340
+ contentClassName="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 z-50 min-w-40 overflow-hidden rounded-xl border p-1.5 shadow-md"
341
+ itemClassName="text-muted-foreground focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-pointer items-center gap-2.5 rounded-lg px-3 py-2 text-sm outline-none select-none"
342
+ />
343
+ </ActionBarPrimitive.Root>
344
+ </div>
345
+ </AuiIf>
346
+ </MessagePrimitive.Root>
347
+ );
348
+ };
349
+
350
+ const ClaudeAttachment: FC = () => {
351
+ const isImage = useAuiState((s) => s.attachment.type === "image");
352
+ const src = useAttachmentSrc();
353
+
354
+ return (
355
+ <AttachmentPrimitive.Root className="group/thumbnail relative">
356
+ <div
357
+ className="overflow-hidden rounded-lg border border-[#E5E0D6] dark:border-[#3d3a35]"
358
+ style={{ width: "80px", height: "80px" }}
359
+ >
360
+ {isImage && src ? (
361
+ <img
362
+ className="h-full w-full object-cover"
363
+ alt="Attachment"
364
+ src={src}
365
+ />
366
+ ) : (
367
+ <div className="flex h-full w-full items-center justify-center bg-white text-[#5b5950] dark:bg-[#2b2a27] dark:text-[#a3a098]">
368
+ <AttachmentPrimitive.unstable_Thumb className="text-xs" />
369
+ </div>
370
+ )}
371
+ </div>
372
+ <AttachmentPrimitive.Remove
373
+ className="absolute -top-1.5 -right-1.5 flex size-5 items-center justify-center rounded-full bg-[#1a1a18] text-white opacity-0 transition-opacity group-focus-within/thumbnail:opacity-100 group-hover/thumbnail:opacity-100 hover:bg-[#3d3a35] dark:bg-white dark:text-[#1a1a18] dark:hover:bg-[#cdc9be]"
374
+ aria-label="Remove attachment"
375
+ >
376
+ <XIcon width={12} height={12} />
377
+ </AttachmentPrimitive.Remove>
378
+ </AttachmentPrimitive.Root>
379
+ );
380
+ };
@@ -0,0 +1,151 @@
1
+ "use client";
2
+
3
+ import { Fragment, type ReactNode } from "react";
4
+ import type { EmbedChatPrefsApi } from "../stock/embed-chat-prefs";
5
+ import { connectedToolIsOn, countToolSurface } from "../stock/tool-surface";
6
+ import { DigichatContextMeter } from "./digichat-context-meter";
7
+ import { DigichatUsageMeter } from "./digichat-usage-meter";
8
+
9
+ /**
10
+ * Plain-text composer footer for the digichat skin. Shows the selected
11
+ * model and the active backend on the left and the context fill on the
12
+ * right. Clicking the model opens the full settings menu — that is the only
13
+ * control here; the settings button and the per-control chips are gone.
14
+ *
15
+ * The backend chip is a backend indicator, so it appears only where BYOK is
16
+ * on the table (`showByok`) and the host named the backend that serves the
17
+ * chat (`providerLabel`). The label is the host's: this package holds no
18
+ * provider vocabulary, and a host that cannot name the backend shows no
19
+ * chip rather than a wrong one.
20
+ */
21
+ export function DigichatComposerToolbar({ prefs }: { prefs: EmbedChatPrefsApi }) {
22
+ const sessionModel = prefs.prefs.model.trim();
23
+ // The chip names the session model. The bar sizes to the model the turn
24
+ // sends (`meterModel`), including a bring-your-own-key binding.
25
+ const modelLabel = sessionModel || "auto";
26
+ const meterModel = prefs.meterModel?.trim() || sessionModel;
27
+ const providerLabel = prefs.providerLabel?.trim() ?? "";
28
+ // The backend is named whenever the host named one — a Foundry tenant is
29
+ // served by Azure AI Foundry, not by the house model provider. It is a
30
+ // button only where the tenant can switch it.
31
+ const showProvider = providerLabel.length > 0;
32
+
33
+ // Connected MCP servers and the tools inside them: one ticket server with
34
+ // five tools reads "1 MCP / 5 tools", not "5 MCPs". The rows come from the
35
+ // same expansion `/tools` renders (`../stock/tool-surface`), so the count
36
+ // and the list behind it cannot disagree. Per-tool toggles live in
37
+ // prefs.extra (tool id -> off); a row follows its own switch.
38
+ const toolSwitch = {
39
+ digisearch: prefs.hasDigisearch,
40
+ vault: prefs.hasVault,
41
+ webSearch: prefs.tenantAllowsWeb,
42
+ extraToolOn: prefs.extraToolOn,
43
+ };
44
+ const mcpCounts = countToolSurface(prefs.mcpServers, {
45
+ isOn: (id) => connectedToolIsOn(id, toolSwitch),
46
+ isToolOn: prefs.extraToolOn,
47
+ });
48
+ const mcpToolTotal = mcpCounts.tools;
49
+
50
+ // The row is a list of facts separated by slashes. Build the list and
51
+ // join it, so a tenant that has no model (or no provider) never leaves a
52
+ // dangling separator at the front.
53
+ const items: ReactNode[] = [];
54
+
55
+ if (prefs.showModels) {
56
+ items.push(
57
+ <button
58
+ key="model"
59
+ type="button"
60
+ className="aui-composer-meta aui-composer-meta-action"
61
+ onClick={prefs.openModels}
62
+ aria-label="Open models"
63
+ >
64
+ {modelLabel}
65
+ </button>,
66
+ );
67
+ }
68
+
69
+ if (showProvider) {
70
+ items.push(
71
+ prefs.showByok ? (
72
+ <button
73
+ key="provider"
74
+ type="button"
75
+ className="aui-composer-meta aui-composer-meta-action"
76
+ onClick={() => prefs.openByok()}
77
+ aria-label="Open provider menu"
78
+ >
79
+ {providerLabel}
80
+ </button>
81
+ ) : (
82
+ <span key="provider" className="aui-composer-meta">
83
+ {providerLabel}
84
+ </span>
85
+ ),
86
+ );
87
+ }
88
+
89
+ if (mcpCounts.servers > 0) {
90
+ items.push(
91
+ <button
92
+ key="mcp"
93
+ type="button"
94
+ className="aui-composer-meta aui-composer-meta-action"
95
+ onClick={() => prefs.openMcp()}
96
+ aria-label="Open MCP servers"
97
+ >
98
+ {mcpCounts.servers} MCP{mcpCounts.servers === 1 ? "" : "s"}
99
+ </button>,
100
+ <button
101
+ key="tools"
102
+ type="button"
103
+ className="aui-composer-meta aui-composer-meta-action"
104
+ onClick={() => prefs.openTools()}
105
+ aria-label="Open tools"
106
+ >
107
+ {mcpCounts.tools} tool{mcpCounts.tools === 1 ? "" : "s"}
108
+ </button>,
109
+ );
110
+ }
111
+
112
+ items.push(
113
+ <button
114
+ key="language"
115
+ type="button"
116
+ className="aui-composer-meta aui-composer-meta-action"
117
+ onClick={() => prefs.openLanguage()}
118
+ aria-label="Open response language"
119
+ >
120
+ {prefs.prefs.language.slice(0, 2).toUpperCase()}
121
+ </button>,
122
+ );
123
+
124
+ return (
125
+ <div data-slot="aui_composer-toolbar-inner" className="aui-composer-toolbar-inner">
126
+ {items.map((item, index) => (
127
+ <Fragment key={index}>
128
+ {index > 0 ? (
129
+ <span className="aui-composer-meta-sep" aria-hidden>
130
+ /
131
+ </span>
132
+ ) : null}
133
+ {item}
134
+ </Fragment>
135
+ ))}
136
+ <span className="aui-composer-meta-spacer" aria-hidden />
137
+ {/* A gate budget outranks the context estimate: it is the number that
138
+ stops the visitor, and it is available even when the backend
139
+ reports no token usage at all (a Foundry agent). A zero budget is
140
+ not a meter, so it falls through to the context slot. */}
141
+ {prefs.usage && prefs.usage.limit > 0 ? (
142
+ <DigichatUsageMeter usage={prefs.usage} />
143
+ ) : prefs.showContextMeter ? (
144
+ <DigichatContextMeter
145
+ model={meterModel}
146
+ toolCount={mcpToolTotal + prefs.catalogTools.length}
147
+ />
148
+ ) : null}
149
+ </div>
150
+ );
151
+ }
@@ -0,0 +1,158 @@
1
+ /**
2
+ * The context meter's share arithmetic, kept out of the component so it
3
+ * can be tested without a React tree.
4
+ *
5
+ * Decision (DIG-4459, #5967): the provider reports only the prompt
6
+ * total, so the system / tools / user split is an estimate. The three
7
+ * quantities are therefore a PARTITION of that reported total, never
8
+ * three independent estimates of it. The measured user text is clamped
9
+ * into the total first, the flat per-tool allowance is clamped into what
10
+ * is left, and the system prompt takes the remainder. The consequence is
11
+ * that the three token counts always sum to the reported total, so no
12
+ * share can print above 100 and the printed numbers always describe the
13
+ * same turn.
14
+ *
15
+ * The percentages are each computed from their own token count and then
16
+ * normalised to exactly 100 by the largest-remainder method, because
17
+ * independent rounding can sum to 99 or 101 and a leftover-derived
18
+ * share would then be wrong (the old leftover share went negative on a
19
+ * rounding remainder).
20
+ */
21
+
22
+ /** Bar segments in the context meter grid. */
23
+ export const CONTEXT_BAR_CELLS = 25;
24
+
25
+ /** Rough tokens per tool schema (name, description, JSON schema). */
26
+ export const CONTEXT_TOKENS_PER_TOOL = 220;
27
+
28
+ export type ContextMeterShares = {
29
+ /** Token counts. They sum to the reported prompt total. */
30
+ systemTokens: number;
31
+ toolTokens: number;
32
+ userTokens: number;
33
+ /** Percentages. They sum to exactly 100 when a turn reported usage. */
34
+ systemPct: number;
35
+ toolPct: number;
36
+ userPct: number;
37
+ /** Bar cells. They sum to the used span. */
38
+ systemCells: number;
39
+ toolCells: number;
40
+ userCells: number;
41
+ /** Bar cells in the used span. */
42
+ usedCells: number;
43
+ };
44
+
45
+ export type ContextMeterInput = {
46
+ /** Prompt total the provider reported for the last turn, 0 when unknown. */
47
+ input: number;
48
+ /** Characters of user text in the thread. */
49
+ userChars: number;
50
+ /** Connected tool count, used for the schema estimate. */
51
+ toolCount: number;
52
+ /** The model's context window, in tokens. */
53
+ window: number;
54
+ };
55
+
56
+ /** Rough tokens for the user text held in the thread. */
57
+ const userTokensFromChars = (userChars: number): number =>
58
+ Math.round(userChars / 4);
59
+
60
+ /**
61
+ * Round exact shares so the rounded values sum to exactly 100.
62
+ *
63
+ * Largest remainder: floor every share, then hand the leftover points
64
+ * to the shares with the biggest fractional part. Each result stays
65
+ * within one point of its exact share and no result is negative.
66
+ */
67
+ const normaliseToHundred = (exact: readonly number[]): number[] => {
68
+ const rounded = exact.map((value) => Math.floor(value));
69
+ const exactTotal = exact.reduce((sum, value) => sum + value, 0);
70
+ // Only a set of exact shares that adds up to a hundred can be
71
+ // normalised. Anything else (no reported usage) is left alone, so the
72
+ // leftover points are never handed out against nothing.
73
+ if (Math.abs(exactTotal - 100) > 0.5) {
74
+ return exact.map((value) => Math.round(value));
75
+ }
76
+ let leftover = 100 - rounded.reduce((sum, value) => sum + value, 0);
77
+ if (leftover <= 0) return rounded;
78
+ const order = exact
79
+ .map((value, index) => ({
80
+ index,
81
+ fraction: value - Math.floor(value),
82
+ }))
83
+ .sort((a, b) => b.fraction - a.fraction || a.index - b.index);
84
+ for (const { index } of order) {
85
+ if (leftover <= 0) break;
86
+ rounded[index] += 1;
87
+ leftover -= 1;
88
+ }
89
+ return rounded;
90
+ };
91
+
92
+ /**
93
+ * Split a reported prompt total into system, tools and user.
94
+ *
95
+ * The token counts are a partition of `input`, so they always sum to it
96
+ * and no share can exceed 100. The percentages are each derived from
97
+ * their own token count and normalised to exactly 100.
98
+ */
99
+ export const computeContextShares = ({
100
+ input,
101
+ userChars,
102
+ toolCount,
103
+ window,
104
+ }: ContextMeterInput): ContextMeterShares => {
105
+ const total = input > 0 ? input : 0;
106
+
107
+ // Partition of the reported total: measured text first, then the flat
108
+ // tool estimate into what is left, then the remainder is the system
109
+ // prompt.
110
+ const userTokens =
111
+ total > 0 ? Math.min(userTokensFromChars(userChars), total) : 0;
112
+ const toolTokens =
113
+ total > 0
114
+ ? Math.min(toolCount * CONTEXT_TOKENS_PER_TOOL, total - userTokens)
115
+ : 0;
116
+ const systemTokens = total - userTokens - toolTokens;
117
+
118
+ const exact =
119
+ total > 0
120
+ ? [
121
+ (userTokens / total) * 100,
122
+ (toolTokens / total) * 100,
123
+ (systemTokens / total) * 100,
124
+ ]
125
+ : [0, 0, 0];
126
+ // With no reported usage there is nothing to normalise: the three
127
+ // exact shares are zero, not a third of a hundred each.
128
+ const [userPct, toolPct, systemPct] =
129
+ total > 0 ? normaliseToHundred(exact) : [0, 0, 0];
130
+
131
+ // The used span grows with the window ratio; inside it the three types
132
+ // take their share of the prompt.
133
+ const ratio = window > 0 ? Math.min(1, total / window) : 0;
134
+ const usedCells =
135
+ total > 0 ? Math.max(1, Math.round(ratio * CONTEXT_BAR_CELLS)) : 0;
136
+ const systemCells = Math.min(
137
+ usedCells,
138
+ total > 0 ? Math.round((usedCells * systemTokens) / total) : 0,
139
+ );
140
+ const toolCells = Math.min(
141
+ usedCells - systemCells,
142
+ total > 0 ? Math.round((usedCells * toolTokens) / total) : 0,
143
+ );
144
+ const userCells = Math.max(0, usedCells - systemCells - toolCells);
145
+
146
+ return {
147
+ systemTokens,
148
+ toolTokens,
149
+ userTokens,
150
+ systemPct,
151
+ toolPct,
152
+ userPct,
153
+ systemCells,
154
+ toolCells,
155
+ userCells,
156
+ usedCells,
157
+ };
158
+ };