@adecore/agents-react 0.38.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. package/dist/agents/AgentModelLabel.d.ts +10 -0
  2. package/dist/agents/AgentModelLabel.js +98 -0
  3. package/dist/agents/model-name.js +2 -1
  4. package/dist/agents/provider-paths.d.ts +2 -1
  5. package/dist/agents/provider-paths.js +7 -3
  6. package/dist/chat/account-choice.js +57 -28
  7. package/dist/chat/bookmarks.js +2 -1
  8. package/dist/chat/drafts.d.ts +0 -1
  9. package/dist/chat/drafts.js +9 -28
  10. package/dist/chat/logic/picker-entries.js +2 -8
  11. package/dist/chat/logic/resume-compaction.js +2 -5
  12. package/dist/chat/logic/timeline-copy.js +2 -8
  13. package/dist/chat/logic/timeline.js +9 -10
  14. package/dist/chat/logic/workflow.js +2 -4
  15. package/dist/chat/subagent-conversation.js +2 -1
  16. package/dist/chat/ui/Composer.js +385 -360
  17. package/dist/chat/ui/GoalDock.js +2 -1
  18. package/dist/chat/ui/LimitState.js +4 -6
  19. package/dist/chat/ui/Pickers.d.ts +2 -1
  20. package/dist/chat/ui/Pickers.js +126 -123
  21. package/dist/chat/ui/PromptEditor.d.ts +19 -0
  22. package/dist/chat/ui/PromptEditor.js +115 -0
  23. package/dist/chat/ui/Timeline.d.ts +2 -1
  24. package/dist/chat/ui/Timeline.js +13 -1
  25. package/dist/chat/ui/composer/ComposerControls.d.ts +3 -0
  26. package/dist/chat/ui/composer/ComposerControls.js +235 -38
  27. package/dist/chat/ui/composer/key-handler.d.ts +1 -1
  28. package/dist/chat/ui/composer/key-handler.js +3 -11
  29. package/dist/chat/ui/composer/prompt-picks.d.ts +4 -0
  30. package/dist/chat/ui/composer/prompt-picks.js +14 -0
  31. package/dist/chat/ui/composer/use-composer-menus.d.ts +4 -49
  32. package/dist/chat/ui/composer/use-composer-menus.js +65 -519
  33. package/dist/chat/ui/composer/use-continuable-turn.js +0 -1
  34. package/dist/chat/ui/composer/use-notice.js +2 -1
  35. package/dist/chat/ui/composer/use-prompt-history.js +1 -1
  36. package/dist/chat/ui/composer/use-prompt-menus.d.ts +73 -0
  37. package/dist/chat/ui/composer/use-prompt-menus.js +499 -0
  38. package/dist/chat/ui/composer/use-take-back.js +2 -2
  39. package/dist/chat/ui/diff/ApprovalReview.js +1 -1
  40. package/dist/chat/ui/highlighter.d.ts +0 -6
  41. package/dist/chat/ui/highlighter.js +0 -12
  42. package/dist/chat/ui/icons.d.ts +0 -1
  43. package/dist/chat/ui/icons.js +0 -3
  44. package/dist/chat/ui/index.d.ts +3 -0
  45. package/dist/chat/ui/index.js +2 -0
  46. package/dist/chat/ui/rows/MessageRows.js +2 -5
  47. package/dist/chat/ui/rows/row-rhythm.js +2 -5
  48. package/dist/chat/ui/theme-watch.js +3 -7
  49. package/dist/host-hooks.d.ts +2 -1
  50. package/dist/host-hooks.js +6 -0
  51. package/dist/host.d.ts +17 -1
  52. package/dist/index.d.ts +1 -1
  53. package/dist/locales/en/agent-chat.d.ts +1 -0
  54. package/dist/locales/en/agent-chat.js +1 -0
  55. package/dist/locales/nl/agent-chat.d.ts +1 -0
  56. package/dist/locales/nl/agent-chat.js +1 -0
  57. package/dist/prompts/logic/usePromptSession.js +2 -1
  58. package/dist/providers/AddAccountForm.js +2 -1
  59. package/dist/providers/CliDetail.js +104 -130
  60. package/dist/providers/account-actions.js +6 -9
  61. package/dist/providers/index.d.ts +1 -0
  62. package/dist/providers/index.js +1 -0
  63. package/dist/state/chats.d.ts +1 -0
  64. package/dist/state/chats.js +6 -11
  65. package/dist/state/provider-accounts.d.ts +1 -1
  66. package/dist/state/provider-accounts.js +2 -8
  67. package/dist/theme.css +3 -0
  68. package/dist/transport.d.ts +2 -0
  69. package/dist/transport.js +14 -0
  70. package/dist/usage/ChartLogo.d.ts +5 -0
  71. package/dist/usage/ChartLogo.js +48 -0
  72. package/dist/usage/LimitsList.js +6 -16
  73. package/dist/usage/UsageBreakdown.js +17 -40
  74. package/dist/usage/UsageLimits.js +183 -211
  75. package/dist/usage/UsagePage.js +3 -17
  76. package/dist/usage/UsageSummary.js +4 -9
  77. package/dist/usage/limits.js +16 -31
  78. package/dist/usage/summary.d.ts +1 -1
  79. package/dist/usage/summary.js +3 -5
  80. package/package.json +26 -26
  81. package/src/agents/AccountDot.tsx +1 -0
  82. package/src/agents/AgentIcon.tsx +2 -0
  83. package/src/agents/AgentModelLabel.tsx +38 -0
  84. package/src/agents/ModelOptionControl.tsx +8 -0
  85. package/src/agents/account-limits.ts +3 -0
  86. package/src/agents/accounts.ts +12 -0
  87. package/src/agents/model-name.ts +13 -1
  88. package/src/agents/model-options.ts +2 -0
  89. package/src/agents/model-requirement.ts +4 -0
  90. package/src/agents/option-words.ts +2 -0
  91. package/src/agents/provider-paths.ts +8 -3
  92. package/src/agents/status-look.ts +1 -0
  93. package/src/chat/account-choice.ts +25 -19
  94. package/src/chat/actions.ts +5 -0
  95. package/src/chat/attachments.ts +8 -0
  96. package/src/chat/bookmarks.ts +13 -1
  97. package/src/chat/chat-client.ts +63 -0
  98. package/src/chat/chat-references.ts +3 -0
  99. package/src/chat/composer-effort.ts +5 -0
  100. package/src/chat/composer-send.ts +4 -0
  101. package/src/chat/drafts.ts +21 -17
  102. package/src/chat/forks.ts +4 -0
  103. package/src/chat/guards.ts +2 -0
  104. package/src/chat/index.ts +7 -0
  105. package/src/chat/logic/ansi.ts +47 -0
  106. package/src/chat/logic/bookmarks.ts +7 -0
  107. package/src/chat/logic/command-looks.ts +29 -0
  108. package/src/chat/logic/context-usage.ts +3 -0
  109. package/src/chat/logic/continue-after-stop.ts +5 -0
  110. package/src/chat/logic/effort.ts +4 -0
  111. package/src/chat/logic/embedded-code.ts +63 -0
  112. package/src/chat/logic/fork.ts +22 -0
  113. package/src/chat/logic/generated-image.ts +7 -0
  114. package/src/chat/logic/goal.ts +7 -0
  115. package/src/chat/logic/handback.ts +2 -0
  116. package/src/chat/logic/limit.ts +8 -0
  117. package/src/chat/logic/picker-entries.ts +16 -4
  118. package/src/chat/logic/resume-compaction.ts +22 -5
  119. package/src/chat/logic/scrubber.ts +38 -5
  120. package/src/chat/logic/thought.ts +5 -0
  121. package/src/chat/logic/thread-cards.ts +6 -0
  122. package/src/chat/logic/timeline-copy.ts +4 -9
  123. package/src/chat/logic/timeline-target.ts +15 -0
  124. package/src/chat/logic/timeline.ts +113 -10
  125. package/src/chat/logic/tools.ts +30 -0
  126. package/src/chat/logic/visual-height.ts +5 -0
  127. package/src/chat/logic/visual-viewport.ts +1 -0
  128. package/src/chat/logic/welcome.ts +3 -0
  129. package/src/chat/logic/workflow.ts +26 -7
  130. package/src/chat/mentions.ts +56 -0
  131. package/src/chat/pending-prompts.ts +16 -0
  132. package/src/chat/persisted-json.ts +15 -0
  133. package/src/chat/preferences.ts +16 -0
  134. package/src/chat/prompt-hold.ts +2 -0
  135. package/src/chat/quote.ts +7 -0
  136. package/src/chat/reading-width.ts +1 -0
  137. package/src/chat/recent-messages.ts +5 -0
  138. package/src/chat/resume-compaction-dismissals.ts +1 -0
  139. package/src/chat/stash.ts +12 -0
  140. package/src/chat/subagent-conversation.ts +24 -4
  141. package/src/chat/subagent-list.ts +14 -0
  142. package/src/chat/subagent-support.ts +1 -0
  143. package/src/chat/subagent-view.ts +9 -0
  144. package/src/chat/thumbnails.ts +14 -0
  145. package/src/chat/timeline-flash.ts +2 -0
  146. package/src/chat/timeline-scroll.ts +23 -0
  147. package/src/chat/ui/AccountPill.tsx +4 -0
  148. package/src/chat/ui/AnsiOutput.tsx +1 -0
  149. package/src/chat/ui/BookmarkMarker.tsx +5 -0
  150. package/src/chat/ui/BookmarkSubmenu.tsx +1 -0
  151. package/src/chat/ui/ChatActivity.tsx +13 -0
  152. package/src/chat/ui/CodeBlock.tsx +8 -0
  153. package/src/chat/ui/Composer.tsx +27 -3
  154. package/src/chat/ui/ComposerInput.tsx +22 -0
  155. package/src/chat/ui/DiffPool.tsx +3 -0
  156. package/src/chat/ui/EditDiff.tsx +5 -0
  157. package/src/chat/ui/GoalDock.tsx +13 -1
  158. package/src/chat/ui/ImageView.tsx +11 -0
  159. package/src/chat/ui/LimitState.tsx +23 -6
  160. package/src/chat/ui/Markdown.tsx +41 -0
  161. package/src/chat/ui/MermaidBlock.tsx +16 -0
  162. package/src/chat/ui/MessageActions.tsx +8 -0
  163. package/src/chat/ui/ModelMarks.tsx +1 -0
  164. package/src/chat/ui/Pickers.tsx +30 -1
  165. package/src/chat/ui/PromptComposer.tsx +2 -0
  166. package/src/chat/ui/PromptEditor.tsx +136 -0
  167. package/src/chat/ui/QuoteButton.tsx +12 -0
  168. package/src/chat/ui/ReferenceChip.tsx +3 -0
  169. package/src/chat/ui/ResumeCompactionDock.tsx +1 -0
  170. package/src/chat/ui/RowTime.tsx +1 -0
  171. package/src/chat/ui/RunSettings.tsx +38 -0
  172. package/src/chat/ui/Scrubber.tsx +14 -0
  173. package/src/chat/ui/SessionLine.tsx +1 -0
  174. package/src/chat/ui/ShellCommand.tsx +32 -0
  175. package/src/chat/ui/SubagentControls.tsx +7 -0
  176. package/src/chat/ui/SubagentInfo.tsx +1 -0
  177. package/src/chat/ui/SubagentStopButton.tsx +6 -0
  178. package/src/chat/ui/SubagentTimeline.tsx +10 -0
  179. package/src/chat/ui/Timeline.tsx +141 -1
  180. package/src/chat/ui/TimelineMenu.tsx +5 -0
  181. package/src/chat/ui/UnifiedDiff.tsx +6 -0
  182. package/src/chat/ui/UploadThumb.tsx +2 -0
  183. package/src/chat/ui/VisualDialogs.tsx +3 -0
  184. package/src/chat/ui/VisualFrame.tsx +15 -0
  185. package/src/chat/ui/Welcome.tsx +1 -0
  186. package/src/chat/ui/chip-clipboard.ts +20 -0
  187. package/src/chat/ui/code-lines.ts +14 -0
  188. package/src/chat/ui/code-theme.ts +1 -0
  189. package/src/chat/ui/composer/ComposerControls.tsx +70 -15
  190. package/src/chat/ui/composer/ComposerMenu.tsx +12 -0
  191. package/src/chat/ui/composer/DraftParts.tsx +2 -0
  192. package/src/chat/ui/composer/QueuedMessages.tsx +4 -0
  193. package/src/chat/ui/composer/chip-paste.ts +16 -0
  194. package/src/chat/ui/composer/chips.ts +6 -0
  195. package/src/chat/ui/composer/editor.ts +9 -0
  196. package/src/chat/ui/composer/insert.ts +2 -0
  197. package/src/chat/ui/composer/key-handler.ts +42 -9
  198. package/src/chat/ui/composer/keys.ts +23 -0
  199. package/src/chat/ui/composer/placeholder.ts +6 -0
  200. package/src/chat/ui/composer/prompt-picks.ts +18 -0
  201. package/src/chat/ui/composer/surface.ts +5 -0
  202. package/src/chat/ui/composer/use-chat-controls.ts +13 -0
  203. package/src/chat/ui/composer/use-compaction-offer.ts +3 -0
  204. package/src/chat/ui/composer/use-composer-menus.ts +20 -275
  205. package/src/chat/ui/composer/use-composer-submit.ts +27 -0
  206. package/src/chat/ui/composer/use-continuable-turn.ts +3 -5
  207. package/src/chat/ui/composer/use-draft-files.ts +24 -0
  208. package/src/chat/ui/composer/use-effort-stepper.ts +8 -0
  209. package/src/chat/ui/composer/use-notice.ts +4 -1
  210. package/src/chat/ui/composer/use-pending-requests.ts +3 -0
  211. package/src/chat/ui/composer/use-prompt-history.ts +20 -11
  212. package/src/chat/ui/composer/use-prompt-hold.ts +3 -0
  213. package/src/chat/ui/composer/use-prompt-menus.ts +361 -0
  214. package/src/chat/ui/composer/use-take-back.ts +20 -2
  215. package/src/chat/ui/diff/ApprovalReview.tsx +18 -15
  216. package/src/chat/ui/diff/DiffFallbackBoundary.tsx +1 -0
  217. package/src/chat/ui/diff/DiffOmitted.tsx +1 -0
  218. package/src/chat/ui/diff/DiffStack.tsx +2 -0
  219. package/src/chat/ui/diff/DiffUnavailable.tsx +1 -0
  220. package/src/chat/ui/diff/EditorEditDiff.tsx +13 -0
  221. package/src/chat/ui/diff/EditorPatchDiff.tsx +4 -0
  222. package/src/chat/ui/diff/LegacyDiffPool.tsx +2 -0
  223. package/src/chat/ui/diff/LegacyEditDiff.tsx +2 -0
  224. package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +8 -0
  225. package/src/chat/ui/diff/RawDiff.tsx +1 -0
  226. package/src/chat/ui/diff/ReviewDiff.tsx +1 -0
  227. package/src/chat/ui/diff/diff-source-budget.ts +8 -0
  228. package/src/chat/ui/diff/editor-edit-model.ts +4 -0
  229. package/src/chat/ui/diff/review-plan.ts +9 -0
  230. package/src/chat/ui/diff/review-settle.ts +1 -0
  231. package/src/chat/ui/diff/syntax-runtime.ts +52 -0
  232. package/src/chat/ui/diff/use-review-stack.ts +9 -0
  233. package/src/chat/ui/diff-syntax.ts +12 -0
  234. package/src/chat/ui/diff-theme.ts +2 -0
  235. package/src/chat/ui/file-links.ts +2 -0
  236. package/src/chat/ui/find-reveal.ts +1 -0
  237. package/src/chat/ui/full-diff.ts +1 -0
  238. package/src/chat/ui/highlighter.ts +29 -15
  239. package/src/chat/ui/icons.tsx +2 -4
  240. package/src/chat/ui/index.ts +7 -0
  241. package/src/chat/ui/intelligent-ui/UiBlockFrame.tsx +25 -0
  242. package/src/chat/ui/intelligent-ui/UiFallbackPart.tsx +1 -0
  243. package/src/chat/ui/intelligent-ui/UiReply.tsx +63 -0
  244. package/src/chat/ui/intelligent-ui/block-local.ts +5 -0
  245. package/src/chat/ui/intelligent-ui/chart-data.ts +15 -0
  246. package/src/chat/ui/intelligent-ui/node-text.ts +17 -0
  247. package/src/chat/ui/intelligent-ui/renderers/binding.ts +1 -0
  248. package/src/chat/ui/intelligent-ui/renderers/chart.tsx +31 -0
  249. package/src/chat/ui/intelligent-ui/renderers/choices.tsx +4 -0
  250. package/src/chat/ui/intelligent-ui/renderers/content.tsx +18 -0
  251. package/src/chat/ui/intelligent-ui/renderers/data.tsx +11 -0
  252. package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +13 -0
  253. package/src/chat/ui/intelligent-ui/renderers/links.tsx +9 -0
  254. package/src/chat/ui/intelligent-ui/renderers/map-slots.ts +15 -0
  255. package/src/chat/ui/intelligent-ui/renderers/map-style.ts +15 -0
  256. package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +48 -0
  257. package/src/chat/ui/intelligent-ui/renderers/map.tsx +20 -0
  258. package/src/chat/ui/intelligent-ui/renderers/place-point.ts +3 -0
  259. package/src/chat/ui/intelligent-ui/renderers/structure.tsx +3 -0
  260. package/src/chat/ui/intelligent-ui/renderers/text.tsx +8 -0
  261. package/src/chat/ui/intelligent-ui/reply-parts.ts +5 -0
  262. package/src/chat/ui/intelligent-ui/table-data.ts +17 -0
  263. package/src/chat/ui/intelligent-ui/use-in-view.ts +4 -0
  264. package/src/chat/ui/intelligent-ui/use-later.ts +4 -0
  265. package/src/chat/ui/intelligent-ui/use-truncated.ts +5 -0
  266. package/src/chat/ui/intelligent-ui/use-ui-links.ts +29 -0
  267. package/src/chat/ui/intelligent-ui/use-ui-queries.ts +60 -0
  268. package/src/chat/ui/markdown-blocks.ts +64 -0
  269. package/src/chat/ui/mermaid.ts +4 -0
  270. package/src/chat/ui/quote-selection.ts +10 -0
  271. package/src/chat/ui/rehype-chips.ts +5 -0
  272. package/src/chat/ui/rehype-fade.ts +2 -0
  273. package/src/chat/ui/remark-html-as-text.ts +5 -0
  274. package/src/chat/ui/reveal.ts +18 -0
  275. package/src/chat/ui/rows/ForksRow.tsx +5 -0
  276. package/src/chat/ui/rows/GeneratedImageRow.tsx +10 -0
  277. package/src/chat/ui/rows/MessageRows.tsx +29 -5
  278. package/src/chat/ui/rows/RowContainer.tsx +1 -0
  279. package/src/chat/ui/rows/Rows.tsx +2 -0
  280. package/src/chat/ui/rows/SubagentRow.tsx +13 -0
  281. package/src/chat/ui/rows/TaskRow.tsx +7 -0
  282. package/src/chat/ui/rows/VisualRow.tsx +4 -0
  283. package/src/chat/ui/rows/WorkRows.tsx +46 -0
  284. package/src/chat/ui/rows/WorkflowRow.tsx +10 -0
  285. package/src/chat/ui/rows/row-rhythm.ts +7 -5
  286. package/src/chat/ui/theme-watch.ts +8 -7
  287. package/src/chat/ui/thread-references.ts +7 -0
  288. package/src/chat/ui/use-diff-source-tokens.ts +8 -0
  289. package/src/chat/ui/useToggleSet.ts +3 -0
  290. package/src/chat/ui/visual-theme.ts +30 -0
  291. package/src/chat/visual-bridge.ts +23 -0
  292. package/src/chat/visual-viewport.ts +12 -0
  293. package/src/host-hooks.ts +15 -5
  294. package/src/host.ts +29 -1
  295. package/src/index.ts +5 -0
  296. package/src/lazy.tsx +9 -0
  297. package/src/locales/en/agent-chat.ts +1 -0
  298. package/src/locales/nl/agent-chat.ts +1 -0
  299. package/src/mounted-registry.ts +4 -0
  300. package/src/port-transport.ts +32 -0
  301. package/src/prompts/index.ts +2 -0
  302. package/src/prompts/logic/focus.ts +5 -0
  303. package/src/prompts/logic/keys.ts +12 -0
  304. package/src/prompts/logic/prompts.ts +8 -0
  305. package/src/prompts/logic/subjects.ts +8 -0
  306. package/src/prompts/logic/useFocusAfterAnswer.ts +2 -0
  307. package/src/prompts/logic/usePromptSession.ts +13 -1
  308. package/src/prompts/ui/ApprovalBody.tsx +2 -0
  309. package/src/prompts/ui/PromptActions.tsx +2 -0
  310. package/src/prompts/ui/PromptCard.tsx +6 -0
  311. package/src/prompts/ui/PromptView.tsx +6 -0
  312. package/src/prompts/ui/QuestionBody.tsx +11 -0
  313. package/src/providers/AccountColors.tsx +1 -0
  314. package/src/providers/AccountDetail.tsx +7 -0
  315. package/src/providers/AccountVariables.tsx +2 -0
  316. package/src/providers/AddAccountForm.tsx +5 -1
  317. package/src/providers/CliDetail.tsx +6 -5
  318. package/src/providers/InstallPage.tsx +11 -0
  319. package/src/providers/ProviderList.tsx +3 -0
  320. package/src/providers/ProvidersPane.tsx +18 -0
  321. package/src/providers/account-actions.ts +10 -8
  322. package/src/providers/index.ts +1 -0
  323. package/src/providers/install-steps.ts +3 -0
  324. package/src/providers/pane-target.ts +5 -0
  325. package/src/providers/provider-abilities.ts +1 -0
  326. package/src/providers/provider-groups.ts +6 -0
  327. package/src/providers/variables.ts +12 -0
  328. package/src/scope.ts +2 -0
  329. package/src/state/chats.ts +61 -14
  330. package/src/state/index.ts +2 -0
  331. package/src/state/provider-accounts.ts +7 -9
  332. package/src/state/providers.ts +1 -0
  333. package/src/state/usage.ts +6 -0
  334. package/src/storage.ts +18 -0
  335. package/src/testing/prompts.ts +5 -0
  336. package/src/theme.css +3 -0
  337. package/src/transport.ts +18 -0
  338. package/src/usage/ChartLogo.tsx +12 -0
  339. package/src/usage/LimitsList.tsx +7 -8
  340. package/src/usage/UsageBreakdown.tsx +11 -4
  341. package/src/usage/UsageChart.tsx +5 -0
  342. package/src/usage/UsageLimits.tsx +10 -7
  343. package/src/usage/UsageLimitsCard.tsx +2 -0
  344. package/src/usage/UsagePage.tsx +16 -19
  345. package/src/usage/UsageSummary.tsx +4 -5
  346. package/src/usage/UsageTiles.tsx +2 -0
  347. package/src/usage/format.ts +7 -0
  348. package/src/usage/limit-groups.ts +14 -0
  349. package/src/usage/limits.ts +11 -13
  350. package/src/usage/money.ts +1 -0
  351. package/src/usage/summary.ts +15 -7
