@vertesia/ui 1.5.0-dev.20260722.120446Z → 1.5.0-dev.20260725.083715Z

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 (307) hide show
  1. package/lib/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
  2. package/lib/core/components/shadcn/filters/filterBar.js +1 -1
  3. package/lib/core/components/shadcn/filters/filterBar.js.map +1 -1
  4. package/lib/core/components/shadcn/modal/dialog.d.ts +1 -1
  5. package/lib/core/components/shadcn/modal/dialog.d.ts.map +1 -1
  6. package/lib/core/components/shadcn/modal/dialog.js +2 -0
  7. package/lib/core/components/shadcn/modal/dialog.js.map +1 -1
  8. package/lib/core/components/shadcn/tabs.d.ts +12 -1
  9. package/lib/core/components/shadcn/tabs.d.ts.map +1 -1
  10. package/lib/core/components/shadcn/tabs.js +12 -4
  11. package/lib/core/components/shadcn/tabs.js.map +1 -1
  12. package/lib/features/agent/chat/AgentRightPanel.d.ts +5 -1
  13. package/lib/features/agent/chat/AgentRightPanel.d.ts.map +1 -1
  14. package/lib/features/agent/chat/AgentRightPanel.js +2 -2
  15. package/lib/features/agent/chat/AgentRightPanel.js.map +1 -1
  16. package/lib/features/agent/chat/ArtifactsTab.d.ts +5 -1
  17. package/lib/features/agent/chat/ArtifactsTab.d.ts.map +1 -1
  18. package/lib/features/agent/chat/ArtifactsTab.js +48 -11
  19. package/lib/features/agent/chat/ArtifactsTab.js.map +1 -1
  20. package/lib/features/agent/chat/DocumentEditingActionCard.d.ts +6 -0
  21. package/lib/features/agent/chat/DocumentEditingActionCard.d.ts.map +1 -0
  22. package/lib/features/agent/chat/DocumentEditingActionCard.js +129 -0
  23. package/lib/features/agent/chat/DocumentEditingActionCard.js.map +1 -0
  24. package/lib/features/agent/chat/ModernAgentConversation.d.ts +19 -1
  25. package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  26. package/lib/features/agent/chat/ModernAgentConversation.js +125 -27
  27. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  28. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.d.ts +45 -0
  29. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.d.ts.map +1 -0
  30. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.js +40 -0
  31. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.js.map +1 -0
  32. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +3 -1
  33. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  34. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +94 -49
  35. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  36. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts +2 -1
  37. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts.map +1 -1
  38. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js +9 -5
  39. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js.map +1 -1
  40. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts +25 -0
  41. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts.map +1 -0
  42. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js +33 -0
  43. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js.map +1 -0
  44. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts +3 -1
  45. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
  46. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +22 -24
  47. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
  48. package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.d.ts +14 -0
  49. package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.d.ts.map +1 -0
  50. package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.js +21 -0
  51. package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.js.map +1 -0
  52. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.d.ts +30 -0
  53. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.d.ts.map +1 -0
  54. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.js +53 -0
  55. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.js.map +1 -0
  56. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.d.ts.map +1 -1
  57. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js +21 -2
  58. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js.map +1 -1
  59. package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.d.ts +6 -0
  60. package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.d.ts.map +1 -0
  61. package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.js +29 -0
  62. package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.js.map +1 -0
  63. package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.d.ts +33 -0
  64. package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.d.ts.map +1 -0
  65. package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.js +129 -0
  66. package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.js.map +1 -0
  67. package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts +7 -0
  68. package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts.map +1 -1
  69. package/lib/features/agent/chat/ModernAgentOutput/utils.js +11 -1
  70. package/lib/features/agent/chat/ModernAgentOutput/utils.js.map +1 -1
  71. package/lib/features/agent/chat/hooks/useAgentStream.d.ts +1 -1
  72. package/lib/features/agent/chat/hooks/useAgentStream.d.ts.map +1 -1
  73. package/lib/features/agent/chat/hooks/useAgentStream.js +24 -9
  74. package/lib/features/agent/chat/hooks/useAgentStream.js.map +1 -1
  75. package/lib/features/agent/chat/index.d.ts +2 -1
  76. package/lib/features/agent/chat/index.d.ts.map +1 -1
  77. package/lib/features/agent/chat/index.js +3 -0
  78. package/lib/features/agent/chat/index.js.map +1 -1
  79. package/lib/features/store/objects/DocumentSearchResults.js +2 -2
  80. package/lib/features/store/objects/DocumentSearchResults.js.map +1 -1
  81. package/lib/features/store/objects/components/ContentOverview.d.ts +2 -1
  82. package/lib/features/store/objects/components/ContentOverview.d.ts.map +1 -1
  83. package/lib/features/store/objects/components/ContentOverview.js +86 -15
  84. package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
  85. package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.d.ts +16 -0
  86. package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.d.ts.map +1 -0
  87. package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.js +116 -0
  88. package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.js.map +1 -0
  89. package/lib/features/store/objects/components/DocumentEditingLockStatus.d.ts +7 -0
  90. package/lib/features/store/objects/components/DocumentEditingLockStatus.d.ts.map +1 -0
  91. package/lib/features/store/objects/components/DocumentEditingLockStatus.js +13 -0
  92. package/lib/features/store/objects/components/DocumentEditingLockStatus.js.map +1 -0
  93. package/lib/features/store/objects/components/DocumentEditingPanel.d.ts +48 -0
  94. package/lib/features/store/objects/components/DocumentEditingPanel.d.ts.map +1 -0
  95. package/lib/features/store/objects/components/DocumentEditingPanel.js +720 -0
  96. package/lib/features/store/objects/components/DocumentEditingPanel.js.map +1 -0
  97. package/lib/features/store/objects/components/TextEditorPanel.d.ts.map +1 -1
  98. package/lib/features/store/objects/components/TextEditorPanel.js +22 -9
  99. package/lib/features/store/objects/components/TextEditorPanel.js.map +1 -1
  100. package/lib/features/store/objects/components/documentArtifactRefs.d.ts +26 -0
  101. package/lib/features/store/objects/components/documentArtifactRefs.d.ts.map +1 -0
  102. package/lib/features/store/objects/components/documentArtifactRefs.js +52 -0
  103. package/lib/features/store/objects/components/documentArtifactRefs.js.map +1 -0
  104. package/lib/features/store/objects/components/documentEditingAgentConfig.d.ts +4 -0
  105. package/lib/features/store/objects/components/documentEditingAgentConfig.d.ts.map +1 -0
  106. package/lib/features/store/objects/components/documentEditingAgentConfig.js +40 -0
  107. package/lib/features/store/objects/components/documentEditingAgentConfig.js.map +1 -0
  108. package/lib/features/store/objects/components/documentEditingPrompts.d.ts +3 -0
  109. package/lib/features/store/objects/components/documentEditingPrompts.d.ts.map +1 -0
  110. package/lib/features/store/objects/components/documentEditingPrompts.js +13 -0
  111. package/lib/features/store/objects/components/documentEditingPrompts.js.map +1 -0
  112. package/lib/features/store/objects/components/documentEditingRun.d.ts +27 -0
  113. package/lib/features/store/objects/components/documentEditingRun.d.ts.map +1 -0
  114. package/lib/features/store/objects/components/documentEditingRun.js +126 -0
  115. package/lib/features/store/objects/components/documentEditingRun.js.map +1 -0
  116. package/lib/features/store/objects/components/documentEditingTarget.d.ts +20 -0
  117. package/lib/features/store/objects/components/documentEditingTarget.d.ts.map +1 -0
  118. package/lib/features/store/objects/components/documentEditingTarget.js +40 -0
  119. package/lib/features/store/objects/components/documentEditingTarget.js.map +1 -0
  120. package/lib/features/store/objects/components/index.d.ts +1 -0
  121. package/lib/features/store/objects/components/index.d.ts.map +1 -1
  122. package/lib/features/store/objects/components/index.js +1 -0
  123. package/lib/features/store/objects/components/index.js.map +1 -1
  124. package/lib/features/store/objects/components/useContentPanelHooks.d.ts +5 -1
  125. package/lib/features/store/objects/components/useContentPanelHooks.d.ts.map +1 -1
  126. package/lib/features/store/objects/components/useContentPanelHooks.js +53 -29
  127. package/lib/features/store/objects/components/useContentPanelHooks.js.map +1 -1
  128. package/lib/features/store/objects/components/useDownloadFile.d.ts +2 -1
  129. package/lib/features/store/objects/components/useDownloadFile.d.ts.map +1 -1
  130. package/lib/features/store/objects/components/useDownloadFile.js +3 -0
  131. package/lib/features/store/objects/components/useDownloadFile.js.map +1 -1
  132. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +1 -1
  133. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js.map +1 -1
  134. package/lib/i18n/locales/ar.json +118 -1
  135. package/lib/i18n/locales/de.json +118 -1
  136. package/lib/i18n/locales/en.json +118 -0
  137. package/lib/i18n/locales/es.json +118 -1
  138. package/lib/i18n/locales/fr.json +118 -1
  139. package/lib/i18n/locales/it.json +118 -1
  140. package/lib/i18n/locales/ja.json +118 -1
  141. package/lib/i18n/locales/ko.json +118 -1
  142. package/lib/i18n/locales/pt.json +118 -1
  143. package/lib/i18n/locales/ru.json +118 -1
  144. package/lib/i18n/locales/tr.json +118 -1
  145. package/lib/i18n/locales/zh-TW.json +118 -1
  146. package/lib/i18n/locales/zh.json +118 -1
  147. package/lib/rich-text/EditorToolbar.d.ts +37 -0
  148. package/lib/rich-text/EditorToolbar.d.ts.map +1 -0
  149. package/lib/rich-text/EditorToolbar.js +215 -0
  150. package/lib/rich-text/EditorToolbar.js.map +1 -0
  151. package/lib/rich-text/VertesiaRichTextEditor.d.ts +26 -0
  152. package/lib/rich-text/VertesiaRichTextEditor.d.ts.map +1 -0
  153. package/lib/rich-text/VertesiaRichTextEditor.js +67 -0
  154. package/lib/rich-text/VertesiaRichTextEditor.js.map +1 -0
  155. package/lib/rich-text/index.d.ts +2 -0
  156. package/lib/rich-text/index.d.ts.map +1 -0
  157. package/lib/rich-text/index.js +2 -0
  158. package/lib/rich-text/index.js.map +1 -0
  159. package/lib/vertesia-ui-core.js +2 -2
  160. package/lib/vertesia-ui-core.js.map +1 -1
  161. package/lib/vertesia-ui-features.js +35 -30
  162. package/lib/vertesia-ui-features.js.map +1 -1
  163. package/lib/vertesia-ui-i18n.js +14 -14
  164. package/lib/vertesia-ui-i18n.js.map +1 -1
  165. package/lib/vertesia-ui-rich-text.js +5 -0
  166. package/lib/vertesia-ui-rich-text.js.map +1 -0
  167. package/lib/vertesia-ui-widgets.js +25 -7
  168. package/lib/vertesia-ui-widgets.js.map +1 -1
  169. package/lib/widgets/markdown/AgentResourceResolver.d.ts +31 -0
  170. package/lib/widgets/markdown/AgentResourceResolver.d.ts.map +1 -0
  171. package/lib/widgets/markdown/AgentResourceResolver.js +39 -0
  172. package/lib/widgets/markdown/AgentResourceResolver.js.map +1 -0
  173. package/lib/widgets/markdown/ArtifactEditingSurface.d.ts +63 -0
  174. package/lib/widgets/markdown/ArtifactEditingSurface.d.ts.map +1 -0
  175. package/lib/widgets/markdown/ArtifactEditingSurface.js +582 -0
  176. package/lib/widgets/markdown/ArtifactEditingSurface.js.map +1 -0
  177. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.d.ts +69 -0
  178. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.d.ts.map +1 -0
  179. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.js +374 -0
  180. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.js.map +1 -0
  181. package/lib/widgets/markdown/MarkdownLink.d.ts +3 -1
  182. package/lib/widgets/markdown/MarkdownLink.d.ts.map +1 -1
  183. package/lib/widgets/markdown/MarkdownLink.js +41 -14
  184. package/lib/widgets/markdown/MarkdownLink.js.map +1 -1
  185. package/lib/widgets/markdown/MarkdownRenderer.d.ts +8 -1
  186. package/lib/widgets/markdown/MarkdownRenderer.d.ts.map +1 -1
  187. package/lib/widgets/markdown/MarkdownRenderer.js +20 -4
  188. package/lib/widgets/markdown/MarkdownRenderer.js.map +1 -1
  189. package/lib/widgets/markdown/index.d.ts +5 -1
  190. package/lib/widgets/markdown/index.d.ts.map +1 -1
  191. package/lib/widgets/markdown/index.js +5 -1
  192. package/lib/widgets/markdown/index.js.map +1 -1
  193. package/lib/widgets/markdown/normalizeCustomSchemeLinks.d.ts.map +1 -1
  194. package/lib/widgets/markdown/normalizeCustomSchemeLinks.js +14 -2
  195. package/lib/widgets/markdown/normalizeCustomSchemeLinks.js.map +1 -1
  196. package/lib/widgets/markdown/textDiff.d.ts +52 -0
  197. package/lib/widgets/markdown/textDiff.d.ts.map +1 -0
  198. package/lib/widgets/markdown/textDiff.js +362 -0
  199. package/lib/widgets/markdown/textDiff.js.map +1 -0
  200. package/lib/widgets/markdown/useArtifactContent.d.ts +5 -3
  201. package/lib/widgets/markdown/useArtifactContent.d.ts.map +1 -1
  202. package/lib/widgets/markdown/useArtifactContent.js +6 -3
  203. package/lib/widgets/markdown/useArtifactContent.js.map +1 -1
  204. package/lib/widgets/markdown/useResolvedUrl.d.ts +1 -5
  205. package/lib/widgets/markdown/useResolvedUrl.d.ts.map +1 -1
  206. package/lib/widgets/markdown/useResolvedUrl.js +19 -31
  207. package/lib/widgets/markdown/useResolvedUrl.js.map +1 -1
  208. package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts +9 -0
  209. package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts.map +1 -1
  210. package/lib/widgets/xml-viewer/hooks/useXMLViewer.js +20 -17
  211. package/lib/widgets/xml-viewer/hooks/useXMLViewer.js.map +1 -1
  212. package/package.json +13 -9
  213. package/src/__tests__/a11y.test.tsx +4 -1
  214. package/src/__tests__/axe-helper.ts +17 -7
  215. package/src/__tests__/setup.ts +25 -0
  216. package/src/core/components/shadcn/filters/filterBar.tsx +1 -0
  217. package/src/core/components/shadcn/modal/dialog.tsx +3 -1
  218. package/src/core/components/shadcn/tabs.test.tsx +65 -0
  219. package/src/core/components/shadcn/tabs.tsx +25 -3
  220. package/src/features/agent/chat/AgentRightPanel.tsx +16 -1
  221. package/src/features/agent/chat/ArtifactsTab.test.tsx +55 -0
  222. package/src/features/agent/chat/ArtifactsTab.tsx +203 -52
  223. package/src/features/agent/chat/DocumentEditingActionCard.test.tsx +105 -0
  224. package/src/features/agent/chat/DocumentEditingActionCard.tsx +210 -0
  225. package/src/features/agent/chat/ModernAgentConversation.test.tsx +237 -40
  226. package/src/features/agent/chat/ModernAgentConversation.tsx +247 -75
  227. package/src/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.test.tsx +79 -0
  228. package/src/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.tsx +94 -0
  229. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +257 -34
  230. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +217 -105
  231. package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.tsx +17 -0
  232. package/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.test.tsx +44 -0
  233. package/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.tsx +88 -0
  234. package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +30 -46
  235. package/src/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.tsx +40 -0
  236. package/src/features/agent/chat/ModernAgentOutput/ResourceChip.test.tsx +148 -0
  237. package/src/features/agent/chat/ModernAgentOutput/ResourceChip.tsx +141 -0
  238. package/src/features/agent/chat/ModernAgentOutput/ToolCallGroup.tsx +33 -1
  239. package/src/features/agent/chat/ModernAgentOutput/resourceLinks.tsx +43 -0
  240. package/src/features/agent/chat/ModernAgentOutput/resourceSummary.test.ts +229 -0
  241. package/src/features/agent/chat/ModernAgentOutput/resourceSummary.ts +152 -0
  242. package/src/features/agent/chat/ModernAgentOutput/utils.test.ts +17 -0
  243. package/src/features/agent/chat/ModernAgentOutput/utils.ts +14 -1
  244. package/src/features/agent/chat/hooks/useAgentStream.test.tsx +188 -0
  245. package/src/features/agent/chat/hooks/useAgentStream.ts +28 -8
  246. package/src/features/agent/chat/index.ts +11 -0
  247. package/src/features/store/objects/DocumentSearchResults.tsx +2 -2
  248. package/src/features/store/objects/components/ContentOverview.tsx +216 -65
  249. package/src/features/store/objects/components/DocumentEditingConfigurationSelector.test.ts +34 -0
  250. package/src/features/store/objects/components/DocumentEditingConfigurationSelector.tsx +211 -0
  251. package/src/features/store/objects/components/DocumentEditingLockStatus.test.tsx +32 -0
  252. package/src/features/store/objects/components/DocumentEditingLockStatus.tsx +45 -0
  253. package/src/features/store/objects/components/DocumentEditingPanel.test.tsx +336 -0
  254. package/src/features/store/objects/components/DocumentEditingPanel.tsx +1236 -0
  255. package/src/features/store/objects/components/TextEditorPanel.test.tsx +48 -0
  256. package/src/features/store/objects/components/TextEditorPanel.tsx +41 -20
  257. package/src/features/store/objects/components/documentArtifactRefs.test.ts +65 -0
  258. package/src/features/store/objects/components/documentArtifactRefs.ts +72 -0
  259. package/src/features/store/objects/components/documentEditingAgentConfig.test.ts +28 -0
  260. package/src/features/store/objects/components/documentEditingAgentConfig.ts +41 -0
  261. package/src/features/store/objects/components/documentEditingPrompts.test.ts +26 -0
  262. package/src/features/store/objects/components/documentEditingPrompts.ts +12 -0
  263. package/src/features/store/objects/components/documentEditingRun.test.ts +176 -0
  264. package/src/features/store/objects/components/documentEditingRun.ts +184 -0
  265. package/src/features/store/objects/components/documentEditingTarget.test.ts +96 -0
  266. package/src/features/store/objects/components/documentEditingTarget.ts +63 -0
  267. package/src/features/store/objects/components/index.ts +1 -0
  268. package/src/features/store/objects/components/useContentPanelHooks.ts +52 -27
  269. package/src/features/store/objects/components/useDownloadFile.test.ts +20 -0
  270. package/src/features/store/objects/components/useDownloadFile.ts +5 -1
  271. package/src/features/store/objects/selection/actions/StartWorkflowButton.tsx +2 -2
  272. package/src/i18n/locales/ar.json +118 -1
  273. package/src/i18n/locales/de.json +118 -1
  274. package/src/i18n/locales/en.json +118 -0
  275. package/src/i18n/locales/es.json +118 -1
  276. package/src/i18n/locales/fr.json +118 -1
  277. package/src/i18n/locales/it.json +118 -1
  278. package/src/i18n/locales/ja.json +118 -1
  279. package/src/i18n/locales/ko.json +118 -1
  280. package/src/i18n/locales/pt.json +118 -1
  281. package/src/i18n/locales/ru.json +118 -1
  282. package/src/i18n/locales/tr.json +118 -1
  283. package/src/i18n/locales/zh-TW.json +118 -1
  284. package/src/i18n/locales/zh.json +118 -1
  285. package/src/rich-text/EditorToolbar.tsx +708 -0
  286. package/src/rich-text/VertesiaRichTextEditor.test.tsx +211 -0
  287. package/src/rich-text/VertesiaRichTextEditor.tsx +241 -0
  288. package/src/rich-text/index.ts +9 -0
  289. package/src/session/UserSession.test.ts +11 -11
  290. package/src/widgets/markdown/AgentResourceResolver.test.ts +24 -0
  291. package/src/widgets/markdown/AgentResourceResolver.tsx +70 -0
  292. package/src/widgets/markdown/ArtifactEditingSurface.test.tsx +537 -0
  293. package/src/widgets/markdown/ArtifactEditingSurface.tsx +878 -0
  294. package/src/widgets/markdown/CollaborativeMarkdownRenderer.test.tsx +484 -0
  295. package/src/widgets/markdown/CollaborativeMarkdownRenderer.tsx +825 -0
  296. package/src/widgets/markdown/MarkdownLink.test.tsx +122 -0
  297. package/src/widgets/markdown/MarkdownLink.tsx +102 -17
  298. package/src/widgets/markdown/MarkdownRenderer.tsx +30 -3
  299. package/src/widgets/markdown/index.ts +39 -1
  300. package/src/widgets/markdown/normalizeCustomSchemeLinks.ts +15 -2
  301. package/src/widgets/markdown/textDiff.test.ts +156 -0
  302. package/src/widgets/markdown/textDiff.ts +399 -0
  303. package/src/widgets/markdown/useArtifactContent.ts +9 -3
  304. package/src/widgets/markdown/useResolvedUrl.test.ts +29 -40
  305. package/src/widgets/markdown/useResolvedUrl.ts +33 -35
  306. package/src/widgets/xml-viewer/hooks/useXMLViewer.test.ts +26 -0
  307. package/src/widgets/xml-viewer/hooks/useXMLViewer.ts +23 -18
