@vertesia/ui 1.5.0-dev.20260717.131047Z → 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 (567) hide show
  1. package/lib/core/components/SidePanel.js +1 -1
  2. package/lib/core/components/SidePanel.js.map +1 -1
  3. package/lib/core/components/shadcn/button.d.ts +10 -3
  4. package/lib/core/components/shadcn/button.d.ts.map +1 -1
  5. package/lib/core/components/shadcn/button.js +41 -9
  6. package/lib/core/components/shadcn/button.js.map +1 -1
  7. package/lib/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
  8. package/lib/core/components/shadcn/filters/filterBar.js +1 -1
  9. package/lib/core/components/shadcn/filters/filterBar.js.map +1 -1
  10. package/lib/core/components/shadcn/modal/dialog.d.ts +1 -1
  11. package/lib/core/components/shadcn/modal/dialog.d.ts.map +1 -1
  12. package/lib/core/components/shadcn/modal/dialog.js +3 -1
  13. package/lib/core/components/shadcn/modal/dialog.js.map +1 -1
  14. package/lib/core/components/shadcn/tabs.d.ts +12 -1
  15. package/lib/core/components/shadcn/tabs.d.ts.map +1 -1
  16. package/lib/core/components/shadcn/tabs.js +12 -4
  17. package/lib/core/components/shadcn/tabs.js.map +1 -1
  18. package/lib/core/components/shadcn/theme/ThemeSwitcher.js +1 -1
  19. package/lib/core/components/shadcn/theme/ThemeSwitcher.js.map +1 -1
  20. package/lib/features/agent/chat/AgentChatPlaybackControls.d.ts.map +1 -1
  21. package/lib/features/agent/chat/AgentChatPlaybackControls.js +2 -2
  22. package/lib/features/agent/chat/AgentChatPlaybackControls.js.map +1 -1
  23. package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts.map +1 -1
  24. package/lib/features/agent/chat/AgentRequestInputOverlay.js +2 -2
  25. package/lib/features/agent/chat/AgentRequestInputOverlay.js.map +1 -1
  26. package/lib/features/agent/chat/AgentRightPanel.d.ts +5 -1
  27. package/lib/features/agent/chat/AgentRightPanel.d.ts.map +1 -1
  28. package/lib/features/agent/chat/AgentRightPanel.js +2 -2
  29. package/lib/features/agent/chat/AgentRightPanel.js.map +1 -1
  30. package/lib/features/agent/chat/ArtifactsTab.d.ts +5 -1
  31. package/lib/features/agent/chat/ArtifactsTab.d.ts.map +1 -1
  32. package/lib/features/agent/chat/ArtifactsTab.js +53 -13
  33. package/lib/features/agent/chat/ArtifactsTab.js.map +1 -1
  34. package/lib/features/agent/chat/DocumentEditingActionCard.d.ts +6 -0
  35. package/lib/features/agent/chat/DocumentEditingActionCard.d.ts.map +1 -0
  36. package/lib/features/agent/chat/DocumentEditingActionCard.js +129 -0
  37. package/lib/features/agent/chat/DocumentEditingActionCard.js.map +1 -0
  38. package/lib/features/agent/chat/ImageLightbox.d.ts.map +1 -1
  39. package/lib/features/agent/chat/ImageLightbox.js +1 -1
  40. package/lib/features/agent/chat/ImageLightbox.js.map +1 -1
  41. package/lib/features/agent/chat/ModernAgentConversation.d.ts +19 -1
  42. package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  43. package/lib/features/agent/chat/ModernAgentConversation.js +125 -27
  44. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  45. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.d.ts +45 -0
  46. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.d.ts.map +1 -0
  47. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.js +40 -0
  48. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.js.map +1 -0
  49. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +3 -1
  50. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  51. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +96 -51
  52. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  53. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts +2 -1
  54. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts.map +1 -1
  55. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js +9 -5
  56. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js.map +1 -1
  57. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts +25 -0
  58. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts.map +1 -0
  59. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js +33 -0
  60. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js.map +1 -0
  61. package/lib/features/agent/chat/ModernAgentOutput/Header.d.ts.map +1 -1
  62. package/lib/features/agent/chat/ModernAgentOutput/Header.js +4 -2
  63. package/lib/features/agent/chat/ModernAgentOutput/Header.js.map +1 -1
  64. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
  65. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +2 -2
  66. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
  67. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts +3 -1
  68. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
  69. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +23 -24
  70. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
  71. package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.d.ts +14 -0
  72. package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.d.ts.map +1 -0
  73. package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.js +21 -0
  74. package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.js.map +1 -0
  75. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.d.ts +30 -0
  76. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.d.ts.map +1 -0
  77. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.js +53 -0
  78. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.js.map +1 -0
  79. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.d.ts.map +1 -1
  80. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js +21 -2
  81. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js.map +1 -1
  82. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts +6 -0
  83. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts.map +1 -1
  84. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js +15 -1
  85. package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js.map +1 -1
  86. package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.d.ts +6 -0
  87. package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.d.ts.map +1 -0
  88. package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.js +29 -0
  89. package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.js.map +1 -0
  90. package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.d.ts +33 -0
  91. package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.d.ts.map +1 -0
  92. package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.js +129 -0
  93. package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.js.map +1 -0
  94. package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts +7 -0
  95. package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts.map +1 -1
  96. package/lib/features/agent/chat/ModernAgentOutput/utils.js +11 -1
  97. package/lib/features/agent/chat/ModernAgentOutput/utils.js.map +1 -1
  98. package/lib/features/agent/chat/hooks/useAgentStream.d.ts +1 -1
  99. package/lib/features/agent/chat/hooks/useAgentStream.d.ts.map +1 -1
  100. package/lib/features/agent/chat/hooks/useAgentStream.js +24 -9
  101. package/lib/features/agent/chat/hooks/useAgentStream.js.map +1 -1
  102. package/lib/features/agent/chat/index.d.ts +2 -1
  103. package/lib/features/agent/chat/index.d.ts.map +1 -1
  104. package/lib/features/agent/chat/index.js +3 -0
  105. package/lib/features/agent/chat/index.js.map +1 -1
  106. package/lib/features/environment/SelectEnvironment.d.ts +28 -0
  107. package/lib/features/environment/SelectEnvironment.d.ts.map +1 -0
  108. package/lib/features/environment/SelectEnvironment.js +97 -0
  109. package/lib/features/environment/SelectEnvironment.js.map +1 -0
  110. package/lib/features/environment/index.d.ts +2 -0
  111. package/lib/features/environment/index.d.ts.map +1 -0
  112. package/lib/features/environment/index.js +2 -0
  113. package/lib/features/environment/index.js.map +1 -0
  114. package/lib/features/index.d.ts +2 -0
  115. package/lib/features/index.d.ts.map +1 -1
  116. package/lib/features/index.js +2 -0
  117. package/lib/features/index.js.map +1 -1
  118. package/lib/features/magic-pdf/AnnotatedImageSlider.js +12 -12
  119. package/lib/features/magic-pdf/AnnotatedImageSlider.js.map +1 -1
  120. package/lib/features/magic-pdf/DownloadPopover.d.ts.map +1 -1
  121. package/lib/features/magic-pdf/DownloadPopover.js +3 -17
  122. package/lib/features/magic-pdf/DownloadPopover.js.map +1 -1
  123. package/lib/features/magic-pdf/ExtractedContentView.js +2 -6
  124. package/lib/features/magic-pdf/ExtractedContentView.js.map +1 -1
  125. package/lib/features/magic-pdf/GroundedExtractionAssistantPanel.d.ts +25 -0
  126. package/lib/features/magic-pdf/GroundedExtractionAssistantPanel.d.ts.map +1 -0
  127. package/lib/features/magic-pdf/GroundedExtractionAssistantPanel.js +42 -0
  128. package/lib/features/magic-pdf/GroundedExtractionAssistantPanel.js.map +1 -0
  129. package/lib/features/magic-pdf/GroundedExtractionView.d.ts +27 -0
  130. package/lib/features/magic-pdf/GroundedExtractionView.d.ts.map +1 -0
  131. package/lib/features/magic-pdf/GroundedExtractionView.js +377 -0
  132. package/lib/features/magic-pdf/GroundedExtractionView.js.map +1 -0
  133. package/lib/features/magic-pdf/MagicPdfProvider.d.ts +0 -2
  134. package/lib/features/magic-pdf/MagicPdfProvider.d.ts.map +1 -1
  135. package/lib/features/magic-pdf/MagicPdfProvider.js +15 -42
  136. package/lib/features/magic-pdf/MagicPdfProvider.js.map +1 -1
  137. package/lib/features/magic-pdf/MagicPdfView.d.ts.map +1 -1
  138. package/lib/features/magic-pdf/MagicPdfView.js +2 -19
  139. package/lib/features/magic-pdf/MagicPdfView.js.map +1 -1
  140. package/lib/features/magic-pdf/index.d.ts +1 -0
  141. package/lib/features/magic-pdf/index.d.ts.map +1 -1
  142. package/lib/features/magic-pdf/index.js +1 -0
  143. package/lib/features/magic-pdf/index.js.map +1 -1
  144. package/lib/features/magic-pdf/types.d.ts +1 -1
  145. package/lib/features/magic-pdf/types.d.ts.map +1 -1
  146. package/lib/features/oauth/McpConnectionsButton.d.ts.map +1 -1
  147. package/lib/features/oauth/McpConnectionsButton.js +2 -2
  148. package/lib/features/oauth/McpConnectionsButton.js.map +1 -1
  149. package/lib/features/oauth/McpConnectionsDialog.d.ts.map +1 -1
  150. package/lib/features/oauth/McpConnectionsDialog.js +1 -1
  151. package/lib/features/oauth/McpConnectionsDialog.js.map +1 -1
  152. package/lib/features/oauth/RemoteMcpConnectionButton.d.ts.map +1 -1
  153. package/lib/features/oauth/RemoteMcpConnectionButton.js +1 -1
  154. package/lib/features/oauth/RemoteMcpConnectionButton.js.map +1 -1
  155. package/lib/features/pdf-viewer/PdfPageSlider.js +2 -2
  156. package/lib/features/pdf-viewer/PdfPageSlider.js.map +1 -1
  157. package/lib/features/pdf-viewer/SimplePdfViewer.d.ts.map +1 -1
  158. package/lib/features/pdf-viewer/SimplePdfViewer.js +3 -3
  159. package/lib/features/pdf-viewer/SimplePdfViewer.js.map +1 -1
  160. package/lib/features/permissions/UserPermissionsProvider.js +1 -1
  161. package/lib/features/permissions/UserPermissionsProvider.js.map +1 -1
  162. package/lib/features/store/objects/DocumentQuickFilter.d.ts.map +1 -1
  163. package/lib/features/store/objects/DocumentQuickFilter.js +5 -5
  164. package/lib/features/store/objects/DocumentQuickFilter.js.map +1 -1
  165. package/lib/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
  166. package/lib/features/store/objects/DocumentSearchResults.js +4 -4
  167. package/lib/features/store/objects/DocumentSearchResults.js.map +1 -1
  168. package/lib/features/store/objects/components/ContentDispositionButton.d.ts.map +1 -1
  169. package/lib/features/store/objects/components/ContentDispositionButton.js +1 -1
  170. package/lib/features/store/objects/components/ContentDispositionButton.js.map +1 -1
  171. package/lib/features/store/objects/components/ContentOverview.d.ts +2 -1
  172. package/lib/features/store/objects/components/ContentOverview.d.ts.map +1 -1
  173. package/lib/features/store/objects/components/ContentOverview.js +159 -31
  174. package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
  175. package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.d.ts +16 -0
  176. package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.d.ts.map +1 -0
  177. package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.js +116 -0
  178. package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.js.map +1 -0
  179. package/lib/features/store/objects/components/DocumentEditingLockStatus.d.ts +7 -0
  180. package/lib/features/store/objects/components/DocumentEditingLockStatus.d.ts.map +1 -0
  181. package/lib/features/store/objects/components/DocumentEditingLockStatus.js +13 -0
  182. package/lib/features/store/objects/components/DocumentEditingLockStatus.js.map +1 -0
  183. package/lib/features/store/objects/components/DocumentEditingPanel.d.ts +48 -0
  184. package/lib/features/store/objects/components/DocumentEditingPanel.d.ts.map +1 -0
  185. package/lib/features/store/objects/components/DocumentEditingPanel.js +720 -0
  186. package/lib/features/store/objects/components/DocumentEditingPanel.js.map +1 -0
  187. package/lib/features/store/objects/components/DocumentIcon.js +1 -1
  188. package/lib/features/store/objects/components/TextEditorPanel.d.ts.map +1 -1
  189. package/lib/features/store/objects/components/TextEditorPanel.js +22 -9
  190. package/lib/features/store/objects/components/TextEditorPanel.js.map +1 -1
  191. package/lib/features/store/objects/components/VectorSearchWidget.js +1 -1
  192. package/lib/features/store/objects/components/VectorSearchWidget.js.map +1 -1
  193. package/lib/features/store/objects/components/documentArtifactRefs.d.ts +26 -0
  194. package/lib/features/store/objects/components/documentArtifactRefs.d.ts.map +1 -0
  195. package/lib/features/store/objects/components/documentArtifactRefs.js +52 -0
  196. package/lib/features/store/objects/components/documentArtifactRefs.js.map +1 -0
  197. package/lib/features/store/objects/components/documentEditingAgentConfig.d.ts +4 -0
  198. package/lib/features/store/objects/components/documentEditingAgentConfig.d.ts.map +1 -0
  199. package/lib/features/store/objects/components/documentEditingAgentConfig.js +40 -0
  200. package/lib/features/store/objects/components/documentEditingAgentConfig.js.map +1 -0
  201. package/lib/features/store/objects/components/documentEditingPrompts.d.ts +3 -0
  202. package/lib/features/store/objects/components/documentEditingPrompts.d.ts.map +1 -0
  203. package/lib/features/store/objects/components/documentEditingPrompts.js +13 -0
  204. package/lib/features/store/objects/components/documentEditingPrompts.js.map +1 -0
  205. package/lib/features/store/objects/components/documentEditingRun.d.ts +27 -0
  206. package/lib/features/store/objects/components/documentEditingRun.d.ts.map +1 -0
  207. package/lib/features/store/objects/components/documentEditingRun.js +126 -0
  208. package/lib/features/store/objects/components/documentEditingRun.js.map +1 -0
  209. package/lib/features/store/objects/components/documentEditingTarget.d.ts +20 -0
  210. package/lib/features/store/objects/components/documentEditingTarget.d.ts.map +1 -0
  211. package/lib/features/store/objects/components/documentEditingTarget.js +40 -0
  212. package/lib/features/store/objects/components/documentEditingTarget.js.map +1 -0
  213. package/lib/features/store/objects/components/index.d.ts +1 -0
  214. package/lib/features/store/objects/components/index.d.ts.map +1 -1
  215. package/lib/features/store/objects/components/index.js +1 -0
  216. package/lib/features/store/objects/components/index.js.map +1 -1
  217. package/lib/features/store/objects/components/useContentPanelHooks.d.ts +6 -3
  218. package/lib/features/store/objects/components/useContentPanelHooks.d.ts.map +1 -1
  219. package/lib/features/store/objects/components/useContentPanelHooks.js +57 -37
  220. package/lib/features/store/objects/components/useContentPanelHooks.js.map +1 -1
  221. package/lib/features/store/objects/components/useDownloadFile.d.ts +2 -1
  222. package/lib/features/store/objects/components/useDownloadFile.d.ts.map +1 -1
  223. package/lib/features/store/objects/components/useDownloadFile.js +3 -0
  224. package/lib/features/store/objects/components/useDownloadFile.js.map +1 -1
  225. package/lib/features/store/objects/layout/renderers.d.ts.map +1 -1
  226. package/lib/features/store/objects/layout/renderers.js +2 -2
  227. package/lib/features/store/objects/layout/renderers.js.map +1 -1
  228. package/lib/features/store/objects/selection/SelectionActions.js +1 -1
  229. package/lib/features/store/objects/selection/SelectionActions.js.map +1 -1
  230. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +1 -1
  231. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js.map +1 -1
  232. package/lib/features/store/types/ContentObjectTypesTable.d.ts.map +1 -1
  233. package/lib/features/store/types/ContentObjectTypesTable.js +4 -4
  234. package/lib/features/store/types/ContentObjectTypesTable.js.map +1 -1
  235. package/lib/features/store/types/IntakePolicyEditor.d.ts +15 -0
  236. package/lib/features/store/types/IntakePolicyEditor.d.ts.map +1 -0
  237. package/lib/features/store/types/IntakePolicyEditor.js +455 -0
  238. package/lib/features/store/types/IntakePolicyEditor.js.map +1 -0
  239. package/lib/features/store/types/IntakePolicyForm.d.ts +11 -0
  240. package/lib/features/store/types/IntakePolicyForm.d.ts.map +1 -0
  241. package/lib/features/store/types/IntakePolicyForm.js +91 -0
  242. package/lib/features/store/types/IntakePolicyForm.js.map +1 -0
  243. package/lib/features/store/types/SelectContentTypeModal.js +1 -1
  244. package/lib/features/store/types/SelectContentTypeModal.js.map +1 -1
  245. package/lib/features/store/types/index.d.ts +2 -0
  246. package/lib/features/store/types/index.d.ts.map +1 -1
  247. package/lib/features/store/types/index.js +2 -0
  248. package/lib/features/store/types/index.js.map +1 -1
  249. package/lib/features/store/types/intake-policy-editor.logic.d.ts +7 -0
  250. package/lib/features/store/types/intake-policy-editor.logic.d.ts.map +1 -0
  251. package/lib/features/store/types/intake-policy-editor.logic.js +60 -0
  252. package/lib/features/store/types/intake-policy-editor.logic.js.map +1 -0
  253. package/lib/features/store/types/typeCatalogCache.d.ts +5 -0
  254. package/lib/features/store/types/typeCatalogCache.d.ts.map +1 -0
  255. package/lib/features/store/types/typeCatalogCache.js +40 -0
  256. package/lib/features/store/types/typeCatalogCache.js.map +1 -0
  257. package/lib/features/user/UserInfo.js +7 -3
  258. package/lib/features/user/UserInfo.js.map +1 -1
  259. package/lib/features/views/ViewExperience.d.ts +20 -0
  260. package/lib/features/views/ViewExperience.d.ts.map +1 -0
  261. package/lib/features/views/ViewExperience.js +188 -0
  262. package/lib/features/views/ViewExperience.js.map +1 -0
  263. package/lib/features/views/ViewNavigation.d.ts +3 -0
  264. package/lib/features/views/ViewNavigation.d.ts.map +1 -0
  265. package/lib/features/views/ViewNavigation.js +36 -0
  266. package/lib/features/views/ViewNavigation.js.map +1 -0
  267. package/lib/features/views/ViewResults.d.ts +9 -0
  268. package/lib/features/views/ViewResults.d.ts.map +1 -0
  269. package/lib/features/views/ViewResults.js +229 -0
  270. package/lib/features/views/ViewResults.js.map +1 -0
  271. package/lib/features/views/ViewSearch.d.ts +3 -0
  272. package/lib/features/views/ViewSearch.d.ts.map +1 -0
  273. package/lib/features/views/ViewSearch.js +74 -0
  274. package/lib/features/views/ViewSearch.js.map +1 -0
  275. package/lib/features/views/index.d.ts +7 -0
  276. package/lib/features/views/index.d.ts.map +1 -0
  277. package/lib/features/views/index.js +7 -0
  278. package/lib/features/views/index.js.map +1 -0
  279. package/lib/features/views/types.d.ts +39 -0
  280. package/lib/features/views/types.d.ts.map +1 -0
  281. package/lib/features/views/types.js +2 -0
  282. package/lib/features/views/types.js.map +1 -0
  283. package/lib/features/views/viewState.d.ts +14 -0
  284. package/lib/features/views/viewState.d.ts.map +1 -0
  285. package/lib/features/views/viewState.js +117 -0
  286. package/lib/features/views/viewState.js.map +1 -0
  287. package/lib/i18n/locales/ar.json +134 -1
  288. package/lib/i18n/locales/de.json +130 -1
  289. package/lib/i18n/locales/en.json +363 -33
  290. package/lib/i18n/locales/es.json +131 -1
  291. package/lib/i18n/locales/fr.json +131 -1
  292. package/lib/i18n/locales/it.json +131 -1
  293. package/lib/i18n/locales/ja.json +129 -1
  294. package/lib/i18n/locales/ko.json +129 -1
  295. package/lib/i18n/locales/pt.json +131 -1
  296. package/lib/i18n/locales/ru.json +132 -1
  297. package/lib/i18n/locales/tr.json +130 -1
  298. package/lib/i18n/locales/zh-TW.json +129 -1
  299. package/lib/i18n/locales/zh.json +129 -1
  300. package/lib/rich-text/EditorToolbar.d.ts +37 -0
  301. package/lib/rich-text/EditorToolbar.d.ts.map +1 -0
  302. package/lib/rich-text/EditorToolbar.js +215 -0
  303. package/lib/rich-text/EditorToolbar.js.map +1 -0
  304. package/lib/rich-text/VertesiaRichTextEditor.d.ts +26 -0
  305. package/lib/rich-text/VertesiaRichTextEditor.d.ts.map +1 -0
  306. package/lib/rich-text/VertesiaRichTextEditor.js +67 -0
  307. package/lib/rich-text/VertesiaRichTextEditor.js.map +1 -0
  308. package/lib/rich-text/index.d.ts +2 -0
  309. package/lib/rich-text/index.d.ts.map +1 -0
  310. package/lib/rich-text/index.js +2 -0
  311. package/lib/rich-text/index.js.map +1 -0
  312. package/lib/router/HistoryNavigator.d.ts.map +1 -1
  313. package/lib/router/HistoryNavigator.js +23 -13
  314. package/lib/router/HistoryNavigator.js.map +1 -1
  315. package/lib/router/Nav.d.ts.map +1 -1
  316. package/lib/router/Nav.js +15 -6
  317. package/lib/router/Nav.js.map +1 -1
  318. package/lib/session/UserSession.d.ts.map +1 -1
  319. package/lib/session/UserSession.js +5 -3
  320. package/lib/session/UserSession.js.map +1 -1
  321. package/lib/vertesia-ui-core.js +4 -4
  322. package/lib/vertesia-ui-core.js.map +1 -1
  323. package/lib/vertesia-ui-features.js +40 -28
  324. package/lib/vertesia-ui-features.js.map +1 -1
  325. package/lib/vertesia-ui-i18n.js +15 -15
  326. package/lib/vertesia-ui-i18n.js.map +1 -1
  327. package/lib/vertesia-ui-rich-text.js +5 -0
  328. package/lib/vertesia-ui-rich-text.js.map +1 -0
  329. package/lib/vertesia-ui-router.js +1 -1
  330. package/lib/vertesia-ui-router.js.map +1 -1
  331. package/lib/vertesia-ui-session.js +1 -1
  332. package/lib/vertesia-ui-session.js.map +1 -1
  333. package/lib/vertesia-ui-shell.js +1 -1
  334. package/lib/vertesia-ui-shell.js.map +1 -1
  335. package/lib/vertesia-ui-widgets.js +25 -7
  336. package/lib/vertesia-ui-widgets.js.map +1 -1
  337. package/lib/widgets/form/Form.js +1 -1
  338. package/lib/widgets/form/Form.js.map +1 -1
  339. package/lib/widgets/json-view/JSONSwitcher.js +1 -1
  340. package/lib/widgets/json-view/JSONSwitcher.js.map +1 -1
  341. package/lib/widgets/json-view/JSONView.js +6 -6
  342. package/lib/widgets/json-view/JSONView.js.map +1 -1
  343. package/lib/widgets/markdown/AgentResourceResolver.d.ts +31 -0
  344. package/lib/widgets/markdown/AgentResourceResolver.d.ts.map +1 -0
  345. package/lib/widgets/markdown/AgentResourceResolver.js +39 -0
  346. package/lib/widgets/markdown/AgentResourceResolver.js.map +1 -0
  347. package/lib/widgets/markdown/ArtifactEditingSurface.d.ts +63 -0
  348. package/lib/widgets/markdown/ArtifactEditingSurface.d.ts.map +1 -0
  349. package/lib/widgets/markdown/ArtifactEditingSurface.js +582 -0
  350. package/lib/widgets/markdown/ArtifactEditingSurface.js.map +1 -0
  351. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.d.ts +69 -0
  352. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.d.ts.map +1 -0
  353. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.js +374 -0
  354. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.js.map +1 -0
  355. package/lib/widgets/markdown/MarkdownLink.d.ts +3 -1
  356. package/lib/widgets/markdown/MarkdownLink.d.ts.map +1 -1
  357. package/lib/widgets/markdown/MarkdownLink.js +41 -14
  358. package/lib/widgets/markdown/MarkdownLink.js.map +1 -1
  359. package/lib/widgets/markdown/MarkdownRenderer.d.ts +8 -1
  360. package/lib/widgets/markdown/MarkdownRenderer.d.ts.map +1 -1
  361. package/lib/widgets/markdown/MarkdownRenderer.js +20 -4
  362. package/lib/widgets/markdown/MarkdownRenderer.js.map +1 -1
  363. package/lib/widgets/markdown/index.d.ts +5 -1
  364. package/lib/widgets/markdown/index.d.ts.map +1 -1
  365. package/lib/widgets/markdown/index.js +5 -1
  366. package/lib/widgets/markdown/index.js.map +1 -1
  367. package/lib/widgets/markdown/normalizeCustomSchemeLinks.d.ts.map +1 -1
  368. package/lib/widgets/markdown/normalizeCustomSchemeLinks.js +14 -2
  369. package/lib/widgets/markdown/normalizeCustomSchemeLinks.js.map +1 -1
  370. package/lib/widgets/markdown/textDiff.d.ts +52 -0
  371. package/lib/widgets/markdown/textDiff.d.ts.map +1 -0
  372. package/lib/widgets/markdown/textDiff.js +362 -0
  373. package/lib/widgets/markdown/textDiff.js.map +1 -0
  374. package/lib/widgets/markdown/useArtifactContent.d.ts +5 -3
  375. package/lib/widgets/markdown/useArtifactContent.d.ts.map +1 -1
  376. package/lib/widgets/markdown/useArtifactContent.js +6 -3
  377. package/lib/widgets/markdown/useArtifactContent.js.map +1 -1
  378. package/lib/widgets/markdown/useResolvedUrl.d.ts +1 -5
  379. package/lib/widgets/markdown/useResolvedUrl.d.ts.map +1 -1
  380. package/lib/widgets/markdown/useResolvedUrl.js +19 -31
  381. package/lib/widgets/markdown/useResolvedUrl.js.map +1 -1
  382. package/lib/widgets/monacoEditor/MonacoEditor.d.ts.map +1 -1
  383. package/lib/widgets/monacoEditor/MonacoEditor.js +29 -0
  384. package/lib/widgets/monacoEditor/MonacoEditor.js.map +1 -1
  385. package/lib/widgets/schema-editor/ManagedSchema.d.ts +8 -0
  386. package/lib/widgets/schema-editor/ManagedSchema.d.ts.map +1 -1
  387. package/lib/widgets/schema-editor/ManagedSchema.js +16 -0
  388. package/lib/widgets/schema-editor/ManagedSchema.js.map +1 -1
  389. package/lib/widgets/schema-editor/editor/EditableSchemaProperty.d.ts +2 -0
  390. package/lib/widgets/schema-editor/editor/EditableSchemaProperty.d.ts.map +1 -1
  391. package/lib/widgets/schema-editor/editor/EditableSchemaProperty.js +1 -0
  392. package/lib/widgets/schema-editor/editor/EditableSchemaProperty.js.map +1 -1
  393. package/lib/widgets/schema-editor/editor/PropertyViewer.d.ts.map +1 -1
  394. package/lib/widgets/schema-editor/editor/PropertyViewer.js +4 -1
  395. package/lib/widgets/schema-editor/editor/PropertyViewer.js.map +1 -1
  396. package/lib/widgets/schema-editor/editor/SchemaEditor.d.ts.map +1 -1
  397. package/lib/widgets/schema-editor/editor/SchemaEditor.js +22 -11
  398. package/lib/widgets/schema-editor/editor/SchemaEditor.js.map +1 -1
  399. package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts +9 -0
  400. package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts.map +1 -1
  401. package/lib/widgets/xml-viewer/hooks/useXMLViewer.js +20 -17
  402. package/lib/widgets/xml-viewer/hooks/useXMLViewer.js.map +1 -1
  403. package/package.json +37 -33
  404. package/src/__tests__/a11y.test.tsx +77 -1
  405. package/src/__tests__/axe-helper.ts +17 -7
  406. package/src/__tests__/setup.ts +25 -0
  407. package/src/core/components/SidePanel.tsx +1 -1
  408. package/src/core/components/shadcn/button.tsx +67 -12
  409. package/src/core/components/shadcn/filters/filterBar.tsx +1 -0
  410. package/src/core/components/shadcn/modal/dialog.tsx +4 -2
  411. package/src/core/components/shadcn/tabs.test.tsx +65 -0
  412. package/src/core/components/shadcn/tabs.tsx +25 -3
  413. package/src/core/components/shadcn/theme/ThemeSwitcher.tsx +1 -1
  414. package/src/features/agent/chat/AgentChatPlaybackControls.tsx +0 -5
  415. package/src/features/agent/chat/AgentRequestInputOverlay.test.tsx +31 -0
  416. package/src/features/agent/chat/AgentRequestInputOverlay.tsx +6 -2
  417. package/src/features/agent/chat/AgentRightPanel.tsx +16 -1
  418. package/src/features/agent/chat/ArtifactsTab.test.tsx +55 -0
  419. package/src/features/agent/chat/ArtifactsTab.tsx +222 -61
  420. package/src/features/agent/chat/DocumentEditingActionCard.test.tsx +105 -0
  421. package/src/features/agent/chat/DocumentEditingActionCard.tsx +210 -0
  422. package/src/features/agent/chat/ImageLightbox.tsx +0 -1
  423. package/src/features/agent/chat/ModernAgentConversation.test.tsx +285 -40
  424. package/src/features/agent/chat/ModernAgentConversation.tsx +247 -77
  425. package/src/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.test.tsx +79 -0
  426. package/src/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.tsx +94 -0
  427. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +263 -35
  428. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +236 -118
  429. package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.tsx +17 -0
  430. package/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.test.tsx +44 -0
  431. package/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.tsx +88 -0
  432. package/src/features/agent/chat/ModernAgentOutput/Header.tsx +3 -3
  433. package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +0 -2
  434. package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +34 -47
  435. package/src/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.tsx +40 -0
  436. package/src/features/agent/chat/ModernAgentOutput/ResourceChip.test.tsx +148 -0
  437. package/src/features/agent/chat/ModernAgentOutput/ResourceChip.tsx +141 -0
  438. package/src/features/agent/chat/ModernAgentOutput/ToolCallGroup.tsx +33 -1
  439. package/src/features/agent/chat/ModernAgentOutput/requestInputMessages.test.ts +80 -0
  440. package/src/features/agent/chat/ModernAgentOutput/requestInputMessages.ts +21 -1
  441. package/src/features/agent/chat/ModernAgentOutput/resourceLinks.tsx +43 -0
  442. package/src/features/agent/chat/ModernAgentOutput/resourceSummary.test.ts +229 -0
  443. package/src/features/agent/chat/ModernAgentOutput/resourceSummary.ts +152 -0
  444. package/src/features/agent/chat/ModernAgentOutput/utils.test.ts +17 -0
  445. package/src/features/agent/chat/ModernAgentOutput/utils.ts +14 -1
  446. package/src/features/agent/chat/hooks/useAgentStream.test.tsx +188 -0
  447. package/src/features/agent/chat/hooks/useAgentStream.ts +28 -8
  448. package/src/features/agent/chat/index.ts +11 -0
  449. package/src/features/environment/SelectEnvironment.tsx +186 -0
  450. package/src/features/environment/index.ts +1 -0
  451. package/src/features/index.ts +2 -0
  452. package/src/features/magic-pdf/AnnotatedImageSlider.tsx +62 -66
  453. package/src/features/magic-pdf/DownloadPopover.tsx +5 -48
  454. package/src/features/magic-pdf/ExtractedContentView.tsx +2 -14
  455. package/src/features/magic-pdf/GroundedExtractionAssistantPanel.tsx +86 -0
  456. package/src/features/magic-pdf/GroundedExtractionView.test.tsx +65 -0
  457. package/src/features/magic-pdf/GroundedExtractionView.tsx +1055 -0
  458. package/src/features/magic-pdf/MagicPdfProvider.tsx +17 -47
  459. package/src/features/magic-pdf/MagicPdfView.tsx +3 -51
  460. package/src/features/magic-pdf/index.ts +1 -0
  461. package/src/features/magic-pdf/types.ts +1 -1
  462. package/src/features/oauth/McpConnectionsButton.tsx +0 -2
  463. package/src/features/oauth/McpConnectionsDialog.tsx +0 -1
  464. package/src/features/oauth/RemoteMcpConnectionButton.tsx +0 -1
  465. package/src/features/pdf-viewer/PdfPageSlider.tsx +5 -5
  466. package/src/features/pdf-viewer/SimplePdfViewer.tsx +11 -12
  467. package/src/features/permissions/UserPermissionsProvider.tsx +1 -1
  468. package/src/features/store/objects/DocumentQuickFilter.tsx +13 -15
  469. package/src/features/store/objects/DocumentSearchResults.tsx +5 -8
  470. package/src/features/store/objects/components/ContentDispositionButton.tsx +5 -1
  471. package/src/features/store/objects/components/ContentOverview.tsx +356 -105
  472. package/src/features/store/objects/components/DocumentEditingConfigurationSelector.test.ts +34 -0
  473. package/src/features/store/objects/components/DocumentEditingConfigurationSelector.tsx +211 -0
  474. package/src/features/store/objects/components/DocumentEditingLockStatus.test.tsx +32 -0
  475. package/src/features/store/objects/components/DocumentEditingLockStatus.tsx +45 -0
  476. package/src/features/store/objects/components/DocumentEditingPanel.test.tsx +336 -0
  477. package/src/features/store/objects/components/DocumentEditingPanel.tsx +1236 -0
  478. package/src/features/store/objects/components/DocumentIcon.tsx +1 -1
  479. package/src/features/store/objects/components/TextEditorPanel.test.tsx +48 -0
  480. package/src/features/store/objects/components/TextEditorPanel.tsx +41 -20
  481. package/src/features/store/objects/components/VectorSearchWidget.tsx +2 -2
  482. package/src/features/store/objects/components/documentArtifactRefs.test.ts +65 -0
  483. package/src/features/store/objects/components/documentArtifactRefs.ts +72 -0
  484. package/src/features/store/objects/components/documentEditingAgentConfig.test.ts +28 -0
  485. package/src/features/store/objects/components/documentEditingAgentConfig.ts +41 -0
  486. package/src/features/store/objects/components/documentEditingPrompts.test.ts +26 -0
  487. package/src/features/store/objects/components/documentEditingPrompts.ts +12 -0
  488. package/src/features/store/objects/components/documentEditingRun.test.ts +176 -0
  489. package/src/features/store/objects/components/documentEditingRun.ts +184 -0
  490. package/src/features/store/objects/components/documentEditingTarget.test.ts +96 -0
  491. package/src/features/store/objects/components/documentEditingTarget.ts +63 -0
  492. package/src/features/store/objects/components/index.ts +1 -0
  493. package/src/features/store/objects/components/useContentPanelHooks.ts +55 -42
  494. package/src/features/store/objects/components/useDownloadFile.test.ts +20 -0
  495. package/src/features/store/objects/components/useDownloadFile.ts +5 -1
  496. package/src/features/store/objects/layout/renderers.tsx +1 -2
  497. package/src/features/store/objects/selection/SelectionActions.tsx +1 -1
  498. package/src/features/store/objects/selection/actions/StartWorkflowButton.tsx +2 -2
  499. package/src/features/store/types/ContentObjectTypesTable.tsx +12 -14
  500. package/src/features/store/types/IntakePolicyEditor.tsx +679 -0
  501. package/src/features/store/types/IntakePolicyForm.tsx +838 -0
  502. package/src/features/store/types/SelectContentTypeModal.tsx +2 -2
  503. package/src/features/store/types/index.ts +2 -0
  504. package/src/features/store/types/intake-policy-editor.logic.test.ts +109 -0
  505. package/src/features/store/types/intake-policy-editor.logic.ts +87 -0
  506. package/src/features/store/types/typeCatalogCache.ts +45 -0
  507. package/src/features/user/UserInfo.tsx +7 -7
  508. package/src/features/views/ViewExperience.test.tsx +273 -0
  509. package/src/features/views/ViewExperience.tsx +408 -0
  510. package/src/features/views/ViewNavigation.test.tsx +86 -0
  511. package/src/features/views/ViewNavigation.tsx +187 -0
  512. package/src/features/views/ViewResults.test.ts +82 -0
  513. package/src/features/views/ViewResults.tsx +609 -0
  514. package/src/features/views/ViewSearch.test.tsx +147 -0
  515. package/src/features/views/ViewSearch.tsx +205 -0
  516. package/src/features/views/index.ts +6 -0
  517. package/src/features/views/types.ts +57 -0
  518. package/src/features/views/viewState.test.ts +111 -0
  519. package/src/features/views/viewState.ts +132 -0
  520. package/src/i18n/locales/ar.json +134 -1
  521. package/src/i18n/locales/de.json +130 -1
  522. package/src/i18n/locales/en.json +1083 -753
  523. package/src/i18n/locales/es.json +131 -1
  524. package/src/i18n/locales/fr.json +131 -1
  525. package/src/i18n/locales/it.json +131 -1
  526. package/src/i18n/locales/ja.json +129 -1
  527. package/src/i18n/locales/ko.json +129 -1
  528. package/src/i18n/locales/pt.json +131 -1
  529. package/src/i18n/locales/ru.json +132 -1
  530. package/src/i18n/locales/tr.json +130 -1
  531. package/src/i18n/locales/zh-TW.json +129 -1
  532. package/src/i18n/locales/zh.json +129 -1
  533. package/src/rich-text/EditorToolbar.tsx +708 -0
  534. package/src/rich-text/VertesiaRichTextEditor.test.tsx +211 -0
  535. package/src/rich-text/VertesiaRichTextEditor.tsx +241 -0
  536. package/src/rich-text/index.ts +9 -0
  537. package/src/router/HistoryNavigator.test.ts +48 -0
  538. package/src/router/HistoryNavigator.ts +24 -13
  539. package/src/router/Nav.tsx +18 -8
  540. package/src/session/UserSession.test.ts +39 -0
  541. package/src/session/UserSession.ts +5 -3
  542. package/src/widgets/form/Form.tsx +1 -1
  543. package/src/widgets/json-view/JSONSwitcher.tsx +2 -2
  544. package/src/widgets/json-view/JSONView.tsx +7 -7
  545. package/src/widgets/markdown/AgentResourceResolver.test.ts +24 -0
  546. package/src/widgets/markdown/AgentResourceResolver.tsx +70 -0
  547. package/src/widgets/markdown/ArtifactEditingSurface.test.tsx +537 -0
  548. package/src/widgets/markdown/ArtifactEditingSurface.tsx +878 -0
  549. package/src/widgets/markdown/CollaborativeMarkdownRenderer.test.tsx +484 -0
  550. package/src/widgets/markdown/CollaborativeMarkdownRenderer.tsx +825 -0
  551. package/src/widgets/markdown/MarkdownLink.test.tsx +122 -0
  552. package/src/widgets/markdown/MarkdownLink.tsx +102 -17
  553. package/src/widgets/markdown/MarkdownRenderer.tsx +30 -3
  554. package/src/widgets/markdown/index.ts +39 -1
  555. package/src/widgets/markdown/normalizeCustomSchemeLinks.ts +15 -2
  556. package/src/widgets/markdown/textDiff.test.ts +156 -0
  557. package/src/widgets/markdown/textDiff.ts +399 -0
  558. package/src/widgets/markdown/useArtifactContent.ts +9 -3
  559. package/src/widgets/markdown/useResolvedUrl.test.ts +29 -40
  560. package/src/widgets/markdown/useResolvedUrl.ts +33 -35
  561. package/src/widgets/monacoEditor/MonacoEditor.tsx +29 -0
  562. package/src/widgets/schema-editor/ManagedSchema.ts +22 -0
  563. package/src/widgets/schema-editor/editor/EditableSchemaProperty.tsx +3 -0
  564. package/src/widgets/schema-editor/editor/PropertyViewer.tsx +14 -3
  565. package/src/widgets/schema-editor/editor/SchemaEditor.tsx +80 -29
  566. package/src/widgets/xml-viewer/hooks/useXMLViewer.test.ts +26 -0
  567. package/src/widgets/xml-viewer/hooks/useXMLViewer.ts +23 -18
