@vertesia/ui 1.5.0-dev.20260722.120446Z → 1.5.0-dev.20260725.083715Z
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
- package/lib/core/components/shadcn/filters/filterBar.js +1 -1
- package/lib/core/components/shadcn/filters/filterBar.js.map +1 -1
- package/lib/core/components/shadcn/modal/dialog.d.ts +1 -1
- package/lib/core/components/shadcn/modal/dialog.d.ts.map +1 -1
- package/lib/core/components/shadcn/modal/dialog.js +2 -0
- package/lib/core/components/shadcn/modal/dialog.js.map +1 -1
- package/lib/core/components/shadcn/tabs.d.ts +12 -1
- package/lib/core/components/shadcn/tabs.d.ts.map +1 -1
- package/lib/core/components/shadcn/tabs.js +12 -4
- package/lib/core/components/shadcn/tabs.js.map +1 -1
- package/lib/features/agent/chat/AgentRightPanel.d.ts +5 -1
- package/lib/features/agent/chat/AgentRightPanel.d.ts.map +1 -1
- package/lib/features/agent/chat/AgentRightPanel.js +2 -2
- package/lib/features/agent/chat/AgentRightPanel.js.map +1 -1
- package/lib/features/agent/chat/ArtifactsTab.d.ts +5 -1
- package/lib/features/agent/chat/ArtifactsTab.d.ts.map +1 -1
- package/lib/features/agent/chat/ArtifactsTab.js +48 -11
- package/lib/features/agent/chat/ArtifactsTab.js.map +1 -1
- package/lib/features/agent/chat/DocumentEditingActionCard.d.ts +6 -0
- package/lib/features/agent/chat/DocumentEditingActionCard.d.ts.map +1 -0
- package/lib/features/agent/chat/DocumentEditingActionCard.js +129 -0
- package/lib/features/agent/chat/DocumentEditingActionCard.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentConversation.d.ts +19 -1
- package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.js +125 -27
- package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.d.ts +45 -0
- package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.d.ts.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.js +40 -0
- package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +3 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +94 -49
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts +2 -1
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js +9 -5
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts +25 -0
- package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js +33 -0
- package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts +3 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +22 -24
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.d.ts +14 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.d.ts.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.js +21 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.d.ts +30 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.d.ts.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.js +53 -0
- package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js +21 -2
- package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.d.ts +6 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.d.ts.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.js +29 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceLinks.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.d.ts +33 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.d.ts.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.js +129 -0
- package/lib/features/agent/chat/ModernAgentOutput/resourceSummary.js.map +1 -0
- package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts +7 -0
- package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/utils.js +11 -1
- package/lib/features/agent/chat/ModernAgentOutput/utils.js.map +1 -1
- package/lib/features/agent/chat/hooks/useAgentStream.d.ts +1 -1
- package/lib/features/agent/chat/hooks/useAgentStream.d.ts.map +1 -1
- package/lib/features/agent/chat/hooks/useAgentStream.js +24 -9
- package/lib/features/agent/chat/hooks/useAgentStream.js.map +1 -1
- package/lib/features/agent/chat/index.d.ts +2 -1
- package/lib/features/agent/chat/index.d.ts.map +1 -1
- package/lib/features/agent/chat/index.js +3 -0
- package/lib/features/agent/chat/index.js.map +1 -1
- package/lib/features/store/objects/DocumentSearchResults.js +2 -2
- package/lib/features/store/objects/DocumentSearchResults.js.map +1 -1
- package/lib/features/store/objects/components/ContentOverview.d.ts +2 -1
- package/lib/features/store/objects/components/ContentOverview.d.ts.map +1 -1
- package/lib/features/store/objects/components/ContentOverview.js +86 -15
- package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
- package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.d.ts +16 -0
- package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.d.ts.map +1 -0
- package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.js +116 -0
- package/lib/features/store/objects/components/DocumentEditingConfigurationSelector.js.map +1 -0
- package/lib/features/store/objects/components/DocumentEditingLockStatus.d.ts +7 -0
- package/lib/features/store/objects/components/DocumentEditingLockStatus.d.ts.map +1 -0
- package/lib/features/store/objects/components/DocumentEditingLockStatus.js +13 -0
- package/lib/features/store/objects/components/DocumentEditingLockStatus.js.map +1 -0
- package/lib/features/store/objects/components/DocumentEditingPanel.d.ts +48 -0
- package/lib/features/store/objects/components/DocumentEditingPanel.d.ts.map +1 -0
- package/lib/features/store/objects/components/DocumentEditingPanel.js +720 -0
- package/lib/features/store/objects/components/DocumentEditingPanel.js.map +1 -0
- package/lib/features/store/objects/components/TextEditorPanel.d.ts.map +1 -1
- package/lib/features/store/objects/components/TextEditorPanel.js +22 -9
- package/lib/features/store/objects/components/TextEditorPanel.js.map +1 -1
- package/lib/features/store/objects/components/documentArtifactRefs.d.ts +26 -0
- package/lib/features/store/objects/components/documentArtifactRefs.d.ts.map +1 -0
- package/lib/features/store/objects/components/documentArtifactRefs.js +52 -0
- package/lib/features/store/objects/components/documentArtifactRefs.js.map +1 -0
- package/lib/features/store/objects/components/documentEditingAgentConfig.d.ts +4 -0
- package/lib/features/store/objects/components/documentEditingAgentConfig.d.ts.map +1 -0
- package/lib/features/store/objects/components/documentEditingAgentConfig.js +40 -0
- package/lib/features/store/objects/components/documentEditingAgentConfig.js.map +1 -0
- package/lib/features/store/objects/components/documentEditingPrompts.d.ts +3 -0
- package/lib/features/store/objects/components/documentEditingPrompts.d.ts.map +1 -0
- package/lib/features/store/objects/components/documentEditingPrompts.js +13 -0
- package/lib/features/store/objects/components/documentEditingPrompts.js.map +1 -0
- package/lib/features/store/objects/components/documentEditingRun.d.ts +27 -0
- package/lib/features/store/objects/components/documentEditingRun.d.ts.map +1 -0
- package/lib/features/store/objects/components/documentEditingRun.js +126 -0
- package/lib/features/store/objects/components/documentEditingRun.js.map +1 -0
- package/lib/features/store/objects/components/documentEditingTarget.d.ts +20 -0
- package/lib/features/store/objects/components/documentEditingTarget.d.ts.map +1 -0
- package/lib/features/store/objects/components/documentEditingTarget.js +40 -0
- package/lib/features/store/objects/components/documentEditingTarget.js.map +1 -0
- package/lib/features/store/objects/components/index.d.ts +1 -0
- package/lib/features/store/objects/components/index.d.ts.map +1 -1
- package/lib/features/store/objects/components/index.js +1 -0
- package/lib/features/store/objects/components/index.js.map +1 -1
- package/lib/features/store/objects/components/useContentPanelHooks.d.ts +5 -1
- package/lib/features/store/objects/components/useContentPanelHooks.d.ts.map +1 -1
- package/lib/features/store/objects/components/useContentPanelHooks.js +53 -29
- package/lib/features/store/objects/components/useContentPanelHooks.js.map +1 -1
- package/lib/features/store/objects/components/useDownloadFile.d.ts +2 -1
- package/lib/features/store/objects/components/useDownloadFile.d.ts.map +1 -1
- package/lib/features/store/objects/components/useDownloadFile.js +3 -0
- package/lib/features/store/objects/components/useDownloadFile.js.map +1 -1
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +1 -1
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.js.map +1 -1
- package/lib/i18n/locales/ar.json +118 -1
- package/lib/i18n/locales/de.json +118 -1
- package/lib/i18n/locales/en.json +118 -0
- package/lib/i18n/locales/es.json +118 -1
- package/lib/i18n/locales/fr.json +118 -1
- package/lib/i18n/locales/it.json +118 -1
- package/lib/i18n/locales/ja.json +118 -1
- package/lib/i18n/locales/ko.json +118 -1
- package/lib/i18n/locales/pt.json +118 -1
- package/lib/i18n/locales/ru.json +118 -1
- package/lib/i18n/locales/tr.json +118 -1
- package/lib/i18n/locales/zh-TW.json +118 -1
- package/lib/i18n/locales/zh.json +118 -1
- package/lib/rich-text/EditorToolbar.d.ts +37 -0
- package/lib/rich-text/EditorToolbar.d.ts.map +1 -0
- package/lib/rich-text/EditorToolbar.js +215 -0
- package/lib/rich-text/EditorToolbar.js.map +1 -0
- package/lib/rich-text/VertesiaRichTextEditor.d.ts +26 -0
- package/lib/rich-text/VertesiaRichTextEditor.d.ts.map +1 -0
- package/lib/rich-text/VertesiaRichTextEditor.js +67 -0
- package/lib/rich-text/VertesiaRichTextEditor.js.map +1 -0
- package/lib/rich-text/index.d.ts +2 -0
- package/lib/rich-text/index.d.ts.map +1 -0
- package/lib/rich-text/index.js +2 -0
- package/lib/rich-text/index.js.map +1 -0
- package/lib/vertesia-ui-core.js +2 -2
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-features.js +35 -30
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-i18n.js +14 -14
- package/lib/vertesia-ui-i18n.js.map +1 -1
- package/lib/vertesia-ui-rich-text.js +5 -0
- package/lib/vertesia-ui-rich-text.js.map +1 -0
- package/lib/vertesia-ui-widgets.js +25 -7
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/lib/widgets/markdown/AgentResourceResolver.d.ts +31 -0
- package/lib/widgets/markdown/AgentResourceResolver.d.ts.map +1 -0
- package/lib/widgets/markdown/AgentResourceResolver.js +39 -0
- package/lib/widgets/markdown/AgentResourceResolver.js.map +1 -0
- package/lib/widgets/markdown/ArtifactEditingSurface.d.ts +63 -0
- package/lib/widgets/markdown/ArtifactEditingSurface.d.ts.map +1 -0
- package/lib/widgets/markdown/ArtifactEditingSurface.js +582 -0
- package/lib/widgets/markdown/ArtifactEditingSurface.js.map +1 -0
- package/lib/widgets/markdown/CollaborativeMarkdownRenderer.d.ts +69 -0
- package/lib/widgets/markdown/CollaborativeMarkdownRenderer.d.ts.map +1 -0
- package/lib/widgets/markdown/CollaborativeMarkdownRenderer.js +374 -0
- package/lib/widgets/markdown/CollaborativeMarkdownRenderer.js.map +1 -0
- package/lib/widgets/markdown/MarkdownLink.d.ts +3 -1
- package/lib/widgets/markdown/MarkdownLink.d.ts.map +1 -1
- package/lib/widgets/markdown/MarkdownLink.js +41 -14
- package/lib/widgets/markdown/MarkdownLink.js.map +1 -1
- package/lib/widgets/markdown/MarkdownRenderer.d.ts +8 -1
- package/lib/widgets/markdown/MarkdownRenderer.d.ts.map +1 -1
- package/lib/widgets/markdown/MarkdownRenderer.js +20 -4
- package/lib/widgets/markdown/MarkdownRenderer.js.map +1 -1
- package/lib/widgets/markdown/index.d.ts +5 -1
- package/lib/widgets/markdown/index.d.ts.map +1 -1
- package/lib/widgets/markdown/index.js +5 -1
- package/lib/widgets/markdown/index.js.map +1 -1
- package/lib/widgets/markdown/normalizeCustomSchemeLinks.d.ts.map +1 -1
- package/lib/widgets/markdown/normalizeCustomSchemeLinks.js +14 -2
- package/lib/widgets/markdown/normalizeCustomSchemeLinks.js.map +1 -1
- package/lib/widgets/markdown/textDiff.d.ts +52 -0
- package/lib/widgets/markdown/textDiff.d.ts.map +1 -0
- package/lib/widgets/markdown/textDiff.js +362 -0
- package/lib/widgets/markdown/textDiff.js.map +1 -0
- package/lib/widgets/markdown/useArtifactContent.d.ts +5 -3
- package/lib/widgets/markdown/useArtifactContent.d.ts.map +1 -1
- package/lib/widgets/markdown/useArtifactContent.js +6 -3
- package/lib/widgets/markdown/useArtifactContent.js.map +1 -1
- package/lib/widgets/markdown/useResolvedUrl.d.ts +1 -5
- package/lib/widgets/markdown/useResolvedUrl.d.ts.map +1 -1
- package/lib/widgets/markdown/useResolvedUrl.js +19 -31
- package/lib/widgets/markdown/useResolvedUrl.js.map +1 -1
- package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts +9 -0
- package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts.map +1 -1
- package/lib/widgets/xml-viewer/hooks/useXMLViewer.js +20 -17
- package/lib/widgets/xml-viewer/hooks/useXMLViewer.js.map +1 -1
- package/package.json +13 -9
- package/src/__tests__/a11y.test.tsx +4 -1
- package/src/__tests__/axe-helper.ts +17 -7
- package/src/__tests__/setup.ts +25 -0
- package/src/core/components/shadcn/filters/filterBar.tsx +1 -0
- package/src/core/components/shadcn/modal/dialog.tsx +3 -1
- package/src/core/components/shadcn/tabs.test.tsx +65 -0
- package/src/core/components/shadcn/tabs.tsx +25 -3
- package/src/features/agent/chat/AgentRightPanel.tsx +16 -1
- package/src/features/agent/chat/ArtifactsTab.test.tsx +55 -0
- package/src/features/agent/chat/ArtifactsTab.tsx +203 -52
- package/src/features/agent/chat/DocumentEditingActionCard.test.tsx +105 -0
- package/src/features/agent/chat/DocumentEditingActionCard.tsx +210 -0
- package/src/features/agent/chat/ModernAgentConversation.test.tsx +237 -40
- package/src/features/agent/chat/ModernAgentConversation.tsx +247 -75
- package/src/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.test.tsx +79 -0
- package/src/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.tsx +94 -0
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +257 -34
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +217 -105
- package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.tsx +17 -0
- package/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.test.tsx +44 -0
- package/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.tsx +88 -0
- package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +30 -46
- package/src/features/agent/chat/ModernAgentOutput/ResourceChangeSummary.tsx +40 -0
- package/src/features/agent/chat/ModernAgentOutput/ResourceChip.test.tsx +148 -0
- package/src/features/agent/chat/ModernAgentOutput/ResourceChip.tsx +141 -0
- package/src/features/agent/chat/ModernAgentOutput/ToolCallGroup.tsx +33 -1
- package/src/features/agent/chat/ModernAgentOutput/resourceLinks.tsx +43 -0
- package/src/features/agent/chat/ModernAgentOutput/resourceSummary.test.ts +229 -0
- package/src/features/agent/chat/ModernAgentOutput/resourceSummary.ts +152 -0
- package/src/features/agent/chat/ModernAgentOutput/utils.test.ts +17 -0
- package/src/features/agent/chat/ModernAgentOutput/utils.ts +14 -1
- package/src/features/agent/chat/hooks/useAgentStream.test.tsx +188 -0
- package/src/features/agent/chat/hooks/useAgentStream.ts +28 -8
- package/src/features/agent/chat/index.ts +11 -0
- package/src/features/store/objects/DocumentSearchResults.tsx +2 -2
- package/src/features/store/objects/components/ContentOverview.tsx +216 -65
- package/src/features/store/objects/components/DocumentEditingConfigurationSelector.test.ts +34 -0
- package/src/features/store/objects/components/DocumentEditingConfigurationSelector.tsx +211 -0
- package/src/features/store/objects/components/DocumentEditingLockStatus.test.tsx +32 -0
- package/src/features/store/objects/components/DocumentEditingLockStatus.tsx +45 -0
- package/src/features/store/objects/components/DocumentEditingPanel.test.tsx +336 -0
- package/src/features/store/objects/components/DocumentEditingPanel.tsx +1236 -0
- package/src/features/store/objects/components/TextEditorPanel.test.tsx +48 -0
- package/src/features/store/objects/components/TextEditorPanel.tsx +41 -20
- package/src/features/store/objects/components/documentArtifactRefs.test.ts +65 -0
- package/src/features/store/objects/components/documentArtifactRefs.ts +72 -0
- package/src/features/store/objects/components/documentEditingAgentConfig.test.ts +28 -0
- package/src/features/store/objects/components/documentEditingAgentConfig.ts +41 -0
- package/src/features/store/objects/components/documentEditingPrompts.test.ts +26 -0
- package/src/features/store/objects/components/documentEditingPrompts.ts +12 -0
- package/src/features/store/objects/components/documentEditingRun.test.ts +176 -0
- package/src/features/store/objects/components/documentEditingRun.ts +184 -0
- package/src/features/store/objects/components/documentEditingTarget.test.ts +96 -0
- package/src/features/store/objects/components/documentEditingTarget.ts +63 -0
- package/src/features/store/objects/components/index.ts +1 -0
- package/src/features/store/objects/components/useContentPanelHooks.ts +52 -27
- package/src/features/store/objects/components/useDownloadFile.test.ts +20 -0
- package/src/features/store/objects/components/useDownloadFile.ts +5 -1
- package/src/features/store/objects/selection/actions/StartWorkflowButton.tsx +2 -2
- package/src/i18n/locales/ar.json +118 -1
- package/src/i18n/locales/de.json +118 -1
- package/src/i18n/locales/en.json +118 -0
- package/src/i18n/locales/es.json +118 -1
- package/src/i18n/locales/fr.json +118 -1
- package/src/i18n/locales/it.json +118 -1
- package/src/i18n/locales/ja.json +118 -1
- package/src/i18n/locales/ko.json +118 -1
- package/src/i18n/locales/pt.json +118 -1
- package/src/i18n/locales/ru.json +118 -1
- package/src/i18n/locales/tr.json +118 -1
- package/src/i18n/locales/zh-TW.json +118 -1
- package/src/i18n/locales/zh.json +118 -1
- package/src/rich-text/EditorToolbar.tsx +708 -0
- package/src/rich-text/VertesiaRichTextEditor.test.tsx +211 -0
- package/src/rich-text/VertesiaRichTextEditor.tsx +241 -0
- package/src/rich-text/index.ts +9 -0
- package/src/session/UserSession.test.ts +11 -11
- package/src/widgets/markdown/AgentResourceResolver.test.ts +24 -0
- package/src/widgets/markdown/AgentResourceResolver.tsx +70 -0
- package/src/widgets/markdown/ArtifactEditingSurface.test.tsx +537 -0
- package/src/widgets/markdown/ArtifactEditingSurface.tsx +878 -0
- package/src/widgets/markdown/CollaborativeMarkdownRenderer.test.tsx +484 -0
- package/src/widgets/markdown/CollaborativeMarkdownRenderer.tsx +825 -0
- package/src/widgets/markdown/MarkdownLink.test.tsx +122 -0
- package/src/widgets/markdown/MarkdownLink.tsx +102 -17
- package/src/widgets/markdown/MarkdownRenderer.tsx +30 -3
- package/src/widgets/markdown/index.ts +39 -1
- package/src/widgets/markdown/normalizeCustomSchemeLinks.ts +15 -2
- package/src/widgets/markdown/textDiff.test.ts +156 -0
- package/src/widgets/markdown/textDiff.ts +399 -0
- package/src/widgets/markdown/useArtifactContent.ts +9 -3
- package/src/widgets/markdown/useResolvedUrl.test.ts +29 -40
- package/src/widgets/markdown/useResolvedUrl.ts +33 -35
- package/src/widgets/xml-viewer/hooks/useXMLViewer.test.ts +26 -0
- package/src/widgets/xml-viewer/hooks/useXMLViewer.ts +23 -18
|
@@ -15,6 +15,17 @@ export interface Tab {
|
|
|
15
15
|
is_allowed?: boolean;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Guard consulted before switching to another tab. Return `false` to cancel the switch — the active
|
|
20
|
+
* tab, the URL hash and the browser history are all left untouched, so the guard can hold the user
|
|
21
|
+
* on a tab that has unsaved changes while a confirmation is shown.
|
|
22
|
+
*
|
|
23
|
+
* It is only called when the target tab differs from the active one, but it may be called more than
|
|
24
|
+
* once for a single attempted switch (a click reaches both the trigger and the Radix root), so any
|
|
25
|
+
* side effect it performs must be idempotent.
|
|
26
|
+
*/
|
|
27
|
+
export type CanChangeTab = (nextTab: string, currentTab?: string) => boolean;
|
|
28
|
+
|
|
18
29
|
const TabsContext = React.createContext<{
|
|
19
30
|
size?: number;
|
|
20
31
|
tabs?: Tab[];
|
|
@@ -23,6 +34,7 @@ const TabsContext = React.createContext<{
|
|
|
23
34
|
responsive?: boolean;
|
|
24
35
|
variant?: 'tabs' | 'pills';
|
|
25
36
|
updateHash?: boolean;
|
|
37
|
+
canChangeTab?: CanChangeTab;
|
|
26
38
|
}>({
|
|
27
39
|
size: undefined,
|
|
28
40
|
tabs: undefined,
|
|
@@ -45,6 +57,7 @@ interface TabsProps {
|
|
|
45
57
|
responsive?: boolean;
|
|
46
58
|
variant?: 'tabs' | 'pills';
|
|
47
59
|
updateHash?: boolean;
|
|
60
|
+
canChangeTab?: CanChangeTab;
|
|
48
61
|
}
|
|
49
62
|
|
|
50
63
|
const Tabs = ({
|
|
@@ -59,6 +72,7 @@ const Tabs = ({
|
|
|
59
72
|
responsive = false,
|
|
60
73
|
variant = 'tabs',
|
|
61
74
|
updateHash = true,
|
|
75
|
+
canChangeTab,
|
|
62
76
|
}: TabsProps) => {
|
|
63
77
|
// Filter tabs based on is_allowed (undefined or true means visible)
|
|
64
78
|
const visibleTabs = React.useMemo(
|
|
@@ -121,6 +135,9 @@ const Tabs = ({
|
|
|
121
135
|
|
|
122
136
|
const handleValueChange = React.useCallback(
|
|
123
137
|
(newValue: string) => {
|
|
138
|
+
// Bail before touching the value or the history so a cancelled switch is a no-op.
|
|
139
|
+
if (newValue !== value && canChangeTab && !canChangeTab(newValue, value)) return;
|
|
140
|
+
|
|
124
141
|
setValue(newValue);
|
|
125
142
|
|
|
126
143
|
// Update the URL hash when tab changes (only if updateHash is true and not controlled by parent)
|
|
@@ -135,7 +152,7 @@ const Tabs = ({
|
|
|
135
152
|
onTabChange(newValue);
|
|
136
153
|
}
|
|
137
154
|
},
|
|
138
|
-
[current, onTabChange, updateHash],
|
|
155
|
+
[current, onTabChange, updateHash, canChangeTab, value],
|
|
139
156
|
);
|
|
140
157
|
|
|
141
158
|
const setTab = React.useCallback(
|
|
@@ -155,6 +172,7 @@ const Tabs = ({
|
|
|
155
172
|
responsive: responsive,
|
|
156
173
|
variant,
|
|
157
174
|
updateHash,
|
|
175
|
+
canChangeTab,
|
|
158
176
|
}}
|
|
159
177
|
>
|
|
160
178
|
<TabsPrimitive.Root
|
|
@@ -176,13 +194,17 @@ interface TabsBarProps {
|
|
|
176
194
|
}
|
|
177
195
|
|
|
178
196
|
const TabsBar = ({ className, sticky, direction }: TabsBarProps) => {
|
|
179
|
-
const { tabs, size, current, setTab, responsive, variant, updateHash } =
|
|
197
|
+
const { tabs, size, current, setTab, responsive, variant, updateHash, canChangeTab } =
|
|
198
|
+
React.useContext(TabsContext);
|
|
180
199
|
|
|
181
200
|
const fullWidth = size !== 0;
|
|
182
201
|
|
|
183
202
|
const handleTabChange = React.useCallback(
|
|
184
203
|
(tabName: string) => {
|
|
185
204
|
if (!tabs || !setTab) return;
|
|
205
|
+
// Consulted here too: this path pushes the tab href before `setTab` reaches the guard in
|
|
206
|
+
// `Tabs`, so a cancelled switch would otherwise leave the URL pointing at the other tab.
|
|
207
|
+
if (tabName !== current && canChangeTab && !canChangeTab(tabName, current)) return;
|
|
186
208
|
|
|
187
209
|
const tab = tabs.find((t) => t.name === tabName);
|
|
188
210
|
|
|
@@ -194,7 +216,7 @@ const TabsBar = ({ className, sticky, direction }: TabsBarProps) => {
|
|
|
194
216
|
|
|
195
217
|
setTab(tabName);
|
|
196
218
|
},
|
|
197
|
-
[tabs, setTab, updateHash],
|
|
219
|
+
[tabs, setTab, updateHash, canChangeTab, current],
|
|
198
220
|
);
|
|
199
221
|
|
|
200
222
|
if (!tabs || !setTab) {
|
|
@@ -633,6 +633,10 @@ export interface AgentRightPanelProps {
|
|
|
633
633
|
/** Show the Artifacts tab (opt-in, default false) */
|
|
634
634
|
showArtifacts?: boolean;
|
|
635
635
|
artifactRefreshKey?: number;
|
|
636
|
+
artifactRefreshDetails?: Record<string, unknown>;
|
|
637
|
+
selectedArtifactPath?: string | null;
|
|
638
|
+
onSelectedArtifactPathChange?: (path: string | null) => void;
|
|
639
|
+
onSendMessage?: (message: string, inputMetadata?: Record<string, unknown>) => void;
|
|
636
640
|
|
|
637
641
|
// Panel control
|
|
638
642
|
onClose: () => void;
|
|
@@ -672,6 +676,10 @@ function AgentRightPanelComponent({
|
|
|
672
676
|
// Artifacts
|
|
673
677
|
showArtifacts = false,
|
|
674
678
|
artifactRefreshKey = 0,
|
|
679
|
+
artifactRefreshDetails,
|
|
680
|
+
selectedArtifactPath,
|
|
681
|
+
onSelectedArtifactPathChange,
|
|
682
|
+
onSendMessage,
|
|
675
683
|
|
|
676
684
|
// Payload
|
|
677
685
|
payloadContent,
|
|
@@ -811,7 +819,14 @@ function AgentRightPanelComponent({
|
|
|
811
819
|
label: t('agent.artifacts'),
|
|
812
820
|
content: withTabBoundary(
|
|
813
821
|
t('agent.artifacts'),
|
|
814
|
-
<ArtifactsTab
|
|
822
|
+
<ArtifactsTab
|
|
823
|
+
runId={runId}
|
|
824
|
+
refreshKey={artifactRefreshKey}
|
|
825
|
+
refreshDetails={artifactRefreshDetails}
|
|
826
|
+
selectedPath={selectedArtifactPath}
|
|
827
|
+
onSelectedPathChange={onSelectedArtifactPathChange}
|
|
828
|
+
onSendMessage={onSendMessage}
|
|
829
|
+
/>,
|
|
815
830
|
),
|
|
816
831
|
is_allowed: showArtifacts,
|
|
817
832
|
},
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** @vitest-environment jsdom */
|
|
2
|
+
|
|
3
|
+
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
4
|
+
import type { ComponentProps } from 'react';
|
|
5
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
6
|
+
import { I18nProvider } from '../../../i18n/index.js';
|
|
7
|
+
import { ArtifactsTab } from './ArtifactsTab.js';
|
|
8
|
+
|
|
9
|
+
vi.mock('@vertesia/ui/session', () => ({
|
|
10
|
+
useUserSession: () => ({ client: {} }),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock('@vertesia/ui/widgets', () => ({
|
|
14
|
+
ArtifactEditingSurface: ({ viewMode }: { viewMode?: 'components' | 'document' }) => (
|
|
15
|
+
<div data-testid="artifact-editing-surface" data-view-mode={viewMode} />
|
|
16
|
+
),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
vi.mock('./hooks/useArtifacts.js', () => ({
|
|
20
|
+
useArtifacts: () => ({
|
|
21
|
+
tree: [],
|
|
22
|
+
flatFiles: [{ path: 'drafts/brief.md' }],
|
|
23
|
+
isLoading: false,
|
|
24
|
+
error: undefined,
|
|
25
|
+
refresh: vi.fn(),
|
|
26
|
+
}),
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
afterEach(cleanup);
|
|
30
|
+
|
|
31
|
+
function renderArtifactEditor(props?: Partial<ComponentProps<typeof ArtifactsTab>>) {
|
|
32
|
+
return render(
|
|
33
|
+
<I18nProvider lng="en">
|
|
34
|
+
<ArtifactsTab runId="run-1" selectedPath="drafts/brief.md" onSendMessage={vi.fn()} {...props} />
|
|
35
|
+
</I18nProvider>,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('ArtifactsTab', () => {
|
|
40
|
+
it('opens editable artifacts in block mode by default', () => {
|
|
41
|
+
renderArtifactEditor();
|
|
42
|
+
|
|
43
|
+
expect(screen.getByTestId('artifact-editing-surface').getAttribute('data-view-mode')).toBe('components');
|
|
44
|
+
expect(screen.getByRole('button', { name: 'Block mode' }).getAttribute('aria-pressed')).toBe('true');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('allows switching from block mode to the full editor', () => {
|
|
48
|
+
renderArtifactEditor();
|
|
49
|
+
|
|
50
|
+
fireEvent.click(screen.getByRole('button', { name: 'Edit mode' }));
|
|
51
|
+
|
|
52
|
+
expect(screen.getByTestId('artifact-editing-surface').getAttribute('data-view-mode')).toBe('document');
|
|
53
|
+
expect(screen.getByRole('button', { name: 'Edit mode' }).getAttribute('aria-pressed')).toBe('true');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import { Button, Center, ErrorBox, Input, Modal, ModalBody, ModalTitle, Switch } from '@vertesia/ui/core';
|
|
1
|
+
import { Button, Center, cn, ErrorBox, Input, Modal, ModalBody, ModalTitle, Switch, VTooltip } from '@vertesia/ui/core';
|
|
2
2
|
import { useUITranslation } from '@vertesia/ui/i18n';
|
|
3
3
|
import { useUserSession } from '@vertesia/ui/session';
|
|
4
|
+
import { ArtifactEditingSurface, type ArtifactSaveStatus, type MarkdownEditingAction } from '@vertesia/ui/widgets';
|
|
4
5
|
import {
|
|
6
|
+
ArrowLeftIcon,
|
|
7
|
+
CheckIcon,
|
|
5
8
|
ChevronDownIcon,
|
|
6
9
|
ChevronRightIcon,
|
|
7
10
|
DownloadIcon,
|
|
8
|
-
EyeIcon,
|
|
9
11
|
FileIcon,
|
|
10
12
|
FolderIcon,
|
|
11
13
|
FolderOpenIcon,
|
|
12
14
|
Loader2Icon,
|
|
13
15
|
PackageIcon,
|
|
16
|
+
PenLineIcon,
|
|
14
17
|
RefreshCwIcon,
|
|
18
|
+
Rows3Icon,
|
|
15
19
|
} from 'lucide-react';
|
|
16
20
|
import React, { useCallback, useId, useMemo, useState } from 'react';
|
|
17
21
|
import type { UniversalDocumentSource } from '../../document-viewer/UniversalDocumentViewer.js';
|
|
@@ -22,6 +26,14 @@ const UniversalDocumentViewer = React.lazy(async () => {
|
|
|
22
26
|
return { default: module.UniversalDocumentViewer };
|
|
23
27
|
});
|
|
24
28
|
|
|
29
|
+
/** Artifact file extensions that open in the Markdown editor rather than downloading. */
|
|
30
|
+
const EDITABLE_ARTIFACT_EXTENSIONS = new Set(['md', 'markdown', 'mdx', 'txt']);
|
|
31
|
+
|
|
32
|
+
function isEditableArtifact(path: string): boolean {
|
|
33
|
+
const extension = path.split('.').pop()?.toLowerCase();
|
|
34
|
+
return extension !== undefined && EDITABLE_ARTIFACT_EXTENSIONS.has(extension);
|
|
35
|
+
}
|
|
36
|
+
|
|
25
37
|
// ---------------------------------------------------------------------------
|
|
26
38
|
// Tree node component
|
|
27
39
|
// ---------------------------------------------------------------------------
|
|
@@ -30,26 +42,18 @@ interface TreeNodeProps {
|
|
|
30
42
|
node: ArtifactTreeNode;
|
|
31
43
|
depth: number;
|
|
32
44
|
runId: string;
|
|
33
|
-
|
|
45
|
+
onOpen: (relativePath: string) => void;
|
|
34
46
|
onDownload: (relativePath: string) => void;
|
|
35
47
|
downloadingPath: string | null;
|
|
36
48
|
forceExpanded?: boolean;
|
|
37
49
|
}
|
|
38
|
-
|
|
39
50
|
//** Convert a raw directory segment (e.g. "out_files") into a readable label ("Out Files"). */
|
|
40
51
|
function formatDirectoryLabel(name: string): string {
|
|
41
52
|
return name.replace(/[_-]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
42
53
|
}
|
|
43
54
|
|
|
44
|
-
function TreeNode({
|
|
45
|
-
|
|
46
|
-
depth,
|
|
47
|
-
runId,
|
|
48
|
-
onPreview,
|
|
49
|
-
onDownload,
|
|
50
|
-
downloadingPath,
|
|
51
|
-
forceExpanded = false,
|
|
52
|
-
}: TreeNodeProps) {
|
|
55
|
+
function TreeNode({ node, depth, runId, onOpen, onDownload, downloadingPath, forceExpanded = false }: TreeNodeProps) {
|
|
56
|
+
const { t } = useUITranslation();
|
|
53
57
|
const [expanded, setExpanded] = useState(false);
|
|
54
58
|
const isExpanded = forceExpanded || expanded;
|
|
55
59
|
|
|
@@ -84,7 +88,7 @@ function TreeNode({
|
|
|
84
88
|
node={child}
|
|
85
89
|
depth={depth + 1}
|
|
86
90
|
runId={runId}
|
|
87
|
-
|
|
91
|
+
onOpen={onOpen}
|
|
88
92
|
onDownload={onDownload}
|
|
89
93
|
downloadingPath={downloadingPath}
|
|
90
94
|
forceExpanded={forceExpanded}
|
|
@@ -97,45 +101,33 @@ function TreeNode({
|
|
|
97
101
|
const isDownloading = downloadingPath === node.path;
|
|
98
102
|
|
|
99
103
|
return (
|
|
100
|
-
<div
|
|
101
|
-
className="group flex min-w-0 items-center gap-1.5 rounded text-sm hover:bg-muted/30"
|
|
102
|
-
style={{ paddingInlineStart: `${depth * 14 + 4}px` }}
|
|
103
|
-
>
|
|
104
|
+
<div className="flex min-w-0 items-center gap-1">
|
|
104
105
|
<Button
|
|
105
106
|
variant="unstyled"
|
|
106
|
-
className="flex min-w-0 flex-1 items-center justify-start gap-1.5 px-1 py-1 text-start"
|
|
107
|
-
|
|
107
|
+
className="flex min-w-0 flex-1 items-center justify-start gap-1.5 rounded px-1 py-1 text-start text-sm hover:bg-muted/30"
|
|
108
|
+
style={{ paddingInlineStart: `${depth * 14 + 4}px` }}
|
|
109
|
+
onClick={() => onOpen(node.path)}
|
|
108
110
|
title={node.path}
|
|
109
111
|
>
|
|
110
112
|
<span className="size-3.5 shrink-0" />
|
|
111
113
|
<FileIcon className="size-4 shrink-0 text-muted" />
|
|
112
114
|
<span className="min-w-0 truncate">{node.name}</span>
|
|
113
115
|
</Button>
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
disabled={isDownloading}
|
|
130
|
-
aria-label="Download artifact"
|
|
131
|
-
>
|
|
132
|
-
{isDownloading ? (
|
|
133
|
-
<Loader2Icon className="size-3.5 animate-spin text-info" />
|
|
134
|
-
) : (
|
|
135
|
-
<DownloadIcon className="size-3.5" />
|
|
136
|
-
)}
|
|
137
|
-
</Button>
|
|
138
|
-
</div>
|
|
116
|
+
<Button
|
|
117
|
+
variant="ghost"
|
|
118
|
+
size="sm"
|
|
119
|
+
className="h-7 w-7 shrink-0 p-0"
|
|
120
|
+
onClick={() => onDownload(node.path)}
|
|
121
|
+
disabled={isDownloading}
|
|
122
|
+
aria-label={t('agent.download')}
|
|
123
|
+
title={t('agent.download')}
|
|
124
|
+
>
|
|
125
|
+
{isDownloading ? (
|
|
126
|
+
<Loader2Icon className="size-3.5 animate-spin text-info" />
|
|
127
|
+
) : (
|
|
128
|
+
<DownloadIcon className="size-3.5" />
|
|
129
|
+
)}
|
|
130
|
+
</Button>
|
|
139
131
|
</div>
|
|
140
132
|
);
|
|
141
133
|
}
|
|
@@ -184,8 +176,8 @@ function downloadUrl(url: string, filename: string) {
|
|
|
184
176
|
}
|
|
185
177
|
|
|
186
178
|
function getArtifactContentType(path: string): string | undefined {
|
|
187
|
-
const
|
|
188
|
-
switch (
|
|
179
|
+
const extension = path.split('.').pop()?.toLowerCase();
|
|
180
|
+
switch (extension) {
|
|
189
181
|
case 'css':
|
|
190
182
|
return 'text/css';
|
|
191
183
|
case 'gif':
|
|
@@ -226,6 +218,128 @@ function getArtifactContentType(path: string): string | undefined {
|
|
|
226
218
|
interface ArtifactsTabProps {
|
|
227
219
|
runId?: string;
|
|
228
220
|
refreshKey?: number;
|
|
221
|
+
refreshDetails?: Record<string, unknown>;
|
|
222
|
+
selectedPath?: string | null;
|
|
223
|
+
onSelectedPathChange?: (path: string | null) => void;
|
|
224
|
+
onSendMessage?: (message: string, inputMetadata?: Record<string, unknown>) => void;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
interface ArtifactMarkdownEditorProps {
|
|
228
|
+
runId: string;
|
|
229
|
+
path: string;
|
|
230
|
+
onBack: () => void;
|
|
231
|
+
onDownload: (path: string) => void;
|
|
232
|
+
refreshKey?: number;
|
|
233
|
+
refreshDetails?: Record<string, unknown>;
|
|
234
|
+
onSendMessage?: (message: string, inputMetadata?: Record<string, unknown>) => void;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function ArtifactMarkdownEditor({
|
|
238
|
+
runId,
|
|
239
|
+
path,
|
|
240
|
+
onBack,
|
|
241
|
+
onDownload,
|
|
242
|
+
refreshKey,
|
|
243
|
+
refreshDetails,
|
|
244
|
+
onSendMessage,
|
|
245
|
+
}: ArtifactMarkdownEditorProps) {
|
|
246
|
+
const { t } = useUITranslation();
|
|
247
|
+
const [viewMode, setViewMode] = useState<'components' | 'document'>('components');
|
|
248
|
+
const [saveStatus, setSaveStatus] = useState<ArtifactSaveStatus>('idle');
|
|
249
|
+
|
|
250
|
+
const handleAction = (action: MarkdownEditingAction) => {
|
|
251
|
+
if (!onSendMessage) return;
|
|
252
|
+
const blockType = action.anchor.block_type.replaceAll('_', ' ');
|
|
253
|
+
const displayMessage = action.comment?.trim() || t('agent.editedSelectionMessage', { blockType });
|
|
254
|
+
onSendMessage(displayMessage, { editing_action: action });
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<div className="flex h-full min-h-0 flex-col">
|
|
259
|
+
<div className="flex shrink-0 items-center gap-1 border-b border-border/60 px-2 py-1">
|
|
260
|
+
<Button
|
|
261
|
+
variant="ghost"
|
|
262
|
+
size="sm"
|
|
263
|
+
className="size-8 shrink-0 rounded-md p-0"
|
|
264
|
+
onClick={onBack}
|
|
265
|
+
aria-label={t('agent.backToArtifacts')}
|
|
266
|
+
title={t('agent.backToArtifacts')}
|
|
267
|
+
>
|
|
268
|
+
<ArrowLeftIcon className="size-4 cn-rtl-flip" />
|
|
269
|
+
</Button>
|
|
270
|
+
<div className="flex min-w-0 flex-1 items-center gap-1.5">
|
|
271
|
+
<span className="truncate text-sm font-medium" title={path}>
|
|
272
|
+
{path.split('/').pop()}
|
|
273
|
+
</span>
|
|
274
|
+
{saveStatus === 'saving' ? (
|
|
275
|
+
<VTooltip description={t('agent.savingWorkingCopy')} asChild>
|
|
276
|
+
<Loader2Icon
|
|
277
|
+
className="size-3.5 shrink-0 animate-spin text-muted"
|
|
278
|
+
aria-label={t('agent.savingWorkingCopy')}
|
|
279
|
+
/>
|
|
280
|
+
</VTooltip>
|
|
281
|
+
) : saveStatus === 'saved' ? (
|
|
282
|
+
<VTooltip description={t('agent.savedToWorkingCopy')} asChild>
|
|
283
|
+
<CheckIcon
|
|
284
|
+
className="size-3.5 shrink-0 text-success"
|
|
285
|
+
aria-label={t('agent.savedToWorkingCopy')}
|
|
286
|
+
/>
|
|
287
|
+
</VTooltip>
|
|
288
|
+
) : null}
|
|
289
|
+
</div>
|
|
290
|
+
<div className="flex items-center gap-0.5">
|
|
291
|
+
<Button
|
|
292
|
+
variant={viewMode === 'components' ? 'primary' : 'ghost'}
|
|
293
|
+
size="sm"
|
|
294
|
+
className="size-8 shrink-0 rounded-md p-0"
|
|
295
|
+
onClick={() => setViewMode('components')}
|
|
296
|
+
aria-label={t('agent.blockMode')}
|
|
297
|
+
aria-pressed={viewMode === 'components'}
|
|
298
|
+
title={t('agent.blockMode')}
|
|
299
|
+
>
|
|
300
|
+
<Rows3Icon className="size-4" />
|
|
301
|
+
</Button>
|
|
302
|
+
<Button
|
|
303
|
+
variant={viewMode === 'document' ? 'primary' : 'ghost'}
|
|
304
|
+
size="sm"
|
|
305
|
+
className="size-8 shrink-0 rounded-md p-0"
|
|
306
|
+
onClick={() => setViewMode('document')}
|
|
307
|
+
aria-label={t('agent.editMode')}
|
|
308
|
+
aria-pressed={viewMode === 'document'}
|
|
309
|
+
title={t('agent.editMode')}
|
|
310
|
+
>
|
|
311
|
+
<PenLineIcon className="size-4" />
|
|
312
|
+
</Button>
|
|
313
|
+
</div>
|
|
314
|
+
<Button
|
|
315
|
+
variant="ghost"
|
|
316
|
+
size="sm"
|
|
317
|
+
className="size-8 shrink-0 rounded-md p-0"
|
|
318
|
+
onClick={() => onDownload(path)}
|
|
319
|
+
aria-label={t('agent.download')}
|
|
320
|
+
title={t('agent.download')}
|
|
321
|
+
>
|
|
322
|
+
<DownloadIcon className="size-4" />
|
|
323
|
+
</Button>
|
|
324
|
+
</div>
|
|
325
|
+
<ArtifactEditingSurface
|
|
326
|
+
runId={runId}
|
|
327
|
+
path={path}
|
|
328
|
+
viewMode={viewMode}
|
|
329
|
+
refreshKey={refreshKey}
|
|
330
|
+
refreshDetails={refreshDetails}
|
|
331
|
+
readOnly={!onSendMessage}
|
|
332
|
+
onAction={handleAction}
|
|
333
|
+
onSaveStatusChange={setSaveStatus}
|
|
334
|
+
onSendMessage={onSendMessage ? (message) => onSendMessage(message) : undefined}
|
|
335
|
+
className={cn(
|
|
336
|
+
'relative min-h-0 flex-1',
|
|
337
|
+
// Edit mode self-scrolls; block mode needs the surface to scroll and pad its blocks.
|
|
338
|
+
viewMode === 'document' ? 'overflow-hidden' : 'overflow-y-auto px-6 py-5',
|
|
339
|
+
)}
|
|
340
|
+
/>
|
|
341
|
+
</div>
|
|
342
|
+
);
|
|
229
343
|
}
|
|
230
344
|
|
|
231
345
|
function ArtifactEmptyState({
|
|
@@ -246,7 +360,14 @@ function ArtifactEmptyState({
|
|
|
246
360
|
);
|
|
247
361
|
}
|
|
248
362
|
|
|
249
|
-
function ArtifactsTabComponent({
|
|
363
|
+
function ArtifactsTabComponent({
|
|
364
|
+
runId,
|
|
365
|
+
refreshKey = 0,
|
|
366
|
+
refreshDetails,
|
|
367
|
+
selectedPath,
|
|
368
|
+
onSelectedPathChange,
|
|
369
|
+
onSendMessage,
|
|
370
|
+
}: ArtifactsTabProps) {
|
|
250
371
|
const { t } = useUITranslation();
|
|
251
372
|
const { client } = useUserSession();
|
|
252
373
|
const [showSystem, setShowSystem] = useState(false);
|
|
@@ -259,6 +380,15 @@ function ArtifactsTabComponent({ runId, refreshKey = 0 }: ArtifactsTabProps) {
|
|
|
259
380
|
const [downloadingPath, setDownloadingPath] = useState<string | null>(null);
|
|
260
381
|
const [previewPath, setPreviewPath] = useState<string | null>(null);
|
|
261
382
|
const [filterValue, setFilterValue] = useState('');
|
|
383
|
+
const [internalSelectedPath, setInternalSelectedPath] = useState<string | null>(null);
|
|
384
|
+
const activeSelectedPath = selectedPath === undefined ? internalSelectedPath : selectedPath;
|
|
385
|
+
const setSelectedPath = useCallback(
|
|
386
|
+
(path: string | null) => {
|
|
387
|
+
if (selectedPath === undefined) setInternalSelectedPath(path);
|
|
388
|
+
onSelectedPathChange?.(path);
|
|
389
|
+
},
|
|
390
|
+
[onSelectedPathChange, selectedPath],
|
|
391
|
+
);
|
|
262
392
|
const normalizedFilterValue = filterValue.trim();
|
|
263
393
|
const filteredTree = useMemo(() => filterArtifactTree(tree, normalizedFilterValue), [tree, normalizedFilterValue]);
|
|
264
394
|
const visibleFileCount = useMemo(() => countTreeFiles(filteredTree), [filteredTree]);
|
|
@@ -280,9 +410,16 @@ function ArtifactsTabComponent({ runId, refreshKey = 0 }: ArtifactsTabProps) {
|
|
|
280
410
|
[client, runId],
|
|
281
411
|
);
|
|
282
412
|
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
413
|
+
const handleOpen = useCallback(
|
|
414
|
+
(relativePath: string) => {
|
|
415
|
+
if (isEditableArtifact(relativePath)) {
|
|
416
|
+
setSelectedPath(relativePath);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
setPreviewPath(relativePath);
|
|
420
|
+
},
|
|
421
|
+
[setSelectedPath],
|
|
422
|
+
);
|
|
286
423
|
|
|
287
424
|
const previewSource: UniversalDocumentSource | null =
|
|
288
425
|
runId && previewPath
|
|
@@ -345,6 +482,20 @@ function ArtifactsTabComponent({ runId, refreshKey = 0 }: ArtifactsTabProps) {
|
|
|
345
482
|
);
|
|
346
483
|
}
|
|
347
484
|
|
|
485
|
+
if (activeSelectedPath) {
|
|
486
|
+
return (
|
|
487
|
+
<ArtifactMarkdownEditor
|
|
488
|
+
runId={runId}
|
|
489
|
+
path={activeSelectedPath}
|
|
490
|
+
refreshKey={refreshKey}
|
|
491
|
+
refreshDetails={refreshDetails}
|
|
492
|
+
onBack={() => setSelectedPath(null)}
|
|
493
|
+
onDownload={(path) => void handleDownload(path)}
|
|
494
|
+
onSendMessage={onSendMessage}
|
|
495
|
+
/>
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
348
499
|
return (
|
|
349
500
|
<div className="flex flex-col h-full">
|
|
350
501
|
{/* Top bar */}
|
|
@@ -396,7 +547,7 @@ function ArtifactsTabComponent({ runId, refreshKey = 0 }: ArtifactsTabProps) {
|
|
|
396
547
|
node={node}
|
|
397
548
|
depth={0}
|
|
398
549
|
runId={runId}
|
|
399
|
-
|
|
550
|
+
onOpen={handleOpen}
|
|
400
551
|
onDownload={handleDownload}
|
|
401
552
|
downloadingPath={downloadingPath}
|
|
402
553
|
forceExpanded={!!normalizedFilterValue}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/** @vitest-environment jsdom */
|
|
2
|
+
|
|
3
|
+
import { cleanup, render, screen } from '@testing-library/react';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { I18nProvider } from '../../../i18n/index.js';
|
|
6
|
+
import { DocumentEditingActionCard, parseMarkdownEditingAction } from './DocumentEditingActionCard.js';
|
|
7
|
+
|
|
8
|
+
afterEach(cleanup);
|
|
9
|
+
|
|
10
|
+
const action = {
|
|
11
|
+
operation_id: 'operation-1',
|
|
12
|
+
resource: { kind: 'store_document', document_id: 'document-1', name: 'Launch plan' },
|
|
13
|
+
action: 'comment',
|
|
14
|
+
anchor: {
|
|
15
|
+
block_id: 'list_item:10:24',
|
|
16
|
+
block_type: 'list_item',
|
|
17
|
+
exact_text: '- Preserve unrelated sections.',
|
|
18
|
+
},
|
|
19
|
+
comment: 'Add one concrete example.',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe('DocumentEditingActionCard', () => {
|
|
23
|
+
it('parses and renders a compact comment card', () => {
|
|
24
|
+
const parsed = parseMarkdownEditingAction(action);
|
|
25
|
+
expect(parsed).toBeDefined();
|
|
26
|
+
if (!parsed) throw new Error('Expected a valid editing action');
|
|
27
|
+
|
|
28
|
+
render(
|
|
29
|
+
<I18nProvider lng="en">
|
|
30
|
+
<DocumentEditingActionCard action={parsed} />
|
|
31
|
+
</I18nProvider>,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(screen.getByText('Add one concrete example.')).not.toBeNull();
|
|
35
|
+
expect(screen.getByText('list item')).not.toBeNull();
|
|
36
|
+
expect(screen.getByText('Launch plan')).not.toBeNull();
|
|
37
|
+
expect(screen.queryByText('operation-1')).toBeNull();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('renders an applied edit as a saved notification', () => {
|
|
41
|
+
const parsed = parseMarkdownEditingAction({
|
|
42
|
+
operation_id: 'operation-2',
|
|
43
|
+
resource: { kind: 'store_document', document_id: 'document-1', name: 'Launch plan' },
|
|
44
|
+
action: 'edit',
|
|
45
|
+
anchor: {
|
|
46
|
+
block_id: 'list:10:24',
|
|
47
|
+
block_type: 'list',
|
|
48
|
+
exact_text: '- Preserve unrelated sections.',
|
|
49
|
+
},
|
|
50
|
+
user_change: {
|
|
51
|
+
before: '- Preserve unrelated sections.',
|
|
52
|
+
after: '- Preserve unrelated sections.\n- And be fun',
|
|
53
|
+
},
|
|
54
|
+
applied: true,
|
|
55
|
+
updated_document_id: 'document-2',
|
|
56
|
+
});
|
|
57
|
+
expect(parsed).toBeDefined();
|
|
58
|
+
if (!parsed) throw new Error('Expected a valid editing action');
|
|
59
|
+
expect(parsed.applied).toBe(true);
|
|
60
|
+
expect(parsed.updated_document_id).toBe('document-2');
|
|
61
|
+
|
|
62
|
+
render(
|
|
63
|
+
<I18nProvider lng="en">
|
|
64
|
+
<DocumentEditingActionCard action={parsed} />
|
|
65
|
+
</I18nProvider>,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
expect(screen.getByText('Edit applied')).not.toBeNull();
|
|
69
|
+
expect(screen.getByText('list')).not.toBeNull();
|
|
70
|
+
expect(screen.getByText('Launch plan')).not.toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('highlights the change as an inline diff', () => {
|
|
74
|
+
const parsed = parseMarkdownEditingAction({
|
|
75
|
+
operation_id: 'operation-3',
|
|
76
|
+
resource: { kind: 'store_document', document_id: 'document-1', name: 'Launch plan' },
|
|
77
|
+
action: 'edit',
|
|
78
|
+
anchor: {
|
|
79
|
+
block_id: 'paragraph:30:40',
|
|
80
|
+
block_type: 'paragraph',
|
|
81
|
+
exact_text: 'Keep the tone light.',
|
|
82
|
+
},
|
|
83
|
+
user_change: {
|
|
84
|
+
before: 'Keep the tone light.',
|
|
85
|
+
after: 'Keep the tone light. Session edit A.',
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
if (!parsed) throw new Error('Expected a valid editing action');
|
|
89
|
+
|
|
90
|
+
const { container } = render(
|
|
91
|
+
<I18nProvider lng="en">
|
|
92
|
+
<DocumentEditingActionCard action={parsed} />
|
|
93
|
+
</I18nProvider>,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const inserted = container.querySelector('ins');
|
|
97
|
+
expect(inserted?.textContent).toBe(' Session edit A.');
|
|
98
|
+
expect(container.querySelector('del')).toBeNull();
|
|
99
|
+
expect(screen.getByText('Keep the tone light.')).not.toBeNull();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('rejects malformed editing metadata', () => {
|
|
103
|
+
expect(parseMarkdownEditingAction({ action: 'comment' })).toBeUndefined();
|
|
104
|
+
});
|
|
105
|
+
});
|