@adecore/agents-react 0.31.12 → 0.33.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 (409) hide show
  1. package/README.md +1 -1
  2. package/dist/agents/AccountDot.js +57 -6
  3. package/dist/agents/AgentIcon.js +88 -12
  4. package/dist/agents/ModelOptionControl.js +153 -18
  5. package/dist/agents/ProviderLogo.js +49 -4
  6. package/dist/agents/account-limits.js +17 -14
  7. package/dist/agents/accounts.js +99 -67
  8. package/dist/agents/agent-marks.js +5 -3
  9. package/dist/agents/model-name.js +42 -31
  10. package/dist/agents/model-options.js +8 -8
  11. package/dist/agents/model-requirement.js +17 -14
  12. package/dist/agents/option-words.d.ts +7 -0
  13. package/dist/agents/option-words.js +35 -0
  14. package/dist/agents/provider-paths.js +6 -6
  15. package/dist/agents/status-look.js +32 -12
  16. package/dist/chat/account-choice.d.ts +13 -0
  17. package/dist/chat/account-choice.js +90 -20
  18. package/dist/chat/actions.js +126 -59
  19. package/dist/chat/activity.js +5 -5
  20. package/dist/chat/attachments.js +74 -53
  21. package/dist/chat/bookmarks.js +58 -48
  22. package/dist/chat/chat-client.js +344 -263
  23. package/dist/chat/chat-references.js +3 -3
  24. package/dist/chat/composer-effort.d.ts +5 -0
  25. package/dist/chat/composer-effort.js +26 -0
  26. package/dist/chat/composer-send.js +13 -13
  27. package/dist/chat/drafts.js +115 -79
  28. package/dist/chat/forks.js +81 -24
  29. package/dist/chat/guards.js +15 -43
  30. package/dist/chat/index.d.ts +3 -1
  31. package/dist/chat/index.js +3 -2
  32. package/dist/chat/logic/ansi.js +170 -167
  33. package/dist/chat/logic/answers.js +2 -2
  34. package/dist/chat/logic/bookmarks.js +19 -19
  35. package/dist/chat/logic/command-looks.js +138 -96
  36. package/dist/chat/logic/context-usage.js +13 -9
  37. package/dist/chat/logic/continue-after-stop.js +15 -19
  38. package/dist/chat/logic/effort.d.ts +14 -0
  39. package/dist/chat/logic/effort.js +34 -0
  40. package/dist/chat/logic/embedded-code.js +387 -326
  41. package/dist/chat/logic/fork.js +134 -110
  42. package/dist/chat/logic/generated-image.js +54 -23
  43. package/dist/chat/logic/goal.js +28 -20
  44. package/dist/chat/logic/handback.js +6 -6
  45. package/dist/chat/logic/json.js +2 -2
  46. package/dist/chat/logic/limit.js +46 -30
  47. package/dist/chat/logic/picker-entries.d.ts +5 -3
  48. package/dist/chat/logic/picker-entries.js +83 -43
  49. package/dist/chat/logic/resume-compaction.d.ts +14 -1
  50. package/dist/chat/logic/resume-compaction.js +93 -24
  51. package/dist/chat/logic/scrubber.js +188 -136
  52. package/dist/chat/logic/thought.js +13 -16
  53. package/dist/chat/logic/thread-cards.js +60 -48
  54. package/dist/chat/logic/timeline-copy.js +30 -38
  55. package/dist/chat/logic/timeline-target.js +92 -63
  56. package/dist/chat/logic/timeline.js +499 -355
  57. package/dist/chat/logic/tool-catalog.js +54 -34
  58. package/dist/chat/logic/tools.js +106 -82
  59. package/dist/chat/logic/visual-height.js +18 -18
  60. package/dist/chat/logic/visual-viewport.js +6 -3
  61. package/dist/chat/logic/welcome.js +9 -9
  62. package/dist/chat/logic/workflow.d.ts +10 -0
  63. package/dist/chat/logic/workflow.js +130 -32
  64. package/dist/chat/mentions.js +149 -109
  65. package/dist/chat/persisted-json.js +51 -54
  66. package/dist/chat/preferences.d.ts +4 -0
  67. package/dist/chat/preferences.js +162 -84
  68. package/dist/chat/prompt-hold.js +9 -9
  69. package/dist/chat/quote.js +21 -30
  70. package/dist/chat/reading-width.js +12 -10
  71. package/dist/chat/recent-messages.js +34 -21
  72. package/dist/chat/resume-compaction-dismissals.js +25 -5
  73. package/dist/chat/runtime-modes.js +3 -3
  74. package/dist/chat/send-key.js +11 -7
  75. package/dist/chat/stash.js +89 -69
  76. package/dist/chat/subagent-conversation.js +181 -139
  77. package/dist/chat/subagent-list.js +65 -67
  78. package/dist/chat/subagent-support.js +12 -7
  79. package/dist/chat/subagent-view.js +132 -44
  80. package/dist/chat/thumbnails.js +88 -60
  81. package/dist/chat/timeline-flash.js +24 -12
  82. package/dist/chat/timeline-scroll.js +116 -86
  83. package/dist/chat/ui/AccountPill.js +126 -15
  84. package/dist/chat/ui/AnsiOutput.js +62 -6
  85. package/dist/chat/ui/BookmarkMarker.js +231 -41
  86. package/dist/chat/ui/BookmarkSubmenu.js +165 -18
  87. package/dist/chat/ui/ChatActivity.js +649 -70
  88. package/dist/chat/ui/ChatReferenceChip.js +104 -9
  89. package/dist/chat/ui/CodeBlock.js +132 -58
  90. package/dist/chat/ui/Composer.js +866 -132
  91. package/dist/chat/ui/ComposerInput.js +309 -120
  92. package/dist/chat/ui/DiffPool.js +75 -14
  93. package/dist/chat/ui/EditDiff.js +160 -21
  94. package/dist/chat/ui/FadingWords.js +32 -3
  95. package/dist/chat/ui/GoalDock.js +235 -42
  96. package/dist/chat/ui/ImageView.js +617 -61
  97. package/dist/chat/ui/LimitState.js +446 -72
  98. package/dist/chat/ui/Markdown.js +991 -173
  99. package/dist/chat/ui/MermaidBlock.js +285 -79
  100. package/dist/chat/ui/MessageActions.js +251 -39
  101. package/dist/chat/ui/ModelMarks.js +55 -5
  102. package/dist/chat/ui/Pickers.js +664 -113
  103. package/dist/chat/ui/PromptComposer.js +281 -51
  104. package/dist/chat/ui/QuoteButton.js +151 -57
  105. package/dist/chat/ui/ResumeCompactionDock.d.ts +6 -2
  106. package/dist/chat/ui/ResumeCompactionDock.js +141 -6
  107. package/dist/chat/ui/RowTime.js +59 -5
  108. package/dist/chat/ui/RunSettings.js +1744 -154
  109. package/dist/chat/ui/Scrubber.js +517 -88
  110. package/dist/chat/ui/ShellCommand.js +183 -107
  111. package/dist/chat/ui/SubagentControls.js +212 -24
  112. package/dist/chat/ui/SubagentInfo.js +170 -13
  113. package/dist/chat/ui/SubagentStopButton.js +115 -24
  114. package/dist/chat/ui/SubagentTimeline.js +357 -91
  115. package/dist/chat/ui/Timeline.js +988 -476
  116. package/dist/chat/ui/TimelineMenu.js +552 -37
  117. package/dist/chat/ui/UnifiedDiff.js +234 -27
  118. package/dist/chat/ui/UploadThumb.js +37 -7
  119. package/dist/chat/ui/VisualDialogs.js +251 -20
  120. package/dist/chat/ui/VisualFrame.js +321 -95
  121. package/dist/chat/ui/Welcome.js +72 -7
  122. package/dist/chat/ui/chips.js +1 -1
  123. package/dist/chat/ui/code-lines.js +36 -36
  124. package/dist/chat/ui/code-streaming.js +1 -1
  125. package/dist/chat/ui/code-theme.js +7 -7
  126. package/dist/chat/ui/composer/ComposerControls.js +329 -21
  127. package/dist/chat/ui/composer/ComposerMenu.js +358 -30
  128. package/dist/chat/ui/composer/DraftParts.js +185 -11
  129. package/dist/chat/ui/composer/QueuedMessages.js +138 -13
  130. package/dist/chat/ui/composer/chips.js +29 -19
  131. package/dist/chat/ui/composer/editor.js +79 -63
  132. package/dist/chat/ui/composer/insert.js +26 -12
  133. package/dist/chat/ui/composer/key-handler.js +180 -127
  134. package/dist/chat/ui/composer/keys.js +90 -73
  135. package/dist/chat/ui/composer/menu-aria.js +18 -9
  136. package/dist/chat/ui/composer/placeholder.js +78 -31
  137. package/dist/chat/ui/composer/surface.js +13 -37
  138. package/dist/chat/ui/composer/use-chat-controls.js +230 -90
  139. package/dist/chat/ui/composer/use-compaction-offer.js +50 -18
  140. package/dist/chat/ui/composer/use-composer-draft.js +65 -34
  141. package/dist/chat/ui/composer/use-composer-menus.js +478 -193
  142. package/dist/chat/ui/composer/use-composer-submit.js +251 -109
  143. package/dist/chat/ui/composer/use-continuable-turn.js +33 -6
  144. package/dist/chat/ui/composer/use-draft-files.js +245 -93
  145. package/dist/chat/ui/composer/use-effort-stepper.d.ts +13 -0
  146. package/dist/chat/ui/composer/use-effort-stepper.js +88 -0
  147. package/dist/chat/ui/composer/use-notice.js +61 -16
  148. package/dist/chat/ui/composer/use-pending-requests.js +38 -17
  149. package/dist/chat/ui/composer/use-prompt-history.js +117 -45
  150. package/dist/chat/ui/composer/use-prompt-hold.js +119 -35
  151. package/dist/chat/ui/composer/use-take-back.js +94 -72
  152. package/dist/chat/ui/diff/ApprovalReview.js +101 -23
  153. package/dist/chat/ui/diff/DiffFallbackBoundary.js +21 -13
  154. package/dist/chat/ui/diff/DiffOmitted.js +56 -6
  155. package/dist/chat/ui/diff/DiffStack.js +92 -8
  156. package/dist/chat/ui/diff/DiffUnavailable.js +56 -6
  157. package/dist/chat/ui/diff/EditorEditDiff.js +263 -51
  158. package/dist/chat/ui/diff/EditorPatchDiff.js +39 -14
  159. package/dist/chat/ui/diff/LegacyDiffPool.js +27 -10
  160. package/dist/chat/ui/diff/LegacyEditDiff.js +97 -20
  161. package/dist/chat/ui/diff/LegacyUnifiedDiff.js +172 -42
  162. package/dist/chat/ui/diff/RawDiff.js +65 -5
  163. package/dist/chat/ui/diff/ReviewDiff.js +77 -5
  164. package/dist/chat/ui/diff/diff-props.js +1 -1
  165. package/dist/chat/ui/diff/diff-source-budget.js +46 -35
  166. package/dist/chat/ui/diff/editor-edit-model.js +18 -14
  167. package/dist/chat/ui/diff/review-plan.js +63 -40
  168. package/dist/chat/ui/diff/review-settle.js +24 -9
  169. package/dist/chat/ui/diff/syntax-runtime.js +236 -201
  170. package/dist/chat/ui/diff/use-review-stack.js +146 -25
  171. package/dist/chat/ui/diff-syntax.js +70 -58
  172. package/dist/chat/ui/diff-theme.js +31 -12
  173. package/dist/chat/ui/file-links.js +39 -10
  174. package/dist/chat/ui/find-reveal.js +29 -8
  175. package/dist/chat/ui/full-diff.js +17 -12
  176. package/dist/chat/ui/highlighter.js +143 -113
  177. package/dist/chat/ui/icons.js +16 -7
  178. package/dist/chat/ui/index.js +1 -1
  179. package/dist/chat/ui/intelligent-ui/UiBlockFrame.js +556 -82
  180. package/dist/chat/ui/intelligent-ui/UiFallbackPart.js +73 -5
  181. package/dist/chat/ui/intelligent-ui/UiReply.js +666 -262
  182. package/dist/chat/ui/intelligent-ui/block-local.js +59 -31
  183. package/dist/chat/ui/intelligent-ui/chart-data.js +77 -67
  184. package/dist/chat/ui/intelligent-ui/node-text.js +77 -91
  185. package/dist/chat/ui/intelligent-ui/registry.js +46 -46
  186. package/dist/chat/ui/intelligent-ui/render-context.js +1 -1
  187. package/dist/chat/ui/intelligent-ui/renderers/base-land.js +1 -1
  188. package/dist/chat/ui/intelligent-ui/renderers/binding.js +3 -3
  189. package/dist/chat/ui/intelligent-ui/renderers/chart.js +1223 -123
  190. package/dist/chat/ui/intelligent-ui/renderers/choices.js +312 -34
  191. package/dist/chat/ui/intelligent-ui/renderers/content.js +1241 -86
  192. package/dist/chat/ui/intelligent-ui/renderers/data.js +619 -54
  193. package/dist/chat/ui/intelligent-ui/renderers/inputs.js +868 -67
  194. package/dist/chat/ui/intelligent-ui/renderers/links.js +366 -62
  195. package/dist/chat/ui/intelligent-ui/renderers/map-slots.js +118 -60
  196. package/dist/chat/ui/intelligent-ui/renderers/map-style.js +85 -78
  197. package/dist/chat/ui/intelligent-ui/renderers/map-view.js +282 -217
  198. package/dist/chat/ui/intelligent-ui/renderers/map.js +621 -69
  199. package/dist/chat/ui/intelligent-ui/renderers/place-point.js +15 -8
  200. package/dist/chat/ui/intelligent-ui/renderers/structure.js +262 -20
  201. package/dist/chat/ui/intelligent-ui/renderers/text.js +674 -49
  202. package/dist/chat/ui/intelligent-ui/reply-parts.js +24 -15
  203. package/dist/chat/ui/intelligent-ui/table-data.js +97 -57
  204. package/dist/chat/ui/intelligent-ui/tones.js +31 -31
  205. package/dist/chat/ui/intelligent-ui/use-in-view.js +30 -12
  206. package/dist/chat/ui/intelligent-ui/use-later.js +28 -13
  207. package/dist/chat/ui/intelligent-ui/use-truncated.js +45 -21
  208. package/dist/chat/ui/intelligent-ui/use-ui-links.js +296 -109
  209. package/dist/chat/ui/intelligent-ui/use-ui-queries.js +256 -246
  210. package/dist/chat/ui/markdown-blocks.js +296 -262
  211. package/dist/chat/ui/mermaid.d.ts +8 -20
  212. package/dist/chat/ui/mermaid.js +47 -84
  213. package/dist/chat/ui/quote-selection.js +32 -29
  214. package/dist/chat/ui/rehype-chips.js +35 -23
  215. package/dist/chat/ui/rehype-fade.js +28 -17
  216. package/dist/chat/ui/remark-html-as-text.js +22 -15
  217. package/dist/chat/ui/reply-context.js +1 -1
  218. package/dist/chat/ui/reveal.js +161 -72
  219. package/dist/chat/ui/rows/ForksRow.js +226 -24
  220. package/dist/chat/ui/rows/GeneratedImageRow.js +541 -87
  221. package/dist/chat/ui/rows/MessageRows.js +1706 -159
  222. package/dist/chat/ui/rows/RowContainer.js +57 -7
  223. package/dist/chat/ui/rows/Rows.js +463 -54
  224. package/dist/chat/ui/rows/SubagentRow.js +577 -60
  225. package/dist/chat/ui/rows/TaskRow.js +359 -40
  226. package/dist/chat/ui/rows/VisualRow.js +233 -11
  227. package/dist/chat/ui/rows/WorkRows.d.ts +3 -0
  228. package/dist/chat/ui/rows/WorkRows.js +1945 -164
  229. package/dist/chat/ui/rows/WorkflowRow.js +756 -37
  230. package/dist/chat/ui/rows/row-rhythm.js +27 -17
  231. package/dist/chat/ui/theme-watch.js +26 -23
  232. package/dist/chat/ui/timestamp-format.js +4 -1
  233. package/dist/chat/ui/use-diff-source-tokens.js +70 -34
  234. package/dist/chat/ui/useToggleSet.js +51 -19
  235. package/dist/chat/ui/visual-theme.js +153 -128
  236. package/dist/chat/visual-bridge.js +123 -123
  237. package/dist/chat/visual-viewport.js +94 -83
  238. package/dist/chat/visuals.js +13 -5
  239. package/dist/host-hooks.d.ts +29 -0
  240. package/dist/host-hooks.js +78 -0
  241. package/dist/host.js +120 -48
  242. package/dist/index.js +1 -1
  243. package/dist/lazy.js +38 -32
  244. package/dist/locales/en/agent-chat.d.ts +42 -0
  245. package/dist/locales/en/agent-chat.js +755 -713
  246. package/dist/locales/en/agent-prompts.js +42 -42
  247. package/dist/locales/en/agent-providers.d.ts +1 -0
  248. package/dist/locales/en/agent-providers.js +166 -165
  249. package/dist/locales/en/agent-usage.d.ts +6 -0
  250. package/dist/locales/en/agent-usage.js +129 -123
  251. package/dist/locales/nl/agent-chat.d.ts +42 -0
  252. package/dist/locales/nl/agent-chat.js +755 -713
  253. package/dist/locales/nl/agent-prompts.js +42 -42
  254. package/dist/locales/nl/agent-providers.d.ts +1 -0
  255. package/dist/locales/nl/agent-providers.js +166 -165
  256. package/dist/locales/nl/agent-usage.d.ts +6 -0
  257. package/dist/locales/nl/agent-usage.js +129 -123
  258. package/dist/locales.js +14 -14
  259. package/dist/mounted-registry.js +31 -30
  260. package/dist/port-transport.js +122 -111
  261. package/dist/prompts/index.js +1 -1
  262. package/dist/prompts/logic/focus.js +15 -14
  263. package/dist/prompts/logic/keys.js +93 -59
  264. package/dist/prompts/logic/prompts.js +27 -14
  265. package/dist/prompts/logic/subjects.js +60 -46
  266. package/dist/prompts/logic/useFocusAfterAnswer.js +60 -20
  267. package/dist/prompts/logic/usePromptSession.js +88 -77
  268. package/dist/prompts/ui/ApprovalBody.js +246 -27
  269. package/dist/prompts/ui/PromptActions.js +215 -9
  270. package/dist/prompts/ui/PromptCard.js +215 -36
  271. package/dist/prompts/ui/PromptView.js +372 -34
  272. package/dist/prompts/ui/QuestionBody.js +269 -73
  273. package/dist/providers/AccountColors.js +36 -5
  274. package/dist/providers/AccountDetail.js +683 -50
  275. package/dist/providers/AccountVariables.js +139 -32
  276. package/dist/providers/AddAccountForm.js +125 -38
  277. package/dist/providers/CliDetail.js +343 -36
  278. package/dist/providers/InstallPage.js +644 -59
  279. package/dist/providers/ProviderList.d.ts +2 -1
  280. package/dist/providers/ProviderList.js +215 -23
  281. package/dist/providers/ProvidersPane.d.ts +4 -2
  282. package/dist/providers/ProvidersPane.js +244 -64
  283. package/dist/providers/account-actions.js +54 -30
  284. package/dist/providers/index.js +1 -1
  285. package/dist/providers/install-steps.js +12 -9
  286. package/dist/providers/pane-target.d.ts +3 -0
  287. package/dist/providers/pane-target.js +34 -13
  288. package/dist/providers/parts.js +62 -12
  289. package/dist/providers/provider-abilities.js +5 -5
  290. package/dist/providers/provider-groups.js +75 -40
  291. package/dist/providers/variables.js +73 -39
  292. package/dist/scope.js +6 -6
  293. package/dist/settings/sections.js +18 -18
  294. package/dist/state/chats.js +404 -192
  295. package/dist/state/index.js +1 -1
  296. package/dist/state/provider-accounts.js +70 -47
  297. package/dist/state/providers.js +45 -16
  298. package/dist/state/usage.js +154 -77
  299. package/dist/storage.js +69 -68
  300. package/dist/testing/index.js +1 -1
  301. package/dist/testing/prompts.js +105 -67
  302. package/dist/transport.js +10 -10
  303. package/dist/ui/PlainTextarea.js +27 -3
  304. package/dist/usage/LimitsList.js +646 -40
  305. package/dist/usage/UsageBreakdown.js +879 -38
  306. package/dist/usage/UsageChart.js +311 -32
  307. package/dist/usage/UsageDialog.js +46 -4
  308. package/dist/usage/UsageLimits.js +467 -34
  309. package/dist/usage/UsageLimitsCard.js +95 -11
  310. package/dist/usage/UsagePage.js +817 -96
  311. package/dist/usage/UsagePane.js +164 -10
  312. package/dist/usage/UsageSummary.js +151 -6
  313. package/dist/usage/UsageTiles.js +164 -14
  314. package/dist/usage/format.js +32 -32
  315. package/dist/usage/index.d.ts +1 -1
  316. package/dist/usage/index.js +2 -1
  317. package/dist/usage/limit-groups.d.ts +2 -0
  318. package/dist/usage/limit-groups.js +77 -59
  319. package/dist/usage/limits.js +78 -43
  320. package/dist/usage/money.js +20 -4
  321. package/dist/usage/summary.js +85 -64
  322. package/package.json +9 -9
  323. package/src/agents/ModelOptionControl.tsx +3 -1
  324. package/src/agents/option-words.ts +27 -0
  325. package/src/chat/account-choice.ts +41 -1
  326. package/src/chat/composer-effort.ts +32 -0
  327. package/src/chat/forks.ts +4 -5
  328. package/src/chat/index.ts +3 -1
  329. package/src/chat/logic/effort.ts +32 -0
  330. package/src/chat/logic/picker-entries.ts +45 -10
  331. package/src/chat/logic/resume-compaction.ts +95 -17
  332. package/src/chat/logic/timeline.ts +11 -3
  333. package/src/chat/logic/workflow.ts +86 -4
  334. package/src/chat/preferences.ts +35 -0
  335. package/src/chat/subagent-view.ts +2 -3
  336. package/src/chat/ui/AnsiOutput.tsx +1 -2
  337. package/src/chat/ui/BookmarkSubmenu.tsx +3 -3
  338. package/src/chat/ui/ChatActivity.tsx +14 -22
  339. package/src/chat/ui/ChatReferenceChip.tsx +2 -3
  340. package/src/chat/ui/Composer.tsx +15 -7
  341. package/src/chat/ui/EditDiff.tsx +2 -3
  342. package/src/chat/ui/LimitState.tsx +2 -2
  343. package/src/chat/ui/Markdown.tsx +10 -8
  344. package/src/chat/ui/MermaidBlock.tsx +57 -49
  345. package/src/chat/ui/Pickers.tsx +49 -29
  346. package/src/chat/ui/PromptComposer.tsx +7 -9
  347. package/src/chat/ui/ResumeCompactionDock.tsx +16 -4
  348. package/src/chat/ui/RunSettings.tsx +67 -28
  349. package/src/chat/ui/Scrubber.tsx +3 -3
  350. package/src/chat/ui/ShellCommand.tsx +2 -0
  351. package/src/chat/ui/SubagentInfo.tsx +2 -3
  352. package/src/chat/ui/SubagentTimeline.tsx +12 -16
  353. package/src/chat/ui/Timeline.tsx +6 -6
  354. package/src/chat/ui/UnifiedDiff.tsx +3 -4
  355. package/src/chat/ui/VisualFrame.tsx +5 -8
  356. package/src/chat/ui/code-theme.ts +4 -4
  357. package/src/chat/ui/composer/placeholder.ts +2 -2
  358. package/src/chat/ui/composer/use-compaction-offer.ts +36 -16
  359. package/src/chat/ui/composer/use-composer-menus.ts +4 -9
  360. package/src/chat/ui/composer/use-composer-submit.ts +2 -1
  361. package/src/chat/ui/composer/use-effort-stepper.ts +56 -0
  362. package/src/chat/ui/composer/use-notice.ts +2 -2
  363. package/src/chat/ui/diff/EditorEditDiff.tsx +9 -13
  364. package/src/chat/ui/diff/LegacyEditDiff.tsx +12 -17
  365. package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +13 -21
  366. package/src/chat/ui/diff/use-review-stack.ts +4 -4
  367. package/src/chat/ui/diff-theme.ts +3 -3
  368. package/src/chat/ui/file-links.ts +2 -2
  369. package/src/chat/ui/intelligent-ui/UiReply.tsx +2 -0
  370. package/src/chat/ui/intelligent-ui/renderers/chart.tsx +3 -3
  371. package/src/chat/ui/intelligent-ui/renderers/content.tsx +5 -6
  372. package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +2 -1
  373. package/src/chat/ui/intelligent-ui/renderers/map.tsx +2 -1
  374. package/src/chat/ui/intelligent-ui/use-truncated.ts +16 -19
  375. package/src/chat/ui/intelligent-ui/use-ui-links.ts +3 -3
  376. package/src/chat/ui/mermaid.ts +52 -96
  377. package/src/chat/ui/rows/ForksRow.tsx +3 -5
  378. package/src/chat/ui/rows/GeneratedImageRow.tsx +2 -1
  379. package/src/chat/ui/rows/MessageRows.tsx +12 -21
  380. package/src/chat/ui/rows/SubagentRow.tsx +3 -3
  381. package/src/chat/ui/rows/TaskRow.tsx +1 -1
  382. package/src/chat/ui/rows/WorkRows.tsx +11 -6
  383. package/src/chat/ui/rows/WorkflowRow.tsx +144 -54
  384. package/src/chat/ui/useToggleSet.ts +15 -18
  385. package/src/chat/ui/visual-theme.ts +2 -3
  386. package/src/host-hooks.ts +113 -0
  387. package/src/lazy.tsx +2 -0
  388. package/src/locales/en/agent-chat.ts +142 -108
  389. package/src/locales/en/agent-prompts.ts +7 -7
  390. package/src/locales/en/agent-providers.ts +35 -34
  391. package/src/locales/en/agent-usage.ts +26 -20
  392. package/src/locales/nl/agent-chat.ts +134 -100
  393. package/src/locales/nl/agent-prompts.ts +5 -5
  394. package/src/locales/nl/agent-providers.ts +33 -32
  395. package/src/locales/nl/agent-usage.ts +25 -20
  396. package/src/providers/CliDetail.tsx +2 -1
  397. package/src/providers/InstallPage.tsx +2 -4
  398. package/src/providers/ProviderList.tsx +12 -2
  399. package/src/providers/ProvidersPane.tsx +45 -15
  400. package/src/providers/pane-target.ts +20 -4
  401. package/src/state/chats.ts +2 -2
  402. package/src/usage/LimitsList.tsx +12 -13
  403. package/src/usage/UsageBreakdown.tsx +17 -6
  404. package/src/usage/UsageLimits.tsx +2 -1
  405. package/src/usage/UsagePage.tsx +3 -3
  406. package/src/usage/index.ts +1 -1
  407. package/src/usage/limit-groups.ts +12 -0
  408. package/src/usage/limits.ts +2 -2
  409. package/src/usage/money.ts +1 -2
