@adecore/agents-react 0.23.1 → 0.24.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 (344) hide show
  1. package/README.md +14 -11
  2. package/dist/agents/AccountDot.js +3 -1
  3. package/dist/agents/accounts.d.ts +3 -2
  4. package/dist/agents/accounts.js +5 -6
  5. package/dist/chat/actions.d.ts +2 -2
  6. package/dist/chat/actions.js +8 -5
  7. package/dist/chat/bookmarks.d.ts +5 -3
  8. package/dist/chat/bookmarks.js +13 -14
  9. package/dist/chat/chat-client.d.ts +1 -0
  10. package/dist/chat/chat-client.js +7 -0
  11. package/dist/chat/composer-send.d.ts +3 -0
  12. package/dist/chat/composer-send.js +22 -0
  13. package/dist/chat/forks.js +22 -5
  14. package/dist/chat/index.d.ts +31 -0
  15. package/dist/chat/index.js +28 -0
  16. package/dist/chat/logic/embedded-code.d.ts +8 -0
  17. package/dist/chat/logic/embedded-code.js +371 -0
  18. package/dist/chat/logic/fork.js +1 -0
  19. package/dist/chat/logic/goal.d.ts +6 -0
  20. package/dist/chat/logic/goal.js +26 -0
  21. package/dist/chat/logic/scrubber.d.ts +1 -1
  22. package/dist/chat/logic/scrubber.js +6 -5
  23. package/dist/chat/logic/thought.d.ts +3 -0
  24. package/dist/chat/logic/thought.js +35 -0
  25. package/dist/chat/logic/thread-cards.js +2 -0
  26. package/dist/chat/logic/timeline.d.ts +5 -0
  27. package/dist/chat/logic/timeline.js +22 -1
  28. package/dist/chat/logic/tool-catalog.d.ts +1 -0
  29. package/dist/chat/logic/tool-catalog.js +3 -1
  30. package/dist/chat/logic/tools.d.ts +14 -0
  31. package/dist/chat/logic/tools.js +57 -0
  32. package/dist/chat/preferences.d.ts +6 -0
  33. package/dist/chat/preferences.js +19 -3
  34. package/dist/chat/reading-width.d.ts +9 -0
  35. package/dist/chat/reading-width.js +27 -0
  36. package/dist/chat/send-key.d.ts +10 -0
  37. package/dist/chat/send-key.js +24 -0
  38. package/dist/chat/ui/BookmarkMarker.js +4 -2
  39. package/dist/chat/ui/BookmarkSubmenu.js +4 -3
  40. package/dist/chat/ui/ChatActivity.js +14 -10
  41. package/dist/chat/ui/ChatReferenceChip.js +3 -4
  42. package/dist/chat/ui/CodeBlock.d.ts +2 -1
  43. package/dist/chat/ui/CodeBlock.js +14 -96
  44. package/dist/chat/ui/Composer.d.ts +1 -1
  45. package/dist/chat/ui/Composer.js +130 -907
  46. package/dist/chat/ui/ComposerInput.js +5 -4
  47. package/dist/chat/ui/EditDiff.js +3 -2
  48. package/dist/chat/ui/GoalDock.d.ts +9 -0
  49. package/dist/chat/ui/GoalDock.js +63 -0
  50. package/dist/chat/ui/LimitState.js +5 -3
  51. package/dist/chat/ui/Markdown.d.ts +2 -1
  52. package/dist/chat/ui/Markdown.js +70 -31
  53. package/dist/chat/ui/MermaidBlock.d.ts +3 -0
  54. package/dist/chat/ui/MermaidBlock.js +73 -0
  55. package/dist/chat/ui/MessageActions.js +12 -7
  56. package/dist/chat/ui/Pickers.js +6 -5
  57. package/dist/chat/ui/PromptComposer.js +4 -3
  58. package/dist/chat/ui/RowTime.d.ts +3 -0
  59. package/dist/chat/ui/RowTime.js +12 -0
  60. package/dist/chat/ui/RunSettings.js +5 -4
  61. package/dist/chat/ui/ShellCommand.d.ts +3 -0
  62. package/dist/chat/ui/ShellCommand.js +110 -0
  63. package/dist/chat/ui/SubagentInfo.js +4 -3
  64. package/dist/chat/ui/SubagentStopButton.js +4 -3
  65. package/dist/chat/ui/SubagentTimeline.js +4 -1
  66. package/dist/chat/ui/Timeline.js +70 -40
  67. package/dist/chat/ui/TimelineMenu.d.ts +2 -1
  68. package/dist/chat/ui/TimelineMenu.js +18 -8
  69. package/dist/chat/ui/UnifiedDiff.js +3 -2
  70. package/dist/chat/ui/VisualFrame.js +6 -7
  71. package/dist/chat/ui/code-theme.d.ts +2 -1
  72. package/dist/chat/ui/code-theme.js +4 -4
  73. package/dist/chat/ui/composer/ComposerControls.d.ts +27 -0
  74. package/dist/chat/ui/composer/ComposerControls.js +34 -0
  75. package/dist/chat/ui/composer/ComposerMenu.d.ts +4 -0
  76. package/dist/chat/ui/composer/ComposerMenu.js +46 -0
  77. package/dist/chat/ui/composer/DraftParts.d.ts +18 -0
  78. package/dist/chat/ui/composer/DraftParts.js +25 -0
  79. package/dist/chat/ui/composer/QueuedMessages.d.ts +10 -0
  80. package/dist/chat/ui/composer/QueuedMessages.js +20 -0
  81. package/dist/chat/ui/composer/key-handler.d.ts +20 -0
  82. package/dist/chat/ui/composer/key-handler.js +150 -0
  83. package/dist/chat/ui/composer/keys.d.ts +7 -2
  84. package/dist/chat/ui/composer/keys.js +19 -9
  85. package/dist/chat/ui/composer/placeholder.d.ts +9 -0
  86. package/dist/chat/ui/composer/placeholder.js +38 -0
  87. package/dist/chat/ui/composer/surface.d.ts +1 -0
  88. package/dist/chat/ui/composer/surface.js +44 -0
  89. package/dist/chat/ui/composer/use-chat-controls.d.ts +28 -0
  90. package/dist/chat/ui/composer/use-chat-controls.js +112 -0
  91. package/dist/chat/ui/composer/use-compaction-offer.d.ts +2 -0
  92. package/dist/chat/ui/composer/use-compaction-offer.js +23 -0
  93. package/dist/chat/ui/composer/use-composer-draft.d.ts +14 -0
  94. package/dist/chat/ui/composer/use-composer-draft.js +39 -0
  95. package/dist/chat/ui/composer/use-composer-menus.d.ts +52 -0
  96. package/dist/chat/ui/composer/use-composer-menus.js +232 -0
  97. package/dist/chat/ui/composer/use-composer-submit.d.ts +32 -0
  98. package/dist/chat/ui/composer/use-composer-submit.js +113 -0
  99. package/dist/chat/ui/composer/use-draft-files.d.ts +27 -0
  100. package/dist/chat/ui/composer/use-draft-files.js +111 -0
  101. package/dist/chat/ui/composer/use-notice.d.ts +6 -0
  102. package/dist/chat/ui/composer/use-notice.js +18 -0
  103. package/dist/chat/ui/composer/use-pending-requests.d.ts +2 -0
  104. package/dist/chat/ui/composer/use-pending-requests.js +22 -0
  105. package/dist/chat/ui/composer/use-prompt-history.d.ts +8 -0
  106. package/dist/chat/ui/composer/use-prompt-history.js +57 -0
  107. package/dist/chat/ui/composer/use-prompt-hold.d.ts +6 -0
  108. package/dist/chat/ui/composer/use-prompt-hold.js +48 -0
  109. package/dist/chat/ui/composer/use-take-back.d.ts +13 -0
  110. package/dist/chat/ui/composer/use-take-back.js +82 -0
  111. package/dist/chat/ui/diff/EditorEditDiff.js +4 -3
  112. package/dist/chat/ui/diff/LegacyDiffPool.js +3 -2
  113. package/dist/chat/ui/diff/LegacyEditDiff.js +3 -2
  114. package/dist/chat/ui/diff/LegacyUnifiedDiff.js +3 -2
  115. package/dist/chat/ui/diff/syntax-runtime.d.ts +2 -1
  116. package/dist/chat/ui/diff/syntax-runtime.js +9 -5
  117. package/dist/chat/ui/diff-syntax.d.ts +2 -2
  118. package/dist/chat/ui/diff-syntax.js +1 -2
  119. package/dist/chat/ui/diff-theme.js +10 -9
  120. package/dist/chat/ui/file-links.d.ts +1 -1
  121. package/dist/chat/ui/file-links.js +7 -5
  122. package/dist/chat/ui/highlighter.d.ts +27 -0
  123. package/dist/chat/ui/highlighter.js +151 -0
  124. package/dist/chat/ui/index.d.ts +67 -0
  125. package/dist/chat/ui/index.js +64 -0
  126. package/dist/chat/ui/intelligent-ui/UiReply.js +3 -2
  127. package/dist/chat/ui/intelligent-ui/renderers/content.js +3 -2
  128. package/dist/chat/ui/intelligent-ui/use-ui-links.js +2 -2
  129. package/dist/chat/ui/intelligent-ui/use-ui-queries.js +4 -3
  130. package/dist/chat/ui/markdown-blocks.d.ts +8 -0
  131. package/dist/chat/ui/markdown-blocks.js +260 -12
  132. package/dist/chat/ui/mermaid.d.ts +23 -0
  133. package/dist/chat/ui/mermaid.js +93 -0
  134. package/dist/chat/ui/rows/ForksRow.js +5 -3
  135. package/dist/chat/ui/rows/GeneratedImageRow.js +6 -4
  136. package/dist/chat/ui/rows/MessageRows.d.ts +4 -0
  137. package/dist/chat/ui/rows/MessageRows.js +36 -13
  138. package/dist/chat/ui/rows/Rows.js +3 -1
  139. package/dist/chat/ui/rows/SubagentRow.js +3 -2
  140. package/dist/chat/ui/rows/VisualRow.js +3 -2
  141. package/dist/chat/ui/rows/WorkRows.d.ts +6 -3
  142. package/dist/chat/ui/rows/WorkRows.js +57 -20
  143. package/dist/chat/ui/rows/WorkflowRow.js +3 -2
  144. package/dist/chat/ui/timestamp-format.d.ts +1 -0
  145. package/dist/chat/ui/timestamp-format.js +2 -0
  146. package/dist/chat/ui/use-diff-source-tokens.js +6 -3
  147. package/dist/chat/ui/visual-theme.js +3 -2
  148. package/dist/host.d.ts +9 -6
  149. package/dist/host.js +19 -8
  150. package/dist/index.d.ts +8 -0
  151. package/dist/index.js +9 -0
  152. package/dist/locales/en/agent-chat.d.ts +658 -0
  153. package/dist/locales/en/agent-chat.js +657 -0
  154. package/dist/locales/en/agent-prompts.d.ts +44 -0
  155. package/dist/locales/en/agent-prompts.js +43 -0
  156. package/dist/locales/en/agent-providers.d.ts +132 -0
  157. package/dist/locales/en/agent-providers.js +131 -0
  158. package/dist/locales/en/agent-usage.d.ts +132 -0
  159. package/dist/locales/en/agent-usage.js +131 -0
  160. package/dist/locales/nl/agent-chat.d.ts +658 -0
  161. package/dist/locales/nl/agent-chat.js +657 -0
  162. package/dist/locales/nl/agent-prompts.d.ts +44 -0
  163. package/dist/locales/nl/agent-prompts.js +43 -0
  164. package/dist/locales/nl/agent-providers.d.ts +132 -0
  165. package/dist/locales/nl/agent-providers.js +131 -0
  166. package/dist/locales/nl/agent-usage.d.ts +132 -0
  167. package/dist/locales/nl/agent-usage.js +131 -0
  168. package/dist/locales.js +9 -8
  169. package/dist/prompts/index.d.ts +11 -0
  170. package/dist/prompts/index.js +12 -0
  171. package/dist/prompts/logic/subjects.d.ts +2 -1
  172. package/dist/prompts/logic/subjects.js +6 -4
  173. package/dist/prompts/ui/ApprovalBody.js +6 -4
  174. package/dist/prompts/ui/PromptCard.js +5 -4
  175. package/dist/prompts/ui/PromptView.js +3 -2
  176. package/dist/prompts/ui/QuestionBody.js +3 -2
  177. package/dist/providers/AccountColors.js +2 -2
  178. package/dist/providers/AccountDetail.js +7 -6
  179. package/dist/providers/AccountVariables.js +3 -1
  180. package/dist/providers/AddAccountForm.js +3 -2
  181. package/dist/providers/ProvidersPane.js +4 -3
  182. package/dist/providers/account-actions.d.ts +4 -3
  183. package/dist/providers/account-actions.js +6 -7
  184. package/dist/providers/index.d.ts +16 -0
  185. package/dist/providers/index.js +17 -0
  186. package/dist/providers/provider-abilities.d.ts +2 -1
  187. package/dist/providers/provider-abilities.js +4 -0
  188. package/dist/state/chats.js +4 -2
  189. package/dist/state/index.d.ts +4 -0
  190. package/dist/state/index.js +5 -0
  191. package/dist/testing/index.d.ts +1 -0
  192. package/dist/testing/index.js +2 -0
  193. package/dist/{prompts/logic/prompts.fixtures.d.ts → testing/prompts.d.ts} +1 -1
  194. package/dist/theme.css +13 -3
  195. package/dist/usage/UsageBreakdown.js +3 -2
  196. package/dist/usage/UsageLimits.js +4 -3
  197. package/dist/usage/UsagePage.js +3 -2
  198. package/dist/usage/index.d.ts +15 -0
  199. package/dist/usage/index.js +15 -0
  200. package/package.json +53 -1031
  201. package/src/agents/AccountDot.tsx +3 -1
  202. package/src/agents/accounts.ts +6 -6
  203. package/src/chat/actions.ts +8 -5
  204. package/src/chat/bookmarks.ts +23 -14
  205. package/src/chat/chat-client.ts +8 -0
  206. package/src/chat/composer-send.ts +27 -0
  207. package/src/chat/forks.ts +27 -6
  208. package/src/chat/index.ts +98 -0
  209. package/src/chat/logic/embedded-code.ts +409 -0
  210. package/src/chat/logic/fork.ts +1 -0
  211. package/src/chat/logic/goal.ts +33 -0
  212. package/src/chat/logic/scrubber.ts +6 -5
  213. package/src/chat/logic/thought.ts +40 -0
  214. package/src/chat/logic/thread-cards.ts +2 -0
  215. package/src/chat/logic/timeline.ts +25 -1
  216. package/src/chat/logic/tool-catalog.ts +5 -1
  217. package/src/chat/logic/tools.ts +76 -0
  218. package/src/chat/preferences.ts +26 -3
  219. package/src/chat/reading-width.ts +39 -0
  220. package/src/chat/send-key.ts +38 -0
  221. package/src/chat/ui/BookmarkMarker.tsx +4 -2
  222. package/src/chat/ui/BookmarkSubmenu.tsx +4 -3
  223. package/src/chat/ui/ChatActivity.tsx +14 -10
  224. package/src/chat/ui/ChatReferenceChip.tsx +3 -4
  225. package/src/chat/ui/CodeBlock.tsx +33 -124
  226. package/src/chat/ui/Composer.tsx +212 -1358
  227. package/src/chat/ui/ComposerInput.tsx +5 -4
  228. package/src/chat/ui/EditDiff.tsx +3 -2
  229. package/src/chat/ui/GoalDock.tsx +104 -0
  230. package/src/chat/ui/LimitState.tsx +5 -3
  231. package/src/chat/ui/Markdown.tsx +113 -39
  232. package/src/chat/ui/MermaidBlock.tsx +119 -0
  233. package/src/chat/ui/MessageActions.tsx +13 -8
  234. package/src/chat/ui/Pickers.tsx +8 -7
  235. package/src/chat/ui/PromptComposer.tsx +4 -3
  236. package/src/chat/ui/RowTime.tsx +21 -0
  237. package/src/chat/ui/RunSettings.tsx +9 -4
  238. package/src/chat/ui/ShellCommand.tsx +134 -0
  239. package/src/chat/ui/SubagentInfo.tsx +4 -3
  240. package/src/chat/ui/SubagentStopButton.tsx +4 -3
  241. package/src/chat/ui/SubagentTimeline.tsx +5 -1
  242. package/src/chat/ui/Timeline.tsx +167 -105
  243. package/src/chat/ui/TimelineMenu.tsx +42 -7
  244. package/src/chat/ui/UnifiedDiff.tsx +3 -2
  245. package/src/chat/ui/VisualFrame.tsx +6 -7
  246. package/src/chat/ui/code-theme.ts +4 -4
  247. package/src/chat/ui/composer/ComposerControls.tsx +147 -0
  248. package/src/chat/ui/composer/ComposerMenu.tsx +138 -0
  249. package/src/chat/ui/composer/DraftParts.tsx +86 -0
  250. package/src/chat/ui/composer/QueuedMessages.tsx +64 -0
  251. package/src/chat/ui/composer/key-handler.ts +174 -0
  252. package/src/chat/ui/composer/keys.ts +32 -10
  253. package/src/chat/ui/composer/placeholder.ts +48 -0
  254. package/src/chat/ui/composer/surface.ts +45 -0
  255. package/src/chat/ui/composer/use-chat-controls.ts +134 -0
  256. package/src/chat/ui/composer/use-compaction-offer.ts +25 -0
  257. package/src/chat/ui/composer/use-composer-draft.ts +71 -0
  258. package/src/chat/ui/composer/use-composer-menus.ts +289 -0
  259. package/src/chat/ui/composer/use-composer-submit.ts +163 -0
  260. package/src/chat/ui/composer/use-draft-files.ts +135 -0
  261. package/src/chat/ui/composer/use-notice.ts +28 -0
  262. package/src/chat/ui/composer/use-pending-requests.ts +23 -0
  263. package/src/chat/ui/composer/use-prompt-history.ts +76 -0
  264. package/src/chat/ui/composer/use-prompt-hold.ts +58 -0
  265. package/src/chat/ui/composer/use-take-back.ts +91 -0
  266. package/src/chat/ui/diff/DiffFallbackBoundary.tsx +2 -2
  267. package/src/chat/ui/diff/EditorEditDiff.tsx +4 -3
  268. package/src/chat/ui/diff/LegacyDiffPool.tsx +3 -2
  269. package/src/chat/ui/diff/LegacyEditDiff.tsx +3 -2
  270. package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +3 -2
  271. package/src/chat/ui/diff/syntax-runtime.ts +10 -5
  272. package/src/chat/ui/diff-syntax.ts +2 -2
  273. package/src/chat/ui/diff-theme.ts +10 -9
  274. package/src/chat/ui/file-links.ts +7 -5
  275. package/src/chat/ui/highlighter.ts +185 -0
  276. package/src/chat/ui/index.ts +109 -0
  277. package/src/chat/ui/intelligent-ui/UiReply.tsx +5 -4
  278. package/src/chat/ui/intelligent-ui/renderers/content.tsx +3 -2
  279. package/src/chat/ui/intelligent-ui/use-ui-links.ts +2 -2
  280. package/src/chat/ui/intelligent-ui/use-ui-queries.ts +4 -3
  281. package/src/chat/ui/markdown-blocks.ts +300 -12
  282. package/src/chat/ui/mermaid.ts +118 -0
  283. package/src/chat/ui/rows/ForksRow.tsx +5 -3
  284. package/src/chat/ui/rows/GeneratedImageRow.tsx +6 -4
  285. package/src/chat/ui/rows/MessageRows.tsx +73 -18
  286. package/src/chat/ui/rows/Rows.tsx +14 -1
  287. package/src/chat/ui/rows/SubagentRow.tsx +3 -2
  288. package/src/chat/ui/rows/VisualRow.tsx +3 -2
  289. package/src/chat/ui/rows/WorkRows.tsx +148 -47
  290. package/src/chat/ui/rows/WorkflowRow.tsx +3 -2
  291. package/src/chat/ui/timestamp-format.ts +2 -0
  292. package/src/chat/ui/use-diff-source-tokens.ts +10 -4
  293. package/src/chat/ui/visual-theme.ts +3 -2
  294. package/src/host.ts +31 -15
  295. package/src/index.ts +49 -0
  296. package/src/locales/en/agent-chat.ts +657 -0
  297. package/src/locales/en/agent-prompts.ts +43 -0
  298. package/src/locales/en/agent-providers.ts +131 -0
  299. package/src/locales/en/agent-usage.ts +131 -0
  300. package/src/locales/nl/agent-chat.ts +657 -0
  301. package/src/locales/nl/agent-prompts.ts +43 -0
  302. package/src/locales/nl/agent-providers.ts +131 -0
  303. package/src/locales/nl/agent-usage.ts +132 -0
  304. package/src/locales.ts +9 -8
  305. package/src/prompts/index.ts +35 -0
  306. package/src/prompts/logic/subjects.ts +12 -4
  307. package/src/prompts/ui/ApprovalBody.tsx +6 -4
  308. package/src/prompts/ui/PromptCard.tsx +11 -4
  309. package/src/prompts/ui/PromptView.tsx +3 -2
  310. package/src/prompts/ui/QuestionBody.tsx +3 -2
  311. package/src/providers/AccountColors.tsx +2 -2
  312. package/src/providers/AccountDetail.tsx +13 -7
  313. package/src/providers/AccountVariables.tsx +3 -1
  314. package/src/providers/AddAccountForm.tsx +3 -2
  315. package/src/providers/ProvidersPane.tsx +4 -3
  316. package/src/providers/account-actions.ts +7 -7
  317. package/src/providers/index.ts +17 -0
  318. package/src/providers/provider-abilities.ts +6 -1
  319. package/src/state/chats.ts +4 -2
  320. package/src/state/index.ts +39 -0
  321. package/src/testing/index.ts +2 -0
  322. package/src/{prompts/logic/prompts.fixtures.ts → testing/prompts.ts} +1 -1
  323. package/src/theme.css +13 -3
  324. package/src/usage/UsageBreakdown.tsx +3 -2
  325. package/src/usage/UsageLimits.tsx +4 -3
  326. package/src/usage/UsagePage.tsx +3 -2
  327. package/src/usage/index.ts +16 -0
  328. package/dist/locales/en/agent-chat.json +0 -597
  329. package/dist/locales/en/agent-prompts.json +0 -43
  330. package/dist/locales/en/agent-providers.json +0 -131
  331. package/dist/locales/en/agent-usage.json +0 -131
  332. package/dist/locales/nl/agent-chat.json +0 -597
  333. package/dist/locales/nl/agent-prompts.json +0 -43
  334. package/dist/locales/nl/agent-providers.json +0 -131
  335. package/dist/locales/nl/agent-usage.json +0 -131
  336. package/src/locales/en/agent-chat.json +0 -597
  337. package/src/locales/en/agent-prompts.json +0 -43
  338. package/src/locales/en/agent-providers.json +0 -131
  339. package/src/locales/en/agent-usage.json +0 -131
  340. package/src/locales/nl/agent-chat.json +0 -597
  341. package/src/locales/nl/agent-prompts.json +0 -43
  342. package/src/locales/nl/agent-providers.json +0 -131
  343. package/src/locales/nl/agent-usage.json +0 -131
  344. /package/dist/{prompts/logic/prompts.fixtures.js → testing/prompts.js} +0 -0
