@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,537 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
forwardRef,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useImperativeHandle,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
type KeyboardEvent,
|
|
11
|
+
} from "react";
|
|
12
|
+
import { cn } from "@stigmer/theme";
|
|
13
|
+
import type { FileChange } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
14
|
+
import { FileChangeType } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
15
|
+
import { ArtifactContentRenderer } from "../execution/ArtifactContentRenderer.js";
|
|
16
|
+
import { FileChangeDiff } from "../execution/FileChangesView.js";
|
|
17
|
+
import type { RevealTarget } from "../internal/useRevealLine.js";
|
|
18
|
+
import type { SelectedWorkspaceFile } from "../internal/store/workspace-file-selection-store.js";
|
|
19
|
+
import { useWorkspaceFileContent } from "./useWorkspaceFileContent.js";
|
|
20
|
+
import type { WorkspaceEntry } from "./useWorkspaceEntries.js";
|
|
21
|
+
import type { WorkspaceFileReader } from "./WorkspaceFileReader.js";
|
|
22
|
+
|
|
23
|
+
/** Line-skeleton widths, mirroring `FileContentStateView` in `ArtifactPreviewModal`. */
|
|
24
|
+
const SKELETON_LINE_WIDTHS = [85, 72, 90, 65, 78, 88, 70, 82] as const;
|
|
25
|
+
|
|
26
|
+
/** Props for {@link FileViewer}. */
|
|
27
|
+
export interface FileViewerProps {
|
|
28
|
+
/** The file to display, keyed by owning entry id + path. */
|
|
29
|
+
readonly selectedFile: SelectedWorkspaceFile;
|
|
30
|
+
/**
|
|
31
|
+
* Live workspace entries. The viewer resolves `selectedFile.entryId` against
|
|
32
|
+
* this list, so a removed entry deselects into a gentle empty state rather
|
|
33
|
+
* than reading a stale value.
|
|
34
|
+
*/
|
|
35
|
+
readonly entries: readonly WorkspaceEntry[];
|
|
36
|
+
/**
|
|
37
|
+
* Platform-injected content reader. `undefined` means the host injects no
|
|
38
|
+
* reader — the viewer shows an honest "not available here" state.
|
|
39
|
+
*/
|
|
40
|
+
readonly reader: WorkspaceFileReader | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* The session {@link FileChange} that touched this file, when it was changed
|
|
43
|
+
* this session. When present the viewer defaults to the authoritative
|
|
44
|
+
* `baseline→candidate` diff (DD-06) and — for a non-DELETE change with a
|
|
45
|
+
* `reader` — offers a labeled toggle to the live "File" view. `undefined`
|
|
46
|
+
* (the default) keeps the viewer a pure read-only browser (backward
|
|
47
|
+
* compatible). Callers correlate the open file to its change with
|
|
48
|
+
* `findChangeForSelection`; a platform builder can pass any `FileChange`.
|
|
49
|
+
*/
|
|
50
|
+
readonly change?: FileChange;
|
|
51
|
+
/**
|
|
52
|
+
* Optional jump-to-line request (e.g. a content-search hit). When present the
|
|
53
|
+
* viewer opens in the live **File** view — a line number refers to current
|
|
54
|
+
* source, not the diff (DR-1) — with the `Diff | File` toggle still available,
|
|
55
|
+
* and the matched line is scrolled into view and highlighted. A new `nonce`
|
|
56
|
+
* re-forces File view and re-scrolls even on an already-open file. No-op when
|
|
57
|
+
* the file is not live-browsable (a DELETE, or no `reader`).
|
|
58
|
+
*/
|
|
59
|
+
readonly reveal?: RevealTarget;
|
|
60
|
+
/** Called when the user closes the viewer (close button or Escape). */
|
|
61
|
+
readonly onClose?: () => void;
|
|
62
|
+
/**
|
|
63
|
+
* Whether to render the built-in header (file name, refresh, close). Defaults
|
|
64
|
+
* to `true` for standalone use. Set `false` when an outer chrome owns the file
|
|
65
|
+
* identity and controls — e.g. the workspace surface, whose editor toolbar
|
|
66
|
+
* (and, from Slice B, its tabs) show the name and collapse control, and where
|
|
67
|
+
* a duplicate header would also collide with the floating `headerActions`
|
|
68
|
+
* overlay. `onClose` still drives Escape-to-close even when the header (and
|
|
69
|
+
* its close button) is hidden.
|
|
70
|
+
*/
|
|
71
|
+
readonly showHeader?: boolean;
|
|
72
|
+
/** Additional CSS classes for the root element. */
|
|
73
|
+
readonly className?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Imperative handle for {@link FileViewer}. Lets an outer chrome (e.g. the
|
|
78
|
+
* workspace surface's toolbar, which hides the built-in header) trigger a
|
|
79
|
+
* re-fetch of the live file content without lifting the viewer's fetch state.
|
|
80
|
+
*/
|
|
81
|
+
export interface FileViewerHandle {
|
|
82
|
+
/** Re-fetch the current file's live content. */
|
|
83
|
+
readonly refresh: () => void;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Read-only viewer for a single workspace file.
|
|
88
|
+
*
|
|
89
|
+
* A thin shell (no new rendering): it resolves the open file, drives
|
|
90
|
+
* {@link useWorkspaceFileContent}, and — for displayable text — delegates to
|
|
91
|
+
* the shared {@link ArtifactContentRenderer} (markdown / YAML / JSON /
|
|
92
|
+
* line-numbered text). Its state body deliberately mirrors
|
|
93
|
+
* `FileContentStateView` in `ArtifactPreviewModal` (same skeleton, error, and
|
|
94
|
+
* delegation), extended with the three states an execution artifact never
|
|
95
|
+
* has — binary, too-large, and unsupported-substrate. The two are **not**
|
|
96
|
+
* merged into one shared component: their surrounding chrome (an artifact's
|
|
97
|
+
* copy/download/apply bar vs. this viewer's header) differs enough that a
|
|
98
|
+
* shared shell would need context-flag props. Keep them as sibling shells that
|
|
99
|
+
* share the one renderer.
|
|
100
|
+
*
|
|
101
|
+
* Framework-agnostic and themed via `--stgm-*` tokens (DD-004/DD-005): a
|
|
102
|
+
* platform builder can mount it directly given a `reader` and a selection.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```tsx
|
|
106
|
+
* <FileViewer
|
|
107
|
+
* selectedFile={{ entryId: entry.id, path: "src/index.ts" }}
|
|
108
|
+
* entries={workspace.entries}
|
|
109
|
+
* reader={workspaceFileReader}
|
|
110
|
+
* onClose={() => setSelected(null)}
|
|
111
|
+
* />
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export const FileViewer = forwardRef<FileViewerHandle, FileViewerProps>(
|
|
115
|
+
function FileViewer(
|
|
116
|
+
{ selectedFile, entries, reader, change, reveal, onClose, showHeader = true, className },
|
|
117
|
+
ref,
|
|
118
|
+
) {
|
|
119
|
+
const entry = entries.find((e) => e.id === selectedFile.entryId) ?? null;
|
|
120
|
+
const path = selectedFile.path;
|
|
121
|
+
|
|
122
|
+
// The hook is called unconditionally — a missing entry passes `null` and the
|
|
123
|
+
// hook stays idle, so hook order is stable across the not-found branch.
|
|
124
|
+
const { content, isLoading, error, isUnsupported, refetch } =
|
|
125
|
+
useWorkspaceFileContent({ entry, path, reader });
|
|
126
|
+
|
|
127
|
+
useImperativeHandle(ref, () => ({ refresh: refetch }), [refetch]);
|
|
128
|
+
|
|
129
|
+
// View mode is meaningful only for a changed file. A deleted file has no live
|
|
130
|
+
// bytes to browse, and browsing needs a reader, so the live "File" view is
|
|
131
|
+
// gated on both; otherwise a changed file shows the diff alone (no lone
|
|
132
|
+
// control). The default is the authoritative diff (DD-06). The mount site
|
|
133
|
+
// remounts the viewer per file (`key`), so this initializes correctly for
|
|
134
|
+
// each opened file without deriving during render.
|
|
135
|
+
const canBrowseLive = reader !== undefined && change?.changeType !== FileChangeType.DELETE;
|
|
136
|
+
const showViewToggle = change !== undefined && canBrowseLive;
|
|
137
|
+
// A reveal (jump-to-line) opens in File view even for a changed file — the
|
|
138
|
+
// line number refers to current source, not the diff (DR-1). Absent a reveal,
|
|
139
|
+
// a changed file still defaults to the authoritative diff (DD-06).
|
|
140
|
+
const [viewMode, setViewMode] = useState<"diff" | "file">(
|
|
141
|
+
reveal ? "file" : change !== undefined ? "diff" : "file",
|
|
142
|
+
);
|
|
143
|
+
// A new reveal on an already-mounted viewer (a second search hit in the same
|
|
144
|
+
// file, so no remount) must return to File view to honor the line.
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (reveal) setViewMode("file");
|
|
147
|
+
// Keyed on the reveal nonce: only a fresh jump-to-line request re-forces
|
|
148
|
+
// File view, so a manual switch to Diff afterward sticks.
|
|
149
|
+
}, [reveal?.nonce]);
|
|
150
|
+
const effectiveView: "diff" | "file" = change
|
|
151
|
+
? canBrowseLive
|
|
152
|
+
? viewMode
|
|
153
|
+
: "diff"
|
|
154
|
+
: "file";
|
|
155
|
+
|
|
156
|
+
const handleKeyDown = useCallback(
|
|
157
|
+
(e: KeyboardEvent<HTMLDivElement>) => {
|
|
158
|
+
if (e.key === "Escape" && onClose) {
|
|
159
|
+
e.stopPropagation();
|
|
160
|
+
onClose();
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
[onClose],
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const lastSlash = path.lastIndexOf("/");
|
|
167
|
+
const basename = lastSlash >= 0 ? path.slice(lastSlash + 1) : path;
|
|
168
|
+
const dir = lastSlash >= 0 ? path.slice(0, lastSlash) : "";
|
|
169
|
+
|
|
170
|
+
// The owning entry is gone (removed while its file was open).
|
|
171
|
+
if (!entry) {
|
|
172
|
+
return (
|
|
173
|
+
<div
|
|
174
|
+
role="region"
|
|
175
|
+
aria-label="File viewer"
|
|
176
|
+
className={cn("flex h-full flex-col", className)}
|
|
177
|
+
onKeyDown={handleKeyDown}
|
|
178
|
+
>
|
|
179
|
+
{showHeader && (
|
|
180
|
+
<ViewerHeader
|
|
181
|
+
basename={basename}
|
|
182
|
+
dir={dir}
|
|
183
|
+
onRefresh={undefined}
|
|
184
|
+
onClose={onClose}
|
|
185
|
+
/>
|
|
186
|
+
)}
|
|
187
|
+
<div className="flex flex-1 items-center justify-center p-8 text-center text-xs text-muted-foreground">
|
|
188
|
+
This file is no longer in the workspace.
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<div
|
|
196
|
+
role="region"
|
|
197
|
+
aria-label="File viewer"
|
|
198
|
+
// Focusable (programmatically / on click) so the Escape-to-close handler
|
|
199
|
+
// fires even when the read-only body holds no natively focusable element.
|
|
200
|
+
tabIndex={-1}
|
|
201
|
+
className={cn("flex h-full flex-col focus:outline-none", className)}
|
|
202
|
+
onKeyDown={handleKeyDown}
|
|
203
|
+
>
|
|
204
|
+
{showHeader && (
|
|
205
|
+
<ViewerHeader
|
|
206
|
+
basename={basename}
|
|
207
|
+
dir={dir}
|
|
208
|
+
onRefresh={effectiveView === "file" && !isUnsupported ? refetch : undefined}
|
|
209
|
+
onClose={onClose}
|
|
210
|
+
/>
|
|
211
|
+
)}
|
|
212
|
+
{showViewToggle && (
|
|
213
|
+
<ViewerModeToggle value={viewMode} onChange={setViewMode} />
|
|
214
|
+
)}
|
|
215
|
+
<div className="min-h-0 flex-1 overflow-y-auto">
|
|
216
|
+
{effectiveView === "diff" && change ? (
|
|
217
|
+
<div className="p-3">
|
|
218
|
+
<FileChangeDiff change={change} showFileName={false} showStats />
|
|
219
|
+
</div>
|
|
220
|
+
) : (
|
|
221
|
+
<>
|
|
222
|
+
{change && (
|
|
223
|
+
<p className="border-b border-border-muted px-4 py-1.5 text-[0.65rem] text-muted-foreground">
|
|
224
|
+
Live — may differ from the reviewed change.
|
|
225
|
+
</p>
|
|
226
|
+
)}
|
|
227
|
+
<FileViewerBody
|
|
228
|
+
basename={basename}
|
|
229
|
+
content={content}
|
|
230
|
+
isLoading={isLoading}
|
|
231
|
+
isUnsupported={isUnsupported}
|
|
232
|
+
error={error}
|
|
233
|
+
onRetry={refetch}
|
|
234
|
+
reveal={reveal}
|
|
235
|
+
/>
|
|
236
|
+
</>
|
|
237
|
+
)}
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
},
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// View toggle — Diff vs live File
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Segmented control for a changed file's two views: the authoritative reviewed
|
|
250
|
+
* **Diff** and the live **File**. A mutually-exclusive choice, so it follows
|
|
251
|
+
* the platform's radiogroup segmented-control pattern (see `library/ScopeToggle`)
|
|
252
|
+
* rather than a tablist — it must not nest a second tablist inside the
|
|
253
|
+
* inspector's own tabs.
|
|
254
|
+
*/
|
|
255
|
+
function ViewerModeToggle({
|
|
256
|
+
value,
|
|
257
|
+
onChange,
|
|
258
|
+
}: {
|
|
259
|
+
readonly value: "diff" | "file";
|
|
260
|
+
readonly onChange: (next: "diff" | "file") => void;
|
|
261
|
+
}) {
|
|
262
|
+
const options: readonly { readonly value: "diff" | "file"; readonly label: string }[] = [
|
|
263
|
+
{ value: "diff", label: "Diff" },
|
|
264
|
+
{ value: "file", label: "File" },
|
|
265
|
+
];
|
|
266
|
+
|
|
267
|
+
// Selection follows focus, matching ActivityRail / SearchModeToggle.
|
|
268
|
+
const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]);
|
|
269
|
+
const selectValue = useCallback(
|
|
270
|
+
(next: "diff" | "file") => {
|
|
271
|
+
onChange(next);
|
|
272
|
+
buttonRefs.current[next === "diff" ? 0 : 1]?.focus();
|
|
273
|
+
},
|
|
274
|
+
[onChange],
|
|
275
|
+
);
|
|
276
|
+
const handleKeyDown = useCallback(
|
|
277
|
+
(e: KeyboardEvent<HTMLButtonElement>) => {
|
|
278
|
+
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
|
|
279
|
+
e.preventDefault();
|
|
280
|
+
selectValue("file");
|
|
281
|
+
} else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
|
|
282
|
+
e.preventDefault();
|
|
283
|
+
selectValue("diff");
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
[selectValue],
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
return (
|
|
290
|
+
<div className="border-b border-border px-3 py-1.5">
|
|
291
|
+
<div
|
|
292
|
+
role="radiogroup"
|
|
293
|
+
aria-label="File view"
|
|
294
|
+
className="inline-flex rounded-md bg-muted p-0.5"
|
|
295
|
+
>
|
|
296
|
+
{options.map((option, index) => {
|
|
297
|
+
const isSelected = value === option.value;
|
|
298
|
+
return (
|
|
299
|
+
<button
|
|
300
|
+
key={option.value}
|
|
301
|
+
ref={(el) => {
|
|
302
|
+
buttonRefs.current[index] = el;
|
|
303
|
+
}}
|
|
304
|
+
type="button"
|
|
305
|
+
role="radio"
|
|
306
|
+
aria-checked={isSelected}
|
|
307
|
+
tabIndex={isSelected ? 0 : -1}
|
|
308
|
+
onClick={() => onChange(option.value)}
|
|
309
|
+
onKeyDown={handleKeyDown}
|
|
310
|
+
className={cn(
|
|
311
|
+
"inline-flex cursor-pointer items-center rounded-sm px-2 py-0.5 text-xs font-medium transition-colors",
|
|
312
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
313
|
+
isSelected
|
|
314
|
+
? "bg-background text-foreground shadow-sm"
|
|
315
|
+
: "text-muted-foreground hover:text-foreground",
|
|
316
|
+
)}
|
|
317
|
+
>
|
|
318
|
+
{option.label}
|
|
319
|
+
</button>
|
|
320
|
+
);
|
|
321
|
+
})}
|
|
322
|
+
</div>
|
|
323
|
+
</div>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ---------------------------------------------------------------------------
|
|
328
|
+
// Body — exactly one state, mirroring FileContentStateView + workspace extras
|
|
329
|
+
// ---------------------------------------------------------------------------
|
|
330
|
+
|
|
331
|
+
function FileViewerBody({
|
|
332
|
+
basename,
|
|
333
|
+
content,
|
|
334
|
+
isLoading,
|
|
335
|
+
isUnsupported,
|
|
336
|
+
error,
|
|
337
|
+
onRetry,
|
|
338
|
+
reveal,
|
|
339
|
+
}: {
|
|
340
|
+
readonly basename: string;
|
|
341
|
+
readonly content: ReturnType<typeof useWorkspaceFileContent>["content"];
|
|
342
|
+
readonly isLoading: boolean;
|
|
343
|
+
readonly isUnsupported: boolean;
|
|
344
|
+
readonly error: Error | null;
|
|
345
|
+
readonly onRetry: () => void;
|
|
346
|
+
readonly reveal?: RevealTarget;
|
|
347
|
+
}) {
|
|
348
|
+
// Unsupported comes first: a missing reader means there is nothing to load.
|
|
349
|
+
if (isUnsupported) {
|
|
350
|
+
return (
|
|
351
|
+
<MessageState>
|
|
352
|
+
Preview isn’t available for this file here.
|
|
353
|
+
</MessageState>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (isLoading) {
|
|
358
|
+
return (
|
|
359
|
+
<div
|
|
360
|
+
role="status"
|
|
361
|
+
className="space-y-2 p-4"
|
|
362
|
+
aria-busy="true"
|
|
363
|
+
aria-label="Loading file"
|
|
364
|
+
>
|
|
365
|
+
{SKELETON_LINE_WIDTHS.map((width, i) => (
|
|
366
|
+
<div
|
|
367
|
+
key={i}
|
|
368
|
+
className="h-4 animate-pulse rounded bg-muted"
|
|
369
|
+
style={{ width: `${width}%` }}
|
|
370
|
+
aria-hidden="true"
|
|
371
|
+
/>
|
|
372
|
+
))}
|
|
373
|
+
</div>
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (error) {
|
|
378
|
+
return (
|
|
379
|
+
<div className="flex flex-col items-center justify-center gap-2 p-8 text-center">
|
|
380
|
+
<ErrorAlertIcon />
|
|
381
|
+
<p className="text-sm text-destructive">{error.message}</p>
|
|
382
|
+
<button
|
|
383
|
+
type="button"
|
|
384
|
+
onClick={onRetry}
|
|
385
|
+
className="text-xs font-medium text-foreground underline underline-offset-2 hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:rounded-sm"
|
|
386
|
+
>
|
|
387
|
+
Retry
|
|
388
|
+
</button>
|
|
389
|
+
</div>
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (!content) {
|
|
394
|
+
// Not loading, not unsupported, no error, yet no content — defensive.
|
|
395
|
+
return <MessageState>Content not available for preview.</MessageState>;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (content.isBinary) {
|
|
399
|
+
return (
|
|
400
|
+
<MessageState>
|
|
401
|
+
Binary file — not shown{content.size ? ` (${formatBytes(content.size)})` : ""}.
|
|
402
|
+
</MessageState>
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (content.text === null && content.truncated) {
|
|
407
|
+
return (
|
|
408
|
+
<MessageState>
|
|
409
|
+
This file is too large to preview
|
|
410
|
+
{content.size ? ` (${formatBytes(content.size)})` : ""}.
|
|
411
|
+
</MessageState>
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (content.text === null) {
|
|
416
|
+
return <MessageState>This file can’t be displayed as text.</MessageState>;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return (
|
|
420
|
+
<ArtifactContentRenderer
|
|
421
|
+
content={content.text}
|
|
422
|
+
fileName={basename}
|
|
423
|
+
isTruncated={content.truncated}
|
|
424
|
+
reveal={reveal}
|
|
425
|
+
/>
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// ---------------------------------------------------------------------------
|
|
430
|
+
// Header
|
|
431
|
+
// ---------------------------------------------------------------------------
|
|
432
|
+
|
|
433
|
+
function ViewerHeader({
|
|
434
|
+
basename,
|
|
435
|
+
dir,
|
|
436
|
+
onRefresh,
|
|
437
|
+
onClose,
|
|
438
|
+
}: {
|
|
439
|
+
readonly basename: string;
|
|
440
|
+
readonly dir: string;
|
|
441
|
+
readonly onRefresh?: () => void;
|
|
442
|
+
readonly onClose?: () => void;
|
|
443
|
+
}) {
|
|
444
|
+
return (
|
|
445
|
+
<div className="flex items-center gap-2 border-b border-border px-3 py-2">
|
|
446
|
+
<FileIcon />
|
|
447
|
+
<div className="flex min-w-0 flex-1 items-baseline gap-1.5">
|
|
448
|
+
<span className="truncate text-xs font-semibold text-foreground" title={dir ? `${dir}/${basename}` : basename}>
|
|
449
|
+
{basename}
|
|
450
|
+
</span>
|
|
451
|
+
{dir && (
|
|
452
|
+
<span className="truncate text-[0.65rem] text-muted-foreground">{dir}</span>
|
|
453
|
+
)}
|
|
454
|
+
</div>
|
|
455
|
+
{onRefresh && (
|
|
456
|
+
<button
|
|
457
|
+
type="button"
|
|
458
|
+
onClick={onRefresh}
|
|
459
|
+
aria-label={`Reload ${basename}`}
|
|
460
|
+
className="shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
461
|
+
>
|
|
462
|
+
<RefreshIcon />
|
|
463
|
+
</button>
|
|
464
|
+
)}
|
|
465
|
+
{onClose && (
|
|
466
|
+
<button
|
|
467
|
+
type="button"
|
|
468
|
+
onClick={onClose}
|
|
469
|
+
aria-label="Close file viewer"
|
|
470
|
+
className="shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
471
|
+
>
|
|
472
|
+
<CloseIcon />
|
|
473
|
+
</button>
|
|
474
|
+
)}
|
|
475
|
+
</div>
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function MessageState({ children }: { readonly children: React.ReactNode }) {
|
|
480
|
+
return (
|
|
481
|
+
<div className="flex h-full items-center justify-center p-8 text-center text-xs text-muted-foreground">
|
|
482
|
+
{children}
|
|
483
|
+
</div>
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// ---------------------------------------------------------------------------
|
|
488
|
+
// Helpers
|
|
489
|
+
// ---------------------------------------------------------------------------
|
|
490
|
+
|
|
491
|
+
/** Human-readable byte size for the too-large / binary states. */
|
|
492
|
+
function formatBytes(n: number): string {
|
|
493
|
+
if (n < 1024) return `${n} B`;
|
|
494
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
495
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// ---------------------------------------------------------------------------
|
|
499
|
+
// Inline SVG icons (SDK independence — no lucide dependency)
|
|
500
|
+
// ---------------------------------------------------------------------------
|
|
501
|
+
|
|
502
|
+
function FileIcon() {
|
|
503
|
+
return (
|
|
504
|
+
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className="shrink-0 text-muted-foreground" aria-hidden="true">
|
|
505
|
+
<path d="M9.5 1.5H5C4.17 1.5 3.5 2.17 3.5 3V13C3.5 13.83 4.17 14.5 5 14.5H11C11.83 14.5 12.5 13.83 12.5 13V4.5L9.5 1.5Z" />
|
|
506
|
+
<path d="M9.5 1.5V4.5H12.5" />
|
|
507
|
+
</svg>
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function CloseIcon() {
|
|
512
|
+
return (
|
|
513
|
+
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
514
|
+
<path d="M3 3L11 11" />
|
|
515
|
+
<path d="M11 3L3 11" />
|
|
516
|
+
</svg>
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function RefreshIcon() {
|
|
521
|
+
return (
|
|
522
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
523
|
+
<path d="M1.5 6a4.5 4.5 0 017.794-3.073M10.5 6a4.5 4.5 0 01-7.794 3.073" />
|
|
524
|
+
<path d="M9.5 1v2.5H7M2.5 11V8.5H5" />
|
|
525
|
+
</svg>
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function ErrorAlertIcon() {
|
|
530
|
+
return (
|
|
531
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className="text-destructive" aria-hidden="true">
|
|
532
|
+
<circle cx="8" cy="8" r="6.5" />
|
|
533
|
+
<path d="M8 5.5V8.5" />
|
|
534
|
+
<circle cx="8" cy="11" r="0.5" fill="currentColor" stroke="none" />
|
|
535
|
+
</svg>
|
|
536
|
+
);
|
|
537
|
+
}
|