@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,97 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { render, cleanup } from "@testing-library/react";
|
|
3
|
+
import { useRevealLine, type RevealTarget } from "../useRevealLine";
|
|
4
|
+
import { prefersReducedMotion } from "../motion-preference";
|
|
5
|
+
|
|
6
|
+
// Mock the motion utility directly: it caches at module scope, so driving it
|
|
7
|
+
// through `matchMedia` would leak state across cases. Mocking gives each test a
|
|
8
|
+
// deterministic reduced-motion answer.
|
|
9
|
+
vi.mock("../motion-preference", () => ({
|
|
10
|
+
prefersReducedMotion: vi.fn(() => false),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
// A tiny harness that renders addressable line rows and drives the hook, so we
|
|
14
|
+
// exercise the real `[data-line]` query + scroll effect against a real DOM.
|
|
15
|
+
function LineView({
|
|
16
|
+
reveal,
|
|
17
|
+
lineCount = 5,
|
|
18
|
+
}: {
|
|
19
|
+
readonly reveal: RevealTarget | undefined;
|
|
20
|
+
readonly lineCount?: number;
|
|
21
|
+
}) {
|
|
22
|
+
const { containerRef, isRevealed } = useRevealLine<HTMLDivElement>(reveal);
|
|
23
|
+
return (
|
|
24
|
+
<div ref={containerRef}>
|
|
25
|
+
{Array.from({ length: lineCount }, (_, i) => {
|
|
26
|
+
const line = i + 1;
|
|
27
|
+
return (
|
|
28
|
+
<span key={line} data-line={line} data-revealed={isRevealed(line)}>
|
|
29
|
+
line {line}
|
|
30
|
+
</span>
|
|
31
|
+
);
|
|
32
|
+
})}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("useRevealLine", () => {
|
|
38
|
+
let scrollSpy: ReturnType<typeof vi.fn>;
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
vi.mocked(prefersReducedMotion).mockReturnValue(false);
|
|
42
|
+
// happy-dom has no real scrollIntoView — stub it to assert calls + options.
|
|
43
|
+
scrollSpy = vi.fn();
|
|
44
|
+
Element.prototype.scrollIntoView =
|
|
45
|
+
scrollSpy as unknown as typeof Element.prototype.scrollIntoView;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
cleanup();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("scrolls the target line into view on mount", () => {
|
|
53
|
+
render(<LineView reveal={{ line: 3, nonce: 1 }} />);
|
|
54
|
+
expect(scrollSpy).toHaveBeenCalledTimes(1);
|
|
55
|
+
expect(scrollSpy).toHaveBeenCalledWith({ block: "center", behavior: "smooth" });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("re-scrolls when the nonce changes, even for the same line", () => {
|
|
59
|
+
const { rerender } = render(<LineView reveal={{ line: 3, nonce: 1 }} />);
|
|
60
|
+
expect(scrollSpy).toHaveBeenCalledTimes(1);
|
|
61
|
+
|
|
62
|
+
rerender(<LineView reveal={{ line: 3, nonce: 2 }} />);
|
|
63
|
+
expect(scrollSpy).toHaveBeenCalledTimes(2);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("does not re-scroll when the nonce is unchanged", () => {
|
|
67
|
+
const { rerender } = render(<LineView reveal={{ line: 3, nonce: 7 }} />);
|
|
68
|
+
expect(scrollSpy).toHaveBeenCalledTimes(1);
|
|
69
|
+
|
|
70
|
+
// Same nonce, different (irrelevant) prop → no additional scroll.
|
|
71
|
+
rerender(<LineView reveal={{ line: 3, nonce: 7 }} lineCount={6} />);
|
|
72
|
+
expect(scrollSpy).toHaveBeenCalledTimes(1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("is a no-op when the target line is out of range", () => {
|
|
76
|
+
render(<LineView reveal={{ line: 99, nonce: 1 }} lineCount={5} />);
|
|
77
|
+
expect(scrollSpy).not.toHaveBeenCalled();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("is a no-op when there is no reveal target", () => {
|
|
81
|
+
render(<LineView reveal={undefined} />);
|
|
82
|
+
expect(scrollSpy).not.toHaveBeenCalled();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("uses instant scroll under prefers-reduced-motion", () => {
|
|
86
|
+
vi.mocked(prefersReducedMotion).mockReturnValue(true);
|
|
87
|
+
render(<LineView reveal={{ line: 2, nonce: 1 }} />);
|
|
88
|
+
expect(scrollSpy).toHaveBeenCalledWith({ block: "center", behavior: "auto" });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("reports the revealed line via isRevealed", () => {
|
|
92
|
+
const { container } = render(<LineView reveal={{ line: 4, nonce: 1 }} />);
|
|
93
|
+
const revealed = container.querySelector('[data-revealed="true"]');
|
|
94
|
+
expect(revealed?.getAttribute("data-line")).toBe("4");
|
|
95
|
+
expect(container.querySelectorAll('[data-revealed="true"]')).toHaveLength(1);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { fileIconCategory } from "../index";
|
|
3
|
+
|
|
4
|
+
describe("fileIconCategory", () => {
|
|
5
|
+
it("classifies common code extensions", () => {
|
|
6
|
+
for (const name of ["a.ts", "b.tsx", "c.js", "m.py", "s.go", "r.rs"]) {
|
|
7
|
+
expect(fileIconCategory(name)).toBe("code");
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("classifies markup, style, data, markdown, image", () => {
|
|
12
|
+
expect(fileIconCategory("index.html")).toBe("markup");
|
|
13
|
+
expect(fileIconCategory("app.css")).toBe("style");
|
|
14
|
+
expect(fileIconCategory("tsconfig.json")).toBe("data");
|
|
15
|
+
expect(fileIconCategory("config.yaml")).toBe("data");
|
|
16
|
+
expect(fileIconCategory("README.md")).toBe("markdown");
|
|
17
|
+
expect(fileIconCategory("logo.png")).toBe("image");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("is case-insensitive on the extension", () => {
|
|
21
|
+
expect(fileIconCategory("Main.TS")).toBe("code");
|
|
22
|
+
expect(fileIconCategory("PHOTO.PNG")).toBe("image");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("falls back to generic for unknown or extension-less names", () => {
|
|
26
|
+
expect(fileIconCategory("Makefile")).toBe("generic");
|
|
27
|
+
expect(fileIconCategory("data.unknownext")).toBe("generic");
|
|
28
|
+
expect(fileIconCategory("trailingdot.")).toBe("generic");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("uses the last dotted segment for multi-dot names", () => {
|
|
32
|
+
expect(fileIconCategory("app.test.ts")).toBe("code");
|
|
33
|
+
expect(fileIconCategory("archive.tar.gz")).toBe("generic");
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
// Monochrome, shape-differentiated file icons. Deliberately NOT colored with
|
|
6
|
+
// language brand colors: DD-005 forbids hardcoded colors, and every glyph must
|
|
7
|
+
// tint from `currentColor` so it inherits the row's `--stgm-*` token in every
|
|
8
|
+
// preset and color mode (like VS Code's "Minimal" icon theme). Recognition
|
|
9
|
+
// comes from distinct shapes per broad category, not from color.
|
|
10
|
+
|
|
11
|
+
/** Broad file categories, chosen so one glyph serves many related extensions. */
|
|
12
|
+
export type FileIconCategory =
|
|
13
|
+
| "code"
|
|
14
|
+
| "markup"
|
|
15
|
+
| "style"
|
|
16
|
+
| "data"
|
|
17
|
+
| "markdown"
|
|
18
|
+
| "image"
|
|
19
|
+
| "generic";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Extension → category. Kept intentionally coarse: adding a language usually
|
|
23
|
+
* means adding one entry here, not a new glyph. Unknown extensions fall back to
|
|
24
|
+
* the generic document icon.
|
|
25
|
+
*/
|
|
26
|
+
const EXTENSION_CATEGORY: Readonly<Record<string, FileIconCategory>> = {
|
|
27
|
+
// code
|
|
28
|
+
js: "code", jsx: "code", mjs: "code", cjs: "code",
|
|
29
|
+
ts: "code", tsx: "code",
|
|
30
|
+
py: "code", rb: "code", go: "code", rs: "code",
|
|
31
|
+
java: "code", kt: "code", c: "code", h: "code",
|
|
32
|
+
cpp: "code", cc: "code", hpp: "code", cs: "code",
|
|
33
|
+
php: "code", swift: "code", sh: "code", bash: "code", zsh: "code",
|
|
34
|
+
lua: "code", dart: "code", scala: "code", proto: "code",
|
|
35
|
+
// markup
|
|
36
|
+
html: "markup", htm: "markup", xml: "markup", vue: "markup", svelte: "markup",
|
|
37
|
+
// style
|
|
38
|
+
css: "style", scss: "style", sass: "style", less: "style",
|
|
39
|
+
// data / config
|
|
40
|
+
json: "data", jsonc: "data", yaml: "data", yml: "data",
|
|
41
|
+
toml: "data", ini: "data", env: "data", csv: "data", tsv: "data",
|
|
42
|
+
// markdown
|
|
43
|
+
md: "markdown", mdx: "markdown", markdown: "markdown",
|
|
44
|
+
// image
|
|
45
|
+
png: "image", jpg: "image", jpeg: "image", gif: "image",
|
|
46
|
+
webp: "image", svg: "image", ico: "image", bmp: "image",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** Classify a file name into a {@link FileIconCategory} by its extension. */
|
|
50
|
+
export function fileIconCategory(fileName: string): FileIconCategory {
|
|
51
|
+
const dot = fileName.lastIndexOf(".");
|
|
52
|
+
if (dot < 0 || dot === fileName.length - 1) return "generic";
|
|
53
|
+
const ext = fileName.slice(dot + 1).toLowerCase();
|
|
54
|
+
return EXTENSION_CATEGORY[ext] ?? "generic";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A monochrome icon for a file, chosen by extension category. Tints from
|
|
59
|
+
* `currentColor`; size via the surrounding font/box.
|
|
60
|
+
*/
|
|
61
|
+
export function FileTypeIcon({ fileName }: { readonly fileName: string }): ReactNode {
|
|
62
|
+
const category = fileIconCategory(fileName);
|
|
63
|
+
return (
|
|
64
|
+
<svg
|
|
65
|
+
width="14"
|
|
66
|
+
height="14"
|
|
67
|
+
viewBox="0 0 16 16"
|
|
68
|
+
fill="none"
|
|
69
|
+
stroke="currentColor"
|
|
70
|
+
strokeWidth="1.3"
|
|
71
|
+
strokeLinecap="round"
|
|
72
|
+
strokeLinejoin="round"
|
|
73
|
+
className="shrink-0"
|
|
74
|
+
aria-hidden="true"
|
|
75
|
+
>
|
|
76
|
+
{/* Every category shares a document outline for a consistent silhouette. */}
|
|
77
|
+
<path d="M9.5 1.5H4.5C3.95 1.5 3.5 1.95 3.5 2.5V13.5C3.5 14.05 3.95 14.5 4.5 14.5H11.5C12.05 14.5 12.5 14.05 12.5 13.5V4.5L9.5 1.5Z" />
|
|
78
|
+
<path d="M9.3 1.7V4.5H12.3" />
|
|
79
|
+
{CATEGORY_GLYPH[category]}
|
|
80
|
+
</svg>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The inner glyph that distinguishes each category, drawn in the doc body. */
|
|
85
|
+
const CATEGORY_GLYPH: Readonly<Record<FileIconCategory, ReactNode>> = {
|
|
86
|
+
code: (
|
|
87
|
+
<>
|
|
88
|
+
<path d="M7 8.5L5.75 9.75L7 11" />
|
|
89
|
+
<path d="M9 8.5L10.25 9.75L9 11" />
|
|
90
|
+
</>
|
|
91
|
+
),
|
|
92
|
+
markup: (
|
|
93
|
+
<>
|
|
94
|
+
<path d="M6.5 8.5L5.5 9.9L6.5 11.3" />
|
|
95
|
+
<path d="M9.5 8.5L10.5 9.9L9.5 11.3" />
|
|
96
|
+
</>
|
|
97
|
+
),
|
|
98
|
+
style: <path d="M5.5 9.3H10.5M5.5 11.1H8.5" />,
|
|
99
|
+
data: (
|
|
100
|
+
<>
|
|
101
|
+
<path d="M7 8.4C6.2 8.4 6.2 9.3 6.2 9.9C6.2 10.5 6.2 11.4 5.4 11.4" />
|
|
102
|
+
<path d="M9 8.4C9.8 8.4 9.8 9.3 9.8 9.9C9.8 10.5 9.8 11.4 10.6 11.4" />
|
|
103
|
+
</>
|
|
104
|
+
),
|
|
105
|
+
markdown: <path d="M5.4 11.3V8.6L6.9 10.1L8.4 8.6V11.3M10.4 8.7V11.1M9.5 10.2L10.4 11.2L11.3 10.2" />,
|
|
106
|
+
image: (
|
|
107
|
+
<>
|
|
108
|
+
<circle cx="6.4" cy="9" r="0.7" />
|
|
109
|
+
<path d="M5.2 11.4L7.3 9.9L10.6 11.6" />
|
|
110
|
+
</>
|
|
111
|
+
),
|
|
112
|
+
generic: <path d="M5.5 9.3H10.5M5.5 11.1H10.5" />,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/** A folder icon; `open` swaps to the opened-folder silhouette. */
|
|
116
|
+
export function FolderTypeIcon({ open = false }: { readonly open?: boolean }): ReactNode {
|
|
117
|
+
return (
|
|
118
|
+
<svg
|
|
119
|
+
width="14"
|
|
120
|
+
height="14"
|
|
121
|
+
viewBox="0 0 16 16"
|
|
122
|
+
fill="none"
|
|
123
|
+
stroke="currentColor"
|
|
124
|
+
strokeWidth="1.3"
|
|
125
|
+
strokeLinecap="round"
|
|
126
|
+
strokeLinejoin="round"
|
|
127
|
+
className="shrink-0"
|
|
128
|
+
aria-hidden="true"
|
|
129
|
+
>
|
|
130
|
+
{open ? (
|
|
131
|
+
<path d="M1.8 5.5V12A1 1 0 002.8 13H12.6L14.5 7.5H4.2L2.8 11" />
|
|
132
|
+
) : (
|
|
133
|
+
<path d="M1.8 4.5A1 1 0 012.8 3.5H6L7.3 5H13.2A1 1 0 0114.2 6V12A1 1 0 0113.2 13H2.8A1 1 0 011.8 12V4.5Z" />
|
|
134
|
+
)}
|
|
135
|
+
</svg>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useState, type DragEvent } from "react";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { FileTypeIcon, FolderTypeIcon } from "../file-icons/index.js";
|
|
5
6
|
import type { TreeNode } from "./tree-node.js";
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -15,6 +16,12 @@ export interface FileTreeNodeProps {
|
|
|
15
16
|
readonly node: TreeNode;
|
|
16
17
|
readonly selectedPath: string;
|
|
17
18
|
readonly onSelect: (path: string) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Optional double-click handler for file nodes. When set, a double-click on a
|
|
21
|
+
* file calls `onActivate(path)` (used by the workspace surface to pin a
|
|
22
|
+
* previewed tab); the single click still fires `onSelect`. Folders ignore it.
|
|
23
|
+
*/
|
|
24
|
+
readonly onActivate?: (path: string) => void;
|
|
18
25
|
readonly depth: number;
|
|
19
26
|
/**
|
|
20
27
|
* When `true`, file nodes (not folders) become draggable and emit
|
|
@@ -24,6 +31,17 @@ export interface FileTreeNodeProps {
|
|
|
24
31
|
* where file referencing is not applicable (e.g., SkillFileBrowser).
|
|
25
32
|
*/
|
|
26
33
|
readonly enableDrag?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* When `true`, render monochrome file-type / folder icons before each name
|
|
36
|
+
* (VS Code explorer feel). Opt-in so drag/skill contexts stay icon-free.
|
|
37
|
+
*/
|
|
38
|
+
readonly showIcons?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* When `true`, draw vertical indent guides down each nested group and indent
|
|
41
|
+
* via nested margins rather than per-row left padding. Opt-in; the default
|
|
42
|
+
* padding-based indentation is preserved for existing consumers.
|
|
43
|
+
*/
|
|
44
|
+
readonly indentGuides?: boolean;
|
|
27
45
|
/**
|
|
28
46
|
* Maximum depth at which folders start collapsed instead of expanded.
|
|
29
47
|
* Folders at depth >= maxInitialDepth render collapsed by default,
|
|
@@ -49,8 +67,11 @@ export function FileTreeNode({
|
|
|
49
67
|
node,
|
|
50
68
|
selectedPath,
|
|
51
69
|
onSelect,
|
|
70
|
+
onActivate,
|
|
52
71
|
depth,
|
|
53
72
|
enableDrag = false,
|
|
73
|
+
showIcons = false,
|
|
74
|
+
indentGuides = false,
|
|
54
75
|
maxInitialDepth,
|
|
55
76
|
}: FileTreeNodeProps) {
|
|
56
77
|
const [expanded, setExpanded] = useState(
|
|
@@ -68,6 +89,10 @@ export function FileTreeNode({
|
|
|
68
89
|
}
|
|
69
90
|
}, [isFolder, node.path, onSelect]);
|
|
70
91
|
|
|
92
|
+
const handleDoubleClick = useCallback(() => {
|
|
93
|
+
if (!isFolder) onActivate?.(node.path);
|
|
94
|
+
}, [isFolder, node.path, onActivate]);
|
|
95
|
+
|
|
71
96
|
const handleDragStart = useCallback(
|
|
72
97
|
(e: DragEvent) => {
|
|
73
98
|
e.dataTransfer.setData(FILE_REF_MIME, JSON.stringify({ path: node.path }));
|
|
@@ -77,42 +102,68 @@ export function FileTreeNode({
|
|
|
77
102
|
);
|
|
78
103
|
|
|
79
104
|
return (
|
|
80
|
-
<li
|
|
105
|
+
<li
|
|
106
|
+
role="treeitem"
|
|
107
|
+
// Depth conveys tree nesting to assistive tech (indentation is visual
|
|
108
|
+
// only). aria-level is 1-based; `depth` is 0-based. aria-expanded stays on
|
|
109
|
+
// the treeitem (the <li>) — its child group is nested here, so this is the
|
|
110
|
+
// element a tree-aware screen reader reads expandability from.
|
|
111
|
+
aria-level={depth + 1}
|
|
112
|
+
aria-expanded={isFolder ? expanded : undefined}
|
|
113
|
+
>
|
|
81
114
|
<button
|
|
82
115
|
type="button"
|
|
83
116
|
onClick={handleClick}
|
|
117
|
+
onDoubleClick={onActivate ? handleDoubleClick : undefined}
|
|
84
118
|
draggable={isDraggable}
|
|
85
119
|
onDragStart={isDraggable ? handleDragStart : undefined}
|
|
86
120
|
className={cn(
|
|
87
|
-
"flex w-full items-center gap-1.5
|
|
121
|
+
"flex w-full items-center gap-1.5 py-1 text-left text-xs transition-colors",
|
|
88
122
|
"hover:bg-muted",
|
|
89
123
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
|
|
90
124
|
isSelected && !isFolder && "bg-muted text-foreground font-medium",
|
|
91
125
|
!isSelected && "text-muted-foreground",
|
|
92
126
|
isDraggable && "cursor-grab active:cursor-grabbing",
|
|
93
127
|
)}
|
|
94
|
-
|
|
128
|
+
// Indent-guide mode indents via the nested group's margin, so rows need
|
|
129
|
+
// only a small constant inset; padding mode keeps the per-depth inset.
|
|
130
|
+
style={{ paddingLeft: indentGuides ? "8px" : `${12 + depth * 12}px`, paddingRight: "8px" }}
|
|
95
131
|
aria-current={isSelected ? "true" : undefined}
|
|
96
132
|
>
|
|
97
133
|
{isFolder && (
|
|
98
|
-
<span
|
|
134
|
+
<span
|
|
135
|
+
aria-hidden="true"
|
|
136
|
+
className="text-[10px] text-muted-foreground-subtle"
|
|
137
|
+
>
|
|
99
138
|
{expanded ? "▼" : "▶"}
|
|
100
139
|
</span>
|
|
101
140
|
)}
|
|
141
|
+
{showIcons &&
|
|
142
|
+
(isFolder ? (
|
|
143
|
+
<FolderTypeIcon open={expanded} />
|
|
144
|
+
) : (
|
|
145
|
+
<FileTypeIcon fileName={node.name} />
|
|
146
|
+
))}
|
|
102
147
|
<span className={cn("truncate", isFolder && "font-medium text-foreground")}>
|
|
103
148
|
{node.name}
|
|
104
149
|
</span>
|
|
105
150
|
</button>
|
|
106
151
|
{isFolder && expanded && node.children && (
|
|
107
|
-
<ul
|
|
152
|
+
<ul
|
|
153
|
+
role="group"
|
|
154
|
+
className={cn(indentGuides && "ml-3 border-l border-border")}
|
|
155
|
+
>
|
|
108
156
|
{node.children.map((child) => (
|
|
109
157
|
<FileTreeNode
|
|
110
158
|
key={child.path}
|
|
111
159
|
node={child}
|
|
112
160
|
selectedPath={selectedPath}
|
|
113
161
|
onSelect={onSelect}
|
|
162
|
+
onActivate={onActivate}
|
|
114
163
|
depth={depth + 1}
|
|
115
164
|
enableDrag={enableDrag}
|
|
165
|
+
showIcons={showIcons}
|
|
166
|
+
indentGuides={indentGuides}
|
|
116
167
|
maxInitialDepth={maxInitialDepth}
|
|
117
168
|
/>
|
|
118
169
|
))}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, fireEvent, cleanup } from "@testing-library/react";
|
|
3
|
+
import { FileTreeNode } from "../FileTreeNode";
|
|
4
|
+
import type { TreeNode } from "../tree-node";
|
|
5
|
+
|
|
6
|
+
afterEach(cleanup);
|
|
7
|
+
|
|
8
|
+
const fileNode: TreeNode = { name: "a.ts", path: "src/a.ts" };
|
|
9
|
+
const folderNode: TreeNode = {
|
|
10
|
+
name: "src",
|
|
11
|
+
path: "src/",
|
|
12
|
+
children: [fileNode],
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe("FileTreeNode — explorer props", () => {
|
|
16
|
+
it("renders an icon before the name when showIcons is set", () => {
|
|
17
|
+
render(
|
|
18
|
+
<ul>
|
|
19
|
+
<FileTreeNode node={fileNode} selectedPath="" onSelect={vi.fn()} depth={0} showIcons />
|
|
20
|
+
</ul>,
|
|
21
|
+
);
|
|
22
|
+
const button = screen.getByText("a.ts").closest("button")!;
|
|
23
|
+
expect(button.querySelector("svg")).toBeTruthy();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("renders no icon by default (unchanged for existing consumers)", () => {
|
|
27
|
+
render(
|
|
28
|
+
<ul>
|
|
29
|
+
<FileTreeNode node={fileNode} selectedPath="" onSelect={vi.fn()} depth={0} />
|
|
30
|
+
</ul>,
|
|
31
|
+
);
|
|
32
|
+
const button = screen.getByText("a.ts").closest("button")!;
|
|
33
|
+
expect(button.querySelector("svg")).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("calls onActivate on double-click of a file", () => {
|
|
37
|
+
const onActivate = vi.fn();
|
|
38
|
+
render(
|
|
39
|
+
<ul>
|
|
40
|
+
<FileTreeNode
|
|
41
|
+
node={fileNode}
|
|
42
|
+
selectedPath=""
|
|
43
|
+
onSelect={vi.fn()}
|
|
44
|
+
onActivate={onActivate}
|
|
45
|
+
depth={0}
|
|
46
|
+
/>
|
|
47
|
+
</ul>,
|
|
48
|
+
);
|
|
49
|
+
fireEvent.doubleClick(screen.getByText("a.ts"));
|
|
50
|
+
expect(onActivate).toHaveBeenCalledWith("src/a.ts");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("does not call onActivate on a folder double-click", () => {
|
|
54
|
+
const onActivate = vi.fn();
|
|
55
|
+
render(
|
|
56
|
+
<ul>
|
|
57
|
+
<FileTreeNode
|
|
58
|
+
node={folderNode}
|
|
59
|
+
selectedPath=""
|
|
60
|
+
onSelect={vi.fn()}
|
|
61
|
+
onActivate={onActivate}
|
|
62
|
+
depth={0}
|
|
63
|
+
/>
|
|
64
|
+
</ul>,
|
|
65
|
+
);
|
|
66
|
+
fireEvent.doubleClick(screen.getByText("src"));
|
|
67
|
+
expect(onActivate).not.toHaveBeenCalled();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("draws an indent guide on nested groups when indentGuides is set", () => {
|
|
71
|
+
const { container } = render(
|
|
72
|
+
<ul>
|
|
73
|
+
<FileTreeNode
|
|
74
|
+
node={folderNode}
|
|
75
|
+
selectedPath=""
|
|
76
|
+
onSelect={vi.fn()}
|
|
77
|
+
depth={0}
|
|
78
|
+
indentGuides
|
|
79
|
+
/>
|
|
80
|
+
</ul>,
|
|
81
|
+
);
|
|
82
|
+
// The nested group carries the guide border class.
|
|
83
|
+
const group = container.querySelector('ul[role="group"]');
|
|
84
|
+
expect(group?.className).toContain("border-l");
|
|
85
|
+
});
|
|
86
|
+
});
|