@adecore/agents-react 0.0.1 → 0.17.0-beta.1

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 (513) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +53 -0
  3. package/dist/agents/AccountDot.d.ts +4 -0
  4. package/dist/agents/AccountDot.js +8 -0
  5. package/dist/agents/AgentIcon.d.ts +6 -0
  6. package/dist/agents/AgentIcon.js +20 -0
  7. package/dist/agents/ModelOptionControl.d.ts +15 -0
  8. package/dist/agents/ModelOptionControl.js +33 -0
  9. package/dist/agents/ProviderLogo.d.ts +8 -0
  10. package/dist/agents/ProviderLogo.js +8 -0
  11. package/dist/agents/account-limits.d.ts +7 -0
  12. package/dist/agents/account-limits.js +40 -0
  13. package/dist/agents/accounts.d.ts +22 -0
  14. package/dist/agents/accounts.js +113 -0
  15. package/dist/agents/model-name.d.ts +12 -0
  16. package/dist/agents/model-name.js +56 -0
  17. package/dist/agents/model-options.d.ts +5 -0
  18. package/dist/agents/model-options.js +15 -0
  19. package/dist/agents/provider-paths.d.ts +2 -0
  20. package/dist/agents/provider-paths.js +8 -0
  21. package/dist/agents/status-look.d.ts +13 -0
  22. package/dist/agents/status-look.js +24 -0
  23. package/dist/chat/account-choice.d.ts +11 -0
  24. package/dist/chat/account-choice.js +30 -0
  25. package/dist/chat/actions.d.ts +6 -0
  26. package/dist/chat/actions.js +68 -0
  27. package/dist/chat/activity.d.ts +5 -0
  28. package/dist/chat/activity.js +6 -0
  29. package/dist/chat/attachments.d.ts +23 -0
  30. package/dist/chat/attachments.js +86 -0
  31. package/dist/chat/bookmarks.d.ts +14 -0
  32. package/dist/chat/bookmarks.js +73 -0
  33. package/dist/chat/chat-client.d.ts +55 -0
  34. package/dist/chat/chat-client.js +279 -0
  35. package/dist/chat/chat-references.d.ts +5 -0
  36. package/dist/chat/chat-references.js +5 -0
  37. package/dist/chat/drafts.d.ts +29 -0
  38. package/dist/chat/drafts.js +116 -0
  39. package/dist/chat/forks.d.ts +2 -0
  40. package/dist/chat/forks.js +28 -0
  41. package/dist/chat/guards.d.ts +12 -0
  42. package/dist/chat/guards.js +69 -0
  43. package/dist/chat/logic/ansi.d.ts +13 -0
  44. package/dist/chat/logic/ansi.js +185 -0
  45. package/dist/chat/logic/answers.d.ts +1 -0
  46. package/dist/chat/logic/answers.js +8 -0
  47. package/dist/chat/logic/bookmarks.d.ts +5 -0
  48. package/dist/chat/logic/bookmarks.js +33 -0
  49. package/dist/chat/logic/context-usage.d.ts +12 -0
  50. package/dist/chat/logic/context-usage.js +21 -0
  51. package/dist/chat/logic/fork.d.ts +45 -0
  52. package/dist/chat/logic/fork.js +148 -0
  53. package/dist/chat/logic/handback.d.ts +2 -0
  54. package/dist/chat/logic/handback.js +8 -0
  55. package/dist/chat/logic/json.d.ts +1 -0
  56. package/dist/chat/logic/json.js +4 -0
  57. package/dist/chat/logic/limit.d.ts +7 -0
  58. package/dist/chat/logic/limit.js +34 -0
  59. package/dist/chat/logic/resume-compaction.d.ts +14 -0
  60. package/dist/chat/logic/resume-compaction.js +47 -0
  61. package/dist/chat/logic/scrubber.d.ts +50 -0
  62. package/dist/chat/logic/scrubber.js +220 -0
  63. package/dist/chat/logic/thread-cards.d.ts +3 -0
  64. package/dist/chat/logic/thread-cards.js +59 -0
  65. package/dist/chat/logic/timeline-copy.d.ts +4 -0
  66. package/dist/chat/logic/timeline-copy.js +59 -0
  67. package/dist/chat/logic/timeline-target.d.ts +12 -0
  68. package/dist/chat/logic/timeline-target.js +34 -0
  69. package/dist/chat/logic/timeline.d.ts +115 -0
  70. package/dist/chat/logic/timeline.js +343 -0
  71. package/dist/chat/logic/tool-catalog.d.ts +10 -0
  72. package/dist/chat/logic/tool-catalog.js +44 -0
  73. package/dist/chat/logic/tools.d.ts +15 -0
  74. package/dist/chat/logic/tools.js +82 -0
  75. package/dist/chat/logic/welcome.d.ts +2 -0
  76. package/dist/chat/logic/welcome.js +10 -0
  77. package/dist/chat/logic/workflow.d.ts +14 -0
  78. package/dist/chat/logic/workflow.js +47 -0
  79. package/dist/chat/mentions.d.ts +65 -0
  80. package/dist/chat/mentions.js +184 -0
  81. package/dist/chat/persisted-json.d.ts +5 -0
  82. package/dist/chat/persisted-json.js +58 -0
  83. package/dist/chat/preferences.d.ts +27 -0
  84. package/dist/chat/preferences.js +139 -0
  85. package/dist/chat/quote.d.ts +2 -0
  86. package/dist/chat/quote.js +25 -0
  87. package/dist/chat/recent-messages.d.ts +17 -0
  88. package/dist/chat/recent-messages.js +36 -0
  89. package/dist/chat/resume-compaction-dismissals.d.ts +3 -0
  90. package/dist/chat/resume-compaction-dismissals.js +14 -0
  91. package/dist/chat/runtime-modes.d.ts +4 -0
  92. package/dist/chat/runtime-modes.js +15 -0
  93. package/dist/chat/stash.d.ts +24 -0
  94. package/dist/chat/stash.js +91 -0
  95. package/dist/chat/subagent-conversation.d.ts +41 -0
  96. package/dist/chat/subagent-conversation.js +167 -0
  97. package/dist/chat/subagent-list.d.ts +19 -0
  98. package/dist/chat/subagent-list.js +106 -0
  99. package/dist/chat/subagent-support.d.ts +6 -0
  100. package/dist/chat/subagent-support.js +14 -0
  101. package/dist/chat/subagent-view.d.ts +35 -0
  102. package/dist/chat/subagent-view.js +96 -0
  103. package/dist/chat/thumbnails.d.ts +10 -0
  104. package/dist/chat/thumbnails.js +89 -0
  105. package/dist/chat/timeline-flash.d.ts +12 -0
  106. package/dist/chat/timeline-flash.js +16 -0
  107. package/dist/chat/timeline-scroll.d.ts +27 -0
  108. package/dist/chat/timeline-scroll.js +152 -0
  109. package/dist/chat/ui/AccountPill.d.ts +3 -0
  110. package/dist/chat/ui/AccountPill.js +23 -0
  111. package/dist/chat/ui/AnsiOutput.d.ts +5 -0
  112. package/dist/chat/ui/AnsiOutput.js +9 -0
  113. package/dist/chat/ui/BookmarkMarker.d.ts +8 -0
  114. package/dist/chat/ui/BookmarkMarker.js +57 -0
  115. package/dist/chat/ui/BookmarkSubmenu.d.ts +3 -0
  116. package/dist/chat/ui/BookmarkSubmenu.js +33 -0
  117. package/dist/chat/ui/ChatActivity.d.ts +7 -0
  118. package/dist/chat/ui/ChatActivity.js +84 -0
  119. package/dist/chat/ui/ChatReferenceChip.d.ts +4 -0
  120. package/dist/chat/ui/ChatReferenceChip.js +16 -0
  121. package/dist/chat/ui/CodeBlock.d.ts +4 -0
  122. package/dist/chat/ui/CodeBlock.js +132 -0
  123. package/dist/chat/ui/Composer.d.ts +15 -0
  124. package/dist/chat/ui/Composer.js +856 -0
  125. package/dist/chat/ui/ComposerInput.d.ts +29 -0
  126. package/dist/chat/ui/ComposerInput.js +127 -0
  127. package/dist/chat/ui/DiffPool.d.ts +5 -0
  128. package/dist/chat/ui/DiffPool.js +26 -0
  129. package/dist/chat/ui/EditDiff.d.ts +4 -0
  130. package/dist/chat/ui/EditDiff.js +21 -0
  131. package/dist/chat/ui/FadingWords.d.ts +3 -0
  132. package/dist/chat/ui/FadingWords.js +5 -0
  133. package/dist/chat/ui/ImageView.d.ts +6 -0
  134. package/dist/chat/ui/ImageView.js +81 -0
  135. package/dist/chat/ui/LimitState.d.ts +8 -0
  136. package/dist/chat/ui/LimitState.js +112 -0
  137. package/dist/chat/ui/Markdown.d.ts +18 -0
  138. package/dist/chat/ui/Markdown.js +159 -0
  139. package/dist/chat/ui/MessageActions.d.ts +9 -0
  140. package/dist/chat/ui/MessageActions.js +53 -0
  141. package/dist/chat/ui/Pickers.d.ts +21 -0
  142. package/dist/chat/ui/Pickers.js +111 -0
  143. package/dist/chat/ui/PromptComposer.d.ts +13 -0
  144. package/dist/chat/ui/PromptComposer.js +56 -0
  145. package/dist/chat/ui/QuoteButton.d.ts +7 -0
  146. package/dist/chat/ui/QuoteButton.js +74 -0
  147. package/dist/chat/ui/ResumeCompactionDock.d.ts +5 -0
  148. package/dist/chat/ui/ResumeCompactionDock.js +14 -0
  149. package/dist/chat/ui/RunSettings.d.ts +28 -0
  150. package/dist/chat/ui/RunSettings.js +153 -0
  151. package/dist/chat/ui/Scrubber.d.ts +17 -0
  152. package/dist/chat/ui/Scrubber.js +118 -0
  153. package/dist/chat/ui/SubagentControls.d.ts +11 -0
  154. package/dist/chat/ui/SubagentControls.js +36 -0
  155. package/dist/chat/ui/SubagentInfo.d.ts +4 -0
  156. package/dist/chat/ui/SubagentInfo.js +34 -0
  157. package/dist/chat/ui/SubagentStopButton.d.ts +7 -0
  158. package/dist/chat/ui/SubagentStopButton.js +34 -0
  159. package/dist/chat/ui/SubagentTimeline.d.ts +4 -0
  160. package/dist/chat/ui/SubagentTimeline.js +107 -0
  161. package/dist/chat/ui/Timeline.d.ts +6 -0
  162. package/dist/chat/ui/Timeline.js +424 -0
  163. package/dist/chat/ui/TimelineMenu.d.ts +12 -0
  164. package/dist/chat/ui/TimelineMenu.js +54 -0
  165. package/dist/chat/ui/UnifiedDiff.d.ts +11 -0
  166. package/dist/chat/ui/UnifiedDiff.js +66 -0
  167. package/dist/chat/ui/UploadThumb.d.ts +4 -0
  168. package/dist/chat/ui/UploadThumb.js +11 -0
  169. package/dist/chat/ui/Welcome.d.ts +4 -0
  170. package/dist/chat/ui/Welcome.js +16 -0
  171. package/dist/chat/ui/chips.d.ts +4 -0
  172. package/dist/chat/ui/chips.js +11 -0
  173. package/dist/chat/ui/code-lines.d.ts +18 -0
  174. package/dist/chat/ui/code-lines.js +46 -0
  175. package/dist/chat/ui/code-streaming.d.ts +1 -0
  176. package/dist/chat/ui/code-streaming.js +3 -0
  177. package/dist/chat/ui/code-theme.d.ts +3 -0
  178. package/dist/chat/ui/code-theme.js +12 -0
  179. package/dist/chat/ui/composer/chips.d.ts +6 -0
  180. package/dist/chat/ui/composer/chips.js +31 -0
  181. package/dist/chat/ui/composer/editor.d.ts +11 -0
  182. package/dist/chat/ui/composer/editor.js +98 -0
  183. package/dist/chat/ui/composer/insert.d.ts +2 -0
  184. package/dist/chat/ui/composer/insert.js +19 -0
  185. package/dist/chat/ui/composer/keys.d.ts +30 -0
  186. package/dist/chat/ui/composer/keys.js +100 -0
  187. package/dist/chat/ui/diff-theme.d.ts +2 -0
  188. package/dist/chat/ui/diff-theme.js +20 -0
  189. package/dist/chat/ui/file-links.d.ts +5 -0
  190. package/dist/chat/ui/file-links.js +20 -0
  191. package/dist/chat/ui/find-reveal.d.ts +7 -0
  192. package/dist/chat/ui/find-reveal.js +13 -0
  193. package/dist/chat/ui/full-diff.d.ts +6 -0
  194. package/dist/chat/ui/full-diff.js +19 -0
  195. package/dist/chat/ui/icons.d.ts +4 -0
  196. package/dist/chat/ui/icons.js +20 -0
  197. package/dist/chat/ui/markdown-blocks.d.ts +6 -0
  198. package/dist/chat/ui/markdown-blocks.js +103 -0
  199. package/dist/chat/ui/quote-selection.d.ts +3 -0
  200. package/dist/chat/ui/quote-selection.js +46 -0
  201. package/dist/chat/ui/rehype-chips.d.ts +23 -0
  202. package/dist/chat/ui/rehype-chips.js +32 -0
  203. package/dist/chat/ui/rehype-fade.d.ts +23 -0
  204. package/dist/chat/ui/rehype-fade.js +36 -0
  205. package/dist/chat/ui/remark-html-as-text.d.ts +8 -0
  206. package/dist/chat/ui/remark-html-as-text.js +26 -0
  207. package/dist/chat/ui/reply-context.d.ts +5 -0
  208. package/dist/chat/ui/reply-context.js +2 -0
  209. package/dist/chat/ui/reveal.d.ts +13 -0
  210. package/dist/chat/ui/reveal.js +114 -0
  211. package/dist/chat/ui/rows/ForksRow.d.ts +4 -0
  212. package/dist/chat/ui/rows/ForksRow.js +38 -0
  213. package/dist/chat/ui/rows/MessageRows.d.ts +40 -0
  214. package/dist/chat/ui/rows/MessageRows.js +204 -0
  215. package/dist/chat/ui/rows/Rows.d.ts +12 -0
  216. package/dist/chat/ui/rows/Rows.js +61 -0
  217. package/dist/chat/ui/rows/SubagentRow.d.ts +17 -0
  218. package/dist/chat/ui/rows/SubagentRow.js +101 -0
  219. package/dist/chat/ui/rows/TaskRow.d.ts +8 -0
  220. package/dist/chat/ui/rows/TaskRow.js +63 -0
  221. package/dist/chat/ui/rows/WorkRows.d.ts +47 -0
  222. package/dist/chat/ui/rows/WorkRows.js +168 -0
  223. package/dist/chat/ui/rows/WorkflowRow.d.ts +8 -0
  224. package/dist/chat/ui/rows/WorkflowRow.js +48 -0
  225. package/dist/chat/ui/rows/row-rhythm.d.ts +6 -0
  226. package/dist/chat/ui/rows/row-rhythm.js +34 -0
  227. package/dist/chat/ui/useToggleSet.d.ts +6 -0
  228. package/dist/chat/ui/useToggleSet.js +25 -0
  229. package/dist/host.d.ts +195 -0
  230. package/dist/host.js +55 -0
  231. package/dist/lazy.d.ts +6 -0
  232. package/dist/lazy.js +57 -0
  233. package/dist/locales/en/agent-chat.json +446 -0
  234. package/dist/locales/en/agent-prompts.json +42 -0
  235. package/dist/locales/en/agent-providers.json +131 -0
  236. package/dist/locales/en/agent-usage.json +131 -0
  237. package/dist/locales/nl/agent-chat.json +446 -0
  238. package/dist/locales/nl/agent-prompts.json +42 -0
  239. package/dist/locales/nl/agent-providers.json +131 -0
  240. package/dist/locales/nl/agent-usage.json +131 -0
  241. package/dist/locales.d.ts +5 -0
  242. package/dist/locales.js +20 -0
  243. package/dist/mounted-registry.d.ts +7 -0
  244. package/dist/mounted-registry.js +39 -0
  245. package/dist/port-transport.d.ts +6 -0
  246. package/dist/port-transport.js +129 -0
  247. package/dist/prompts/logic/focus.d.ts +2 -0
  248. package/dist/prompts/logic/focus.js +23 -0
  249. package/dist/prompts/logic/keys.d.ts +42 -0
  250. package/dist/prompts/logic/keys.js +94 -0
  251. package/dist/prompts/logic/prompts.d.ts +34 -0
  252. package/dist/prompts/logic/prompts.fixtures.d.ts +5 -0
  253. package/dist/prompts/logic/prompts.fixtures.js +79 -0
  254. package/dist/prompts/logic/prompts.js +32 -0
  255. package/dist/prompts/logic/subjects.d.ts +50 -0
  256. package/dist/prompts/logic/subjects.js +63 -0
  257. package/dist/prompts/logic/useFocusAfterAnswer.d.ts +6 -0
  258. package/dist/prompts/logic/useFocusAfterAnswer.js +28 -0
  259. package/dist/prompts/logic/usePromptSession.d.ts +24 -0
  260. package/dist/prompts/logic/usePromptSession.js +87 -0
  261. package/dist/prompts/ui/ApprovalBody.d.ts +13 -0
  262. package/dist/prompts/ui/ApprovalBody.js +34 -0
  263. package/dist/prompts/ui/PromptActions.d.ts +27 -0
  264. package/dist/prompts/ui/PromptActions.js +18 -0
  265. package/dist/prompts/ui/PromptCard.d.ts +16 -0
  266. package/dist/prompts/ui/PromptCard.js +50 -0
  267. package/dist/prompts/ui/PromptView.d.ts +2 -0
  268. package/dist/prompts/ui/PromptView.js +47 -0
  269. package/dist/prompts/ui/QuestionBody.d.ts +9 -0
  270. package/dist/prompts/ui/QuestionBody.js +87 -0
  271. package/dist/providers/AccountColors.d.ts +5 -0
  272. package/dist/providers/AccountColors.js +8 -0
  273. package/dist/providers/AccountDetail.d.ts +12 -0
  274. package/dist/providers/AccountDetail.js +69 -0
  275. package/dist/providers/AccountVariables.d.ts +8 -0
  276. package/dist/providers/AccountVariables.js +43 -0
  277. package/dist/providers/AddAccountForm.d.ts +10 -0
  278. package/dist/providers/AddAccountForm.js +54 -0
  279. package/dist/providers/CliDetail.d.ts +12 -0
  280. package/dist/providers/CliDetail.js +54 -0
  281. package/dist/providers/ProvidersPane.d.ts +9 -0
  282. package/dist/providers/ProvidersPane.js +86 -0
  283. package/dist/providers/account-actions.d.ts +7 -0
  284. package/dist/providers/account-actions.js +57 -0
  285. package/dist/providers/parts.d.ts +9 -0
  286. package/dist/providers/parts.js +18 -0
  287. package/dist/providers/provider-abilities.d.ts +2 -0
  288. package/dist/providers/provider-abilities.js +7 -0
  289. package/dist/providers/variables.d.ts +13 -0
  290. package/dist/providers/variables.js +63 -0
  291. package/dist/scope.d.ts +11 -0
  292. package/dist/scope.js +10 -0
  293. package/dist/settings/sections.d.ts +13 -0
  294. package/dist/settings/sections.js +32 -0
  295. package/dist/state/chats.d.ts +44 -0
  296. package/dist/state/chats.js +199 -0
  297. package/dist/state/provider-accounts.d.ts +17 -0
  298. package/dist/state/provider-accounts.js +67 -0
  299. package/dist/state/providers.d.ts +17 -0
  300. package/dist/state/providers.js +33 -0
  301. package/dist/state/usage.d.ts +44 -0
  302. package/dist/state/usage.js +121 -0
  303. package/dist/storage.d.ts +20 -0
  304. package/dist/storage.js +89 -0
  305. package/dist/theme.css +196 -0
  306. package/dist/transport.d.ts +24 -0
  307. package/dist/transport.js +18 -0
  308. package/dist/ui/PlainTextarea.d.ts +2 -0
  309. package/dist/ui/PlainTextarea.js +5 -0
  310. package/dist/usage/LimitsList.d.ts +18 -0
  311. package/dist/usage/LimitsList.js +73 -0
  312. package/dist/usage/UsageBreakdown.d.ts +9 -0
  313. package/dist/usage/UsageBreakdown.js +67 -0
  314. package/dist/usage/UsageChart.d.ts +10 -0
  315. package/dist/usage/UsageChart.js +47 -0
  316. package/dist/usage/UsageDialog.d.ts +6 -0
  317. package/dist/usage/UsageDialog.js +12 -0
  318. package/dist/usage/UsageLimits.d.ts +1 -0
  319. package/dist/usage/UsageLimits.js +57 -0
  320. package/dist/usage/UsageLimitsCard.d.ts +4 -0
  321. package/dist/usage/UsageLimitsCard.js +26 -0
  322. package/dist/usage/UsagePage.d.ts +7 -0
  323. package/dist/usage/UsagePage.js +154 -0
  324. package/dist/usage/UsagePane.d.ts +3 -0
  325. package/dist/usage/UsagePane.js +21 -0
  326. package/dist/usage/UsageSummary.d.ts +12 -0
  327. package/dist/usage/UsageSummary.js +15 -0
  328. package/dist/usage/UsageTiles.d.ts +7 -0
  329. package/dist/usage/UsageTiles.js +19 -0
  330. package/dist/usage/format.d.ts +13 -0
  331. package/dist/usage/format.js +55 -0
  332. package/dist/usage/limit-groups.d.ts +22 -0
  333. package/dist/usage/limit-groups.js +86 -0
  334. package/dist/usage/limits.d.ts +4 -0
  335. package/dist/usage/limits.js +58 -0
  336. package/dist/usage/money.d.ts +1 -0
  337. package/dist/usage/money.js +13 -0
  338. package/dist/usage/summary.d.ts +34 -0
  339. package/dist/usage/summary.js +94 -0
  340. package/package.json +895 -3
  341. package/src/agents/AccountDot.tsx +14 -0
  342. package/src/agents/AgentIcon.tsx +26 -0
  343. package/src/agents/ModelOptionControl.tsx +68 -0
  344. package/src/agents/ProviderLogo.tsx +20 -0
  345. package/src/agents/account-limits.ts +61 -0
  346. package/src/agents/accounts.ts +137 -0
  347. package/src/agents/model-name.ts +70 -0
  348. package/src/agents/model-options.ts +21 -0
  349. package/src/agents/provider-paths.ts +10 -0
  350. package/src/agents/status-look.ts +43 -0
  351. package/src/chat/account-choice.ts +44 -0
  352. package/src/chat/actions.ts +72 -0
  353. package/src/chat/activity.ts +8 -0
  354. package/src/chat/attachments.ts +118 -0
  355. package/src/chat/bookmarks.ts +87 -0
  356. package/src/chat/chat-client.ts +345 -0
  357. package/src/chat/chat-references.ts +11 -0
  358. package/src/chat/drafts.ts +165 -0
  359. package/src/chat/forks.ts +33 -0
  360. package/src/chat/guards.ts +84 -0
  361. package/src/chat/logic/ansi.ts +206 -0
  362. package/src/chat/logic/answers.ts +8 -0
  363. package/src/chat/logic/bookmarks.ts +42 -0
  364. package/src/chat/logic/context-usage.ts +36 -0
  365. package/src/chat/logic/fork.ts +200 -0
  366. package/src/chat/logic/handback.ts +10 -0
  367. package/src/chat/logic/json.ts +4 -0
  368. package/src/chat/logic/limit.ts +46 -0
  369. package/src/chat/logic/resume-compaction.ts +71 -0
  370. package/src/chat/logic/scrubber.ts +289 -0
  371. package/src/chat/logic/thread-cards.ts +64 -0
  372. package/src/chat/logic/timeline-copy.ts +65 -0
  373. package/src/chat/logic/timeline-target.ts +52 -0
  374. package/src/chat/logic/timeline.ts +419 -0
  375. package/src/chat/logic/tool-catalog.ts +62 -0
  376. package/src/chat/logic/tools.ts +102 -0
  377. package/src/chat/logic/welcome.ts +13 -0
  378. package/src/chat/logic/workflow.ts +66 -0
  379. package/src/chat/mentions.ts +232 -0
  380. package/src/chat/persisted-json.ts +67 -0
  381. package/src/chat/preferences.ts +179 -0
  382. package/src/chat/quote.ts +26 -0
  383. package/src/chat/recent-messages.ts +59 -0
  384. package/src/chat/resume-compaction-dismissals.ts +17 -0
  385. package/src/chat/runtime-modes.ts +18 -0
  386. package/src/chat/stash.ts +122 -0
  387. package/src/chat/subagent-conversation.ts +203 -0
  388. package/src/chat/subagent-list.ts +141 -0
  389. package/src/chat/subagent-support.ts +21 -0
  390. package/src/chat/subagent-view.ts +137 -0
  391. package/src/chat/thumbnails.ts +108 -0
  392. package/src/chat/timeline-flash.ts +33 -0
  393. package/src/chat/timeline-scroll.ts +199 -0
  394. package/src/chat/ui/AccountPill.tsx +31 -0
  395. package/src/chat/ui/AnsiOutput.tsx +18 -0
  396. package/src/chat/ui/BookmarkMarker.tsx +108 -0
  397. package/src/chat/ui/BookmarkSubmenu.tsx +53 -0
  398. package/src/chat/ui/ChatActivity.tsx +198 -0
  399. package/src/chat/ui/ChatReferenceChip.tsx +28 -0
  400. package/src/chat/ui/CodeBlock.tsx +175 -0
  401. package/src/chat/ui/Composer.tsx +1326 -0
  402. package/src/chat/ui/ComposerInput.tsx +187 -0
  403. package/src/chat/ui/DiffPool.tsx +36 -0
  404. package/src/chat/ui/EditDiff.tsx +30 -0
  405. package/src/chat/ui/FadingWords.tsx +18 -0
  406. package/src/chat/ui/ImageView.tsx +159 -0
  407. package/src/chat/ui/LimitState.tsx +165 -0
  408. package/src/chat/ui/Markdown.tsx +258 -0
  409. package/src/chat/ui/MessageActions.tsx +88 -0
  410. package/src/chat/ui/Pickers.tsx +272 -0
  411. package/src/chat/ui/PromptComposer.tsx +119 -0
  412. package/src/chat/ui/QuoteButton.tsx +103 -0
  413. package/src/chat/ui/ResumeCompactionDock.tsx +28 -0
  414. package/src/chat/ui/RunSettings.tsx +546 -0
  415. package/src/chat/ui/Scrubber.tsx +265 -0
  416. package/src/chat/ui/SubagentControls.tsx +66 -0
  417. package/src/chat/ui/SubagentInfo.tsx +67 -0
  418. package/src/chat/ui/SubagentStopButton.tsx +45 -0
  419. package/src/chat/ui/SubagentTimeline.tsx +170 -0
  420. package/src/chat/ui/Timeline.tsx +628 -0
  421. package/src/chat/ui/TimelineMenu.tsx +124 -0
  422. package/src/chat/ui/UnifiedDiff.tsx +99 -0
  423. package/src/chat/ui/UploadThumb.tsx +13 -0
  424. package/src/chat/ui/Welcome.tsx +22 -0
  425. package/src/chat/ui/chips.ts +15 -0
  426. package/src/chat/ui/code-lines.ts +60 -0
  427. package/src/chat/ui/code-streaming.ts +4 -0
  428. package/src/chat/ui/code-theme.ts +15 -0
  429. package/src/chat/ui/composer/chips.ts +43 -0
  430. package/src/chat/ui/composer/editor.ts +118 -0
  431. package/src/chat/ui/composer/insert.ts +22 -0
  432. package/src/chat/ui/composer/keys.ts +144 -0
  433. package/src/chat/ui/diff-theme.ts +23 -0
  434. package/src/chat/ui/file-links.ts +24 -0
  435. package/src/chat/ui/find-reveal.ts +23 -0
  436. package/src/chat/ui/full-diff.ts +25 -0
  437. package/src/chat/ui/icons.tsx +24 -0
  438. package/src/chat/ui/markdown-blocks.ts +123 -0
  439. package/src/chat/ui/quote-selection.ts +53 -0
  440. package/src/chat/ui/rehype-chips.ts +62 -0
  441. package/src/chat/ui/rehype-fade.ts +64 -0
  442. package/src/chat/ui/remark-html-as-text.ts +36 -0
  443. package/src/chat/ui/reply-context.ts +4 -0
  444. package/src/chat/ui/reveal.ts +135 -0
  445. package/src/chat/ui/rows/ForksRow.tsx +79 -0
  446. package/src/chat/ui/rows/MessageRows.tsx +415 -0
  447. package/src/chat/ui/rows/Rows.tsx +81 -0
  448. package/src/chat/ui/rows/SubagentRow.tsx +209 -0
  449. package/src/chat/ui/rows/TaskRow.tsx +91 -0
  450. package/src/chat/ui/rows/WorkRows.tsx +445 -0
  451. package/src/chat/ui/rows/WorkflowRow.tsx +120 -0
  452. package/src/chat/ui/rows/row-rhythm.ts +37 -0
  453. package/src/chat/ui/useToggleSet.ts +39 -0
  454. package/src/host.ts +324 -0
  455. package/src/lazy.tsx +64 -0
  456. package/src/locales/en/agent-chat.json +446 -0
  457. package/src/locales/en/agent-prompts.json +42 -0
  458. package/src/locales/en/agent-providers.json +131 -0
  459. package/src/locales/en/agent-usage.json +131 -0
  460. package/src/locales/nl/agent-chat.json +446 -0
  461. package/src/locales/nl/agent-prompts.json +42 -0
  462. package/src/locales/nl/agent-providers.json +131 -0
  463. package/src/locales/nl/agent-usage.json +131 -0
  464. package/src/locales.ts +33 -0
  465. package/src/mounted-registry.ts +45 -0
  466. package/src/port-transport.ts +146 -0
  467. package/src/prompts/logic/focus.ts +25 -0
  468. package/src/prompts/logic/keys.ts +116 -0
  469. package/src/prompts/logic/prompts.fixtures.ts +84 -0
  470. package/src/prompts/logic/prompts.ts +58 -0
  471. package/src/prompts/logic/subjects.ts +122 -0
  472. package/src/prompts/logic/useFocusAfterAnswer.ts +38 -0
  473. package/src/prompts/logic/usePromptSession.ts +114 -0
  474. package/src/prompts/ui/ApprovalBody.tsx +109 -0
  475. package/src/prompts/ui/PromptActions.tsx +109 -0
  476. package/src/prompts/ui/PromptCard.tsx +101 -0
  477. package/src/prompts/ui/PromptView.tsx +111 -0
  478. package/src/prompts/ui/QuestionBody.tsx +174 -0
  479. package/src/providers/AccountColors.tsx +8 -0
  480. package/src/providers/AccountDetail.tsx +209 -0
  481. package/src/providers/AccountVariables.tsx +126 -0
  482. package/src/providers/AddAccountForm.tsx +121 -0
  483. package/src/providers/CliDetail.tsx +157 -0
  484. package/src/providers/ProvidersPane.tsx +236 -0
  485. package/src/providers/account-actions.ts +67 -0
  486. package/src/providers/parts.tsx +25 -0
  487. package/src/providers/provider-abilities.ts +9 -0
  488. package/src/providers/variables.ts +87 -0
  489. package/src/scope.ts +30 -0
  490. package/src/settings/sections.ts +47 -0
  491. package/src/state/chats.ts +279 -0
  492. package/src/state/provider-accounts.ts +87 -0
  493. package/src/state/providers.ts +50 -0
  494. package/src/state/usage.ts +196 -0
  495. package/src/storage.ts +113 -0
  496. package/src/theme.css +196 -0
  497. package/src/transport.ts +50 -0
  498. package/src/ui/PlainTextarea.tsx +6 -0
  499. package/src/usage/LimitsList.tsx +232 -0
  500. package/src/usage/UsageBreakdown.tsx +210 -0
  501. package/src/usage/UsageChart.tsx +113 -0
  502. package/src/usage/UsageDialog.tsx +20 -0
  503. package/src/usage/UsageLimits.tsx +145 -0
  504. package/src/usage/UsageLimitsCard.tsx +36 -0
  505. package/src/usage/UsagePage.tsx +276 -0
  506. package/src/usage/UsagePane.tsx +52 -0
  507. package/src/usage/UsageSummary.tsx +50 -0
  508. package/src/usage/UsageTiles.tsx +34 -0
  509. package/src/usage/format.ts +71 -0
  510. package/src/usage/limit-groups.ts +119 -0
  511. package/src/usage/limits.ts +71 -0
  512. package/src/usage/money.ts +14 -0
  513. package/src/usage/summary.ts +137 -0
