@adecore/agents-react 0.23.1 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/README.md +14 -11
  2. package/dist/agents/AccountDot.js +3 -1
  3. package/dist/agents/accounts.d.ts +3 -2
  4. package/dist/agents/accounts.js +5 -6
  5. package/dist/chat/actions.d.ts +2 -2
  6. package/dist/chat/actions.js +8 -5
  7. package/dist/chat/bookmarks.d.ts +5 -3
  8. package/dist/chat/bookmarks.js +13 -14
  9. package/dist/chat/chat-client.d.ts +1 -0
  10. package/dist/chat/chat-client.js +7 -0
  11. package/dist/chat/composer-send.d.ts +3 -0
  12. package/dist/chat/composer-send.js +22 -0
  13. package/dist/chat/forks.js +22 -5
  14. package/dist/chat/index.d.ts +31 -0
  15. package/dist/chat/index.js +28 -0
  16. package/dist/chat/logic/embedded-code.d.ts +8 -0
  17. package/dist/chat/logic/embedded-code.js +371 -0
  18. package/dist/chat/logic/fork.js +1 -0
  19. package/dist/chat/logic/goal.d.ts +6 -0
  20. package/dist/chat/logic/goal.js +26 -0
  21. package/dist/chat/logic/scrubber.d.ts +1 -1
  22. package/dist/chat/logic/scrubber.js +6 -5
  23. package/dist/chat/logic/thought.d.ts +3 -0
  24. package/dist/chat/logic/thought.js +35 -0
  25. package/dist/chat/logic/thread-cards.js +2 -0
  26. package/dist/chat/logic/timeline.d.ts +5 -0
  27. package/dist/chat/logic/timeline.js +22 -1
  28. package/dist/chat/logic/tool-catalog.d.ts +1 -0
  29. package/dist/chat/logic/tool-catalog.js +3 -1
  30. package/dist/chat/logic/tools.d.ts +14 -0
  31. package/dist/chat/logic/tools.js +57 -0
  32. package/dist/chat/preferences.d.ts +6 -0
  33. package/dist/chat/preferences.js +19 -3
  34. package/dist/chat/reading-width.d.ts +9 -0
  35. package/dist/chat/reading-width.js +27 -0
  36. package/dist/chat/send-key.d.ts +10 -0
  37. package/dist/chat/send-key.js +24 -0
  38. package/dist/chat/ui/BookmarkMarker.js +4 -2
  39. package/dist/chat/ui/BookmarkSubmenu.js +4 -3
  40. package/dist/chat/ui/ChatActivity.js +14 -10
  41. package/dist/chat/ui/ChatReferenceChip.js +3 -4
  42. package/dist/chat/ui/CodeBlock.d.ts +2 -1
  43. package/dist/chat/ui/CodeBlock.js +14 -96
  44. package/dist/chat/ui/Composer.d.ts +1 -1
  45. package/dist/chat/ui/Composer.js +130 -907
  46. package/dist/chat/ui/ComposerInput.js +5 -4
  47. package/dist/chat/ui/EditDiff.js +3 -2
  48. package/dist/chat/ui/GoalDock.d.ts +9 -0
  49. package/dist/chat/ui/GoalDock.js +63 -0
  50. package/dist/chat/ui/LimitState.js +5 -3
  51. package/dist/chat/ui/Markdown.d.ts +2 -1
  52. package/dist/chat/ui/Markdown.js +70 -31
  53. package/dist/chat/ui/MermaidBlock.d.ts +3 -0
  54. package/dist/chat/ui/MermaidBlock.js +73 -0
  55. package/dist/chat/ui/MessageActions.js +12 -7
  56. package/dist/chat/ui/Pickers.js +6 -5
  57. package/dist/chat/ui/PromptComposer.js +4 -3
  58. package/dist/chat/ui/RowTime.d.ts +3 -0
  59. package/dist/chat/ui/RowTime.js +12 -0
  60. package/dist/chat/ui/RunSettings.js +5 -4
  61. package/dist/chat/ui/ShellCommand.d.ts +3 -0
  62. package/dist/chat/ui/ShellCommand.js +110 -0
  63. package/dist/chat/ui/SubagentInfo.js +4 -3
  64. package/dist/chat/ui/SubagentStopButton.js +4 -3
  65. package/dist/chat/ui/SubagentTimeline.js +4 -1
  66. package/dist/chat/ui/Timeline.js +70 -40
  67. package/dist/chat/ui/TimelineMenu.d.ts +2 -1
  68. package/dist/chat/ui/TimelineMenu.js +18 -8
  69. package/dist/chat/ui/UnifiedDiff.js +3 -2
  70. package/dist/chat/ui/VisualFrame.js +6 -7
  71. package/dist/chat/ui/code-theme.d.ts +2 -1
  72. package/dist/chat/ui/code-theme.js +4 -4
  73. package/dist/chat/ui/composer/ComposerControls.d.ts +27 -0
  74. package/dist/chat/ui/composer/ComposerControls.js +34 -0
  75. package/dist/chat/ui/composer/ComposerMenu.d.ts +4 -0
  76. package/dist/chat/ui/composer/ComposerMenu.js +46 -0
  77. package/dist/chat/ui/composer/DraftParts.d.ts +18 -0
  78. package/dist/chat/ui/composer/DraftParts.js +25 -0
  79. package/dist/chat/ui/composer/QueuedMessages.d.ts +10 -0
  80. package/dist/chat/ui/composer/QueuedMessages.js +20 -0
  81. package/dist/chat/ui/composer/key-handler.d.ts +20 -0
  82. package/dist/chat/ui/composer/key-handler.js +150 -0
  83. package/dist/chat/ui/composer/keys.d.ts +7 -2
  84. package/dist/chat/ui/composer/keys.js +19 -9
  85. package/dist/chat/ui/composer/placeholder.d.ts +9 -0
  86. package/dist/chat/ui/composer/placeholder.js +38 -0
  87. package/dist/chat/ui/composer/surface.d.ts +1 -0
  88. package/dist/chat/ui/composer/surface.js +44 -0
  89. package/dist/chat/ui/composer/use-chat-controls.d.ts +28 -0
  90. package/dist/chat/ui/composer/use-chat-controls.js +112 -0
  91. package/dist/chat/ui/composer/use-compaction-offer.d.ts +2 -0
  92. package/dist/chat/ui/composer/use-compaction-offer.js +23 -0
  93. package/dist/chat/ui/composer/use-composer-draft.d.ts +14 -0
  94. package/dist/chat/ui/composer/use-composer-draft.js +39 -0
  95. package/dist/chat/ui/composer/use-composer-menus.d.ts +52 -0
  96. package/dist/chat/ui/composer/use-composer-menus.js +232 -0
  97. package/dist/chat/ui/composer/use-composer-submit.d.ts +32 -0
  98. package/dist/chat/ui/composer/use-composer-submit.js +113 -0
  99. package/dist/chat/ui/composer/use-draft-files.d.ts +27 -0
  100. package/dist/chat/ui/composer/use-draft-files.js +111 -0
  101. package/dist/chat/ui/composer/use-notice.d.ts +6 -0
  102. package/dist/chat/ui/composer/use-notice.js +18 -0
  103. package/dist/chat/ui/composer/use-pending-requests.d.ts +2 -0
  104. package/dist/chat/ui/composer/use-pending-requests.js +22 -0
  105. package/dist/chat/ui/composer/use-prompt-history.d.ts +8 -0
  106. package/dist/chat/ui/composer/use-prompt-history.js +57 -0
  107. package/dist/chat/ui/composer/use-prompt-hold.d.ts +6 -0
  108. package/dist/chat/ui/composer/use-prompt-hold.js +48 -0
  109. package/dist/chat/ui/composer/use-take-back.d.ts +13 -0
  110. package/dist/chat/ui/composer/use-take-back.js +82 -0
  111. package/dist/chat/ui/diff/EditorEditDiff.js +4 -3
  112. package/dist/chat/ui/diff/LegacyDiffPool.js +3 -2
  113. package/dist/chat/ui/diff/LegacyEditDiff.js +3 -2
  114. package/dist/chat/ui/diff/LegacyUnifiedDiff.js +3 -2
  115. package/dist/chat/ui/diff/syntax-runtime.d.ts +2 -1
  116. package/dist/chat/ui/diff/syntax-runtime.js +9 -5
  117. package/dist/chat/ui/diff-syntax.d.ts +2 -2
  118. package/dist/chat/ui/diff-syntax.js +1 -2
  119. package/dist/chat/ui/diff-theme.js +10 -9
  120. package/dist/chat/ui/file-links.d.ts +1 -1
  121. package/dist/chat/ui/file-links.js +7 -5
  122. package/dist/chat/ui/highlighter.d.ts +27 -0
  123. package/dist/chat/ui/highlighter.js +151 -0
  124. package/dist/chat/ui/index.d.ts +67 -0
  125. package/dist/chat/ui/index.js +64 -0
  126. package/dist/chat/ui/intelligent-ui/UiReply.js +3 -2
  127. package/dist/chat/ui/intelligent-ui/renderers/content.js +3 -2
  128. package/dist/chat/ui/intelligent-ui/use-ui-links.js +2 -2
  129. package/dist/chat/ui/intelligent-ui/use-ui-queries.js +4 -3
  130. package/dist/chat/ui/markdown-blocks.d.ts +8 -0
  131. package/dist/chat/ui/markdown-blocks.js +260 -12
  132. package/dist/chat/ui/mermaid.d.ts +23 -0
  133. package/dist/chat/ui/mermaid.js +93 -0
  134. package/dist/chat/ui/rows/ForksRow.js +5 -3
  135. package/dist/chat/ui/rows/GeneratedImageRow.js +6 -4
  136. package/dist/chat/ui/rows/MessageRows.d.ts +4 -0
  137. package/dist/chat/ui/rows/MessageRows.js +36 -13
  138. package/dist/chat/ui/rows/Rows.js +3 -1
  139. package/dist/chat/ui/rows/SubagentRow.js +3 -2
  140. package/dist/chat/ui/rows/VisualRow.js +3 -2
  141. package/dist/chat/ui/rows/WorkRows.d.ts +6 -3
  142. package/dist/chat/ui/rows/WorkRows.js +57 -20
  143. package/dist/chat/ui/rows/WorkflowRow.js +3 -2
  144. package/dist/chat/ui/timestamp-format.d.ts +1 -0
  145. package/dist/chat/ui/timestamp-format.js +2 -0
  146. package/dist/chat/ui/use-diff-source-tokens.js +6 -3
  147. package/dist/chat/ui/visual-theme.js +3 -2
  148. package/dist/host.d.ts +9 -6
  149. package/dist/host.js +19 -8
  150. package/dist/index.d.ts +8 -0
  151. package/dist/index.js +9 -0
  152. package/dist/locales/en/agent-chat.d.ts +658 -0
  153. package/dist/locales/en/agent-chat.js +657 -0
  154. package/dist/locales/en/agent-prompts.d.ts +44 -0
  155. package/dist/locales/en/agent-prompts.js +43 -0
  156. package/dist/locales/en/agent-providers.d.ts +132 -0
  157. package/dist/locales/en/agent-providers.js +131 -0
  158. package/dist/locales/en/agent-usage.d.ts +132 -0
  159. package/dist/locales/en/agent-usage.js +131 -0
  160. package/dist/locales/nl/agent-chat.d.ts +658 -0
  161. package/dist/locales/nl/agent-chat.js +657 -0
  162. package/dist/locales/nl/agent-prompts.d.ts +44 -0
  163. package/dist/locales/nl/agent-prompts.js +43 -0
  164. package/dist/locales/nl/agent-providers.d.ts +132 -0
  165. package/dist/locales/nl/agent-providers.js +131 -0
  166. package/dist/locales/nl/agent-usage.d.ts +132 -0
  167. package/dist/locales/nl/agent-usage.js +131 -0
  168. package/dist/locales.js +9 -8
  169. package/dist/prompts/index.d.ts +11 -0
  170. package/dist/prompts/index.js +12 -0
  171. package/dist/prompts/logic/subjects.d.ts +2 -1
  172. package/dist/prompts/logic/subjects.js +6 -4
  173. package/dist/prompts/ui/ApprovalBody.js +6 -4
  174. package/dist/prompts/ui/PromptCard.js +5 -4
  175. package/dist/prompts/ui/PromptView.js +3 -2
  176. package/dist/prompts/ui/QuestionBody.js +3 -2
  177. package/dist/providers/AccountColors.js +2 -2
  178. package/dist/providers/AccountDetail.js +7 -6
  179. package/dist/providers/AccountVariables.js +3 -1
  180. package/dist/providers/AddAccountForm.js +3 -2
  181. package/dist/providers/ProvidersPane.js +4 -3
  182. package/dist/providers/account-actions.d.ts +4 -3
  183. package/dist/providers/account-actions.js +6 -7
  184. package/dist/providers/index.d.ts +16 -0
  185. package/dist/providers/index.js +17 -0
  186. package/dist/providers/provider-abilities.d.ts +2 -1
  187. package/dist/providers/provider-abilities.js +4 -0
  188. package/dist/state/chats.js +4 -2
  189. package/dist/state/index.d.ts +4 -0
  190. package/dist/state/index.js +5 -0
  191. package/dist/testing/index.d.ts +1 -0
  192. package/dist/testing/index.js +2 -0
  193. package/dist/{prompts/logic/prompts.fixtures.d.ts → testing/prompts.d.ts} +1 -1
  194. package/dist/theme.css +13 -3
  195. package/dist/usage/UsageBreakdown.js +3 -2
  196. package/dist/usage/UsageLimits.js +4 -3
  197. package/dist/usage/UsagePage.js +3 -2
  198. package/dist/usage/index.d.ts +15 -0
  199. package/dist/usage/index.js +15 -0
  200. package/package.json +53 -1031
  201. package/src/agents/AccountDot.tsx +3 -1
  202. package/src/agents/accounts.ts +6 -6
  203. package/src/chat/actions.ts +8 -5
  204. package/src/chat/bookmarks.ts +23 -14
  205. package/src/chat/chat-client.ts +8 -0
  206. package/src/chat/composer-send.ts +27 -0
  207. package/src/chat/forks.ts +27 -6
  208. package/src/chat/index.ts +98 -0
  209. package/src/chat/logic/embedded-code.ts +409 -0
  210. package/src/chat/logic/fork.ts +1 -0
  211. package/src/chat/logic/goal.ts +33 -0
  212. package/src/chat/logic/scrubber.ts +6 -5
  213. package/src/chat/logic/thought.ts +40 -0
  214. package/src/chat/logic/thread-cards.ts +2 -0
  215. package/src/chat/logic/timeline.ts +25 -1
  216. package/src/chat/logic/tool-catalog.ts +5 -1
  217. package/src/chat/logic/tools.ts +76 -0
  218. package/src/chat/preferences.ts +26 -3
  219. package/src/chat/reading-width.ts +39 -0
  220. package/src/chat/send-key.ts +38 -0
  221. package/src/chat/ui/BookmarkMarker.tsx +4 -2
  222. package/src/chat/ui/BookmarkSubmenu.tsx +4 -3
  223. package/src/chat/ui/ChatActivity.tsx +14 -10
  224. package/src/chat/ui/ChatReferenceChip.tsx +3 -4
  225. package/src/chat/ui/CodeBlock.tsx +33 -124
  226. package/src/chat/ui/Composer.tsx +212 -1358
  227. package/src/chat/ui/ComposerInput.tsx +5 -4
  228. package/src/chat/ui/EditDiff.tsx +3 -2
  229. package/src/chat/ui/GoalDock.tsx +104 -0
  230. package/src/chat/ui/LimitState.tsx +5 -3
  231. package/src/chat/ui/Markdown.tsx +113 -39
  232. package/src/chat/ui/MermaidBlock.tsx +119 -0
  233. package/src/chat/ui/MessageActions.tsx +13 -8
  234. package/src/chat/ui/Pickers.tsx +8 -7
  235. package/src/chat/ui/PromptComposer.tsx +4 -3
  236. package/src/chat/ui/RowTime.tsx +21 -0
  237. package/src/chat/ui/RunSettings.tsx +9 -4
  238. package/src/chat/ui/ShellCommand.tsx +134 -0
  239. package/src/chat/ui/SubagentInfo.tsx +4 -3
  240. package/src/chat/ui/SubagentStopButton.tsx +4 -3
  241. package/src/chat/ui/SubagentTimeline.tsx +5 -1
  242. package/src/chat/ui/Timeline.tsx +167 -105
  243. package/src/chat/ui/TimelineMenu.tsx +42 -7
  244. package/src/chat/ui/UnifiedDiff.tsx +3 -2
  245. package/src/chat/ui/VisualFrame.tsx +6 -7
  246. package/src/chat/ui/code-theme.ts +4 -4
  247. package/src/chat/ui/composer/ComposerControls.tsx +147 -0
  248. package/src/chat/ui/composer/ComposerMenu.tsx +138 -0
  249. package/src/chat/ui/composer/DraftParts.tsx +86 -0
  250. package/src/chat/ui/composer/QueuedMessages.tsx +64 -0
  251. package/src/chat/ui/composer/key-handler.ts +174 -0
  252. package/src/chat/ui/composer/keys.ts +32 -10
  253. package/src/chat/ui/composer/placeholder.ts +48 -0
  254. package/src/chat/ui/composer/surface.ts +45 -0
  255. package/src/chat/ui/composer/use-chat-controls.ts +134 -0
  256. package/src/chat/ui/composer/use-compaction-offer.ts +25 -0
  257. package/src/chat/ui/composer/use-composer-draft.ts +71 -0
  258. package/src/chat/ui/composer/use-composer-menus.ts +289 -0
  259. package/src/chat/ui/composer/use-composer-submit.ts +163 -0
  260. package/src/chat/ui/composer/use-draft-files.ts +135 -0
  261. package/src/chat/ui/composer/use-notice.ts +28 -0
  262. package/src/chat/ui/composer/use-pending-requests.ts +23 -0
  263. package/src/chat/ui/composer/use-prompt-history.ts +76 -0
  264. package/src/chat/ui/composer/use-prompt-hold.ts +58 -0
  265. package/src/chat/ui/composer/use-take-back.ts +91 -0
  266. package/src/chat/ui/diff/DiffFallbackBoundary.tsx +2 -2
  267. package/src/chat/ui/diff/EditorEditDiff.tsx +4 -3
  268. package/src/chat/ui/diff/LegacyDiffPool.tsx +3 -2
  269. package/src/chat/ui/diff/LegacyEditDiff.tsx +3 -2
  270. package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +3 -2
  271. package/src/chat/ui/diff/syntax-runtime.ts +10 -5
  272. package/src/chat/ui/diff-syntax.ts +2 -2
  273. package/src/chat/ui/diff-theme.ts +10 -9
  274. package/src/chat/ui/file-links.ts +7 -5
  275. package/src/chat/ui/highlighter.ts +185 -0
  276. package/src/chat/ui/index.ts +109 -0
  277. package/src/chat/ui/intelligent-ui/UiReply.tsx +5 -4
  278. package/src/chat/ui/intelligent-ui/renderers/content.tsx +3 -2
  279. package/src/chat/ui/intelligent-ui/use-ui-links.ts +2 -2
  280. package/src/chat/ui/intelligent-ui/use-ui-queries.ts +4 -3
  281. package/src/chat/ui/markdown-blocks.ts +300 -12
  282. package/src/chat/ui/mermaid.ts +118 -0
  283. package/src/chat/ui/rows/ForksRow.tsx +5 -3
  284. package/src/chat/ui/rows/GeneratedImageRow.tsx +6 -4
  285. package/src/chat/ui/rows/MessageRows.tsx +73 -18
  286. package/src/chat/ui/rows/Rows.tsx +14 -1
  287. package/src/chat/ui/rows/SubagentRow.tsx +3 -2
  288. package/src/chat/ui/rows/VisualRow.tsx +3 -2
  289. package/src/chat/ui/rows/WorkRows.tsx +148 -47
  290. package/src/chat/ui/rows/WorkflowRow.tsx +3 -2
  291. package/src/chat/ui/timestamp-format.ts +2 -0
  292. package/src/chat/ui/use-diff-source-tokens.ts +10 -4
  293. package/src/chat/ui/visual-theme.ts +3 -2
  294. package/src/host.ts +31 -15
  295. package/src/index.ts +49 -0
  296. package/src/locales/en/agent-chat.ts +657 -0
  297. package/src/locales/en/agent-prompts.ts +43 -0
  298. package/src/locales/en/agent-providers.ts +131 -0
  299. package/src/locales/en/agent-usage.ts +131 -0
  300. package/src/locales/nl/agent-chat.ts +657 -0
  301. package/src/locales/nl/agent-prompts.ts +43 -0
  302. package/src/locales/nl/agent-providers.ts +131 -0
  303. package/src/locales/nl/agent-usage.ts +132 -0
  304. package/src/locales.ts +9 -8
  305. package/src/prompts/index.ts +35 -0
  306. package/src/prompts/logic/subjects.ts +12 -4
  307. package/src/prompts/ui/ApprovalBody.tsx +6 -4
  308. package/src/prompts/ui/PromptCard.tsx +11 -4
  309. package/src/prompts/ui/PromptView.tsx +3 -2
  310. package/src/prompts/ui/QuestionBody.tsx +3 -2
  311. package/src/providers/AccountColors.tsx +2 -2
  312. package/src/providers/AccountDetail.tsx +13 -7
  313. package/src/providers/AccountVariables.tsx +3 -1
  314. package/src/providers/AddAccountForm.tsx +3 -2
  315. package/src/providers/ProvidersPane.tsx +4 -3
  316. package/src/providers/account-actions.ts +7 -7
  317. package/src/providers/index.ts +17 -0
  318. package/src/providers/provider-abilities.ts +6 -1
  319. package/src/state/chats.ts +4 -2
  320. package/src/state/index.ts +39 -0
  321. package/src/testing/index.ts +2 -0
  322. package/src/{prompts/logic/prompts.fixtures.ts → testing/prompts.ts} +1 -1
  323. package/src/theme.css +13 -3
  324. package/src/usage/UsageBreakdown.tsx +3 -2
  325. package/src/usage/UsageLimits.tsx +4 -3
  326. package/src/usage/UsagePage.tsx +3 -2
  327. package/src/usage/index.ts +16 -0
  328. package/dist/locales/en/agent-chat.json +0 -597
  329. package/dist/locales/en/agent-prompts.json +0 -43
  330. package/dist/locales/en/agent-providers.json +0 -131
  331. package/dist/locales/en/agent-usage.json +0 -131
  332. package/dist/locales/nl/agent-chat.json +0 -597
  333. package/dist/locales/nl/agent-prompts.json +0 -43
  334. package/dist/locales/nl/agent-providers.json +0 -131
  335. package/dist/locales/nl/agent-usage.json +0 -131
  336. package/src/locales/en/agent-chat.json +0 -597
  337. package/src/locales/en/agent-prompts.json +0 -43
  338. package/src/locales/en/agent-providers.json +0 -131
  339. package/src/locales/en/agent-usage.json +0 -131
  340. package/src/locales/nl/agent-chat.json +0 -597
  341. package/src/locales/nl/agent-prompts.json +0 -43
  342. package/src/locales/nl/agent-providers.json +0 -131
  343. package/src/locales/nl/agent-usage.json +0 -131
  344. /package/dist/{prompts/logic/prompts.fixtures.js → testing/prompts.js} +0 -0
