@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,235 @@
1
+ "use client";
2
+
3
+ import { memo, useEffect, useRef, type ComponentPropsWithoutRef, type FC } from "react";
4
+ import {
5
+ ComposerPrimitive,
6
+ unstable_defaultDirectiveFormatter,
7
+ unstable_useTriggerPopoverScopeContext,
8
+ type Unstable_DirectiveFormatter,
9
+ type Unstable_TriggerItem,
10
+ } from "@assistant-ui/react";
11
+ import { cn } from "./cn";
12
+ import { OverlayScrollbar } from "../overlay-scrollbar";
13
+ import { PaletteRowPreview } from "../stock/palette-row-preview";
14
+
15
+ type IconComponent = FC<{ className?: string }>;
16
+
17
+ type DirectiveBehaviorProps = {
18
+ /** Formatter used to serialize the selected item into composer text. */
19
+ formatter?: Unstable_DirectiveFormatter | undefined;
20
+ /** Called after the directive text has been inserted into the composer. */
21
+ onInserted?: ((item: Unstable_TriggerItem) => void) | undefined;
22
+ };
23
+
24
+ type ActionBehaviorProps = {
25
+ /** Formatter used to serialize the audit-trail chip (when `removeOnExecute` is false). */
26
+ formatter?: Unstable_DirectiveFormatter | undefined;
27
+ /** Invoked with the selected item at the moment of selection. */
28
+ onExecute: (item: Unstable_TriggerItem) => void;
29
+ /** If `true`, strip the trigger text from the composer after executing. @default false */
30
+ removeOnExecute?: boolean | undefined;
31
+ };
32
+
33
+ type ComposerTriggerPopoverBaseProps = Omit<
34
+ ComponentPropsWithoutRef<typeof ComposerPrimitive.Unstable_TriggerPopover>,
35
+ "children"
36
+ > & {
37
+ /**
38
+ * Maps icon keys to components. Unused on the digichat skin (flat command
39
+ * + description rows). Kept so adapter bundles can still pass `iconMap`.
40
+ */
41
+ iconMap?: Record<string, IconComponent>;
42
+ fallbackIcon?: IconComponent;
43
+ backLabel?: string;
44
+ emptyCategoriesLabel?: string;
45
+ /** Label shown when no items match. @default "No matching items" */
46
+ emptyItemsLabel?: string;
47
+ /** Label shown while an async adapter is resolving items. @default "Loading…" */
48
+ loadingLabel?: string;
49
+ };
50
+
51
+ type ComposerTriggerPopoverProps = ComposerTriggerPopoverBaseProps &
52
+ (
53
+ | {
54
+ directive: DirectiveBehaviorProps;
55
+ action?: never;
56
+ }
57
+ | {
58
+ action: ActionBehaviorProps;
59
+ directive?: never;
60
+ }
61
+ );
62
+
63
+ type ItemsProps = {
64
+ emptyLabel: string;
65
+ loadingLabel: string;
66
+ };
67
+
68
+ const Items: FC<ItemsProps> = ({ emptyLabel, loadingLabel }) => {
69
+ const { isLoading } = unstable_useTriggerPopoverScopeContext();
70
+ const listRef = useRef<HTMLDivElement | null>(null);
71
+ return (
72
+ <ComposerPrimitive.Unstable_TriggerPopoverItems>
73
+ {(items) => (
74
+ <div className="relative min-h-0">
75
+ <div
76
+ data-slot="composer-trigger-popover-items"
77
+ className="flex max-h-[min(50vh,22rem)] flex-col overflow-y-auto py-1"
78
+ ref={(node) => {
79
+ listRef.current = node;
80
+ node
81
+ ?.querySelector<HTMLElement>("[data-highlighted]")
82
+ ?.scrollIntoView({ block: "nearest" });
83
+ }}
84
+ >
85
+ {items.map((item, index) => {
86
+ const direct = (item as unknown as { value?: unknown }).value;
87
+ const value =
88
+ typeof direct === "string"
89
+ ? direct
90
+ : typeof item.metadata?.value === "string"
91
+ ? item.metadata.value
92
+ : "";
93
+ const detailId = item.description ? `slash-desc-${item.id}` : undefined;
94
+ return (
95
+ <ComposerPrimitive.Unstable_TriggerPopoverItem
96
+ key={item.id}
97
+ item={item}
98
+ index={index}
99
+ aria-describedby={detailId}
100
+ className="group flex w-full cursor-pointer flex-col items-stretch px-2 py-1 text-start text-sm outline-none"
101
+ >
102
+ <span className="flex w-full items-center gap-2">
103
+ <span className="shrink-0 font-medium">{item.label}</span>
104
+ {value ? (
105
+ <span
106
+ data-slot="palette-value"
107
+ className="text-ink-mute ml-auto min-w-0 truncate text-end text-xs leading-tight"
108
+ >
109
+ {value}
110
+ </span>
111
+ ) : null}
112
+ </span>
113
+ {item.description ? (
114
+ <span
115
+ id={detailId}
116
+ data-slot="palette-detail"
117
+ className="text-ink-mute hidden truncate text-xs leading-tight group-data-[highlighted]:block"
118
+ >
119
+ {item.description}
120
+ </span>
121
+ ) : null}
122
+ </ComposerPrimitive.Unstable_TriggerPopoverItem>
123
+ );
124
+ })}
125
+ {items.length === 0 && (
126
+ <div className="text-ink-mute px-3 py-2 text-sm">
127
+ {isLoading ? loadingLabel : emptyLabel}
128
+ </div>
129
+ )}
130
+ </div>
131
+ <OverlayScrollbar targetRef={listRef} width={6} />
132
+ </div>
133
+ )}
134
+ </ComposerPrimitive.Unstable_TriggerPopoverItems>
135
+ );
136
+ };
137
+
138
+ /**
139
+ * Arrow-key handling for the picker.
140
+ *
141
+ * The composer textarea keeps focus while the picker is open, so the keys are
142
+ * caught at document level (capture) rather than on the popover.
143
+ *
144
+ * It lives inside `Unstable_TriggerPopover` because the popover scope is the
145
+ * only place that exposes the picker's own `close()`. A synthetic Escape
146
+ * dispatched on `document` has `document` as its target, so it never reaches
147
+ * the composer's own keydown handler and the picker stayed open (DIG-4599).
148
+ */
149
+ const TriggerPopoverArrowKeys: FC = () => {
150
+ const { open, close } = unstable_useTriggerPopoverScopeContext();
151
+ useEffect(() => {
152
+ if (!open) return;
153
+ const onKey = (event: KeyboardEvent) => {
154
+ if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") return;
155
+ const popover = document.querySelector<HTMLElement>(
156
+ '[data-slot="composer-trigger-popover"]',
157
+ );
158
+ if (!popover) return;
159
+ event.preventDefault();
160
+ event.stopPropagation();
161
+ if (event.key === "ArrowRight") {
162
+ // Same as Enter: submit the highlighted row.
163
+ popover.querySelector<HTMLElement>("[data-highlighted]")?.click();
164
+ return;
165
+ }
166
+ // Same as Escape: go back / close the picker, through the picker's own
167
+ // scope. Upstream's Escape handler closes the popover the same way.
168
+ close();
169
+ };
170
+ document.addEventListener("keydown", onKey, true);
171
+ return () => document.removeEventListener("keydown", onKey, true);
172
+ }, [open, close]);
173
+ return null;
174
+ };
175
+
176
+ /**
177
+ * Pre-built popover UI for a trigger-driven picker (mentions, slash commands).
178
+ * digichat: flat list, no category drill-down, no per-row icons, composer width.
179
+ */
180
+ const ComposerTriggerPopoverImpl: FC<ComposerTriggerPopoverProps> = ({
181
+ emptyItemsLabel = "No matching items",
182
+ loadingLabel = "Loading…",
183
+ className,
184
+ directive,
185
+ action,
186
+ iconMap: _iconMap,
187
+ fallbackIcon: _fallbackIcon,
188
+ backLabel: _backLabel,
189
+ emptyCategoriesLabel: _emptyCategoriesLabel,
190
+ ...props
191
+ }) => {
192
+ const warnedRef = useRef(false);
193
+ if (
194
+ process.env.NODE_ENV !== "production" &&
195
+ !warnedRef.current &&
196
+ Boolean(directive) === Boolean(action)
197
+ ) {
198
+ warnedRef.current = true;
199
+ console.warn(
200
+ "[assistant-ui] ComposerTriggerPopover requires exactly one of `directive` or `action` props.",
201
+ );
202
+ }
203
+
204
+ return (
205
+ <ComposerPrimitive.Unstable_TriggerPopover
206
+ data-slot="composer-trigger-popover"
207
+ className={cn(
208
+ "aui-composer-trigger-popover bg-surface-2 text-ink absolute inset-x-0 bottom-full z-50 mb-1 w-full overflow-hidden rounded-none border",
209
+ className,
210
+ )}
211
+ {...props}
212
+ >
213
+ {directive ? (
214
+ <ComposerPrimitive.Unstable_TriggerPopover.Directive
215
+ formatter={directive.formatter ?? unstable_defaultDirectiveFormatter}
216
+ onInserted={directive.onInserted}
217
+ />
218
+ ) : action ? (
219
+ <ComposerPrimitive.Unstable_TriggerPopover.Action
220
+ formatter={action.formatter ?? unstable_defaultDirectiveFormatter}
221
+ onExecute={action.onExecute}
222
+ removeOnExecute={action.removeOnExecute}
223
+ />
224
+ ) : null}
225
+ <TriggerPopoverArrowKeys />
226
+ <PaletteRowPreview />
227
+ <Items emptyLabel={emptyItemsLabel} loadingLabel={loadingLabel} />
228
+ </ComposerPrimitive.Unstable_TriggerPopover>
229
+ );
230
+ };
231
+ ComposerTriggerPopoverImpl.displayName = "ComposerTriggerPopover";
232
+
233
+ export const ComposerTriggerPopover = memo(
234
+ ComposerTriggerPopoverImpl,
235
+ ) as FC<ComposerTriggerPopoverProps>;
@@ -0,0 +1,6 @@
1
+ declare module "@assistant-ui/react-markdown/styles/dot.css";
2
+
3
+ // WS4: moved skins import the package stylesheets via relative side-effect
4
+ // imports. The app got its `*.css` declarations from Next's global types;
5
+ // the package needs its own.
6
+ declare module "*.css";
@@ -0,0 +1,238 @@
1
+ "use client";
2
+
3
+ import { memo } from "react";
4
+ import { cva, type VariantProps } from "class-variance-authority";
5
+ import type { FileMessagePartComponent } from "@assistant-ui/react";
6
+ import { DotMatrix } from "../DotMatrix";
7
+ import { cn } from "./cn";
8
+
9
+ const fileVariants = cva(
10
+ "aui-file-root inline-flex items-center gap-3 rounded-none transition-colors",
11
+ {
12
+ variants: {
13
+ variant: {
14
+ outline: "border-hair hover:bg-surface-2/50 border",
15
+ ghost: "hover:bg-surface-2/50",
16
+ muted: "bg-surface-2/50 hover:bg-surface-2",
17
+ },
18
+ size: {
19
+ sm: "px-2.5 py-1.5 text-xs",
20
+ default: "px-3 py-2 text-sm",
21
+ lg: "px-4 py-3 text-base",
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ variant: "outline",
26
+ size: "default",
27
+ },
28
+ },
29
+ );
30
+
31
+
32
+ export type FileDataKind = "data-uri" | "url" | "base64" | "id";
33
+
34
+ function getFileDataKind(
35
+ data: string,
36
+ sourceType?: "url" | "id",
37
+ ): FileDataKind {
38
+ if (sourceType === "url" && /^data:/i.test(data)) return "data-uri";
39
+ if (sourceType) return sourceType;
40
+ if (/^data:/i.test(data)) return "data-uri";
41
+ if (/^https?:\/\//i.test(data)) return "url";
42
+ return "base64";
43
+ }
44
+
45
+ function getBase64Size(base64: string): number {
46
+ const commaIndex = base64.indexOf(",");
47
+ const base64Data = commaIndex >= 0 ? base64.slice(commaIndex + 1) : base64;
48
+ const padding = (base64Data.match(/=/g) || []).length;
49
+ return Math.floor((base64Data.length * 3) / 4) - padding;
50
+ }
51
+
52
+ function formatFileSize(bytes: number): string {
53
+ if (bytes < 1024) {
54
+ return `${bytes} B`;
55
+ }
56
+ if (bytes < 1024 * 1024) {
57
+ return `${(bytes / 1024).toFixed(1)} KB`;
58
+ }
59
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
60
+ }
61
+
62
+ export type FileRootProps = React.ComponentProps<"div"> &
63
+ VariantProps<typeof fileVariants>;
64
+
65
+ function FileRoot({
66
+ className,
67
+ variant,
68
+ size,
69
+ children,
70
+ ...props
71
+ }: FileRootProps) {
72
+ return (
73
+ <div
74
+ data-slot="file-root"
75
+ data-variant={variant}
76
+ data-size={size}
77
+ className={cn(fileVariants({ variant, size, className }))}
78
+ {...props}
79
+ >
80
+ {children}
81
+ </div>
82
+ );
83
+ }
84
+
85
+ type FileIconDisplayProps = React.ComponentProps<"span"> & {
86
+ mimeType?: string;
87
+ };
88
+
89
+ function FileIconDisplay({
90
+ mimeType,
91
+ className,
92
+ children,
93
+ ...props
94
+ }: FileIconDisplayProps) {
95
+ return (
96
+ <span
97
+ data-slot="file-icon"
98
+ className={cn("text-ink-mute shrink-0", className)}
99
+ {...props}
100
+ >
101
+ {children ?? (
102
+ <DotMatrix
103
+ state="attach"
104
+ label={mimeType ? `${mimeType} file` : "File"}
105
+ className="size-3.5"
106
+ />
107
+ )}
108
+ </span>
109
+ );
110
+ }
111
+
112
+ function FileName({
113
+ className,
114
+ children,
115
+ ...props
116
+ }: React.ComponentProps<"span">) {
117
+ return (
118
+ <span
119
+ data-slot="file-name"
120
+ className={cn("min-w-0 flex-1 truncate font-medium", className)}
121
+ {...props}
122
+ >
123
+ {children || "Unnamed file"}
124
+ </span>
125
+ );
126
+ }
127
+
128
+ type FileSizeProps = React.ComponentProps<"span"> & {
129
+ bytes: number;
130
+ };
131
+
132
+ function FileSize({ bytes, className, ...props }: FileSizeProps) {
133
+ return (
134
+ <span
135
+ data-slot="file-size"
136
+ className={cn("text-ink-mute shrink-0", className)}
137
+ {...props}
138
+ >
139
+ {formatFileSize(bytes)}
140
+ </span>
141
+ );
142
+ }
143
+
144
+ type FileDownloadProps = Omit<React.ComponentProps<"a">, "href"> & {
145
+ data: string;
146
+ mimeType: string;
147
+ filename?: string;
148
+ sourceType?: "url" | "id";
149
+ };
150
+
151
+ function FileDownload({
152
+ data,
153
+ mimeType,
154
+ filename,
155
+ sourceType,
156
+ className,
157
+ children,
158
+ ...props
159
+ }: FileDownloadProps) {
160
+ if (typeof data !== "string") return null;
161
+ const kind = getFileDataKind(data, sourceType);
162
+ if (kind === "id") return null;
163
+ if (kind === "url" && !/^(https?:\/\/|blob:)/i.test(data)) return null;
164
+ const href = kind === "base64" ? `data:${mimeType};base64,${data}` : data;
165
+
166
+ return (
167
+ <a
168
+ data-slot="file-download"
169
+ href={href}
170
+ download={filename || "download"}
171
+ {...(kind === "url" && { target: "_blank", rel: "noopener noreferrer" })}
172
+ className={cn(
173
+ "text-ink-mute hover:bg-accent hover:text-accent-foreground shrink-0 rounded-none p-1 transition-colors",
174
+ className,
175
+ )}
176
+ {...props}
177
+ >
178
+ {children || <DotMatrix state="download" label="Download" className="size-3.5" />}
179
+ </a>
180
+ );
181
+ }
182
+
183
+ const FileImpl: FileMessagePartComponent = ({
184
+ filename,
185
+ data,
186
+ mimeType,
187
+ sourceType,
188
+ }) => {
189
+ const kind = getFileDataKind(data, sourceType);
190
+ const showSize =
191
+ typeof data === "string" && (kind === "base64" || kind === "data-uri");
192
+
193
+ return (
194
+ <FileRoot>
195
+ <FileIconDisplay mimeType={mimeType} />
196
+ <div className="flex min-w-0 flex-1 flex-col gap-0.5">
197
+ <FileName>{filename}</FileName>
198
+ {showSize && (
199
+ <FileSize bytes={getBase64Size(data)} className="text-xs" />
200
+ )}
201
+ </div>
202
+ <FileDownload
203
+ data={data}
204
+ mimeType={mimeType}
205
+ {...(filename !== undefined && { filename })}
206
+ {...(sourceType !== undefined && { sourceType })}
207
+ />
208
+ </FileRoot>
209
+ );
210
+ };
211
+
212
+ const File = memo(FileImpl) as unknown as FileMessagePartComponent & {
213
+ Root: typeof FileRoot;
214
+ Icon: typeof FileIconDisplay;
215
+ Name: typeof FileName;
216
+ Size: typeof FileSize;
217
+ Download: typeof FileDownload;
218
+ };
219
+
220
+ File.displayName = "File";
221
+ File.Root = FileRoot;
222
+ File.Icon = FileIconDisplay;
223
+ File.Name = FileName;
224
+ File.Size = FileSize;
225
+ File.Download = FileDownload;
226
+
227
+ export {
228
+ File,
229
+ FileRoot,
230
+ FileIconDisplay,
231
+ FileName,
232
+ FileSize,
233
+ FileDownload,
234
+ fileVariants,
235
+ getFileDataKind,
236
+ getBase64Size,
237
+ formatFileSize,
238
+ };
@@ -0,0 +1,92 @@
1
+ "use client";
2
+
3
+ import { AuiIf, useAuiState, ThreadPrimitive } from "@assistant-ui/react";
4
+ import { useCallback, useEffect, useRef, useState, type FC } from "react";
5
+
6
+ import { DotMatrix } from "../DotMatrix";
7
+ import { dismissKeyboardIfCoarse } from "./composer-keyboard";
8
+ import { SuggestionScrollRow } from "../suggestion-scroll-row";
9
+
10
+ const FollowupSuggestionsRow: FC = () => {
11
+ const suggestions = useAuiState((s) => s.thread.suggestions);
12
+ const scrollRef = useRef<HTMLDivElement>(null);
13
+ const rtlRef = useRef<boolean | null>(null);
14
+ const [fades, setFades] = useState({ left: false, right: false });
15
+
16
+ const updateFades = useCallback(() => {
17
+ const el = scrollRef.current;
18
+ if (!el) return;
19
+ const maxScroll = el.scrollWidth - el.clientWidth;
20
+ // scrollLeft runs 0..-max in RTL; normalize to hidden width per physical edge.
21
+ const fromStart = Math.abs(el.scrollLeft);
22
+ // getComputedStyle forces a style recalc per scroll event; direction is stable, read it once.
23
+ const rtl = (rtlRef.current ??= getComputedStyle(el).direction === "rtl");
24
+ const [left, right] = rtl
25
+ ? [maxScroll - fromStart, fromStart]
26
+ : [fromStart, maxScroll - fromStart];
27
+ setFades((prev) => {
28
+ const next = { left: left > 1, right: right > 1 };
29
+ return prev.left === next.left && prev.right === next.right ? prev : next;
30
+ });
31
+ }, []);
32
+
33
+ useEffect(() => {
34
+ updateFades();
35
+ const el = scrollRef.current;
36
+ if (!el?.firstElementChild) return undefined;
37
+ const observer = new ResizeObserver(updateFades);
38
+ observer.observe(el);
39
+ observer.observe(el.firstElementChild);
40
+ return () => observer.disconnect();
41
+ }, [updateFades]);
42
+
43
+ const maskImage = `linear-gradient(to right, ${
44
+ fades.left ? "transparent, black 2rem" : "black"
45
+ }, ${fades.right ? "black calc(100% - 2rem), transparent" : "black"})`;
46
+
47
+ return (
48
+ <SuggestionScrollRow
49
+ ref={scrollRef}
50
+ onScroll={updateFades}
51
+ // overflow-x clips both axes; py-1/-my-1 gives focus rings vertical room without changing outer height.
52
+ className="aui-thread-followup-suggestions -my-1 w-full py-1"
53
+ style={{ maskImage, WebkitMaskImage: maskImage }}
54
+ >
55
+ <div className="mx-auto flex min-h-8 w-max items-center gap-2 px-0.5">
56
+ {suggestions.map((suggestion, idx) => (
57
+ <ThreadPrimitive.Suggestion
58
+ key={idx}
59
+ className="aui-thread-followup-suggestion bg-surface hover:bg-surface-2 rounded-none border px-3 py-1 text-sm whitespace-nowrap transition-colors ease-in"
60
+ prompt={suggestion.prompt}
61
+ send
62
+ onClick={() => dismissKeyboardIfCoarse()}
63
+ >
64
+ <DotMatrix
65
+ state="example"
66
+ decorative
67
+ className="aui-thread-followup-suggestion-mark size-3.5"
68
+ />
69
+ {suggestion.title ?? suggestion.prompt}
70
+ {suggestion.label && (
71
+ <span className="aui-thread-followup-suggestion-label text-ink-mute ms-1">
72
+ {suggestion.label}
73
+ </span>
74
+ )}
75
+ </ThreadPrimitive.Suggestion>
76
+ ))}
77
+ </div>
78
+ </SuggestionScrollRow>
79
+ );
80
+ };
81
+
82
+ export const ThreadFollowupSuggestions: FC = () => (
83
+ <AuiIf
84
+ condition={(s) =>
85
+ !s.thread.isEmpty &&
86
+ !s.thread.isRunning &&
87
+ s.thread.suggestions.length > 0
88
+ }
89
+ >
90
+ <FollowupSuggestionsRow />
91
+ </AuiIf>
92
+ );
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Upper bound on the text repairPartialJson will scan. Tool arguments and
3
+ * results arrive from the model stream and nothing bounds their length, so
4
+ * the repair must never see an unbounded string: a long whitespace run used
5
+ * to cost a quadratic scan and froze the render thread (CodeQL
6
+ * js/polynomial-redos, alert 196, DIG-4818). Longer text is shown raw, never
7
+ * cut.
8
+ *
9
+ * The cap guards the repair path only. Valid JSON of any size still goes
10
+ * through JSON.parse and is pretty-printed as before; the one behaviour
11
+ * change above the cap is that partial (unparsable) JSON longer than the cap
12
+ * is shown raw instead of repaired and pretty-printed. The cap is defence in
13
+ * depth: the whitespace strip itself is linear now, but every regex in the
14
+ * repair stays bounded by this budget.
15
+ */
16
+ export const MAX_REPAIR_INPUT_CHARS = 64 * 1024;
17
+
18
+ /** Pretty-print tool args the same way results already use JSON.stringify(..., null, 2). */
19
+ export function formatJsonDump(value: unknown): string {
20
+ if (value === undefined) return "";
21
+ if (typeof value === "string") {
22
+ const trimmed = value.trim();
23
+ if (!trimmed) return value;
24
+ try {
25
+ return JSON.stringify(JSON.parse(trimmed), null, 2);
26
+ } catch {
27
+ const repaired = repairPartialJson(trimmed);
28
+ if (repaired !== undefined) {
29
+ try {
30
+ return JSON.stringify(JSON.parse(repaired), null, 2);
31
+ } catch {
32
+ /* fall through to raw text */
33
+ }
34
+ }
35
+ return value;
36
+ }
37
+ }
38
+ try {
39
+ return JSON.stringify(value, null, 2);
40
+ } catch {
41
+ return String(value);
42
+ }
43
+ }
44
+
45
+ export function formatToolDurationMs(ms: number): string {
46
+ if (!Number.isFinite(ms) || ms < 0) return "0ms";
47
+ const rounded = Math.round(ms);
48
+ if (rounded < 100) return `${rounded}ms`;
49
+ const seconds = rounded / 1000;
50
+ if (seconds < 10) return `${(Math.round(seconds * 10) / 10).toFixed(1)}s`;
51
+ if (seconds < 60) return `${Math.floor(seconds)}s`;
52
+ return `${Math.floor(seconds / 60)}m ${Math.floor(seconds % 60)}s`;
53
+ }
54
+
55
+ /**
56
+ * Close a truncated JSON prefix (streaming tool args) so it can be parsed:
57
+ * closes an open string, drops a dangling key / trailing comma / colon, and
58
+ * closes open objects and arrays. Returns undefined when the text is not a
59
+ * JSON object/array prefix.
60
+ */
61
+ export function repairPartialJson(text: string): string | undefined {
62
+ const src = text.trim();
63
+ if (!src || (src[0] !== "{" && src[0] !== "[")) return undefined;
64
+ // Cap before any scan of the text: the repair below runs regexes over it.
65
+ // Defence in depth for those regexes, not the fix itself: the quadratic
66
+ // whitespace strip is gone (trimEnd below).
67
+ if (src.length > MAX_REPAIR_INPUT_CHARS) return undefined;
68
+ const stack: string[] = [];
69
+ let inString = false;
70
+ let escaped = false;
71
+ for (const ch of src) {
72
+ if (inString) {
73
+ if (escaped) escaped = false;
74
+ else if (ch === "\\") escaped = true;
75
+ else if (ch === '"') inString = false;
76
+ continue;
77
+ }
78
+ if (ch === '"') inString = true;
79
+ else if (ch === "{" || ch === "[") stack.push(ch);
80
+ else if (ch === "}" || ch === "]") {
81
+ if (stack.pop() !== (ch === "}" ? "{" : "[")) return undefined;
82
+ }
83
+ }
84
+ let out = src;
85
+ if (inString) {
86
+ if (escaped) out = out.slice(0, -1);
87
+ out += '"';
88
+ }
89
+ for (let i = 0; i < 4; i++) {
90
+ const before = out;
91
+ // trimEnd, not out.replace(/\s+$/, ""): the regex re-tries the anchor at
92
+ // every position of a whitespace run and costs quadratic time on a long
93
+ // run, which froze the render thread (CodeQL js/polynomial-redos, alert
94
+ // 196). Same result, one linear pass.
95
+ out = out.trimEnd();
96
+ out = out.replace(/,$/, "");
97
+ out = out.replace(/:$/, "");
98
+ // dangling key with no value inside an object: {"a":1,"b"
99
+ if (stack[stack.length - 1] === "{") out = out.replace(/([{,])\s*"(?:[^"\\]|\\.)*"$/, "$1");
100
+ out = out.replace(/,$/, "");
101
+ // partial literal (tru, fals, nul) or number ending in '-' / '.' / 'e'
102
+ // No lookbehind here: a lookbehind literal is a parse-time SyntaxError on
103
+ // Safari below 16.4 and on older embedded WebViews, and this module is
104
+ // imported by the rendered thread — one unparsable regex would take the
105
+ // whole thread down. The prefix is captured and written back instead.
106
+ out = out.replace(/([:\[,]\s*)(t|tr|tru|f|fa|fal|fals|n|nu|nul)$/, "$1");
107
+ out = out.replace(/(\d)[.eE+-]+$/, "$1").replace(/([:\[,]\s*)-$/, "$1");
108
+ if (out === before) break;
109
+ }
110
+ while (stack.length) out += stack.pop() === "{" ? "}" : "]";
111
+ return out;
112
+ }