@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
@@ -40,6 +40,7 @@ function ReadyDiff({
40
40
  const theme = useHostCodeThemes()[mode];
41
41
  const language = getFiletypeFromFileName(path);
42
42
  const tokens = useDiffSourceTokens(document, language, theme);
43
+
43
44
  return (
44
45
  <DiffView
45
46
  engine={engine}
@@ -79,38 +80,48 @@ export function EditorDocumentDiff({
79
80
  const { t } = useTranslation('agent-chat');
80
81
  const withinLimits = canRenderEditorDiff(document);
81
82
  const [loaded, setLoaded] = useState<{ load: EngineLoader; engine: EditorEngine | null }>();
83
+
82
84
  useEffect(() => {
83
85
  if (!withinLimits || engines.has(loadEngine)) {
84
86
  return;
85
87
  }
88
+
86
89
  let active = true;
90
+
87
91
  void Promise.resolve()
88
92
  .then(loadEngine)
89
93
  .then(
90
94
  (engine) => {
91
95
  engines.set(loadEngine, engine);
96
+
92
97
  if (active) {
93
98
  setLoaded({ load: loadEngine, engine });
94
99
  }
95
100
  },
96
101
  () => {
97
102
  engines.set(loadEngine, null);
103
+
98
104
  if (active) {
99
105
  setLoaded({ load: loadEngine, engine: null });
100
106
  }
101
107
  }
102
108
  );
109
+
103
110
  return () => {
104
111
  active = false;
105
112
  };
106
113
  }, [loadEngine, withinLimits]);
114
+
107
115
  const engine = loaded?.load === loadEngine ? loaded.engine : engines.get(loadEngine);
116
+
108
117
  if (!withinLimits) {
109
118
  return <DiffOmitted path={path} />;
110
119
  }
120
+
111
121
  if (engine === undefined) {
112
122
  return <div className="px-3 py-2 text-xs text-text-faint">{t('work.loadingDiff')}</div>;
113
123
  }
124
+
114
125
  return engine === null ? (
115
126
  fallback
116
127
  ) : (
@@ -135,8 +146,10 @@ export default function EditorEditDiff({
135
146
  sourceChecked || diffSourceBudget(change.before, change.after)
136
147
  ? diffDocument(patchOf(change.before, change.after, 3, { maxWork: EDITOR_DIFF_WORK }), { old: change.before, new: change.after })
137
148
  : undefined;
149
+
138
150
  if (document === undefined) {
139
151
  return <DiffOmitted path={change.path} />;
140
152
  }
153
+
141
154
  return <EditorDocumentDiff document={document} path={change.path} loadEngine={loadEngine} fallback={fallback} maxHeight={maxHeight} />;
142
155
  }
@@ -32,19 +32,23 @@ export default function EditorPatchDiff({
32
32
  }) {
33
33
  const oldText = contents?.old;
34
34
  const newText = contents?.new;
35
+
35
36
  const document = useMemo(() => {
36
37
  if (!sourceChecked && (!diffSourceBudget(oldText ?? '', newText ?? '') || !patchSourceBudget(change.diff))) {
37
38
  return undefined;
38
39
  }
40
+
39
41
  return diffDocument(
40
42
  parsePatch(change.diff),
41
43
  oldText === undefined || newText === undefined ? undefined : { old: oldText, new: newText },
42
44
  ignoreWhitespace
43
45
  );
44
46
  }, [change.diff, oldText, newText, ignoreWhitespace, sourceChecked]);
47
+
45
48
  if (document === undefined) {
46
49
  return <DiffOmitted path={change.path} />;
47
50
  }
51
+
48
52
  return (
49
53
  <EditorDocumentDiff
50
54
  document={document}
@@ -7,8 +7,10 @@ export function LegacyDiffPool({ children }: { children: ReactNode }) {
7
7
  const host = useChatHost();
8
8
  const outer = useWorkerPool();
9
9
  const workerFactory = host.code.legacyDiffWorker;
10
+
10
11
  if (outer !== undefined || workerFactory === undefined) {
11
12
  return children;
12
13
  }
14
+
13
15
  return <DiffPool workerFactory={workerFactory}>{children}</DiffPool>;
14
16
  }
@@ -11,6 +11,7 @@ export default function LegacyEditDiff({ change, maxHeight }: { change: FileChan
11
11
  const theme = useDiffTheme();
12
12
  const oldFile = { name: change.path, contents: change.before };
13
13
  const newFile = { name: change.path, contents: change.after };
14
+
14
15
  const options = {
15
16
  diffStyle: 'unified' as const,
16
17
  theme,
@@ -19,6 +20,7 @@ export default function LegacyEditDiff({ change, maxHeight }: { change: FileChan
19
20
  overflow: 'wrap' as const,
20
21
  hunkSeparators: 'simple' as const
21
22
  };
23
+
22
24
  return (
23
25
  <div className="chat-diff select-text" style={maxHeight === undefined ? undefined : { maxHeight, overflow: 'auto' }}>
24
26
  <LegacyDiffPool>
@@ -16,11 +16,14 @@ function asPatch(change: ChatFileChange): string | null {
16
16
  if (!/^@@/m.test(change.diff)) {
17
17
  return null;
18
18
  }
19
+
19
20
  if (/^\+\+\+ /m.test(change.diff)) {
20
21
  return change.diff;
21
22
  }
23
+
22
24
  const from = change.kind === 'add' ? '/dev/null' : `a/${change.path}`;
23
25
  const to = change.kind === 'delete' ? '/dev/null' : `b/${change.path}`;
26
+
24
27
  return `--- ${from}\n+++ ${to}\n${change.diff}`;
25
28
  }
26
29
 
@@ -37,9 +40,11 @@ function lineClass(line: string): string {
37
40
  if (line.startsWith('+')) {
38
41
  return 'text-chat-added';
39
42
  }
43
+
40
44
  if (line.startsWith('-')) {
41
45
  return 'text-chat-removed';
42
46
  }
47
+
43
48
  return 'text-text-muted';
44
49
  }
45
50
 
@@ -52,6 +57,7 @@ export default function LegacyUnifiedDiff({ change, overflow = 'wrap', diffStyle
52
57
  const newText = contents?.new;
53
58
  const full = patch === null || oldText === undefined || newText === undefined ? null : fullFileDiff(patch, change.path, { old: oldText, new: newText });
54
59
  const unfolds = full !== null;
60
+
55
61
  const options = {
56
62
  theme,
57
63
  themeType: resolved,
@@ -61,6 +67,7 @@ export default function LegacyUnifiedDiff({ change, overflow = 'wrap', diffStyle
61
67
  hunkSeparators: unfolds ? ('line-info' as const) : ('simple' as const),
62
68
  unsafeCSS: fill ? FILL_CSS : undefined
63
69
  };
70
+
64
71
  if (patch === null) {
65
72
  return (
66
73
  <pre className="max-h-64 overflow-auto px-3 py-2 font-mono text-code select-text" style={fill ? undefined : { maxHeight }}>
@@ -75,6 +82,7 @@ export default function LegacyUnifiedDiff({ change, overflow = 'wrap', diffStyle
75
82
  </pre>
76
83
  );
77
84
  }
85
+
78
86
  return (
79
87
  <div
80
88
  className={clsx('chat-diff select-text', fill && 'flex grow flex-col')}
@@ -3,6 +3,7 @@ import { useReviewSettled } from './review-settle';
3
3
 
4
4
  export function RawDiff({ text, overflow, fill, maxHeight }: { text: string; overflow: 'wrap' | 'scroll'; fill: boolean; maxHeight?: number }) {
5
5
  useReviewSettled();
6
+
6
7
  return (
7
8
  <pre
8
9
  data-diff-raw
@@ -8,6 +8,7 @@ const UnifiedDiff = lazyNamed(() => import('../UnifiedDiff'), 'default');
8
8
 
9
9
  export function ReviewDiff({ entry, maxHeight = 480 }: { entry: ReviewEntry; maxHeight?: number }) {
10
10
  const { t } = useTranslation('agent-chat');
11
+
11
12
  return (
12
13
  <div data-review-entry={entry.key}>
13
14
  <LazyBoundary fallback={<p className="px-3 py-2 text-xs text-text-faint">{t('work.loadingDiff')}</p>}>
@@ -13,18 +13,22 @@ export function textFitsBudget(text: string, budget: TextBudget): boolean {
13
13
  if (text.length > budget.characters) {
14
14
  return false;
15
15
  }
16
+
16
17
  let rows = text.length === 0 ? 0 : 1;
17
18
  let length = 0;
19
+
18
20
  for (let index = 0; index < text.length; index++) {
19
21
  if (text.charCodeAt(index) === 10) {
20
22
  if (index < text.length - 1 && ++rows > budget.rows) {
21
23
  return false;
22
24
  }
25
+
23
26
  length = 0;
24
27
  } else if (++length > budget.lineLength) {
25
28
  return false;
26
29
  }
27
30
  }
31
+
28
32
  return rows <= budget.rows;
29
33
  }
30
34
 
@@ -41,16 +45,20 @@ export function legacyDiffBudget(before: string, after: string, patch = ''): boo
41
45
  if (before.length + after.length + patch.length > LEGACY_BUDGET.characters) {
42
46
  return false;
43
47
  }
48
+
44
49
  let rows = 2;
50
+
45
51
  for (const text of [before, after, patch]) {
46
52
  if (!textFitsBudget(text, LEGACY_BUDGET)) {
47
53
  return false;
48
54
  }
55
+
49
56
  for (let index = 0; index < text.length; index++) {
50
57
  if (text.charCodeAt(index) === 10 && ++rows > LEGACY_BUDGET.rows) {
51
58
  return false;
52
59
  }
53
60
  }
54
61
  }
62
+
55
63
  return true;
56
64
  }
@@ -7,15 +7,19 @@ export const EDITOR_DIFF_LIMITS = { rows: 2000, characters: 2000000, lineLength:
7
7
  export function canRenderEditorDiff(document: DiffDocument): boolean {
8
8
  let rows = 0;
9
9
  let characters = 0;
10
+
10
11
  for (const row of diffRows(document, 'unified')) {
11
12
  if (row.kind === 'gap') {
12
13
  continue;
13
14
  }
15
+
14
16
  const text = row.new === undefined ? document.sides.old[row.old!.index]! : document.sides.new[row.new.index]!;
15
17
  characters += text.length + (rows > 0 ? 1 : 0);
18
+
16
19
  if (++rows > EDITOR_DIFF_LIMITS.rows || characters > EDITOR_DIFF_LIMITS.characters || text.length > EDITOR_DIFF_LIMITS.lineLength) {
17
20
  return false;
18
21
  }
19
22
  }
23
+
20
24
  return true;
21
25
  }
@@ -17,6 +17,7 @@ export interface ReviewBudget {
17
17
  }
18
18
 
19
19
  export const REVIEW_BUDGET: ReviewBudget = { editors: 3, rows: 2000, characters: 2000000 };
20
+
20
21
  export type ReviewSlot = 'editor' | 'deferred';
21
22
 
22
23
  export function reviewEntries(patches: readonly ChatFileChange[], edits: readonly FileChange[]): readonly ReviewEntry[] {
@@ -29,20 +30,25 @@ export function reviewEntries(patches: readonly ChatFileChange[], edits: readonl
29
30
  export function reviewSize(entry: ReviewEntry, budget: ReviewBudget = REVIEW_BUDGET): { rows: number; characters: number } {
30
31
  const texts = entry.change.kind === 'edit' ? [entry.change.edit.before, entry.change.edit.after] : [entry.change.patch.diff];
31
32
  const characters = texts.reduce((sum, text) => sum + text.length, 0);
33
+
32
34
  if (characters > budget.characters) {
33
35
  return { rows: budget.rows + 1, characters };
34
36
  }
37
+
35
38
  let rows = 0;
39
+
36
40
  for (const text of texts) {
37
41
  if (text.length > 0) {
38
42
  rows++;
39
43
  }
44
+
40
45
  for (let index = text.indexOf('\n'); index >= 0; index = text.indexOf('\n', index + 1)) {
41
46
  if (++rows > budget.rows) {
42
47
  return { rows, characters };
43
48
  }
44
49
  }
45
50
  }
51
+
46
52
  return { rows, characters };
47
53
  }
48
54
 
@@ -53,8 +59,10 @@ export function planReview(entries: readonly ReviewEntry[], promoted: string | n
53
59
  let editors = 0;
54
60
  let rows = 0;
55
61
  let characters = 0;
62
+
56
63
  for (const entry of ordered) {
57
64
  const size = reviewSize(entry, budget);
65
+
58
66
  // The first review keeps its own omission and error handling, even when the group estimate exceeds the budget.
59
67
  if (editors === 0 || (editors < budget.editors && rows + size.rows <= budget.rows && characters + size.characters <= budget.characters)) {
60
68
  slots.set(entry.key, 'editor');
@@ -65,5 +73,6 @@ export function planReview(entries: readonly ReviewEntry[], promoted: string | n
65
73
  slots.set(entry.key, 'deferred');
66
74
  }
67
75
  }
76
+
68
77
  return slots;
69
78
  }
@@ -6,6 +6,7 @@ export function useReviewSettled(): void {
6
6
  const scope = useContext(ReviewSettleContext);
7
7
  const key = scope?.key;
8
8
  const settle = scope?.settle;
9
+
9
10
  useEffect(() => {
10
11
  if (key !== undefined) {
11
12
  settle?.(key);
@@ -45,10 +45,12 @@ const shared = new WeakMap<ChatHost['code'], DiffSyntaxRuntime>();
45
45
  /* The runtime of a host's `code`, made on first use and shared by every diff under that host. */
46
46
  export function diffSyntaxRuntime(code: ChatHost['code']): DiffSyntaxRuntime {
47
47
  let runtime = shared.get(code);
48
+
48
49
  if (runtime === undefined) {
49
50
  runtime = createDiffSyntaxRuntime({ workerFactory: code.syntaxWorker, themes: () => code.custom, highlighter: code.highlighter });
50
51
  shared.set(code, runtime);
51
52
  }
53
+
52
54
  return runtime;
53
55
  }
54
56
 
@@ -61,13 +63,17 @@ export function createDiffSyntaxRuntime(options: DiffSyntaxRuntimeOptions): Diff
61
63
  let stopped = false;
62
64
  // The names of the custom themes the current worker or fallback has, each sent once.
63
65
  const sentThemes = new Set<string>();
66
+
64
67
  const themes = (): readonly ThemeRegistration[] => (typeof options.themes === 'function' ? options.themes() : options.themes);
68
+
65
69
  const settle = (session: Session): void => {
66
70
  for (const pending of session.pending.values()) {
67
71
  pending.resolve(null);
68
72
  }
73
+
69
74
  session.pending.clear();
70
75
  };
76
+
71
77
  const stopWorker = (): void => {
72
78
  worker?.removeEventListener('message', onMessage);
73
79
  worker?.removeEventListener('error', onError);
@@ -75,57 +81,75 @@ export function createDiffSyntaxRuntime(options: DiffSyntaxRuntimeOptions): Diff
75
81
  worker?.terminate();
76
82
  worker = undefined;
77
83
  };
84
+
78
85
  const loadHighlighter = (): Promise<Highlighter> => {
79
86
  highlighter ??= import('shiki').then(({ createHighlighter }) => createHighlighter({ langs: [], themes: [] }));
87
+
80
88
  return highlighter;
81
89
  };
90
+
82
91
  const start = (): void => {
83
92
  const current = themes();
84
93
  sentThemes.clear();
94
+
85
95
  for (const theme of current) {
86
96
  if (theme.name !== undefined) {
87
97
  sentThemes.add(theme.name);
88
98
  }
89
99
  }
100
+
90
101
  dispatch({ type: 'start', themes: current, maxLines: 100000 });
91
102
  };
103
+
92
104
  /* Sends a custom theme the host added after the start, ahead of the first request drawn in it. */
93
105
  const sendTheme = (name: string): void => {
94
106
  if (sentThemes.has(name)) {
95
107
  return;
96
108
  }
109
+
97
110
  const theme = themes().find((candidate) => candidate.name === name);
111
+
98
112
  if (theme !== undefined) {
99
113
  sentThemes.add(name);
100
114
  dispatch({ type: 'themes', themes: [theme] });
101
115
  }
102
116
  };
117
+
103
118
  const startFallback = (): void => {
104
119
  stopWorker();
120
+
105
121
  if (fallback !== undefined || stopped) {
106
122
  return;
107
123
  }
124
+
108
125
  fallback = createSyntaxService({ highlighter: options.highlighter ?? loadHighlighter, send: receive });
109
126
  start();
127
+
110
128
  for (const [generation, session] of sessions) {
111
129
  session.sent = false;
130
+
112
131
  for (const index of session.pending.keys()) {
113
132
  request(generation, session, index);
114
133
  }
115
134
  }
116
135
  };
136
+
117
137
  const onError = (event: Event): void => {
118
138
  event.preventDefault();
119
139
  startFallback();
120
140
  };
141
+
121
142
  const onMessage = (event: MessageEvent<DiffSyntaxOutput>): void => {
122
143
  receive(event.data);
123
144
  };
145
+
124
146
  const initialize = (): void => {
125
147
  if (started || stopped) {
126
148
  return;
127
149
  }
150
+
128
151
  started = true;
152
+
129
153
  if (options.workerFactory !== undefined) {
130
154
  try {
131
155
  worker = options.workerFactory();
@@ -133,6 +157,7 @@ export function createDiffSyntaxRuntime(options: DiffSyntaxRuntimeOptions): Diff
133
157
  worker.addEventListener('error', onError);
134
158
  worker.addEventListener('messageerror', onError);
135
159
  start();
160
+
136
161
  return;
137
162
  } catch {
138
163
  startFallback();
@@ -141,10 +166,12 @@ export function createDiffSyntaxRuntime(options: DiffSyntaxRuntimeOptions): Diff
141
166
  startFallback();
142
167
  }
143
168
  };
169
+
144
170
  const dispatch = (message: DiffSyntaxInput): void => {
145
171
  if (stopped) {
146
172
  return;
147
173
  }
174
+
148
175
  if (worker !== undefined) {
149
176
  try {
150
177
  worker.postMessage(message);
@@ -159,18 +186,23 @@ export function createDiffSyntaxRuntime(options: DiffSyntaxRuntimeOptions): Diff
159
186
  });
160
187
  }
161
188
  };
189
+
162
190
  const request = (generation: number, session: Session, index: number, resend = false): void => {
163
191
  const document = !session.sent || resend ? session.definition : undefined;
192
+
164
193
  if (document !== undefined) {
165
194
  sendTheme(document.theme);
166
195
  }
196
+
167
197
  session.sent = true;
168
198
  dispatch({ type: 'request', generation, index, ...(document ? { document } : {}) });
169
199
  };
200
+
170
201
  const receive = (message: DiffSyntaxOutput): void => {
171
202
  if (stopped || message.type === 'ready') {
172
203
  return;
173
204
  }
205
+
174
206
  if (message.type === 'error') {
175
207
  if (worker !== undefined) {
176
208
  startFallback();
@@ -179,50 +211,66 @@ export function createDiffSyntaxRuntime(options: DiffSyntaxRuntimeOptions): Diff
179
211
  settle(session);
180
212
  }
181
213
  }
214
+
182
215
  return;
183
216
  }
217
+
184
218
  const session = sessions.get(message.generation);
185
219
  const pending = session?.pending.get(message.index);
220
+
186
221
  if (session === undefined || pending === undefined) {
187
222
  return;
188
223
  }
224
+
189
225
  if (message.type === 'failed' && message.reason === 'missing' && !pending.resent) {
190
226
  pending.resent = true;
191
227
  request(message.generation, session, message.index, true);
228
+
192
229
  return;
193
230
  }
231
+
194
232
  session.pending.delete(message.index);
195
233
  pending.resolve(message.type === 'tokens' ? message : null);
196
234
  };
235
+
197
236
  return {
198
237
  open(document) {
199
238
  const generation = ++nextGeneration;
200
239
  const session: Session = { definition: document, pending: new Map(), sent: false };
201
240
  sessions.set(generation, session);
241
+
202
242
  return {
203
243
  request(index) {
204
244
  if (stopped || !sessions.has(generation)) {
205
245
  return Promise.resolve(null);
206
246
  }
247
+
207
248
  const existing = session.pending.get(index);
249
+
208
250
  if (existing !== undefined) {
209
251
  return existing.promise;
210
252
  }
253
+
211
254
  let resolve!: Pending['resolve'];
255
+
212
256
  const promise = new Promise<DiffSyntaxTokens | null>((done) => {
213
257
  resolve = done;
214
258
  });
259
+
215
260
  session.pending.set(index, { promise, resolve, resent: false });
216
261
  const initialized = started;
217
262
  initialize();
263
+
218
264
  // Fallback initialization replays pending requests, including this first one.
219
265
  if (initialized || worker !== undefined) {
220
266
  request(generation, session, index);
221
267
  }
268
+
222
269
  return promise;
223
270
  },
224
271
  cancel() {
225
272
  settle(session);
273
+
226
274
  if (started) {
227
275
  dispatch({ type: 'cancel', generation });
228
276
  }
@@ -230,6 +278,7 @@ export function createDiffSyntaxRuntime(options: DiffSyntaxRuntimeOptions): Diff
230
278
  dispose() {
231
279
  settle(session);
232
280
  sessions.delete(generation);
281
+
233
282
  if (started) {
234
283
  dispatch({ type: 'dispose', generation });
235
284
  }
@@ -238,12 +287,15 @@ export function createDiffSyntaxRuntime(options: DiffSyntaxRuntimeOptions): Diff
238
287
  },
239
288
  dispose() {
240
289
  stopped = true;
290
+
241
291
  for (const session of sessions.values()) {
242
292
  settle(session);
243
293
  }
294
+
244
295
  sessions.clear();
245
296
  stopWorker();
246
297
  fallback?.dispose();
298
+
247
299
  if (options.highlighter === undefined) {
248
300
  void highlighter?.then(
249
301
  (loaded) => loaded.dispose(),
@@ -15,24 +15,33 @@ export function useReviewStack(entries: readonly ReviewEntry[]): ReviewStack {
15
15
  const [admitted, setAdmitted] = useState<string[]>(() => selected.slice(0, 1));
16
16
  const retained = admitted.filter((key) => selected.includes(key));
17
17
  const live = retained.length === 0 ? selected.slice(0, 1) : retained;
18
+
18
19
  if (live.length !== admitted.length || live.some((key, index) => key !== admitted[index])) {
19
20
  setAdmitted(live);
20
21
  }
22
+
21
23
  const [settled, setSettled] = useState<ReadonlySet<string>>(() => new Set());
24
+
22
25
  if ([...settled].some((key) => !selected.includes(key))) {
23
26
  setSettled(new Set([...settled].filter((key) => selected.includes(key))));
24
27
  }
28
+
25
29
  const settle = (key: string) => {
26
30
  setSettled((previous) => (previous.has(key) ? previous : new Set([...previous, key])));
27
31
  };
32
+
28
33
  const allSettled = live.every((key) => settled.has(key));
29
34
  const next = selected.find((key) => !live.includes(key));
35
+
30
36
  useEffect(() => {
31
37
  if (next === undefined || !allSettled) {
32
38
  return;
33
39
  }
40
+
34
41
  const frame = window.requestAnimationFrame(() => setAdmitted((previous) => [...previous, next]));
42
+
35
43
  return () => window.cancelAnimationFrame(frame);
36
44
  }, [next, allSettled]);
45
+
37
46
  return { slots, ready: new Set(live), promote: setPromoted, settle };
38
47
  }
@@ -24,31 +24,41 @@ export function diffSourceTokens(document: DiffDocument, mapping: DiffSyntaxMapp
24
24
  const pending = { old: new Set<number>(), new: new Set<number>() };
25
25
  const listeners = new Set<() => void>();
26
26
  let disposed = false;
27
+
27
28
  return {
28
29
  get(side, index) {
29
30
  const location = mapping.locate(side, index);
31
+
30
32
  if (disposed || location === undefined || document.sides[side][index] === undefined) {
31
33
  return null;
32
34
  }
35
+
33
36
  const { block, line } = location;
34
37
  const known = cache[side].get(block);
38
+
35
39
  if (known !== undefined) {
36
40
  return known[line] ?? null;
37
41
  }
42
+
38
43
  if (!pending[side].has(block)) {
39
44
  pending[side].add(block);
40
45
  const text = mapping.documents[side].blocks[block]!.split('\n');
46
+
41
47
  const publish = (lines: ReadonlyArray<readonly LineToken[] | null>): void => {
42
48
  if (disposed) {
43
49
  return;
44
50
  }
51
+
45
52
  pending[side].delete(block);
46
53
  cache[side].set(block, lines);
54
+
47
55
  for (const listener of [...listeners]) {
48
56
  listener();
49
57
  }
50
58
  };
59
+
51
60
  const count = text.length;
61
+
52
62
  void sides[side].request(block).then(
53
63
  (packet) => {
54
64
  if (!disposed) {
@@ -58,12 +68,14 @@ export function diffSourceTokens(document: DiffDocument, mapping: DiffSyntaxMapp
58
68
  () => publish(text.map(() => null))
59
69
  );
60
70
  }
71
+
61
72
  return undefined;
62
73
  },
63
74
  subscribe(listener) {
64
75
  if (!disposed) {
65
76
  listeners.add(listener);
66
77
  }
78
+
67
79
  return () => {
68
80
  listeners.delete(listener);
69
81
  };
@@ -14,6 +14,7 @@ function registerThemes(custom: ChatHost['code']['custom']): void {
14
14
  if (registered.has(theme.name)) {
15
15
  continue;
16
16
  }
17
+
17
18
  registered.add(theme.name);
18
19
  registerCustomTheme(theme.name, () => Promise.resolve(theme));
19
20
  }
@@ -24,5 +25,6 @@ export function useDiffTheme(): ThemesType {
24
25
  const { code } = useChatHost();
25
26
  registerThemes(code.custom);
26
27
  const { light, dark } = useHostCodeThemes();
28
+
27
29
  return { light, dark };
28
30
  }
@@ -22,11 +22,13 @@ export function useFileLinkTarget(text: string): FileRef | null {
22
22
  const cwd = useFileLinkCwd();
23
23
  const scopeId = useFileLinkScopeId();
24
24
  const { fileLinks } = useChatHost();
25
+
25
26
  return fileLinks?.target(text, cwd, scopeId) ?? null;
26
27
  }
27
28
 
28
29
  /* Follows a reference the way the app opens a file or a folder. */
29
30
  export function useOpenFileLink(): (cwd: string | null, ref: FileRef, scopeId?: string | null) => void {
30
31
  const { fileLinks } = useChatHost();
32
+
31
33
  return (cwd, ref, scopeId = null) => fileLinks?.open(cwd, ref, scopeId);
32
34
  }
@@ -15,6 +15,7 @@ export const FindRevealContext = createContext<FindReveal | null>(null);
15
15
  export function useOpenForFind(itemId: string, field: ChatFindField, setOpen: (open: boolean) => void): void {
16
16
  const reveal = useContext(FindRevealContext);
17
17
  const wanted = reveal !== null && reveal.itemId === itemId && reveal.field === field;
18
+
18
19
  useEffect(() => {
19
20
  if (wanted) {
20
21
  setOpen(true);