@adecore/agents-react 0.39.0 → 0.40.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 (317) hide show
  1. package/dist/agents/AgentModelLabel.js +2 -1
  2. package/dist/agents/model-name.js +2 -1
  3. package/dist/agents/provider-paths.d.ts +2 -1
  4. package/dist/agents/provider-paths.js +7 -3
  5. package/dist/chat/account-choice.js +57 -28
  6. package/dist/chat/bookmarks.js +2 -1
  7. package/dist/chat/drafts.d.ts +0 -1
  8. package/dist/chat/drafts.js +9 -28
  9. package/dist/chat/logic/picker-entries.js +2 -8
  10. package/dist/chat/logic/resume-compaction.js +2 -5
  11. package/dist/chat/logic/timeline-copy.js +2 -8
  12. package/dist/chat/logic/timeline.js +9 -10
  13. package/dist/chat/logic/workflow.js +2 -4
  14. package/dist/chat/subagent-conversation.js +2 -1
  15. package/dist/chat/ui/GoalDock.js +2 -1
  16. package/dist/chat/ui/LimitState.js +4 -6
  17. package/dist/chat/ui/composer/use-continuable-turn.js +0 -1
  18. package/dist/chat/ui/composer/use-notice.js +2 -1
  19. package/dist/chat/ui/composer/use-prompt-history.js +1 -1
  20. package/dist/chat/ui/composer/use-prompt-menus.js +216 -253
  21. package/dist/chat/ui/composer/use-take-back.js +2 -2
  22. package/dist/chat/ui/diff/ApprovalReview.js +1 -1
  23. package/dist/chat/ui/highlighter.d.ts +0 -6
  24. package/dist/chat/ui/highlighter.js +0 -12
  25. package/dist/chat/ui/icons.d.ts +0 -1
  26. package/dist/chat/ui/icons.js +0 -3
  27. package/dist/chat/ui/rows/MessageRows.js +2 -5
  28. package/dist/chat/ui/rows/row-rhythm.js +2 -5
  29. package/dist/chat/ui/theme-watch.js +3 -7
  30. package/dist/prompts/logic/usePromptSession.js +2 -1
  31. package/dist/providers/AddAccountForm.js +2 -1
  32. package/dist/providers/CliDetail.js +104 -130
  33. package/dist/providers/account-actions.js +6 -9
  34. package/dist/state/chats.d.ts +1 -0
  35. package/dist/state/chats.js +6 -11
  36. package/dist/state/provider-accounts.d.ts +1 -1
  37. package/dist/state/provider-accounts.js +2 -8
  38. package/dist/transport.d.ts +2 -0
  39. package/dist/transport.js +14 -0
  40. package/dist/usage/ChartLogo.d.ts +5 -0
  41. package/dist/usage/ChartLogo.js +48 -0
  42. package/dist/usage/LimitsList.js +6 -16
  43. package/dist/usage/UsageBreakdown.js +17 -40
  44. package/dist/usage/UsageLimits.js +183 -211
  45. package/dist/usage/UsagePage.js +3 -17
  46. package/dist/usage/UsageSummary.js +4 -9
  47. package/dist/usage/limits.js +16 -31
  48. package/dist/usage/summary.d.ts +1 -1
  49. package/dist/usage/summary.js +3 -5
  50. package/package.json +27 -27
  51. package/src/agents/AccountDot.tsx +1 -0
  52. package/src/agents/AgentIcon.tsx +2 -0
  53. package/src/agents/AgentModelLabel.tsx +4 -6
  54. package/src/agents/ModelOptionControl.tsx +8 -0
  55. package/src/agents/account-limits.ts +3 -0
  56. package/src/agents/accounts.ts +12 -0
  57. package/src/agents/model-name.ts +13 -1
  58. package/src/agents/model-options.ts +2 -0
  59. package/src/agents/model-requirement.ts +4 -0
  60. package/src/agents/option-words.ts +2 -0
  61. package/src/agents/provider-paths.ts +8 -3
  62. package/src/agents/status-look.ts +1 -0
  63. package/src/chat/account-choice.ts +25 -19
  64. package/src/chat/actions.ts +5 -0
  65. package/src/chat/attachments.ts +8 -0
  66. package/src/chat/bookmarks.ts +13 -1
  67. package/src/chat/chat-client.ts +63 -0
  68. package/src/chat/chat-references.ts +3 -0
  69. package/src/chat/composer-effort.ts +5 -0
  70. package/src/chat/composer-send.ts +4 -0
  71. package/src/chat/drafts.ts +21 -17
  72. package/src/chat/forks.ts +4 -0
  73. package/src/chat/guards.ts +2 -0
  74. package/src/chat/index.ts +7 -0
  75. package/src/chat/logic/ansi.ts +47 -0
  76. package/src/chat/logic/bookmarks.ts +7 -0
  77. package/src/chat/logic/command-looks.ts +29 -0
  78. package/src/chat/logic/context-usage.ts +3 -0
  79. package/src/chat/logic/continue-after-stop.ts +5 -0
  80. package/src/chat/logic/effort.ts +4 -0
  81. package/src/chat/logic/embedded-code.ts +63 -0
  82. package/src/chat/logic/fork.ts +22 -0
  83. package/src/chat/logic/generated-image.ts +7 -0
  84. package/src/chat/logic/goal.ts +7 -0
  85. package/src/chat/logic/handback.ts +2 -0
  86. package/src/chat/logic/limit.ts +8 -0
  87. package/src/chat/logic/picker-entries.ts +16 -4
  88. package/src/chat/logic/resume-compaction.ts +22 -5
  89. package/src/chat/logic/scrubber.ts +38 -5
  90. package/src/chat/logic/thought.ts +5 -0
  91. package/src/chat/logic/thread-cards.ts +6 -0
  92. package/src/chat/logic/timeline-copy.ts +4 -9
  93. package/src/chat/logic/timeline-target.ts +15 -0
  94. package/src/chat/logic/timeline.ts +113 -10
  95. package/src/chat/logic/tools.ts +30 -0
  96. package/src/chat/logic/visual-height.ts +5 -0
  97. package/src/chat/logic/visual-viewport.ts +1 -0
  98. package/src/chat/logic/welcome.ts +3 -0
  99. package/src/chat/logic/workflow.ts +26 -7
  100. package/src/chat/mentions.ts +56 -0
  101. package/src/chat/pending-prompts.ts +16 -0
  102. package/src/chat/persisted-json.ts +15 -0
  103. package/src/chat/preferences.ts +16 -0
  104. package/src/chat/prompt-hold.ts +2 -0
  105. package/src/chat/quote.ts +7 -0
  106. package/src/chat/reading-width.ts +1 -0
  107. package/src/chat/recent-messages.ts +5 -0
  108. package/src/chat/resume-compaction-dismissals.ts +1 -0
  109. package/src/chat/stash.ts +12 -0
  110. package/src/chat/subagent-conversation.ts +24 -4
  111. package/src/chat/subagent-list.ts +14 -0
  112. package/src/chat/subagent-support.ts +1 -0
  113. package/src/chat/subagent-view.ts +9 -0
  114. package/src/chat/thumbnails.ts +14 -0
  115. package/src/chat/timeline-flash.ts +2 -0
  116. package/src/chat/timeline-scroll.ts +23 -0
  117. package/src/chat/ui/AccountPill.tsx +4 -0
  118. package/src/chat/ui/AnsiOutput.tsx +1 -0
  119. package/src/chat/ui/BookmarkMarker.tsx +5 -0
  120. package/src/chat/ui/BookmarkSubmenu.tsx +1 -0
  121. package/src/chat/ui/ChatActivity.tsx +13 -0
  122. package/src/chat/ui/CodeBlock.tsx +8 -0
  123. package/src/chat/ui/Composer.tsx +15 -0
  124. package/src/chat/ui/ComposerInput.tsx +22 -0
  125. package/src/chat/ui/DiffPool.tsx +3 -0
  126. package/src/chat/ui/EditDiff.tsx +5 -0
  127. package/src/chat/ui/GoalDock.tsx +13 -1
  128. package/src/chat/ui/ImageView.tsx +11 -0
  129. package/src/chat/ui/LimitState.tsx +23 -6
  130. package/src/chat/ui/Markdown.tsx +41 -0
  131. package/src/chat/ui/MermaidBlock.tsx +16 -0
  132. package/src/chat/ui/MessageActions.tsx +8 -0
  133. package/src/chat/ui/ModelMarks.tsx +1 -0
  134. package/src/chat/ui/Pickers.tsx +26 -0
  135. package/src/chat/ui/PromptComposer.tsx +2 -0
  136. package/src/chat/ui/PromptEditor.tsx +6 -0
  137. package/src/chat/ui/QuoteButton.tsx +12 -0
  138. package/src/chat/ui/ReferenceChip.tsx +3 -0
  139. package/src/chat/ui/ResumeCompactionDock.tsx +1 -0
  140. package/src/chat/ui/RowTime.tsx +1 -0
  141. package/src/chat/ui/RunSettings.tsx +38 -0
  142. package/src/chat/ui/Scrubber.tsx +14 -0
  143. package/src/chat/ui/SessionLine.tsx +1 -0
  144. package/src/chat/ui/ShellCommand.tsx +32 -0
  145. package/src/chat/ui/SubagentControls.tsx +7 -0
  146. package/src/chat/ui/SubagentInfo.tsx +1 -0
  147. package/src/chat/ui/SubagentStopButton.tsx +6 -0
  148. package/src/chat/ui/SubagentTimeline.tsx +10 -0
  149. package/src/chat/ui/Timeline.tsx +119 -0
  150. package/src/chat/ui/TimelineMenu.tsx +5 -0
  151. package/src/chat/ui/UnifiedDiff.tsx +6 -0
  152. package/src/chat/ui/UploadThumb.tsx +2 -0
  153. package/src/chat/ui/VisualDialogs.tsx +3 -0
  154. package/src/chat/ui/VisualFrame.tsx +15 -0
  155. package/src/chat/ui/Welcome.tsx +1 -0
  156. package/src/chat/ui/chip-clipboard.ts +20 -0
  157. package/src/chat/ui/code-lines.ts +14 -0
  158. package/src/chat/ui/code-theme.ts +1 -0
  159. package/src/chat/ui/composer/ComposerControls.tsx +5 -0
  160. package/src/chat/ui/composer/ComposerMenu.tsx +12 -0
  161. package/src/chat/ui/composer/DraftParts.tsx +2 -0
  162. package/src/chat/ui/composer/QueuedMessages.tsx +4 -0
  163. package/src/chat/ui/composer/chip-paste.ts +16 -0
  164. package/src/chat/ui/composer/chips.ts +6 -0
  165. package/src/chat/ui/composer/editor.ts +9 -0
  166. package/src/chat/ui/composer/insert.ts +2 -0
  167. package/src/chat/ui/composer/key-handler.ts +39 -0
  168. package/src/chat/ui/composer/keys.ts +23 -0
  169. package/src/chat/ui/composer/placeholder.ts +6 -0
  170. package/src/chat/ui/composer/prompt-picks.ts +1 -0
  171. package/src/chat/ui/composer/surface.ts +5 -0
  172. package/src/chat/ui/composer/use-chat-controls.ts +13 -0
  173. package/src/chat/ui/composer/use-compaction-offer.ts +3 -0
  174. package/src/chat/ui/composer/use-composer-menus.ts +2 -0
  175. package/src/chat/ui/composer/use-composer-submit.ts +27 -0
  176. package/src/chat/ui/composer/use-continuable-turn.ts +3 -5
  177. package/src/chat/ui/composer/use-draft-files.ts +24 -0
  178. package/src/chat/ui/composer/use-effort-stepper.ts +8 -0
  179. package/src/chat/ui/composer/use-notice.ts +4 -1
  180. package/src/chat/ui/composer/use-pending-requests.ts +3 -0
  181. package/src/chat/ui/composer/use-prompt-history.ts +20 -11
  182. package/src/chat/ui/composer/use-prompt-hold.ts +3 -0
  183. package/src/chat/ui/composer/use-prompt-menus.ts +34 -14
  184. package/src/chat/ui/composer/use-take-back.ts +20 -2
  185. package/src/chat/ui/diff/ApprovalReview.tsx +18 -15
  186. package/src/chat/ui/diff/DiffFallbackBoundary.tsx +1 -0
  187. package/src/chat/ui/diff/DiffOmitted.tsx +1 -0
  188. package/src/chat/ui/diff/DiffStack.tsx +2 -0
  189. package/src/chat/ui/diff/DiffUnavailable.tsx +1 -0
  190. package/src/chat/ui/diff/EditorEditDiff.tsx +13 -0
  191. package/src/chat/ui/diff/EditorPatchDiff.tsx +4 -0
  192. package/src/chat/ui/diff/LegacyDiffPool.tsx +2 -0
  193. package/src/chat/ui/diff/LegacyEditDiff.tsx +2 -0
  194. package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +8 -0
  195. package/src/chat/ui/diff/RawDiff.tsx +1 -0
  196. package/src/chat/ui/diff/ReviewDiff.tsx +1 -0
  197. package/src/chat/ui/diff/diff-source-budget.ts +8 -0
  198. package/src/chat/ui/diff/editor-edit-model.ts +4 -0
  199. package/src/chat/ui/diff/review-plan.ts +9 -0
  200. package/src/chat/ui/diff/review-settle.ts +1 -0
  201. package/src/chat/ui/diff/syntax-runtime.ts +52 -0
  202. package/src/chat/ui/diff/use-review-stack.ts +9 -0
  203. package/src/chat/ui/diff-syntax.ts +12 -0
  204. package/src/chat/ui/diff-theme.ts +2 -0
  205. package/src/chat/ui/file-links.ts +2 -0
  206. package/src/chat/ui/find-reveal.ts +1 -0
  207. package/src/chat/ui/full-diff.ts +1 -0
  208. package/src/chat/ui/highlighter.ts +29 -15
  209. package/src/chat/ui/icons.tsx +2 -4
  210. package/src/chat/ui/index.ts +4 -0
  211. package/src/chat/ui/intelligent-ui/UiBlockFrame.tsx +25 -0
  212. package/src/chat/ui/intelligent-ui/UiFallbackPart.tsx +1 -0
  213. package/src/chat/ui/intelligent-ui/UiReply.tsx +63 -0
  214. package/src/chat/ui/intelligent-ui/block-local.ts +5 -0
  215. package/src/chat/ui/intelligent-ui/chart-data.ts +15 -0
  216. package/src/chat/ui/intelligent-ui/node-text.ts +17 -0
  217. package/src/chat/ui/intelligent-ui/renderers/binding.ts +1 -0
  218. package/src/chat/ui/intelligent-ui/renderers/chart.tsx +31 -0
  219. package/src/chat/ui/intelligent-ui/renderers/choices.tsx +4 -0
  220. package/src/chat/ui/intelligent-ui/renderers/content.tsx +18 -0
  221. package/src/chat/ui/intelligent-ui/renderers/data.tsx +11 -0
  222. package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +13 -0
  223. package/src/chat/ui/intelligent-ui/renderers/links.tsx +9 -0
  224. package/src/chat/ui/intelligent-ui/renderers/map-slots.ts +15 -0
  225. package/src/chat/ui/intelligent-ui/renderers/map-style.ts +15 -0
  226. package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +48 -0
  227. package/src/chat/ui/intelligent-ui/renderers/map.tsx +20 -0
  228. package/src/chat/ui/intelligent-ui/renderers/place-point.ts +3 -0
  229. package/src/chat/ui/intelligent-ui/renderers/structure.tsx +3 -0
  230. package/src/chat/ui/intelligent-ui/renderers/text.tsx +8 -0
  231. package/src/chat/ui/intelligent-ui/reply-parts.ts +5 -0
  232. package/src/chat/ui/intelligent-ui/table-data.ts +17 -0
  233. package/src/chat/ui/intelligent-ui/use-in-view.ts +4 -0
  234. package/src/chat/ui/intelligent-ui/use-later.ts +4 -0
  235. package/src/chat/ui/intelligent-ui/use-truncated.ts +5 -0
  236. package/src/chat/ui/intelligent-ui/use-ui-links.ts +29 -0
  237. package/src/chat/ui/intelligent-ui/use-ui-queries.ts +60 -0
  238. package/src/chat/ui/markdown-blocks.ts +64 -0
  239. package/src/chat/ui/mermaid.ts +4 -0
  240. package/src/chat/ui/quote-selection.ts +10 -0
  241. package/src/chat/ui/rehype-chips.ts +5 -0
  242. package/src/chat/ui/rehype-fade.ts +2 -0
  243. package/src/chat/ui/remark-html-as-text.ts +5 -0
  244. package/src/chat/ui/reveal.ts +18 -0
  245. package/src/chat/ui/rows/ForksRow.tsx +5 -0
  246. package/src/chat/ui/rows/GeneratedImageRow.tsx +10 -0
  247. package/src/chat/ui/rows/MessageRows.tsx +29 -5
  248. package/src/chat/ui/rows/RowContainer.tsx +1 -0
  249. package/src/chat/ui/rows/Rows.tsx +2 -0
  250. package/src/chat/ui/rows/SubagentRow.tsx +13 -0
  251. package/src/chat/ui/rows/TaskRow.tsx +7 -0
  252. package/src/chat/ui/rows/VisualRow.tsx +4 -0
  253. package/src/chat/ui/rows/WorkRows.tsx +46 -0
  254. package/src/chat/ui/rows/WorkflowRow.tsx +10 -0
  255. package/src/chat/ui/rows/row-rhythm.ts +7 -5
  256. package/src/chat/ui/theme-watch.ts +8 -7
  257. package/src/chat/ui/thread-references.ts +7 -0
  258. package/src/chat/ui/use-diff-source-tokens.ts +8 -0
  259. package/src/chat/ui/useToggleSet.ts +3 -0
  260. package/src/chat/ui/visual-theme.ts +30 -0
  261. package/src/chat/visual-bridge.ts +23 -0
  262. package/src/chat/visual-viewport.ts +12 -0
  263. package/src/host-hooks.ts +6 -4
  264. package/src/host.ts +2 -0
  265. package/src/index.ts +3 -0
  266. package/src/lazy.tsx +9 -0
  267. package/src/mounted-registry.ts +4 -0
  268. package/src/port-transport.ts +32 -0
  269. package/src/prompts/index.ts +2 -0
  270. package/src/prompts/logic/focus.ts +5 -0
  271. package/src/prompts/logic/keys.ts +12 -0
  272. package/src/prompts/logic/prompts.ts +8 -0
  273. package/src/prompts/logic/subjects.ts +8 -0
  274. package/src/prompts/logic/useFocusAfterAnswer.ts +2 -0
  275. package/src/prompts/logic/usePromptSession.ts +13 -1
  276. package/src/prompts/ui/ApprovalBody.tsx +2 -0
  277. package/src/prompts/ui/PromptActions.tsx +2 -0
  278. package/src/prompts/ui/PromptCard.tsx +6 -0
  279. package/src/prompts/ui/PromptView.tsx +6 -0
  280. package/src/prompts/ui/QuestionBody.tsx +11 -0
  281. package/src/providers/AccountColors.tsx +1 -0
  282. package/src/providers/AccountDetail.tsx +7 -0
  283. package/src/providers/AccountVariables.tsx +2 -0
  284. package/src/providers/AddAccountForm.tsx +5 -1
  285. package/src/providers/CliDetail.tsx +6 -5
  286. package/src/providers/InstallPage.tsx +11 -0
  287. package/src/providers/ProviderList.tsx +3 -0
  288. package/src/providers/ProvidersPane.tsx +18 -0
  289. package/src/providers/account-actions.ts +10 -8
  290. package/src/providers/install-steps.ts +3 -0
  291. package/src/providers/pane-target.ts +5 -0
  292. package/src/providers/provider-abilities.ts +1 -0
  293. package/src/providers/provider-groups.ts +6 -0
  294. package/src/providers/variables.ts +12 -0
  295. package/src/scope.ts +2 -0
  296. package/src/state/chats.ts +61 -14
  297. package/src/state/index.ts +2 -0
  298. package/src/state/provider-accounts.ts +7 -9
  299. package/src/state/providers.ts +1 -0
  300. package/src/state/usage.ts +6 -0
  301. package/src/storage.ts +18 -0
  302. package/src/testing/prompts.ts +5 -0
  303. package/src/transport.ts +18 -0
  304. package/src/usage/ChartLogo.tsx +12 -0
  305. package/src/usage/LimitsList.tsx +7 -8
  306. package/src/usage/UsageBreakdown.tsx +11 -4
  307. package/src/usage/UsageChart.tsx +5 -0
  308. package/src/usage/UsageLimits.tsx +10 -7
  309. package/src/usage/UsageLimitsCard.tsx +2 -0
  310. package/src/usage/UsagePage.tsx +16 -19
  311. package/src/usage/UsageSummary.tsx +4 -5
  312. package/src/usage/UsageTiles.tsx +2 -0
  313. package/src/usage/format.ts +7 -0
  314. package/src/usage/limit-groups.ts +14 -0
  315. package/src/usage/limits.ts +11 -13
  316. package/src/usage/money.ts +1 -0
  317. package/src/usage/summary.ts +15 -7