@@ -1,16 +1,16 @@
1
1
  import { useReviewSettled } from './review-settle';
2
- import { useEffect, useMemo, useState, type ReactNode } from 'react';
2
+ import { useEffect, useState, type ReactNode } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { getFiletypeFromFileName } from '@pierre/diffs';
5
5
  import type { EditorEngine } from '@adecore/editor';
6
6
  import { DiffView } from '@adecore/editor-react';
7
7
  import { diffDocument, patchOf, type DiffDocument } from '@adecore/merge';
8
- import { useChatHost } from '../../../host';
9
8
  import type { FileChange } from '../../logic/tools';
10
9
  import { useDiffSourceTokens } from '../use-diff-source-tokens';
11
10
  import { diffSourceBudget } from './diff-source-budget';
12
11
  import { DiffOmitted } from './DiffOmitted';
13
12
  import { canRenderEditorDiff, EDITOR_DIFF_LIMITS, EDITOR_DIFF_WORK } from './editor-edit-model';
13
+ import { useHostCodeMode, useHostCodeThemes } from '../../../host-hooks';
14
14
 
15
15
  type EngineLoader = () => Promise<EditorEngine>;
16
16
 
@@ -34,11 +34,10 @@ function ReadyDiff({
34
34
  layout: 'unified' | 'split';
35
35
  maxHeight: number;
36
36
  }) {
37
- const host = useChatHost();
38
37
  useReviewSettled();
39
38
  const { t } = useTranslation('agent-chat');
40
- const mode = host.code.useMode();
41
- const theme = host.code.useThemes()[mode];
39
+ const mode = useHostCodeMode();
40
+ const theme = useHostCodeThemes()[mode];
42
41
  const language = getFiletypeFromFileName(path);
43
42
  const tokens = useDiffSourceTokens(document, language, theme);
44
43
  return (
@@ -78,7 +77,7 @@ export function EditorDocumentDiff({
78
77
  maxHeight?: number;
79
78
  }) {
80
79
  const { t } = useTranslation('agent-chat');
81
- const withinLimits = useMemo(() => canRenderEditorDiff(document), [document]);
80
+ const withinLimits = canRenderEditorDiff(document);
82
81
  const [loaded, setLoaded] = useState<{ load: EngineLoader; engine: EditorEngine | null }>();
83
82
  useEffect(() => {
84
83
  if (!withinLimits || engines.has(loadEngine)) {
@@ -132,13 +131,10 @@ export default function EditorEditDiff({
132
131
  fallback: ReactNode;
133
132
  maxHeight?: number;
134
133
  }) {
135
- const document = useMemo(
136
- () =>
137
- sourceChecked || diffSourceBudget(change.before, change.after)
138
- ? diffDocument(patchOf(change.before, change.after, 3, { maxWork: EDITOR_DIFF_WORK }), { old: change.before, new: change.after })
139
- : undefined,
140
- [change.before, change.after, sourceChecked]
141
- );
134
+ const document =
135
+ sourceChecked || diffSourceBudget(change.before, change.after)
136
+ ? diffDocument(patchOf(change.before, change.after, 3, { maxWork: EDITOR_DIFF_WORK }), { old: change.before, new: change.after })
137
+ : undefined;
142
138
  if (document === undefined) {
143
139
  return <DiffOmitted path={change.path} />;
144
140
  }
@@ -1,29 +1,24 @@
1
1
  import { LegacyDiffPool } from './LegacyDiffPool';
2
2
  import { useReviewSettled } from './review-settle';
3
- import { useMemo } from 'react';
4
3
  import { MultiFileDiff } from '@pierre/diffs/react';
5
- import { useChatHost } from '../../../host';
6
4
  import { useDiffTheme } from '../diff-theme';
7
5
  import type { FileChange } from '../../logic/tools';
6
+ import { useHostCodeMode } from '../../../host-hooks';
8
7
 
9
8
  export default function LegacyEditDiff({ change, maxHeight }: { change: FileChange; maxHeight?: number }) {
10
- const host = useChatHost();
11
9
  useReviewSettled();
12
- const resolved = host.code.useMode();
10
+ const resolved = useHostCodeMode();
13
11
  const theme = useDiffTheme();
14
- const oldFile = useMemo(() => ({ name: change.path, contents: change.before }), [change.path, change.before]);
15
- const newFile = useMemo(() => ({ name: change.path, contents: change.after }), [change.path, change.after]);
16
- const options = useMemo(
17
- () => ({
18
- diffStyle: 'unified' as const,
19
- theme,
20
- themeType: resolved,
21
- disableFileHeader: true,
22
- overflow: 'wrap' as const,
23
- hunkSeparators: 'simple' as const
24
- }),
25
- [theme, resolved]
26
- );
12
+ const oldFile = { name: change.path, contents: change.before };
13
+ const newFile = { name: change.path, contents: change.after };
14
+ const options = {
15
+ diffStyle: 'unified' as const,
16
+ theme,
17
+ themeType: resolved,
18
+ disableFileHeader: true,
19
+ overflow: 'wrap' as const,
20
+ hunkSeparators: 'simple' as const
21
+ };
27
22
  return (
28
23
  <div className="chat-diff select-text" style={maxHeight === undefined ? undefined : { maxHeight, overflow: 'auto' }}>
29
24
  <LegacyDiffPool>
@@ -1,13 +1,12 @@
1
1
  import { LegacyDiffPool } from './LegacyDiffPool';
2
2
  import { useReviewSettled } from './review-settle';
3
- import { useMemo } from 'react';
4
3
  import clsx from 'clsx';
5
4
  import { FileDiff, PatchDiff } from '@pierre/diffs/react';
6
5
  import type { ChatFileChange } from '@adecore/agent-contracts';
7
- import { useChatHost } from '../../../host';
8
6
  import { useDiffTheme } from '../diff-theme';
9
7
  import { fullFileDiff } from '../full-diff';
10
8
  import type { UnifiedDiffProps } from './diff-props';
9
+ import { useHostCodeMode } from '../../../host-hooks';
11
10
 
12
11
  /*
13
12
  * A patch the CLI reported itself. The renderer needs a file header, which a CLI that only sends hunks
@@ -45,30 +44,23 @@ function lineClass(line: string): string {
45
44
  }
46
45
 
47
46
  export default function LegacyUnifiedDiff({ change, overflow = 'wrap', diffStyle = 'unified', fill = false, contents, maxHeight }: UnifiedDiffProps) {
48
- const host = useChatHost();
49
47
  useReviewSettled();
50
- const resolved = host.code.useMode();
48
+ const resolved = useHostCodeMode();
51
49
  const theme = useDiffTheme();
52
- const patch = useMemo(() => asPatch(change), [change]);
50
+ const patch = asPatch(change);
53
51
  const oldText = contents?.old;
54
52
  const newText = contents?.new;
55
- const full = useMemo(
56
- () => (patch === null || oldText === undefined || newText === undefined ? null : fullFileDiff(patch, change.path, { old: oldText, new: newText })),
57
- [patch, change.path, oldText, newText]
58
- );
53
+ const full = patch === null || oldText === undefined || newText === undefined ? null : fullFileDiff(patch, change.path, { old: oldText, new: newText });
59
54
  const unfolds = full !== null;
60
- const options = useMemo(
61
- () => ({
62
- theme,
63
- themeType: resolved,
64
- disableFileHeader: true,
65
- diffStyle,
66
- overflow,
67
- hunkSeparators: unfolds ? ('line-info' as const) : ('simple' as const),
68
- unsafeCSS: fill ? FILL_CSS : undefined
69
- }),
70
- [diffStyle, overflow, theme, resolved, fill, unfolds]
71
- );
55
+ const options = {
56
+ theme,
57
+ themeType: resolved,
58
+ disableFileHeader: true,
59
+ diffStyle,
60
+ overflow,
61
+ hunkSeparators: unfolds ? ('line-info' as const) : ('simple' as const),
62
+ unsafeCSS: fill ? FILL_CSS : undefined
63
+ };
72
64
  if (patch === null) {
73
65
  return (
74
66
  <pre className="max-h-64 overflow-auto px-3 py-2 font-mono text-code select-text" style={fill ? undefined : { maxHeight }}>
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useMemo, useState } from 'react';
1
+ import { useEffect, useState } from 'react';
2
2
  import { planReview, type ReviewEntry, type ReviewSlot } from './review-plan';
3
3
 
4
4
  export interface ReviewStack {
@@ -10,7 +10,7 @@ export interface ReviewStack {
10
10
 
11
11
  export function useReviewStack(entries: readonly ReviewEntry[]): ReviewStack {
12
12
  const [promoted, setPromoted] = useState<string | null>(null);
13
- const slots = useMemo(() => planReview(entries, promoted), [entries, promoted]);
13
+ const slots = planReview(entries, promoted);
14
14
  const selected = [...slots].filter(([, slot]) => slot === 'editor').map(([key]) => key);
15
15
  const [admitted, setAdmitted] = useState<string[]>(() => selected.slice(0, 1));
16
16
  const retained = admitted.filter((key) => selected.includes(key));
@@ -22,9 +22,9 @@ export function useReviewStack(entries: readonly ReviewEntry[]): ReviewStack {
22
22
  if ([...settled].some((key) => !selected.includes(key))) {
23
23
  setSettled(new Set([...settled].filter((key) => selected.includes(key))));
24
24
  }
25
- const settle = useCallback((key: string) => {
25
+ const settle = (key: string) => {
26
26
  setSettled((previous) => (previous.has(key) ? previous : new Set([...previous, key])));
27
- }, []);
27
+ };
28
28
  const allSettled = live.every((key) => settled.has(key));
29
29
  const next = selected.find((key) => !live.includes(key));
30
30
  useEffect(() => {
@@ -1,6 +1,6 @@
1
- import { useMemo } from 'react';
2
1
  import { registerCustomTheme, type ThemesType } from '@pierre/diffs';
3
2
  import { useChatHost, type ChatHost } from '../../host';
3
+ import { useHostCodeThemes } from '../../host-hooks';
4
4
 
5
5
  // The library's registry is global and refuses a name twice, so what is registered is kept by name.
6
6
  const registered = new Set<string>();
@@ -23,6 +23,6 @@ function registerThemes(custom: ChatHost['code']['custom']): void {
23
23
  export function useDiffTheme(): ThemesType {
24
24
  const { code } = useChatHost();
25
25
  registerThemes(code.custom);
26
- const { light, dark } = code.useThemes();
27
- return useMemo(() => ({ light, dark }), [light, dark]);
26
+ const { light, dark } = useHostCodeThemes();
27
+ return { light, dark };
28
28
  }
@@ -1,4 +1,4 @@
1
- import { createContext, useContext, useMemo } from 'react';
1
+ import { createContext, useContext } from 'react';
2
2
  import { useChatHost, type FileRef } from '../../host';
3
3
  import { ChatScopeContext } from '../../scope';
4
4
 
@@ -22,7 +22,7 @@ export function useFileLinkTarget(text: string): FileRef | null {
22
22
  const cwd = useFileLinkCwd();
23
23
  const scopeId = useFileLinkScopeId();
24
24
  const { fileLinks } = useChatHost();
25
- return useMemo(() => fileLinks?.target(text, cwd, scopeId) ?? null, [fileLinks, text, cwd, scopeId]);
25
+ return fileLinks?.target(text, cwd, scopeId) ?? null;
26
26
  }
27
27
 
28
28
  /* Follows a reference the way the app opens a file or a folder. */
@@ -399,6 +399,8 @@ function UiBlockBody({
399
399
  /* Set for a finished reply the machine keeps no readings for, which nothing will ever look up. */
400
400
  unrecorded: boolean;
401
401
  }): ReactNode {
402
+ // The block's state changes in place and only its revision says so; compiled, the evaluation of the first revision would stay.
403
+ 'use no memo';
402
404
  const host = useChatHost();
403
405
  const { t } = useTranslation('agent-chat');
404
406
  const key = JSON.stringify([context.scopeId, context.chatId, context.itemId, block.id]);
@@ -1,4 +1,4 @@
1
- import { useMemo, useState } from 'react';
1
+ import { useState } from 'react';
2
2
  import type { TFunction } from 'i18next';
3
3
  import clsx from 'clsx';
4
4
  import { useTranslation } from 'react-i18next';
@@ -439,7 +439,7 @@ function DrawnChart({
439
439
  const [measure, width] = useMeasuredWidth();
440
440
  const character = useChartCharacter();
441
441
  const [measureRows, rowsCut] = useTruncated<HTMLDivElement>(true);
442
- const values = useMemo(() => uiChartValues(data), [data]);
442
+ const values = uiChartValues(data);
443
443
  const { kind } = chart;
444
444
  return (
445
445
  <figure className="chat-ui-flat flex flex-col gap-2.5 rounded-xl bg-ui-tile p-3.5">
@@ -473,7 +473,7 @@ function DrawnChart({
473
473
  */
474
474
  export function ChartRenderer({ node, context }: UiRendererProps<UiProps<'Chart'>>) {
475
475
  const { kind, data, unit } = node.props;
476
- const chart = useMemo(() => (node.complete ? uiChartData(data, kind) : null), [node.complete, data, kind]);
476
+ const chart = node.complete ? uiChartData(data, kind) : null;
477
477
  if (!node.complete) {
478
478
  return <div aria-hidden className="rounded-xl bg-ui-tile" style={{ height: chartHeight(kind, data.length) }} />;
479
479
  }
@@ -12,6 +12,7 @@ import { ImageThumb } from '../../ImageView';
12
12
  import { uiChildrenOf, uiNodeLabel, uiNodeText, uiUrlDomain } from '../node-text';
13
13
  import type { UiRendererProps } from '../render-context';
14
14
  import { useTruncated } from '../use-truncated';
15
+ import { useHostAttachmentUrl } from '../../../../host-hooks';
15
16
 
16
17
  // The number of every source by its node id, which the list counts in its own order.
17
18
  const SourceNumbers = createContext<ReadonlyMap<string, number>>(new Map());
@@ -55,8 +56,7 @@ function AttachedImage({
55
56
  cell: boolean;
56
57
  unnamed: boolean;
57
58
  }) {
58
- const host = useChatHost();
59
- const source = host.attachments.useUrl(scopeId, chatId, attachmentId);
59
+ const source = useHostAttachmentUrl(scopeId, chatId, attachmentId);
60
60
  if (!cell) {
61
61
  return <ImageThumb source={source} alt={alt} className="max-h-70 max-w-full rounded-xl object-contain" />;
62
62
  }
@@ -117,12 +117,11 @@ const COMPARE_MAX_HEIGHT = 384;
117
117
  */
118
118
  export function CompareRenderer({ node, children, context }: UiRendererProps<UiProps<'Compare'>>) {
119
119
  const { t } = useTranslation('agent-chat');
120
- const host = useChatHost();
121
120
  const { before, after } = node.props;
122
121
  const beforeLabel = node.props.beforeLabel || t('blocks.compare.before');
123
122
  const afterLabel = node.props.afterLabel || t('blocks.compare.after');
124
- const first = host.attachments.useUrl(context.scopeId, context.chatId, before);
125
- const second = host.attachments.useUrl(context.scopeId, context.chatId, after);
123
+ const first = useHostAttachmentUrl(context.scopeId, context.chatId, before);
124
+ const second = useHostAttachmentUrl(context.scopeId, context.chatId, after);
126
125
  const [split, setSplit] = useState(0.5);
127
126
  const [aspect, setAspect] = useState(COMPARE_ASPECT);
128
127
  const caption = uiNodeLabel(node);
@@ -205,7 +204,7 @@ export function ModelRenderer({ node, children, context }: UiRendererProps<UiPro
205
204
  const { t } = useTranslation('agent-chat');
206
205
  const host = useChatHost();
207
206
  const { attachment, alt } = node.props;
208
- const source = host.attachments.useUrl(context.scopeId, context.chatId, attachment);
207
+ const source = useHostAttachmentUrl(context.scopeId, context.chatId, attachment);
209
208
  const caption = uiNodeLabel(node);
210
209
  const label = alt ?? (caption === '' ? t('blocks.model.label') : caption);
211
210
  const View = host.ModelView;
@@ -7,6 +7,7 @@ import { ButtonGroup, IconButton } from '@adecore/ui';
7
7
  import { useChatHost, type UiMapSource } from '../../../../host';
8
8
  import { watchTheme } from '../../theme-watch';
9
9
  import { baseMapStyle, boundsOf, localizedTextField, mapFitOptions, spreadOverlaps, type MapColors, type MapPoint } from './map-style';
10
+ import { useHostCodeMode } from '../../../../host-hooks';
10
11
 
11
12
  /*
12
13
  * What the view is drawing: `base` by the source's choice, `offline` and `unavailable` as a fallback from tiles that
@@ -63,7 +64,7 @@ function readColors(element: HTMLElement): MapColors {
63
64
  export function MapView({ markers, source, selected, onPick, onStatus }: MapViewProps) {
64
65
  const { t, i18n } = useTranslation('agent-chat');
65
66
  const host = useChatHost();
66
- const mode = host.code.useMode();
67
+ const mode = useHostCodeMode();
67
68
  const workerUrl = host.maps?.workerUrl;
68
69
  const element = useRef<HTMLDivElement>(null);
69
70
  const [map, setMap] = useState<MapLibreMap | null>(null);
@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
5
5
  import type { UiProps, UiViewNode } from '@adecore/intelligent-ui';
6
6
  import { Button, LazyBoundary, Spinner } from '@adecore/ui';
7
7
  import { useChatHost, type UiMapSource } from '../../../../host';
8
+ import { useHostMapSource } from '../../../../host-hooks';
8
9
  import { lazyNamed } from '../../../../lazy';
9
10
  import { DiffFallbackBoundary } from '../../diff/DiffFallbackBoundary';
10
11
  import { uiChildrenOf, uiNodeLabel } from '../node-text';
@@ -170,7 +171,7 @@ function MapFrame({ markers, source, selected, onPick }: FrameProps) {
170
171
 
171
172
  /* Asks the host for its tiles in a component of its own, since a host that offers none has no hook to call. */
172
173
  function HostedFrame({ maps, scopeId, ...frame }: Omit<FrameProps, 'source'> & { maps: NonNullable<ReturnType<typeof useChatHost>['maps']>; scopeId: string }) {
173
- return <MapFrame {...frame} source={maps.useSource(scopeId)} />;
174
+ return <MapFrame {...frame} source={useHostMapSource(maps, scopeId)} />;
174
175
  }
175
176
 
176
177
  /*
@@ -1,4 +1,4 @@
1
- import { useCallback, useState } from 'react';
1
+ import { useState } from 'react';
2
2
 
3
3
  function cut(element: HTMLElement): boolean {
4
4
  return element.scrollWidth > element.clientWidth + 1;
@@ -11,23 +11,20 @@ function cut(element: HTMLElement): boolean {
11
11
  */
12
12
  export function useTruncated<T extends HTMLElement>(within = false): [(element: T | null) => (() => void) | undefined, boolean] {
13
13
  const [truncated, setTruncated] = useState(false);
14
- const measure = useCallback(
15
- (element: T | null) => {
16
- if (element === null || typeof ResizeObserver === 'undefined') {
17
- return undefined;
18
- }
19
- const check = () => setTruncated(within ? [...element.querySelectorAll<HTMLElement>('.truncate')].some(cut) : cut(element));
20
- check();
21
- const resize = new ResizeObserver(check);
22
- resize.observe(element);
23
- const text = new MutationObserver(check);
24
- text.observe(element, { characterData: true, childList: true, subtree: true });
25
- return () => {
26
- resize.disconnect();
27
- text.disconnect();
28
- };
29
- },
30
- [within]
31
- );
14
+ const measure = (element: T | null) => {
15
+ if (element === null || typeof ResizeObserver === 'undefined') {
16
+ return undefined;
17
+ }
18
+ const check = () => setTruncated(within ? [...element.querySelectorAll<HTMLElement>('.truncate')].some(cut) : cut(element));
19
+ check();
20
+ const resize = new ResizeObserver(check);
21
+ resize.observe(element);
22
+ const text = new MutationObserver(check);
23
+ text.observe(element, { characterData: true, childList: true, subtree: true });
24
+ return () => {
25
+ resize.disconnect();
26
+ text.disconnect();
27
+ };
28
+ };
32
29
  return [measure, truncated];
33
30
  }
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
1
+ import { useEffect, useRef, useState } from 'react';
2
2
  import type { ChatUiLinkReading, ChatUiQueryState } from '@adecore/agent-contracts';
3
3
  import { uiLinkTargets, type UiLinkTarget } from '@adecore/intelligent-ui/links';
4
4
  import { uiInputValues, UI_HOST_LIMITS, type UiBlock, UiState } from '@adecore/intelligent-ui';
@@ -40,7 +40,7 @@ export function useUiLinks(
40
40
  const signature = JSON.stringify(targets);
41
41
  const input = JSON.stringify(values);
42
42
  const tickets = JSON.stringify(reads);
43
- const defaultInput = useMemo(() => JSON.stringify(uiInputValues(block, new UiState(block))), [block]);
43
+ const defaultInput = JSON.stringify(uiInputValues(block, new UiState(block)));
44
44
  const link = host?.link;
45
45
  useEffect(() => {
46
46
  asked.current.clear();
@@ -134,7 +134,7 @@ export function useUiLinks(
134
134
  });
135
135
  };
136
136
  const hostOpenUrl = host?.openUrl;
137
- const openUrl = useCallback((url: string) => hostOpenUrl?.(context.scopeId, url), [hostOpenUrl, context.scopeId]);
137
+ const openUrl = (url: string) => hostOpenUrl?.(context.scopeId, url);
138
138
  return {
139
139
  link: link ? resolution : undefined,
140
140
  openLink: link && host?.openLink ? open : undefined,
@@ -1,116 +1,72 @@
1
- import type { MermaidConfig } from 'mermaid';
1
+ import type { RenderOptions } from 'pele';
2
2
 
3
- /* The colors of a diagram, each a token of the theme resolved to an opaque color over the frame it is drawn in. */
4
- export interface MermaidColors {
5
- ground: string;
6
- node: string;
7
- nodeBorder: string;
8
- text: string;
9
- line: string;
10
- cluster: string;
11
- clusterBorder: string;
12
- note: string;
13
- }
14
-
15
- /* The token each color reads. Borders carry an alpha, so they are resolved over the ground. */
16
- export const MERMAID_TOKENS: Readonly<Record<keyof MermaidColors, string>> = {
17
- ground: '--chat-code-bg',
18
- node: '--surface-raised',
19
- nodeBorder: '--border-strong',
20
- text: '--text',
21
- line: '--text-muted',
22
- cluster: '--surface',
23
- clusterBorder: '--border',
24
- note: '--accent-soft'
3
+ /* The variable of Pele each token of the theme fills. Borders carry an alpha, so every color is resolved over the ground. */
4
+ export const DIAGRAM_TOKENS: Readonly<Record<string, string>> = {
5
+ '--pele-bg': '--chat-code-bg',
6
+ '--pele-fg': '--text',
7
+ '--pele-muted': '--text-muted',
8
+ '--pele-line': '--text-muted',
9
+ '--pele-surface': '--surface-raised',
10
+ '--pele-surface-alt': '--surface',
11
+ '--pele-border': '--border-strong',
12
+ '--pele-accent': '--accent',
13
+ '--pele-critical': '--status-error',
14
+ '--pele-series-1': '--accent',
15
+ '--pele-series-2': '--chart-2',
16
+ '--pele-series-3': '--chart-3',
17
+ '--pele-series-4': '--chart-4',
18
+ '--pele-series-5': '--chart-5',
19
+ '--pele-series-6': '--chart-6'
25
20
  };
26
21
 
27
- // Mermaid's own list plus what keeps a diagram in the theme and out of HTML: a directive in the text cannot change these.
28
- const SECURE_KEYS = [
29
- 'secure',
30
- 'securityLevel',
31
- 'startOnLoad',
32
- 'maxTextSize',
33
- 'maxEdges',
34
- 'suppressErrorRendering',
35
- 'htmlLabels',
36
- 'theme',
37
- 'themeVariables',
38
- 'themeCSS',
39
- 'darkMode',
40
- 'fontFamily'
41
- ];
22
+ export const GROUND_TOKEN = DIAGRAM_TOKENS['--pele-bg']!;
23
+
24
+ export interface DiagramFont {
25
+ family: string;
26
+ mono: string;
27
+ size: number;
28
+ }
42
29
 
43
30
  /*
44
- * Strict: no HTML in labels, no click handlers, no links that run script. What comes out is drawn as an
45
- * image as well, which never runs script and loads nothing, whatever the text of the diagram says.
31
+ * At the size it was laid out at, since an image has no container to shrink to. No links and no images, and no
32
+ * math or icon resolver, which are the only markup Pele inserts unfiltered. What comes out is drawn as an image as
33
+ * well, which never runs script and loads nothing, whatever the text of the diagram says.
46
34
  */
47
- export function mermaidConfig(colors: MermaidColors, dark: boolean, font: { family: string; size: string }): MermaidConfig {
35
+ export function renderOptions(font: DiagramFont, idPrefix: string): RenderOptions {
48
36
  return {
49
- startOnLoad: false,
50
- securityLevel: 'strict',
51
- htmlLabels: false,
52
- suppressErrorRendering: true,
53
- secure: SECURE_KEYS,
54
- theme: 'base',
55
- darkMode: dark,
37
+ responsive: false,
38
+ idPrefix,
56
39
  fontFamily: font.family,
57
- themeVariables: {
58
- darkMode: dark,
59
- fontFamily: font.family,
60
- fontSize: font.size,
61
- background: colors.ground,
62
- primaryColor: colors.node,
63
- primaryTextColor: colors.text,
64
- primaryBorderColor: colors.nodeBorder,
65
- secondaryColor: colors.note,
66
- tertiaryColor: colors.cluster,
67
- mainBkg: colors.node,
68
- nodeBorder: colors.nodeBorder,
69
- textColor: colors.text,
70
- titleColor: colors.text,
71
- lineColor: colors.line,
72
- clusterBkg: colors.cluster,
73
- clusterBorder: colors.clusterBorder,
74
- edgeLabelBackground: colors.ground,
75
- noteBkgColor: colors.note,
76
- noteTextColor: colors.text,
77
- noteBorderColor: colors.nodeBorder
78
- }
40
+ fontFamilyMono: font.mono,
41
+ fontSize: font.size,
42
+ links: false,
43
+ images: false
79
44
  };
80
45
  }
81
46
 
82
- export interface SizedSvg {
83
- svg: string;
84
- width: number;
85
- height: number;
86
- }
87
-
88
47
  const ROOT = /<svg\b[^>]*>/;
89
- const VIEW_BOX = /\sviewBox="([^"]*)"/;
90
- const SIZE_ATTRIBUTES = /\s(?:width|height|style)="[^"]*"/g;
48
+ const STYLE = /\sstyle="/;
49
+
50
+ function attributeText(value: string): string {
51
+ return value.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;');
52
+ }
91
53
 
92
54
  /*
93
- * The diagram at the size it was laid out at. Mermaid fits it to the width of its container instead,
94
- * which an image does not have, so the root takes the size its view box names. Null for anything
95
- * without one.
55
+ * The drawing with the colors set on its root. An image does not see the variables of the page, so they are
56
+ * written in, and a change of the theme only writes them again.
96
57
  */
97
- export function sizedSvg(svg: string): SizedSvg | null {
58
+ export function themedSvg(svg: string, colors: Readonly<Record<string, string>>): string {
98
59
  const root = ROOT.exec(svg);
99
- const viewBox = root === null ? null : VIEW_BOX.exec(root[0]);
100
- if (root === null || viewBox === null) {
101
- return null;
102
- }
103
- const [, , boxWidth, boxHeight] = viewBox[1]!
104
- .trim()
105
- .split(/[\s,]+/)
106
- .map(Number);
107
- if (!Number.isFinite(boxWidth) || !Number.isFinite(boxHeight) || boxWidth! <= 0 || boxHeight! <= 0) {
108
- return null;
60
+ if (root === null) {
61
+ return svg;
109
62
  }
110
- const width = Math.ceil(boxWidth!);
111
- const height = Math.ceil(boxHeight!);
112
- const tag = root[0].replace(SIZE_ATTRIBUTES, '').replace(/^<svg\b/, `<svg width="${width}" height="${height}"`);
113
- return { svg: svg.slice(0, root.index) + tag + svg.slice(root.index + root[0].length), width, height };
63
+ const variables = attributeText(
64
+ Object.entries(colors)
65
+ .map(([name, value]) => `${name}:${value};`)
66
+ .join('')
67
+ );
68
+ const tag = STYLE.test(root[0]) ? root[0].replace(STYLE, (style) => style + variables) : root[0].replace(/^<svg\b/, `<svg style="${variables}"`);
69
+ return svg.slice(0, root.index) + tag + svg.slice(root.index + root[0].length);
114
70
  }
115
71
 
116
72
  export function svgDataUrl(svg: string): string {
@@ -2,8 +2,8 @@ import { useTranslation } from 'react-i18next';
2
2
  import { ChevronRight, GitFork } from 'lucide-react';
3
3
  import { useForkIdsAfter } from '../../forks';
4
4
  import { ROW_GUTTER } from '../icons';
5
- import { useChatHost } from '../../../host';
6
5
  import { Icon, Menu } from '@adecore/ui';
6
+ import { useHostChatPlace } from '../../../host-hooks';
7
7
 
8
8
  const LINE = '-mx-1 mb-0.5 flex h-7 items-center gap-2 rounded-md px-1 text-xs text-text-muted';
9
9
  const LINK = `${LINE} hover:bg-surface-hover hover:text-text data-[popup-open]:bg-surface-hover data-[popup-open]:text-text`;
@@ -52,8 +52,7 @@ function ForksLabel({ label, link = true }: { label: string; link?: boolean }) {
52
52
 
53
53
  // A fork no longer in this project has nowhere to lead, so it only says it is there.
54
54
  function OneFork({ forkId, label }: { forkId: string; label: string }) {
55
- const host = useChatHost();
56
- const place = host.useChatPlace(forkId);
55
+ const place = useHostChatPlace(forkId);
57
56
  return (
58
57
  <div className="flex">
59
58
  {place.title === null ? (
@@ -70,9 +69,8 @@ function OneFork({ forkId, label }: { forkId: string; label: string }) {
70
69
  }
71
70
 
72
71
  function ForkItem({ forkId }: { forkId: string }) {
73
- const host = useChatHost();
74
72
  const { t } = useTranslation('agent-chat');
75
- const place = host.useChatPlace(forkId);
73
+ const place = useHostChatPlace(forkId);
76
74
  return (
77
75
  <Menu.Item disabled={place.title === null} onClick={place.go}>
78
76
  <Icon icon={GitFork} size={14} /> <span className="truncate">{place.title ?? t('fork.pill.label')}</span>
@@ -16,6 +16,7 @@ import { useFileLinkCwd, useOpenFileLink } from '../file-links';
16
16
  import { ImageThumb } from '../ImageView';
17
17
  import { ROW_GUTTER } from '../icons';
18
18
  import { RunningFor, WorkRow } from './WorkRows';
19
+ import { useHostAttachmentUrl } from '../../../host-hooks';
19
20
 
20
21
  const SAVED_FLASH_MS = 2000;
21
22
 
@@ -94,7 +95,7 @@ function ReadyImage({ chatId, attachment, prompt, transparent }: { chatId: strin
94
95
  const { t } = useTranslation('agent-chat');
95
96
  const { id: scopeId } = useChatScope();
96
97
  const { attachments } = useChatHost();
97
- const source = attachments.useUrl(scopeId, chatId, attachment.id);
98
+ const source = useHostAttachmentUrl(scopeId, chatId, attachment.id);
98
99
  const savedKey = `${scopeId}\n${chatId}\n${attachment.id}`;
99
100
  const [lightbox, setLightbox] = useState(false);
100
101
  const [saving, setSaving] = useState(false);