@@ -0,0 +1,53 @@
1
+ import { useMemo } from 'react';
2
+ import clsx from 'clsx';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Bookmark, Pencil, X } from 'lucide-react';
5
+ import { goToBookmark, removeBookmark } from '../bookmarks';
6
+ import { bookmarkLabel, bookmarksInThreadOrder } from '../logic/bookmarks';
7
+ import { chatHost } from '../../host';
8
+ import { useChatScope } from '../../scope';
9
+ import { useChatRow } from '../../state/chats';
10
+ import { Icon, Menu } from '@adecore/ui';
11
+
12
+ /*
13
+ * "Bookmarks" in the menu of a chat node or a chat view: the chat's bookmarks in the order of the
14
+ * thread, each a jump to its message, with rename and remove on the row. A chat without bookmarks
15
+ * draws nothing.
16
+ */
17
+ export function BookmarkSubmenu({ chatId }: { chatId: string }) {
18
+ const { t } = useTranslation('agent-chat');
19
+ const scope = useChatScope();
20
+ const bookmarks = useChatRow(chatId, (row) => row?.bookmarks);
21
+ const order = useChatRow(chatId, (row) => row?.order);
22
+ const place = chatHost().useChatPlace(chatId);
23
+ const sorted = useMemo(() => bookmarksInThreadOrder(bookmarks ?? [], order ?? []), [bookmarks, order]);
24
+ if (sorted.length === 0) {
25
+ return null;
26
+ }
27
+
28
+ const go = (itemId: string, rename: boolean): void => {
29
+ if (!goToBookmark(scope, chatId, itemId, rename)) {
30
+ place.go();
31
+ }
32
+ };
33
+
34
+ return (
35
+ <Menu.SubmenuRoot>
36
+ <Menu.SubmenuTrigger>
37
+ <Icon icon={Bookmark} size={14} /> {t('bookmarks.menu')}
38
+ </Menu.SubmenuTrigger>
39
+ <Menu.Popup className="max-w-80 min-w-56">
40
+ {sorted.map((bookmark) => (
41
+ <Menu.Row key={bookmark.itemId} aria-label={bookmarkLabel(bookmark)}>
42
+ <Menu.Item onClick={() => go(bookmark.itemId, false)}>
43
+ <Icon icon={Bookmark} size={14} className="shrink-0 text-accent" />
44
+ <span className={clsx('min-w-0 grow truncate', bookmark.name === undefined && 'text-text-muted')}>{bookmarkLabel(bookmark)}</span>
45
+ </Menu.Item>
46
+ <Menu.RowAction icon={Pencil} label={t('bookmarks.rename')} onClick={() => go(bookmark.itemId, true)} />
47
+ <Menu.RowAction icon={X} label={t('bookmarks.remove')} onClick={() => void removeBookmark(scope, chatId, bookmark)} />
48
+ </Menu.Row>
49
+ ))}
50
+ </Menu.Popup>
51
+ </Menu.SubmenuRoot>
52
+ );
53
+ }
@@ -0,0 +1,198 @@
1
+ import { useMemo, type ReactNode } from 'react';
2
+ import clsx from 'clsx';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Activity, Square, SquareTerminal } from 'lucide-react';
5
+ import type { ChatBackgroundTask, ChatSubagentItem } from '@adecore/agent-contracts';
6
+ import { backgroundCounts } from '../activity';
7
+ import { badgeCountOf, entryTimeOf, flyoutSubagents, statusWordOf, subagentTitle, summaryWordOf, taskIdOf } from '../subagent-list';
8
+ import { useSubagentSupport } from '../subagent-support';
9
+ import { canOpenSubagent, crumbOf, openFromMain, useSubagentTrail } from '../subagent-view';
10
+ import { SubagentStopButton } from './SubagentStopButton';
11
+ import { formatElapsedShort } from '@adecore/ui/format';
12
+ import { Icon, IconButton, Popover, Surface, useNow } from '@adecore/ui';
13
+ import { useChatRow } from '../../state/chats';
14
+ import { chatHost } from '../../host';
15
+ import { useChatScope } from '../../scope';
16
+ import { useChatActions } from '../actions';
17
+ import { statusLookOf, type StatusWord } from '../../agents/status-look';
18
+
19
+ const NO_TASKS: readonly ChatBackgroundTask[] = [];
20
+
21
+ const NO_ITEMS: readonly string[] = [];
22
+
23
+ const ROW_BODY = 'flex min-h-9 min-w-0 grow items-center gap-2.5 rounded-md px-2.5 py-1.5 text-left';
24
+
25
+ /* What the chat keeps working on beside the thread, in one small line over the composer: its sub-agents and what runs in the background. */
26
+ export function ChatActivity({ chatId }: { chatId: string }) {
27
+ const { t } = useTranslation('agent-chat');
28
+ const order = useChatRow(chatId, (row) => row?.order) ?? NO_ITEMS;
29
+ const structure = useChatRow(chatId, (row) => row?.structure);
30
+ const subagents = useMemo(() => (structure === undefined ? [] : flyoutSubagents(order, structure)), [order, structure]);
31
+ const { id } = useChatScope();
32
+ const tasks = chatHost().tasks.useTasks(id);
33
+ const words = useMemo(
34
+ () =>
35
+ subagents.map((item) => {
36
+ const taskId = taskIdOf(item);
37
+ return statusWordOf(item, taskId === null ? null : (tasks?.[taskId] ?? null));
38
+ }),
39
+ [subagents, tasks]
40
+ );
41
+ const background = useChatRow(chatId, (row) => row?.info.background) ?? NO_TASKS;
42
+ if (subagents.length === 0 && background.length === 0) {
43
+ return null;
44
+ }
45
+ const { shells, monitors } = backgroundCounts(background);
46
+ const Slot = chatHost().SubagentSlot;
47
+ return (
48
+ <div className="mb-3 flex flex-wrap items-center gap-2">
49
+ {subagents.length > 0 &&
50
+ (Slot === null ? (
51
+ <Flyout
52
+ icon={<StatusIcon word={summaryWordOf(words)} />}
53
+ label={t('activity.subagents', { count: badgeCountOf(words) })}
54
+ heading={t('activity.subagents', { count: subagents.length })}
55
+ >
56
+ <SubagentRows chatId={chatId} items={subagents} />
57
+ </Flyout>
58
+ ) : (
59
+ <Slot chatId={chatId} items={subagents} />
60
+ ))}
61
+ {background.length > 0 && (
62
+ <Flyout
63
+ icon={<Icon icon={shells > 0 ? SquareTerminal : Activity} size={14} />}
64
+ label={[shells > 0 && t('activity.shells', { count: shells }), monitors > 0 && t('activity.monitors', { count: monitors })]
65
+ .filter(Boolean)
66
+ .join(' · ')}
67
+ >
68
+ <BackgroundTaskRows chatId={chatId} tasks={background} />
69
+ </Flyout>
70
+ )}
71
+ </div>
72
+ );
73
+ }
74
+
75
+ export function StatusIcon({ word }: { word: StatusWord }) {
76
+ const look = statusLookOf(word);
77
+ return <Icon icon={look.icon} size={14} className={clsx('shrink-0', look.tone, look.spins && 'animate-spin')} />;
78
+ }
79
+
80
+ /* A chip that opens a list over it, headed by what the chip says unless the list counts more than the chip does. */
81
+ function Flyout({ icon, label, heading = label, children }: { icon: ReactNode; label: string; heading?: string; children: ReactNode }) {
82
+ return (
83
+ <Popover.Root>
84
+ <Surface
85
+ render={<Popover.Trigger />}
86
+ className="pointer-events-auto inline-flex h-7.5 items-center gap-1.5 rounded-full px-3 text-xs text-text-muted hover:text-text"
87
+ >
88
+ {icon}
89
+ {label}
90
+ </Surface>
91
+ <Popover.Popup side="top" className="w-80">
92
+ <div className="px-2.5 pt-1.5 pb-1 text-xs text-text-faint">{heading}</div>
93
+ {children}
94
+ </Popover.Popup>
95
+ </Popover.Root>
96
+ );
97
+ }
98
+
99
+ /* One line of a flyout; with `onOpen` the line leads somewhere and closes the flyout on the way. */
100
+ function ActivityRow({
101
+ icon,
102
+ title,
103
+ detail,
104
+ time,
105
+ onOpen,
106
+ children
107
+ }: {
108
+ icon: ReactNode;
109
+ title: ReactNode;
110
+ detail?: string | null;
111
+ time: string;
112
+ onOpen?: () => void;
113
+ children?: ReactNode;
114
+ }) {
115
+ const body = (
116
+ <>
117
+ {icon}
118
+ <span className="flex min-w-0 grow flex-col">
119
+ <span className="truncate text-sm text-text">{title}</span>
120
+ {detail && <span className="truncate font-mono text-xs text-text-faint">{detail}</span>}
121
+ </span>
122
+ {time !== '' && <span className="shrink-0 text-xs text-text-faint tabular-nums">{time}</span>}
123
+ </>
124
+ );
125
+ // The stop is a button of its own beside the line, since a button cannot hold another.
126
+ return (
127
+ <div className={clsx('flex items-center gap-1 rounded-md', onOpen && 'hover:bg-surface-hover')}>
128
+ {onOpen ? (
129
+ <Popover.Close className={ROW_BODY} onClick={onOpen}>
130
+ {body}
131
+ </Popover.Close>
132
+ ) : (
133
+ <div className={ROW_BODY}>{body}</div>
134
+ )}
135
+ {children}
136
+ </div>
137
+ );
138
+ }
139
+
140
+ /* Only drawn while the flyout is open, so the clock ticks for nobody otherwise. */
141
+ function SubagentRows({ chatId, items }: { chatId: string; items: readonly ChatSubagentItem[] }) {
142
+ const now = useNow(
143
+ 1000,
144
+ items.some((item) => item.status === 'running')
145
+ );
146
+ return items.map((item) => <SubagentEntry key={item.id} chatId={chatId} item={item} now={now} />);
147
+ }
148
+
149
+ function SubagentEntry({ chatId, item, now }: { chatId: string; item: ChatSubagentItem; now: number }) {
150
+ const { t } = useTranslation('agent-chat');
151
+ const { id } = useChatScope();
152
+ const refused = useSubagentSupport((s) => s.unsupported[id] === true);
153
+ const task = chatHost().tasks.useTask(id, taskIdOf(item));
154
+ const { show } = useSubagentTrail(chatId);
155
+ const word = statusWordOf(item, task);
156
+ return (
157
+ <ActivityRow
158
+ icon={<StatusIcon word={word} />}
159
+ title={
160
+ <>
161
+ {subagentTitle(item)}
162
+ {/* The icon says the state to the eye; a screen reader hears it with the title. */}
163
+ <span className="sr-only">, {t(`common.status.${word}`)}</span>
164
+ </>
165
+ }
166
+ time={entryTimeOf(item, task, now)}
167
+ onOpen={canOpenSubagent(item, refused) ? () => show(openFromMain(crumbOf(item))) : undefined}
168
+ >
169
+ <SubagentStopButton chatId={chatId} item={item} className="mr-1.5" />
170
+ </ActivityRow>
171
+ );
172
+ }
173
+
174
+ /* Only drawn while the flyout is open, so the clock ticks for nobody otherwise. */
175
+ function BackgroundTaskRows({ chatId, tasks }: { chatId: string; tasks: readonly ChatBackgroundTask[] }) {
176
+ const { t } = useTranslation('agent-chat');
177
+ const now = useNow(1000);
178
+ const actions = useChatActions();
179
+
180
+ function stop(taskId: string): void {
181
+ void actions.stopTask(chatId, taskId).catch((e: unknown) => {
182
+ const description = e instanceof Error ? e.message : t('subagents.noAnswer');
183
+ chatHost().notify({ kind: 'error', title: t('activity.stopFailed'), description });
184
+ });
185
+ }
186
+
187
+ return tasks.map((task) => (
188
+ <ActivityRow
189
+ key={task.id}
190
+ icon={<Icon icon={task.kind === 'shell' ? SquareTerminal : Activity} size={14} className="shrink-0 text-text-faint" />}
191
+ title={task.description || task.command || t(`activity.${task.kind}`)}
192
+ detail={task.description ? task.command : null}
193
+ time={formatElapsedShort(now - task.startedAt)}
194
+ >
195
+ <IconButton icon={Square} size="xs" label={t('activity.stop')} className="mr-1.5" onClick={() => stop(task.id)} />
196
+ </ActivityRow>
197
+ ));
198
+ }
@@ -0,0 +1,28 @@
1
+ import clsx from 'clsx';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { MessageSquare, X } from 'lucide-react';
4
+ import { chatHost } from '../../host';
5
+ import { CHIP_IN_MESSAGE, MENTION_TONE } from './chips';
6
+ import { Icon, Tooltip } from '@adecore/ui';
7
+
8
+ /* A chat attached to a message, under the title it has now rather than the one it had when it was attached. */
9
+ export function ChatReferenceChip({ chatId, onRemove }: { chatId: string; onRemove?: () => void }) {
10
+ const { t } = useTranslation('agent-chat');
11
+ const chat = chatHost()
12
+ .useReferableChats()
13
+ .find((candidate) => candidate.id === chatId);
14
+ const title = chat === undefined ? t('chatReference.gone') : chat.title || t('chatReference.untitled');
15
+ return (
16
+ <span className={clsx(MENTION_TONE, CHIP_IN_MESSAGE)}>
17
+ <Icon icon={MessageSquare} size={12} className="shrink-0" />
18
+ <span className="truncate">{title}</span>
19
+ {onRemove && (
20
+ <Tooltip label={t('chatReference.remove', { title })} name>
21
+ <button className="-mr-0.5 shrink-0 rounded-sm opacity-70 hover:opacity-100" onClick={onRemove}>
22
+ <Icon icon={X} size={12} />
23
+ </button>
24
+ </Tooltip>
25
+ )}
26
+ </span>
27
+ );
28
+ }
@@ -0,0 +1,175 @@
1
+ import { Fragment, memo, useContext, useEffect, useMemo, useState, type CSSProperties } from 'react';
2
+ import clsx from 'clsx';
3
+ import type { BundledLanguage, BundledTheme, createHighlighter, ThemeRegistration } from 'shiki/bundle/web';
4
+ import { IncrementalLines, type CodeToken, type Tokenize } from './code-lines';
5
+ import { CodeStreamingContext } from './code-streaming';
6
+ import { WHOLE_FADE_CLASS } from './rehype-fade';
7
+ import { shikiThemeOf, useCodeTheme } from './code-theme';
8
+
9
+ type Highlighter = Awaited<ReturnType<typeof createHighlighter>>;
10
+ type GrammarState = ReturnType<Highlighter['getLastGrammarState']>;
11
+ // A plain language needs no grammar; shiki draws it in the theme's own foreground.
12
+ const PLAIN = 'text';
13
+
14
+ let highlighter: Highlighter | null = null;
15
+ let highlighterLoad: Promise<Highlighter> | null = null;
16
+ const loadedLanguages = new Set<string>([PLAIN]);
17
+ const languageLoads = new Map<string, Promise<void>>();
18
+ const loadedThemes = new Set<string>();
19
+ const themeLoads = new Map<string, Promise<void>>();
20
+ const tokenizers = new Map<string, { tokenize: Tokenize<GrammarState>; fg: string | undefined }>();
21
+
22
+ // Shiki loads on the first code block, not with the app; the web bundle covers the languages a coding agent writes.
23
+ function loadHighlighter(): Promise<Highlighter> {
24
+ highlighterLoad ??= import('shiki/bundle/web').then(async ({ createHighlighter }) => {
25
+ highlighter = await createHighlighter({ themes: [], langs: [] });
26
+ return highlighter;
27
+ });
28
+ return highlighterLoad;
29
+ }
30
+
31
+ function resolveLanguage(lang: string, known: Record<string, unknown> | null): string {
32
+ return known !== null && lang in known ? lang : PLAIN;
33
+ }
34
+
35
+ let bundled: Record<string, unknown> | null = null;
36
+
37
+ /* Loads the highlighter and the grammar of one language, once each. */
38
+ function loadLanguage(lang: string): Promise<void> {
39
+ let load = languageLoads.get(lang);
40
+ if (!load) {
41
+ load = Promise.all([loadHighlighter(), import('shiki/bundle/web')]).then(async ([loaded, { bundledLanguages }]) => {
42
+ bundled = bundledLanguages;
43
+ const language = resolveLanguage(lang, bundledLanguages);
44
+ if (!loadedLanguages.has(language)) {
45
+ await loaded.loadLanguage(language as BundledLanguage);
46
+ loadedLanguages.add(language);
47
+ }
48
+ });
49
+ languageLoads.set(lang, load);
50
+ }
51
+ return load;
52
+ }
53
+
54
+ /* Loads a theme into the highlighter, once. */
55
+ function loadTheme(theme: string): Promise<void> {
56
+ let load = themeLoads.get(theme);
57
+ if (!load) {
58
+ load = loadHighlighter().then(async (loaded) => {
59
+ await loaded.loadTheme(shikiThemeOf(theme) as BundledTheme | ThemeRegistration);
60
+ loadedThemes.add(theme);
61
+ });
62
+ themeLoads.set(theme, load);
63
+ }
64
+ return load;
65
+ }
66
+
67
+ /* The tokenizer for a language in a theme, or null while shiki, the grammar or the theme is still on its way. */
68
+ function tokenizerFor(lang: string, theme: string): { tokenize: Tokenize<GrammarState>; fg: string | undefined } | null {
69
+ if (highlighter === null || bundled === null || !languageLoads.has(lang) || !loadedThemes.has(theme)) {
70
+ return null;
71
+ }
72
+ const language = resolveLanguage(lang, bundled);
73
+ if (!loadedLanguages.has(language)) {
74
+ return null;
75
+ }
76
+ const key = `${language}:${theme}`;
77
+ let entry = tokenizers.get(key);
78
+ if (!entry) {
79
+ const loaded = highlighter;
80
+ const options = { lang: language as BundledLanguage, theme };
81
+ entry = {
82
+ tokenize: (code, state) => {
83
+ const result = loaded.codeToTokens(code, state ? { ...options, grammarState: state } : options);
84
+ return { lines: result.tokens, state: result.grammarState };
85
+ },
86
+ fg: loaded.getTheme(theme).fg
87
+ };
88
+ tokenizers.set(key, entry);
89
+ }
90
+ return entry;
91
+ }
92
+
93
+ function styleOf(token: CodeToken): CSSProperties | undefined {
94
+ const style = token.fontStyle ?? 0;
95
+ if (token.color === undefined && style <= 0) {
96
+ return undefined;
97
+ }
98
+ const decorations = [style & 4 ? 'underline' : '', style & 8 ? 'line-through' : ''].filter(Boolean).join(' ');
99
+ return {
100
+ color: token.color,
101
+ ...(style & 1 ? { fontStyle: 'italic' } : {}),
102
+ ...(style & 2 ? { fontWeight: 'bold' } : {}),
103
+ ...(decorations ? { textDecoration: decorations } : {})
104
+ };
105
+ }
106
+
107
+ /* A line whose tokens did not change keeps its elements, so a selection in it survives the next delta. */
108
+ const CodeLine = memo(function CodeLine({ tokens }: { tokens: CodeToken[] }) {
109
+ return (
110
+ <span className="line">
111
+ {tokens.map((token, index) => (
112
+ <span key={index} style={styleOf(token)}>
113
+ {token.content}
114
+ </span>
115
+ ))}
116
+ </span>
117
+ );
118
+ });
119
+
120
+ /*
121
+ * A fenced block, highlighted a line at a time. Until shiki and the grammar are there it holds its
122
+ * place invisibly in the same shape, so there is never a frame of bare code that turns colored. A
123
+ * fence that is still open is tokenized as it grows, and the same component carries on once it
124
+ * closes, so closing it does not draw it again.
125
+ */
126
+ export function CodeBlock({ code, lang }: { code: string; lang: string }) {
127
+ const theme = useCodeTheme();
128
+ const streaming = useContext(CodeStreamingContext);
129
+ // What loaded last, so a block whose theme changes draws again once the new one is in.
130
+ const [loaded, setLoaded] = useState<string | null>(null);
131
+ const tokenizer = tokenizerFor(lang, theme);
132
+ // Only a block that had to wait fades in; one drawn highlighted from its first frame just stands there.
133
+ const [waited] = useState(tokenizer === null);
134
+ const lines = useMemo(() => (tokenizer ? new IncrementalLines(tokenizer.tokenize) : null), [tokenizer]);
135
+ const tokens = useMemo(() => lines?.update(code, !streaming) ?? null, [lines, code, streaming]);
136
+
137
+ useEffect(() => {
138
+ if (tokenizer !== null) {
139
+ return;
140
+ }
141
+ let cancelled = false;
142
+ Promise.all([loadLanguage(lang), loadTheme(theme)])
143
+ .then(() => !cancelled && setLoaded(`${lang}:${theme}`))
144
+ .catch(() => !cancelled && setLoaded('failed'));
145
+ return () => {
146
+ cancelled = true;
147
+ };
148
+ }, [lang, theme, tokenizer]);
149
+
150
+ if (tokenizer === null || tokens === null) {
151
+ const failed = loaded === 'failed';
152
+ return (
153
+ <div className={clsx('chat-code', !failed && 'invisible')} aria-hidden={failed ? undefined : true}>
154
+ <pre>
155
+ <code>{code}</code>
156
+ </pre>
157
+ </div>
158
+ );
159
+ }
160
+ return (
161
+ <div className={clsx('chat-code', waited && WHOLE_FADE_CLASS)}>
162
+ <pre style={{ color: tokenizer.fg }}>
163
+ <code>
164
+ {tokens.map((line, index) => (
165
+ // Lines only ever grow at the end, so the place of a line is a stable key.
166
+ <Fragment key={index}>
167
+ <CodeLine tokens={line} />
168
+ {index < tokens.length - 1 && '\n'}
169
+ </Fragment>
170
+ ))}
171
+ </code>
172
+ </pre>
173
+ </div>
174
+ );
175
+ }