package/src/host.ts CHANGED
@@ -18,7 +18,9 @@ import type {
18
18
  ChatUiLinkPayload,
19
19
  ChatUiLinkReading,
20
20
  ChatVisual,
21
- ProviderPlatform
21
+ ModelSelection,
22
+ ProviderPlatform,
23
+ RuntimeMode
22
24
  } from '@adecore/agent-contracts';
23
25
  import { isApplePlatform } from '@adecore/ui';
24
26
  import type { Referables } from './chat/chat-references';
@@ -168,6 +170,28 @@ export interface ComposerSlotProps {
168
170
  insert(text: string): void;
169
171
  }
170
172
 
173
+ /* What a send action of the app's gets: the message as it stands when a person picks the action. */
174
+ export interface ComposerSendDraft {
175
+ scopeId: string;
176
+ chatId: string;
177
+ /* The draft as Send would send it, trimmed and with its quote; empty when nothing is written. */
178
+ text: string;
179
+ provider: AgentKind;
180
+ selection: ModelSelection;
181
+ runtimeMode: RuntimeMode;
182
+ }
183
+
184
+ /* An action of the app's own in the menu beside the composer's send button, such as sending the draft later. */
185
+ export interface ComposerSendAction {
186
+ /* Unique among the actions of one composer. */
187
+ id: string;
188
+ label: string;
189
+ icon?: LucideIcon;
190
+ disabled?: boolean;
191
+ /* Runs once a person picks it; the draft stays in the box, and the host shows a failure itself. */
192
+ run(draft: ComposerSendDraft): void;
193
+ }
194
+
171
195
  /* What an app's own chips for a chat's sub-agents get, in place of the one flyout over the composer. */