@@ -0,0 +1,44 @@
1
+ /** @vitest-environment jsdom */
2
+
3
+ import { cleanup, fireEvent, render, screen } from '@testing-library/react';
4
+ import { afterEach, describe, expect, it } from 'vitest';
5
+ import { I18nProvider } from '../../../../i18n/index.js';
6
+ import { AGENT_LINE_CLAMP_CLASS, CollapsibleAgentMarkdown } from './CollapsibleAgentMarkdown.js';
7
+
8
+ afterEach(cleanup);
9
+
10
+ const renderMarkdown = (markdown: string, testId = 'prose') =>
11
+ render(
12
+ <I18nProvider lng="en">
13
+ <CollapsibleAgentMarkdown data-testid={testId}>{markdown}</CollapsibleAgentMarkdown>
14
+ </I18nProvider>,
15
+ );
16
+
17
+ describe('CollapsibleAgentMarkdown', () => {
18
+ it('renders markdown without a toggle when the content is short', () => {
19
+ renderMarkdown('A **short** note.');
20
+ expect(screen.getByText('short')).not.toBeNull();
21
+ expect(screen.queryByRole('button', { name: /Show more/ })).toBeNull();
22
+ });
23
+
24
+ it('renders a markdown table', () => {
25
+ renderMarkdown('| A | B |\n| - | - |\n| 1 | 2 |');
26
+ expect(screen.getByRole('table')).not.toBeNull();
27
+ });
28
+
29
+ it('collapses long content behind a show more / show less toggle', () => {
30
+ const long = Array.from({ length: 10 }, (_, i) => `Line number ${i + 1} of the reference block.`).join('\n\n');
31
+ renderMarkdown(long);
32
+
33
+ const prose = screen.getByTestId('prose');
34
+ const showMore = screen.getByRole('button', { name: /Show more/ });
35
+ expect(showMore.getAttribute('aria-expanded')).toBe('false');
36
+ expect(prose.getAttribute('class') ?? '').toContain(AGENT_LINE_CLAMP_CLASS.split(' ')[0]);
37
+
38
+ fireEvent.click(showMore);
39
+
40
+ const showLess = screen.getByRole('button', { name: /Show less/ });
41
+ expect(showLess.getAttribute('aria-expanded')).toBe('true');
42
+ expect(prose.getAttribute('class') ?? '').not.toContain('[-webkit-line-clamp:6]');
43
+ });
44
+ });
@@ -0,0 +1,88 @@
1
+ import { cn } from '@vertesia/ui/core';
2
+ import { useUITranslation } from '@vertesia/ui/i18n';
3
+ import { MarkdownRenderer, type MarkdownRendererProps } from '@vertesia/ui/widgets';
4
+ import { ChevronDown } from 'lucide-react';
5
+ import { useState } from 'react';
6
+
7
+ /** Prose styling used for agent/model message markdown, shared across the conversation UI. */
8
+ export const AGENT_PROSE_CLASS = [
9
+ 'agent-markdown vprose prose max-w-none break-words text-sm leading-6 text-foreground/80',
10
+ 'prose-p:my-2 prose-p:leading-6 prose-li:my-0.5 prose-pre:my-3 prose-headings:tracking-normal',
11
+ 'prose-headings:text-foreground prose-strong:text-foreground prose-code:text-foreground',
12
+ 'prose-a:text-foreground prose-a:underline prose-a:decoration-muted prose-a:underline-offset-4',
13
+ '[&_p]:text-foreground/80 [&_li]:text-foreground/80 [&_li::marker]:text-muted',
14
+ ].join(' ');
15
+
16
+ /** Clamp applied to collapsed markdown. Kept as a literal so Tailwind emits the utility. */
17
+ export const AGENT_LINE_CLAMP_CLASS =
18
+ '[display:-webkit-box] overflow-hidden [-webkit-box-orient:vertical] [-webkit-line-clamp:6]';
19
+
20
+ export const AGENT_COLLAPSE_LINES = 6;
21
+ export const AGENT_COLLAPSE_THRESHOLD = 520;
22
+
23
+ interface CollapsibleAgentMarkdownProps {
24
+ /** Raw markdown to render. */
25
+ children: string;
26
+ artifactRunId?: string;
27
+ components?: MarkdownRendererProps['components'];
28
+ /** Disable the show more/less affordance regardless of length. */
29
+ disableCollapse?: boolean;
30
+ className?: string;
31
+ 'data-testid'?: string;
32
+ }
33
+
34
+ /**
35
+ * Renders agent/model markdown with the shared prose styling and, when the content is long,
36
+ * collapses it to a few lines behind a "Show more" / "Show less" toggle. Mirrors the treatment
37
+ * used for summary thought prose so edit/comment cards read like ordinary model messages.
38
+ */
39
+ export function CollapsibleAgentMarkdown({
40
+ children,
41
+ artifactRunId,
42
+ components,
43
+ disableCollapse,
44
+ className,
45
+ 'data-testid': dataTestId,
46
+ }: CollapsibleAgentMarkdownProps) {
47
+ const { t } = useUITranslation();
48
+ const normalizedText = children.trim();
49
+ const [isExpanded, setIsExpanded] = useState(false);
50
+ const explicitLineCount = normalizedText ? normalizedText.split(/\r?\n/).length : 0;
51
+ const isLong = normalizedText.length > AGENT_COLLAPSE_THRESHOLD || explicitLineCount > AGENT_COLLAPSE_LINES;
52
+ const collapsed = isLong && !disableCollapse && !isExpanded;
53
+
54
+ return (
55
+ <div className={cn('min-w-0', className)}>
56
+ <div
57
+ data-testid={dataTestId}
58
+ className={cn(AGENT_PROSE_CLASS, collapsed && AGENT_LINE_CLAMP_CLASS)}
59
+ style={{ overflowWrap: 'anywhere' }}
60
+ >
61
+ <MarkdownRenderer artifactRunId={artifactRunId} components={components}>
62
+ {children}
63
+ </MarkdownRenderer>
64
+ </div>
65
+ {isLong && !disableCollapse ? (
66
+ <div className="mt-1.5 flex justify-end">
67
+ <button
68
+ type="button"
69
+ aria-expanded={isExpanded}
70
+ className={cn(
71
+ 'inline-flex items-center gap-1 text-sm font-medium text-muted transition-colors',
72
+ '[text-decoration:none] hover:text-foreground hover:[text-decoration:none]',
73
+ 'focus-visible:text-foreground focus-visible:[text-decoration:none]',
74
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
75
+ )}
76
+ onClick={() => setIsExpanded((current) => !current)}
77
+ >
78
+ {isExpanded ? t('agent.showLess') : t('agent.showMore')}
79
+ <ChevronDown
80
+ className={cn('size-4 transition-transform', isExpanded && 'rotate-180')}
81
+ aria-hidden="true"
82
+ />
83
+ </button>
84
+ </div>
85
+ ) : null}
86
+ </div>
87
+ );
88
+ }
@@ -6,7 +6,7 @@ import {
6
6
  } from '@vertesia/common';
