@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,211 @@
1
+ /** @vitest-environment jsdom */
2
+
3
+ import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
4
+ import { MarkdownRichTextEditor, type MarkdownRichTextEditorProps, setEditorMarkdown } from '@vertesia/rich-text';
5
+ import { describe, expect, it, vi } from 'vitest';
6
+ import { I18nProvider } from '../i18n/index.js';
7
+ import {
8
+ VertesiaMarkdownComponentEditor,
9
+ VertesiaMarkdownDocumentEditor,
10
+ VertesiaMarkdownRichTextEditor,
11
+ vertesiaRichTextRenderers,
12
+ } from './VertesiaRichTextEditor.js';
13
+
14
+ describe('VertesiaMarkdownRichTextEditor', () => {
15
+ it('renders Markdown and emits serialized Markdown after an editor update', async () => {
16
+ const onChange = vi.fn();
17
+ let editor: Parameters<NonNullable<MarkdownRichTextEditorProps['onEditor']>>[0] = null;
18
+
19
+ render(
20
+ <VertesiaMarkdownRichTextEditor
21
+ value="# Original heading"
22
+ onChange={onChange}
23
+ onEditor={(nextEditor) => {
24
+ editor = nextEditor;
25
+ }}
26
+ />,
27
+ );
28
+
29
+ expect(await screen.findByRole('heading', { name: 'Original heading' })).not.toBeNull();
30
+ await waitFor(() => expect(editor).not.toBeNull());
31
+
32
+ act(() => {
33
+ if (editor) setEditorMarkdown(editor, '## Updated heading', true);
34
+ });
35
+
36
+ expect(await screen.findByRole('heading', { name: 'Updated heading' })).not.toBeNull();
37
+ expect(onChange).toHaveBeenLastCalledWith(expect.stringContaining('## Updated heading'));
38
+ });
39
+
40
+ it('provides adapters for each Vertesia-specific Markdown surface', () => {
41
+ expect(vertesiaRichTextRenderers.codeBlock).toBeTypeOf('function');
42
+ expect(vertesiaRichTextRenderers.image).toBeTypeOf('function');
43
+ expect(vertesiaRichTextRenderers.link).toBeTypeOf('function');
44
+ expect(vertesiaRichTextRenderers.opaqueBlock).toBeTypeOf('function');
45
+ });
46
+
47
+ it('keeps the editor instance when a consumer passes new renderer component identities', async () => {
48
+ const onEditor = vi.fn();
49
+ const view = render(<MarkdownRichTextEditor value="Paragraph" codeBlock={() => null} onEditor={onEditor} />);
50
+ await screen.findByText('Paragraph');
51
+ const initialEditor = onEditor.mock.calls.find(([value]) => value)?.[0];
52
+
53
+ view.rerender(<MarkdownRichTextEditor value="Paragraph" codeBlock={() => null} onEditor={onEditor} />);
54
+
55
+ expect(onEditor.mock.calls.filter(([value]) => value).at(-1)?.[0]).toBe(initialEditor);
56
+ expect(new Set(onEditor.mock.calls.flatMap(([value]) => (value ? [value] : [])))).toHaveLength(1);
57
+ });
58
+
59
+ it('defers external values while focused so the caret is not reset', async () => {
60
+ const view = render(<MarkdownRichTextEditor value="Original paragraph" />);
61
+ const textbox = await screen.findByRole('textbox');
62
+ fireEvent.focus(textbox);
63
+
64
+ view.rerender(<MarkdownRichTextEditor value="External paragraph" />);
65
+ expect(screen.getByText('Original paragraph')).not.toBeNull();
66
+ expect(screen.queryByText('External paragraph')).toBeNull();
67
+
68
+ fireEvent.blur(textbox);
69
+ expect(await screen.findByText('External paragraph')).not.toBeNull();
70
+ });
71
+
72
+ it('offers a formatting toolbar for both the compact component editor and the full-document editor', async () => {
73
+ const { rerender } = render(
74
+ <I18nProvider lng="en">
75
+ <VertesiaMarkdownComponentEditor value="Component text" />
76
+ </I18nProvider>,
77
+ );
78
+
79
+ expect(await screen.findByText('Component text')).not.toBeNull();
80
+ // The compact component editor now carries the same formatting toolbar as the document editor.
81
+ expect(await screen.findByRole('toolbar')).not.toBeNull();
82
+
83
+ rerender(
84
+ <I18nProvider lng="en">
85
+ <VertesiaMarkdownDocumentEditor value="# Full document" />
86
+ </I18nProvider>,
87
+ );
88
+
89
+ expect(await screen.findByRole('heading', { name: 'Full document' })).not.toBeNull();
90
+ expect(await screen.findByRole('toolbar')).not.toBeNull();
91
+ expect(screen.getByRole('textbox', { name: 'Markdown document editor' })).not.toBeNull();
92
+ });
93
+
94
+ it('puts the document hand-off in the editor toolbar and highlights it when edits are pending', async () => {
95
+ const onSendChangesToAgent = vi.fn();
96
+ const view = render(
97
+ <I18nProvider lng="en">
98
+ <VertesiaMarkdownDocumentEditor
99
+ value="Document text"
100
+ onSendChangesToAgent={onSendChangesToAgent}
101
+ hasUnsentChanges={false}
102
+ />
103
+ </I18nProvider>,
104
+ );
105
+
106
+ const inactiveButton = await screen.findByRole('button', { name: 'Send changes to agent' });
107
+ expect(screen.getByRole('toolbar').contains(inactiveButton)).toBe(true);
108
+ expect(inactiveButton).toHaveProperty('disabled', true);
109
+ expect(inactiveButton.className).not.toContain('bg-primary');
110
+
111
+ view.rerender(
112
+ <I18nProvider lng="en">
113
+ <VertesiaMarkdownDocumentEditor
114
+ value="Document text"
115
+ onSendChangesToAgent={onSendChangesToAgent}
116
+ hasUnsentChanges
117
+ />
118
+ </I18nProvider>,
119
+ );
120
+
121
+ const activeButton = screen.getByRole('button', { name: 'Send changes to agent' });
122
+ expect(activeButton).toHaveProperty('disabled', false);
123
+ expect(activeButton.className).toContain('bg-primary');
124
+ fireEvent.click(activeButton);
125
+ expect(onSendChangesToAgent).toHaveBeenCalledTimes(1);
126
+ });
127
+
128
+ it('sends selected-text comments while full-document content editing is locked', async () => {
129
+ const onSendComment = vi.fn();
130
+ let editor: Parameters<NonNullable<MarkdownRichTextEditorProps['onEditor']>>[0] = null;
131
+ render(
132
+ <I18nProvider lng="en">
133
+ <VertesiaMarkdownDocumentEditor
134
+ value="Locked paragraph."
135
+ editable={false}
136
+ onSendComment={onSendComment}
137
+ onEditor={(nextEditor) => {
138
+ editor = nextEditor;
139
+ }}
140
+ />
141
+ </I18nProvider>,
142
+ );
143
+
144
+ await waitFor(() => expect(editor).not.toBeNull());
145
+ act(() => {
146
+ editor?.commands.setTextSelection({ from: 1, to: 7 });
147
+ });
148
+ expect(screen.getByRole('button', { name: 'Bold' })).toHaveProperty('disabled', true);
149
+ const commentButton = await screen.findByRole('button', { name: 'Comment on selection' });
150
+ expect(screen.getByRole('toolbar').contains(commentButton)).toBe(false);
151
+ expect(commentButton.parentElement?.className).toContain('pointer-events-auto');
152
+ expect(commentButton.parentElement?.className).toContain('z-100');
153
+ fireEvent.click(commentButton);
154
+ expect(screen.getByRole('dialog', { name: 'Comment on selection' })).not.toBeNull();
155
+ fireEvent.change(screen.getByPlaceholderText('Describe how the agent should improve this section...'), {
156
+ target: { value: 'Make this claim measurable.' },
157
+ });
158
+ fireEvent.click(screen.getByRole('button', { name: 'Add comment' }));
159
+ fireEvent.click(screen.getByRole('button', { name: 'Send to agent' }));
160
+
161
+ await waitFor(() => expect(onSendComment).toHaveBeenCalledTimes(1));
162
+ expect(onSendComment).toHaveBeenCalledWith(expect.stringContaining('“Locked”'));
163
+ expect(onSendComment).toHaveBeenCalledWith(expect.stringContaining('Make this claim measurable.'));
164
+ });
165
+
166
+ it('opens structurally preserving normalized Markdown directly in rich-text mode', async () => {
167
+ const onChange = vi.fn();
168
+ render(
169
+ <I18nProvider lng="en">
170
+ <VertesiaMarkdownDocumentEditor value={'Setext heading\n=============='} onChange={onChange} />
171
+ </I18nProvider>,
172
+ );
173
+
174
+ expect(await screen.findByRole('heading', { name: 'Setext heading' })).not.toBeNull();
175
+ expect(screen.getByRole('textbox', { name: 'Markdown document editor' })).not.toBeNull();
176
+ expect(screen.queryByRole('heading', { name: 'Some Markdown formatting may change' })).toBeNull();
177
+ expect(onChange).not.toHaveBeenCalled();
178
+ });
179
+
180
+ it('lets the user preserve Markdown source when normalization is lossy', async () => {
181
+ const onChange = vi.fn();
182
+ render(
183
+ <I18nProvider lng="en">
184
+ <VertesiaMarkdownDocumentEditor value="#" onChange={onChange} />
185
+ </I18nProvider>,
186
+ );
187
+
188
+ expect(await screen.findByRole('heading', { name: 'Some Markdown formatting may change' })).not.toBeNull();
189
+ fireEvent.click(screen.getByRole('button', { name: 'Edit Markdown source' }));
190
+
191
+ const sourceEditor = screen.getByRole('textbox', { name: 'Markdown source editor' });
192
+ expect((sourceEditor as HTMLTextAreaElement).value).toBe('#');
193
+ fireEvent.change(sourceEditor, { target: { value: '##' } });
194
+ expect(onChange).toHaveBeenLastCalledWith('##');
195
+ });
196
+
197
+ it('lets the user accept a lossy conversion and continue in rich-text mode', async () => {
198
+ render(
199
+ <I18nProvider lng="en">
200
+ <VertesiaMarkdownDocumentEditor value="#" />
201
+ </I18nProvider>,
202
+ );
203
+
204
+ fireEvent.click(await screen.findByRole('button', { name: 'Continue with rich text' }));
205
+
206
+ expect(await screen.findByRole('textbox', { name: 'Markdown document editor' })).not.toBeNull();
207
+ });
208
+
209
+ // Table row/column actions live in the toolbar's Table context dropdown (only when the caret is
210
+ // in a table); list indent/outdent is handled by Tab / Shift-Tab rather than a toolbar control.
211
+ });
@@ -0,0 +1,241 @@
1
+ import {
2
+ type Editor,
3
+ getMarkdownCompatibility,
4
+ MarkdownComponentEditor,
5
+ type MarkdownComponentEditorProps,
6
+ MarkdownDocumentEditor,
7
+ type MarkdownDocumentEditorProps,
8
+ MarkdownRichTextEditor,
9
+ type MarkdownRichTextEditorProps,
10
+ type RichTextCodeBlockRendererProps,
11
+ type RichTextImageRendererProps,
12
+ type RichTextLinkRendererProps,
13
+ type RichTextOpaqueBlockRendererProps,
14
+ type RichTextRenderers,
15
+ } from '@vertesia/rich-text';
16
+ import { Button, cn, Modal, ModalBody, ModalFooter, ModalTitle, Textarea } from '@vertesia/ui/core';
17
+ import { useUITranslation } from '@vertesia/ui/i18n';
18
+ import { type ReactNode, useCallback, useState } from 'react';
19
+ import { CodeBlockHandlerProvider, useCodeBlockContext } from '../widgets/markdown/CodeBlockContext.js';
20
+ import { useCodeBlockRendererRegistry } from '../widgets/markdown/CodeBlockRendering.js';
21
+ import { createDefaultCodeBlockHandlers, ExpandCodeBlockHandler } from '../widgets/markdown/codeBlockHandlers.js';
22
+ import { MarkdownFigure } from '../widgets/markdown/MarkdownFigure.js';
23
+ import { MarkdownImage } from '../widgets/markdown/MarkdownImage.js';
24
+ import { MarkdownLink } from '../widgets/markdown/MarkdownLink.js';
25
+ import { MarkdownRenderer } from '../widgets/markdown/MarkdownRenderer.js';
26
+ import { EditorToolbar, SendChangesToAgentButton } from './EditorToolbar.js';
27
+
28
+ const defaultCodeBlockHandlers = createDefaultCodeBlockHandlers();
29
+
30
+ function VertesiaCodeBlockPreview({ code, language }: RichTextCodeBlockRendererProps) {
31
+ const registry = useCodeBlockRendererRegistry();
32
+ const CustomRenderer = language ? registry?.getComponent(language) : undefined;
33
+ const DefaultRenderer = language ? defaultCodeBlockHandlers[language] : undefined;
34
+ const Renderer = language?.startsWith('expand:') ? ExpandCodeBlockHandler : (CustomRenderer ?? DefaultRenderer);
35
+
36
+ if (!Renderer) return null;
37
+ return <Renderer code={code} language={language} />;
38
+ }
39
+
40
+ function VertesiaImagePreview({ src, alt, title }: RichTextImageRendererProps) {
41
+ const { artifactRunId } = useCodeBlockContext();
42
+ if (title) {
43
+ return <MarkdownFigure src={src} alt={alt} caption={title} artifactRunId={artifactRunId} />;
44
+ }
45
+ return <MarkdownImage src={src} alt={alt} artifactRunId={artifactRunId} />;
46
+ }
47
+
48
+ function VertesiaLinkPreview({ href, title, children }: RichTextLinkRendererProps) {
49
+ const { artifactRunId } = useCodeBlockContext();
50
+ return (
51
+ <MarkdownLink href={href} artifactRunId={artifactRunId}>
52
+ <span title={title}>{children}</span>
53
+ </MarkdownLink>
54
+ );
55
+ }
56
+
57
+ function VertesiaOpaqueBlockPreview({ raw }: RichTextOpaqueBlockRendererProps) {
58
+ const { artifactRunId } = useCodeBlockContext();
59
+ return <MarkdownRenderer artifactRunId={artifactRunId}>{raw}</MarkdownRenderer>;
60
+ }
61
+
62
+ export const vertesiaRichTextRenderers: RichTextRenderers = {
63
+ codeBlock: VertesiaCodeBlockPreview,
64
+ image: VertesiaImagePreview,
65
+ link: VertesiaLinkPreview,
66
+ opaqueBlock: VertesiaOpaqueBlockPreview,
67
+ };
68
+
69
+ interface VertesiaEditorProps {
70
+ artifactRunId?: string;
71
+ }
72
+
73
+ export interface VertesiaMarkdownRichTextEditorProps
74
+ extends Omit<MarkdownRichTextEditorProps, keyof RichTextRenderers>,
75
+ VertesiaEditorProps {}
76
+
77
+ export function VertesiaMarkdownRichTextEditor({ artifactRunId, ...props }: VertesiaMarkdownRichTextEditorProps) {
78
+ return (
79
+ <CodeBlockHandlerProvider artifactRunId={artifactRunId} MarkdownRenderer={MarkdownRenderer}>
80
+ <MarkdownRichTextEditor {...props} {...vertesiaRichTextRenderers} />
81
+ </CodeBlockHandlerProvider>
82
+ );
83
+ }
84
+
85
+ export interface VertesiaMarkdownComponentEditorProps
86
+ extends Omit<MarkdownComponentEditorProps, keyof RichTextRenderers>,
87
+ VertesiaEditorProps {}
88
+
89
+ export function VertesiaMarkdownComponentEditor({
90
+ artifactRunId,
91
+ className,
92
+ contentClassName,
93
+ editorClassName,
94
+ onEditor,
95
+ ...props
96
+ }: VertesiaMarkdownComponentEditorProps) {
97
+ const [editor, setEditor] = useState<Editor | null>(null);
98
+ const handleEditor = useCallback(
99
+ (next: Editor | null) => {
100
+ setEditor(next);
101
+ onEditor?.(next);
102
+ },
103
+ [onEditor],
104
+ );
105
+
106
+ return (
107
+ <CodeBlockHandlerProvider artifactRunId={artifactRunId} MarkdownRenderer={MarkdownRenderer}>
108
+ <div className={cn('overflow-hidden rounded-md border border-mixer-muted/30 bg-background', className)}>
109
+ <EditorToolbar editor={editor} editable={props.editable !== false} />
110
+ <MarkdownComponentEditor
111
+ {...props}
112
+ {...vertesiaRichTextRenderers}
113
+ onEditor={handleEditor}
114
+ contentClassName={cn('max-h-80 overflow-auto', contentClassName)}
115
+ editorClassName={cn('vprose prose-sm min-h-24 max-w-none px-3 py-2 outline-none', editorClassName)}
116
+ />
117
+ </div>
118
+ </CodeBlockHandlerProvider>
119
+ );
120
+ }
121
+
122
+ export interface VertesiaMarkdownDocumentEditorProps
123
+ extends Omit<MarkdownDocumentEditorProps, keyof RichTextRenderers>,
124
+ VertesiaEditorProps {
125
+ /** When set, the toolbar shows comment controls that queue and send a batch to the agent. */
126
+ onSendComment?: (message: string) => void | Promise<void>;
127
+ /** When set, the editor shows the document-level hand-off action in its own toolbar. */
128
+ onSendChangesToAgent?: () => void | Promise<void>;
129
+ hasUnsentChanges?: boolean;
130
+ isSendingChanges?: boolean;
131
+ sendChangesDisabled?: boolean;
132
+ /** Compact status rendered immediately beside the document hand-off action. */
133
+ toolbarStatus?: ReactNode;
134
+ }
135
+
136
+ export function VertesiaMarkdownDocumentEditor({
137
+ artifactRunId,
138
+ className,
139
+ contentClassName,
140
+ editorClassName,
141
+ onEditor,
142
+ onSendComment,
143
+ onSendChangesToAgent,
144
+ hasUnsentChanges = false,
145
+ isSendingChanges = false,
146
+ sendChangesDisabled = false,
147
+ toolbarStatus,
148
+ ...props
149
+ }: VertesiaMarkdownDocumentEditorProps) {
150
+ const { t } = useUITranslation();
151
+ const [editor, setEditor] = useState<Editor | null>(null);
152
+ const handleEditor = useCallback(
153
+ (next: Editor | null) => {
154
+ setEditor(next);
155
+ onEditor?.(next);
156
+ },
157
+ [onEditor],
158
+ );
159
+ const [editingMode, setEditingMode] = useState<'choose' | 'rich-text' | 'source'>(() =>
160
+ getMarkdownCompatibility(props.value) === 'lossy' ? 'choose' : 'rich-text',
161
+ );
162
+ const resolvedEditingMode = editingMode === 'choose' ? 'source' : editingMode;
163
+
164
+ return (
165
+ <CodeBlockHandlerProvider artifactRunId={artifactRunId} MarkdownRenderer={MarkdownRenderer}>
166
+ {resolvedEditingMode === 'rich-text' ? (
167
+ <div className={cn('flex h-full min-h-0 flex-col bg-background', className)}>
168
+ <EditorToolbar
169
+ editor={editor}
170
+ editable={props.editable !== false}
171
+ onSendComment={onSendComment}
172
+ onSendChangesToAgent={onSendChangesToAgent}
173
+ hasUnsentChanges={hasUnsentChanges}
174
+ isSendingChanges={isSendingChanges}
175
+ sendChangesDisabled={sendChangesDisabled}
176
+ toolbarStatus={toolbarStatus}
177
+ />
178
+ <MarkdownDocumentEditor
179
+ {...props}
180
+ {...vertesiaRichTextRenderers}
181
+ onEditor={handleEditor}
182
+ ariaLabel={props.ariaLabel ?? t('richText.documentEditor')}
183
+ className="flex min-h-0 flex-1 flex-col"
184
+ contentClassName={cn('min-h-0 flex-1 overflow-auto', contentClassName)}
185
+ editorClassName={cn(
186
+ 'vprose prose-sm mx-auto min-h-full w-full max-w-5xl px-6 py-5 outline-none',
187
+ editorClassName,
188
+ )}
189
+ />
190
+ </div>
191
+ ) : (
192
+ <div className={cn('flex h-full min-h-0 flex-col bg-background', className)}>
193
+ {onSendChangesToAgent ? (
194
+ <div className="flex min-h-10 shrink-0 items-center justify-end gap-1 border-b border-mixer-muted/25 px-1.5 py-1">
195
+ {toolbarStatus}
196
+ <SendChangesToAgentButton
197
+ onSend={onSendChangesToAgent}
198
+ hasUnsentChanges={hasUnsentChanges}
199
+ isSending={isSendingChanges}
200
+ disabled={sendChangesDisabled}
201
+ />
202
+ </div>
203
+ ) : null}
204
+ <Textarea
205
+ value={props.value}
206
+ onChange={(event) => props.onChange?.(event.target.value)}
207
+ onFocus={() => props.onFocusChange?.(true)}
208
+ onBlur={() => props.onFocusChange?.(false)}
209
+ disabled={props.editable === false}
210
+ aria-label={t('richText.markdownSourceEditor')}
211
+ className={cn(
212
+ 'vertesia-markdown-document-editor-content min-h-0 flex-1 resize-none rounded-none',
213
+ 'border-0 px-6 py-5 font-mono text-sm leading-6 outline-none focus-visible:ring-0',
214
+ contentClassName,
215
+ editorClassName,
216
+ )}
217
+ />
218
+ </div>
219
+ )}
220
+ <Modal
221
+ isOpen={editingMode === 'choose'}
222
+ onClose={() => setEditingMode('source')}
223
+ className="sm:max-w-lg"
224
+ disableCloseOnClickOutside
225
+ >
226
+ <ModalTitle>{t('richText.sourceCompatibilityWarningTitle')}</ModalTitle>
227
+ <ModalBody>
228
+ <p className="text-sm text-muted">{t('richText.sourceCompatibilityWarningDescription')}</p>
229
+ </ModalBody>
230
+ <ModalFooter align="right">
231
+ <Button variant="outline" onClick={() => setEditingMode('source')}>
232
+ {t('richText.editMarkdownSource')}
233
+ </Button>
234
+ <Button variant="secondary" onClick={() => setEditingMode('rich-text')}>
235
+ {t('richText.continueRichText')}
236
+ </Button>
237
+ </ModalFooter>
238
+ </Modal>
239
+ </CodeBlockHandlerProvider>
240
+ );
241
+ }
@@ -0,0 +1,9 @@
1
+ export {
2
+ VertesiaMarkdownComponentEditor,
3
+ type VertesiaMarkdownComponentEditorProps,
4
+ VertesiaMarkdownDocumentEditor,
5
+ type VertesiaMarkdownDocumentEditorProps,
6
+ VertesiaMarkdownRichTextEditor,
7
+ type VertesiaMarkdownRichTextEditorProps,
8
+ vertesiaRichTextRenderers,
9
+ } from './VertesiaRichTextEditor';
@@ -0,0 +1,48 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { HistoryNavigator } from './HistoryNavigator';
3
+
4
+ describe('HistoryNavigator link interception', () => {
5
+ afterEach(() => {
6
+ document.body.replaceChildren();
7
+ vi.restoreAllMocks();
8
+ });
9
+
10
+ it('leaves targeted internal links to native browser navigation', () => {
11
+ const navigator = new HistoryNavigator();
12
+ const navigate = vi.spyOn(navigator as unknown as { _navigate: () => void }, '_navigate');
13
+ const link = document.createElement('a');
14
+ const icon = document.createElement('span');
15
+ link.href = '/store/executions/run-1?p=project&a=account';
16
+ link.target = '_blank';
17
+ link.append(icon);
18
+ document.body.append(link);
19
+ navigator.start();
20
+
21
+ const event = new MouseEvent('click', { bubbles: true, cancelable: true, button: 0 });
22
+ icon.dispatchEvent(event);
23
+
24
+ expect(event.defaultPrevented).toBe(false);
25
+ expect(navigate).not.toHaveBeenCalled();
26
+ navigator.stop();
27
+ });
28
+
29
+ it('continues to intercept ordinary same-origin links', () => {
30
+ const navigator = new HistoryNavigator();
31
+ const navigate = vi
32
+ .spyOn(navigator as unknown as { _navigate: () => void }, '_navigate')
33
+ .mockImplementation(() => undefined);
34
+ const link = document.createElement('a');
35
+ const label = document.createElement('span');
36
+ link.href = '/store/executions/run-1';
37
+ link.append(label);
38
+ document.body.append(link);
39
+ navigator.start();
40
+
41
+ const event = new MouseEvent('click', { bubbles: true, cancelable: true, button: 0 });
42
+ label.dispatchEvent(event);
43
+
44
+ expect(event.defaultPrevented).toBe(true);
45
+ expect(navigate).toHaveBeenCalledOnce();
46
+ navigator.stop();
47
+ });
48
+ });
@@ -72,14 +72,16 @@ export interface NavigateOptions {
72
72
  title?: string;
73
73
  }