@@ -1,10 +1,14 @@
1
1
  import type { AgentKind, ChatPreferencesPayload, ModelSelection, ProviderInfo, ProviderAccounts, RuntimeMode } from '@adecore/agent-contracts';
2
+ import { type ChatReadingWidth } from './reading-width.js';
3
+ import { type ChatSendKey } from './send-key.js';
2
4
  export interface ChatPreferences {
3
5
  selectionByProvider: Partial<Record<AgentKind, ModelSelection>>;
4
6
  lastProvider: AgentKind | null;
5
7
  runtimeMode: RuntimeMode;
6
8
  terminalRuntimeMode: RuntimeMode;
7
9
  accountByMachine: Record<string, Partial<Record<AgentKind, string>>>;
10
+ readingWidth: ChatReadingWidth;
11
+ sendKey: ChatSendKey;
8
12
  changedAt: number;
9
13
  }
10
14
  export declare const DEFAULT_CHAT_PREFERENCES: ChatPreferences;
@@ -22,6 +26,8 @@ export declare let CHAT_PREFERENCES_KEY: string;
22
26
  export declare function chatPreferencesKey(): string;
23
27
  export declare function reloadChatPreferences(): void;
24
28
  export declare function rememberChatPreferences(patch: Partial<ChatPreferences>): void;
