@stigmer/react 3.1.1 → 3.1.2
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 +24 -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/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/MessageEntry.d.ts +29 -0
- package/execution/MessageEntry.d.ts.map +1 -1
- package/execution/MessageEntry.js +29 -5
- package/execution/MessageEntry.js.map +1 -1
- package/execution/MessageThread.d.ts +76 -13
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +178 -24
- package/execution/MessageThread.js.map +1 -1
- package/execution/PlanArtifactCard.d.ts +27 -18
- package/execution/PlanArtifactCard.d.ts.map +1 -1
- package/execution/PlanArtifactCard.js +13 -23
- package/execution/PlanArtifactCard.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 +45 -0
- package/execution/PlanStreamingCard.d.ts.map +1 -0
- package/execution/PlanStreamingCard.js +35 -0
- package/execution/PlanStreamingCard.js.map +1 -0
- package/execution/index.d.ts +5 -1
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +2 -0
- package/execution/index.js.map +1 -1
- package/execution/useCreateAgentExecution.d.ts +10 -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 +21 -0
- package/library/detect-plan-artifact.d.ts.map +1 -1
- package/library/detect-plan-artifact.js +20 -0
- 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 +315 -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 +9 -0
- package/session/plan-document.d.ts.map +1 -0
- package/session/plan-document.js +17 -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 +6 -0
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +1 -0
- 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 +52 -10
- package/src/composer/__tests__/SessionComposer-contract.test.tsx +58 -4
- package/src/execution/ArtifactContentRenderer.tsx +101 -24
- package/src/execution/FilePathContext.tsx +8 -1
- package/src/execution/FilePathLink.tsx +22 -6
- package/src/execution/MessageEntry.tsx +111 -1
- package/src/execution/MessageThread.tsx +291 -36
- package/src/execution/PlanArtifactCard.tsx +63 -31
- package/src/execution/PlanDocumentMessage.tsx +96 -0
- package/src/execution/PlanStreamingCard.tsx +137 -0
- package/src/execution/__tests__/ArtifactContentRenderer.test.tsx +80 -1
- package/src/execution/__tests__/FilePathLink.test.tsx +87 -0
- package/src/execution/__tests__/MessageThread.test.tsx +101 -0
- package/src/execution/__tests__/PlanArtifactCard.test.tsx +66 -12
- package/src/execution/__tests__/PlanDocumentMessage.test.tsx +55 -0
- package/src/execution/__tests__/PlanStreamingCard.test.tsx +49 -0
- package/src/execution/__tests__/buildThreadItems-plan-completion.test.ts +504 -22
- package/src/execution/__tests__/message-entry.test.tsx +48 -0
- package/src/execution/__tests__/useCreateAgentExecution.test.tsx +85 -0
- package/src/execution/index.ts +7 -1
- package/src/execution/useCreateAgentExecution.ts +16 -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 +63 -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 +43 -3
- 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 +48 -0
- package/src/library/__tests__/detect-streaming-plan.test.ts +234 -0
- package/src/library/detect-plan-artifact.ts +35 -0
- package/src/library/detect-streaming-plan.ts +108 -0
- package/src/library/index.ts +5 -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 +617 -90
- 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 +9 -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__/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 +35 -1
- 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 +19 -0
- package/src/session/useOpenFileChange.ts +53 -0
- package/src/session/usePlanDraft.ts +109 -0
- package/src/session/useSessionConversation.ts +7 -0
- 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 +145 -0
- package/src/workspace/ExplorerTree.tsx +247 -0
- package/src/workspace/FileViewer.tsx +516 -0
- package/src/workspace/WorkspaceContentSearch.tsx +478 -0
- package/src/workspace/WorkspaceContentSearcher.ts +70 -0
- package/src/workspace/WorkspaceFileLister.ts +11 -0
- package/src/workspace/WorkspaceFileReader.ts +57 -0
- package/src/workspace/WorkspaceFileSearch.tsx +429 -0
- package/src/workspace/WorkspaceSurface.tsx +781 -0
- package/src/workspace/__tests__/EditorTabs.test.tsx +100 -0
- package/src/workspace/__tests__/ExplorerTree.test.tsx +152 -0
- package/src/workspace/__tests__/FileViewer.test.tsx +421 -0
- package/src/workspace/__tests__/WorkspaceContentSearch.test.tsx +196 -0
- package/src/workspace/__tests__/WorkspaceFileSearch.test.tsx +206 -0
- package/src/workspace/__tests__/WorkspaceSurface.test.tsx +348 -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 +29 -0
- package/workspace/EditorTabs.d.ts.map +1 -0
- package/workspace/EditorTabs.js +66 -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 +45 -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 +182 -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 +195 -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/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 +155 -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 +184 -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
|
@@ -72,6 +72,67 @@ describe("ResizableSplit", () => {
|
|
|
72
72
|
});
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
+
describe("collapsedPane", () => {
|
|
76
|
+
/** The pane wrapper div for a rendered probe. */
|
|
77
|
+
function paneOf(testId: string): HTMLElement {
|
|
78
|
+
return screen.getByTestId(testId).parentElement as HTMLElement;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
it("hides the collapsed pane and the separator; the sibling flexes", () => {
|
|
82
|
+
renderSplit({ collapsedPane: "secondary" });
|
|
83
|
+
expect(paneOf("secondary").className).toContain("hidden");
|
|
84
|
+
expect(paneOf("primary").className).toContain("flex-1");
|
|
85
|
+
const separator = screen.getByRole("separator", { hidden: true });
|
|
86
|
+
expect(separator.className).toContain("hidden");
|
|
87
|
+
expect(separator.getAttribute("tabindex")).toBe("-1");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("overrides the resizable pane's fixed width while collapsed", () => {
|
|
91
|
+
renderSplit({ resizablePane: "primary", collapsedPane: "secondary" });
|
|
92
|
+
// Primary is normally pixel-sized; with its sibling collapsed it flexes.
|
|
93
|
+
expect(paneOf("primary").style.width).toBe("");
|
|
94
|
+
expect(paneOf("primary").className).toContain("flex-1");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("keeps both children mounted (same DOM nodes) across a collapse toggle", () => {
|
|
98
|
+
const { rerender } = render(
|
|
99
|
+
<ResizableSplit
|
|
100
|
+
resizablePane="primary"
|
|
101
|
+
collapsedPane="secondary"
|
|
102
|
+
primary={<div data-testid="primary">Primary</div>}
|
|
103
|
+
secondary={<div data-testid="secondary">Secondary</div>}
|
|
104
|
+
/>,
|
|
105
|
+
);
|
|
106
|
+
const primaryBefore = screen.getByTestId("primary");
|
|
107
|
+
const secondaryBefore = screen.getByTestId("secondary");
|
|
108
|
+
rerender(
|
|
109
|
+
<ResizableSplit
|
|
110
|
+
resizablePane="primary"
|
|
111
|
+
collapsedPane="none"
|
|
112
|
+
primary={<div data-testid="primary">Primary</div>}
|
|
113
|
+
secondary={<div data-testid="secondary">Secondary</div>}
|
|
114
|
+
/>,
|
|
115
|
+
);
|
|
116
|
+
// Identical node references — expanding never remounted either pane.
|
|
117
|
+
expect(screen.getByTestId("primary")).toBe(primaryBefore);
|
|
118
|
+
expect(screen.getByTestId("secondary")).toBe(secondaryBefore);
|
|
119
|
+
expect(paneOf("primary").style.width).toBe("384px");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("restores the persisted width when expanding after a collapse", () => {
|
|
123
|
+
localStorage.setItem("collapse-key", "451");
|
|
124
|
+
renderSplit({
|
|
125
|
+
resizablePane: "primary",
|
|
126
|
+
collapsedPane: "secondary",
|
|
127
|
+
storageKey: "collapse-key",
|
|
128
|
+
minSize: 300,
|
|
129
|
+
maxSize: 600,
|
|
130
|
+
});
|
|
131
|
+
const separator = screen.getByRole("separator", { hidden: true });
|
|
132
|
+
expect(separator.getAttribute("aria-valuenow")).toBe("451");
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
75
136
|
describe("keyboard interaction", () => {
|
|
76
137
|
it("increases panel width on ArrowLeft", () => {
|
|
77
138
|
renderSplit({ defaultSize: 400 });
|
|
@@ -145,11 +206,72 @@ describe("ResizableSplit", () => {
|
|
|
145
206
|
expect(separator.getAttribute("tabindex")).toBe("0");
|
|
146
207
|
});
|
|
147
208
|
|
|
148
|
-
it("has accessible label", () => {
|
|
209
|
+
it("has a default accessible label", () => {
|
|
149
210
|
renderSplit();
|
|
150
211
|
const separator = screen.getByRole("separator");
|
|
151
|
-
expect(separator.getAttribute("aria-label")).toBe(
|
|
152
|
-
|
|
212
|
+
expect(separator.getAttribute("aria-label")).toBe("Resize panel");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("uses a custom accessible label", () => {
|
|
216
|
+
renderSplit({ ariaLabel: "Resize file explorer" });
|
|
217
|
+
const separator = screen.getByRole("separator");
|
|
218
|
+
expect(separator.getAttribute("aria-label")).toBe("Resize file explorer");
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe("resizablePane", () => {
|
|
223
|
+
it("grows a primary pane on ArrowRight (toward its own side)", () => {
|
|
224
|
+
renderSplit({ resizablePane: "primary", defaultSize: 400 });
|
|
225
|
+
const separator = screen.getByRole("separator");
|
|
226
|
+
|
|
227
|
+
fireEvent.keyDown(separator, { key: "ArrowRight" });
|
|
228
|
+
|
|
229
|
+
expect(separator.getAttribute("aria-valuenow")).toBe("420");
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("shrinks a primary pane on ArrowLeft", () => {
|
|
233
|
+
renderSplit({ resizablePane: "primary", defaultSize: 400 });
|
|
234
|
+
const separator = screen.getByRole("separator");
|
|
235
|
+
|
|
236
|
+
fireEvent.keyDown(separator, { key: "ArrowLeft" });
|
|
237
|
+
|
|
238
|
+
expect(separator.getAttribute("aria-valuenow")).toBe("380");
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe("width re-initialization on key change", () => {
|
|
243
|
+
it("reloads the width from the new storageKey without remounting", () => {
|
|
244
|
+
localStorage.setItem("key-a", "300");
|
|
245
|
+
localStorage.setItem("key-b", "520");
|
|
246
|
+
const { rerender } = render(
|
|
247
|
+
<ResizableSplit
|
|
248
|
+
primary={<div data-testid="primary">Primary</div>}
|
|
249
|
+
secondary={<div data-testid="secondary">Secondary</div>}
|
|
250
|
+
storageKey="key-a"
|
|
251
|
+
minSize={200}
|
|
252
|
+
maxSize={600}
|
|
253
|
+
/>,
|
|
254
|
+
);
|
|
255
|
+
const primary = screen.getByTestId("primary");
|
|
256
|
+
expect(screen.getByRole("separator").getAttribute("aria-valuenow")).toBe(
|
|
257
|
+
"300",
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
rerender(
|
|
261
|
+
<ResizableSplit
|
|
262
|
+
primary={<div data-testid="primary">Primary</div>}
|
|
263
|
+
secondary={<div data-testid="secondary">Secondary</div>}
|
|
264
|
+
storageKey="key-b"
|
|
265
|
+
resizablePane="primary"
|
|
266
|
+
minSize={200}
|
|
267
|
+
maxSize={600}
|
|
268
|
+
/>,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// Same DOM node — the child was re-flowed, not remounted.
|
|
272
|
+
expect(screen.getByTestId("primary")).toBe(primary);
|
|
273
|
+
expect(screen.getByRole("separator").getAttribute("aria-valuenow")).toBe(
|
|
274
|
+
"520",
|
|
153
275
|
);
|
|
154
276
|
});
|
|
155
277
|
});
|
|
@@ -3,6 +3,7 @@ import { render, cleanup } from "@testing-library/react";
|
|
|
3
3
|
import type { ComponentType, ReactNode } from "react";
|
|
4
4
|
import {
|
|
5
5
|
MARKDOWN_COMPONENTS,
|
|
6
|
+
extractLeadingH1,
|
|
6
7
|
unwrapEnclosingMarkdownFence,
|
|
7
8
|
} from "../markdown-components";
|
|
8
9
|
|
|
@@ -63,6 +64,73 @@ describe("unwrapEnclosingMarkdownFence", () => {
|
|
|
63
64
|
const once = unwrapEnclosingMarkdownFence(wrapped);
|
|
64
65
|
expect(unwrapEnclosingMarkdownFence(once)).toBe(once);
|
|
65
66
|
});
|
|
67
|
+
|
|
68
|
+
describe("allowBareFence (plan-document surfaces)", () => {
|
|
69
|
+
it.each([
|
|
70
|
+
["a bare whole-body fence", "```\n# Plan\n- step\n```", "# Plan\n- step"],
|
|
71
|
+
[
|
|
72
|
+
"a bare fence with surrounding whitespace",
|
|
73
|
+
"\n```\n# Plan\n```\n",
|
|
74
|
+
"# Plan",
|
|
75
|
+
],
|
|
76
|
+
[
|
|
77
|
+
"still unwraps the tagged form",
|
|
78
|
+
"```markdown\n# Plan\n```",
|
|
79
|
+
"# Plan",
|
|
80
|
+
],
|
|
81
|
+
])("unwraps %s", (_label, input, expected) => {
|
|
82
|
+
expect(unwrapEnclosingMarkdownFence(input, true)).toBe(expected);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it.each([
|
|
86
|
+
["a language-tagged code block", "```python\nprint('hi')\n```"],
|
|
87
|
+
["an unclosed bare fence (mid-stream)", "```\n# Plan in progr"],
|
|
88
|
+
["trailing content after the fence", "```\n# Plan\n```\nthen more"],
|
|
89
|
+
["leading content before the fence", "intro\n```\n# Plan\n```"],
|
|
90
|
+
["plain markdown (no fence)", "# Plan\n\n- step"],
|
|
91
|
+
])("leaves %s untouched even with allowBareFence", (_label, input) => {
|
|
92
|
+
expect(unwrapEnclosingMarkdownFence(input, true)).toBe(input);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("extractLeadingH1", () => {
|
|
98
|
+
it("splits a leading # heading into title and body", () => {
|
|
99
|
+
expect(extractLeadingH1("# The Plan\n\nFirst paragraph.")).toEqual({
|
|
100
|
+
title: "The Plan",
|
|
101
|
+
body: "First paragraph.",
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("handles a document that is only a heading", () => {
|
|
106
|
+
expect(extractLeadingH1("# Just a Title")).toEqual({
|
|
107
|
+
title: "Just a Title",
|
|
108
|
+
body: "",
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("ignores leading whitespace before the heading", () => {
|
|
113
|
+
expect(extractLeadingH1("\n\n# The Plan\nBody")).toEqual({
|
|
114
|
+
title: "The Plan",
|
|
115
|
+
body: "Body",
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("returns null title when the document does not open with an H1", () => {
|
|
120
|
+
const doc = "Intro paragraph.\n\n# Later Heading\nBody";
|
|
121
|
+
expect(extractLeadingH1(doc)).toEqual({ title: null, body: doc });
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("does not treat deeper headings as the title", () => {
|
|
125
|
+
const doc = "## Section\nBody";
|
|
126
|
+
expect(extractLeadingH1(doc)).toEqual({ title: null, body: doc });
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("trims trailing whitespace from the title", () => {
|
|
130
|
+
expect(extractLeadingH1("# Spaced Title \nBody").title).toBe(
|
|
131
|
+
"Spaced Title",
|
|
132
|
+
);
|
|
133
|
+
});
|
|
66
134
|
});
|
|
67
135
|
|
|
68
136
|
describe("MARKDOWN_COMPONENTS.code (shared highlight seam)", () => {
|
|
@@ -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 }));
|
|
@@ -81,17 +106,20 @@ export function FileTreeNode({
|
|
|
81
106
|
<button
|
|
82
107
|
type="button"
|
|
83
108
|
onClick={handleClick}
|
|
109
|
+
onDoubleClick={onActivate ? handleDoubleClick : undefined}
|
|
84
110
|
draggable={isDraggable}
|
|
85
111
|
onDragStart={isDraggable ? handleDragStart : undefined}
|
|
86
112
|
className={cn(
|
|
87
|
-
"flex w-full items-center gap-1.5
|
|
113
|
+
"flex w-full items-center gap-1.5 py-1 text-left text-xs transition-colors",
|
|
88
114
|
"hover:bg-muted",
|
|
89
115
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
|
|
90
116
|
isSelected && !isFolder && "bg-muted text-foreground font-medium",
|
|
91
117
|
!isSelected && "text-muted-foreground",
|
|
92
118
|
isDraggable && "cursor-grab active:cursor-grabbing",
|
|
93
119
|
)}
|
|
94
|
-
|
|
120
|
+
// Indent-guide mode indents via the nested group's margin, so rows need
|
|
121
|
+
// only a small constant inset; padding mode keeps the per-depth inset.
|
|
122
|
+
style={{ paddingLeft: indentGuides ? "8px" : `${12 + depth * 12}px`, paddingRight: "8px" }}
|
|
95
123
|
aria-current={isSelected ? "true" : undefined}
|
|
96
124
|
>
|
|
97
125
|
{isFolder && (
|
|
@@ -99,20 +127,32 @@ export function FileTreeNode({
|
|
|
99
127
|
{expanded ? "▼" : "▶"}
|
|
100
128
|
</span>
|
|
101
129
|
)}
|
|
130
|
+
{showIcons &&
|
|
131
|
+
(isFolder ? (
|
|
132
|
+
<FolderTypeIcon open={expanded} />
|
|
133
|
+
) : (
|
|
134
|
+
<FileTypeIcon fileName={node.name} />
|
|
135
|
+
))}
|
|
102
136
|
<span className={cn("truncate", isFolder && "font-medium text-foreground")}>
|
|
103
137
|
{node.name}
|
|
104
138
|
</span>
|
|
105
139
|
</button>
|
|
106
140
|
{isFolder && expanded && node.children && (
|
|
107
|
-
<ul
|
|
141
|
+
<ul
|
|
142
|
+
role="group"
|
|
143
|
+
className={cn(indentGuides && "ml-3 border-l border-border")}
|
|
144
|
+
>
|
|
108
145
|
{node.children.map((child) => (
|
|
109
146
|
<FileTreeNode
|
|
110
147
|
key={child.path}
|
|
111
148
|
node={child}
|
|
112
149
|
selectedPath={selectedPath}
|
|
113
150
|
onSelect={onSelect}
|
|
151
|
+
onActivate={onActivate}
|
|
114
152
|
depth={depth + 1}
|
|
115
153
|
enableDrag={enableDrag}
|
|
154
|
+
showIcons={showIcons}
|
|
155
|
+
indentGuides={indentGuides}
|
|
116
156
|
maxInitialDepth={maxInitialDepth}
|
|
117
157
|
/>
|
|
118
158
|
))}
|