74
74
 
75
- function getElementHrefAsUrl(elem: HTMLElement) {
76
- if (elem && elem.tagName.toLowerCase() === 'a') {
77
- const href = (elem as HTMLAnchorElement)?.href.trim();
78
- if (typeof href === 'string' && href.length > 0) {
79
- return new URL(href);
80
- }
75
+ function getClosestAnchor(target: EventTarget | null): HTMLAnchorElement | null {
76
+ if (!(target instanceof Element)) {
77
+ return null;
81
78
  }
82
- return null;
79
+ return target.closest<HTMLAnchorElement>('a[href]');
80
+ }
81
+
82
+ function getElementHrefAsUrl(elem: HTMLAnchorElement) {
83
+ const href = elem.href.trim();
84
+ return href.length > 0 ? new URL(href) : null;
83
85
  }
84
86
 
85
87
  export class HistoryNavigator {
@@ -236,16 +238,25 @@ export class HistoryNavigator {
236
238
  this.fireLocationChange(new AfterLocationChangeEvent(type, to, state));
237
239
  };
238
240
  const _linkNavListener = (ev: MouseEvent) => {
239
- const target = ev.target as HTMLElement;
240
- // Skip anchors with download attribute or blob: URLs - they are file downloads, not navigation
241
+ // Let the browser handle modifier / non-primary clicks natively (open in new tab/window,
242
+ // download). Intercepting them here would preventDefault those affordances.
243
+ if (ev.defaultPrevented || ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey || ev.button !== 0) {
244
+ return;
245
+ }
246
+ const anchor = getClosestAnchor(ev.target);
247
+ if (!anchor) {
248
+ return;
249
+ }
250
+ const anchorTarget = anchor.getAttribute('target');
251
+ // Downloads, blob URLs, and explicitly targeted links require native browser navigation.
241
252
  if (
242
- target.tagName.toLowerCase() === 'a' &&
243
- ((target as HTMLAnchorElement).hasAttribute('download') ||
244
- (target as HTMLAnchorElement).href?.startsWith('blob:'))
253
+ anchor.hasAttribute('download') ||
254
+ anchor.href.startsWith('blob:') ||
255
+ (!!anchorTarget && anchorTarget.toLowerCase() !== '_self')
245
256
  ) {
246
257
  return;
247
258
  }
248
- const url = getElementHrefAsUrl(target);
259
+ const url = getElementHrefAsUrl(anchor);
249
260
  if (url && url.origin === window.location.origin) {
250
261
  ev.preventDefault();
251
262
  const to = new URL(this.addStickyParams(url.href));
@@ -1,7 +1,16 @@
1
- import type { SyntheticEvent } from 'react';
1
+ import type { MouseEvent, SyntheticEvent } from 'react';
2
2
  import { withMountBasename } from './path';
3
3
  import { useNavigate, useRouterContext } from './Router';
4
4
 
5
+ /**
6
+ * True when the click carries a modifier the browser uses for alternate link behavior
7
+ * (open in new tab/window, download) or is not a primary-button click. Such clicks must fall
8
+ * through to the native anchor so the browser can handle them instead of the SPA router.
9
+ */
10
+ function isModifiedClick(ev: MouseEvent): boolean {
11
+ return ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey || ev.button !== 0;
12
+ }
13
+
5
14
  /**
6
15
  * Wraps a <a href="..."> and perform the navigation to href through the router.
7
16
  */
@@ -71,19 +80,20 @@ export function NavLink({
71
80
  skipStickyParams,
72
81
  }: NavLinkProps) {
73
82
  const { router } = useRouterContext();
74
- // In-app route = not an external URL, `_blank` target, or bare hash. Relative paths count too —
75
- // they must be routed, else the global link listener re-applies the module base path (wrong URL).
83
+ // Resolve internal URLs even when a target makes navigation native. New-tab links still need
84
+ // the mount path and tenant sticky params, but must not be intercepted by the SPA router.
76
85
  const isAnchorOrEmpty = !href || href.startsWith('#');
77
- const isExternal = /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith('//') || (!!target && target !== '_self');
78
- const isInternal = !isAnchorOrEmpty && !isExternal;
86
+ const isExternalUrl = /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith('//');
87
+ const isInternalUrl = !isAnchorOrEmpty && !isExternalUrl;
88
+ const isNativeNavigation = !!target && target !== '_self';
79
89
  // Keep the rendered href under the served `<base href>` mount (correct middle-click / hover /
80
90
  // open-in-new-tab); the onClick navigates via the router which applies the same rule. No-op when
81
91
  // origin-served (Studio UI). Click handler below passes the raw `href` — navigate() re-bases it.
82
- const resolvedHref = isInternal
92
+ const resolvedHref = isInternalUrl
83
93
  ? withMountBasename(!skipStickyParams ? router.getTopRouter().navigator.addStickyParams(href) : href)
84
94
  : href;
85
- const _onClick = (ev: SyntheticEvent) => {
86
- if (ev.defaultPrevented || !isInternal) {
95
+ const _onClick = (ev: MouseEvent) => {
96
+ if (ev.defaultPrevented || !isInternalUrl || isNativeNavigation || isModifiedClick(ev)) {
87
97
  return;
88
98
  }
89
99
  ev.stopPropagation();
@@ -0,0 +1,39 @@
1
+ import type { VertesiaClient } from '@vertesia/client';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+ import { UserSession } from './UserSession';
4
+
5
+ function createSession(onboardingProgress: () => Promise<Record<string, boolean>>) {
6
+ const client = {
7
+ account: { onboardingProgress },
8
+ } as unknown as VertesiaClient;
9
+ const setSession = vi.fn<(session: UserSession) => void>();
10
+ const session = new UserSession(client, setSession);
11
+ return { session, setSession };
12
+ }
13
+
14
+ describe('UserSession.fetchOnboardingStatus', () => {
15
+ it('does not replace the session when an incomplete status is unchanged', async () => {
16
+ const { session, setSession } = createSession(async () => ({ project_created: false }));
17
+ session.onboardingComplete = false;
18
+
19
+ await expect(session.fetchOnboardingStatus()).resolves.toBe(false);
20
+ expect(setSession).not.toHaveBeenCalled();
21
+ });
22
+
23
+ it('updates the session and reports when onboarding becomes complete', async () => {
24
+ const { session, setSession } = createSession(async () => ({ project_created: true }));
25
+ session.onboardingComplete = false;
26
+
27
+ await expect(session.fetchOnboardingStatus()).resolves.toBe(true);
28
+ expect(setSession).toHaveBeenCalledOnce();
29
+ expect(setSession.mock.calls[0]?.[0]?.onboardingComplete).toBe(true);
30
+ });
31
+
32
+ it('publishes the initial incomplete status without reporting completion', async () => {
33
+ const { session, setSession } = createSession(async () => ({ project_created: false }));
34
+
35
+ await expect(session.fetchOnboardingStatus()).resolves.toBe(false);
36
+ expect(setSession).toHaveBeenCalledOnce();
37
+ expect(setSession.mock.calls[0]?.[0]?.onboardingComplete).toBe(false);
38
+ });
39
+ });