29
+ export declare function rememberReadingWidth(readingWidth: ChatReadingWidth): void;
30
+ export declare function rememberSendKey(sendKey: ChatSendKey): void;
25
31
  export declare function rememberChatSelection(provider: AgentKind, selection: ModelSelection): void;
26
32
  export declare function rememberChatAccount(scopeId: string, provider: AgentKind, account: string | null): void;
27
33
  export declare function forgetChatSelection(provider: AgentKind): void;
@@ -1,5 +1,7 @@
1
1
  import { chatStorageKey, chatStorageLegacyKeys, createChatStore, onChatStorageConfiguration } from '../storage.js';
2
2
  import { persistedJson } from './persisted-json.js';
3
+ import { DEFAULT_READING_WIDTH, isChatReadingWidth } from './reading-width.js';
4
+ import { DEFAULT_SEND_KEY, isChatSendKey } from './send-key.js';
3
5
  const storageKey = () => chatStorageKey('preferences');
4
6
  export const DEFAULT_CHAT_PREFERENCES = {
5
7
  selectionByProvider: {},
@@ -7,6 +9,8 @@ export const DEFAULT_CHAT_PREFERENCES = {
7
9
  runtimeMode: 'full-access',
8
10
  terminalRuntimeMode: 'full-access',
9
11
  accountByMachine: {},
12
+ readingWidth: DEFAULT_READING_WIDTH,
13
+ sendKey: DEFAULT_SEND_KEY,
10
14
  changedAt: 0
11
15
  };
12
16
  /*
@@ -26,6 +30,8 @@ export function parseChatPreferences(raw) {
26
30
  runtimeMode: stored.runtimeMode ?? DEFAULT_CHAT_PREFERENCES.runtimeMode,
27
31
  terminalRuntimeMode: stored.terminalRuntimeMode ?? DEFAULT_CHAT_PREFERENCES.terminalRuntimeMode,
28
32
  accountByMachine: { ...stored.accountByMachine },
33
+ readingWidth: isChatReadingWidth(stored.readingWidth) ? stored.readingWidth : DEFAULT_READING_WIDTH,
34
+ sendKey: isChatSendKey(stored.sendKey) ? stored.sendKey : DEFAULT_SEND_KEY,
29
35
  changedAt: typeof stored.changedAt === 'number' ? stored.changedAt : 0
30
36
  };
31
37
  }
@@ -108,15 +114,25 @@ export function chatPreferencesKey() {
108
114
  export function reloadChatPreferences() {
109
115
  useChatPreferences.setState(storage.read(), true);
110
116
  }
117
+ function store(preferences) {
118
+ useChatPreferences.setState(preferences);
119
+ storage.write(preferences);
120
+ }
111
121
  function write(preferences) {
112
- const next = { ...preferences, changedAt: Date.now() };
113
- useChatPreferences.setState(next);
114
- storage.write(next);
122
+ store({ ...preferences, changedAt: Date.now() });
115
123
  }
116
124
  /* The composer writes here on every change and the settings dialog too; both edit the same remembered default. */
117
125
  export function rememberChatPreferences(patch) {
118
126
  write({ ...useChatPreferences.getState(), ...patch });
119
127
  }
128
+ /* The reading width leaves `changedAt` alone: it is no pick a machine weighs against another client's. */
129
+ export function rememberReadingWidth(readingWidth) {
130
+ store({ ...useChatPreferences.getState(), readingWidth });
131
+ }
132
+ /* Leaves `changedAt` alone, like the reading width. */
133
+ export function rememberSendKey(sendKey) {
134
+ store({ ...useChatPreferences.getState(), sendKey });
135
+ }
120
136
  export function rememberChatSelection(provider, selection) {
121
137
  const current = useChatPreferences.getState();
122
138
  const previous = current.selectionByProvider[provider];
@@ -0,0 +1,9 @@
1
+ import type { CSSProperties } from 'react';
2
+ export type ChatReadingWidth = 'narrow' | 'comfortable' | 'full';
3
+ export declare const CHAT_READING_WIDTHS: readonly ChatReadingWidth[];
4
+ export declare const DEFAULT_READING_WIDTH: ChatReadingWidth;
5
+ export declare function isChatReadingWidth(value: unknown): value is ChatReadingWidth;
6
+ export declare function readingMeasure(width: ChatReadingWidth): number;
7
+ export declare function readingWidthStyle(width: ChatReadingWidth): CSSProperties;
8
+ export declare function readingWidthLabel(width: ChatReadingWidth): string;
9
+ export declare function readingWidthHint(width: ChatReadingWidth): string;
@@ -0,0 +1,27 @@
1
+ import i18next from 'i18next';
2
+ export const CHAT_READING_WIDTHS = ['narrow', 'comfortable', 'full'];
3
+ export const DEFAULT_READING_WIDTH = 'comfortable';
4
+ // About 75 and 90 characters of the thread's text a line.
5
+ const MEASURES = {
6
+ narrow: 640,
7
+ comfortable: 768,
8
+ full: Number.POSITIVE_INFINITY
9
+ };
10
+ export function isChatReadingWidth(value) {
11
+ return typeof value === 'string' && CHAT_READING_WIDTHS.includes(value);
12
+ }
13
+ /* The widest the content column runs, in pixels; `full` is unbounded. */
14
+ export function readingMeasure(width) {
15
+ return MEASURES[width];
16
+ }
17
+ /* What `.chat-column-content` and `.chat-composer-content` read their width from. */
18
+ export function readingWidthStyle(width) {
19
+ const measure = MEASURES[width];
20
+ return { '--chat-measure': Number.isFinite(measure) ? `${measure}px` : '100%' };
21
+ }
22
+ export function readingWidthLabel(width) {
23
+ return i18next.t(`agent-chat:readingWidth.${width}.label`);
24
+ }
25
+ export function readingWidthHint(width) {
26
+ return i18next.t(`agent-chat:readingWidth.${width}.hint`);
27
+ }
@@ -0,0 +1,10 @@
1
+ import { type Shortcut } from '@adecore/ui';
2
+ export type ChatSendKey = 'enter' | 'mod-enter-multiline' | 'mod-enter';
3
+ export declare const CHAT_SEND_KEYS: readonly ChatSendKey[];
4
+ export declare const DEFAULT_SEND_KEY: ChatSendKey;
5
+ export declare const SEND_AND_NEW_SHORTCUT: Shortcut;
6
+ export declare function isChatSendKey(value: unknown): value is ChatSendKey;
7
+ export declare function modSends(sendKey: ChatSendKey, multiline: boolean): boolean;
8
+ export declare function sendShortcut(sendKey: ChatSendKey, multiline: boolean): Shortcut;
9
+ export declare function sendKeyLabel(sendKey: ChatSendKey, apple: boolean): string;
10
+ export declare function sendKeyHint(sendKey: ChatSendKey, apple: boolean): string;
@@ -0,0 +1,24 @@
1
+ import i18next from 'i18next';
2
+ import { KEY_SHORTCUTS, shortcut } from '@adecore/ui';
3
+ export const CHAT_SEND_KEYS = ['enter', 'mod-enter-multiline', 'mod-enter'];
4
+ export const DEFAULT_SEND_KEY = 'enter';
5
+ /* Sends, then opens a new chat through the host's `openNewChat`; a key of the composer, for a menu or a list of keys to print. */
6
+ export const SEND_AND_NEW_SHORTCUT = /* @__PURE__ */ shortcut('Mod+Alt+Enter');
7
+ export function isChatSendKey(value) {
8
+ return typeof value === 'string' && CHAT_SEND_KEYS.includes(value);
9
+ }
10
+ /* Whether Enter alone adds a line to a message, so only Mod+Enter sends it. */
11
+ export function modSends(sendKey, multiline) {
12
+ return sendKey === 'mod-enter' || (sendKey === 'mod-enter-multiline' && multiline);
13
+ }
14
+ /* The key that sends a message, as a hint prints it. */
15
+ export function sendShortcut(sendKey, multiline) {
16
+ return modSends(sendKey, multiline) ? KEY_SHORTCUTS.modEnter : KEY_SHORTCUTS.enter;
17
+ }
18
+ /* `apple` writes Mod as the Command sign, and as Ctrl elsewhere. */
19
+ export function sendKeyLabel(sendKey, apple) {
20
+ return i18next.t(`agent-chat:sendKey.${sendKey}.label`, { mod: apple ? '⌘' : 'Ctrl' });
21
+ }
22
+ export function sendKeyHint(sendKey, apple) {
23
+ return i18next.t(`agent-chat:sendKey.${sendKey}.hint`, { mod: apple ? '⌘' : 'Ctrl' });
24
+ }
@@ -6,6 +6,7 @@ import { Bookmark, Pencil, X } from 'lucide-react';
6
6
  import { CHAT_BOOKMARK_LIMITS } from '@adecore/agent-contracts';
7
7
  import { nameBookmark, removeBookmark, useBookmarkNaming } from '../bookmarks.js';
8
8
  import { bookmarkLabel } from '../logic/bookmarks.js';
9
+ import { useChatHost } from '../../host.js';
9
10
  import { useChatScope } from '../../scope.js';
10
11
  import { ButtonGroup, Icon, IconButton, Input } from '@adecore/ui';
11
12
  /*
@@ -15,6 +16,7 @@ import { ButtonGroup, Icon, IconButton, Input } from '@adecore/ui';
15
16
  export function BookmarkMarker({ chatId, itemId, bookmark }) {
16
17
  const { t } = useTranslation('agent-chat');
17
18
  const scope = useChatScope();
19
+ const host = useChatHost();
18
20
  const chatKey = scope.keyOf(chatId);
19
21
  const naming = useBookmarkNaming((s) => s.chatKey === chatKey && s.itemId === itemId);
20
22
  if (bookmark === null && !naming) {
@@ -23,9 +25,9 @@ export function BookmarkMarker({ chatId, itemId, bookmark }) {
23
25
  return (_jsxs("div", { className: "group/bookmark mb-2 flex h-7 min-w-0 items-center gap-2 text-xs select-none", children: [_jsx(Icon, { icon: Bookmark, size: 14, className: "shrink-0 text-accent" }), naming ? (_jsx(NameField, { initial: bookmark?.name ?? '', onDone: (name) => {
24
26
  useBookmarkNaming.getState().close();
25
27
  if (name !== null) {
26
- void nameBookmark(scope, chatId, itemId, name, bookmark?.name);
28
+ void nameBookmark(scope, host, chatId, itemId, name, bookmark?.name);
27
29
  }
28
- } })) : (bookmark !== null && (_jsx("span", { className: clsx('min-w-0 truncate', bookmark.name === undefined ? 'text-text-muted' : 'text-text'), children: bookmarkLabel(bookmark) }))), _jsx("span", { className: "min-w-6 grow border-t border-border" }), bookmark !== null && !naming && (_jsxs(ButtonGroup, { render: _jsx("span", {}), className: "-ml-2 max-w-0 shrink-0 overflow-hidden opacity-0 group-hover/bookmark:ml-0 group-hover/bookmark:max-w-none group-hover/bookmark:opacity-100 group-has-focus-visible/bookmark:ml-0 group-has-focus-visible/bookmark:max-w-none group-has-focus-visible/bookmark:opacity-100", children: [_jsx(IconButton, { icon: Pencil, size: "xs", label: t('bookmarks.rename'), onClick: () => useBookmarkNaming.getState().open(chatKey, itemId) }), _jsx(IconButton, { icon: X, size: "xs", label: t('bookmarks.remove'), onClick: () => void removeBookmark(scope, chatId, bookmark) })] }))] }));
30
+ } })) : (bookmark !== null && (_jsx("span", { className: clsx('min-w-0 truncate', bookmark.name === undefined ? 'text-text-muted' : 'text-text'), children: bookmarkLabel(bookmark) }))), _jsx("span", { className: "min-w-6 grow border-t border-border" }), bookmark !== null && !naming && (_jsxs(ButtonGroup, { render: _jsx("span", {}), className: "-ml-2 max-w-0 shrink-0 overflow-hidden opacity-0 group-hover/bookmark:ml-0 group-hover/bookmark:max-w-none group-hover/bookmark:opacity-100 group-has-focus-visible/bookmark:ml-0 group-has-focus-visible/bookmark:max-w-none group-has-focus-visible/bookmark:opacity-100", children: [_jsx(IconButton, { icon: Pencil, size: "xs", label: t('bookmarks.rename'), onClick: () => useBookmarkNaming.getState().open(chatKey, itemId) }), _jsx(IconButton, { icon: X, size: "xs", label: t('bookmarks.remove'), onClick: () => void removeBookmark(scope, host, chatId, bookmark) })] }))] }));
29
31
  }
30
32
  /* Settles with the name on Enter or when the focus leaves, and with null on Escape, which keeps the name as it was. */
31
33
  function NameField({ initial, onDone }) {
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
5
5
  import { Bookmark, Pencil, X } from 'lucide-react';
6
6
  import { goToBookmark, removeBookmark } from '../bookmarks.js';
7
7
  import { bookmarkLabel, bookmarksInThreadOrder } from '../logic/bookmarks.js';
8
- import { chatHost } from '../../host.js';
8
+ import { useChatHost } from '../../host.js';
9
9
  import { useChatScope } from '../../scope.js';
10
10
  import { useChatRow } from '../../state/chats.js';
11
11
  import { Icon, Menu } from '@adecore/ui';
@@ -15,7 +15,8 @@ export function BookmarkSubmenu({ chatId }) {
15
15
  const scope = useChatScope();
16
16
  const bookmarks = useChatRow(chatId, (row) => row?.bookmarks);
17
17
  const order = useChatRow(chatId, (row) => row?.order);
18
- const place = chatHost().useChatPlace(chatId);
18
+ const host = useChatHost();
19
+ const place = host.useChatPlace(chatId);
19
20
  const sorted = useMemo(() => bookmarksInThreadOrder(bookmarks ?? [], order ?? []), [bookmarks, order]);
20
21
  if (sorted.length === 0) {
21
22
  return null;
@@ -25,5 +26,5 @@ export function BookmarkSubmenu({ chatId }) {
25
26
  place.go();
26
27
  }
27
28
  };
28
- return (_jsxs(Menu.SubmenuRoot, { children: [_jsxs(Menu.SubmenuTrigger, { children: [_jsx(Icon, { icon: Bookmark, size: 14 }), " ", t('bookmarks.menu')] }), _jsx(Menu.Popup, { className: "max-w-80 min-w-56", children: sorted.map((bookmark) => (_jsxs(Menu.Row, { "aria-label": bookmarkLabel(bookmark), children: [_jsxs(Menu.Item, { onClick: () => go(bookmark.itemId, false), children: [_jsx(Icon, { icon: Bookmark, size: 14, className: "shrink-0 text-accent" }), _jsx("span", { className: clsx('min-w-0 grow truncate', bookmark.name === undefined && 'text-text-muted'), children: bookmarkLabel(bookmark) })] }), _jsx(Menu.RowAction, { icon: Pencil, label: t('bookmarks.rename'), onClick: () => go(bookmark.itemId, true) }), _jsx(Menu.RowAction, { icon: X, label: t('bookmarks.remove'), onClick: () => void removeBookmark(scope, chatId, bookmark) })] }, bookmark.itemId))) })] }));
29
+ return (_jsxs(Menu.SubmenuRoot, { children: [_jsxs(Menu.SubmenuTrigger, { children: [_jsx(Icon, { icon: Bookmark, size: 14 }), " ", t('bookmarks.menu')] }), _jsx(Menu.Popup, { className: "max-w-80 min-w-56", children: sorted.map((bookmark) => (_jsxs(Menu.Row, { "aria-label": bookmarkLabel(bookmark), children: [_jsxs(Menu.Item, { onClick: () => go(bookmark.itemId, false), children: [_jsx(Icon, { icon: Bookmark, size: 14, className: "shrink-0 text-accent" }), _jsx("span", { className: clsx('min-w-0 grow truncate', bookmark.name === undefined && 'text-text-muted'), children: bookmarkLabel(bookmark) })] }), _jsx(Menu.RowAction, { icon: Pencil, label: t('bookmarks.rename'), onClick: () => go(bookmark.itemId, true) }), _jsx(Menu.RowAction, { icon: X, label: t('bookmarks.remove'), onClick: () => void removeBookmark(scope, host, chatId, bookmark) })] }, bookmark.itemId))) })] }));
29
30
  }
@@ -1,6 +1,7 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo } from 'react';
3
3
  import clsx from 'clsx';
4
+ import { useShallow } from 'zustand/shallow';
4
5
  import { useTranslation } from 'react-i18next';
5
6
  import { Activity, MessageCircleQuestionMark, Square, SquareTerminal } from 'lucide-react';
6
7
  import { backgroundCounts } from '../activity.js';
@@ -11,7 +12,7 @@ import { SubagentStopButton } from './SubagentStopButton.js';
11
12
  import { formatElapsedShort } from '@adecore/ui/format';
12
13
  import { Icon, IconButton, Popover, Surface, useNow } from '@adecore/ui';
13
14
  import { useChatRow, useChats } from '../../state/chats.js';
14
- import { chatHost } from '../../host.js';
15
+ import { useChatHost } from '../../host.js';
15
16
  import { useChatScope } from '../../scope.js';
16
17
  import { useChatActions } from '../actions.js';
17
18
  import { statusLookOf } from '../../agents/status-look.js';
@@ -20,18 +21,19 @@ const NO_ITEMS = [];
20
21
  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';
21
22
  /* 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. */
22
23
  export function ChatActivity({ chatId }) {
24
+ const host = useChatHost();
23
25
  const { t } = useTranslation('agent-chat');
24
26
  const order = useChatRow(chatId, (row) => row?.order) ?? NO_ITEMS;
25
27
  const structure = useChatRow(chatId, (row) => row?.structure);
26
28
  const subagents = useMemo(() => (structure === undefined ? [] : flyoutSubagents(order, structure)), [order, structure]);
27
29
  const { id, keyOf } = useChatScope();
28
- const tasks = chatHost().tasks.useTasks(id);
29
- const statuses = useChats((s) => s.statusByKey);
30
- const progress = useMemo(() => progressOf(subagents.map((item) => {
30
+ const tasks = host.tasks.useTasks(id);
31
+ // The infos of these sub-agents' own chats, so the info of any other chat changing does not draw this again.
32
+ const children = useChats(useShallow((s) => subagents.map((item) => (item.childId === undefined ? undefined : s.statusByKey[keyOf(item.childId)]?.info))));
33
+ const progress = useMemo(() => progressOf(subagents.map((item, index) => {
31
34
  const taskId = taskIdOf(item);
32
- const child = item.childId === undefined ? undefined : statuses[keyOf(item.childId)]?.info;
33
- return taskRowStateOf(item, taskId === null ? null : (tasks?.[taskId] ?? null), child);
34
- })), [subagents, tasks, statuses, keyOf]);
35
+ return taskRowStateOf(item, taskId === null ? null : (tasks?.[taskId] ?? null), children[index]);
36
+ })), [subagents, tasks, children]);
35
37
  const words = useMemo(() => subagents.map((item) => {
36
38
  const taskId = taskIdOf(item);
37
39
  return statusWordOf(item, taskId === null ? null : (tasks?.[taskId] ?? null));
@@ -41,7 +43,7 @@ export function ChatActivity({ chatId }) {
41
43
  return null;
42
44
  }
43
45
  const { shells, monitors } = backgroundCounts(background);
44
- const Slot = chatHost().SubagentSlot;
46
+ const Slot = host.SubagentSlot;
45
47
  const counting = progress.active > 0 && progress.total > 1;
46
48
  const label = counting
47
49
  ? [t('activity.progress', { done: progress.done, total: progress.total }), progress.waiting > 0 && t('activity.needsYou', { count: progress.waiting })]
@@ -73,10 +75,11 @@ function SubagentRows({ chatId, items }) {
73
75
  return items.map((item) => _jsx(SubagentEntry, { chatId: chatId, item: item, now: now }, item.id));
74
76
  }
75
77
  function SubagentEntry({ chatId, item, now }) {
78
+ const host = useChatHost();
76
79
  const { t } = useTranslation('agent-chat');
77
80
  const { id, keyOf } = useChatScope();
78
81
  const refused = useSubagentSupport((s) => s.unsupported[id] === true);
79
- const task = chatHost().tasks.useTask(id, taskIdOf(item));
82
+ const task = host.tasks.useTask(id, taskIdOf(item));
80
83
  const { show } = useSubagentTrail(chatId);
81
84
  const child = useChats((s) => (item.childId === undefined ? undefined : s.statusByKey[keyOf(item.childId)]?.info));
82
85
  const word = statusWordOf(item, task);
@@ -85,13 +88,14 @@ function SubagentEntry({ chatId, item, now }) {
85
88
  }
86
89
  /* Only drawn while the flyout is open, so the clock ticks for nobody otherwise. */
87
90
  function BackgroundTaskRows({ chatId, tasks }) {
91
+ const host = useChatHost();
88
92
  const { t } = useTranslation('agent-chat');
89
93
  const now = useNow(1000);
90
94
  const actions = useChatActions();
91
95
  function stop(taskId) {
92
96
  void actions.stopTask(chatId, taskId).catch((e) => {
93
97
  const description = e instanceof Error ? e.message : t('subagents.noAnswer');
94
- chatHost().notify({ kind: 'error', title: t('activity.stopFailed'), description });
98
+ host.notify({ kind: 'error', title: t('activity.stopFailed'), description });
95
99
  });
96
100
  }
97
101
  return tasks.map((task) => (_jsx(ActivityRow, { icon: _jsx(Icon, { icon: task.kind === 'shell' ? SquareTerminal : Activity, size: 14, className: "shrink-0 text-text-faint" }), title: task.description || task.command || t(`activity.${task.kind}`), detail: task.description ? task.command : null, time: formatElapsedShort(now - task.startedAt), children: _jsx(IconButton, { icon: Square, size: "xs", label: t('activity.stop'), className: "mr-1.5", onClick: () => stop(task.id) }) }, task.id)));
@@ -2,15 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import clsx from 'clsx';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { MessageSquare, X } from 'lucide-react';
5
- import { chatHost } from '../../host.js';
5
+ import { useChatHost } from '../../host.js';
6
6
  import { CHIP_IN_MESSAGE, MENTION_TONE } from './chips.js';
7
7
  import { Icon, Tooltip } from '@adecore/ui';
8
8
  /* A chat attached to a message, under the title it has now rather than the one it had when it was attached. */
9
9
  export function ChatReferenceChip({ chatId, onRemove }) {
10
+ const host = useChatHost();
10
11
  const { t } = useTranslation('agent-chat');
11
- const chat = chatHost()
12
- .useReferableChats()
13
- .find((candidate) => candidate.id === chatId);
12
+ const chat = host.useReferableChats().find((candidate) => candidate.id === chatId);
14
13
  const title = chat === undefined ? t('chatReference.gone') : chat.title || t('chatReference.untitled');
15
14
  return (_jsxs("span", { className: clsx(MENTION_TONE, CHIP_IN_MESSAGE), children: [_jsx(Icon, { icon: MessageSquare, size: 12, className: "shrink-0" }), _jsx("span", { className: "truncate", children: title }), onRemove && (_jsx(Tooltip, { label: t('chatReference.remove', { title }), name: true, children: _jsx("button", { className: "-mr-0.5 shrink-0 rounded-sm opacity-70 hover:opacity-100", onClick: onRemove, children: _jsx(Icon, { icon: X, size: 12 }) }) }))] }));
16
15
  }
@@ -1,6 +1,7 @@
1
1
  import { type ReactNode } from 'react';
2
- export declare function CodeBlock({ code, lang, actions }: {
2
+ export declare function CodeBlock({ code, lang, actions, view }: {
3
3
  code: string;
4
4
  lang: string;
5
5
  actions?: ReactNode;
6
+ view?: ReactNode;
6
7
  }): import("react").JSX.Element;
@@ -7,113 +7,31 @@ import { ButtonGroup, IconButton } from '@adecore/ui';
7
7
  import { IncrementalLines } from './code-lines.js';
8
8
  import { CodeStreamingContext } from './code-streaming.js';
9
9
  import { WHOLE_FADE_CLASS } from './rehype-fade.js';
10
- import { shikiThemeOf, useCodeTheme } from './code-theme.js';
11
- // A plain language needs no grammar; shiki draws it in the theme's own foreground.
12
- const PLAIN = 'text';
10
+ import { useChatHost } from '../../host.js';
11
+ import { useCodeTheme } from './code-theme.js';
12
+ import { tokenStyle, useCodeHighlighter } from './highlighter.js';
13
13
  const COPIED_MS = 1500;
14
- let highlighter = null;
15
- let highlighterLoad = null;
16
- const loadedLanguages = new Set([PLAIN]);
17
- const languageLoads = new Map();
18
- const loadedThemes = new Set();
19
- const themeLoads = new Map();
20
- const tokenizers = new Map();
21
- // Shiki loads on the first code block, not with the app; the web bundle covers the languages a coding agent writes.
22
- function loadHighlighter() {
23
- highlighterLoad ??= import('shiki/bundle/web').then(async ({ createHighlighter }) => {
24
- highlighter = await createHighlighter({ themes: [], langs: [] });
25
- return highlighter;
26
- });
27
- return highlighterLoad;
28
- }
29
- function resolveLanguage(lang, known) {
30
- return known !== null && lang in known ? lang : PLAIN;
31
- }
32
- let bundled = null;
33
- /* Loads the highlighter and the grammar of one language, once each. */
34
- function loadLanguage(lang) {
35
- let load = languageLoads.get(lang);
36
- if (!load) {
37
- load = Promise.all([loadHighlighter(), import('shiki/bundle/web')]).then(async ([loaded, { bundledLanguages }]) => {
38
- bundled = bundledLanguages;
39
- const language = resolveLanguage(lang, bundledLanguages);
40
- if (!loadedLanguages.has(language)) {
41
- await loaded.loadLanguage(language);
42
- loadedLanguages.add(language);
43
- }
44
- });
45
- languageLoads.set(lang, load);
46
- }
47
- return load;
48
- }
49
- /* Loads a theme into the highlighter, once. */
50
- function loadTheme(theme) {
51
- let load = themeLoads.get(theme);
52
- if (!load) {
53
- load = loadHighlighter().then(async (loaded) => {
54
- await loaded.loadTheme(shikiThemeOf(theme));
55
- loadedThemes.add(theme);
56
- });
57
- themeLoads.set(theme, load);
58
- }
59
- return load;
60
- }
61
- /* The tokenizer for a language in a theme, or null while shiki, the grammar or the theme is still on its way. */
62
- function tokenizerFor(lang, theme) {
63
- if (highlighter === null || bundled === null || !languageLoads.has(lang) || !loadedThemes.has(theme)) {
64
- return null;
65
- }
66
- const language = resolveLanguage(lang, bundled);
67
- if (!loadedLanguages.has(language)) {
68
- return null;
69
- }
70
- const key = `${language}:${theme}`;
71
- let entry = tokenizers.get(key);
72
- if (!entry) {
73
- const loaded = highlighter;
74
- const options = { lang: language, theme };
75
- entry = {
76
- tokenize: (code, state) => {
77
- const result = loaded.codeToTokens(code, state ? { ...options, grammarState: state } : options);
78
- return { lines: result.tokens, state: result.grammarState };
79
- },
80
- fg: loaded.getTheme(theme).fg
81
- };
82
- tokenizers.set(key, entry);
83
- }
84
- return entry;
85
- }
86
- function styleOf(token) {
87
- const style = token.fontStyle ?? 0;
88
- if (token.color === undefined && style <= 0) {
89
- return undefined;
90
- }
91
- const decorations = [style & 4 ? 'underline' : '', style & 8 ? 'line-through' : ''].filter(Boolean).join(' ');
92
- return {
93
- color: token.color,
94
- ...(style & 1 ? { fontStyle: 'italic' } : {}),
95
- ...(style & 2 ? { fontWeight: 'bold' } : {}),
96
- ...(decorations ? { textDecoration: decorations } : {})
97
- };
98
- }
99
14
  /* A line whose tokens did not change keeps its elements, so a selection in it survives the next delta. */
100
15
  const CodeLine = memo(function CodeLine({ tokens }) {
101
- return (_jsx("span", { className: "line", children: tokens.map((token, index) => (_jsx("span", { style: styleOf(token), children: token.content }, index))) }));
16
+ return (_jsx("span", { className: "line", children: tokens.map((token, index) => (_jsx("span", { style: tokenStyle(token), children: token.content }, index))) }));
102
17
  });
103
18
  /*
104
19
  * A fenced block, highlighted a line at a time. Until shiki and the grammar are there it holds its
105
20
  * place invisibly, so bare code never turns colored. An open fence is tokenized as it grows, and
106
- * closing it does not draw it again.
21
+ * closing it does not draw it again. `view` takes the place of the code, such as what the code draws,
22
+ * and Copy still copies the code.
107
23
  */
108
- export function CodeBlock({ code, lang, actions }) {
24
+ export function CodeBlock({ code, lang, actions, view }) {
109
25
  const { t } = useTranslation('agent-chat');
110
26
  const theme = useCodeTheme();
27
+ const { custom } = useChatHost().code;
28
+ const highlighter = useCodeHighlighter();
111
29
  const streaming = useContext(CodeStreamingContext);
112
30
  const [copyState, setCopyState] = useState('idle');
113
31
  const copying = useRef(false);
114
32
  // What loaded last, so a block whose theme changes draws again once the new one is in.
115
33
  const [loaded, setLoaded] = useState(null);
116
- const tokenizer = tokenizerFor(lang, theme);
34
+ const tokenizer = highlighter.tokenizer(lang, theme);
117
35
  // Only a block that had to wait fades in; one drawn highlighted from its first frame just stands there.
118
36
  const [waited] = useState(tokenizer === null);
119
37
  const lines = useMemo(() => (tokenizer ? new IncrementalLines(tokenizer.tokenize) : null), [tokenizer]);
@@ -148,15 +66,15 @@ export function CodeBlock({ code, lang, actions }) {
148
66
  return;
149
67
  }
150
68
  let cancelled = false;
151
- Promise.all([loadLanguage(lang), loadTheme(theme)])
69
+ Promise.all([highlighter.loadLanguage(lang), highlighter.loadTheme(theme, custom)])
152
70
  .then(() => !cancelled && setLoaded(`${lang}:${theme}`))
153
71
  .catch(() => !cancelled && setLoaded('failed'));
154
72
  return () => {
155
73
  cancelled = true;
156
74
  };
157
- }, [lang, theme, tokenizer]);
75
+ }, [highlighter, lang, theme, custom, tokenizer]);
158
76
  const pending = (tokenizer === null || tokens === null) && loaded !== 'failed';
159
- return (_jsxs("div", { className: "chat-code", "data-copy-state": copyState, children: [_jsx("pre", { className: clsx(pending && 'invisible', !pending && tokens !== null && waited && WHOLE_FADE_CLASS), "aria-hidden": pending || undefined, style: { color: tokenizer?.fg }, children: _jsx("code", { children: tokens === null
77
+ return (_jsxs("div", { className: "chat-code", "data-copy-state": copyState, children: [view ?? (_jsx("pre", { className: clsx(pending && 'invisible', !pending && tokens !== null && waited && WHOLE_FADE_CLASS), "aria-hidden": pending || undefined, style: { color: tokenizer?.fg }, children: _jsx("code", { children: tokens === null
160
78
  ? code
161
- : tokens.map((line, index) => (_jsxs(Fragment, { children: [_jsx(CodeLine, { tokens: line }), index < tokens.length - 1 && '\n'] }, index))) }) }), _jsxs(ButtonGroup, { className: "chat-code-actions", role: "group", "aria-label": t('code.actions'), children: [_jsx(IconButton, { icon: copyState === 'copied' ? Check : Copy, size: "sm", label: copyState === 'copied' ? t('timeline.actions.copied') : t('timeline.menu.copyCode'), busy: copyState === 'copying', "aria-busy": copyState === 'copying', "aria-disabled": copyState === 'copying', onClick: () => void copy() }), actions] }), _jsx("span", { className: "sr-only", role: "status", children: copyState === 'copied' ? t('timeline.actions.copied') : '' }), copyState === 'failed' && (_jsx("p", { className: "chat-code-error", role: "alert", children: t('code.copyFailed') }))] }));
79
+ : tokens.map((line, index) => (_jsxs(Fragment, { children: [_jsx(CodeLine, { tokens: line }), index < tokens.length - 1 && '\n'] }, index))) }) })), _jsxs(ButtonGroup, { className: "chat-code-actions", role: "group", "aria-label": t('code.actions'), children: [_jsx(IconButton, { icon: copyState === 'copied' ? Check : Copy, size: "sm", label: copyState === 'copied' ? t('timeline.actions.copied') : t('timeline.menu.copyCode'), busy: copyState === 'copying', "aria-busy": copyState === 'copying', "aria-disabled": copyState === 'copying', onClick: () => void copy() }), actions] }), _jsx("span", { className: "sr-only", role: "status", children: copyState === 'copied' ? t('timeline.actions.copied') : '' }), copyState === 'failed' && (_jsx("p", { className: "chat-code-error", role: "alert", children: t('code.copyFailed') }))] }));
162
80
  }
@@ -8,7 +8,7 @@ interface ComposerProps {
8
8
  disabled: boolean;
9
9
  readOnly?: boolean;
10
10
  providerFixed: boolean;
11
- onSend(text: string, extras: ChatSendExtras): void;
11
+ onSend(text: string, extras: ChatSendExtras): void | Promise<boolean>;
12
12
  onRetarget(provider: AgentKind, selection: ModelSelection): Promise<void>;
13
13
  }
14
14
  export declare function Composer({ chatId, info, focused, answerPromptsElsewhere, disabled, readOnly, providerFixed, onSend, onRetarget }: ComposerProps): import("react").JSX.Element;