@@ -34,9 +34,11 @@ function indentInFence(e: KeyboardEvent, view: EditorView): boolean {
34
34
  const { state } = view;
35
35
  const { from, to, head } = state.selection.main;
36
36
  const tree = ensureSyntaxTree(state, state.doc.length, 50) ?? syntaxTree(state);
37
+
37
38
  if (!inFenceBody(tree, state.doc.toString(), head)) {
38
39
  return false;
39
40
  }
41
+
40
42
  // Handled even when there is nothing to outdent, so Shift+Tab never throws the focus out of a block.
41
43
  if (e.shiftKey) {
42
44
  indentLess(view);
@@ -45,48 +47,62 @@ function indentInFence(e: KeyboardEvent, view: EditorView): boolean {
45
47
  } else {
46
48
  view.dispatch(state.replaceSelection(tabSpaces(head - state.doc.lineAt(head).from)), { scrollIntoView: true, userEvent: 'input' });
47
49
  }
50
+
48
51
  return true;
49
52
  }
50
53
 
51
54
  /* The keys an open picker takes: the arrows walk it, and Tab (or Enter, outside the commands) picks. */
52
55
  export function pickInMenu(e: KeyboardEvent, menus: ComposerMenus): boolean {
53
56
  const { activeIndex } = menus;
57
+
54
58
  if (menus.commandMenuOpen) {
55
59
  if (menus.moveInMenu(e.key, menus.commands.length)) {
56
60
  return true;
57
61
  }
62
+
58
63
  if (e.key === 'Tab') {
59
64
  menus.completeCommand(menus.commands[activeIndex]!);
65
+
60
66
  return true;
61
67
  }
62
68
  }
69
+
63
70
  if (menus.skillMenuOpen) {
64
71
  if (menus.moveInMenu(e.key, menus.skillMatches.length)) {
65
72
  return true;
66
73
  }
74
+
67
75
  if (e.key === 'Tab' || e.key === 'Enter') {
68
76
  menus.chooseSkill((menus.skillMatches[activeIndex] ?? menus.skillMatches[0]!).name);
77
+
69
78
  return true;
70
79
  }
71
80
  }
81
+
72
82
  if (menus.mentionMenuOpen && menus.mentionCount > 0) {
73
83
  if (menus.moveInMenu(e.key, menus.mentionCount)) {
74
84
  return true;
75
85
  }
86
+
76
87
  if (e.key === 'Tab' || e.key === 'Enter') {
77
88
  menus.chooseMentionAt(activeIndex);
89
+
78
90
  return true;
79
91
  }
80
92
  }
93
+
81
94
  if (menus.referenceMenuOpen && menus.referenceMatches.length > 0) {
82
95
  if (menus.moveInMenu(e.key, menus.referenceMatches.length)) {
83
96
  return true;
84
97
  }
98
+
85
99
  if (e.key === 'Tab' || e.key === 'Enter') {
86
100
  menus.chooseReferenceAt(activeIndex);
101
+
87
102
  return true;
88
103
  }
89
104
  }
105
+
90
106
  return false;
91
107
  }
92
108
 
@@ -98,6 +114,7 @@ function enter(e: KeyboardEvent, view: EditorView, { host, sendKey, submit }: Ke
98
114
  const tree = ensureSyntaxTree(state, state.doc.length, 50) ?? syntaxTree(state);
99
115
  const line = state.doc.lineAt(head);
100
116
  const list = from === to && !inCode(tree, state.doc.toString(), head) ? listItemAt(line.text) : null;
117
+
101
118
  const action = enterAction(
102
119
  { shift: e.shiftKey, mod: isModHeld(e, host.isApplePlatform()), alt: e.altKey },
103
120
  {
@@ -109,9 +126,11 @@ function enter(e: KeyboardEvent, view: EditorView, { host, sendKey, submit }: Ke
109
126
  startsNewChat: host.openNewChat !== null
110
127
  }
111
128
  );
129
+
112
130
  if (action === 'newline') {
113
131
  return insertNewline(view);
114
132
  }
133
+
115
134
  if (action === 'continue-list') {
116
135
  if (list) {
117
136
  view.dispatch({
@@ -121,57 +140,77 @@ function enter(e: KeyboardEvent, view: EditorView, { host, sendKey, submit }: Ke
121
140
  userEvent: 'input'
122
141
  });
123
142
  }
143
+
124
144
  return true;
125
145
  }
146
+
126
147
  if (action === 'leave-list') {
127
148
  view.dispatch({ changes: { from: line.from, to: line.to }, selection: { anchor: line.from }, userEvent: 'delete' });
149
+
128
150
  return true;
129
151
  }
152
+
130
153
  submit(SEND_HOW[action]);
154
+
131
155
  return true;
132
156
  }
133
157
 
134
158
  /* Runs before the editor's own keymap; true means the key is handled and its default prevented. */
135
159
  export function composerKeyHandler(input: KeyHandlerInput): (e: KeyboardEvent, view: EditorView) => boolean {
136
160
  const { chatId, host, text, history } = input;
161
+
137
162
  return (e, view) => {
138
163
  input.noteKey(e);
164
+
139
165
  // Escape leaves the node, unless it first has a recalled prompt to put back.
140
166
  if (e.key === 'Escape' && !e.isComposing) {
141
167
  if (!history.leave()) {
142
168
  return false;
143
169
  }
170
+
144
171
  e.stopPropagation();
172
+
145
173
  return true;
146
174
  }
175
+
147
176
  // The app's own shortcuts stay the app's while you type: Mod+Shift+Enter would otherwise send as well.
148
177
  if (host.isAppShortcut(e)) {
149
178
  return false;
150
179
  }
180
+
151
181
  e.stopPropagation();
182
+
152
183
  // A key inside an IME composition belongs to the composition, Enter and the arrows included.
153
184
  if (e.isComposing) {
154
185
  return false;
155
186
  }
187
+
156
188
  // Reading back through a long answer should not mean leaving the box you are typing in.
157
189
  if (e.key === 'PageUp' || e.key === 'PageDown') {
158
190
  return pageTimeline(chatId, e.key === 'PageUp' ? -1 : 1);
159
191
  }
192
+
160
193
  if (matchesShortcut(STASH_SHORTCUT, e, host.isApplePlatform())) {
161
194
  input.toggleStash();
195
+
162
196
  return true;
163
197
  }
198
+
164
199
  if (pickInMenu(e, input.menus)) {
165
200
  return true;
166
201
  }
202
+
167
203
  if (e.key === 'Tab' && !e.altKey && !e.metaKey && !e.ctrlKey && indentInFence(e, view)) {
168
204
  return true;
169
205
  }
206
+
170
207
  if (e.key === 'Enter') {
171
208
  return enter(e, view, input);
172
209
  }
210
+
173
211
  const { from, to } = view.state.selection.main;
174
212
  const direction = recallDirection({ key: e.key, text, from, to, recalled: history.recalled(text) });
213
+
175
214
  return direction !== null && history.recall(direction);
176
215
  };
177
216
  }
@@ -40,11 +40,14 @@ const LIST_ITEM = /^([ \t]*)(?:([-*+])|(\d{1,9})([.)]))([ \t]+)(\[[ xX]\](?:[ \t
40
40
  /* The list item a line starts, if it starts one. */
41
41
  export function listItemAt(line: string): ListItem | null {
42
42
  const match = LIST_ITEM.exec(line);
43
+
43
44
  if (match === null) {
44
45
  return null;
45
46
  }
47
+
46
48
  const [prefix, indent, bullet, number, delimiter, space, task] = match;
47
49
  const marker = bullet ?? `${Number(number) + 1}${delimiter}`;
50
+
48
51
  return { prefix, next: `${indent}${marker}${space}${task ? '[ ] ' : ''}`, empty: line.slice(prefix.length).trim() === '' };
49
52
  }
50
53
 
@@ -60,25 +63,32 @@ export function enterAction(
60
63
  { inOpenFence, list, column, sendKey = 'enter', multiline = false, startsNewChat = false }: EnterContext
61
64
  ): EnterAction {
62
65
  const modOnly = modSends(sendKey, multiline);
66
+
63
67
  if (keys.mod) {
64
68
  if (keys.alt && startsNewChat) {
65
69
  return 'send-and-new';
66
70
  }
71
+
67
72
  return keys.alt || (modOnly && keys.shift) ? 'send-alternate' : 'send';
68
73
  }
74
+
69
75
  if (keys.shift || inOpenFence) {
70
76
  return 'newline';
71
77
  }
78
+
72
79
  if (list !== null) {
73
80
  // A caret in front of the marker moves the item down rather than splitting its marker.
74
81
  if (column < list.prefix.length) {
75
82
  return 'newline';
76
83
  }
84
+
77
85
  return list.empty ? 'leave-list' : 'continue-list';
78
86
  }
87
+
79
88
  if (keys.alt) {
80
89
  return 'send-alternate';
81
90
  }
91
+
82
92
  return modOnly ? 'newline' : 'send';
83
93
  }
84
94
 
@@ -89,6 +99,7 @@ function enclosingFence(tree: Tree, pos: number): SyntaxNode | null {
89
99
  return node;
90
100
  }
91
101
  }
102
+
92
103
  return null;
93
104
  }
94
105
 
@@ -100,16 +111,20 @@ function isOpenFence(fence: SyntaxNode): boolean {
100
111
  /* Whether `pos` sits inside a fenced code block that has an opening fence and no closing one yet. */
101
112
  export function inOpenFence(tree: Tree, pos: number): boolean {
102
113
  const fence = enclosingFence(tree, pos);
114
+
103
115
  return fence !== null && pos > fence.from && isOpenFence(fence);
104
116
  }
105
117
 
106
118
  /* Whether `pos` sits in the body of a fenced code block, open or closed: below its opening line and before its end. */
107
119
  export function inFenceBody(tree: Tree, text: string, pos: number): boolean {
108
120
  const fence = enclosingFence(tree, pos);
121
+
109
122
  if (fence === null) {
110
123
  return false;
111
124
  }
125
+
112
126
  const openingEnd = text.indexOf('\n', fence.from);
127
+
113
128
  return openingEnd !== -1 && pos > openingEnd && (pos < fence.to || isOpenFence(fence));
114
129
  }
115
130
 
@@ -127,18 +142,23 @@ const INLINE_BLOCK = /^(Paragraph|ATXHeading\d|SetextHeading\d)$/;
127
142
  */
128
143
  export function inCode(tree: Tree, text: string, pos: number): boolean {
129
144
  let blockFrom: number | null = null;
145
+
130
146
  for (let node: SyntaxNode | null = tree.resolveInner(pos, -1); node !== null; node = node.parent) {
131
147
  if (node.name === 'FencedCode') {
132
148
  return pos > node.from && (pos < node.to || isOpenFence(node));
133
149
  }
150
+
134
151
  if (node.name === 'InlineCode' && pos > node.from && pos < node.to) {
135
152
  return true;
136
153
  }
154
+
137
155
  if (blockFrom === null && INLINE_BLOCK.test(node.name)) {
138
156
  blockFrom = node.from;
139
157
  }
140
158
  }
159
+
141
160
  let from = blockFrom ?? text.lastIndexOf('\n', pos - 1) + 1;
161
+
142
162
  tree.iterate({
143
163
  from,
144
164
  to: pos,
@@ -148,6 +168,7 @@ export function inCode(tree: Tree, text: string, pos: number): boolean {
148
168
  }
149
169
  }
150
170
  });
171
+
151
172
  return /(^|[^\\])`/.test(text.slice(from, pos));
152
173
  }
153
174
 
@@ -169,8 +190,10 @@ export function recallDirection({ key, text, from, to, recalled }: RecallContext
169
190
  if (key === 'ArrowUp' && (text === '' || (recalled && from === 0))) {
170
191
  return -1;
171
192
  }
193
+
172
194
  if (key === 'ArrowDown' && recalled && to === text.length) {
173
195
  return 1;
174
196
  }
197
+
175
198
  return null;
176
199
  }
@@ -10,12 +10,15 @@ function hintedPlaceholder(lead: string, joiner: string, hints: ReadonlyArray<{
10
10
  const extra = document.createElement('span');
11
11
  extra.className = 'composer-hints-extra';
12
12
  extra.append(joiner);
13
+
13
14
  for (const hint of hints) {
14
15
  const cap = document.createElement('kbd');
15
16
  cap.textContent = hint.key;
16
17
  extra.append(cap, hint.label);
17
18
  }
19
+
18
20
  root.append(lead, extra);
21
+
19
22
  return root;
20
23
  }
21
24
 
@@ -32,13 +35,16 @@ export function useHintedPlaceholder({ chatId, disabled, readOnly, mentionable,
32
35
  const { t } = useTranslation('agent-chat');
33
36
  const scope = useChatScope();
34
37
  const lead = useHostComposerPlaceholder(scope.id, chatId);
38
+
35
39
  return useMemo(() => {
36
40
  if (disabled) {
37
41
  return t('composer.placeholder.disconnected');
38
42
  }
43
+
39
44
  if (readOnly) {
40
45
  return t('composer.placeholder.readOnly');
41
46
  }
47
+
42
48
  return hintedPlaceholder(lead ?? t('composer.placeholder.lead'), t('composer.placeholder.joiner'), [
43
49
  { key: '/', label: t('composer.placeholder.commands') },
44
50
  ...(mentionable ? [{ key: '@', label: t('composer.placeholder.files') }] : []),
@@ -7,6 +7,7 @@ import type { PromptDraft } from './use-prompt-menus';
7
7
  export function promptPicks(value: PromptDraft, referables: Referables): Pick<ChatSendExtras, 'mentions' | 'skills' | 'chats' | 'references'> {
8
8
  const text = value.text.trim();
9
9
  const tokens = referenceTokens([...value.chats, ...value.references], referables.items, value.referenceNames);
10
+
10
11
  return {
11
12
  mentions: presentMentions(text, value.mentions),
12
13
  skills: presentSkills(text, value.skills),
@@ -33,13 +33,18 @@ const ACTIONS = [
33
33
  export function surfacePartOf(target: EventTarget | null, surface: Element): 'input' | 'action' | 'surface' {
34
34
  // Not `instanceof Element`, which fails for an element of another window.
35
35
  const element = target as Element | null;
36
+
36
37
  if (typeof element?.closest !== 'function' || !surface.contains(element)) {
37
38
  return 'action';
38
39
  }
40
+
39
41
  const input = element.closest('.cm-editor');
42
+
40
43
  if (input !== null && surface.contains(input)) {
41
44
  return 'input';
42
45
  }
46
+
43
47
  const action = element.closest(ACTIONS);
48
+
44
49
  return action !== null && surface.contains(action) ? 'action' : 'surface';
45
50
  }
@@ -32,8 +32,10 @@ export function useChatControls({ chatId, info, notice: { setNotice, showError }
32
32
  if (configuring.current) {
33
33
  return;
34
34
  }
35
+
35
36
  configuring.current = true;
36
37
  setConfigurationPending(true);
38
+
37
39
  void work()
38
40
  .catch(showError)
39
41
  .finally(() => {
@@ -49,6 +51,7 @@ export function useChatControls({ chatId, info, notice: { setNotice, showError }
49
51
  if (patch.selection) {
50
52
  rememberChatSelection(info.provider, patch.selection);
51
53
  }
54
+
52
55
  if (patch.account) {
53
56
  rememberChatAccount(scope.id, info.provider, patch.account);
54
57
  }
@@ -59,18 +62,23 @@ export function useChatControls({ chatId, info, notice: { setNotice, showError }
59
62
  /* A model of another CLI re-points the whole chat; one of this CLI's own is a configure. */
60
63
  const chooseModel = (provider: AgentKind, slug: string): void => {
61
64
  const selection: ModelSelection = { model: slug, options: {} };
65
+
62
66
  if (provider !== info.provider) {
63
67
  runConfiguration(() => onRetarget(provider, selection).then(() => rememberChatSelection(provider, selection)));
68
+
64
69
  return;
65
70
  }
71
+
66
72
  configure({ selection });
67
73
  };
68
74
 
69
75
  const stop = (shiftKey: boolean): void => {
70
76
  if (composerStopOf(shiftKey) === 'turn') {
71
77
  void actions.stopTurn(chatId, false).catch(showError);
78
+
72
79
  return;
73
80
  }
81
+
74
82
  host.confirm.stopSubagents(scope.id, chatId, () => void actions.stopTurn(chatId, true).catch(showError));
75
83
  };
76
84
 
@@ -81,8 +89,10 @@ export function useChatControls({ chatId, info, notice: { setNotice, showError }
81
89
  } catch (e) {
82
90
  if (errorCode(e) === 'chat-busy') {
83
91
  setConfirmClear(true);
92
+
84
93
  return;
85
94
  }
95
+
86
96
  setNotice(t('composer.notice.clearFailed'));
87
97
  }
88
98
  };
@@ -101,12 +111,15 @@ export function useChatControls({ chatId, info, notice: { setNotice, showError }
101
111
  switch (name) {
102
112
  case 'model':
103
113
  setModelPickerOpen(true);
114
+
104
115
  return true;
105
116
  case 'compact':
106
117
  void actions.compact(chatId).catch(() => undefined);
118
+
107
119
  return true;
108
120
  case 'clear':
109
121
  void clearThread();
122
+
110
123
  return true;
111
124
  default:
112
125
  return false;
@@ -12,8 +12,10 @@ const PAGE_CLOCK: DueClock = {
12
12
  if (typeof window === 'undefined') {
13
13
  return () => {};
14
14
  }
15
+
15
16
  window.addEventListener('focus', wake);
16
17
  document.addEventListener('visibilitychange', wake);
18
+
17
19
  return () => {
18
20
  window.removeEventListener('focus', wake);
19
21
  document.removeEventListener('visibilitychange', wake);
@@ -38,6 +40,7 @@ export function useCompactionOffer(chatId: string, info: ChatInfo, compaction: P
38
40
  if (dueAt === null) {
39
41
  return;
40
42
  }
43
+
41
44
  return watchDueAt(dueAt, setNow, PAGE_CLOCK);
42
45
  }, [dueAt]);
43
46
 
@@ -34,6 +34,7 @@ export function useComposerMenus({ chatId, info, capabilities, providerName, dra
34
34
  */
35
35
  useEffect(() => {
36
36
  let stale = false;
37
+
37
38
  scope.chats
38
39
  .listSkills(chatId)
39
40
  .then((found) => {
@@ -42,6 +43,7 @@ export function useComposerMenus({ chatId, info, capabilities, providerName, dra
42
43
  }
43
44
  })
44
45
  .catch(() => undefined);
46
+
45
47
  return () => {
46
48
  stale = true;
47
49
  };
@@ -62,9 +62,11 @@ export function useComposerSubmit({
62
62
  /* Opens the host's new chat once the host took the message; a host that answers nothing took it. */
63
63
  const afterSend = (sent: void | Promise<boolean>, andNew: boolean): void => {
64
64
  const openNewChat = host.openNewChat;
65
+
65
66
  if (!andNew || openNewChat === null) {
66
67
  return;
67
68
  }
69
+
68
70
  void Promise.resolve(sent)
69
71
  .then((taken) => {
70
72
  if (taken !== false) {
@@ -79,39 +81,53 @@ export function useComposerSubmit({
79
81
  const delivery = steerable && (alternate ? sendDelivery === 'queue' : sendDelivery === 'steer') ? 'steer' : 'queue';
80
82
  const { text, quote, quoteSource } = draft;
81
83
  const trimmed = text.trim();
84
+
82
85
  if (isEmptyDraft(draft) || !writable || guard.tooLong) {
83
86
  return;
84
87
  }
88
+
85
89
  // Recheck restored drafts and files that finished reading concurrently.
86
90
  const rejected = checkAttachmentLimits(
87
91
  0,
88
92
  draft.attachments.map((attachment) => ({ ...attachment, bytes: uploadBytes(attachment) }))
89
93
  ).rejected[0];
94
+
90
95
  if (rejected) {
91
96
  setNotice(`${rejected.name}: ${rejected.reason}`);
97
+
92
98
  return;
93
99
  }
100
+
94
101
  hold.release('send');
95
102
  const { commandQuery } = menus;
96
103
  const chosen = menus.commands[menus.activeIndex];
104
+
97
105
  if (commandQuery !== null && chosen?.local && controls.runCommand(chosen.name)) {
98
106
  box.clear();
107
+
99
108
  return;
100
109
  }
110
+
101
111
  if (commandQuery !== null && chosen?.skill) {
102
112
  menus.chooseSkill(chosen.name);
113
+
103
114
  return;
104
115
  }
116
+
105
117
  // Whoever sends wants to see what comes back, wherever they were reading.
106
118
  scrollTimelineToEnd(chatId, 'instant');
119
+
107
120
  if (commandQuery !== null && chosen && !chosen.local) {
108
121
  afterSend(onSend(`/${chosen.name}`, {}), how === 'and-new');
109
122
  box.clear();
123
+
110
124
  return;
111
125
  }
126
+
112
127
  const chats = presentReferences(trimmed, draft.chats, menus.referenceChips);
113
128
  // A machine without `viewReferences` drops the field, so only the chats it does know reach it.
114
129
  const references = menus.referables.chatsOnly ? [] : presentReferences(trimmed, draft.references, menus.referenceChips);
130
+
115
131
  const sent = onSend(withQuote(quote, trimmed), {
116
132
  delivery,
117
133
  mentions: presentMentions(trimmed, draft.mentions),
@@ -121,6 +137,7 @@ export function useComposerSubmit({
121
137
  attachments: draft.attachments,
122
138
  references: references.length > 0 ? references : undefined
123
139
  });
140
+
124
141
  afterSend(sent, how === 'and-new');
125
142
  box.clear();
126
143
  menus.close();
@@ -129,9 +146,11 @@ export function useComposerSubmit({
129
146
 
130
147
  // A menu row means the draft as it stands when it is picked, so the registration reads the latest submit.
131
148
  const submitRef = useRef(submit);
149
+
132
150
  useEffect(() => {
133
151
  submitRef.current = submit;
134
152
  });
153
+
135
154
  useEffect(() => registerComposerSender(scope.keyOf(chatId), (how) => submitRef.current(how)), [scope, chatId]);
136
155
 
137
156
  /*
@@ -142,11 +161,13 @@ export function useComposerSubmit({
142
161
  const restoreStashed = (prompt: StashedPrompt): void => {
143
162
  const stashedQuote = prompt.quote;
144
163
  const here = stashedQuote !== undefined && stashedQuote.chatId === chatId;
164
+
145
165
  const { chats, dropped } = restorableChats(
146
166
  prompt.chats,
147
167
  new Set(menus.referables.items.filter((item) => item.kind === 'chat').map((item) => item.id)),
148
168
  chatId
149
169
  );
170
+
150
171
  setDraft((current) => ({
151
172
  ...current,
152
173
  text: stashedQuote === undefined || here ? prompt.text : withQuote(stashedQuote.text, prompt.text),
@@ -156,9 +177,11 @@ export function useComposerSubmit({
156
177
  references: [...prompt.references],
157
178
  ...(here ? { quote: stashedQuote.text, quoteSource: stashedQuote.itemId } : {})
158
179
  }));
180
+
159
181
  if (dropped > 0) {
160
182
  setNotice(t('composer.notice.stashChatsDropped', { count: dropped }), 'info');
161
183
  }
184
+
162
185
  history.forget();
163
186
  inputRef.current?.focus();
164
187
  };
@@ -168,6 +191,7 @@ export function useComposerSubmit({
168
191
  if (!writable) {
169
192
  return;
170
193
  }
194
+
171
195
  scrollTimelineToEnd(chatId, 'instant');
172
196
  afterSend(onSend(CONTINUE_PROMPT, {}), false);
173
197
  };
@@ -176,11 +200,14 @@ export function useComposerSubmit({
176
200
  const toggleStash = (): void => {
177
201
  if (isEmptyDraft(draft)) {
178
202
  const newest = useStash.getState().prompts[0];
203
+
179
204
  if (newest) {
180
205
  restoreStashed(newest);
181
206
  }
207
+
182
208
  return;
183
209
  }
210
+
184
211
  if (stashDraft(draft, chatId)) {
185
212
  box.clear();
186
213
  menus.close();
@@ -1,4 +1,3 @@
1
- import { useMemo } from 'react';
2
1
  import type { ChatInfo, ChatItem } from '@adecore/agent-contracts';
3
2
  import { useChatRow } from '../../../state/chats';
4
3
  import { continuableTurn } from '../../logic/continue-after-stop';
@@ -7,8 +6,7 @@ import { continuableTurn } from '../../logic/continue-after-stop';
7
6
  export function useContinuableTurn(chatId: string, info: ChatInfo): string | null {
8
7
  const order = useChatRow(chatId, (row) => row?.order);
9
8
  const structure = useChatRow(chatId, (row) => row?.structure);
10
- return useMemo(() => {
11
- const items = (order ?? []).map((id) => structure?.[id]).filter((item): item is ChatItem => item !== undefined);
12
- return continuableTurn(info, items);
13
- }, [info, order, structure]);
9
+ const items = (order ?? []).map((id) => structure?.[id]).filter((item): item is ChatItem => item !== undefined);
10
+
11
+ return continuableTurn(info, items);
14
12
  }