@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
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
export interface TextDiffSegment {
|
|
2
|
+
type: 'equal' | 'added' | 'removed';
|
|
3
|
+
text: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// Above this token count the LCS table gets expensive; fall back to a whole-text replace.
|
|
7
|
+
const MAX_LCS_TOKENS = 600;
|
|
8
|
+
|
|
9
|
+
function tokenize(text: string): string[] {
|
|
10
|
+
return text.split(/(\s+)/).filter((token) => token.length > 0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function pushSegment(segments: TextDiffSegment[], type: TextDiffSegment['type'], text: string): void {
|
|
14
|
+
if (!text) return;
|
|
15
|
+
const last = segments[segments.length - 1];
|
|
16
|
+
if (last && last.type === type) {
|
|
17
|
+
last.text += text;
|
|
18
|
+
} else {
|
|
19
|
+
segments.push({ type, text });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function lcsSegments(before: string[], after: string[], segments: TextDiffSegment[]): void {
|
|
24
|
+
// lengths[i][j] = LCS length of before[i..] and after[j..]
|
|
25
|
+
const rows = before.length + 1;
|
|
26
|
+
const cols = after.length + 1;
|
|
27
|
+
const lengths = new Uint32Array(rows * cols);
|
|
28
|
+
for (let i = before.length - 1; i >= 0; i--) {
|
|
29
|
+
for (let j = after.length - 1; j >= 0; j--) {
|
|
30
|
+
lengths[i * cols + j] =
|
|
31
|
+
before[i] === after[j]
|
|
32
|
+
? lengths[(i + 1) * cols + j + 1] + 1
|
|
33
|
+
: Math.max(lengths[(i + 1) * cols + j], lengths[i * cols + j + 1]);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let i = 0;
|
|
38
|
+
let j = 0;
|
|
39
|
+
while (i < before.length && j < after.length) {
|
|
40
|
+
if (before[i] === after[j]) {
|
|
41
|
+
pushSegment(segments, 'equal', before[i]);
|
|
42
|
+
i++;
|
|
43
|
+
j++;
|
|
44
|
+
} else if (lengths[(i + 1) * cols + j] >= lengths[i * cols + j + 1]) {
|
|
45
|
+
pushSegment(segments, 'removed', before[i]);
|
|
46
|
+
i++;
|
|
47
|
+
} else {
|
|
48
|
+
pushSegment(segments, 'added', after[j]);
|
|
49
|
+
j++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
pushSegment(segments, 'removed', before.slice(i).join(''));
|
|
53
|
+
pushSegment(segments, 'added', after.slice(j).join(''));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Word-level diff of two texts, returned as ordered segments suitable for
|
|
58
|
+
* rendering a unified inline diff. Whitespace is kept in the segments so the
|
|
59
|
+
* concatenation of `equal` + `removed` segments equals `before`, and
|
|
60
|
+
* `equal` + `added` segments equals `after`.
|
|
61
|
+
*/
|
|
62
|
+
export function diffWordSegments(before: string, after: string): TextDiffSegment[] {
|
|
63
|
+
if (before === after) {
|
|
64
|
+
return before ? [{ type: 'equal', text: before }] : [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const beforeTokens = tokenize(before);
|
|
68
|
+
const afterTokens = tokenize(after);
|
|
69
|
+
|
|
70
|
+
// Trim the common prefix and suffix so the quadratic LCS only runs on the changed middle.
|
|
71
|
+
let start = 0;
|
|
72
|
+
while (start < beforeTokens.length && start < afterTokens.length && beforeTokens[start] === afterTokens[start]) {
|
|
73
|
+
start++;
|
|
74
|
+
}
|
|
75
|
+
let beforeEnd = beforeTokens.length;
|
|
76
|
+
let afterEnd = afterTokens.length;
|
|
77
|
+
while (beforeEnd > start && afterEnd > start && beforeTokens[beforeEnd - 1] === afterTokens[afterEnd - 1]) {
|
|
78
|
+
beforeEnd--;
|
|
79
|
+
afterEnd--;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const segments: TextDiffSegment[] = [];
|
|
83
|
+
pushSegment(segments, 'equal', beforeTokens.slice(0, start).join(''));
|
|
84
|
+
|
|
85
|
+
const beforeMiddle = beforeTokens.slice(start, beforeEnd);
|
|
86
|
+
const afterMiddle = afterTokens.slice(start, afterEnd);
|
|
87
|
+
if (beforeMiddle.length * afterMiddle.length > MAX_LCS_TOKENS * MAX_LCS_TOKENS) {
|
|
88
|
+
pushSegment(segments, 'removed', beforeMiddle.join(''));
|
|
89
|
+
pushSegment(segments, 'added', afterMiddle.join(''));
|
|
90
|
+
} else {
|
|
91
|
+
lcsSegments(beforeMiddle, afterMiddle, segments);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
pushSegment(segments, 'equal', beforeTokens.slice(beforeEnd).join(''));
|
|
95
|
+
return segments;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
interface LineOp {
|
|
99
|
+
type: 'equal' | 'removed' | 'added';
|
|
100
|
+
line: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function lcsLineOps(before: string[], after: string[]): LineOp[] {
|
|
104
|
+
const ops: LineOp[] = [];
|
|
105
|
+
|
|
106
|
+
let start = 0;
|
|
107
|
+
while (start < before.length && start < after.length && before[start] === after[start]) {
|
|
108
|
+
ops.push({ type: 'equal', line: before[start] });
|
|
109
|
+
start++;
|
|
110
|
+
}
|
|
111
|
+
let beforeEnd = before.length;
|
|
112
|
+
let afterEnd = after.length;
|
|
113
|
+
const tail: LineOp[] = [];
|
|
114
|
+
while (beforeEnd > start && afterEnd > start && before[beforeEnd - 1] === after[afterEnd - 1]) {
|
|
115
|
+
beforeEnd--;
|
|
116
|
+
afterEnd--;
|
|
117
|
+
tail.unshift({ type: 'equal', line: before[beforeEnd] });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const beforeMiddle = before.slice(start, beforeEnd);
|
|
121
|
+
const afterMiddle = after.slice(start, afterEnd);
|
|
122
|
+
if (beforeMiddle.length * afterMiddle.length > MAX_LCS_TOKENS * MAX_LCS_TOKENS) {
|
|
123
|
+
for (const line of beforeMiddle) ops.push({ type: 'removed', line });
|
|
124
|
+
for (const line of afterMiddle) ops.push({ type: 'added', line });
|
|
125
|
+
return [...ops, ...tail];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const rows = beforeMiddle.length + 1;
|
|
129
|
+
const cols = afterMiddle.length + 1;
|
|
130
|
+
const lengths = new Uint32Array(rows * cols);
|
|
131
|
+
for (let i = beforeMiddle.length - 1; i >= 0; i--) {
|
|
132
|
+
for (let j = afterMiddle.length - 1; j >= 0; j--) {
|
|
133
|
+
lengths[i * cols + j] =
|
|
134
|
+
beforeMiddle[i] === afterMiddle[j]
|
|
135
|
+
? lengths[(i + 1) * cols + j + 1] + 1
|
|
136
|
+
: Math.max(lengths[(i + 1) * cols + j], lengths[i * cols + j + 1]);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
let i = 0;
|
|
140
|
+
let j = 0;
|
|
141
|
+
while (i < beforeMiddle.length && j < afterMiddle.length) {
|
|
142
|
+
if (beforeMiddle[i] === afterMiddle[j]) {
|
|
143
|
+
ops.push({ type: 'equal', line: beforeMiddle[i] });
|
|
144
|
+
i++;
|
|
145
|
+
j++;
|
|
146
|
+
} else if (lengths[(i + 1) * cols + j] >= lengths[i * cols + j + 1]) {
|
|
147
|
+
ops.push({ type: 'removed', line: beforeMiddle[i] });
|
|
148
|
+
i++;
|
|
149
|
+
} else {
|
|
150
|
+
ops.push({ type: 'added', line: afterMiddle[j] });
|
|
151
|
+
j++;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
while (i < beforeMiddle.length) ops.push({ type: 'removed', line: beforeMiddle[i++] });
|
|
155
|
+
while (j < afterMiddle.length) ops.push({ type: 'added', line: afterMiddle[j++] });
|
|
156
|
+
return [...ops, ...tail];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function splitLinesWithEndings(text: string): string[] {
|
|
160
|
+
return text.match(/[^\n]*\n|[^\n]+$/g) ?? [];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Document-scale text diff. Exact lines are aligned first, then word-level
|
|
165
|
+
* highlighting is applied only inside each changed line hunk. This avoids the
|
|
166
|
+
* word diff's bounded-LCS fallback turning a large document middle into one
|
|
167
|
+
* wholesale removal and addition.
|
|
168
|
+
*/
|
|
169
|
+
export function diffTextSegments(before: string, after: string): TextDiffSegment[] {
|
|
170
|
+
if (!before.includes('\n') && !after.includes('\n')) return diffWordSegments(before, after);
|
|
171
|
+
if (before === after) return before ? [{ type: 'equal', text: before }] : [];
|
|
172
|
+
|
|
173
|
+
const segments: TextDiffSegment[] = [];
|
|
174
|
+
const ops = lcsLineOps(splitLinesWithEndings(before), splitLinesWithEndings(after));
|
|
175
|
+
let index = 0;
|
|
176
|
+
while (index < ops.length) {
|
|
177
|
+
const op = ops[index];
|
|
178
|
+
if (op.type === 'equal') {
|
|
179
|
+
pushSegment(segments, 'equal', op.line);
|
|
180
|
+
index++;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
let removed = '';
|
|
185
|
+
let added = '';
|
|
186
|
+
while (index < ops.length && ops[index].type !== 'equal') {
|
|
187
|
+
const changed = ops[index++];
|
|
188
|
+
if (changed.type === 'removed') removed += changed.line;
|
|
189
|
+
if (changed.type === 'added') added += changed.line;
|
|
190
|
+
}
|
|
191
|
+
for (const segment of diffWordSegments(removed, added)) {
|
|
192
|
+
pushSegment(segments, segment.type, segment.text);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return segments;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface TextLineChangeRegion {
|
|
199
|
+
/** Zero-based line in the current document where the change starts. */
|
|
200
|
+
startLine: number;
|
|
201
|
+
/** Inclusive zero-based line in the current document where the change ends. */
|
|
202
|
+
endLine: number;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Returns distinct current-document line hunks for a change minimap. */
|
|
206
|
+
export function getTextLineChangeRegions(before: string, after: string): TextLineChangeRegion[] {
|
|
207
|
+
if (before === after) return [];
|
|
208
|
+
|
|
209
|
+
const regions: TextLineChangeRegion[] = [];
|
|
210
|
+
const ops = lcsLineOps(before.split('\n'), after.split('\n'));
|
|
211
|
+
let currentLine = 0;
|
|
212
|
+
let startLine: number | undefined;
|
|
213
|
+
|
|
214
|
+
const closeRegion = () => {
|
|
215
|
+
if (startLine === undefined) return;
|
|
216
|
+
regions.push({ startLine, endLine: Math.max(startLine, currentLine - 1) });
|
|
217
|
+
startLine = undefined;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
for (const op of ops) {
|
|
221
|
+
if (op.type === 'equal') {
|
|
222
|
+
closeRegion();
|
|
223
|
+
currentLine++;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
startLine ??= currentLine;
|
|
227
|
+
if (op.type === 'added') currentLine++;
|
|
228
|
+
}
|
|
229
|
+
closeRegion();
|
|
230
|
+
return regions;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
interface LineChange {
|
|
234
|
+
/** Inclusive start and exclusive end in base-document line coordinates. */
|
|
235
|
+
start: number;
|
|
236
|
+
end: number;
|
|
237
|
+
replacement: string[];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export type TextRebaseResult = { status: 'rebased'; content: string } | { status: 'conflict'; content: string };
|
|
241
|
+
|
|
242
|
+
function lineChanges(base: string[], changed: string[]): LineChange[] {
|
|
243
|
+
const changes: LineChange[] = [];
|
|
244
|
+
const ops = lcsLineOps(base, changed);
|
|
245
|
+
let baseIndex = 0;
|
|
246
|
+
let index = 0;
|
|
247
|
+
while (index < ops.length) {
|
|
248
|
+
if (ops[index].type === 'equal') {
|
|
249
|
+
baseIndex++;
|
|
250
|
+
index++;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const start = baseIndex;
|
|
255
|
+
const replacement: string[] = [];
|
|
256
|
+
while (index < ops.length && ops[index].type !== 'equal') {
|
|
257
|
+
const op = ops[index++];
|
|
258
|
+
if (op.type === 'removed') baseIndex++;
|
|
259
|
+
if (op.type === 'added') replacement.push(op.line);
|
|
260
|
+
}
|
|
261
|
+
changes.push({ start, end: baseIndex, replacement });
|
|
262
|
+
}
|
|
263
|
+
return changes;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function sameLineChange(left: LineChange, right: LineChange): boolean {
|
|
267
|
+
return (
|
|
268
|
+
left.start === right.start &&
|
|
269
|
+
left.end === right.end &&
|
|
270
|
+
left.replacement.length === right.replacement.length &&
|
|
271
|
+
left.replacement.every((line, index) => line === right.replacement[index])
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function lineChangesOverlap(left: LineChange, right: LineChange): boolean {
|
|
276
|
+
const leftInsertion = left.start === left.end;
|
|
277
|
+
const rightInsertion = right.start === right.end;
|
|
278
|
+
if (leftInsertion && rightInsertion) return left.start === right.start;
|
|
279
|
+
if (leftInsertion) return left.start >= right.start && left.start <= right.end;
|
|
280
|
+
if (rightInsertion) return right.start >= left.start && right.start <= left.end;
|
|
281
|
+
return left.start < right.end && right.start < left.end;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Conservatively reapplies local line edits onto a concurrently changed document.
|
|
286
|
+
* Identical edits are deduplicated. Any overlapping base ranges return a conflict
|
|
287
|
+
* and preserve `local` unchanged for recovery by the caller.
|
|
288
|
+
*/
|
|
289
|
+
export function rebaseTextChanges(base: string, local: string, remote: string): TextRebaseResult {
|
|
290
|
+
if (local === remote) return { status: 'rebased', content: local };
|
|
291
|
+
if (local === base) return { status: 'rebased', content: remote };
|
|
292
|
+
if (remote === base) return { status: 'rebased', content: local };
|
|
293
|
+
|
|
294
|
+
const baseLines = base.split('\n');
|
|
295
|
+
const localChanges = lineChanges(baseLines, local.split('\n'));
|
|
296
|
+
const remoteChanges = lineChanges(baseLines, remote.split('\n'));
|
|
297
|
+
const uniqueLocalChanges: LineChange[] = [];
|
|
298
|
+
|
|
299
|
+
for (const localChange of localChanges) {
|
|
300
|
+
let duplicate = false;
|
|
301
|
+
for (const remoteChange of remoteChanges) {
|
|
302
|
+
if (sameLineChange(localChange, remoteChange)) {
|
|
303
|
+
duplicate = true;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
if (lineChangesOverlap(localChange, remoteChange)) {
|
|
307
|
+
return { status: 'conflict', content: local };
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (!duplicate) uniqueLocalChanges.push(localChange);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const combined = [...remoteChanges, ...uniqueLocalChanges].sort(
|
|
314
|
+
(left, right) => left.start - right.start || left.end - right.end,
|
|
315
|
+
);
|
|
316
|
+
const merged: string[] = [];
|
|
317
|
+
let cursor = 0;
|
|
318
|
+
for (const change of combined) {
|
|
319
|
+
merged.push(...baseLines.slice(cursor, change.start), ...change.replacement);
|
|
320
|
+
cursor = change.end;
|
|
321
|
+
}
|
|
322
|
+
merged.push(...baseLines.slice(cursor));
|
|
323
|
+
return { status: 'rebased', content: merged.join('\n') };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export interface UnifiedLineDiffOptions {
|
|
327
|
+
/** Unchanged lines shown around each change (default 2). */
|
|
328
|
+
context?: number;
|
|
329
|
+
/** When set, returns undefined if the formatted diff would exceed this length. */
|
|
330
|
+
maxChars?: number;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Line-level unified diff (`@@ -a,b +c,d @@` hunks with `-`/`+`/space prefixes)
|
|
335
|
+
* of two texts. Returns undefined when the texts are identical, or when the
|
|
336
|
+
* formatted diff exceeds `maxChars` — callers fall back to a non-diff summary.
|
|
337
|
+
*/
|
|
338
|
+
export function createUnifiedLineDiff(
|
|
339
|
+
before: string,
|
|
340
|
+
after: string,
|
|
341
|
+
options: UnifiedLineDiffOptions = {},
|
|
342
|
+
): string | undefined {
|
|
343
|
+
if (before === after) return undefined;
|
|
344
|
+
const context = options.context ?? 2;
|
|
345
|
+
|
|
346
|
+
const ops = lcsLineOps(before.split('\n'), after.split('\n'));
|
|
347
|
+
|
|
348
|
+
// Group changed ops into hunks, merging hunks whose context would overlap.
|
|
349
|
+
const changed: Array<{ start: number; end: number }> = [];
|
|
350
|
+
for (let index = 0; index < ops.length; index++) {
|
|
351
|
+
if (ops[index].type === 'equal') continue;
|
|
352
|
+
const previous = changed[changed.length - 1];
|
|
353
|
+
if (previous && index - previous.end <= context * 2) {
|
|
354
|
+
previous.end = index + 1;
|
|
355
|
+
} else {
|
|
356
|
+
changed.push({ start: index, end: index + 1 });
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (changed.length === 0) return undefined;
|
|
360
|
+
|
|
361
|
+
const lines: string[] = [];
|
|
362
|
+
let beforeLine = 1;
|
|
363
|
+
let afterLine = 1;
|
|
364
|
+
let cursor = 0;
|
|
365
|
+
for (const hunk of changed) {
|
|
366
|
+
const start = Math.max(hunk.start - context, cursor === 0 ? 0 : cursor);
|
|
367
|
+
for (; cursor < start; cursor++) {
|
|
368
|
+
if (ops[cursor].type !== 'added') beforeLine++;
|
|
369
|
+
if (ops[cursor].type !== 'removed') afterLine++;
|
|
370
|
+
}
|
|
371
|
+
const end = Math.min(hunk.end + context, ops.length);
|
|
372
|
+
let beforeCount = 0;
|
|
373
|
+
let afterCount = 0;
|
|
374
|
+
const body: string[] = [];
|
|
375
|
+
for (let index = start; index < end; index++) {
|
|
376
|
+
const op = ops[index];
|
|
377
|
+
if (op.type === 'equal') {
|
|
378
|
+
body.push(` ${op.line}`);
|
|
379
|
+
beforeCount++;
|
|
380
|
+
afterCount++;
|
|
381
|
+
} else if (op.type === 'removed') {
|
|
382
|
+
body.push(`-${op.line}`);
|
|
383
|
+
beforeCount++;
|
|
384
|
+
} else {
|
|
385
|
+
body.push(`+${op.line}`);
|
|
386
|
+
afterCount++;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
lines.push(`@@ -${beforeLine},${beforeCount} +${afterLine},${afterCount} @@`, ...body);
|
|
390
|
+
for (; cursor < end; cursor++) {
|
|
391
|
+
if (ops[cursor].type !== 'added') beforeLine++;
|
|
392
|
+
if (ops[cursor].type !== 'removed') afterLine++;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const formatted = lines.join('\n');
|
|
397
|
+
if (options.maxChars !== undefined && formatted.length > options.maxChars) return undefined;
|
|
398
|
+
return formatted;
|
|
399
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* useArtifactContent - Hook to fetch artifact content from
|
|
2
|
+
* useArtifactContent - Hook to fetch artifact content from blob storage
|
|
3
3
|
*
|
|
4
4
|
* Used by expand:* code blocks to fetch and render artifact content inline.
|
|
5
5
|
*/
|
|
@@ -19,6 +19,8 @@ export interface ArtifactContentState<T = unknown> {
|
|
|
19
19
|
error: string | undefined;
|
|
20
20
|
/** Detected content type */
|
|
21
21
|
contentType: 'json' | 'text' | 'binary' | undefined;
|
|
22
|
+
/** Storage ETag when exposed by the signed artifact response. */
|
|
23
|
+
etag: string | undefined;
|
|
22
24
|
/** Retry the fetch */
|
|
23
25
|
retry: () => void;
|
|
24
26
|
}
|
|
@@ -59,8 +61,8 @@ function detectContentType(path: string): 'json' | 'text' | 'binary' {
|
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
/**
|
|
62
|
-
* Hook to fetch artifact content from
|
|
63
|
-
* Handles caching, loading states, and error handling.
|
|
64
|
+
* Hook to fetch artifact content from blob storage.
|
|
65
|
+
* Handles signed-URL caching, loading states, and error handling.
|
|
64
66
|
*
|
|
65
67
|
* @example
|
|
66
68
|
* ```tsx
|
|
@@ -90,6 +92,7 @@ export function useArtifactContent<T = unknown>({
|
|
|
90
92
|
isLoading: boolean;
|
|
91
93
|
error: string | undefined;
|
|
92
94
|
contentType: 'json' | 'text' | 'binary' | undefined;
|
|
95
|
+
etag?: string;
|
|
93
96
|
}>({
|
|
94
97
|
data: undefined,
|
|
95
98
|
rawContent: undefined,
|
|
@@ -156,6 +159,7 @@ export function useArtifactContent<T = unknown>({
|
|
|
156
159
|
isLoading: false,
|
|
157
160
|
error: undefined,
|
|
158
161
|
contentType,
|
|
162
|
+
etag: response.headers.get('etag')?.replace(/^"(.*)"$/, '$1'),
|
|
159
163
|
});
|
|
160
164
|
return;
|
|
161
165
|
}
|
|
@@ -181,6 +185,7 @@ export function useArtifactContent<T = unknown>({
|
|
|
181
185
|
isLoading: false,
|
|
182
186
|
error: undefined,
|
|
183
187
|
contentType,
|
|
188
|
+
etag: response.headers.get('etag')?.replace(/^"(.*)"$/, '$1'),
|
|
184
189
|
});
|
|
185
190
|
} catch (err) {
|
|
186
191
|
const errorMessage = err instanceof Error ? err.message : 'Failed to fetch artifact';
|
|
@@ -216,6 +221,7 @@ export function useArtifactContent<T = unknown>({
|
|
|
216
221
|
isLoading: state.isLoading,
|
|
217
222
|
error: state.error,
|
|
218
223
|
contentType: state.contentType,
|
|
224
|
+
etag: state.etag,
|
|
219
225
|
retry,
|
|
220
226
|
};
|
|
221
227
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import { parseUrlScheme } from './useResolvedUrl';
|
|
3
3
|
|
|
4
4
|
describe('parseUrlScheme', () => {
|
|
5
5
|
it('should parse artifact: URLs', () => {
|
|
@@ -50,9 +50,29 @@ describe('parseUrlScheme', () => {
|
|
|
50
50
|
expect(result.path).toBe('doc-id-123');
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
it('should
|
|
53
|
+
it('should normalize collection:// to its resource id', () => {
|
|
54
54
|
const { scheme, path } = parseUrlScheme('collection://my-collection');
|
|
55
|
-
expect(
|
|
55
|
+
expect(scheme).toBe('collection');
|
|
56
|
+
expect(path).toBe('my-collection');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it.each([
|
|
60
|
+
['interaction:int-1', 'interaction', 'int-1'],
|
|
61
|
+
['prompt:prm-1', 'prompt', 'prm-1'],
|
|
62
|
+
['agent:agt-1', 'agent', 'agt-1'],
|
|
63
|
+
['workflow:wf-1', 'workflow', 'wf-1'],
|
|
64
|
+
['process:proc-1', 'process', 'proc-1'],
|
|
65
|
+
['run:run-1', 'run', 'run-1'],
|
|
66
|
+
])('should parse %s as a route scheme', (raw, scheme, path) => {
|
|
67
|
+
const result = parseUrlScheme(raw);
|
|
68
|
+
expect(result.scheme).toBe(scheme);
|
|
69
|
+
expect(result.path).toBe(path);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should strip the // authority from interaction:// URLs', () => {
|
|
73
|
+
const result = parseUrlScheme('interaction://int-1');
|
|
74
|
+
expect(result.scheme).toBe('interaction');
|
|
75
|
+
expect(result.path).toBe('int-1');
|
|
56
76
|
});
|
|
57
77
|
|
|
58
78
|
it('should parse standard URLs', () => {
|
|
@@ -61,6 +81,12 @@ describe('parseUrlScheme', () => {
|
|
|
61
81
|
expect(result.path).toBe('https://example.com/page');
|
|
62
82
|
});
|
|
63
83
|
|
|
84
|
+
it('should treat an unknown scheme as standard', () => {
|
|
85
|
+
const result = parseUrlScheme('mailto:user@example.com');
|
|
86
|
+
expect(result.scheme).toBe('standard');
|
|
87
|
+
expect(result.path).toBe('mailto:user@example.com');
|
|
88
|
+
});
|
|
89
|
+
|
|
64
90
|
it('should parse relative URLs as standard', () => {
|
|
65
91
|
const result = parseUrlScheme('/path/to/page');
|
|
66
92
|
expect(result.scheme).toBe('standard');
|
|
@@ -78,40 +104,3 @@ describe('parseUrlScheme', () => {
|
|
|
78
104
|
expect(result.path).toBe('');
|
|
79
105
|
});
|
|
80
106
|
});
|
|
81
|
-
|
|
82
|
-
describe('mapSchemeToRoute', () => {
|
|
83
|
-
it('should map store: to /store/objects/', () => {
|
|
84
|
-
const result = mapSchemeToRoute('store', 'abc123');
|
|
85
|
-
expect(result).toBe('/store/objects/abc123');
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should map document: to /store/objects/', () => {
|
|
89
|
-
const result = mapSchemeToRoute('document', 'doc-id');
|
|
90
|
-
expect(result).toBe('/store/objects/doc-id');
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should map collection: to /store/collections/', () => {
|
|
94
|
-
const result = mapSchemeToRoute('collection', 'my-collection');
|
|
95
|
-
expect(result).toBe('/store/collections/my-collection');
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should return null for artifact: scheme', () => {
|
|
99
|
-
const result = mapSchemeToRoute('artifact', 'path/to/file');
|
|
100
|
-
expect(result).toBeNull();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should return null for image: scheme', () => {
|
|
104
|
-
const result = mapSchemeToRoute('image', 'path/to/image');
|
|
105
|
-
expect(result).toBeNull();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should return null for standard scheme', () => {
|
|
109
|
-
const result = mapSchemeToRoute('standard', 'https://example.com');
|
|
110
|
-
expect(result).toBeNull();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('should return null for empty path', () => {
|
|
114
|
-
const result = mapSchemeToRoute('store', '');
|
|
115
|
-
expect(result).toBeNull();
|
|
116
|
-
});
|
|
117
|
-
});
|
|
@@ -7,7 +7,31 @@ import {
|
|
|
7
7
|
useArtifactUrlCache,
|
|
8
8
|
} from '../../features/agent/chat/useArtifactUrlCache';
|
|
9
9
|
|
|
10
|
-
export type UrlScheme =
|
|
10
|
+
export type UrlScheme =
|
|
11
|
+
| 'artifact'
|
|
12
|
+
| 'image'
|
|
13
|
+
| 'store'
|
|
14
|
+
| 'document'
|
|
15
|
+
| 'collection'
|
|
16
|
+
| 'interaction'
|
|
17
|
+
| 'prompt'
|
|
18
|
+
| 'agent'
|
|
19
|
+
| 'workflow'
|
|
20
|
+
| 'process'
|
|
21
|
+
| 'run'
|
|
22
|
+
| 'standard';
|
|
23
|
+
|
|
24
|
+
const AGENT_RESOURCE_SCHEMES = new Set<UrlScheme>([
|
|
25
|
+
'store',
|
|
26
|
+
'document',
|
|
27
|
+
'collection',
|
|
28
|
+
'interaction',
|
|
29
|
+
'prompt',
|
|
30
|
+
'agent',
|
|
31
|
+
'workflow',
|
|
32
|
+
'process',
|
|
33
|
+
'run',
|
|
34
|
+
]);
|
|
11
35
|
|
|
12
36
|
export interface ResolvedUrlState {
|
|
13
37
|
/** The resolved URL, or undefined if not yet resolved */
|
|
@@ -49,34 +73,16 @@ export function parseUrlScheme(rawUrl: string): { scheme: UrlScheme; path: strin
|
|
|
49
73
|
if (rawUrl.startsWith('image:')) {
|
|
50
74
|
return { scheme: 'image', path: rawUrl.slice(6).trim() };
|
|
51
75
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (rawUrl.startsWith('collection:')) {
|
|
59
|
-
return { scheme: 'collection', path: stripSchemePath(rawUrl.slice(11)) };
|
|
76
|
+
const colonIndex = rawUrl.indexOf(':');
|
|
77
|
+
if (colonIndex > 0) {
|
|
78
|
+
const scheme = rawUrl.slice(0, colonIndex);
|
|
79
|
+
if (AGENT_RESOURCE_SCHEMES.has(scheme as UrlScheme)) {
|
|
80
|
+
return { scheme: scheme as UrlScheme, path: stripSchemePath(rawUrl.slice(colonIndex + 1)) };
|
|
81
|
+
}
|
|
60
82
|
}
|
|
61
83
|
return { scheme: 'standard', path: rawUrl };
|
|
62
84
|
}
|
|
63
85
|
|
|
64
|
-
/**
|
|
65
|
-
* Maps internal URL schemes to application routes
|
|
66
|
-
*/
|
|
67
|
-
export function mapSchemeToRoute(scheme: UrlScheme, path: string): string | null {
|
|
68
|
-
switch (scheme) {
|
|
69
|
-
case 'store':
|
|
70
|
-
return path ? `/store/objects/${path}` : null;
|
|
71
|
-
case 'document':
|
|
72
|
-
return path ? `/store/objects/${path}` : null;
|
|
73
|
-
case 'collection':
|
|
74
|
-
return path ? `/store/collections/${path}` : null;
|
|
75
|
-
default:
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
86
|
/**
|
|
81
87
|
* Hook to resolve custom URL schemes (artifact:, image:, etc.) to actual URLs.
|
|
82
88
|
* Handles caching, loading states, and error handling.
|
|
@@ -95,19 +101,11 @@ export function useResolvedUrl({
|
|
|
95
101
|
urlCacheRef.current = urlCache;
|
|
96
102
|
const { scheme, path } = parseUrlScheme(rawUrl);
|
|
97
103
|
|
|
98
|
-
// For schemes that map to routes, resolve immediately
|
|
99
|
-
const mappedRoute = mapSchemeToRoute(scheme, path);
|
|
100
|
-
|
|
101
104
|
const [state, setState] = useState<{
|
|
102
105
|
url: string | undefined;
|
|
103
106
|
isLoading: boolean;
|
|
104
107
|
error: string | undefined;
|
|
105
108
|
}>(() => {
|
|
106
|
-
// If it's a mapped route, use that immediately
|
|
107
|
-
if (mappedRoute) {
|
|
108
|
-
return { url: mappedRoute, isLoading: false, error: undefined };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
109
|
// If it's a standard URL, use as-is
|
|
112
110
|
if (scheme === 'standard') {
|
|
113
111
|
return { url: rawUrl, isLoading: false, error: undefined };
|
|
@@ -133,7 +131,7 @@ export function useResolvedUrl({
|
|
|
133
131
|
|
|
134
132
|
const fetchUrl = useCallback(async () => {
|
|
135
133
|
// Skip if already resolved or standard URL
|
|
136
|
-
if (
|
|
134
|
+
if (scheme === 'standard') {
|
|
137
135
|
return;
|
|
138
136
|
}
|
|
139
137
|
|
|
@@ -203,7 +201,7 @@ export function useResolvedUrl({
|
|
|
203
201
|
console.error('Failed to resolve URL:', path, err);
|
|
204
202
|
setState({ url: undefined, isLoading: false, error: errorMessage });
|
|
205
203
|
}
|
|
206
|
-
}, [scheme, path, artifactRunId, disposition
|
|
204
|
+
}, [scheme, path, artifactRunId, disposition]);
|
|
207
205
|
|
|
208
206
|
useEffect(() => {
|
|
209
207
|
// Skip if already resolved
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { parseXML } from './useXMLViewer';
|
|
3
|
+
|
|
4
|
+
describe('parseXML', () => {
|
|
5
|
+
it('parses valid XML', () => {
|
|
6
|
+
const result = parseXML('<root><item id="1">value</item></root>');
|
|
7
|
+
|
|
8
|
+
expect(result.valid).toBe(true);
|
|
9
|
+
expect(result.json).not.toBeNull();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('allows parsererror elements in valid XML content', () => {
|
|
13
|
+
const result = parseXML('<root><parsererror>expected content</parsererror></root>');
|
|
14
|
+
|
|
15
|
+
expect(result.valid).toBe(true);
|
|
16
|
+
expect(result.json).not.toBeNull();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('rejects invalid XML', () => {
|
|
20
|
+
const result = parseXML('<root><item></root>');
|
|
21
|
+
|
|
22
|
+
expect(result.valid).toBe(false);
|
|
23
|
+
expect(result.json).toBeNull();
|
|
24
|
+
expect(result.errorMessage).toContain('Fail to parse:');
|
|
25
|
+
});
|
|
26
|
+
});
|