7
7
  import { Badge, Button, cn, Dropdown, MenuItem, useToast } from '@vertesia/ui/core';
8
8
  import { useUITranslation } from '@vertesia/ui/i18n';
9
- import { NavLink, useRouterContext } from '@vertesia/ui/router';
9
+ import { useRouterContext } from '@vertesia/ui/router';
10
10
  import { useUserSession } from '@vertesia/ui/session';
11
11
  import { MarkdownRenderer } from '@vertesia/ui/widgets';
12
12
  import dayjs from 'dayjs';
@@ -29,9 +29,11 @@ import { memo, useEffect, useMemo, useRef, useState } from 'react';
29
29
  import { useDownloadFile } from '../../../store/objects/components/useDownloadFile.js';
30
30
  import { PulsatingCircle } from '../AnimatedThinkingDots';
31
31
  import { AskUserWidget } from '../AskUserWidget';
32
+ import { DocumentEditingActionCard, parseMarkdownEditingAction } from '../DocumentEditingActionCard.js';
32
33
  import { useImageLightbox } from '../ImageLightbox';
33
34
  import { getArtifactCacheKey, useArtifactUrlCache } from '../useArtifactUrlCache.js';
34
35
  import { ThinkingMessages } from '../WaitingMessages';
36
+ import { createAgentMarkdownAnchor } from './AgentMarkdownAnchor';
35
37
  import { AttachmentPreviewList, parseUserMessageAttachments } from './AttachmentPreview';