@@ -0,0 +1,109 @@
1
+ // Public API: exports.test.ts snapshots this list, so a name added or dropped here is an API change.
2
+ export { AccountPill } from './AccountPill';
3
+ export { AnsiOutput } from './AnsiOutput';
4
+ export { BookmarkMarker } from './BookmarkMarker';
5
+ export { BookmarkSubmenu } from './BookmarkSubmenu';
6
+ export { ChatActivity, StatusIcon } from './ChatActivity';
7
+ export { ChatReferenceChip } from './ChatReferenceChip';
8
+ export { CodeBlock } from './CodeBlock';
9
+ export { Composer } from './Composer';
10
+ export { type ComposerInputHandle, ComposerInput, type InputSelection } from './ComposerInput';
11
+ export { default as DiffPool } from './DiffPool';
12
+ export { default as EditDiff } from './EditDiff';
13
+ export { FadingWords } from './FadingWords';
14
+ export { GoalDock, GoalPill } from './GoalDock';
15
+ export { ImageThumb, type ImageThumbProps } from './ImageView';
16
+ export { LimitDock, LimitPill } from './LimitState';
17
+ export { ReplyMarkdown, MessageMarkdown, Markdown } from './Markdown';
18
+ export { MermaidBlock } from './MermaidBlock';
19
+ export { MessageActions } from './MessageActions';
20
+ export { ModelPicker, StashPicker } from './Pickers';
21
+ export { type PromptHold, PromptComposer } from './PromptComposer';
22
+ export { QuoteButton } from './QuoteButton';
23
+ export { ResumeCompactionDock } from './ResumeCompactionDock';
24
+ export { RowTime } from './RowTime';
25
+ export { RunSettings } from './RunSettings';
26
+ export { Scrubber, type CardChat } from './Scrubber';
27
+ export { SubagentTitleCrumb, SubagentBreadcrumb, SubagentActions } from './SubagentControls';
28
+ export { SubagentInfo } from './SubagentInfo';
29
+ export { SubagentStopButton } from './SubagentStopButton';
30
+ export { SubagentTimeline } from './SubagentTimeline';
31
+ export { Timeline } from './Timeline';
32
+ export { TimelineMenuPopup, BookmarkMenuItems } from './TimelineMenu';
33
+ export { default as UnifiedDiff } from './UnifiedDiff';
34
+ export { UploadThumb } from './UploadThumb';
35
+ export { VisualDialogs } from './VisualDialogs';
36
+ export { type VisualFrameProps, VisualFrame } from './VisualFrame';
37
+ export { WelcomeGreeting } from './Welcome';
38
+ export { chipDecorations, type ChipChoices } from './composer/chips';
39
+ export { externalChange, markdownLanguage, composerEditorExtensions } from './composer/editor';
40
+ export { insertAtSelection } from './composer/insert';
41
+ export {
42
+ listItemAt,
43
+ enterAction,
44
+ inOpenFence,
45
+ recallDirection,
46
+ type EnterAction,
47
+ type EnterKeys,
48
+ type ListItem,
49
+ type EnterContext,
50
+ type RecallContext
51
+ } from './composer/keys';
52
+ export {
53
+ createDiffSyntaxRuntime,
54
+ diffSyntaxRuntime,
55
+ type DiffSyntaxRuntime,
56
+ type DiffSyntaxRuntimeOptions,
57
+ type DiffSyntaxSession,
58
+ type DiffSyntaxMapping,
59
+ type DiffSourceTokenSession,
60
+ diffSourceTokens
61
+ } from './diff-syntax';
62
+ export { useDiffTheme } from './diff-theme';
63
+ export type { UnifiedDiffProps } from './diff/diff-props';
64
+ export { FileLinkContext, useFileLinkCwd, useFileLinkScopeId, useOpenFileLink } from './file-links';
65
+ export { type ChatFindField, type FindReveal, useOpenForFind } from './find-reveal';
66
+ export type { DiffContents } from './full-diff';
67
+ export { type UiBlockFrameProps, UiBlockFrame, type UiBlockFix, type UiBlockFooterProps } from './intelligent-ui/UiBlockFrame';
68
+ export { UiFallbackPart, type UiFallbackProblem } from './intelligent-ui/UiFallbackPart';
69
+ export { UiReply } from './intelligent-ui/UiReply';
70
+ export { uiChartData, type UiChartSeries, type UiChartValues, type UiChartData } from './intelligent-ui/chart-data';
71
+ export { uiNodeLabel } from './intelligent-ui/node-text';
72
+ export { UI_RENDERERS } from './intelligent-ui/registry';
73
+ export type {
74
+ UiRenderContext,
75
+ UiRendererProps,
76
+ UiLinkTarget,
77
+ UiLink,
78
+ UiAnswer,
79
+ UiLiveStatus,
80
+ UiLiveValue,
81
+ UiRenderer,
82
+ UiRenderers
83
+ } from './intelligent-ui/render-context';
84
+ export { uiTableColumns, type UiColumnKind, type UiTableColumn } from './intelligent-ui/table-data';
85
+ export type { QuoteTaker } from './quote-selection';
86
+ export { ReplyContext } from './reply-context';
87
+ export { ForksRow } from './rows/ForksRow';
88
+ export { GeneratedImageRow } from './rows/GeneratedImageRow';
89
+ export {
90
+ UserRow,
91
+ ReportRow,
92
+ ReplyHeader,
93
+ AssistantRow,
94
+ ThinkingRow,
95
+ LiveThoughtRow,
96
+ NoteRow,
97
+ AgentTurnRow,
98
+ CompactionRow,
99
+ ApprovalHistoryRow,
100
+ QuestionHistoryRow
101
+ } from './rows/MessageRows';
102
+ export { type RowContainerProps, RowContainer } from './rows/RowContainer';
103
+ export { Row, type RowProps } from './rows/Rows';
104
+ export { SubagentRow, SubagentBranchRow } from './rows/SubagentRow';
105
+ export { TaskRow } from './rows/TaskRow';
106
+ export { VisualRow } from './rows/VisualRow';
107
+ export { ToggleLine, WorkRow, RunningFor, WorkLiveRow, WorkGroupRow, TurnFoldRow, ChangedFilesRow, WorkingRow } from './rows/WorkRows';
108
+ export { WorkflowRow } from './rows/WorkflowRow';
109
+ export { useDiffSourceTokens } from './use-diff-source-tokens';
@@ -26,7 +26,7 @@ import {
26
26
  } from '@adecore/intelligent-ui';
