@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,122 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { type AgentResourceResolver, AgentResourceResolverProvider } from './AgentResourceResolver.js';
|
|
6
|
+
import type { MarkdownLinkProps } from './MarkdownLink.js';
|
|
7
|
+
import { MarkdownLink } from './MarkdownLink.js';
|
|
8
|
+
|
|
9
|
+
vi.mock('./useResolvedUrl.js', async (importOriginal) => {
|
|
10
|
+
const actual = await importOriginal<typeof import('./useResolvedUrl.js')>();
|
|
11
|
+
return {
|
|
12
|
+
...actual,
|
|
13
|
+
useResolvedUrl: () => ({
|
|
14
|
+
url: 'https://example.test/artifact',
|
|
15
|
+
isLoading: false,
|
|
16
|
+
error: undefined,
|
|
17
|
+
scheme: 'artifact' as const,
|
|
18
|
+
retry: vi.fn(),
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(cleanup);
|
|
24
|
+
|
|
25
|
+
describe('MarkdownLink artifact navigation', () => {
|
|
26
|
+
it('opens a Markdown artifact in the host viewer', () => {
|
|
27
|
+
const onArtifactOpen = vi.fn();
|
|
28
|
+
render(
|
|
29
|
+
<MarkdownLink href="artifact:files/report.md" artifactRunId="run-1" onArtifactOpen={onArtifactOpen}>
|
|
30
|
+
Report
|
|
31
|
+
</MarkdownLink>,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
fireEvent.click(screen.getByRole('link', { name: 'Report' }));
|
|
35
|
+
|
|
36
|
+
expect(onArtifactOpen).toHaveBeenCalledWith('files/report.md');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('keeps modified clicks and non-Markdown artifacts as downloads', () => {
|
|
40
|
+
const onArtifactOpen = vi.fn();
|
|
41
|
+
const view = render(
|
|
42
|
+
<MarkdownLink href="artifact:files/report.md" artifactRunId="run-1" onArtifactOpen={onArtifactOpen}>
|
|
43
|
+
Report
|
|
44
|
+
</MarkdownLink>,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
fireEvent.click(screen.getByRole('link', { name: 'Report' }), { metaKey: true });
|
|
48
|
+
expect(onArtifactOpen).not.toHaveBeenCalled();
|
|
49
|
+
|
|
50
|
+
view.rerender(
|
|
51
|
+
<MarkdownLink href="artifact:files/report.pdf" artifactRunId="run-1" onArtifactOpen={onArtifactOpen}>
|
|
52
|
+
Report PDF
|
|
53
|
+
</MarkdownLink>,
|
|
54
|
+
);
|
|
55
|
+
fireEvent.click(screen.getByRole('link', { name: 'Report PDF' }));
|
|
56
|
+
|
|
57
|
+
expect(onArtifactOpen).not.toHaveBeenCalled();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('MarkdownLink agent resources', () => {
|
|
62
|
+
const ExistingLink = ({ href, children, className }: MarkdownLinkProps) => (
|
|
63
|
+
<a href={href} className={className}>
|
|
64
|
+
{children}
|
|
65
|
+
</a>
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
it('routes a custom resource scheme through the host resolver', () => {
|
|
69
|
+
const resolver = vi.fn<AgentResourceResolver>(() => ({ kind: 'navigate', href: '/custom/interactions/int-1' }));
|
|
70
|
+
render(
|
|
71
|
+
<AgentResourceResolverProvider value={resolver}>
|
|
72
|
+
<MarkdownLink href="interaction:int-1" artifactRunId="run-1">
|
|
73
|
+
Interaction
|
|
74
|
+
</MarkdownLink>
|
|
75
|
+
</AgentResourceResolverProvider>,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
expect(screen.getByRole('link', { name: 'Interaction' }).getAttribute('href')).toBe(
|
|
79
|
+
'/custom/interactions/int-1',
|
|
80
|
+
);
|
|
81
|
+
expect(resolver).toHaveBeenCalledWith(
|
|
82
|
+
{ type: 'interaction', id: 'int-1' },
|
|
83
|
+
{ workflowRunId: 'run-1', source: 'markdown', rawHref: 'interaction:int-1' },
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('supports a host activation callback', () => {
|
|
88
|
+
const onActivate = vi.fn();
|
|
89
|
+
render(
|
|
90
|
+
<AgentResourceResolverProvider value={() => ({ kind: 'activate', onActivate })}>
|
|
91
|
+
<MarkdownLink href="store:doc-1">Document</MarkdownLink>
|
|
92
|
+
</AgentResourceResolverProvider>,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
fireEvent.click(screen.getByRole('button', { name: 'Document' }));
|
|
96
|
+
expect(onActivate).toHaveBeenCalledOnce();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('renders plain text when no host resolver is configured', () => {
|
|
100
|
+
render(<MarkdownLink href="collection:col-1">Collection</MarkdownLink>);
|
|
101
|
+
|
|
102
|
+
expect(screen.queryByRole('link')).toBeNull();
|
|
103
|
+
expect(screen.getByText('Collection').tagName).toBe('SPAN');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('preserves the class name when delegating standard and resolved resource links', () => {
|
|
107
|
+
const resolver: AgentResourceResolver = () => ({ kind: 'navigate', href: '/custom/interactions/int-1' });
|
|
108
|
+
render(
|
|
109
|
+
<AgentResourceResolverProvider value={resolver}>
|
|
110
|
+
<MarkdownLink href="https://example.com" className="standard-link" ExistingLink={ExistingLink}>
|
|
111
|
+
Standard
|
|
112
|
+
</MarkdownLink>
|
|
113
|
+
<MarkdownLink href="interaction:int-1" className="resource-link" ExistingLink={ExistingLink}>
|
|
114
|
+
Resource
|
|
115
|
+
</MarkdownLink>
|
|
116
|
+
</AgentResourceResolverProvider>,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
expect(screen.getByRole('link', { name: 'Standard' }).className).toBe('standard-link');
|
|
120
|
+
expect(screen.getByRole('link', { name: 'Resource' }).className).toBe('resource-link');
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { Button } from '@vertesia/ui/core';
|
|
1
2
|
import type { Element } from 'hast';
|
|
2
3
|
import type React from 'react';
|
|
4
|
+
import { parseAgentResourceHref, useAgentResourceResolver } from './AgentResourceResolver';
|
|
3
5
|
import { CodeBlockPlaceholder } from './CodeBlockPlaceholder';
|
|
4
|
-
import {
|
|
6
|
+
import { parseUrlScheme, useResolvedUrl } from './useResolvedUrl';
|
|
5
7
|
|
|
6
8
|
export interface MarkdownLinkProps {
|
|
7
9
|
node?: Element;
|
|
@@ -9,6 +11,8 @@ export interface MarkdownLinkProps {
|
|
|
9
11
|
children?: React.ReactNode;
|
|
10
12
|
className?: string;
|
|
11
13
|
artifactRunId?: string;
|
|
14
|
+
/** Open a Markdown artifact in the host artifact viewer instead of downloading it. */
|
|
15
|
+
onArtifactOpen?: (path: string) => void;
|
|
12
16
|
/** Existing link component to delegate to for standard URLs */
|
|
13
17
|
ExistingLink?: React.ComponentType<MarkdownLinkProps>;
|
|
14
18
|
}
|
|
@@ -23,51 +27,108 @@ export function MarkdownLink({
|
|
|
23
27
|
children,
|
|
24
28
|
className,
|
|
25
29
|
artifactRunId,
|
|
30
|
+
onArtifactOpen,
|
|
26
31
|
ExistingLink,
|
|
27
32
|
...rest
|
|
28
33
|
}: MarkdownLinkProps) {
|
|
29
34
|
const rawHref = href || '';
|
|
35
|
+
const resource = parseAgentResourceHref(rawHref);
|
|
36
|
+
if (resource) {
|
|
37
|
+
return (
|
|
38
|
+
<AgentResourceMarkdownLink
|
|
39
|
+
resource={resource}
|
|
40
|
+
rawHref={rawHref}
|
|
41
|
+
artifactRunId={artifactRunId}
|
|
42
|
+
className={className}
|
|
43
|
+
ExistingLink={ExistingLink}
|
|
44
|
+
rest={rest}
|
|
45
|
+
>
|
|
46
|
+
{children}
|
|
47
|
+
</AgentResourceMarkdownLink>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
30
51
|
const { scheme, path } = parseUrlScheme(rawHref);
|
|
31
52
|
|
|
32
|
-
// For
|
|
33
|
-
|
|
34
|
-
if (mappedRoute) {
|
|
53
|
+
// For standard URLs, delegate to existing component or render directly
|
|
54
|
+
if (scheme === 'standard') {
|
|
35
55
|
if (typeof ExistingLink === 'function') {
|
|
36
56
|
return (
|
|
37
|
-
<ExistingLink node={node} href={
|
|
57
|
+
<ExistingLink node={node} href={href} className={className} {...rest}>
|
|
38
58
|
{children}
|
|
39
59
|
</ExistingLink>
|
|
40
60
|
);
|
|
41
61
|
}
|
|
42
62
|
return (
|
|
43
|
-
<a href={
|
|
63
|
+
<a href={href} {...rest} className={className} target="_blank" rel="noopener noreferrer">
|
|
44
64
|
{children}
|
|
45
65
|
</a>
|
|
46
66
|
);
|
|
47
67
|
}
|
|
48
68
|
|
|
49
|
-
// For
|
|
50
|
-
|
|
69
|
+
// For artifact: and image: URLs, use the resolver
|
|
70
|
+
return (
|
|
71
|
+
<ResolvedLink
|
|
72
|
+
rawHref={rawHref}
|
|
73
|
+
artifactRunId={artifactRunId}
|
|
74
|
+
artifactPath={scheme === 'artifact' ? path : undefined}
|
|
75
|
+
onArtifactOpen={onArtifactOpen}
|
|
76
|
+
className={className}
|
|
77
|
+
rest={rest}
|
|
78
|
+
>
|
|
79
|
+
{children}
|
|
80
|
+
</ResolvedLink>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function AgentResourceMarkdownLink({
|
|
85
|
+
resource,
|
|
86
|
+
rawHref,
|
|
87
|
+
artifactRunId,
|
|
88
|
+
className,
|
|
89
|
+
children,
|
|
90
|
+
ExistingLink,
|
|
91
|
+
rest,
|
|
92
|
+
}: {
|
|
93
|
+
resource: NonNullable<ReturnType<typeof parseAgentResourceHref>>;
|
|
94
|
+
rawHref: string;
|
|
95
|
+
artifactRunId?: string;
|
|
96
|
+
className?: string;
|
|
97
|
+
children?: React.ReactNode;
|
|
98
|
+
ExistingLink?: React.ComponentType<MarkdownLinkProps>;
|
|
99
|
+
rest: Record<string, unknown>;
|
|
100
|
+
}) {
|
|
101
|
+
const resolve = useAgentResourceResolver();
|
|
102
|
+
const target = resolve(resource, {
|
|
103
|
+
workflowRunId: artifactRunId,
|
|
104
|
+
source: 'markdown',
|
|
105
|
+
rawHref,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (target.kind === 'navigate') {
|
|
51
109
|
if (typeof ExistingLink === 'function') {
|
|
52
110
|
return (
|
|
53
|
-
<ExistingLink
|
|
111
|
+
<ExistingLink href={target.href} className={className} {...rest}>
|
|
54
112
|
{children}
|
|
55
113
|
</ExistingLink>
|
|
56
114
|
);
|
|
57
115
|
}
|
|
58
116
|
return (
|
|
59
|
-
<a href={href} {...rest} className={className} target="_blank" rel="noopener noreferrer">
|
|
117
|
+
<a href={target.href} {...rest} className={className} target="_blank" rel="noopener noreferrer">
|
|
60
118
|
{children}
|
|
61
119
|
</a>
|
|
62
120
|
);
|
|
63
121
|
}
|
|
64
122
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
123
|
+
if (target.kind === 'activate') {
|
|
124
|
+
return (
|
|
125
|
+
<Button variant="link" size="none" className={className} onClick={target.onActivate}>
|
|
126
|
+
{children}
|
|
127
|
+
</Button>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return <span className={className}>{children}</span>;
|
|
71
132
|
}
|
|
72
133
|
|
|
73
134
|
/**
|
|
@@ -76,12 +137,16 @@ export function MarkdownLink({
|
|
|
76
137
|
function ResolvedLink({
|
|
77
138
|
rawHref,
|
|
78
139
|
artifactRunId,
|
|
140
|
+
artifactPath,
|
|
141
|
+
onArtifactOpen,
|
|
79
142
|
className,
|
|
80
143
|
children,
|
|
81
144
|
rest,
|
|
82
145
|
}: {
|
|
83
146
|
rawHref: string;
|
|
84
147
|
artifactRunId?: string;
|
|
148
|
+
artifactPath?: string;
|
|
149
|
+
onArtifactOpen?: (path: string) => void;
|
|
85
150
|
className?: string;
|
|
86
151
|
children?: React.ReactNode;
|
|
87
152
|
rest: Record<string, unknown>;
|
|
@@ -109,7 +174,27 @@ function ResolvedLink({
|
|
|
109
174
|
}
|
|
110
175
|
|
|
111
176
|
return (
|
|
112
|
-
<a
|
|
177
|
+
<a
|
|
178
|
+
href={url || '#'}
|
|
179
|
+
{...rest}
|
|
180
|
+
className={className}
|
|
181
|
+
target="_blank"
|
|
182
|
+
rel="noopener noreferrer"
|
|
183
|
+
onClick={(event) => {
|
|
184
|
+
if (
|
|
185
|
+
artifactPath &&
|
|
186
|
+
/\.md$/i.test(artifactPath) &&
|
|
187
|
+
onArtifactOpen &&
|
|
188
|
+
!event.metaKey &&
|
|
189
|
+
!event.ctrlKey &&
|
|
190
|
+
!event.shiftKey &&
|
|
191
|
+
event.button === 0
|
|
192
|
+
) {
|
|
193
|
+
event.preventDefault();
|
|
194
|
+
onArtifactOpen(artifactPath);
|
|
195
|
+
}
|
|
196
|
+
}}
|
|
197
|
+
>
|
|
113
198
|
{children}
|
|
114
199
|
</a>
|
|
115
200
|
);
|
|
@@ -26,7 +26,20 @@ type MarkdownParent = { children?: unknown[] };
|
|
|
26
26
|
type RemarkPluginList = NonNullable<React.ComponentProps<typeof Markdown>['remarkPlugins']>;
|
|
27
27
|
|
|
28
28
|
// Custom URL schemes that we handle in our components
|
|
29
|
-
const ALLOWED_CUSTOM_SCHEMES = [
|
|
29
|
+
const ALLOWED_CUSTOM_SCHEMES = [
|
|
30
|
+
'artifact:',
|
|
31
|
+
'image:',
|
|
32
|
+
'store:',
|
|
33
|
+
'document:',
|
|
34
|
+
'document://',
|
|
35
|
+
'collection:',
|
|
36
|
+
'interaction:',
|
|
37
|
+
'prompt:',
|
|
38
|
+
'agent:',
|
|
39
|
+
'workflow:',
|
|
40
|
+
'process:',
|
|
41
|
+
'run:',
|
|
42
|
+
];
|
|
30
43
|
|
|
31
44
|
/**
|
|
32
45
|
* Custom URL transform that allows our custom schemes while using
|
|
@@ -66,6 +79,8 @@ export interface MarkdownRendererProps {
|
|
|
66
79
|
* Optional workflow run id used to resolve shorthand artifact paths (e.g. artifact:out/result.csv)
|
|
67
80
|
*/
|
|
68
81
|
artifactRunId?: string;
|
|
82
|
+
/** Open Markdown artifact links in a host-provided viewer instead of downloading them. */
|
|
83
|
+
onArtifactOpen?: (path: string) => void;
|
|
69
84
|
/** Additional className for the markdown wrapper */
|
|
70
85
|
className?: string;
|
|
71
86
|
/** Additional className for code blocks */
|
|
@@ -80,6 +95,11 @@ export interface MarkdownRendererProps {
|
|
|
80
95
|
onProposalSelect?: (optionId: string) => void;
|
|
81
96
|
/** Callback when user submits free-form response to proposal */
|
|
82
97
|
onProposalSubmit?: (response: string) => void;
|
|
98
|
+
/**
|
|
99
|
+
* Keep parser source positions aligned with `children` by skipping source-changing
|
|
100
|
+
* normalization. Editing surfaces use this when node offsets are part of an anchor.
|
|
101
|
+
*/
|
|
102
|
+
preserveSourcePositions?: boolean;
|
|
83
103
|
}
|
|
84
104
|
|
|
85
105
|
// Create default handlers once, outside component
|
|
@@ -91,6 +111,7 @@ export function MarkdownRenderer({
|
|
|
91
111
|
remarkPlugins = [],
|
|
92
112
|
removeComments = true,
|
|
93
113
|
artifactRunId,
|
|
114
|
+
onArtifactOpen,
|
|
94
115
|
className,
|
|
95
116
|
codeClassName,
|
|
96
117
|
inlineCodeClassName,
|
|
@@ -98,11 +119,15 @@ export function MarkdownRenderer({
|
|
|
98
119
|
imageClassName,
|
|
99
120
|
onProposalSelect,
|
|
100
121
|
onProposalSubmit,
|
|
122
|
+
preserveSourcePositions = false,
|
|
101
123
|
}: MarkdownRendererProps) {
|
|
102
124
|
const codeBlockRegistry = useCodeBlockRendererRegistry();
|
|
103
125
|
const normalizedMarkdown = React.useMemo(
|
|
104
|
-
() =>
|
|
105
|
-
|
|
126
|
+
() =>
|
|
127
|
+
preserveSourcePositions
|
|
128
|
+
? children
|
|
129
|
+
: normalizeDirectives(normalizeCustomSchemeLinks(preprocessMathDelimiters(children))),
|
|
130
|
+
[children, preserveSourcePositions],
|
|
106
131
|
);
|
|
107
132
|
|
|
108
133
|
// Remark plugins (markdown parsing)
|
|
@@ -218,6 +243,7 @@ export function MarkdownRenderer({
|
|
|
218
243
|
href={href}
|
|
219
244
|
className={linkClassName}
|
|
220
245
|
artifactRunId={artifactRunId}
|
|
246
|
+
onArtifactOpen={onArtifactOpen}
|
|
221
247
|
ExistingLink={
|
|
222
248
|
typeof ExistingLink === 'function'
|
|
223
249
|
? (ExistingLink as React.ComponentType<MarkdownLinkProps>)
|
|
@@ -272,6 +298,7 @@ export function MarkdownRenderer({
|
|
|
272
298
|
}, [
|
|
273
299
|
components,
|
|
274
300
|
artifactRunId,
|
|
301
|
+
onArtifactOpen,
|
|
275
302
|
codeBlockRegistry,
|
|
276
303
|
codeClassName,
|
|
277
304
|
inlineCodeClassName,
|
|
@@ -1,8 +1,25 @@
|
|
|
1
|
+
export {
|
|
2
|
+
type AgentResourceLinkReference,
|
|
3
|
+
type AgentResourceResolver,
|
|
4
|
+
AgentResourceResolverProvider,
|
|
5
|
+
type AgentResourceTarget,
|
|
6
|
+
parseAgentResourceHref,
|
|
7
|
+
type ResourceResolveContext,
|
|
8
|
+
useAgentResourceResolver,
|
|
9
|
+
} from './AgentResourceResolver';
|
|
1
10
|
export {
|
|
2
11
|
ArtifactContentRenderer,
|
|
3
12
|
type ArtifactContentRendererProps,
|
|
4
13
|
type ExpandRenderType,
|
|
5
14
|
} from './ArtifactContentRenderer';
|
|
15
|
+
export {
|
|
16
|
+
ArtifactEditingSurface,
|
|
17
|
+
type ArtifactEditingSurfaceDocumentEdit,
|
|
18
|
+
type ArtifactEditingSurfaceProps,
|
|
19
|
+
type ArtifactSaveStatus,
|
|
20
|
+
applyArtifactRefreshChanges,
|
|
21
|
+
isArtifactRefreshEvent,
|
|
22
|
+
} from './ArtifactEditingSurface';
|
|
6
23
|
export {
|
|
7
24
|
type CodeBlockHandlerContext,
|
|
8
25
|
CodeBlockHandlerProvider,
|
|
@@ -20,6 +37,17 @@ export {
|
|
|
20
37
|
useCodeBlockComponent,
|
|
21
38
|
useCodeBlockRendererRegistry,
|
|
22
39
|
} from './CodeBlockRendering';
|
|
40
|
+
export {
|
|
41
|
+
applyMarkdownEditingChange,
|
|
42
|
+
CollaborativeMarkdownRenderer,
|
|
43
|
+
type CollaborativeMarkdownRendererProps,
|
|
44
|
+
createMarkdownBlockAnchor,
|
|
45
|
+
formatMarkdownEditingAction,
|
|
46
|
+
type MarkdownBlockAnchor,
|
|
47
|
+
type MarkdownBlockType,
|
|
48
|
+
type MarkdownEditingAction,
|
|
49
|
+
type MarkdownEditingResource,
|
|
50
|
+
} from './CollaborativeMarkdownRenderer';
|
|
23
51
|
export {
|
|
24
52
|
ChartCodeBlockHandler,
|
|
25
53
|
createDefaultCodeBlockHandlers,
|
|
@@ -33,13 +61,23 @@ export { MarkdownImage, type MarkdownImageProps } from './MarkdownImage';
|
|
|
33
61
|
export { MarkdownLink, type MarkdownLinkProps } from './MarkdownLink';
|
|
34
62
|
export { MarkdownRenderer, type MarkdownRendererProps } from './MarkdownRenderer';
|
|
35
63
|
export { MermaidDiagram } from './MermaidDiagram';
|
|
64
|
+
export {
|
|
65
|
+
createUnifiedLineDiff,
|
|
66
|
+
diffTextSegments,
|
|
67
|
+
diffWordSegments,
|
|
68
|
+
getTextLineChangeRegions,
|
|
69
|
+
rebaseTextChanges,
|
|
70
|
+
type TextDiffSegment,
|
|
71
|
+
type TextLineChangeRegion,
|
|
72
|
+
type TextRebaseResult,
|
|
73
|
+
type UnifiedLineDiffOptions,
|
|
74
|
+
} from './textDiff';
|
|
36
75
|
export {
|
|
37
76
|
type ArtifactContentState,
|
|
38
77
|
type UseArtifactContentOptions,
|
|
39
78
|
useArtifactContent,
|
|
40
79
|
} from './useArtifactContent';
|
|
41
80
|
export {
|
|
42
|
-
mapSchemeToRoute,
|
|
43
81
|
parseUrlScheme,
|
|
44
82
|
type ResolvedUrlState,
|
|
45
83
|
type UrlScheme,
|
|
@@ -1,9 +1,22 @@
|
|
|
1
|
-
const CUSTOM_SCHEME_PREFIXES = [
|
|
1
|
+
const CUSTOM_SCHEME_PREFIXES = [
|
|
2
|
+
'artifact:',
|
|
3
|
+
'image:',
|
|
4
|
+
'store:',
|
|
5
|
+
'document://',
|
|
6
|
+
'collection:',
|
|
7
|
+
'interaction:',
|
|
8
|
+
'prompt:',
|
|
9
|
+
'agent:',
|
|
10
|
+
'workflow:',
|
|
11
|
+
'process:',
|
|
12
|
+
'run:',
|
|
13
|
+
];
|
|
2
14
|
|
|
3
15
|
// Excludes `[` from the link-text and destination classes: a long run of `[` (or
|
|
4
16
|
// `[](image:` groups) with no closing delimiter would otherwise be re-scanned at every
|
|
5
17
|
// start position, giving polynomial backtracking (CodeQL js/polynomial-redos).
|
|
6
|
-
const CUSTOM_LINK_REGEX =
|
|
18
|
+
const CUSTOM_LINK_REGEX =
|
|
19
|
+
/(!?\[[^[\]\n]*\]\()((?:artifact:|image:|store:|document:\/\/|collection:|interaction:|prompt:|agent:|workflow:|process:|run:)[^)[\n]+)(\))/g;
|
|
7
20
|
const INLINE_CODE_REGEX = /`[^`\n]*`/g;
|
|
8
21
|
const FENCED_CODE_BLOCK_REGEX = /(^|\n)(`{3,}|~{3,})[^\n]*\n[\s\S]*?\n\2(?=\n|$)/g;
|
|
9
22
|
const LINK_TITLE_SUFFIX_REGEX = /\s+("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\([^)]*\))\s*$/;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
createUnifiedLineDiff,
|
|
4
|
+
diffTextSegments,
|
|
5
|
+
diffWordSegments,
|
|
6
|
+
getTextLineChangeRegions,
|
|
7
|
+
rebaseTextChanges,
|
|
8
|
+
type TextDiffSegment,
|
|
9
|
+
} from './textDiff.js';
|
|
10
|
+
|
|
11
|
+
function joinByTypes(segments: TextDiffSegment[], types: TextDiffSegment['type'][]): string {
|
|
12
|
+
return segments
|
|
13
|
+
.filter((segment) => types.includes(segment.type))
|
|
14
|
+
.map((segment) => segment.text)
|
|
15
|
+
.join('');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('diffWordSegments', () => {
|
|
19
|
+
it('returns a single equal segment for identical texts', () => {
|
|
20
|
+
expect(diffWordSegments('same text', 'same text')).toEqual([{ type: 'equal', text: 'same text' }]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('marks an appended sentence as added', () => {
|
|
24
|
+
const before = 'Keep this paragraph unchanged.';
|
|
25
|
+
const after = 'Keep this paragraph unchanged. Session edit A.';
|
|
26
|
+
|
|
27
|
+
expect(diffWordSegments(before, after)).toEqual([
|
|
28
|
+
{ type: 'equal', text: 'Keep this paragraph unchanged.' },
|
|
29
|
+
{ type: 'added', text: ' Session edit A.' },
|
|
30
|
+
]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('marks a replaced word with a removal and an addition', () => {
|
|
34
|
+
const segments = diffWordSegments('Keep the tone light.', 'Keep the tone formal.');
|
|
35
|
+
|
|
36
|
+
expect(segments).toEqual([
|
|
37
|
+
{ type: 'equal', text: 'Keep the tone ' },
|
|
38
|
+
{ type: 'removed', text: 'light.' },
|
|
39
|
+
{ type: 'added', text: 'formal.' },
|
|
40
|
+
]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('reconstructs both sides from the segments across multiline edits', () => {
|
|
44
|
+
const before = '- Preserve all sections.\n- Keep the tone light.\n- Ship it.';
|
|
45
|
+
const after = '- Preserve all sections.\n- Keep the tone light and clear.\n- Log every revision.\n- Ship it.';
|
|
46
|
+
const segments = diffWordSegments(before, after);
|
|
47
|
+
|
|
48
|
+
expect(joinByTypes(segments, ['equal', 'removed'])).toBe(before);
|
|
49
|
+
expect(joinByTypes(segments, ['equal', 'added'])).toBe(after);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('handles a full replacement', () => {
|
|
53
|
+
expect(diffWordSegments('old', 'new')).toEqual([
|
|
54
|
+
{ type: 'removed', text: 'old' },
|
|
55
|
+
{ type: 'added', text: 'new' },
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('handles empty sides', () => {
|
|
60
|
+
expect(diffWordSegments('', 'added')).toEqual([{ type: 'added', text: 'added' }]);
|
|
61
|
+
expect(diffWordSegments('removed', '')).toEqual([{ type: 'removed', text: 'removed' }]);
|
|
62
|
+
expect(diffWordSegments('', '')).toEqual([]);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('diffTextSegments', () => {
|
|
67
|
+
it('keeps unchanged lines aligned across a document-scale changed middle', () => {
|
|
68
|
+
const preserved = Array.from({ length: 220 }, (_, index) => `Preserved line ${index}.`).join('\n');
|
|
69
|
+
const before = `# Release notes\nOpening before.\n${preserved}\nClosing before.`;
|
|
70
|
+
const after = `# Release notes\nOpening after.\n${preserved}\nClosing after.`;
|
|
71
|
+
|
|
72
|
+
const segments = diffTextSegments(before, after);
|
|
73
|
+
|
|
74
|
+
expect(joinByTypes(segments, ['equal', 'removed'])).toBe(before);
|
|
75
|
+
expect(joinByTypes(segments, ['equal', 'added'])).toBe(after);
|
|
76
|
+
expect(
|
|
77
|
+
segments.some((segment) => segment.type === 'equal' && segment.text.includes('Preserved line 100.')),
|
|
78
|
+
).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('returns distinct line regions separated by unchanged content', () => {
|
|
82
|
+
expect(getTextLineChangeRegions('One\nTwo\nThree', 'One\nChanged\nThree\nFour')).toEqual([
|
|
83
|
+
{ startLine: 1, endLine: 1 },
|
|
84
|
+
{ startLine: 3, endLine: 3 },
|
|
85
|
+
]);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('createUnifiedLineDiff', () => {
|
|
90
|
+
const BEFORE = ['# Title', '', 'First paragraph.', '', '- one', '- two', '- three', '', 'Closing line.'].join('\n');
|
|
91
|
+
|
|
92
|
+
it('returns undefined for identical texts', () => {
|
|
93
|
+
expect(createUnifiedLineDiff(BEFORE, BEFORE)).toBeUndefined();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('produces a hunk with context and correct markers for a line change', () => {
|
|
97
|
+
const after = BEFORE.replace('- two', '- two (updated)');
|
|
98
|
+
const diff = createUnifiedLineDiff(BEFORE, after, { context: 1 });
|
|
99
|
+
expect(diff).toBe(['@@ -5,3 +5,3 @@', ' - one', '-- two', '+- two (updated)', ' - three'].join('\n'));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('merges nearby changes into one hunk and separates distant ones', () => {
|
|
103
|
+
const after = BEFORE.replace('# Title', '# New Title').replace('Closing line.', 'Closing line!');
|
|
104
|
+
const diff = createUnifiedLineDiff(BEFORE, after, { context: 1 });
|
|
105
|
+
const headers = diff?.split('\n').filter((line) => line.startsWith('@@'));
|
|
106
|
+
expect(headers).toHaveLength(2);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('handles pure insertions and deletions', () => {
|
|
110
|
+
const inserted = createUnifiedLineDiff('a\nb', 'a\nnew\nb', { context: 0 });
|
|
111
|
+
expect(inserted).toBe('@@ -2,0 +2,1 @@\n+new');
|
|
112
|
+
const deleted = createUnifiedLineDiff('a\nold\nb', 'a\nb', { context: 0 });
|
|
113
|
+
expect(deleted).toBe('@@ -2,1 +2,0 @@\n-old');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('returns undefined when the diff exceeds maxChars', () => {
|
|
117
|
+
const after = BEFORE.replace('First paragraph.', 'Rewritten paragraph.');
|
|
118
|
+
expect(createUnifiedLineDiff(BEFORE, after, { maxChars: 10 })).toBeUndefined();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('rebaseTextChanges', () => {
|
|
123
|
+
const BASE = ['# Brief', '', 'Overview.', '', '## Goals', '- First', '- Second', '', 'Closing.'].join('\n');
|
|
124
|
+
|
|
125
|
+
it('combines non-overlapping local and remote changes', () => {
|
|
126
|
+
const local = BASE.replace('- Second', '- Second (local)');
|
|
127
|
+
const remote = BASE.replace('Overview.', 'Overview updated remotely.');
|
|
128
|
+
|
|
129
|
+
expect(rebaseTextChanges(BASE, local, remote)).toEqual({
|
|
130
|
+
status: 'rebased',
|
|
131
|
+
content: remote.replace('- Second', '- Second (local)'),
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('returns the local version unchanged when the remote content did not change', () => {
|
|
136
|
+
const local = BASE.replace('Closing.', 'Local closing.');
|
|
137
|
+
expect(rebaseTextChanges(BASE, local, BASE)).toEqual({ status: 'rebased', content: local });
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('deduplicates an identical concurrent edit', () => {
|
|
141
|
+
const changed = BASE.replace('- First', '- First (shared)');
|
|
142
|
+
expect(rebaseTextChanges(BASE, changed, changed)).toEqual({ status: 'rebased', content: changed });
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('reports overlapping replacements as a conflict and preserves the local version', () => {
|
|
146
|
+
const local = BASE.replace('Overview.', 'Local overview.');
|
|
147
|
+
const remote = BASE.replace('Overview.', 'Remote overview.');
|
|
148
|
+
expect(rebaseTextChanges(BASE, local, remote)).toEqual({ status: 'conflict', content: local });
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('treats competing insertions at the same base position as a conflict', () => {
|
|
152
|
+
const local = BASE.replace('## Goals', 'Local note.\n\n## Goals');
|
|
153
|
+
const remote = BASE.replace('## Goals', 'Remote note.\n\n## Goals');
|
|
154
|
+
expect(rebaseTextChanges(BASE, local, remote)).toEqual({ status: 'conflict', content: local });
|
|
155
|
+
});
|
|
156
|
+
});
|