36
38
  import { MessageDeliveryStatus } from './MessageDeliveryStatus';
37
39
  import { processContentForMarkdown } from './processContentForMarkdown';
@@ -108,6 +110,8 @@ export interface MessageItemProps extends MessageItemClassNames {
108
110
  showPulsatingCircle?: boolean;
109
111
  /** Callback when user sends a message (e.g., from proposal selection) */
110
112
  onSendMessage?: (message: string, metadata?: Record<string, unknown>) => void;
113
+ /** Open a Markdown artifact in the surrounding agent panel. */
114
+ onOpenArtifact?: (path: string) => void;
111
115
  /** Whether a REQUEST_INPUT message has already been answered by a later user message */
112
116
  requestInputAnswered?: boolean;
113
117
  /** Sparse per-type overrides for MESSAGE_STYLES (deep-merged with defaults) */
@@ -197,6 +201,7 @@ function MessageItemComponent({
197
201
  message,
198
202
  showPulsatingCircle = false,
199
203
  onSendMessage,
204
+ onOpenArtifact,
200
205
  requestInputAnswered = false,
201
206
  className,
202
207
  cardClassName,
@@ -304,6 +309,13 @@ function MessageItemComponent({
304
309
 
305
310
  const visibleMessageContent = parsedUserAttachments?.body ?? messageContent;
306
311
  const messageAttachments = parsedUserAttachments?.attachments ?? [];
312
+ const editingAction = useMemo(
313
+ () =>
314
+ message.type === AgentMessageType.QUESTION
315
+ ? parseMarkdownEditingAction(message.details?.editing_action)
316
+ : undefined,
317
+ [message.details, message.type],
318
+ );
307
319
 
308
320
  // PERFORMANCE: Memoize processed content - expensive regex operations only run when messageContent changes
309
321
  const processedContent = useMemo(() => {
@@ -367,52 +379,13 @@ function MessageItemComponent({
367
379
  // PERFORMANCE: Memoize markdown components to prevent MarkdownRenderer remounts
368
380
  const markdownComponents = useMemo(
369
381
  () => ({
370
- a: ({
371
- node,
372
- ref,
373
- ...props
374
- }: {
375
- node?: unknown;
376
- ref?: unknown;
377
- href?: string;
378
- children?: React.ReactNode;
379
- }) => {
380
- const href = props.href || '';
382
+ a: createAgentMarkdownAnchor({
383
+ StoreLinkComponent,
384
+ CollectionLinkComponent,
381
385
  // Carry the active account (`a`) & project (`p`) params on internal routes so
382
386
  // copy-link / open-in-new-tab preserve the current tenant.
383
- const withParams = href.startsWith('/') ? router.getTopRouter().navigator.addStickyParams(href) : href;
384
- if (href.includes('/store/objects')) {
385
- if (StoreLinkComponent) {
386
- const documentId = href.split('/store/objects/')[1] || '';
387
- return (
388
- <StoreLinkComponent href={withParams} documentId={documentId}>
389
- {props.children}
390
- </StoreLinkComponent>
391
- );
392
- }
393
- return (
394
- <NavLink href={withParams} topLevelNav>
395
- {props.children}
396
- </NavLink>
397
- );
398
- }
399
- if (href.includes('/store/collections')) {
400
- if (CollectionLinkComponent) {
401
- const collectionId = href.split('/store/collections/')[1] || '';
402
- return (
403
- <CollectionLinkComponent href={withParams} collectionId={collectionId}>
404
- {props.children}
405
- </CollectionLinkComponent>
406
- );
407
- }
408
- return (
409
- <NavLink href={withParams} topLevelNav>
410
- {props.children}
411
- </NavLink>
412
- );
413
- }
414
- return <a {...props} target="_blank" rel="noopener noreferrer" />;
415
- },
387
+ addStickyParams: (href: string) => router.getTopRouter().navigator.addStickyParams(href),
388
+ }),
416
389
  img: ({ node, ref, ...props }: { node?: unknown; ref?: unknown; src?: string; alt?: string }) => {
417
390
  return (
418
391
  <Button
@@ -466,6 +439,7 @@ function MessageItemComponent({
466
439
  >
467
440
  <MarkdownRenderer
468
441
  artifactRunId={runId}
442
+ onArtifactOpen={onOpenArtifact}
469
443
  onProposalSelect={(optionId) => onSendMessage?.(optionId)}
470
444
  onProposalSubmit={(text) => onSendMessage?.(text)}
471
445
  components={markdownComponents}
@@ -662,6 +636,8 @@ function MessageItemComponent({
662
636
  compact
663
637
  answered={requestInputAnswered}
664
638
  />
639
+ ) : editingAction ? (
640
+ <DocumentEditingActionCard action={editingAction} />
665
641
  ) : (
666
642
  visibleMessageContent && (
667
643
  <div
@@ -677,6 +653,7 @@ function MessageItemComponent({
677
653
  <AttachmentPreviewList
678
654
  items={messageAttachments}
679
655
  artifactRunId={runId}
656
+ onOpenArtifact={onOpenArtifact}
680
657
  variant="message"
681
658
  className={cn(visibleMessageContent && 'mt-3')}
682
659
  StoreLinkComponent={StoreLinkComponent}
@@ -723,7 +700,13 @@ function MessageItemComponent({
723
700
  variant="outline"
724
701
  size="xs"
725
702
  className="px-2 py-1 text-xs"
726
- onClick={() => window.open(url, '_blank')}
703
+ onClick={() => {
704
+ if (/\.md$/i.test(artifactPath) && onOpenArtifact) {
705
+ onOpenArtifact(artifactPath);
706
+ return;
707
+ }
708
+ window.open(url, '_blank');
709
+ }}
727
710
  title={artifactPath}
728
711
  >
729
712
  {displayName}
@@ -790,6 +773,7 @@ const MessageItem = memo(MessageItemComponent, (prevProps, nextProps) => {
790
773
  prevProps.message.workflow_run_id === nextProps.message.workflow_run_id &&
791
774
  prevProps.showPulsatingCircle === nextProps.showPulsatingCircle &&
792
775
  prevProps.onSendMessage === nextProps.onSendMessage &&
776
+ prevProps.onOpenArtifact === nextProps.onOpenArtifact &&
793
777
  prevProps.requestInputAnswered === nextProps.requestInputAnswered &&
794
778
  prevProps.messageStyleOverrides === nextProps.messageStyleOverrides &&
795
779
  MESSAGE_ITEM_CLASS_NAME_KEYS.every((key) => prevProps[key] === nextProps[key])
@@ -0,0 +1,40 @@
1
+ import type { AgentResourceReference } from '@vertesia/common';
2
+ import { Button, cn } from '@vertesia/ui/core';
3
+ import { useUITranslation } from '@vertesia/ui/i18n';
4
+ import { useState } from 'react';
5
+ import { ResourceChipList } from './ResourceChip';
6
+
7
+ const COLLAPSE_THRESHOLD = 5;
8
+
9
+ interface ResourceChangeSummaryProps {
10
+ resources: AgentResourceReference[];
11
+ workflowRunId?: string;
12
+ className?: string;
13
+ }
14
+
15
+ /**
16
+ * End-of-turn summary listing every resource the assistant created/updated/deleted during the turn,
17
+ * as deep-linkable chips. Rendered once the turn is complete, from structured tool metadata rather
18
+ * than parsed prose. Collapses to the first few entries with a "view all" toggle when long.
19
+ */
20
+ export function ResourceChangeSummary({ resources, workflowRunId, className }: ResourceChangeSummaryProps) {
21
+ const { t } = useUITranslation();
22
+ const [expanded, setExpanded] = useState(false);
23
+
24
+ if (resources.length === 0) return null;
25
+
26
+ const collapsible = resources.length > COLLAPSE_THRESHOLD;
27
+ const visible = collapsible && !expanded ? resources.slice(0, COLLAPSE_THRESHOLD) : resources;
28
+
29
+ return (
30
+ <div className={cn('mx-auto w-full max-w-3xl py-2', className)} data-agent-resource-summary>
31
+ <div className="mb-2 text-xs font-medium text-muted">{t('agent.resourcesChanged')}</div>
32
+ <ResourceChipList resources={visible} workflowRunId={workflowRunId} />
33
+ {collapsible && (
34
+ <Button variant="outline" size="xs" className="mt-2" onClick={() => setExpanded((prev) => !prev)}>
35
+ {expanded ? t('agent.resourceShowLess') : t('agent.resourceViewAll', { total: resources.length })}
36
+ </Button>
37
+ )}
38
+ </div>
39
+ );
40
+ }
@@ -0,0 +1,148 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { cleanup, fireEvent, render, screen } from '@testing-library/react';
4
+ import type { AgentResourceReference } from '@vertesia/common';
5
+ import {
6
+ Boxes,
7
+ BrainCircuit,
8
+ Database,
9
+ FileType,
10
+ MessagesSquare,
11
+ PanelsTopLeft,
12
+ PlayCircle,
13
+ SquareTerminal,
14
+ Workflow,
15
+ } from 'lucide-react';
16
+ import type React from 'react';
17
+ import { afterEach, describe, expect, it, vi } from 'vitest';
18
+ import { I18nProvider } from '../../../../i18n/index.js';
19
+ import { ReactRouterContext, type RouterContext } from '../../../../router/index.js';
20
+ import { AgentResourceResolverProvider } from '../../../../widgets/markdown/AgentResourceResolver';
21
+ import { ResourceChangeSummary } from './ResourceChangeSummary';
22
+ import { ResourceChip } from './ResourceChip';
23
+ import { getResourceIcon } from './resourceLinks';
24
+
25
+ function makeRouterContext(): RouterContext {
26
+ return {
27
+ location: window.location,
28
+ route: { path: '/', Component: () => null },
29
+ params: {},
30
+ state: null,
31
+ matchedRoutePath: '/',
32
+ navigate: vi.fn(),
33
+ router: {
34
+ navigate: vi.fn(),
35
+ getTopRouter: () => ({ navigator: { addStickyParams: (href: string) => href } }),
36
+ },
37
+ } as unknown as RouterContext;
38
+ }
39
+
40
+ function renderWithProviders(ui: React.ReactNode) {
41
+ return render(
42
+ <I18nProvider lng="en">
43
+ <ReactRouterContext.Provider value={makeRouterContext()}>{ui}</ReactRouterContext.Provider>
44
+ </I18nProvider>,
45
+ );
46
+ }
47
+
48
+ const ref = (over: Partial<AgentResourceReference> = {}): AgentResourceReference => ({
49
+ type: 'document',
50
+ id: 'd1',
51
+ label: 'My Document',
52
+ action: 'created',
53
+ ...over,
54
+ });
55
+
56
+ afterEach(() => cleanup());
57
+
58
+ describe('ResourceChip', () => {
59
+ it('uses the same icons as the Studio navigation', () => {
60
+ expect(getResourceIcon('document')).toBe(Database);
61
+ expect(getResourceIcon('collection')).toBe(Boxes);
62
+ expect(getResourceIcon('content_type')).toBe(FileType);
63
+ expect(getResourceIcon('interaction')).toBe(MessagesSquare);
64
+ expect(getResourceIcon('prompt')).toBe(SquareTerminal);
65
+ expect(getResourceIcon('agent')).toBe(BrainCircuit);
66
+ expect(getResourceIcon('workflow')).toBe(Workflow);
67
+ expect(getResourceIcon('process')).toBe(Workflow);
68
+ expect(getResourceIcon('process_run')).toBe(PlayCircle);
69
+ expect(getResourceIcon('interaction_run')).toBe(PlayCircle);
70
+ expect(getResourceIcon('view')).toBe(PanelsTopLeft);
71
+ });
72
+
73
+ it('renders a navigable link supplied by the host resolver', () => {
74
+ renderWithProviders(
75
+ <AgentResourceResolverProvider value={() => ({ kind: 'navigate', href: '/documents/d1' })}>
76
+ <ResourceChip resource={ref()} />
77
+ </AgentResourceResolverProvider>,
78
+ );
79
+ const link = screen.getByRole('link', { name: /My Document/ });
80
+ expect(link.getAttribute('href')).toContain('/documents/d1');
81
+ expect(screen.getByText('Created')).not.toBeNull();
82
+ });
83
+
84
+ it('renders a non-interactive chip when the host does not configure a resolver', () => {
85
+ renderWithProviders(<ResourceChip resource={ref({ type: 'interaction', id: 'int-1', label: 'Chat' })} />);
86
+ expect(screen.queryByRole('link')).toBeNull();
87
+ expect(screen.getByText('Chat')).not.toBeNull();
88
+ });
89
+
90
+ it('renders a deleted resource as a non-interactive chip (no link)', () => {
91
+ renderWithProviders(<ResourceChip resource={ref({ action: 'deleted', label: 'Gone' })} />);
92
+ expect(screen.queryByRole('link')).toBeNull();
93
+ expect(screen.getByText('Gone')).not.toBeNull();
94
+ expect(screen.getByText('Deleted')).not.toBeNull();
95
+ });
96
+
97
+ it('honors a resolver override from context', () => {
98
+ renderWithProviders(
99
+ <AgentResourceResolverProvider value={() => ({ kind: 'navigate', href: '/custom/path' })}>
100
+ <ResourceChip resource={ref()} />
101
+ </AgentResourceResolverProvider>,
102
+ );
103
+ expect(screen.getByRole('link').getAttribute('href')).toContain('/custom/path');
104
+ });
105
+
106
+ it('supports a host activation callback instead of navigation', () => {
107
+ const onActivate = vi.fn();
108
+ renderWithProviders(
109
+ <AgentResourceResolverProvider value={() => ({ kind: 'activate', onActivate })}>
110
+ <ResourceChip resource={ref()} />
111
+ </AgentResourceResolverProvider>,
112
+ );
113
+
114
+ fireEvent.click(screen.getByRole('button', { name: /My Document/ }));
115
+ expect(onActivate).toHaveBeenCalledOnce();
116
+ expect(screen.queryByRole('link')).toBeNull();
117
+ });
118
+ });
119
+
120
+ describe('ResourceChangeSummary', () => {
121
+ it('renders nothing when there are no resources', () => {
122
+ const { container } = renderWithProviders(<ResourceChangeSummary resources={[]} />);
123
+ expect(container.querySelector('[data-agent-resource-summary]')).toBeNull();
124
+ });
125
+
126
+ it('renders a header and a chip per resource', () => {
127
+ const { container } = renderWithProviders(
128
+ <ResourceChangeSummary
129
+ resources={[ref({ id: 'd1', label: 'Doc One' }), ref({ type: 'collection', id: 'c1', label: 'Col' })]}
130
+ />,
131
+ );
132
+ expect(container.querySelector('[data-agent-resource-summary]')?.className).not.toContain('bg-muted');
133
+ expect(screen.getByText('Resources changed')).not.toBeNull();
134
+ expect(screen.getByText('Doc One')).not.toBeNull();
135
+ expect(screen.getByText('Col')).not.toBeNull();
136
+ });
137
+
138
+ it('collapses beyond five resources and expands on click', () => {
139
+ const many = Array.from({ length: 7 }, (_, i) => ref({ id: `d${i}`, label: `Doc ${i}` }));
140
+ renderWithProviders(<ResourceChangeSummary resources={many} />);
141
+ // Only 5 shown initially
142
+ expect(screen.queryByText('Doc 5')).toBeNull();
143
+ const toggle = screen.getByRole('button', { name: /View all 7 changes/ });
144
+ fireEvent.click(toggle);
145
+ expect(screen.getByText('Doc 5')).not.toBeNull();
146
+ expect(screen.getByText('Doc 6')).not.toBeNull();
147
+ });
148
+ });
@@ -0,0 +1,141 @@
1
+ import type { AgentResourceReference } from '@vertesia/common';
2
+ import { Badge, Button, cn } from '@vertesia/ui/core';
3
+ import { useUITranslation } from '@vertesia/ui/i18n';
4
+ import { NavLink } from '@vertesia/ui/router';
5
+ import { useAgentResourceResolver } from '@vertesia/ui/widgets';
6
+ import { getResourceActionVariant, getResourceIcon } from './resourceLinks';
7
+
8
+ interface ResourceChipProps {
9
+ resource: AgentResourceReference;
10
+ /** Originating run, passed to the resolver for run-scoped resources. */
11
+ workflowRunId?: string;
12
+ /** Hide the action badge when surrounding text already communicates the action. */
13
+ showAction?: boolean;
14
+ className?: string;
15
+ }
16
+
17
+ /**
18
+ * A compact resource chip for something an agent tool created/updated/deleted. The host resolver may
19
+ * make it navigable (NavLink applies tenant sticky params), activate a host callback, or leave it
20
+ * non-interactive.
21
+ */
22
+ export function ResourceChip({ resource, workflowRunId, showAction = true, className }: ResourceChipProps) {
23
+ const { t } = useUITranslation();
24
+ const resolve = useAgentResourceResolver();
25
+ const target = resolve(resource, { workflowRunId, source: 'structured' });
26
+ const Icon = getResourceIcon(resource.type);
27
+ // Literal keys (not a dynamic lookup) so i18next-cli can statically extract them.
28
+ const actionLabel =
29
+ resource.action === 'created'
30
+ ? t('agent.resourceActionCreated')
31
+ : resource.action === 'deleted'
32
+ ? t('agent.resourceActionDeleted')
33
+ : t('agent.resourceActionUpdated');
34
+
35
+ const body = (
36
+ <>
37
+ <Icon className="size-3.5 shrink-0 text-foreground" aria-hidden="true" />
38
+ <span className="truncate">{resource.label}</span>
39
+ {showAction && (
40
+ <Badge variant={getResourceActionVariant(resource.action)} className="shrink-0 px-1.5 py-0 text-[10px]">
41
+ {actionLabel}
42
+ </Badge>
43
+ )}
44
+ </>
45
+ );
46
+
47
+ const baseClass = cn(
48
+ 'inline-flex max-w-full items-center gap-1.5 rounded-md border border-border bg-background px-2 py-1 text-xs',
49
+ className,
50
+ );
51
+
52
+ if (target.kind === 'navigate') {
53
+ return (
54
+ <NavLink
55
+ href={target.href}
56
+ topLevelNav
57
+ className={cn(baseClass, 'font-medium transition-colors hover:bg-muted/60')}
58
+ >
59
+ {body}
60
+ </NavLink>
61
+ );
62
+ }
63
+
64
+ if (target.kind === 'activate') {
65
+ return (
66
+ <Button
67
+ variant="unstyled"
68
+ onClick={target.onActivate}
69
+ className={cn(baseClass, 'font-medium transition-colors hover:bg-muted/60')}
70
+ >
71
+ {body}
72
+ </Button>
73
+ );
74
+ }
75
+
76
+ // Deleted or unresolvable resource: no navigation target, render a non-interactive chip.
77
+ return <span className={cn(baseClass, 'text-muted')}>{body}</span>;
78
+ }
79
+
80
+ interface ResourceActivityLinkListProps {
81
+ resources: AgentResourceReference[];
82
+ workflowRunId?: string;
83
+ className?: string;
84
+ }
85
+
86
+ /** Inline action + deep-link presentation for completed tool timeline rows. */
87
+ export function ResourceActivityLinkList({ resources, workflowRunId, className }: ResourceActivityLinkListProps) {
88
+ const { t } = useUITranslation();
89
+ if (resources.length === 0) return null;
90
+
91
+ return (
92
+ <div
93
+ className={cn('flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1', className)}
94
+ data-agent-resource-activity
95
+ >
96
+ {resources.map((resource) => {
97
+ const actionLabel =
98
+ resource.action === 'created'
99
+ ? t('agent.resourceActionCreated')
100
+ : resource.action === 'deleted'
101
+ ? t('agent.resourceActionDeleted')
102
+ : t('agent.resourceActionUpdated');
103
+ return (
104
+ <span
105
+ key={`${resource.type}:${resource.id}:${resource.action}`}
106
+ className="inline-flex min-w-0 items-center gap-1.5"
107
+ >
108
+ <span className="shrink-0 text-sm text-muted">{actionLabel}</span>
109
+ <ResourceChip
110
+ resource={resource}
111
+ workflowRunId={workflowRunId}
112
+ showAction={false}
113
+ className="py-0.5"
114
+ />
115
+ </span>
116
+ );
117
+ })}
118
+ </div>
119
+ );
120
+ }
121
+
122
+ interface ResourceChipListProps {
123
+ resources: AgentResourceReference[];
124
+ workflowRunId?: string;
125
+ className?: string;
126
+ }
127
+
128
+ export function ResourceChipList({ resources, workflowRunId, className }: ResourceChipListProps) {
129
+ if (resources.length === 0) return null;
130
+ return (
131
+ <div className={cn('flex flex-wrap gap-1.5', className)}>
132
+ {resources.map((resource) => (
133
+ <ResourceChip
134
+ key={`${resource.type}:${resource.id}:${resource.action}`}
135
+ resource={resource}
136
+ workflowRunId={workflowRunId}
137
+ />
138
+ ))}
139
+ </div>
140
+ );
141
+ }