27
27
  import { ErrorBoundary, Tooltip } from '@adecore/ui';
28
28
  import type { ChatUiAnswer, ChatUiQueryState } from '@adecore/agent-contracts';
29
- import { chatHost } from '../../../host';
29
+ import { useChatHost } from '../../../host';
30
30
  import { UiReplyNavigationContext } from '../reply-context';
31
31
  import { ReplyMarkdown } from '../Markdown';
32
32
  import { rehypeFadeWords } from '../rehype-fade';
@@ -314,7 +314,7 @@ interface UiNodeBoundaryState {
314
314
  }
315
315
 
316
316
  class UiNodeBoundary extends Component<UiNodeBoundaryProps, UiNodeBoundaryState> {
317
- state = { failed: false, node: this.props.node };
317
+ override state = { failed: false, node: this.props.node };
318
318
 
319
319
  static getDerivedStateFromError(): { failed: boolean } {
320
320
  return { failed: true };
@@ -324,7 +324,7 @@ class UiNodeBoundary extends Component<UiNodeBoundaryProps, UiNodeBoundaryState>
324
324
  return props.node === state.node ? null : { failed: false, node: props.node };
325
325
  }
326
326
 
327
- render(): ReactNode {
327
+ override render(): ReactNode {
328
328
  return this.state.failed ? <UiFallbackPart fallback={this.props.node.fallback} problem={{ kind: 'failed' }} /> : this.props.children;
329
329
  }
330
330
  }
@@ -379,6 +379,7 @@ function UiBlockBody({
379
379
  answered?: ChatUiAnswer;
380
380
  frozen?: ChatUiQueryState['blocks'][string];
381
381
  }): ReactNode {
382
+ const host = useChatHost();
382
383
  const { t } = useTranslation('agent-chat');
383
384
  const key = JSON.stringify([context.scopeId, context.chatId, context.itemId, block.id]);
384
385
  const [state] = useState(() => localState(key, block, answered));
@@ -428,7 +429,7 @@ function UiBlockBody({
428
429
  [sources, element]
429
430
  );
430
431
  const liveValue = useUiLiveValues(block, evaluation.nodes, queries.currentReadings);
431
- const sendChoice = chatHost().intelligentUi?.sendChoice;
432
+ const sendChoice = host.intelligentUi?.sendChoice;
432
433
  const rendering: UiRenderContext = {
433
434
  ...context,
434
435
  ...links,
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
4
4
  import type { UiProps } from '@adecore/intelligent-ui';
5
5
  import { PreviewCard, Tooltip } from '@adecore/ui';
6
6
  import { formatNumber } from '@adecore/ui/format';
7
- import { chatHost } from '../../../../host';
7
+ import { useChatHost } from '../../../../host';
8
8
  import { CodeBlock } from '../../CodeBlock';
9
9
  import { CodeStreamingContext } from '../../code-streaming';
10
10
  import { ImageThumb } from '../../ImageView';
@@ -26,7 +26,8 @@ export function CodeBlockRenderer({ node }: UiRendererProps<UiProps<'CodeBlock'>
26
26
 
27
27
  /* Its own component, so the host's hook is only called for an image that names an attachment. */
28
28
  function AttachedImage({ scopeId, chatId, attachmentId, alt }: { scopeId: string; chatId: string; attachmentId: string; alt: string }) {
29
- const source = chatHost().attachments.useUrl(scopeId, chatId, attachmentId);
29
+ const host = useChatHost();
30
+ const source = host.attachments.useUrl(scopeId, chatId, attachmentId);
30
31
  return <ImageThumb source={source} alt={alt} className="max-h-70 max-w-full object-contain" />;
31
32
  }
32
33
 
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, 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';
5
- import { chatHost } from '../../../host';
5
+ import { useChatHost } from '../../../host';
6
6
  import type { UiRenderContext } from './render-context';
7
7
 
8
8
  interface LinkState {
@@ -23,7 +23,7 @@ export function useUiLinks(
23
23
  const [checked, setChecked] = useState<Record<string, LinkState>>({});
24
24
  // What each node was last checked as, so a change of input checks only the targets it moved.
25
25
  const asked = useRef(new Map<string, string>());
26
- const host = chatHost().intelligentUi;
26
+ const host = useChatHost().intelligentUi;
27
27
  const values = uiInputValues(block, state);
28
28
  const scope = state.scope();
29
29
  const queryValues = Object.fromEntries(
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useMemo, useRef, useState } from 'react';
2
2
  import type { ChatUiQueryReading, ChatUiQueryState } from '@adecore/agent-contracts';
3
3
  import { uiInputValues, UI_CATALOG_VERSION, UI_HOST_LIMITS, UiState, type UiBlock, type UiViewNode, type UiNode } from '@adecore/intelligent-ui';
4
- import { chatHost } from '../../../host';
4
+ import { useChatHost } from '../../../host';
5
5
  import type { UiLiveValue, UiLiveStatus, UiRenderContext } from './render-context';
6
6
 
7
7
  interface LocalReading {
@@ -43,6 +43,7 @@ export function useUiQueries(
43
43
  element: HTMLDivElement | null,
44
44
  frozen?: ChatUiQueryState['blocks'][string]
45
45
  ) {
46
+ const host = useChatHost();
46
47
  const [readings, setReadings] = useState<Record<string, LocalReading>>({});
47
48
  const [busyInput, setBusyInput] = useState<string | null>(null);
48
49
  const lastRead = useRef({ inputKey: '', at: 0 });
@@ -70,8 +71,8 @@ export function useUiQueries(
70
71
  };
71
72
  const reads = readsFor(JSON.parse(inputs));
72
73
  const ready = reads !== null;
73
- const query = chatHost().intelligentUi?.query;
74
- const subscribe = chatHost().intelligentUi?.subscribe;
74
+ const query = host.intelligentUi?.query;
75
+ const subscribe = host.intelligentUi?.subscribe;
75
76
  useEffect(() => {
76
77
  const scope = state.scope();
77
78
  for (const name of Object.keys(block.queries)) {
@@ -2,6 +2,20 @@ export interface MarkdownBlock {
2
2
  text: string;
3
3
  /* The block ends inside a code fence that has not closed yet. */
4
4
  openFence: boolean;
5
+ /* Set when the block is one code fence at the top level of the text, read as markdown reads it. */
6
+ fence?: MarkdownFence;
7
+ }
8
+
9
+ export interface MarkdownFence {
10
+ /* The run of backticks or tildes that opens it. */
11
+ marker: string;
12
+ /* What follows the marker on its line, trimmed. Its first word is the language. */
13
+ info: string;
14
+ /* The lines between the fences, less the indentation of the opening fence. */
15
+ body: string;
16
+ closed: boolean;
17
+ /* The fence as written, from its opening marker to the end of its closing one or of the text. */
18
+ source: string;
5
19
  }
6
20
 
7
21
  interface OpenFence {
@@ -10,45 +24,202 @@ interface OpenFence {
10
24
  indent: number;
11
25
  }
12
26
 
27
+ interface TopFence {
28
+ // Where the opening marker stands in the text.
29
+ from: number;
30
+ marker: string;
31
+ info: string;
32
+ indent: number;
33
+ body: string;
34
+ // The line ending of the last line in `body`, which goes in front of the next one; null before the first.
35
+ ending: string | null;
36
+ // Markdown decodes an escape or a character reference in the language, so such a fence is left to it.
37
+ direct: boolean;
38
+ }
39
+
40
+ /* The outermost list item a line may still belong to. */
41
+ interface ListItem {
42
+ // The column its content starts at; a line less indented than that is not part of it.
43
+ column: number;
44
+ // The bullet, or the delimiter of a number, which a next item of the same list repeats.
45
+ kind: string;
46
+ // False once a line may or may not have ended it, which only markdown itself can tell.
47
+ certain: boolean;
48
+ // Nothing stands in it yet.
49
+ empty: boolean;
50
+ }
51
+
13
52
  const FENCE = /^([ \t]*)(`{3,}|~{3,})(.*)$/;
53
+ const CLOSING_FENCE = /^ {0,3}(`{3,}|~{3,})[ \t]*$/;
14
54
  // Spaces and tabs only: a line that holds a no-break space is text to markdown, not a blank line.
15
55
  const BLANK = /^[ \t]*$/;
16
56
  // A line that continues what stood before the blank line: an indented paragraph of a list item, or
17
57
  // the next item of a loose list. Splitting there would change what the markdown means.
18
58
  const CONTINUES = /^([ \t]|[-*+][ \t]|\d+[.)][ \t])/;
59
+ const LIST_MARKER = /^[ \t]*(?:([-*+])|(\d{1,9})([.)]))(?:([ \t]+)(.*))?$/;
60
+ // A list item with nothing after its marker.
61
+ const EMPTY_ITEM = /^ {0,3}(?:[-*+]|\d{1,9}[.)])[ \t]*$/;
62
+ const THEMATIC_BREAK = /^ {0,3}([-*_])(?:[ \t]*\1){2,}[ \t]*$/;
63
+ // A heading or a quote, which ends a list item instead of continuing its paragraph.
64
+ const ENDS_ITEM = /^ {0,3}(?:#{1,6}(?:[ \t]|$)|>)/;
65
+ const HTML_START = /^ {0,3}<[A-Za-z/!?]/;
66
+ // The HTML that runs on until a marker rather than a blank line, with that marker.
67
+ const HTML_UNTIL: [RegExp, RegExp][] = [
68
+ [/^ {0,3}<(?:pre|script|style|textarea)(?:[ \t>]|$)/i, /<\/(?:pre|script|style|textarea)>/i],
69
+ [/^ {0,3}<!--/, /-->/],
70
+ [/^ {0,3}<\?/, /\?>/],
71
+ [/^ {0,3}<![A-Za-z]/, />/],
72
+ [/^ {0,3}<!\[CDATA\[/, /\]\]>/]
73
+ ];
19
74
  // A reference or footnote definition may be used anywhere in the text, so a block cannot stand alone.
20
75
  const DEFINITION = /^ {0,3}\[[^\]]+\]:/m;
76
+ // Markdown ends a line at a lone carriage return too, which a split at line feeds would not see.
77
+ const LONE_RETURN = /\r(?!\n|$)/;
21
78
  // How much deeper than its opening a closing fence may sit; deeper than that it is a line of code.
22
79
  const CLOSING_INDENT = 3;
80
+ // From four columns in, a line is indented code rather than the start of a block.
81
+ const CODE_INDENT = 4;
82
+ const TAB_STOP = 4;
23
83
  // A section title: an ATX heading, or a line of only bold text, which models write as a heading too.
24
84
  const HEADING = /^ {0,3}#{1,6}(?:[ \t]|$)/;
25
85
  const BOLD_LINE = /^ {0,3}\*\*(?:[^*]|\*(?!\*))+\*\*:?[ \t]*$/;
26
86
 
27
87
  /*
28
88
  * A reply cut into blocks at blank lines outside a code fence, so each block parses on its own and
29
- * only the one still growing is parsed again. Joined back together the blocks are the text.
89
+ * only the one still growing is parsed again. A code fence at the top level is a block of its own,
90
+ * through its closing line and the blank lines under it, so it can be drawn without parsing at all.
91
+ * Joined back together the blocks are the text.
30
92
  */
31
93
  export function splitMarkdownBlocks(text: string): MarkdownBlock[] {
32
- if (DEFINITION.test(text)) {
94
+ if (DEFINITION.test(text) || LONE_RETURN.test(text)) {
33
95
  return [{ text, openFence: isFenceOpenAtEnd(text) }];
34
96
  }
35
97
  const lines = text.split('\n');
36
98
  const blocks: MarkdownBlock[] = [];
37
99
  let start = 0;
38
100
  let offset = 0;
39
- let fence: OpenFence | null = null;
101
+ // The fence as a reading line by line sees it, blind to lists and indented code. A blank line is
102
+ // only cut at where this reading agrees, so every such cut is one this split made before it
103
+ // followed fences any further.
104
+ let lineFence: OpenFence | null = null;
40
105
  let afterBlank = false;
106
+ // A fence inside a list item.
107
+ let fence: OpenFence | null = null;
108
+ let top: TopFence | null = null;
109
+ // A top-level fence that has closed; its block takes the blank lines under it.
110
+ let closed: { fence: TopFence; source: string } | null = null;
111
+ let item: ListItem | null = null;
112
+ // The marker that ends the raw HTML the line stands in.
113
+ let html: RegExp | null = null;
114
+ // Whether the top level is still known here. Past HTML, or a line that may or may not end a list
115
+ // item, a fence is left inside the block it stands in, which markdown parses as a whole.
116
+ let known = true;
117
+ // Where the line above starts when it is blank.
118
+ let blankAbove: number | null = null;
41
119
 
42
- lines.forEach((line, index) => {
43
- if (fence === null && afterBlank && !BLANK.test(line) && !CONTINUES.test(line)) {
44
- blocks.push({ text: text.slice(start, offset), openFence: false });
45
- start = offset;
120
+ const cutAt = (end: number): void => {
121
+ if (end > start) {
122
+ blocks.push({ text: text.slice(start, end), openFence: false });
46
123
  }
47
- fence = nextFence(fence, line);
48
- afterBlank = fence === null && BLANK.test(line) && index > 0;
124
+ start = end;
125
+ };
126
+
127
+ for (const [index, line] of lines.entries()) {
128
+ const at = offset;
49
129
  offset += line.length + 1;
50
- });
51
- blocks.push({ text: text.slice(start), openFence: fence !== null });
130
+ const content = withoutReturn(line);
131
+ const blank = BLANK.test(line);
132
+ // Blank to markdown, which a line ending in a carriage return is too.
133
+ const empty = BLANK.test(content);
134
+ const blankCut = lineFence === null && afterBlank && !blank && !CONTINUES.test(line);
135
+ lineFence = nextFence(lineFence, line);
136
+ afterBlank = lineFence === null && blank && index > 0;
137
+ if (top !== null) {
138
+ if (closesFence(top.marker, content)) {
139
+ closed = { fence: top, source: text.slice(top.from, at + content.length) };
140
+ top = null;
141
+ } else if (index < lines.length - 1 || line !== '') {
142
+ // The empty line after a line ending at the end of the text is no line of the fence.
143
+ top.body += (top.ending ?? '') + outdent(content, top.indent);
144
+ top.ending = line.length > content.length ? '\r\n' : '\n';
145
+ }
146
+ continue;
147
+ }
148
+ if (closed !== null) {
149
+ if (empty) {
150
+ continue;
151
+ }
152
+ blocks.push(fenceBlock(text.slice(start, at), closed.fence, closed.source, true));
153
+ start = at;
154
+ closed = null;
155
+ }
156
+ if (html !== null) {
157
+ // Raw HTML holds no fence and no blank line to cut at until its marker.
158
+ if (html.test(content)) {
159
+ html = null;
160
+ }
161
+ continue;
162
+ }
163
+ if (known && fence !== null && item !== null && !empty && columns(content) < item.column) {
164
+ // Code is no paragraph a line may continue lazily, so a line left of the item's content ends
165
+ // the item and its code. A next item of the same list keeps the list going.
166
+ fence = null;
167
+ if (!LIST_MARKER.test(content)) {
168
+ item = null;
169
+ }
170
+ }
171
+ if (blankCut && fence === null) {
172
+ cutAt(at);
173
+ }
174
+ const above = index > 0 ? withoutReturn(lines[index - 1]!) : '';
175
+ const opening = known && fence === null ? openingFence(content) : null;
176
+ if (opening !== null && EMPTY_ITEM.test(above)) {
177
+ // Markdown reads an empty item right above a fence as a list or as text depending on what
178
+ // stands before it, so the two stay in one block.
179
+ known = false;
180
+ } else if (opening !== null && opening.indent < CODE_INDENT && (item === null || opening.indent < item.column)) {
181
+ // The blank line right above goes with the fence. Code in a list item this fence ends takes
182
+ // every blank line under it but the last, and at the end of a block it would take them all.
183
+ cutAt(blankAbove !== null && blankAbove > start ? blankAbove : at);
184
+ // Three columns at most, so the indentation is spaces.
185
+ top = { ...opening, from: at + opening.indent, body: '', ending: null };
186
+ item = null;
187
+ blankAbove = null;
188
+ continue;
189
+ }
190
+ if (known && item?.empty === true && empty) {
191
+ // An item with nothing on its first line ends at a blank line right under it.
192
+ item = null;
193
+ }
194
+ if (known && fence === null && !empty) {
195
+ // A fence in an item is followed only while the item is known to be open, and less than four
196
+ // columns deeper than its content: deeper it is code of the item, or of an item inside it.
197
+ if (opening !== null && item !== null && (!item.certain || opening.indent < item.column || opening.indent >= item.column + CODE_INDENT)) {
198
+ known = false;
199
+ } else if (opening === null) {
200
+ item = nextItem(item, content, index > 0 && !BLANK.test(above));
201
+ }
202
+ }
203
+ if (fence === null && HTML_START.test(content)) {
204
+ known = false;
205
+ const until = HTML_UNTIL.find(([begins]) => begins.test(content))?.[1];
206
+ html = until !== undefined && !until.test(content) ? until : null;
207
+ }
208
+ if (known && (fence !== null || item !== null)) {
209
+ fence = nextFence(fence, fence === null ? itemContent(content) : content);
210
+ }
211
+ if (item?.empty === true && !empty && columns(content) >= item.column) {
212
+ item = { ...item, empty: false };
213
+ }
214
+ blankAbove = empty ? at : null;
215
+ }
216
+ if (top !== null) {
217
+ blocks.push(fenceBlock(text.slice(start), top, text.slice(top.from), false));
218
+ } else if (closed !== null) {
219
+ blocks.push(fenceBlock(text.slice(start), closed.fence, closed.source, true));
220
+ } else {
221
+ blocks.push({ text: text.slice(start), openFence: (known ? fence : lineFence) !== null });
222
+ }
52
223
  return blocks;
53
224
  }
54
225
 
@@ -117,7 +288,124 @@ function nextFence(fence: OpenFence | null, line: string): OpenFence | null {
117
288
  function isFenceOpenAtEnd(text: string): boolean {
118
289
  let fence: OpenFence | null = null;
119
290
  for (const line of text.split('\n')) {
120
- fence = nextFence(fence, line);
291
+ fence = nextFence(fence, withoutReturn(line));
121
292
  }
122
293
  return fence !== null;
123
294
  }
295
+
296
+ /* The fence a line opens wherever it stands, with its indentation in columns. */
297
+ function openingFence(content: string): Pick<TopFence, 'marker' | 'info' | 'indent' | 'direct'> | null {
298
+ const match = FENCE.exec(content);
299
+ if (!match) {
300
+ return null;
301
+ }
302
+ const marker = match[2]!;
303
+ const info = match[3]!.replace(/^[ \t]+|[ \t]+$/g, '');
304
+ // A backtick fence's info string may not hold a backtick, or the line is inline code.
305
+ if (marker[0] === '`' && info.includes('`')) {
306
+ return null;
307
+ }
308
+ return { marker, info, indent: columns(content), direct: !/[\\&]/.test(info.split(/[ \t]/, 1)[0]!) };
309
+ }
310
+
311
+ /* A line without the carriage return of a CRLF line ending. */
312
+ function withoutReturn(line: string): string {
313
+ return line.endsWith('\r') ? line.slice(0, -1) : line;
314
+ }
315
+
316
+ function closesFence(marker: string, content: string): boolean {
317
+ const run = CLOSING_FENCE.exec(content)?.[1];
318
+ return run !== undefined && run[0] === marker[0] && run.length >= marker.length;
319
+ }
320
+
321
+ function fenceBlock(text: string, fence: TopFence, source: string, closed: boolean): MarkdownBlock {
322
+ if (!fence.direct) {
323
+ return { text, openFence: !closed };
324
+ }
325
+ return { text, openFence: !closed, fence: { marker: fence.marker, info: fence.info, body: fence.body, closed, source } };
326
+ }
327
+
328
+ /* The list item a line that is neither blank nor a fence leaves open, as far as a fence needs to know. */
329
+ function nextItem(item: ListItem | null, content: string, underLine: boolean): ListItem | null {
330
+ const indent = columns(content);
331
+ if (item !== null && indent >= item.column) {
332
+ return item;
333
+ }
334
+ if (THEMATIC_BREAK.test(content) || ENDS_ITEM.test(content)) {
335
+ return null;
336
+ }
337
+ const marker = indent < CODE_INDENT ? itemMarker(content) : null;
338
+ if (marker === null) {
339
+ if (item === null || !underLine) {
340
+ return null;
341
+ }
342
+ // A line right under an item may continue its paragraph, or follow an item that ends in something else.
343
+ return { ...item, certain: false };
344
+ }
345
+ // Right under another line, only a bullet or a 1 with text after it surely starts a list; anything
346
+ // else may be text of a paragraph above.
347
+ const certain = !underLine || (item !== null && item.certain && item.kind === marker.kind) || (marker.first && marker.rest !== '');
348
+ return { column: marker.column, kind: marker.kind, certain, empty: marker.rest === '' };
349
+ }
350
+
351
+ /* The marker a list item starts with: its kind, whether it may start a list under text, and the column its content starts at. */
352
+ function itemMarker(content: string): { kind: string; first: boolean; column: number; rest: string } | null {
353
+ const match = LIST_MARKER.exec(content);
354
+ if (match === null) {
355
+ return null;
356
+ }
357
+ const [, bullet, number, delimiter, spacing = '', rest = ''] = match;
358
+ const indent = columns(content);
359
+ const width = bullet !== undefined ? 1 : number!.length + 1;
360
+ // The content starts after the spaces behind the marker, unless there are five or more: then the
361
+ // item starts with indented code one column after the marker.
362
+ const gap = rest === '' ? 1 : columns(spacing, indent + width);
363
+ const column = indent + width + (gap > CODE_INDENT ? 1 : gap);
364
+ return { kind: bullet ?? delimiter!, first: bullet !== undefined || Number(number) === 1, column, rest };
365
+ }
366
+
367
+ /* A list item's line with its marker as spaces, so a fence on that line is seen where the item's content starts. */
368
+ function itemContent(content: string): string {
369
+ const marker = itemMarker(content);
370
+ return marker === null ? content : ' '.repeat(marker.column) + marker.rest;
371
+ }
372
+
373
+ /* The columns of the indentation of a line, which starts at column `from`; a tab reaches to the next stop of four. */
374
+ function columns(line: string, from = 0): number {
375
+ let column = from;
376
+ for (const character of line) {
377
+ if (character === '\t') {
378
+ column += TAB_STOP - (column % TAB_STOP);
379
+ } else if (character === ' ') {
380
+ column += 1;
381
+ } else {
382
+ break;
383
+ }
384
+ }
385
+ return column - from;
386
+ }
387
+
388
+ /*
389
+ * A line of a fence less up to `indent` columns of indentation. A tab reaches to the next stop of
390
+ * four, and what of it lies past `indent` stays as spaces, as markdown reads it.
391
+ */
392
+ function outdent(line: string, indent: number): string {
393
+ let column = 0;
394
+ let index = 0;
395
+ while (column < indent && index < line.length) {
396
+ const character = line[index];
397
+ if (character === '\t') {
398
+ const stop = column + TAB_STOP - (column % TAB_STOP);
399
+ if (stop > indent) {
400
+ return ' '.repeat(stop - indent) + line.slice(index + 1);
401
+ }
402
+ column = stop;
403
+ } else if (character === ' ') {
404
+ column += 1;
405
+ } else {
406
+ break;
407
+ }
408
+ index += 1;
409
+ }
410
+ return line.slice(index);
411
+ }