172
196
  export interface SubagentSlotProps {
173
197
  chatId: string;
@@ -453,6 +477,8 @@ export interface ChatHost {
453
477
  useComposerPlaceholder(scopeId: string, chatId: string): string | null;
454
478
  /* The app's own control in the composer's row, after the pickers; null draws none. */
455
479
  ComposerSlot: ComponentType<ComposerSlotProps> | null;
480
+ /* Actions of the app's own behind a chevron on the send button, in the order given; left out, or none, Send stands alone. */
481
+ useComposerSendActions?(scopeId: string, chatId: string): readonly ComposerSendAction[];
456
482
  /* The app's own chips for a chat's sub-agents over the composer; null draws the one flyout. */
457
483
  SubagentSlot: ComponentType<SubagentSlotProps> | null;
458
484
  /* Cards of the app's own in a thread, in any order. Hand back the same array while none changed, or the thread lays out its rows again on every render. */
@@ -525,9 +551,11 @@ const ChatHostContext = createContext<ChatHost>(DEFAULT_HOST);
525
551
  export function ChatHostProvider({ host, children }: { host: Partial<ChatHost>; children?: ReactNode }) {
526
552
  const parent = useContext(ChatHostContext);
527
553
  const [pinned] = useState(() => ({ host, value: { ...parent, ...host } }));
554
+
528
555
  if (pinned.host !== host) {
529
556
  throw new Error('A ChatHostProvider reads its host once: hand it the same object on every render, or a new key to mount it with another');
530
557
  }
558
+
531
559
  return createElement(ChatHostContext.Provider, { value: pinned.value }, children);
532
560
  }
533
561
 
package/src/index.ts CHANGED
@@ -9,6 +9,8 @@ export {
9
9
  type TimelineFind,
10
10
  type ComposerDictationProps,
11
11
  type ComposerSlotProps,
12
+ type ComposerSendAction,
13
+ type ComposerSendDraft,
12
14
  type SubagentSlotProps,
13
15
  type ThreadCard,
14
16
  type VisualHost,
@@ -27,7 +29,9 @@ export {
27
29
  type TextareaProps,
28
30
  type ResourceUrl
29
31
  } from './host';
32
+
30
33
  export { type ChatScope, ChatScopeContext, useChatScope } from './scope';
34
+
31
35
  export {
32
36
  type ChatRequestMap,
33
37
  type ChatEventMap,
@@ -37,6 +41,7 @@ export {
37
41
  errorCode,
38
42
  isConnectionError
39
43
  } from './transport';
44
+
40
45
  export { portTransport, type PortTransport } from './port-transport';
41
46
  export { configureAgentMarks, type AgentMark } from './agents/agent-marks';
42
47
  export { configureChatStorage, type ChatStorageRecord, type ChatStorageAdapter, type ChatStorageOptions } from './storage';
package/src/lazy.tsx CHANGED
@@ -13,6 +13,7 @@ const openErrorListeners = new Set<(error: unknown) => void>();
13
13
  */
14
14
  export function setLazyPrefetch(next: (load: Loader) => void): void {
15
15
  register = next;
16
+
16
17
  for (const load of loaders) {
17
18
  next(load);
18
19
  }
@@ -24,6 +25,7 @@ export function setLazyPrefetch(next: (load: Loader) => void): void {
24
25
  */
25
26
  export function onLazyOpenError(listener: (error: unknown) => void): () => void {
26
27
  openErrorListeners.add(listener);
28
+
27
29
  return () => {
28
30
  openErrorListeners.delete(listener);
29
31
  };
@@ -41,11 +43,14 @@ export function lazyNamed<Module extends Record<Name, ComponentType<any>>, Name
41
43
  ): ComponentType<ComponentProps<Module[Name]>> {
42
44
  // Through `LoadedComponent`, so a `LazyBoundary` around it draws the moment the module lands.
43
45
  const loaded = new LoadedComponent<ComponentType<any>>(async () => (await load())[name]);
46
+
44
47
  const open = (): Promise<ComponentType<any>> => loaded.load();
48
+
45
49
  if (options?.prefetch !== false) {
46
50
  loaders.push(open);
47
51
  register?.(open);
48
52
  }
53
+
49
54
  // Only a render loads through here; a prefetch calls `open` itself, so its failures report nothing.
50
55
  const Lazy = lazy(async () => {
51
56
  try {
@@ -54,14 +59,18 @@ export function lazyNamed<Module extends Record<Name, ComponentType<any>>, Name
54
59
  for (const listener of openErrorListeners) {
55
60
  listener(error);
56
61
  }
62
+
57
63
  throw error;
58
64
  }
59
65
  });
66
+
60
67
  const LazyNamed = (props: ComponentProps<Module[Name]>) => {
61
68
  // `loaded` fills in place; compiled, the element drawn before the module landed would stay.
62
69
  'use no memo';
63
70
  const Component: ComponentType<any> = loaded.current ?? Lazy;
71
+
64
72
  return <Component {...props} />;
65
73
  };
74
+
66
75
  return LazyNamed;
67
76
  }
@@ -391,6 +391,7 @@ export default {
391
391
  continue: 'Continue',
392
392
  continueHint: 'Continue the stopped turn',
393
393
  send: 'Send',
394
+ sendActions: 'More send options',
394
395
  clear: {
395
396
  title: 'Clear thread?',
396
397
  description: 'A turn is running.',
@@ -391,6 +391,7 @@ export default {
391
391
  continue: 'Doorgaan',
392
392
  continueHint: 'Ga verder met de gestopte beurt',
393
393
  send: 'Versturen',
394
+ sendActions: 'Meer verzendopties',
394
395
  clear: {
395
396
  title: 'Thread wissen?',
396
397
  description: 'Er loopt een turn.',
@@ -27,12 +27,15 @@ export class MountedRegistry<T extends MountedEntry> extends Map<string, T> {
27
27
  */
28
28
  async reattachAll(reattach: (id: string, entry: T) => Promise<void>): Promise<void> {
29
29
  const queue = [...this];
30
+
30
31
  const work = async (): Promise<void> => {
31
32
  for (let next = queue.shift(); next !== undefined; next = queue.shift()) {
32
33
  const [id, entry] = next;
34
+
33
35
  if (entry.attached || this.get(id) !== entry) {
34
36
  continue;
35
37
  }
38
+
36
39
  try {
37
40
  await reattach(id, entry);
38
41
  } catch {
@@ -40,6 +43,7 @@ export class MountedRegistry<T extends MountedEntry> extends Map<string, T> {
40
43
  }
41
44
  }
42
45
  };
46
+
43
47
  await Promise.all(Array.from({ length: REATTACH_CONCURRENCY }, work));
44
48
  }
45
49
  }
@@ -34,32 +34,45 @@ export function portTransport(port: FramePort): PortTransport {
34
34
 
35
35
  const answer = (id: string, frame: { ok: true; result: unknown } | { ok: false; error: { code: string; message: string } }): void => {
36
36
  const entry = pending.get(id);
37
+
37
38
  if (!entry) {
38
39
  return;
39
40
  }
41
+
40
42
  pending.delete(id);
43
+
41
44
  if (!frame.ok) {
42
45
  entry.reject(new ChatTransportError(frame.error.code, frame.error.message));
46
+
43
47
  return;
44
48
  }
49
+
45
50
  const result = AGENT_REQUEST_SCHEMAS[entry.type].result.safeParse(frame.result);
51
+
46
52
  if (!result.success) {
47
53
  entry.reject(new ChatTransportError('bad-reply', i18next.t('agent-chat:transport.badReply', { type: entry.type })));
54
+
48
55
  return;
49
56
  }
57
+
50
58
  entry.resolve(result.data);
51
59
  };
52
60
 
53
61
  const dispatch = (event: string, payload: unknown): void => {
54
62
  if (!isEventType(event)) {
55
63
  console.warn('Dropped an unknown event', event);
64
+
56
65
  return;
57
66
  }
67
+
58
68
  const parsed = AGENT_EVENT_SCHEMAS[event].safeParse(payload);
69
+
59
70
  if (!parsed.success) {
60
71
  console.warn(`Dropped a malformed ${event} event`);
72
+
61
73
  return;
62
74
  }
75
+
63
76
  // A copy, so a handler that unsubscribes while it runs does not change the set being walked.
64
77
  for (const handler of [...(eventHandlers.get(event) ?? [])]) {
65
78
  (handler as (payload: unknown) => void)(parsed.data);
@@ -70,21 +83,30 @@ export function portTransport(port: FramePort): PortTransport {
70
83
  if (status === 'closed') {
71
84
  return;
72
85
  }
86
+
73
87
  const parsed = parseServerFrame(raw);
88
+
74
89
  if (!parsed.ok) {
75
90
  console.warn('Dropped a malformed frame', parsed.message);
91
+
76
92
  return;
77
93
  }
94
+
78
95
  const frame = parsed.value;
96
+
79
97
  if ('event' in frame) {
80
98
  dispatch(frame.event, frame.payload);
99
+
81
100
  return;
82
101
  }
102
+
83
103
  if (frame.id === null) {
84
104
  // An answer without an id is about a frame the host could not read; nothing waits for it.
85
105
  console.warn('The chat host refused a frame', frame);
106
+
86
107
  return;
87
108
  }
109
+
88
110
  answer(frame.id, frame);
89
111
  });
90
112
 
@@ -96,29 +118,36 @@ export function portTransport(port: FramePort): PortTransport {
96
118
  if (status !== 'open') {
97
119
  return Promise.reject(new ChatTransportError('not-connected', i18next.t('agent-chat:transport.notConnected')));
98
120
  }
121
+
99
122
  const id = String(nextId++);
123
+
100
124
  const promise = new Promise<ChatRequestMap[T]['result']>((resolve, reject) => {
101
125
  pending.set(id, { type, resolve: resolve as (result: unknown) => void, reject });
102
126
  });
127
+
103
128
  try {
104
129
  port.send({ id, type, payload });
105
130
  } catch {
106
131
  // A payload the port cannot clone throws here, and the caller only listens to the promise.
107
132
  pending.delete(id);
133
+
108
134
  return Promise.reject(new ChatTransportError('not-sent', i18next.t('agent-chat:transport.notSent', { type })));
109
135
  }
136
+
110
137
  return promise;
111
138
  },
112
139
  on<E extends AgentEventType>(event: E, handler: (payload: ChatEventMap[E]) => void): () => void {
113
140
  const handlers = eventHandlers.get(event) ?? new Set();
114
141
  eventHandlers.set(event, handlers);
115
142
  handlers.add(handler);
143
+
116
144
  return () => {
117
145
  handlers.delete(handler);
118
146
  };
119
147
  },
120
148
  subscribeStatus(handler: (status: ChatTransportStatus) => void): () => void {
121
149
  statusHandlers.add(handler);
150
+
122
151
  return () => {
123
152
  statusHandlers.delete(handler);
124
153
  };
@@ -127,13 +156,16 @@ export function portTransport(port: FramePort): PortTransport {
127
156
  if (status === 'closed') {
128
157
  return;
129
158
  }
159
+
130
160
  status = 'closed';
131
161
  stopListening();
132
162
  const waiting = [...pending.values()];
133
163
  pending.clear();
164
+
134
165
  for (const entry of waiting) {
135
166
  entry.reject(new ChatTransportError('disconnected', i18next.t('agent-chat:transport.closedBeforeAnswer')));
136
167
  }
168
+
137
169
  for (const handler of [...statusHandlers]) {
138
170
  handler(status);
139
171
  }
@@ -2,6 +2,7 @@
2
2
  export { focusPromptStart } from './logic/focus';
3
3
  export { PROMPT_SHORTCUTS, pageKey, type PromptKeyEvent } from './logic/keys';
4
4
  export { type PendingPrompt, type PromptAction, emptyPromptDraft, orderPrompts, type PromptAnswer, type PromptDraft } from './logic/prompts';
5
+
5
6
  export {
6
7
  type HostPrompt,
7
8
  type PromptSubject,
@@ -13,6 +14,7 @@ export {
13
14
  isBlockingSubject,
14
15
  answerPrompt
15
16
  } from './logic/subjects';
17
+
16
18
  export { useFocusAfterAnswer, type FocusAfterAnswer } from './logic/useFocusAfterAnswer';
17
19
  export { usePromptSession, type PromptSession } from './logic/usePromptSession';
18
20
  export { PromptPrimary, ApprovalActions, type ApprovalButton } from './ui/PromptActions';
@@ -5,15 +5,19 @@
5
5
  */
6
6
  export function focusPromptStart(root: ParentNode | null | undefined): boolean {
7
7
  const card = root?.querySelector<HTMLElement>('.prompt-card');
8
+
8
9
  if (!card) {
9
10
  return false;
10
11
  }
12
+
11
13
  const start =
12
14
  card.querySelector<HTMLElement>('[data-prompt-entry]:not(:disabled)') ??
13
15
  card.querySelector<HTMLElement>('[role="toolbar"] button:not(:disabled):not([data-prompt-primary]):not([data-prompt-lasting])') ??
14
16
  card.querySelector<HTMLElement>('[data-prompt-primary]:not(:disabled)') ??
15
17
  card.querySelector<HTMLElement>('.prompt-heading');
18
+
16
19
  start?.focus({ preventScroll: true });
20
+
17
21
  return start !== null;
18
22
  }
19
23
 
@@ -21,5 +25,6 @@ export function focusPromptStart(root: ParentNode | null | undefined): boolean {
21
25
  export function focusPromptHeading(root: ParentNode | null | undefined): boolean {
22
26
  const heading = root?.querySelector<HTMLElement>('.prompt-card .prompt-heading');
23
27
  heading?.focus({ preventScroll: true });
28
+
24
29
  return !!heading;
25
30
  }
@@ -30,6 +30,7 @@ export function stepIndex(index: number, count: number, to: ListStep): number {
30
30
  if (count === 0) {
31
31
  return -1;
32
32
  }
33
+
33
34
  switch (to) {
34
35
  case 'first':
35
36
  return 0;
@@ -50,19 +51,25 @@ export function choiceKey(event: PromptKeyEvent, { multiSelect, anyChosen }: { m
50
51
  if (!isPlain(event)) {
51
52
  return null;
52
53
  }
54
+
53
55
  const step = LIST_KEYS[event.key];
56
+
54
57
  if (step) {
55
58
  return { kind: 'move', to: step };
56
59
  }
60
+
57
61
  if (event.code === 'Space') {
58
62
  return { kind: 'pick' };
59
63
  }
64
+
60
65
  if (event.key === 'Enter') {
61
66
  if (!multiSelect) {
62
67
  return { kind: 'pick-and-commit' };
63
68
  }
69
+
64
70
  return anyChosen ? { kind: 'commit' } : { kind: 'pick' };
65
71
  }
72
+
66
73
  return null;
67
74
  }
68
75
 
@@ -74,13 +81,16 @@ export function answerFieldKey(
74
81
  if (!isPlain(event)) {
75
82
  return null;
76
83
  }
84
+
77
85
  if (event.key === 'ArrowUp' && belowChoices && caretAtStart) {
78
86
  return { kind: 'to-last-choice' };
79
87
  }
88
+
80
89
  if (event.key === 'Enter') {
81
90
  // An empty answer is not sent, and an empty line in front of the answer is never what Enter meant.
82
91
  return hasText ? { kind: 'commit' } : { kind: 'ignore' };
83
92
  }
93
+
84
94
  return null;
85
95
  }
86
96
 
@@ -101,9 +111,11 @@ export function pageKey(event: PromptKeyEvent, apple: boolean, typing: boolean):
101
111
  if (typing || event.isComposing) {
102
112
  return null;
103
113
  }
114
+
104
115
  if (matchesShortcut(PROMPT_SHORTCUTS.previousPrompt, event, apple)) {
105
116
  return -1;
106
117
  }
118
+
107
119
  return matchesShortcut(PROMPT_SHORTCUTS.nextPrompt, event, apple) ? 1 : null;
108
120
  }
109
121
 
@@ -2,17 +2,20 @@ import type { ChatApprovalItem, ChatQuestionItem, ChatQuestion } from '@adecore/
2
2
  import { toggleChoice } from '../../chat/logic/answers';
3
3
 
4
4
  export type PendingPrompt = ChatApprovalItem | ChatQuestionItem;
5
+
5
6
  export type PromptAction =
6
7
  | { kind: 'approve'; decision: 'allow' | 'allow-always' | 'deny'; message?: string }
7
8
  | { kind: 'answer'; answers: Record<string, string> }
8
9
  | { kind: 'dismiss' }
9
10
  // A terminal's permission request, answered with one of the choices the host offered.
10
11
  | { kind: 'choose'; choiceId: string };
12
+
11
13
  export interface PromptAnswer {
12
14
  choices: string[];
13
15
  text: string;
14
16
  custom: boolean;
15
17
  }
18
+
16
19
  export interface PromptDraft {
17
20
  index: number;
18
21
  answers: Record<string, PromptAnswer>;
@@ -23,9 +26,11 @@ export interface PromptDraft {
23
26
  export function emptyPromptDraft(): PromptDraft {
24
27
  return { index: 0, answers: {}, reason: '', showReason: false };
25
28
  }
29
+
26
30
  export function answerValue(answer: PromptAnswer): string {
27
31
  return answer.custom ? answer.text.trim() : answer.choices.join(', ');
28
32
  }
33
+
29
34
  export function questionAnswer(draft: PromptDraft, question: ChatQuestion): PromptAnswer {
30
35
  return draft.answers[question.id] ?? { choices: [], text: '', custom: question.choices.length === 0 };
31
36
  }
@@ -46,13 +51,16 @@ export function orderPrompts<T>(prompts: readonly T[], blocking: (prompt: T) =>
46
51
 
47
52
  export function nextPrompt(items: readonly PendingPrompt[], activeId: string | null): PendingPrompt | null {
48
53
  const active = items.find((item) => item.requestId === activeId);
54
+
49
55
  if (active) {
50
56
  return active;
51
57
  }
58
+
52
59
  return orderPrompts(items, isBlockingPrompt, (item) => item.createdAt)[0] ?? null;
53
60
  }
54
61
 
55
62
  export function promptAnswers(item: ChatQuestionItem, draft: PromptDraft): Record<string, string> | null {
56
63
  const answers = Object.fromEntries(item.questions.map((question) => [question.id, answerValue(questionAnswer(draft, question))]));
64
+
57
65
  return Object.values(answers).every((answer) => answer !== '') ? answers : null;
58
66
  }
@@ -78,8 +78,10 @@ export function approvalButtons(subject: PromptSubject, reason: string): Approva
78
78
  if (subject.kind !== 'chat' || subject.item.kind !== 'approval') {
79
79
  return [];
80
80
  }
81
+
81
82
  const { item } = subject;
82
83
  const message = reason.trim();
84
+
83
85
  return [
84
86
  ...(item.canAllowAlways && item.allowAlways
85
87
  ? [
@@ -116,18 +118,24 @@ export async function answerPrompt(
116
118
  if (subject.kind === 'host') {
117
119
  return host.prompts.answer(subject.prompt, action);
118
120
  }
121
+
119
122
  if (chat === null) {
120
123
  throw new Error(i18next.t('agent-prompts:error.notConnected'));
121
124
  }
125
+
122
126
  const { item, nodeId } = subject;
127
+
123
128
  if (action.kind === 'approve') {
124
129
  return chat.approve(nodeId, item.requestId, action.decision, action.message);
125
130
  }
131
+
126
132
  if (action.kind === 'answer') {
127
133
  return chat.answer(nodeId, item.requestId, action.answers);
128
134
  }
135
+
129
136
  if (action.kind === 'dismiss') {
130
137
  return chat.dismiss(nodeId, item.id);
131
138
  }
139
+
132
140
  throw new Error(i18next.t('agent-prompts:error.noChoices'));
133
141
  }
@@ -19,7 +19,9 @@ export function useFocusAfterAnswer(activeId: string | null, container: RefObjec
19
19
  if (!pending.current) {
20
20
  return;
21
21
  }
22
+
22
23
  pending.current = false;
24
+
23
25
  if (!focusPromptStart(container.current)) {
24
26
  onEmpty();
25
27
  }
@@ -1,4 +1,5 @@
1
1
  import { useRef, useState, type MouseEvent } from 'react';
2
+ import { errorMessage } from '../../transport';
2
3
  import { emptyPromptDraft, type PromptDraft } from './prompts';
3
4
 
4
5
  interface PromptSessionOptions<T> {
@@ -42,13 +43,16 @@ export function usePromptSession<T>({ prompts, idOf, pick, disabled }: PromptSes
42
43
  const busy = useRef(false);
43
44
  const pressedRequest = useRef<string | null>(null);
44
45
  const stillCompleted = completed.filter((id) => prompts.some((prompt) => idOf(prompt) === id));
46
+
45
47
  if (stillCompleted.length !== completed.length) {
46
48
  setCompleted(stillCompleted);
47
49
  }
50
+
48
51
  const waiting = prompts.filter((prompt) => !completed.includes(idOf(prompt)));
49
52
  const active = pick(waiting, activeId, activeIndex);
50
53
  const activeKey = active === null ? null : idOf(active);
51
54
  const index = active === null ? 0 : waiting.indexOf(active);
55
+
52
56
  if (index !== activeIndex) {
53
57
  setActiveIndex(index);
54
58
  }
@@ -60,32 +64,40 @@ export function usePromptSession<T>({ prompts, idOf, pick, disabled }: PromptSes
60
64
 
61
65
  const finish = (id: string) => {
62
66
  setCompleted((current) => [...current, id]);
67
+
63
68
  setDrafts((current) => {
64
69
  const next = { ...current };
65
70
  delete next[id];
71
+
66
72
  return next;
67
73
  });
68
74
  };
75
+
69
76
  const act = async (prompt: T, send: () => Promise<void>): Promise<'sent' | 'failed' | 'busy'> => {
70
77
  if (busy.current || disabled) {
71
78
  return 'busy';
72
79
  }
80
+
73
81
  const id = idOf(prompt);
74
82
  busy.current = true;
75
83
  setSending(true);
76
84
  setError(null);
85
+
77
86
  try {
78
87
  await send();
79
88
  finish(id);
89
+
80
90
  return 'sent';
81
91
  } catch (cause) {
82
- setError({ id, message: cause instanceof Error ? cause.message : String(cause) });
92
+ setError({ id, message: errorMessage(cause) });
93
+
83
94
  return 'failed';
84
95
  } finally {
85
96
  busy.current = false;
86
97
  setSending(false);
87
98
  }
88
99
  };
100
+
89
101
  return {
90
102
  active,
91
103
  activeId: activeKey,
@@ -32,6 +32,7 @@ function ApprovalDetails({ item }: { item: ChatApprovalItem }) {
32
32
  const input = typeof item.input === 'object' && item.input !== null ? (item.input as Record<string, unknown>) : {};
33
33
  // Some agents describe a command by the command itself, which the box under it already shows.
34
34
  const describes = item.description && item.description.trim() !== (typeof input.command === 'string' ? input.command.trim() : null);
35
+
35
36
  return (
36
37
  <>
37
38
  {describes && <p className="text-sm text-text-muted">{item.description}</p>}
@@ -82,6 +83,7 @@ export function ApprovalBody({
82
83
  const host = useChatHost();
83
84
  const { t } = useTranslation('agent-prompts');
84
85
  const AnswerField = host.dictation.Textarea;
86
+
85
87
  return (
86
88
  <>
87
89
  <ApprovalDetails item={item} />
@@ -32,6 +32,7 @@ export function ApprovalActions({
32
32
  sending: boolean;
33
33
  }) {
34
34
  const { t } = useTranslation('agent-prompts');
35
+
35
36
  return (
36
37
  <>
37
38
  {onAddReason && (
@@ -87,6 +88,7 @@ export function QuestionActions({
87
88
  onCommit(): void;
88
89
  }) {
89
90
  const { t } = useTranslation(['agent-prompts', 'agent-chat']);
91
+
90
92
  return (
91
93
  <>
92
94
  {index > 0 && (
@@ -35,6 +35,7 @@ interface PromptCardProps {
35
35
  function onCardKeyDown(event: KeyboardEvent<HTMLDivElement>, apple: boolean) {
36
36
  const card = event.currentTarget;
37
37
  const target = event.target instanceof HTMLElement ? event.target : null;
38
+
38
39
  if (!event.defaultPrevented) {
39
40
  if (isPrimaryKey(event.nativeEvent, apple)) {
40
41
  event.preventDefault();
@@ -46,6 +47,7 @@ function onCardKeyDown(event: KeyboardEvent<HTMLDivElement>, apple: boolean) {
46
47
  } else {
47
48
  const toolbar = target?.closest('[role="toolbar"]');
48
49
  const step = toolbar ? toolbarKey(event.nativeEvent) : null;
50
+
49
51
  if (toolbar && step !== null) {
50
52
  event.preventDefault();
51
53
  const buttons = [...toolbar.querySelectorAll<HTMLButtonElement>('button:not(:disabled)')];
@@ -54,6 +56,7 @@ function onCardKeyDown(event: KeyboardEvent<HTMLDivElement>, apple: boolean) {
54
56
  }
55
57
  }
56
58
  }
59
+
57
60
  if (event.defaultPrevented || staysInCard(event.nativeEvent)) {
58
61
  event.stopPropagation();
59
62
  }
@@ -63,6 +66,7 @@ function onCardKeyDown(event: KeyboardEvent<HTMLDivElement>, apple: boolean) {
63
66
  export function PromptCard({ kind, heading, meta, top, actions, busy, notice, error, children }: PromptCardProps) {
64
67
  const { t } = useTranslation('agent-prompts');
65
68
  const { isApplePlatform } = useChatHost();
69
+
66
70
  const card = (
67
71
  <div
68
72
  className="prompt-card flex min-h-0 flex-col gap-2 p-3"
@@ -96,9 +100,11 @@ export function PromptCard({ kind, heading, meta, top, actions, busy, notice, er
96
100
  </div>
97
101
  </div>
98
102
  );
103
+
99
104
  if (top === undefined) {
100
105
  return card;
101
106
  }
107
+
102
108
  return (
103
109
  <div className="flex min-h-0 flex-col">
104
110
  {top}