@@ -0,0 +1,878 @@
1
+ import { Button, Center, cn, errorMessage, MessageBox, Spinner, useToast, VTooltip } from '@vertesia/ui/core';
2
+ import { useUITranslation } from '@vertesia/ui/i18n';
3
+ import { useUserSession } from '@vertesia/ui/session';
4
+ import { Check, GitCommitHorizontal } from 'lucide-react';
5
+ import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
+ import {
7
+ applyMarkdownEditingChange,
8
+ CollaborativeMarkdownRenderer,
9
+ type MarkdownEditingAction,
10
+ } from './CollaborativeMarkdownRenderer.js';
11
+ import { getTextLineChangeRegions, rebaseTextChanges } from './textDiff.js';
12
+
13
+ const HYDRATION_RETRY_DELAY_MS = 500;
14
+ const HYDRATION_RETRY_LIMIT = 60;
15
+ const DOCUMENT_SAVE_DEBOUNCE_MS = 400;
16
+
17
+ const MarkdownDocumentEditor = lazy(() =>
18
+ import('@vertesia/ui/rich-text').then((module) => ({ default: module.VertesiaMarkdownDocumentEditor })),
19
+ );
20
+
21
+ export function isArtifactRefreshEvent(details: Record<string, unknown> | undefined, path: string): boolean {
22
+ if (details?.path !== path) return false;
23
+ if (details.event_class === 'artifact_updated') return true;
24
+
25
+ // Compatibility for runs handled by workers that predate the dedicated
26
+ // artifact_updated event. Their path-bearing completion is still reliable.
27
+ return (
28
+ details.event_class === 'activity' &&
29
+ details.tool_status === 'completed' &&
30
+ (details.tool === 'edit_artifact' || details.tool === 'write_artifact')
31
+ );
32
+ }
33
+
34
+ export function applyArtifactRefreshChanges(
35
+ content: string,
36
+ details: Record<string, unknown> | undefined,
37
+ ): string | undefined {
38
+ if (!Array.isArray(details?.changes) || details.changes.length === 0) return undefined;
39
+
40
+ let updated = content;
41
+ for (const value of details.changes) {
42
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
43
+ const change = value as Record<string, unknown>;
44
+ if (change.operation !== 'replace' || typeof change.before !== 'string' || typeof change.after !== 'string') {
45
+ return undefined;
46
+ }
47
+
48
+ if (!updated.includes(change.before)) return undefined;
49
+ if (change.replace_all === true) {
50
+ updated = updated.split(change.before).join(change.after);
51
+ } else {
52
+ if (updated.indexOf(change.before) !== updated.lastIndexOf(change.before)) return undefined;
53
+ updated = updated.replace(change.before, change.after);
54
+ }
55
+ }
56
+
57
+ return updated;
58
+ }
59
+
60
+ function getErrorStatus(error: unknown): number | undefined {
61
+ if (!error || typeof error !== 'object' || !('status' in error)) return undefined;
62
+ return typeof error.status === 'number' ? error.status : undefined;
63
+ }
64
+
65
+ function MarkdownChangeRuler({
66
+ regions,
67
+ totalLines,
68
+ onNavigate,
69
+ }: {
70
+ regions: MarkdownChangeRegion[];
71
+ totalLines: number;
72
+ onNavigate: (line: number) => void;
73
+ }) {
74
+ const { t } = useUITranslation();
75
+ if (regions.length === 0) return null;
76
+
77
+ return (
78
+ <nav
79
+ className="absolute inset-y-12 end-1 z-10 w-3 rounded-full border border-mixer-muted/25 bg-muted/20"
80
+ aria-label={t('agent.changeRuler')}
81
+ >
82
+ {regions.map((region) => {
83
+ const top = (region.startLine / Math.max(1, totalLines)) * 100;
84
+ const height = Math.max(1.5, ((region.endLine - region.startLine + 1) / Math.max(1, totalLines)) * 100);
85
+ return (
86
+ <VTooltip
87
+ key={`${region.startLine}:${region.endLine}`}
88
+ description={t('agent.changedRegion', { line: region.startLine + 1 })}
89
+ asChild
90
+ >
91
+ <button
92
+ type="button"
93
+ className="absolute inset-x-0 rounded-full bg-attention transition-colors hover:bg-info focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-info"
94
+ style={{ top: `${Math.min(98.5, top)}%`, height: `${Math.min(100 - top, height)}%` }}
95
+ onClick={() => onNavigate(region.startLine)}
96
+ aria-label={t('agent.changedRegion', { line: region.startLine + 1 })}
97
+ />
98
+ </VTooltip>
99
+ );
100
+ })}
101
+ </nav>
102
+ );
103
+ }
104
+
105
+ export interface ArtifactEditingSurfaceDocumentEdit {
106
+ /** Working-copy content the agent last knew (hydration, its own edits, or the last hand-off). */
107
+ previous: string;
108
+ /** Persisted working-copy content after the user's direct edits. */
109
+ current: string;
110
+ }
111
+
112
+ interface ArtifactDocumentConflict {
113
+ baseContent: string;
114
+ localContent: string;
115
+ remoteContent?: string;
116
+ remoteGeneration?: string;
117
+ pendingAction?: MarkdownEditingAction;
118
+ }
119
+
120
+ interface MarkdownDocumentEditorHandle {
121
+ getMarkdown(): string;
122
+ commands: {
123
+ setContent(content: string, options: { contentType: 'markdown'; emitUpdate: boolean }): boolean;
124
+ };
125
+ }
126
+
127
+ /** Working-copy save state of the document editor. `idle` means nothing to show. */
128
+ export type ArtifactSaveStatus = 'idle' | 'saving' | 'saved';
129
+
130
+ export interface ArtifactEditingSurfaceProps {
131
+ runId?: string;
132
+ path: string;
133
+ initialContent?: string;
134
+ refreshKey?: number;
135
+ refreshDetails?: Record<string, unknown>;
136
+ readOnly?: boolean;
137
+ /** Keep comment controls enabled when content mutations are read-only. */
138
+ allowComments?: boolean;
139
+ viewMode?: 'components' | 'document';
140
+ /** Canonical Markdown used by the full editor's change ruler. */
141
+ baselineContent?: string;
142
+ className?: string;
143
+ onAction?: (action: MarkdownEditingAction) => void | Promise<void>;
144
+ /** Send a composed batch of span comments to the agent as one conversation message. */
145
+ onSendMessage?: (message: string) => void | Promise<void>;
146
+ /** Hand persisted full-document edits to the active agent. */
147
+ onSendChangesToAgent?: () => void | Promise<void>;
148
+ hasUnsentChanges?: boolean;
149
+ isSendingChanges?: boolean;
150
+ sendChangesDisabled?: boolean;
151
+ /** Compact status rendered beside the full editor's document hand-off action. */
152
+ toolbarStatus?: React.ReactNode;
153
+ onContentChange?: (content: string, generation?: string) => void;
154
+ onDocumentEdit?: () => void;
155
+ /**
156
+ * Reports document-editor save state so a parent can render its own indicator
157
+ * (e.g. next to the file name). When provided, the built-in floating badge is
158
+ * suppressed and the parent owns the display.
159
+ */
160
+ onSaveStatusChange?: (status: ArtifactSaveStatus) => void;
161
+ /**
162
+ * Imperative flush used before handing direct full-document edits to the agent.
163
+ * Resolves false when the flush failed; otherwise returns the delta since the
164
+ * agent's last known state and advances that baseline to the flushed content.
165
+ */
166
+ flushChangesRef?: React.MutableRefObject<(() => Promise<false | ArtifactEditingSurfaceDocumentEdit>) | null>;
167
+ }
168
+
169
+ export interface MarkdownChangeRegion {
170
+ startLine: number;
171
+ endLine: number;
172
+ }
173
+
174
+ /** Maps structured diff hunks onto current-document line numbers. */
175
+ export function getMarkdownChangeRegions(before: string, after: string): MarkdownChangeRegion[] {
176
+ return getTextLineChangeRegions(before, after);
177
+ }
178
+
179
+ /**
180
+ * Shared artifact-backed Markdown editor used by both agent workspaces and
181
+ * document editing sessions. The artifact is updated conditionally using its
182
+ * backend-neutral blob generation; the previously rendered content stays in
183
+ * place while a newer generation is loading.
184
+ */
185
+ export function ArtifactEditingSurface({
186
+ runId,
187
+ path,
188
+ initialContent = '',
189
+ refreshKey = 0,
190
+ refreshDetails,
191
+ readOnly = false,
192
+ allowComments = false,
193
+ viewMode = 'components',
194
+ baselineContent,
195
+ className,
196
+ onAction,
197
+ onSendMessage,
198
+ onSendChangesToAgent,
199
+ hasUnsentChanges = false,
200
+ isSendingChanges = false,
201
+ sendChangesDisabled = false,
202
+ toolbarStatus,
203
+ onContentChange,
204
+ onDocumentEdit,
205
+ onSaveStatusChange,
206
+ flushChangesRef,
207
+ }: ArtifactEditingSurfaceProps) {
208
+ const { client } = useUserSession();
209
+ const { t } = useUITranslation();
210
+ const toast = useToast();
211
+ const [content, setContent] = useState(initialContent);
212
+ const [generation, setGeneration] = useState<string | undefined>();
213
+ const [highlightChangesFrom, setHighlightChangesFrom] = useState<string | undefined>();
214
+ const [highlightVersion, setHighlightVersion] = useState(0);
215
+ const [isLoading, setIsLoading] = useState(Boolean(runId));
216
+ const [isSavingDocument, setIsSavingDocument] = useState(false);
217
+ const [isDocumentSavePending, setIsDocumentSavePending] = useState(false);
218
+ const [loadError, setLoadError] = useState<string | undefined>();
219
+ const [documentConflict, setDocumentConflict] = useState<ArtifactDocumentConflict>();
220
+ const [isResolvingConflict, setIsResolvingConflict] = useState(false);
221
+ const commentsEnabled = !readOnly || allowComments;
222
+
223
+ // Working-copy save state for the document editor, shared with the built-in badge and
224
+ // optionally reported to a parent that renders its own indicator.
225
+ const saveStatus: ArtifactSaveStatus =
226
+ viewMode !== 'document' || documentConflict
227
+ ? 'idle'
228
+ : isSavingDocument || isDocumentSavePending
229
+ ? 'saving'
230
+ : generation
231
+ ? 'saved'
232
+ : 'idle';
233
+ useEffect(() => {
234
+ onSaveStatusChange?.(saveStatus);
235
+ }, [saveStatus, onSaveStatusChange]);
236
+
237
+ const contentRef = useRef(content);
238
+ const generationRef = useRef(generation);
239
+ const loadRequestRef = useRef(0);
240
+ const documentSaveTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
241
+ const pendingDocumentContentRef = useRef<string | undefined>(undefined);
242
+ const documentSaveInFlightRef = useRef(false);
243
+ const documentSavePromiseRef = useRef<Promise<boolean> | undefined>(undefined);
244
+ const persistPendingDocumentContentRef = useRef<() => Promise<boolean>>(() => Promise.resolve(false));
245
+ const documentEditorFocusedRef = useRef(false);
246
+ // Latches true once the user focuses the editor. The editor re-serializes (normalizes) the
247
+ // loaded Markdown when it mounts — e.g. after switching into edit mode — which fires an onChange
248
+ // that is NOT a user edit. We ignore those pre-interaction changes so they don't autosave the
249
+ // artifact or post a spurious "working copy updated" message to the chat.
250
+ const documentEditorInteractedRef = useRef(false);
251
+ const documentEditorRef = useRef<MarkdownDocumentEditorHandle | null>(null);
252
+ const documentBaseGenerationRef = useRef<string | undefined>(generation);
253
+ const documentBaseContentRef = useRef(initialContent);
254
+ const documentConflictRef = useRef<ArtifactDocumentConflict | undefined>(undefined);
255
+ // Working-copy content the agent is known to have seen: hydration and its own
256
+ // edits (loadContent) plus explicit hand-offs (flushDocumentChanges). Direct user
257
+ // edits are diffed against this baseline when they are sent to the agent.
258
+ const agentKnownContentRef = useRef(initialContent);
259
+ const documentEditorContainerRef = useRef<HTMLDivElement | null>(null);
260
+ const changeRegions = useMemo(
261
+ () => getMarkdownChangeRegions(baselineContent ?? content, content),
262
+ [baselineContent, content],
263
+ );
264
+ const documentLineCount = useMemo(() => content.split('\n').length, [content]);
265
+
266
+ const updateDocumentConflict = useCallback((conflict: ArtifactDocumentConflict | undefined) => {
267
+ documentConflictRef.current = conflict;
268
+ setDocumentConflict(conflict);
269
+ }, []);
270
+
271
+ useEffect(() => {
272
+ contentRef.current = content;
273
+ }, [content]);
274
+
275
+ useEffect(() => {
276
+ generationRef.current = generation;
277
+ }, [generation]);
278
+
279
+ // Entering edit mode mounts a fresh editor that re-serializes the loaded content. Require a new
280
+ // user focus before those serialization changes count as edits. Keyed on viewMode (a stable
281
+ // trigger) — not the per-render onEditor callback, which would reset the latch mid-typing.
282
+ useEffect(() => {
283
+ if (viewMode !== 'document') return;
284
+ documentEditorInteractedRef.current = false;
285
+ documentEditorFocusedRef.current = false;
286
+ }, [viewMode]);
287
+
288
+ const loadContent = useCallback(
289
+ async (allowHydrationRetry: boolean) => {
290
+ if (!runId) return;
291
+ const requestId = ++loadRequestRef.current;
292
+ setIsLoading(true);
293
+ setLoadError(undefined);
294
+
295
+ for (let attempt = 0; attempt <= HYDRATION_RETRY_LIMIT; attempt++) {
296
+ try {
297
+ const response = await client.agents.getArtifactContent(runId, path);
298
+ if (loadRequestRef.current !== requestId) return;
299
+ const previousContent = contentRef.current;
300
+ if (previousContent && previousContent !== response.content) {
301
+ setHighlightChangesFrom(previousContent);
302
+ setHighlightVersion((value) => value + 1);
303
+ }
304
+ contentRef.current = response.content;
305
+ generationRef.current = response.generation;
306
+ // Server states reached outside the user's typing pipeline come from
307
+ // hydration or the agent's own tools, so the agent knows them.
308
+ agentKnownContentRef.current = response.content;
309
+ if (
310
+ !documentEditorFocusedRef.current &&
311
+ pendingDocumentContentRef.current === undefined &&
312
+ !documentSaveInFlightRef.current
313
+ ) {
314
+ documentBaseGenerationRef.current = response.generation;
315
+ documentBaseContentRef.current = response.content;
316
+ }
317
+ setContent(response.content);
318
+ setGeneration(response.generation);
319
+ setLoadError(undefined);
320
+ setIsLoading(false);
321
+ onContentChange?.(response.content, response.generation);
322
+ return;
323
+ } catch (error: unknown) {
324
+ if (
325
+ loadRequestRef.current !== requestId ||
326
+ !allowHydrationRetry ||
327
+ getErrorStatus(error) !== 404 ||
328
+ attempt === HYDRATION_RETRY_LIMIT
329
+ ) {
330
+ if (loadRequestRef.current !== requestId) return;
331
+ setLoadError(errorMessage(error, t('agent.failedToLoadArtifact')));
332
+ setIsLoading(false);
333
+ return;
334
+ }
335
+ await new Promise((resolve) => setTimeout(resolve, HYDRATION_RETRY_DELAY_MS));
336
+ }
337
+ }
338
+ },
339
+ [client, onContentChange, path, runId, t],
340
+ );
341
+
342
+ useEffect(() => {
343
+ loadRequestRef.current++;
344
+ contentRef.current = initialContent;
345
+ generationRef.current = undefined;
346
+ agentKnownContentRef.current = initialContent;
347
+ documentBaseGenerationRef.current = undefined;
348
+ documentBaseContentRef.current = initialContent;
349
+ documentConflictRef.current = undefined;
350
+ documentEditorFocusedRef.current = false;
351
+ documentEditorInteractedRef.current = false;
352
+ setContent(initialContent);
353
+ setGeneration(undefined);
354
+ setHighlightChangesFrom(undefined);
355
+ setLoadError(undefined);
356
+ setDocumentConflict(undefined);
357
+ setIsResolvingConflict(false);
358
+ setIsDocumentSavePending(false);
359
+ setIsLoading(Boolean(runId));
360
+ if (runId) void loadContent(true);
361
+ return () => {
362
+ loadRequestRef.current++;
363
+ };
364
+ }, [initialContent, loadContent, runId]);
365
+
366
+ useEffect(() => {
367
+ if (!runId || refreshKey <= 0) return;
368
+ if (documentConflictRef.current) return;
369
+
370
+ const previousContent = contentRef.current;
371
+ const changedContent = applyArtifactRefreshChanges(previousContent, refreshDetails);
372
+ if (changedContent !== undefined && changedContent !== previousContent) {
373
+ contentRef.current = changedContent;
374
+ generationRef.current = undefined;
375
+ setHighlightChangesFrom(previousContent);
376
+ setHighlightVersion((value) => value + 1);
377
+ setContent(changedContent);
378
+ setGeneration(undefined);
379
+ onContentChange?.(changedContent);
380
+ }
381
+
382
+ void loadContent(false);
383
+ }, [loadContent, onContentChange, refreshDetails, refreshKey, runId]);
384
+
385
+ const applyResolvedDocumentContent = useCallback(
386
+ (nextContent: string, nextGeneration: string) => {
387
+ const previousContent = contentRef.current;
388
+ contentRef.current = nextContent;
389
+ generationRef.current = nextGeneration;
390
+ documentBaseContentRef.current = nextContent;
391
+ documentBaseGenerationRef.current = nextGeneration;
392
+ pendingDocumentContentRef.current = undefined;
393
+ updateDocumentConflict(undefined);
394
+ if (documentEditorRef.current?.getMarkdown() !== nextContent) {
395
+ documentEditorRef.current?.commands.setContent(nextContent, {
396
+ contentType: 'markdown',
397
+ emitUpdate: false,
398
+ });
399
+ }
400
+ if (previousContent !== nextContent) {
401
+ setHighlightChangesFrom(previousContent);
402
+ setHighlightVersion((value) => value + 1);
403
+ }
404
+ setContent(nextContent);
405
+ setGeneration(nextGeneration);
406
+ setIsDocumentSavePending(false);
407
+ onContentChange?.(nextContent, nextGeneration);
408
+ },
409
+ [onContentChange, updateDocumentConflict],
410
+ );
411
+
412
+ const reconcileDocumentConflict = useCallback(
413
+ async (
414
+ baseContent: string,
415
+ localContent: string,
416
+ pendingAction?: MarkdownEditingAction,
417
+ ): Promise<false | { content: string; generation: string }> => {
418
+ if (!runId) return false;
419
+
420
+ // Preserve the unsaved side immediately. No reload path may replace it
421
+ // until a merge succeeds or the user explicitly leaves the editor.
422
+ contentRef.current = localContent;
423
+ pendingDocumentContentRef.current = undefined;
424
+ setContent(localContent);
425
+ setIsDocumentSavePending(false);
426
+ setIsResolvingConflict(true);
427
+
428
+ let remoteContent: string | undefined;
429
+ let remoteGeneration: string | undefined;
430
+ try {
431
+ const remote = await client.agents.getArtifactContent(runId, path);
432
+ remoteContent = remote.content;
433
+ remoteGeneration = remote.generation;
434
+ agentKnownContentRef.current = remote.content;
435
+
436
+ const rebased = rebaseTextChanges(baseContent, localContent, remote.content);
437
+ if (rebased.status === 'conflict') {
438
+ updateDocumentConflict({
439
+ baseContent,
440
+ localContent,
441
+ remoteContent: remote.content,
442
+ remoteGeneration: remote.generation,
443
+ pendingAction,
444
+ });
445
+ return false;
446
+ }
447
+
448
+ let nextGeneration = remote.generation;
449
+ if (rebased.content !== remote.content) {
450
+ try {
451
+ const response = await client.agents.updateArtifactContent(runId, path, {
452
+ content: rebased.content,
453
+ generation: remote.generation,
454
+ });
455
+ nextGeneration = response.generation;
456
+ } catch (error: unknown) {
457
+ if (getErrorStatus(error) === 412) {
458
+ updateDocumentConflict({
459
+ baseContent: remote.content,
460
+ localContent: rebased.content,
461
+ remoteContent: remote.content,
462
+ remoteGeneration: remote.generation,
463
+ pendingAction,
464
+ });
465
+ return false;
466
+ }
467
+ throw error;
468
+ }
469
+ }
470
+
471
+ applyResolvedDocumentContent(rebased.content, nextGeneration);
472
+ toast({ status: 'success', title: t('agent.artifactConflictRebased'), duration: 3000 });
473
+ return { content: rebased.content, generation: nextGeneration };
474
+ } catch (error: unknown) {
475
+ updateDocumentConflict({
476
+ baseContent,
477
+ localContent,
478
+ remoteContent,
479
+ remoteGeneration,
480
+ pendingAction,
481
+ });
482
+ toast({
483
+ status: 'error',
484
+ title: t('agent.artifactConflictTitle'),
485
+ description: errorMessage(error, t('agent.failedToLoadArtifact')),
486
+ duration: 5000,
487
+ });
488
+ return false;
489
+ } finally {
490
+ setIsResolvingConflict(false);
491
+ }
492
+ },
493
+ [applyResolvedDocumentContent, client.agents, path, runId, t, toast, updateDocumentConflict],
494
+ );
495
+
496
+ const handleAction = useCallback(
497
+ async (action: MarkdownEditingAction) => {
498
+ if (action.action === 'comment') {
499
+ await onAction?.(action);
500
+ return;
501
+ }
502
+
503
+ if (documentConflictRef.current) {
504
+ toast({ status: 'warning', title: t('agent.artifactConflictTitle'), duration: 3000 });
505
+ return;
506
+ }
507
+
508
+ if (!runId || !generationRef.current) {
509
+ toast({ status: 'warning', title: t('agent.artifactEditingUnavailable'), duration: 3000 });
510
+ return;
511
+ }
512
+
513
+ const previousContent = contentRef.current;
514
+ const nextContent = applyMarkdownEditingChange(previousContent, action);
515
+ if (nextContent === undefined) {
516
+ toast({
517
+ status: 'error',
518
+ title: t('agent.documentEditApplyFailed'),
519
+ description: t('agent.documentEditApplyFailedDescription'),
520
+ duration: 5000,
521
+ });
522
+ return;
523
+ }
524
+
525
+ try {
526
+ const response = await client.agents.updateArtifactContent(runId, path, {
527
+ content: nextContent,
528
+ generation: generationRef.current,
529
+ });
530
+ contentRef.current = nextContent;
531
+ generationRef.current = response.generation;
532
+ documentBaseContentRef.current = nextContent;
533
+ documentBaseGenerationRef.current = response.generation;
534
+ setHighlightChangesFrom(previousContent);
535
+ setHighlightVersion((value) => value + 1);
536
+ setContent(nextContent);
537
+ setGeneration(response.generation);
538
+ onContentChange?.(nextContent, response.generation);
539
+ await onAction?.({ ...action, applied: true, base_version: response.generation });
540
+ agentKnownContentRef.current = nextContent;
541
+ } catch (error: unknown) {
542
+ const isConflict = getErrorStatus(error) === 412;
543
+ if (isConflict) {
544
+ const resolved = await reconcileDocumentConflict(previousContent, nextContent, action);
545
+ if (resolved) {
546
+ await onAction?.({ ...action, applied: true, base_version: resolved.generation });
547
+ agentKnownContentRef.current = resolved.content;
548
+ }
549
+ return;
550
+ }
551
+ toast({
552
+ status: 'error',
553
+ title: t('agent.documentEditApplyFailed'),
554
+ description: errorMessage(error, t('store.errorSavingTextDefault')),
555
+ duration: 5000,
556
+ });
557
+ }
558
+ },
559
+ [client, onAction, onContentChange, path, reconcileDocumentConflict, runId, t, toast],
560
+ );
561
+
562
+ const persistPendingDocumentContent = useCallback((): Promise<boolean> => {
563
+ if (!runId) return Promise.resolve(false);
564
+ if (documentSavePromiseRef.current) return documentSavePromiseRef.current;
565
+
566
+ const savePromise = (async () => {
567
+ documentSaveInFlightRef.current = true;
568
+ setIsSavingDocument(true);
569
+ try {
570
+ while (pendingDocumentContentRef.current !== undefined) {
571
+ const nextContent = pendingDocumentContentRef.current;
572
+ pendingDocumentContentRef.current = undefined;
573
+ const expectedGeneration = documentBaseGenerationRef.current;
574
+ if (!expectedGeneration) {
575
+ throw new Error(t('agent.artifactEditingUnavailable'));
576
+ }
577
+
578
+ const response = await client.agents.updateArtifactContent(runId, path, {
579
+ content: nextContent,
580
+ generation: expectedGeneration,
581
+ });
582
+ generationRef.current = response.generation;
583
+ documentBaseGenerationRef.current = response.generation;
584
+ documentBaseContentRef.current = nextContent;
585
+ setGeneration(response.generation);
586
+ onContentChange?.(contentRef.current, response.generation);
587
+ }
588
+ setIsDocumentSavePending(false);
589
+ return true;
590
+ } catch (error: unknown) {
591
+ const isConflict = getErrorStatus(error) === 412;
592
+ if (isConflict) {
593
+ const resolved = await reconcileDocumentConflict(
594
+ documentBaseContentRef.current,
595
+ contentRef.current,
596
+ );
597
+ return Boolean(resolved);
598
+ }
599
+ toast({
600
+ status: 'error',
601
+ title: t('agent.documentEditApplyFailed'),
602
+ description: errorMessage(error, t('store.errorSavingTextDefault')),
603
+ duration: 5000,
604
+ });
605
+ return false;
606
+ } finally {
607
+ documentSaveInFlightRef.current = false;
608
+ setIsSavingDocument(false);
609
+ }
610
+ })();
611
+
612
+ documentSavePromiseRef.current = savePromise;
613
+ void savePromise.finally(() => {
614
+ if (documentSavePromiseRef.current === savePromise) documentSavePromiseRef.current = undefined;
615
+ });
616
+ return savePromise;
617
+ }, [client.agents, onContentChange, path, reconcileDocumentConflict, runId, t, toast]);
618
+ persistPendingDocumentContentRef.current = persistPendingDocumentContent;
619
+
620
+ const flushDocumentChanges = useCallback(async (): Promise<false | ArtifactEditingSurfaceDocumentEdit> => {
621
+ if (documentSaveTimeoutRef.current !== undefined) {
622
+ clearTimeout(documentSaveTimeoutRef.current);
623
+ documentSaveTimeoutRef.current = undefined;
624
+ }
625
+
626
+ const latestEditorContent = documentEditorRef.current?.getMarkdown();
627
+ if (latestEditorContent !== undefined && latestEditorContent !== contentRef.current) {
628
+ contentRef.current = latestEditorContent;
629
+ setContent(latestEditorContent);
630
+ onContentChange?.(latestEditorContent, generationRef.current);
631
+ pendingDocumentContentRef.current = latestEditorContent;
632
+ }
633
+
634
+ const activeConflict = documentConflictRef.current;
635
+ if (activeConflict) {
636
+ const resolved = await reconcileDocumentConflict(activeConflict.baseContent, contentRef.current);
637
+ if (!resolved) return false;
638
+ }
639
+
640
+ while (pendingDocumentContentRef.current !== undefined || documentSavePromiseRef.current) {
641
+ const saved = await persistPendingDocumentContent();
642
+ if (!saved) return false;
643
+ }
644
+ if (!runId || !generationRef.current) return false;
645
+
646
+ const previous = agentKnownContentRef.current;
647
+ agentKnownContentRef.current = contentRef.current;
648
+ return { previous, current: contentRef.current };
649
+ }, [onContentChange, persistPendingDocumentContent, reconcileDocumentConflict, runId]);
650
+
651
+ useEffect(() => {
652
+ if (!flushChangesRef) return;
653
+ flushChangesRef.current = flushDocumentChanges;
654
+ return () => {
655
+ if (flushChangesRef.current === flushDocumentChanges) flushChangesRef.current = null;
656
+ };
657
+ }, [flushChangesRef, flushDocumentChanges]);
658
+
659
+ const handleDocumentChange = useCallback(
660
+ (nextContent: string) => {
661
+ if (nextContent === contentRef.current) return;
662
+ // A focused editor intentionally keeps its local document mounted while an agent
663
+ // refresh updates the backing artifact. Ignore the editor's stale base-content
664
+ // emission so it cannot be mistaken for a user edit and saved over the refresh.
665
+ if (
666
+ documentEditorFocusedRef.current &&
667
+ pendingDocumentContentRef.current === undefined &&
668
+ !documentSaveInFlightRef.current &&
669
+ nextContent === documentBaseContentRef.current
670
+ ) {
671
+ return;
672
+ }
673
+ // The editor normalizes loaded Markdown on mount, firing onChange before the user has
674
+ // touched it. Adopt that serialization as the baseline silently — no save, no message.
675
+ if (!documentEditorInteractedRef.current) {
676
+ contentRef.current = nextContent;
677
+ setContent(nextContent);
678
+ return;
679
+ }
680
+ contentRef.current = nextContent;
681
+ setContent(nextContent);
682
+ onDocumentEdit?.();
683
+ onContentChange?.(nextContent, generationRef.current);
684
+
685
+ const activeConflict = documentConflictRef.current;
686
+ if (activeConflict) {
687
+ updateDocumentConflict({ ...activeConflict, localContent: nextContent });
688
+ setIsDocumentSavePending(false);
689
+ return;
690
+ }
691
+
692
+ setIsDocumentSavePending(true);
693
+ pendingDocumentContentRef.current = nextContent;
694
+ if (documentSaveTimeoutRef.current !== undefined) clearTimeout(documentSaveTimeoutRef.current);
695
+ documentSaveTimeoutRef.current = setTimeout(() => {
696
+ documentSaveTimeoutRef.current = undefined;
697
+ void persistPendingDocumentContent();
698
+ }, DOCUMENT_SAVE_DEBOUNCE_MS);
699
+ },
700
+ [onContentChange, onDocumentEdit, persistPendingDocumentContent, updateDocumentConflict],
701
+ );
702
+
703
+ const handleDocumentFocusChange = useCallback((focused: boolean) => {
704
+ documentEditorFocusedRef.current = focused;
705
+ if (focused) documentEditorInteractedRef.current = true;
706
+ if (focused) {
707
+ documentBaseGenerationRef.current = generationRef.current;
708
+ documentBaseContentRef.current = contentRef.current;
709
+ } else if (pendingDocumentContentRef.current === undefined && !documentSaveInFlightRef.current) {
710
+ documentBaseGenerationRef.current = generationRef.current;
711
+ documentBaseContentRef.current = contentRef.current;
712
+ }
713
+ }, []);
714
+
715
+ const retryDocumentConflict = useCallback(async () => {
716
+ const activeConflict = documentConflictRef.current;
717
+ if (!activeConflict || isResolvingConflict) return;
718
+ const resolved = await reconcileDocumentConflict(
719
+ activeConflict.baseContent,
720
+ contentRef.current,
721
+ activeConflict.pendingAction,
722
+ );
723
+ if (resolved && activeConflict.pendingAction) {
724
+ await onAction?.({
725
+ ...activeConflict.pendingAction,
726
+ applied: true,
727
+ base_version: resolved.generation,
728
+ });
729
+ agentKnownContentRef.current = resolved.content;
730
+ }
731
+ }, [isResolvingConflict, onAction, reconcileDocumentConflict]);
732
+
733
+ const navigateToChangedLine = useCallback(
734
+ (line: number) => {
735
+ const scrollContainer = documentEditorContainerRef.current?.querySelector<HTMLElement>(
736
+ '.vertesia-markdown-document-editor-content',
737
+ );
738
+ if (!scrollContainer) return;
739
+ const ratio = Math.min(1, line / Math.max(1, documentLineCount - 1));
740
+ scrollContainer.scrollTo({
741
+ top: ratio * Math.max(0, scrollContainer.scrollHeight - scrollContainer.clientHeight),
742
+ behavior: 'smooth',
743
+ });
744
+ },
745
+ [documentLineCount],
746
+ );
747
+
748
+ useEffect(
749
+ () => () => {
750
+ if (documentSaveTimeoutRef.current !== undefined) clearTimeout(documentSaveTimeoutRef.current);
751
+ const latestEditorContent = documentEditorRef.current?.getMarkdown();
752
+ if (latestEditorContent !== undefined && latestEditorContent !== contentRef.current) {
753
+ contentRef.current = latestEditorContent;
754
+ pendingDocumentContentRef.current = latestEditorContent;
755
+ }
756
+ if (pendingDocumentContentRef.current !== undefined) {
757
+ void persistPendingDocumentContentRef.current();
758
+ }
759
+ },
760
+ [],
761
+ );
762
+
763
+ return (
764
+ <div className={className ?? 'relative h-full min-h-0 overflow-y-auto px-4 py-3'}>
765
+ {documentConflict ? (
766
+ <div className="absolute inset-x-4 top-3 z-30" role="alert">
767
+ <MessageBox status="warning" title={t('agent.artifactConflictTitle')} className="shadow-lg">
768
+ <div className="space-y-3">
769
+ <p>{t('agent.artifactConflictDescription')}</p>
770
+ <div className="flex justify-end">
771
+ <Button
772
+ variant="outline"
773
+ size="sm"
774
+ disabled={isResolvingConflict}
775
+ onClick={() => void retryDocumentConflict()}
776
+ >
777
+ {isResolvingConflict ? <Spinner size="sm" /> : null}
778
+ {t('agent.artifactConflictRetry')}
779
+ </Button>
780
+ </div>
781
+ </div>
782
+ </MessageBox>
783
+ </div>
784
+ ) : null}
785
+ {isLoading && !content ? (
786
+ <Center className="h-full min-h-[200px]">
787
+ <Spinner size="lg" />
788
+ </Center>
789
+ ) : loadError && !content ? (
790
+ <Center className="h-full min-h-[200px] flex-col gap-3 text-center text-muted">
791
+ <span>{loadError}</span>
792
+ <Button variant="outline" size="sm" onClick={() => void loadContent(false)}>
793
+ {t('agent.retry')}
794
+ </Button>
795
+ </Center>
796
+ ) : viewMode === 'document' ? (
797
+ <Suspense
798
+ fallback={
799
+ <Center className="h-full min-h-[200px]">
800
+ <Spinner size="lg" />
801
+ </Center>
802
+ }
803
+ >
804
+ <div className="flex h-full min-h-0 flex-col">
805
+ <div ref={documentEditorContainerRef} className="relative min-h-0 flex-1">
806
+ <MarkdownDocumentEditor
807
+ value={content}
808
+ onChange={handleDocumentChange}
809
+ artifactRunId={runId}
810
+ editable={!readOnly && Boolean(runId)}
811
+ externalValueSync="when-blurred"
812
+ onFocusChange={handleDocumentFocusChange}
813
+ onEditor={(editor) => {
814
+ documentEditorRef.current =
815
+ editor as unknown as MarkdownDocumentEditorHandle | null;
816
+ }}
817
+ onSendComment={
818
+ onSendMessage && runId && !documentConflict && commentsEnabled
819
+ ? onSendMessage
820
+ : undefined
821
+ }
822
+ onSendChangesToAgent={onSendChangesToAgent}
823
+ hasUnsentChanges={hasUnsentChanges}
824
+ isSendingChanges={isSendingChanges}
825
+ sendChangesDisabled={sendChangesDisabled || !runId || Boolean(documentConflict)}
826
+ toolbarStatus={toolbarStatus}
827
+ contentClassName="pe-5"
828
+ />
829
+ <MarkdownChangeRuler
830
+ regions={changeRegions}
831
+ totalLines={documentLineCount}
832
+ onNavigate={navigateToChangedLine}
833
+ />
834
+ {!onSaveStatusChange && saveStatus !== 'idle' ? (
835
+ <div
836
+ className={cn(
837
+ 'pointer-events-none absolute bottom-3 end-6 flex items-center gap-1.5 rounded-full',
838
+ 'border border-mixer-muted/25 bg-background/90 px-2.5 py-1 text-[11px] shadow-sm',
839
+ isSavingDocument || isDocumentSavePending ? 'text-muted' : 'text-success',
840
+ )}
841
+ >
842
+ {isSavingDocument || isDocumentSavePending ? (
843
+ <Spinner size="sm" />
844
+ ) : (
845
+ <Check className="size-3" />
846
+ )}
847
+ {isSavingDocument || isDocumentSavePending
848
+ ? t('agent.savingWorkingCopy')
849
+ : t('agent.savedToWorkingCopy')}
850
+ </div>
851
+ ) : null}
852
+ </div>
853
+ </div>
854
+ </Suspense>
855
+ ) : (
856
+ <div className="vprose prose-sm mx-auto max-w-5xl">
857
+ <CollaborativeMarkdownRenderer
858
+ artifactRunId={runId}
859
+ resource={{ kind: 'agent_artifact', run_id: runId ?? 'pending', path }}
860
+ baseVersion={generation}
861
+ readOnly={readOnly || !runId || Boolean(documentConflict)}
862
+ allowComments={commentsEnabled && Boolean(runId) && !documentConflict}
863
+ highlightChangesFrom={highlightChangesFrom}
864
+ highlightVersion={highlightVersion}
865
+ onAction={handleAction}
866
+ >
867
+ {content}
868
+ </CollaborativeMarkdownRenderer>
869
+ </div>
870
+ )}
871
+ {isLoading && content ? (
872
+ <div className="absolute end-3 top-3 rounded-full bg-background/80 p-2 shadow-sm">
873
+ <GitCommitHorizontal className="size-4 animate-pulse text-muted" />
874
+ </div>
875
+ ) : null}
876
+ </div>
877
+ );
878
+ }