@stigmer/react 3.1.1 → 3.1.3
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/composer/SessionComposer.d.ts +25 -1
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +19 -8
- package/composer/SessionComposer.js.map +1 -1
- package/execution/ArtifactContentRenderer.d.ts +11 -1
- package/execution/ArtifactContentRenderer.d.ts.map +1 -1
- package/execution/ArtifactContentRenderer.js +39 -15
- package/execution/ArtifactContentRenderer.js.map +1 -1
- package/execution/ArtifactPreviewModal.d.ts +5 -5
- package/execution/ArtifactPreviewModal.js +4 -4
- package/execution/ArtifactPreviewModal.js.map +1 -1
- package/execution/FileChangeProgressBar.d.ts +47 -0
- package/execution/FileChangeProgressBar.d.ts.map +1 -0
- package/execution/FileChangeProgressBar.js +49 -0
- package/execution/FileChangeProgressBar.js.map +1 -0
- package/execution/FileChangesView.d.ts.map +1 -1
- package/execution/FileChangesView.js +19 -4
- package/execution/FileChangesView.js.map +1 -1
- package/execution/FilePathContext.d.ts +8 -1
- package/execution/FilePathContext.d.ts.map +1 -1
- package/execution/FilePathContext.js.map +1 -1
- package/execution/FilePathLink.d.ts.map +1 -1
- package/execution/FilePathLink.js +21 -6
- package/execution/FilePathLink.js.map +1 -1
- package/execution/FileReviewAtoms.d.ts +27 -0
- package/execution/FileReviewAtoms.d.ts.map +1 -0
- package/execution/FileReviewAtoms.js +64 -0
- package/execution/FileReviewAtoms.js.map +1 -0
- package/execution/FileReviewCard.d.ts +2 -2
- package/execution/FileReviewCard.d.ts.map +1 -1
- package/execution/FileReviewCard.js +32 -62
- package/execution/FileReviewCard.js.map +1 -1
- package/execution/MessageEntry.d.ts +20 -0
- package/execution/MessageEntry.d.ts.map +1 -1
- package/execution/MessageEntry.js +19 -5
- package/execution/MessageEntry.js.map +1 -1
- package/execution/MessageThread.d.ts +75 -13
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +195 -27
- package/execution/MessageThread.js.map +1 -1
- package/execution/PlanArtifactCard.d.ts +36 -21
- package/execution/PlanArtifactCard.d.ts.map +1 -1
- package/execution/PlanArtifactCard.js +39 -25
- package/execution/PlanArtifactCard.js.map +1 -1
- package/execution/PlanCompletionCard.d.ts +4 -4
- package/execution/PlanCompletionCard.d.ts.map +1 -1
- package/execution/PlanCompletionCard.js +4 -4
- package/execution/PlanCompletionCard.js.map +1 -1
- package/execution/PlanDocumentMessage.d.ts +32 -0
- package/execution/PlanDocumentMessage.d.ts.map +1 -0
- package/execution/PlanDocumentMessage.js +37 -0
- package/execution/PlanDocumentMessage.js.map +1 -0
- package/execution/PlanStreamingCard.d.ts +50 -0
- package/execution/PlanStreamingCard.d.ts.map +1 -0
- package/execution/PlanStreamingCard.js +40 -0
- package/execution/PlanStreamingCard.js.map +1 -0
- package/execution/index.d.ts +9 -1
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +4 -0
- package/execution/index.js.map +1 -1
- package/execution/use-build-from-plan-hotkey.d.ts +1 -1
- package/execution/use-build-from-plan-hotkey.js +1 -1
- package/execution/useArtifactCopy.d.ts +51 -0
- package/execution/useArtifactCopy.d.ts.map +1 -0
- package/execution/useArtifactCopy.js +75 -0
- package/execution/useArtifactCopy.js.map +1 -0
- package/execution/useArtifactDownload.d.ts.map +1 -1
- package/execution/useArtifactDownload.js +17 -5
- package/execution/useArtifactDownload.js.map +1 -1
- package/execution/useCreateAgentExecution.d.ts +12 -0
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js +5 -1
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/github/decodeGitHubContent.d.ts +29 -0
- package/github/decodeGitHubContent.d.ts.map +1 -0
- package/github/decodeGitHubContent.js +72 -0
- package/github/decodeGitHubContent.js.map +1 -0
- package/github/index.d.ts +1 -0
- package/github/index.d.ts.map +1 -1
- package/github/index.js +1 -0
- package/github/index.js.map +1 -1
- package/github/useGitHubFileReader.d.ts +26 -0
- package/github/useGitHubFileReader.d.ts.map +1 -0
- package/github/useGitHubFileReader.js +106 -0
- package/github/useGitHubFileReader.js.map +1 -0
- package/github/useGitHubTreeLister.d.ts.map +1 -1
- package/github/useGitHubTreeLister.js +6 -2
- package/github/useGitHubTreeLister.js.map +1 -1
- package/index.d.ts +9 -9
- package/index.d.ts.map +1 -1
- package/index.js +5 -5
- package/index.js.map +1 -1
- package/internal/ResizableSplit.d.ts +72 -22
- package/internal/ResizableSplit.d.ts.map +1 -1
- package/internal/ResizableSplit.js +96 -38
- package/internal/ResizableSplit.js.map +1 -1
- package/internal/VirtualizedThread.d.ts +5 -3
- package/internal/VirtualizedThread.d.ts.map +1 -1
- package/internal/VirtualizedThread.js +6 -4
- package/internal/VirtualizedThread.js.map +1 -1
- package/internal/file-icons/index.d.ts +17 -0
- package/internal/file-icons/index.d.ts.map +1 -0
- package/internal/file-icons/index.js +60 -0
- package/internal/file-icons/index.js.map +1 -0
- package/internal/file-tree/FileTreeNode.d.ts +18 -1
- package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
- package/internal/file-tree/FileTreeNode.js +11 -2
- package/internal/file-tree/FileTreeNode.js.map +1 -1
- package/internal/markdown-components.d.ts +38 -1
- package/internal/markdown-components.d.ts.map +1 -1
- package/internal/markdown-components.js +86 -11
- package/internal/markdown-components.js.map +1 -1
- package/{workflow → internal}/motion-preference.d.ts +7 -4
- package/internal/motion-preference.d.ts.map +1 -0
- package/{workflow → internal}/motion-preference.js +7 -4
- package/internal/motion-preference.js.map +1 -0
- package/internal/store/index.d.ts +33 -0
- package/internal/store/index.d.ts.map +1 -1
- package/internal/store/index.js +59 -0
- package/internal/store/index.js.map +1 -1
- package/internal/store/workspace-editors-store.d.ts +145 -0
- package/internal/store/workspace-editors-store.d.ts.map +1 -0
- package/internal/store/workspace-editors-store.js +208 -0
- package/internal/store/workspace-editors-store.js.map +1 -0
- package/internal/store/workspace-file-selection-store.d.ts +38 -0
- package/internal/store/workspace-file-selection-store.d.ts.map +1 -0
- package/internal/store/workspace-file-selection-store.js +62 -0
- package/internal/store/workspace-file-selection-store.js.map +1 -0
- package/internal/useRevealLine.d.ts +49 -0
- package/internal/useRevealLine.d.ts.map +1 -0
- package/internal/useRevealLine.js +43 -0
- package/internal/useRevealLine.js.map +1 -0
- package/library/detect-plan-artifact.d.ts +43 -4
- package/library/detect-plan-artifact.d.ts.map +1 -1
- package/library/detect-plan-artifact.js +45 -5
- package/library/detect-plan-artifact.js.map +1 -1
- package/library/detect-streaming-plan.d.ts +45 -0
- package/library/detect-streaming-plan.d.ts.map +1 -0
- package/library/detect-streaming-plan.js +83 -0
- package/library/detect-streaming-plan.js.map +1 -0
- package/library/index.d.ts +4 -1
- package/library/index.d.ts.map +1 -1
- package/library/index.js +2 -1
- package/library/index.js.map +1 -1
- package/package.json +4 -4
- package/session/NewSessionViewer.d.ts +19 -4
- package/session/NewSessionViewer.d.ts.map +1 -1
- package/session/NewSessionViewer.js +52 -25
- package/session/NewSessionViewer.js.map +1 -1
- package/session/PlanEditor.d.ts +55 -0
- package/session/PlanEditor.d.ts.map +1 -0
- package/session/PlanEditor.js +88 -0
- package/session/PlanEditor.js.map +1 -0
- package/session/PlanStreamingDocument.d.ts +30 -0
- package/session/PlanStreamingDocument.d.ts.map +1 -0
- package/session/PlanStreamingDocument.js +37 -0
- package/session/PlanStreamingDocument.js.map +1 -0
- package/session/SessionPanelChip.d.ts +26 -0
- package/session/SessionPanelChip.d.ts.map +1 -0
- package/session/SessionPanelChip.js +40 -0
- package/session/SessionPanelChip.js.map +1 -0
- package/session/SessionViewer.d.ts +34 -7
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +334 -33
- package/session/SessionViewer.js.map +1 -1
- package/session/{inspector → facets}/ArtifactsTab.d.ts +10 -2
- package/session/facets/ArtifactsTab.d.ts.map +1 -0
- package/session/{inspector → facets}/ArtifactsTab.js +8 -3
- package/session/facets/ArtifactsTab.js.map +1 -0
- package/session/{inspector → facets}/ChangesTab.d.ts +3 -3
- package/session/facets/ChangesTab.d.ts.map +1 -0
- package/session/{inspector → facets}/ChangesTab.js +3 -3
- package/session/facets/ChangesTab.js.map +1 -0
- package/session/facets/InspectTab.d.ts.map +1 -0
- package/session/facets/InspectTab.js.map +1 -0
- package/session/{inspector → facets}/SetupTab.d.ts +11 -2
- package/session/facets/SetupTab.d.ts.map +1 -0
- package/session/{inspector → facets}/SetupTab.js +4 -3
- package/session/facets/SetupTab.js.map +1 -0
- package/session/{inspector → facets}/UsageTab.d.ts +1 -1
- package/session/facets/UsageTab.d.ts.map +1 -0
- package/session/{inspector → facets}/UsageTab.js +1 -1
- package/session/facets/UsageTab.js.map +1 -0
- package/session/{inspector → facets}/index.d.ts +0 -6
- package/session/facets/index.d.ts.map +1 -0
- package/session/{inspector → facets}/index.js +0 -3
- package/session/facets/index.js.map +1 -0
- package/session/index.d.ts +11 -2
- package/session/index.d.ts.map +1 -1
- package/session/index.js +15 -2
- package/session/index.js.map +1 -1
- package/session/plan-document.d.ts +21 -0
- package/session/plan-document.d.ts.map +1 -0
- package/session/plan-document.js +28 -0
- package/session/plan-document.js.map +1 -0
- package/session/useOpenFileChange.d.ts +20 -0
- package/session/useOpenFileChange.d.ts.map +1 -0
- package/session/useOpenFileChange.js +31 -0
- package/session/useOpenFileChange.js.map +1 -0
- package/session/usePlanDraft.d.ts +52 -0
- package/session/usePlanDraft.d.ts.map +1 -0
- package/session/usePlanDraft.js +63 -0
- package/session/usePlanDraft.js.map +1 -0
- package/session/useSessionConversation.d.ts +14 -1
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +20 -2
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionPageFlow.d.ts.map +1 -1
- package/session/useSessionPageFlow.js +1 -0
- package/session/useSessionPageFlow.js.map +1 -1
- package/session/useSessionPanel.d.ts +120 -0
- package/session/useSessionPanel.d.ts.map +1 -0
- package/session/useSessionPanel.js +130 -0
- package/session/useSessionPanel.js.map +1 -0
- package/session/useSessionRailViews.d.ts +45 -0
- package/session/useSessionRailViews.d.ts.map +1 -0
- package/session/useSessionRailViews.js +105 -0
- package/session/useSessionRailViews.js.map +1 -0
- package/src/composer/SessionComposer.tsx +53 -10
- package/src/composer/__tests__/SessionComposer-contract.test.tsx +58 -4
- package/src/execution/ArtifactContentRenderer.tsx +101 -24
- package/src/execution/ArtifactPreviewModal.tsx +9 -9
- package/src/execution/FileChangeProgressBar.tsx +121 -0
- package/src/execution/FileChangesView.tsx +18 -4
- package/src/execution/FilePathContext.tsx +8 -1
- package/src/execution/FilePathLink.tsx +22 -6
- package/src/execution/FileReviewAtoms.tsx +89 -0
- package/src/execution/FileReviewCard.tsx +57 -90
- package/src/execution/MessageEntry.tsx +49 -1
- package/src/execution/MessageThread.tsx +308 -39
- package/src/execution/PlanArtifactCard.tsx +174 -64
- package/src/execution/PlanCompletionCard.tsx +5 -5
- package/src/execution/PlanDocumentMessage.tsx +96 -0
- package/src/execution/PlanStreamingCard.tsx +141 -0
- package/src/execution/__tests__/ArtifactContentRenderer.test.tsx +80 -1
- package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +6 -6
- package/src/execution/__tests__/FileChangeProgressBar.test.tsx +125 -0
- package/src/execution/__tests__/FilePathLink.test.tsx +87 -0
- package/src/execution/__tests__/FileReviewCard.test.tsx +66 -7
- package/src/execution/__tests__/MessageThread.test.tsx +102 -1
- package/src/execution/__tests__/PlanArtifactCard.test.tsx +114 -26
- package/src/execution/__tests__/PlanCompletionCard.test.tsx +2 -2
- package/src/execution/__tests__/PlanDocumentMessage.test.tsx +55 -0
- package/src/execution/__tests__/PlanStreamingCard.test.tsx +56 -0
- package/src/execution/__tests__/buildThreadItems-plan-completion.test.ts +600 -22
- package/src/execution/__tests__/message-entry.test.tsx +17 -0
- package/src/execution/__tests__/useArtifactCopy.test.tsx +90 -0
- package/src/execution/__tests__/useArtifactDownload.test.tsx +2 -0
- package/src/execution/__tests__/useCreateAgentExecution.test.tsx +85 -0
- package/src/execution/index.ts +13 -1
- package/src/execution/use-build-from-plan-hotkey.ts +1 -1
- package/src/execution/useArtifactCopy.ts +109 -0
- package/src/execution/useArtifactDownload.ts +17 -5
- package/src/execution/useCreateAgentExecution.ts +18 -1
- package/src/github/__tests__/decodeGitHubContent.test.ts +102 -0
- package/src/github/__tests__/useGitHubFileReader.test.ts +183 -0
- package/src/github/decodeGitHubContent.ts +83 -0
- package/src/github/index.ts +1 -0
- package/src/github/useGitHubFileReader.ts +152 -0
- package/src/github/useGitHubTreeLister.ts +6 -2
- package/src/index.ts +69 -9
- package/src/internal/ResizableSplit.tsx +184 -51
- package/src/internal/VirtualizedThread.tsx +12 -4
- package/src/internal/__tests__/ResizableSplit.test.tsx +125 -3
- package/src/internal/__tests__/markdown-components.test.tsx +68 -0
- package/src/internal/__tests__/useRevealLine.test.tsx +97 -0
- package/src/internal/file-icons/__tests__/file-icons.test.tsx +35 -0
- package/src/internal/file-icons/index.tsx +137 -0
- package/src/internal/file-tree/FileTreeNode.tsx +56 -5
- package/src/internal/file-tree/__tests__/FileTreeNode-explorer.test.tsx +86 -0
- package/src/internal/markdown-components.tsx +116 -11
- package/src/{workflow → internal}/motion-preference.ts +7 -4
- package/src/internal/store/__tests__/workspace-editors-store.test.ts +216 -0
- package/src/internal/store/__tests__/workspace-file-selection-store.test.ts +118 -0
- package/src/internal/store/index.ts +94 -0
- package/src/internal/store/workspace-editors-store.ts +315 -0
- package/src/internal/store/workspace-file-selection-store.ts +88 -0
- package/src/internal/useRevealLine.ts +81 -0
- package/src/library/__tests__/detect-plan-artifact.test.ts +69 -0
- package/src/library/__tests__/detect-streaming-plan.test.ts +234 -0
- package/src/library/detect-plan-artifact.ts +62 -5
- package/src/library/detect-streaming-plan.ts +108 -0
- package/src/library/index.ts +7 -0
- package/src/session/NewSessionViewer.tsx +123 -55
- package/src/session/PlanEditor.tsx +361 -0
- package/src/session/PlanStreamingDocument.tsx +97 -0
- package/src/session/SessionPanelChip.tsx +103 -0
- package/src/session/SessionViewer.tsx +655 -96
- package/src/session/__tests__/NewSessionViewer.test.tsx +148 -0
- package/src/session/__tests__/PlanEditor.test.tsx +217 -0
- package/src/session/__tests__/PlanStreamingDocument.test.tsx +50 -0
- package/src/session/__tests__/SessionPanelChip.test.tsx +42 -0
- package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +66 -4
- package/src/session/__tests__/SessionViewer-transcriptFileOpen.test.tsx +205 -0
- package/src/session/__tests__/audienceWiring.test.tsx +55 -19
- package/src/session/__tests__/useOpenFileChange.test.tsx +82 -0
- package/src/session/__tests__/usePlanDraft.test.ts +102 -0
- package/src/session/__tests__/useSessionConversation.test.tsx +44 -0
- package/src/session/__tests__/useSessionPanel.test.ts +298 -0
- package/src/session/__tests__/useSessionRailViews.test.tsx +124 -0
- package/src/session/{inspector → facets}/ArtifactsTab.tsx +27 -5
- package/src/session/{inspector → facets}/ChangesTab.tsx +3 -3
- package/src/session/{inspector → facets}/SetupTab.tsx +13 -1
- package/src/session/{inspector → facets}/UsageTab.tsx +1 -1
- package/src/session/{inspector → facets}/__tests__/ArtifactsTab.test.tsx +42 -8
- package/src/session/facets/__tests__/SetupTab.test.tsx +44 -0
- package/src/session/{inspector → facets}/index.ts +0 -16
- package/src/session/index.ts +30 -13
- package/src/session/plan-document.ts +30 -0
- package/src/session/useOpenFileChange.ts +53 -0
- package/src/session/usePlanDraft.ts +109 -0
- package/src/session/useSessionConversation.ts +38 -3
- package/src/session/useSessionPageFlow.ts +1 -0
- package/src/session/useSessionPanel.ts +288 -0
- package/src/session/useSessionRailViews.tsx +200 -0
- package/src/workflow/WorkflowExecutionGraph.tsx +1 -1
- package/src/workflow/index.ts +1 -1
- package/src/workflow/useFollowExecution.ts +1 -1
- package/src/workspace/EditorTabs.tsx +174 -0
- package/src/workspace/ExplorerTree.tsx +253 -0
- package/src/workspace/FileViewer.tsx +537 -0
- package/src/workspace/WorkspaceContentSearch.tsx +502 -0
- package/src/workspace/WorkspaceContentSearcher.ts +70 -0
- package/src/workspace/WorkspaceEditor.tsx +19 -16
- package/src/workspace/WorkspaceFileLister.ts +11 -0
- package/src/workspace/WorkspaceFileReader.ts +57 -0
- package/src/workspace/WorkspaceFileSearch.tsx +452 -0
- package/src/workspace/WorkspaceSurface.tsx +839 -0
- package/src/workspace/__tests__/EditorTabs.test.tsx +102 -0
- package/src/workspace/__tests__/ExplorerTree.test.tsx +204 -0
- package/src/workspace/__tests__/FileViewer.test.tsx +439 -0
- package/src/workspace/__tests__/WorkspaceContentSearch.test.tsx +229 -0
- package/src/workspace/__tests__/WorkspaceEditor-initialPanel.test.tsx +19 -0
- package/src/workspace/__tests__/WorkspaceFileSearch.test.tsx +227 -0
- package/src/workspace/__tests__/WorkspaceSurface.test.tsx +418 -0
- package/src/workspace/__tests__/a11y/harness.tsx +140 -0
- package/src/workspace/__tests__/a11y/mock-capabilities.ts +138 -0
- package/src/workspace/__tests__/a11y/workspace-editor.a11y.test.tsx +65 -0
- package/src/workspace/__tests__/a11y/workspace-explorer-search.a11y.test.tsx +112 -0
- package/src/workspace/__tests__/a11y/workspace-viewer-tabs.a11y.test.tsx +69 -0
- package/src/workspace/__tests__/findChangeForSelection.test.ts +132 -0
- package/src/workspace/__tests__/matchContentPreview.test.ts +55 -0
- package/src/workspace/__tests__/matchWorkspaceFiles.test.ts +104 -0
- package/src/workspace/__tests__/resolveWorkspaceFileSelection.test.ts +135 -0
- package/src/workspace/__tests__/useWorkspaceContentSearch.test.tsx +196 -0
- package/src/workspace/__tests__/useWorkspaceFileContent.test.tsx +190 -0
- package/src/workspace/__tests__/useWorkspaceFileSearch.test.tsx +162 -0
- package/src/workspace/findChangeForSelection.ts +61 -0
- package/src/workspace/index.ts +56 -2
- package/src/workspace/matchContentPreview.ts +51 -0
- package/src/workspace/matchWorkspaceFiles.ts +86 -0
- package/src/workspace/resolveWorkspaceFileSelection.ts +119 -0
- package/src/workspace/useWorkspaceContentSearch.ts +228 -0
- package/src/workspace/useWorkspaceFileContent.ts +129 -0
- package/src/workspace/useWorkspaceFileSearch.ts +184 -0
- package/src/workspace/useWorkspaceFiles.ts +27 -27
- package/src/workspace/workspaceListingCache.ts +93 -0
- package/styles.css +1 -1
- package/workflow/WorkflowExecutionGraph.js +1 -1
- package/workflow/WorkflowExecutionGraph.js.map +1 -1
- package/workflow/index.d.ts +1 -1
- package/workflow/index.d.ts.map +1 -1
- package/workflow/index.js +1 -1
- package/workflow/index.js.map +1 -1
- package/workflow/useFollowExecution.js +1 -1
- package/workflow/useFollowExecution.js.map +1 -1
- package/workspace/EditorTabs.d.ts +47 -0
- package/workspace/EditorTabs.d.ts.map +1 -0
- package/workspace/EditorTabs.js +78 -0
- package/workspace/EditorTabs.js.map +1 -0
- package/workspace/ExplorerTree.d.ts +32 -0
- package/workspace/ExplorerTree.d.ts.map +1 -0
- package/workspace/ExplorerTree.js +49 -0
- package/workspace/ExplorerTree.js.map +1 -0
- package/workspace/FileViewer.d.ts +93 -0
- package/workspace/FileViewer.d.ts.map +1 -0
- package/workspace/FileViewer.js +193 -0
- package/workspace/FileViewer.js.map +1 -0
- package/workspace/WorkspaceContentSearch.d.ts +41 -0
- package/workspace/WorkspaceContentSearch.d.ts.map +1 -0
- package/workspace/WorkspaceContentSearch.js +216 -0
- package/workspace/WorkspaceContentSearch.js.map +1 -0
- package/workspace/WorkspaceContentSearcher.d.ts +59 -0
- package/workspace/WorkspaceContentSearcher.d.ts.map +1 -0
- package/workspace/WorkspaceContentSearcher.js +7 -0
- package/workspace/WorkspaceContentSearcher.js.map +1 -0
- package/workspace/WorkspaceEditor.d.ts.map +1 -1
- package/workspace/WorkspaceEditor.js +8 -10
- package/workspace/WorkspaceEditor.js.map +1 -1
- package/workspace/WorkspaceFileLister.d.ts +11 -0
- package/workspace/WorkspaceFileLister.d.ts.map +1 -1
- package/workspace/WorkspaceFileReader.d.ts +48 -0
- package/workspace/WorkspaceFileReader.d.ts.map +1 -0
- package/workspace/WorkspaceFileReader.js +11 -0
- package/workspace/WorkspaceFileReader.js.map +1 -0
- package/workspace/WorkspaceFileSearch.d.ts +32 -0
- package/workspace/WorkspaceFileSearch.d.ts.map +1 -0
- package/workspace/WorkspaceFileSearch.js +175 -0
- package/workspace/WorkspaceFileSearch.js.map +1 -0
- package/workspace/WorkspaceSurface.d.ts +151 -0
- package/workspace/WorkspaceSurface.d.ts.map +1 -0
- package/workspace/WorkspaceSurface.js +220 -0
- package/workspace/WorkspaceSurface.js.map +1 -0
- package/workspace/findChangeForSelection.d.ts +19 -0
- package/workspace/findChangeForSelection.d.ts.map +1 -0
- package/workspace/findChangeForSelection.js +46 -0
- package/workspace/findChangeForSelection.js.map +1 -0
- package/workspace/index.d.ts +24 -2
- package/workspace/index.d.ts.map +1 -1
- package/workspace/index.js +13 -1
- package/workspace/index.js.map +1 -1
- package/workspace/matchContentPreview.d.ts +21 -0
- package/workspace/matchContentPreview.d.ts.map +1 -0
- package/workspace/matchContentPreview.js +36 -0
- package/workspace/matchContentPreview.js.map +1 -0
- package/workspace/matchWorkspaceFiles.d.ts +30 -0
- package/workspace/matchWorkspaceFiles.d.ts.map +1 -0
- package/workspace/matchWorkspaceFiles.js +58 -0
- package/workspace/matchWorkspaceFiles.js.map +1 -0
- package/workspace/resolveWorkspaceFileSelection.d.ts +19 -0
- package/workspace/resolveWorkspaceFileSelection.d.ts.map +1 -0
- package/workspace/resolveWorkspaceFileSelection.js +115 -0
- package/workspace/resolveWorkspaceFileSelection.js.map +1 -0
- package/workspace/useWorkspaceContentSearch.d.ts +69 -0
- package/workspace/useWorkspaceContentSearch.d.ts.map +1 -0
- package/workspace/useWorkspaceContentSearch.js +154 -0
- package/workspace/useWorkspaceContentSearch.js.map +1 -0
- package/workspace/useWorkspaceFileContent.d.ts +73 -0
- package/workspace/useWorkspaceFileContent.d.ts.map +1 -0
- package/workspace/useWorkspaceFileContent.js +57 -0
- package/workspace/useWorkspaceFileContent.js.map +1 -0
- package/workspace/useWorkspaceFileSearch.d.ts +61 -0
- package/workspace/useWorkspaceFileSearch.d.ts.map +1 -0
- package/workspace/useWorkspaceFileSearch.js +119 -0
- package/workspace/useWorkspaceFileSearch.js.map +1 -0
- package/workspace/useWorkspaceFiles.d.ts +13 -5
- package/workspace/useWorkspaceFiles.d.ts.map +1 -1
- package/workspace/useWorkspaceFiles.js +18 -19
- package/workspace/useWorkspaceFiles.js.map +1 -1
- package/workspace/workspaceListingCache.d.ts +47 -0
- package/workspace/workspaceListingCache.d.ts.map +1 -0
- package/workspace/workspaceListingCache.js +55 -0
- package/workspace/workspaceListingCache.js.map +1 -0
- package/session/inspector/ArtifactsTab.d.ts.map +0 -1
- package/session/inspector/ArtifactsTab.js.map +0 -1
- package/session/inspector/ChangesTab.d.ts.map +0 -1
- package/session/inspector/ChangesTab.js.map +0 -1
- package/session/inspector/InspectTab.d.ts.map +0 -1
- package/session/inspector/InspectTab.js.map +0 -1
- package/session/inspector/SessionInspector.d.ts +0 -64
- package/session/inspector/SessionInspector.d.ts.map +0 -1
- package/session/inspector/SessionInspector.js +0 -53
- package/session/inspector/SessionInspector.js.map +0 -1
- package/session/inspector/SetupTab.d.ts.map +0 -1
- package/session/inspector/SetupTab.js.map +0 -1
- package/session/inspector/UsageTab.d.ts.map +0 -1
- package/session/inspector/UsageTab.js.map +0 -1
- package/session/inspector/WorkspaceTab.d.ts +0 -35
- package/session/inspector/WorkspaceTab.d.ts.map +0 -1
- package/session/inspector/WorkspaceTab.js +0 -75
- package/session/inspector/WorkspaceTab.js.map +0 -1
- package/session/inspector/index.d.ts.map +0 -1
- package/session/inspector/index.js.map +0 -1
- package/session/inspector/useSessionInspector.d.ts +0 -49
- package/session/inspector/useSessionInspector.d.ts.map +0 -1
- package/session/inspector/useSessionInspector.js +0 -122
- package/session/inspector/useSessionInspector.js.map +0 -1
- package/src/session/inspector/SessionInspector.tsx +0 -162
- package/src/session/inspector/WorkspaceTab.tsx +0 -245
- package/src/session/inspector/__tests__/useSessionInspector.test.ts +0 -251
- package/src/session/inspector/useSessionInspector.ts +0 -178
- package/src/workspace/WorkspaceEntryFiles.tsx +0 -196
- package/workflow/motion-preference.d.ts.map +0 -1
- package/workflow/motion-preference.js.map +0 -1
- package/workspace/WorkspaceEntryFiles.d.ts +0 -22
- package/workspace/WorkspaceEntryFiles.d.ts.map +0 -1
- package/workspace/WorkspaceEntryFiles.js +0 -58
- package/workspace/WorkspaceEntryFiles.js.map +0 -1
- /package/session/{inspector → facets}/InspectTab.d.ts +0 -0
- /package/session/{inspector → facets}/InspectTab.js +0 -0
- /package/src/{workflow → internal}/__tests__/motion-preference.test.ts +0 -0
- /package/src/session/{inspector → facets}/InspectTab.tsx +0 -0
- /package/src/session/{inspector → facets}/__tests__/ChangesTab.test.tsx +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, fireEvent, cleanup } from "@testing-library/react";
|
|
3
|
+
import { WorkspaceFileSearch } from "../WorkspaceFileSearch";
|
|
4
|
+
import { __clearWorkspaceListingCache } from "../workspaceListingCache";
|
|
5
|
+
import type { WorkspaceEntry } from "../useWorkspaceEntries";
|
|
6
|
+
import type { WorkspaceFileEntry, WorkspaceFileLister } from "../WorkspaceFileLister";
|
|
7
|
+
|
|
8
|
+
let entryIdCounter = 0;
|
|
9
|
+
|
|
10
|
+
function makeEntry(overrides?: Partial<WorkspaceEntry>): WorkspaceEntry {
|
|
11
|
+
return {
|
|
12
|
+
id: `ws-${++entryIdCounter}`,
|
|
13
|
+
name: "acme/api",
|
|
14
|
+
type: "git",
|
|
15
|
+
gitUrl: "https://github.com/acme/api",
|
|
16
|
+
gitBranch: "main",
|
|
17
|
+
...overrides,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const FILES: WorkspaceFileEntry[] = [
|
|
22
|
+
{ path: "src/index.ts", isDirectory: false },
|
|
23
|
+
{ path: "src/button.tsx", isDirectory: false },
|
|
24
|
+
{ path: "README.md", isDirectory: false },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
function typeQuery(value: string) {
|
|
28
|
+
const input = screen.getByRole("combobox");
|
|
29
|
+
fireEvent.change(input, { target: { value } });
|
|
30
|
+
return input;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe("WorkspaceFileSearch", () => {
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
__clearWorkspaceListingCache();
|
|
36
|
+
});
|
|
37
|
+
afterEach(cleanup);
|
|
38
|
+
|
|
39
|
+
it("shows an unavailable state and disables input when no lister is injected", () => {
|
|
40
|
+
render(
|
|
41
|
+
<WorkspaceFileSearch
|
|
42
|
+
entries={[makeEntry()]}
|
|
43
|
+
lister={undefined}
|
|
44
|
+
onOpenFile={vi.fn()}
|
|
45
|
+
/>,
|
|
46
|
+
);
|
|
47
|
+
expect(screen.getByText(/search isn.t available here/i)).toBeTruthy();
|
|
48
|
+
expect(screen.getByRole("combobox")).toHaveProperty("disabled", true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("shows a hint when the query is empty", () => {
|
|
52
|
+
render(
|
|
53
|
+
<WorkspaceFileSearch
|
|
54
|
+
entries={[makeEntry()]}
|
|
55
|
+
lister={vi.fn(async () => FILES)}
|
|
56
|
+
onOpenFile={vi.fn()}
|
|
57
|
+
/>,
|
|
58
|
+
);
|
|
59
|
+
expect(
|
|
60
|
+
screen.getByText(/search files by name across the whole workspace/i),
|
|
61
|
+
).toBeTruthy();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("renders a flat result list and opens a file on click (single entry)", async () => {
|
|
65
|
+
const entry = makeEntry();
|
|
66
|
+
const onOpenFile = vi.fn();
|
|
67
|
+
render(
|
|
68
|
+
<WorkspaceFileSearch
|
|
69
|
+
entries={[entry]}
|
|
70
|
+
lister={vi.fn(async () => FILES)}
|
|
71
|
+
onOpenFile={onOpenFile}
|
|
72
|
+
/>,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
typeQuery("button");
|
|
76
|
+
const option = await screen.findByRole("option");
|
|
77
|
+
fireEvent.click(option);
|
|
78
|
+
|
|
79
|
+
expect(onOpenFile).toHaveBeenCalledWith(entry.id, "src/button.tsx");
|
|
80
|
+
// Single entry → no group header.
|
|
81
|
+
expect(screen.queryByText("acme/api")).toBeNull();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("groups results by entry when more than one entry matches", async () => {
|
|
85
|
+
const a = makeEntry({ name: "acme/api" });
|
|
86
|
+
const b = makeEntry({ name: "acme/web" });
|
|
87
|
+
const lister: WorkspaceFileLister = vi.fn(async (e) =>
|
|
88
|
+
e.id === a.id
|
|
89
|
+
? [{ path: "src/button.tsx", isDirectory: false }]
|
|
90
|
+
: [{ path: "lib/button.ts", isDirectory: false }],
|
|
91
|
+
);
|
|
92
|
+
render(
|
|
93
|
+
<WorkspaceFileSearch entries={[a, b]} lister={lister} onOpenFile={vi.fn()} />,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
typeQuery("button");
|
|
97
|
+
const options = await screen.findAllByRole("option");
|
|
98
|
+
expect(options).toHaveLength(2);
|
|
99
|
+
expect(screen.getByText("acme/api")).toBeTruthy();
|
|
100
|
+
expect(screen.getByText("acme/web")).toBeTruthy();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("highlights the matched substring", async () => {
|
|
104
|
+
const { container } = render(
|
|
105
|
+
<WorkspaceFileSearch
|
|
106
|
+
entries={[makeEntry()]}
|
|
107
|
+
lister={vi.fn(async () => FILES)}
|
|
108
|
+
onOpenFile={vi.fn()}
|
|
109
|
+
/>,
|
|
110
|
+
);
|
|
111
|
+
typeQuery("button");
|
|
112
|
+
await screen.findByRole("option");
|
|
113
|
+
const highlight = container.querySelector(".font-semibold");
|
|
114
|
+
expect(highlight?.textContent).toBe("button");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("marks the currently open file with aria-current", async () => {
|
|
118
|
+
const entry = makeEntry();
|
|
119
|
+
render(
|
|
120
|
+
<WorkspaceFileSearch
|
|
121
|
+
entries={[entry]}
|
|
122
|
+
lister={vi.fn(async () => FILES)}
|
|
123
|
+
onOpenFile={vi.fn()}
|
|
124
|
+
selectedFile={{ entryId: entry.id, path: "src/button.tsx" }}
|
|
125
|
+
/>,
|
|
126
|
+
);
|
|
127
|
+
typeQuery("button");
|
|
128
|
+
const option = await screen.findByRole("option");
|
|
129
|
+
expect(option.getAttribute("aria-current")).toBe("true");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("shows a no-match state", async () => {
|
|
133
|
+
render(
|
|
134
|
+
<WorkspaceFileSearch
|
|
135
|
+
entries={[makeEntry()]}
|
|
136
|
+
lister={vi.fn(async () => FILES)}
|
|
137
|
+
onOpenFile={vi.fn()}
|
|
138
|
+
/>,
|
|
139
|
+
);
|
|
140
|
+
typeQuery("zzzzz");
|
|
141
|
+
// Assert the visible message, not the parallel sr-only live-region copy.
|
|
142
|
+
expect(
|
|
143
|
+
await screen.findByText(/no files matching/i, { ignore: ".sr-only" }),
|
|
144
|
+
).toBeTruthy();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("renders a truncation notice from a notice entry", async () => {
|
|
148
|
+
render(
|
|
149
|
+
<WorkspaceFileSearch
|
|
150
|
+
entries={[makeEntry()]}
|
|
151
|
+
lister={vi.fn(async () => [
|
|
152
|
+
{ path: "src/button.tsx", isDirectory: false },
|
|
153
|
+
{ path: "... truncated", isDirectory: false, notice: true as const },
|
|
154
|
+
])}
|
|
155
|
+
onOpenFile={vi.fn()}
|
|
156
|
+
/>,
|
|
157
|
+
);
|
|
158
|
+
typeQuery("button");
|
|
159
|
+
await screen.findByRole("option");
|
|
160
|
+
expect(screen.getByText(/too many files to search in full/i)).toBeTruthy();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("moves virtual focus with ArrowDown and opens on Enter", async () => {
|
|
164
|
+
const entry = makeEntry();
|
|
165
|
+
const onOpenFile = vi.fn();
|
|
166
|
+
render(
|
|
167
|
+
<WorkspaceFileSearch
|
|
168
|
+
entries={[entry]}
|
|
169
|
+
lister={vi.fn(async () => FILES)}
|
|
170
|
+
onOpenFile={onOpenFile}
|
|
171
|
+
/>,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const input = typeQuery("src"); // matches src/index.ts and src/button.tsx
|
|
175
|
+
await screen.findAllByRole("option");
|
|
176
|
+
|
|
177
|
+
fireEvent.keyDown(input, { key: "ArrowDown" });
|
|
178
|
+
const active = input.getAttribute("aria-activedescendant");
|
|
179
|
+
expect(active).toBeTruthy();
|
|
180
|
+
const focusedOption = document.getElementById(active!);
|
|
181
|
+
expect(focusedOption?.getAttribute("aria-selected")).toBe("true");
|
|
182
|
+
|
|
183
|
+
fireEvent.keyDown(input, { key: "Enter" });
|
|
184
|
+
expect(onOpenFile).toHaveBeenCalledTimes(1);
|
|
185
|
+
expect(onOpenFile.mock.calls[0][0]).toBe(entry.id);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("announces result status in a polite live region", async () => {
|
|
189
|
+
render(
|
|
190
|
+
<WorkspaceFileSearch
|
|
191
|
+
entries={[makeEntry()]}
|
|
192
|
+
lister={vi.fn(async () => FILES)}
|
|
193
|
+
onOpenFile={vi.fn()}
|
|
194
|
+
/>,
|
|
195
|
+
);
|
|
196
|
+
const status = screen.getByRole("status");
|
|
197
|
+
expect(status.getAttribute("aria-live")).toBe("polite");
|
|
198
|
+
|
|
199
|
+
typeQuery("button");
|
|
200
|
+
await screen.findByRole("option");
|
|
201
|
+
expect(status.textContent).toMatch(/matching file/i);
|
|
202
|
+
|
|
203
|
+
typeQuery("zzzzz");
|
|
204
|
+
await screen.findByText(/no files matching/i, { ignore: ".sr-only" });
|
|
205
|
+
expect(status.textContent).toMatch(/no files matching/i);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("caps rendered results and reports the total", async () => {
|
|
209
|
+
const entry = makeEntry();
|
|
210
|
+
const many: WorkspaceFileEntry[] = Array.from({ length: 250 }, (_, i) => ({
|
|
211
|
+
path: `src/match-${i}.ts`,
|
|
212
|
+
isDirectory: false,
|
|
213
|
+
}));
|
|
214
|
+
render(
|
|
215
|
+
<WorkspaceFileSearch
|
|
216
|
+
entries={[entry]}
|
|
217
|
+
lister={vi.fn(async () => many)}
|
|
218
|
+
onOpenFile={vi.fn()}
|
|
219
|
+
/>,
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
typeQuery("match");
|
|
223
|
+
await screen.findAllByRole("option");
|
|
224
|
+
expect(screen.getAllByRole("option")).toHaveLength(200);
|
|
225
|
+
expect(screen.getByText(/showing the first 200 of 250 matches/i)).toBeTruthy();
|
|
226
|
+
});
|
|
227
|
+
});
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent, within } from "@testing-library/react";
|
|
3
|
+
import { forwardRef } from "react";
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// WorkspaceSurface composes the activity rail (Explorer / Search), a resizable
|
|
7
|
+
// sidebar, and the read-only editor. The explorer tree, search, and viewer are
|
|
8
|
+
// exercised by their own suites; here we mock them as probes and prove the
|
|
9
|
+
// surface's own composition — rail switching, the editor tabs/toolbar, collapse,
|
|
10
|
+
// breadcrumbs, and that the active file (+ its change) reaches a chrome-less
|
|
11
|
+
// FileViewer.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
type CapturedProps = Record<string, unknown>;
|
|
15
|
+
|
|
16
|
+
const viewerProps: CapturedProps[] = [];
|
|
17
|
+
vi.mock("../FileViewer", () => ({
|
|
18
|
+
FileViewer: forwardRef((props: CapturedProps, _ref) => {
|
|
19
|
+
viewerProps.push(props);
|
|
20
|
+
return <div data-testid="viewer-probe" />;
|
|
21
|
+
}),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
vi.mock("../ExplorerTree", () => ({
|
|
25
|
+
ExplorerTree: () => <div data-testid="explorer-probe" />,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
vi.mock("../WorkspaceFileSearch", () => ({
|
|
29
|
+
WorkspaceFileSearch: () => <div data-testid="search-probe" />,
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
vi.mock("../WorkspaceContentSearch", () => ({
|
|
33
|
+
WorkspaceContentSearch: () => <div data-testid="content-search-probe" />,
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
import { WorkspaceSurface } from "../WorkspaceSurface";
|
|
37
|
+
import { virtualEntryId } from "../../internal/store/index.js";
|
|
38
|
+
import type { WorkspaceEntry } from "../useWorkspaceEntries";
|
|
39
|
+
import type { WorkspaceFileLister } from "../WorkspaceFileLister";
|
|
40
|
+
import type { WorkspaceFileReader } from "../WorkspaceFileReader";
|
|
41
|
+
import type { WorkspaceContentSearcher } from "../WorkspaceContentSearcher";
|
|
42
|
+
|
|
43
|
+
const gitEntry: WorkspaceEntry = {
|
|
44
|
+
id: "e1",
|
|
45
|
+
name: "acme/app",
|
|
46
|
+
type: "git",
|
|
47
|
+
gitUrl: "https://github.com/acme/app.git",
|
|
48
|
+
gitBranch: "main",
|
|
49
|
+
} as WorkspaceEntry;
|
|
50
|
+
|
|
51
|
+
const lister: WorkspaceFileLister = vi.fn(async () => []) as unknown as WorkspaceFileLister;
|
|
52
|
+
const reader: WorkspaceFileReader = vi.fn(async () => null) as unknown as WorkspaceFileReader;
|
|
53
|
+
const searcher: WorkspaceContentSearcher = vi.fn(async () => ({
|
|
54
|
+
matches: [],
|
|
55
|
+
truncated: false,
|
|
56
|
+
})) as unknown as WorkspaceContentSearcher;
|
|
57
|
+
|
|
58
|
+
function renderSurface(overrides: Partial<React.ComponentProps<typeof WorkspaceSurface>> = {}) {
|
|
59
|
+
const onOpenFile = vi.fn();
|
|
60
|
+
const onActivateEditor = vi.fn();
|
|
61
|
+
const onPinEditor = vi.fn();
|
|
62
|
+
const onCloseEditor = vi.fn();
|
|
63
|
+
const onCollapse = vi.fn();
|
|
64
|
+
render(
|
|
65
|
+
<WorkspaceSurface
|
|
66
|
+
entries={[gitEntry]}
|
|
67
|
+
lister={lister}
|
|
68
|
+
reader={reader}
|
|
69
|
+
editors={[{ entryId: "e1", path: "src/main.go", preview: false }]}
|
|
70
|
+
selectedFile={{ entryId: "e1", path: "src/main.go" }}
|
|
71
|
+
onOpenFile={onOpenFile}
|
|
72
|
+
onActivateEditor={onActivateEditor}
|
|
73
|
+
onPinEditor={onPinEditor}
|
|
74
|
+
onCloseEditor={onCloseEditor}
|
|
75
|
+
onCollapse={onCollapse}
|
|
76
|
+
{...overrides}
|
|
77
|
+
/>,
|
|
78
|
+
);
|
|
79
|
+
return { onOpenFile, onActivateEditor, onPinEditor, onCloseEditor, onCollapse };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
beforeEach(() => {
|
|
83
|
+
viewerProps.length = 0;
|
|
84
|
+
});
|
|
85
|
+
afterEach(() => {
|
|
86
|
+
cleanup();
|
|
87
|
+
vi.clearAllMocks();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("WorkspaceSurface", () => {
|
|
91
|
+
it("renders the activity rail with Explorer selected by default", () => {
|
|
92
|
+
renderSurface();
|
|
93
|
+
const explorer = screen.getByRole("radio", { name: "Explorer" });
|
|
94
|
+
const search = screen.getByRole("radio", { name: "Search" });
|
|
95
|
+
expect(explorer.getAttribute("aria-checked")).toBe("true");
|
|
96
|
+
expect(search.getAttribute("aria-checked")).toBe("false");
|
|
97
|
+
// Explorer view: the explorer tree renders; the search surface does not.
|
|
98
|
+
expect(screen.getByTestId("explorer-probe")).toBeTruthy();
|
|
99
|
+
expect(screen.queryByTestId("search-probe")).toBeNull();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("switches to the search surface when Search is selected", () => {
|
|
103
|
+
renderSurface();
|
|
104
|
+
fireEvent.click(screen.getByRole("radio", { name: "Search" }));
|
|
105
|
+
expect(screen.getByTestId("search-probe")).toBeTruthy();
|
|
106
|
+
expect(screen.queryByTestId("explorer-probe")).toBeNull();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("shows only filename search (no Name|Text toggle) when no content searcher is injected", () => {
|
|
110
|
+
renderSurface();
|
|
111
|
+
fireEvent.click(screen.getByRole("radio", { name: "Search" }));
|
|
112
|
+
expect(screen.queryByRole("radiogroup", { name: "Search mode" })).toBeNull();
|
|
113
|
+
expect(screen.getByTestId("search-probe")).toBeTruthy();
|
|
114
|
+
expect(screen.queryByTestId("content-search-probe")).toBeNull();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("offers a Name|Text toggle when a content searcher is injected, routing Text to content search", () => {
|
|
118
|
+
renderSurface({ searcher });
|
|
119
|
+
fireEvent.click(screen.getByRole("radio", { name: "Search" }));
|
|
120
|
+
|
|
121
|
+
const toggle = screen.getByRole("radiogroup", { name: "Search mode" });
|
|
122
|
+
expect(toggle).toBeTruthy();
|
|
123
|
+
// Filename search is the default mode.
|
|
124
|
+
expect(screen.getByTestId("search-probe")).toBeTruthy();
|
|
125
|
+
expect(screen.queryByTestId("content-search-probe")).toBeNull();
|
|
126
|
+
|
|
127
|
+
// Switching to Text routes to the content-search surface.
|
|
128
|
+
fireEvent.click(within(toggle).getByRole("radio", { name: "Text" }));
|
|
129
|
+
expect(screen.getByTestId("content-search-probe")).toBeTruthy();
|
|
130
|
+
expect(screen.queryByTestId("search-probe")).toBeNull();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("hands the open file (and its change) to a chrome-less FileViewer", () => {
|
|
134
|
+
const change = { path: "src/main.go" } as never;
|
|
135
|
+
renderSurface({ change });
|
|
136
|
+
const props = viewerProps.at(-1);
|
|
137
|
+
expect(props?.selectedFile).toEqual({ entryId: "e1", path: "src/main.go" });
|
|
138
|
+
expect(props?.showHeader).toBe(false);
|
|
139
|
+
expect(props?.change).toBe(change);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("forwards a jump-to-line reveal to the FileViewer", () => {
|
|
143
|
+
const reveal = { line: 12, nonce: 3 };
|
|
144
|
+
renderSurface({ reveal });
|
|
145
|
+
expect(viewerProps.at(-1)?.reveal).toBe(reveal);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("shows the open file as a tab", () => {
|
|
149
|
+
renderSurface();
|
|
150
|
+
const tab = screen.getByRole("tab", { name: /main\.go/ });
|
|
151
|
+
expect(tab).toBeTruthy();
|
|
152
|
+
expect(tab.getAttribute("aria-selected")).toBe("true");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("associates the active tab with the single editor tabpanel", () => {
|
|
156
|
+
renderSurface();
|
|
157
|
+
const tab = screen.getByRole("tab", { name: /main\.go/ });
|
|
158
|
+
const panel = screen.getByRole("tabpanel");
|
|
159
|
+
const panelId = panel.getAttribute("id");
|
|
160
|
+
expect(panelId).toBeTruthy();
|
|
161
|
+
expect(tab.getAttribute("id")).toBeTruthy();
|
|
162
|
+
// Tab -> panel and panel -> active tab, both directions wired.
|
|
163
|
+
expect(tab.getAttribute("aria-controls")).toBe(panelId);
|
|
164
|
+
expect(panel.getAttribute("aria-labelledby")).toBe(tab.getAttribute("id"));
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("renders no tabpanel when no editor is open (nothing to label)", () => {
|
|
168
|
+
renderSurface({ selectedFile: null, editors: [] });
|
|
169
|
+
expect(screen.queryByRole("tabpanel")).toBeNull();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("collapses back to chat when the toolbar control is clicked", () => {
|
|
173
|
+
const { onCollapse } = renderSurface();
|
|
174
|
+
fireEvent.click(screen.getByRole("button", { name: "Back to chat" }));
|
|
175
|
+
expect(onCollapse).toHaveBeenCalledTimes(1);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("shows an empty editor when no file is selected", () => {
|
|
179
|
+
renderSurface({ selectedFile: null, editors: [] });
|
|
180
|
+
expect(viewerProps.length).toBe(0);
|
|
181
|
+
expect(screen.getByText("Select a file to view its contents.")).toBeTruthy();
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// Host-injected rail views (extraViews) — the unified-panel extension seam
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
const configView = {
|
|
190
|
+
id: "configure",
|
|
191
|
+
label: "Config",
|
|
192
|
+
icon: <span data-testid="config-icon" />,
|
|
193
|
+
content: <div data-testid="config-probe" />,
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const changesView = {
|
|
197
|
+
id: "changes",
|
|
198
|
+
label: "Changes",
|
|
199
|
+
icon: <span />,
|
|
200
|
+
badge: 3,
|
|
201
|
+
content: <div data-testid="changes-probe" />,
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
describe("WorkspaceSurface extraViews", () => {
|
|
205
|
+
it("renders injected views in the rail after the built-ins", () => {
|
|
206
|
+
renderSurface({ extraViews: [configView, changesView] });
|
|
207
|
+
const radios = screen.getAllByRole("radio");
|
|
208
|
+
expect(radios.map((r) => r.getAttribute("title"))).toEqual([
|
|
209
|
+
"Explorer",
|
|
210
|
+
"Search",
|
|
211
|
+
"Config",
|
|
212
|
+
"Changes",
|
|
213
|
+
]);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("shows a count badge on a rail view and folds it into the accessible name", () => {
|
|
217
|
+
renderSurface({ extraViews: [changesView] });
|
|
218
|
+
const changes = screen.getByRole("radio", { name: "Changes (3)" });
|
|
219
|
+
expect(changes.textContent).toContain("3");
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("renders an extra view's content in the sidebar (with heading) when selected, keeping the editor area", () => {
|
|
223
|
+
renderSurface({ extraViews: [configView] });
|
|
224
|
+
fireEvent.click(screen.getByRole("radio", { name: "Config" }));
|
|
225
|
+
expect(screen.getByTestId("config-probe")).toBeTruthy();
|
|
226
|
+
expect(screen.queryByTestId("explorer-probe")).toBeNull();
|
|
227
|
+
// Sidebar heading + the editor area (file tab) both present.
|
|
228
|
+
expect(screen.getByText("Config")).toBeTruthy();
|
|
229
|
+
expect(screen.getByRole("tab", { name: /main\.go/ })).toBeTruthy();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("supports controlled view selection via view/onViewChange", () => {
|
|
233
|
+
const onViewChange = vi.fn();
|
|
234
|
+
renderSurface({
|
|
235
|
+
extraViews: [configView],
|
|
236
|
+
view: "configure",
|
|
237
|
+
onViewChange,
|
|
238
|
+
});
|
|
239
|
+
expect(screen.getByTestId("config-probe")).toBeTruthy();
|
|
240
|
+
fireEvent.click(screen.getByRole("radio", { name: "Search" }));
|
|
241
|
+
expect(onViewChange).toHaveBeenCalledWith("search");
|
|
242
|
+
// Controlled: the view does not change until the owner re-renders.
|
|
243
|
+
expect(screen.getByTestId("config-probe")).toBeTruthy();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("falls back to the explorer when the active view id no longer exists", () => {
|
|
247
|
+
// e.g. a contextual view (Inspect) that disappeared while active.
|
|
248
|
+
renderSurface({ extraViews: [], view: "inspect" });
|
|
249
|
+
expect(screen.getByTestId("explorer-probe")).toBeTruthy();
|
|
250
|
+
expect(
|
|
251
|
+
screen.getByRole("radio", { name: "Explorer" }).getAttribute("aria-checked"),
|
|
252
|
+
).toBe("true");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("moves across built-in and injected views with arrow keys", () => {
|
|
256
|
+
renderSurface({ extraViews: [configView] });
|
|
257
|
+
const search = screen.getByRole("radio", { name: "Search" });
|
|
258
|
+
fireEvent.click(search);
|
|
259
|
+
fireEvent.keyDown(search, { key: "ArrowDown" });
|
|
260
|
+
expect(screen.getByTestId("config-probe")).toBeTruthy();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("reaches an injected view beyond the second by keyboard (focus follows selection)", () => {
|
|
264
|
+
// Regression guard: the old handler moved selection but never DOM focus, so
|
|
265
|
+
// focus stayed pinned on the entered button and every view past the second
|
|
266
|
+
// (Config, Changes, …) was keyboard-unreachable. Each arrow must advance
|
|
267
|
+
// focus so the next arrow computes from the new position.
|
|
268
|
+
renderSurface({ extraViews: [configView, changesView] });
|
|
269
|
+
const explorer = screen.getByRole("radio", { name: "Explorer" });
|
|
270
|
+
explorer.focus();
|
|
271
|
+
expect(document.activeElement).toBe(explorer);
|
|
272
|
+
|
|
273
|
+
// Step 1: Explorer -> Search, focus moves with the selection.
|
|
274
|
+
fireEvent.keyDown(document.activeElement!, { key: "ArrowDown" });
|
|
275
|
+
const search = screen.getByRole("radio", { name: "Search" });
|
|
276
|
+
expect(document.activeElement).toBe(search);
|
|
277
|
+
expect(search.getAttribute("aria-checked")).toBe("true");
|
|
278
|
+
|
|
279
|
+
// Step 2: Search -> Config. The step the old code could not make.
|
|
280
|
+
fireEvent.keyDown(document.activeElement!, { key: "ArrowDown" });
|
|
281
|
+
const config = screen.getByRole("radio", { name: "Config" });
|
|
282
|
+
expect(document.activeElement).toBe(config);
|
|
283
|
+
expect(config.getAttribute("aria-checked")).toBe("true");
|
|
284
|
+
expect(screen.getByTestId("config-probe")).toBeTruthy();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("jumps to the last and first rail views with End and Home", () => {
|
|
288
|
+
renderSurface({ extraViews: [configView, changesView] });
|
|
289
|
+
const explorer = screen.getByRole("radio", { name: "Explorer" });
|
|
290
|
+
explorer.focus();
|
|
291
|
+
|
|
292
|
+
fireEvent.keyDown(document.activeElement!, { key: "End" });
|
|
293
|
+
const changes = screen.getByRole("radio", { name: "Changes (3)" });
|
|
294
|
+
expect(document.activeElement).toBe(changes);
|
|
295
|
+
expect(changes.getAttribute("aria-checked")).toBe("true");
|
|
296
|
+
|
|
297
|
+
fireEvent.keyDown(document.activeElement!, { key: "Home" });
|
|
298
|
+
const explorerAgain = screen.getByRole("radio", { name: "Explorer" });
|
|
299
|
+
expect(document.activeElement).toBe(explorerAgain);
|
|
300
|
+
expect(explorerAgain.getAttribute("aria-checked")).toBe("true");
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("moves focus with selection in the Name|Text search-mode toggle", () => {
|
|
304
|
+
renderSurface({ searcher });
|
|
305
|
+
fireEvent.click(screen.getByRole("radio", { name: "Search" }));
|
|
306
|
+
const toggle = screen.getByRole("radiogroup", { name: "Search mode" });
|
|
307
|
+
const name = within(toggle).getByRole("radio", { name: "Name" });
|
|
308
|
+
name.focus();
|
|
309
|
+
|
|
310
|
+
fireEvent.keyDown(document.activeElement!, { key: "ArrowRight" });
|
|
311
|
+
const text = within(toggle).getByRole("radio", { name: "Text" });
|
|
312
|
+
expect(document.activeElement).toBe(text);
|
|
313
|
+
expect(text.getAttribute("aria-checked")).toBe("true");
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
// Host-injected virtual documents — editor tabs that are not workspace files
|
|
319
|
+
// (the session's plan.md today). Same open/pin/close semantics as file tabs;
|
|
320
|
+
// only the body rendering diverges.
|
|
321
|
+
// ---------------------------------------------------------------------------
|
|
322
|
+
|
|
323
|
+
const PLAN_ENTRY_ID = virtualEntryId("plan");
|
|
324
|
+
|
|
325
|
+
const planDocument = {
|
|
326
|
+
entryId: PLAN_ENTRY_ID,
|
|
327
|
+
path: "plan.md",
|
|
328
|
+
content: <div data-testid="plan-doc-probe" />,
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
describe("WorkspaceSurface virtualDocuments", () => {
|
|
332
|
+
it("renders the virtual document's content — no FileViewer, no breadcrumbs", () => {
|
|
333
|
+
renderSurface({
|
|
334
|
+
virtualDocuments: [planDocument],
|
|
335
|
+
editors: [{ entryId: PLAN_ENTRY_ID, path: "plan.md", preview: false }],
|
|
336
|
+
selectedFile: { entryId: PLAN_ENTRY_ID, path: "plan.md" },
|
|
337
|
+
});
|
|
338
|
+
expect(screen.getByTestId("plan-doc-probe")).toBeTruthy();
|
|
339
|
+
expect(viewerProps.length).toBe(0);
|
|
340
|
+
expect(screen.queryByRole("navigation", { name: "File location" })).toBeNull();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("shows the virtual document as an ordinary tab (basename label)", () => {
|
|
344
|
+
renderSurface({
|
|
345
|
+
virtualDocuments: [planDocument],
|
|
346
|
+
editors: [
|
|
347
|
+
{ entryId: "e1", path: "src/main.go", preview: false },
|
|
348
|
+
{ entryId: PLAN_ENTRY_ID, path: "plan.md", preview: false },
|
|
349
|
+
],
|
|
350
|
+
selectedFile: { entryId: PLAN_ENTRY_ID, path: "plan.md" },
|
|
351
|
+
});
|
|
352
|
+
const tab = screen.getByRole("tab", { name: /plan\.md/ });
|
|
353
|
+
expect(tab.getAttribute("aria-selected")).toBe("true");
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it("keeps file tabs on the FileViewer path while a virtual document is injected", () => {
|
|
357
|
+
renderSurface({
|
|
358
|
+
virtualDocuments: [planDocument],
|
|
359
|
+
editors: [
|
|
360
|
+
{ entryId: "e1", path: "src/main.go", preview: false },
|
|
361
|
+
{ entryId: PLAN_ENTRY_ID, path: "plan.md", preview: false },
|
|
362
|
+
],
|
|
363
|
+
selectedFile: { entryId: "e1", path: "src/main.go" },
|
|
364
|
+
});
|
|
365
|
+
expect(screen.queryByTestId("plan-doc-probe")).toBeNull();
|
|
366
|
+
expect(viewerProps.at(-1)?.selectedFile).toEqual({
|
|
367
|
+
entryId: "e1",
|
|
368
|
+
path: "src/main.go",
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("a virtual entry id can never alias a real file of the same path", () => {
|
|
373
|
+
// The NUL-namespaced id is the collision guard: a workspace file named
|
|
374
|
+
// plan.md under a real entry must not hijack the virtual document body.
|
|
375
|
+
renderSurface({
|
|
376
|
+
virtualDocuments: [planDocument],
|
|
377
|
+
editors: [{ entryId: "e1", path: "plan.md", preview: false }],
|
|
378
|
+
selectedFile: { entryId: "e1", path: "plan.md" },
|
|
379
|
+
});
|
|
380
|
+
expect(screen.queryByTestId("plan-doc-probe")).toBeNull();
|
|
381
|
+
expect(viewerProps.at(-1)?.selectedFile).toEqual({
|
|
382
|
+
entryId: "e1",
|
|
383
|
+
path: "plan.md",
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it("scrolls the document body vertically only — wide content reflows, never a sideways scrollbar", () => {
|
|
388
|
+
renderSurface({
|
|
389
|
+
virtualDocuments: [planDocument],
|
|
390
|
+
editors: [{ entryId: PLAN_ENTRY_ID, path: "plan.md", preview: false }],
|
|
391
|
+
selectedFile: { entryId: PLAN_ENTRY_ID, path: "plan.md" },
|
|
392
|
+
});
|
|
393
|
+
const body = screen.getByTestId("plan-doc-probe").parentElement!;
|
|
394
|
+
expect(body.className).toContain("overflow-y-auto");
|
|
395
|
+
// overflow-y alone computes overflow-x to auto; the explicit hidden +
|
|
396
|
+
// min-w-0 pair is what forces documents to reflow at narrow widths.
|
|
397
|
+
expect(body.className).toContain("overflow-x-hidden");
|
|
398
|
+
expect(body.className).toContain("min-w-0");
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// ---------------------------------------------------------------------------
|
|
403
|
+
// Workspace entry management in the Explorer sidebar
|
|
404
|
+
// ---------------------------------------------------------------------------
|
|
405
|
+
|
|
406
|
+
describe("WorkspaceSurface entry management", () => {
|
|
407
|
+
it("renders an Add Folder footer action when onAddLocalFolder is provided", () => {
|
|
408
|
+
const onAddLocalFolder = vi.fn();
|
|
409
|
+
renderSurface({ onAddLocalFolder });
|
|
410
|
+
fireEvent.click(screen.getByRole("button", { name: "Add Folder" }));
|
|
411
|
+
expect(onAddLocalFolder).toHaveBeenCalledTimes(1);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it("omits the Add Folder action without the callback (web has no native picker)", () => {
|
|
415
|
+
renderSurface();
|
|
416
|
+
expect(screen.queryByRole("button", { name: "Add Folder" })).toBeNull();
|
|
417
|
+
});
|
|
418
|
+
});
|