@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
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Launcher panel behavior: the session panel chip is PERSISTENT chrome (always
|
|
6
|
+
// mounted, not gated on attached context), and opening the panel homes on the
|
|
7
|
+
// Config facet — the pre-session view that carries the run defaults, since the
|
|
8
|
+
// Explorer has no workspace yet.
|
|
9
|
+
//
|
|
10
|
+
// The composer is replaced with a prop-capturing probe so these tests exercise
|
|
11
|
+
// only the panel chrome (chip + WorkspaceSurface rail) without pulling in the
|
|
12
|
+
// full composer's provider requirements. The Config facet (SetupTab) renders
|
|
13
|
+
// for real so "homes on Config" is asserted through the live rail.
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
vi.mock("../../composer", async (importOriginal) => {
|
|
17
|
+
const actual = await importOriginal<typeof import("../../composer")>();
|
|
18
|
+
return {
|
|
19
|
+
...actual,
|
|
20
|
+
SessionComposer: () => <div data-testid="composer-probe" />,
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// A launcher flow with ZERO attached context — no agent, no workspace, no
|
|
25
|
+
// MCP/skills/vars. This is the case the old `hasContext` gate hid the chip for.
|
|
26
|
+
const emptyWorkspace = {
|
|
27
|
+
entries: [],
|
|
28
|
+
hasEntries: false,
|
|
29
|
+
toInput: vi.fn().mockReturnValue([]),
|
|
30
|
+
addGitRepo: vi.fn(),
|
|
31
|
+
addLocalPath: vi.fn(),
|
|
32
|
+
remove: vi.fn(),
|
|
33
|
+
clear: vi.fn(),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const stubEmptyFlow = {
|
|
37
|
+
harness: "native" as const,
|
|
38
|
+
setHarness: vi.fn(),
|
|
39
|
+
modelId: undefined,
|
|
40
|
+
setModelId: vi.fn(),
|
|
41
|
+
agentRef: null,
|
|
42
|
+
setAgentRef: vi.fn(),
|
|
43
|
+
resolution: null,
|
|
44
|
+
setResolution: vi.fn(),
|
|
45
|
+
mcpServerUsages: [],
|
|
46
|
+
setMcpServerUsages: vi.fn(),
|
|
47
|
+
skillRefs: [],
|
|
48
|
+
setSkillRefs: vi.fn(),
|
|
49
|
+
workspace: emptyWorkspace,
|
|
50
|
+
sessionVariables: { entries: [], isEmpty: true, clear: vi.fn() },
|
|
51
|
+
isSubmitting: false,
|
|
52
|
+
submitError: null,
|
|
53
|
+
submit: vi.fn(),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// A launcher flow WITH attached context (a git workspace entry). Used to prove
|
|
57
|
+
// the composer stays centered regardless of context — the invariant that
|
|
58
|
+
// replaced the old `hasContext` position flip (DD-16 layout stability).
|
|
59
|
+
const stubPopulatedFlow = {
|
|
60
|
+
...stubEmptyFlow,
|
|
61
|
+
workspace: {
|
|
62
|
+
...emptyWorkspace,
|
|
63
|
+
hasEntries: true,
|
|
64
|
+
entries: [
|
|
65
|
+
{
|
|
66
|
+
id: "e1",
|
|
67
|
+
name: "acme/app",
|
|
68
|
+
type: "git" as const,
|
|
69
|
+
gitUrl: "https://github.com/acme/app.git",
|
|
70
|
+
gitBranch: "main",
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Swappable flow so individual tests can substitute the context-attached
|
|
77
|
+
// variant. The mock factory reads `mockFlow` at call time (the `mock` prefix
|
|
78
|
+
// is why Vitest permits referencing it inside the hoisted factory); tests
|
|
79
|
+
// reassign it before render and `beforeEach` resets it to the empty flow.
|
|
80
|
+
let mockFlow: typeof stubEmptyFlow | typeof stubPopulatedFlow = stubEmptyFlow;
|
|
81
|
+
|
|
82
|
+
vi.mock("../useNewSessionFlow", () => ({
|
|
83
|
+
useNewSessionFlow: () => mockFlow,
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
import { NewSessionViewer } from "../NewSessionViewer";
|
|
87
|
+
|
|
88
|
+
beforeEach(() => {
|
|
89
|
+
cleanup();
|
|
90
|
+
mockFlow = stubEmptyFlow;
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
afterEach(() => {
|
|
94
|
+
cleanup();
|
|
95
|
+
vi.clearAllMocks();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe("NewSessionViewer — persistent panel chip", () => {
|
|
99
|
+
it("renders the chip with zero attached context (not gated on hasContext)", () => {
|
|
100
|
+
render(<NewSessionViewer org="acme" onSessionCreated={vi.fn()} />);
|
|
101
|
+
expect(screen.getByRole("button", { name: "Show panel" })).toBeTruthy();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("homes on the Config facet when the panel opens", () => {
|
|
105
|
+
render(<NewSessionViewer org="acme" onSessionCreated={vi.fn()} />);
|
|
106
|
+
|
|
107
|
+
fireEvent.click(screen.getByRole("button", { name: "Show panel" }));
|
|
108
|
+
|
|
109
|
+
// The Config rail view is the active (checked) one; Explorer is not — the
|
|
110
|
+
// launcher's `defaultView: "configure"` home view.
|
|
111
|
+
const config = screen.getByRole("radio", { name: "Config" });
|
|
112
|
+
const explorer = screen.getByRole("radio", { name: "Explorer" });
|
|
113
|
+
expect(config.getAttribute("aria-checked")).toBe("true");
|
|
114
|
+
expect(explorer.getAttribute("aria-checked")).toBe("false");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("keeps the chip mounted as the collapse control once the panel is open", () => {
|
|
118
|
+
render(<NewSessionViewer org="acme" onSessionCreated={vi.fn()} />);
|
|
119
|
+
|
|
120
|
+
fireEvent.click(screen.getByRole("button", { name: "Show panel" }));
|
|
121
|
+
// Chip is still present with zero context — now as the hide affordance, so
|
|
122
|
+
// an open panel always has its toggle (the vanishing-toggle defect the old
|
|
123
|
+
// context gate could produce is impossible by construction).
|
|
124
|
+
expect(screen.getByRole("button", { name: "Hide panel" })).toBeTruthy();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe("NewSessionViewer — composer stays centered", () => {
|
|
129
|
+
// The composer's wrapper is the probe's parent (wrapper > h1 + composer +
|
|
130
|
+
// footer). `my-auto` is unconditional safe-centering; the old `my-6`
|
|
131
|
+
// top-anchor that appeared once context was attached is gone (DD-16).
|
|
132
|
+
const composerWrapper = () => screen.getByTestId("composer-probe").parentElement;
|
|
133
|
+
|
|
134
|
+
it("centers the composer with zero attached context", () => {
|
|
135
|
+
render(<NewSessionViewer org="acme" onSessionCreated={vi.fn()} />);
|
|
136
|
+
expect(composerWrapper()?.className).toContain("my-auto");
|
|
137
|
+
expect(composerWrapper()?.className).not.toContain("my-6");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("keeps the composer centered when context is attached (no position flip)", () => {
|
|
141
|
+
mockFlow = stubPopulatedFlow;
|
|
142
|
+
render(<NewSessionViewer org="acme" onSessionCreated={vi.fn()} />);
|
|
143
|
+
// Regression guard: attaching a workspace previously top-anchored the
|
|
144
|
+
// composer. Centering must now hold irrespective of attached context.
|
|
145
|
+
expect(composerWrapper()?.className).toContain("my-auto");
|
|
146
|
+
expect(composerWrapper()?.className).not.toContain("my-6");
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, fireEvent, cleanup, waitFor } from "@testing-library/react";
|
|
3
|
+
import { create } from "@bufbuild/protobuf";
|
|
4
|
+
import type { Stigmer } from "@stigmer/sdk";
|
|
5
|
+
import { ExecutionArtifactSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
|
|
6
|
+
import { ExecutionArtifactKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
7
|
+
import { StigmerContext } from "../../context";
|
|
8
|
+
import type { SessionPlan } from "../../library/detect-plan-artifact";
|
|
9
|
+
import type { PlanDraftController } from "../usePlanDraft";
|
|
10
|
+
import { PlanEditor } from "../PlanEditor";
|
|
11
|
+
|
|
12
|
+
afterEach(cleanup);
|
|
13
|
+
|
|
14
|
+
const PLAN_TEXT = "# Rollout Plan\n\n## Phase 1\n\nDo the first thing.";
|
|
15
|
+
|
|
16
|
+
const plan: SessionPlan = {
|
|
17
|
+
executionId: "aex_1",
|
|
18
|
+
artifact: create(ExecutionArtifactSchema, {
|
|
19
|
+
name: "plan.md",
|
|
20
|
+
kind: ExecutionArtifactKind.FILE,
|
|
21
|
+
sizeBytes: 128n,
|
|
22
|
+
storageKey: "artifacts/aex_1/plan.md",
|
|
23
|
+
contentHash: "hash-a",
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function stigmerMock(opts?: { text?: string; truncated?: boolean }): Stigmer {
|
|
28
|
+
return {
|
|
29
|
+
agentExecution: {
|
|
30
|
+
getArtifactContent: vi.fn().mockResolvedValue({
|
|
31
|
+
content: new TextEncoder().encode(opts?.text ?? PLAN_TEXT),
|
|
32
|
+
contentType: "text/markdown",
|
|
33
|
+
truncated: opts?.truncated ?? false,
|
|
34
|
+
}),
|
|
35
|
+
},
|
|
36
|
+
} as unknown as Stigmer;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function draftMock(overrides?: Partial<PlanDraftController>): PlanDraftController {
|
|
40
|
+
return {
|
|
41
|
+
draftText: null,
|
|
42
|
+
isEdited: false,
|
|
43
|
+
setDraft: vi.fn(),
|
|
44
|
+
readDraft: () => null,
|
|
45
|
+
...overrides,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function renderEditor(opts?: {
|
|
50
|
+
stigmer?: Stigmer;
|
|
51
|
+
draft?: PlanDraftController;
|
|
52
|
+
onBuildFromPlan?: () => void;
|
|
53
|
+
buildDisabled?: boolean;
|
|
54
|
+
readOnly?: boolean;
|
|
55
|
+
}) {
|
|
56
|
+
return render(
|
|
57
|
+
<StigmerContext.Provider value={opts?.stigmer ?? stigmerMock()}>
|
|
58
|
+
<PlanEditor
|
|
59
|
+
plan={plan}
|
|
60
|
+
draft={opts?.draft ?? draftMock()}
|
|
61
|
+
onBuildFromPlan={opts?.onBuildFromPlan}
|
|
62
|
+
buildDisabled={opts?.buildDisabled}
|
|
63
|
+
readOnly={opts?.readOnly}
|
|
64
|
+
/>
|
|
65
|
+
</StigmerContext.Provider>,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
describe("PlanEditor", () => {
|
|
70
|
+
it("renders the plan as a document with the H1 lifted into the header", async () => {
|
|
71
|
+
renderEditor();
|
|
72
|
+
|
|
73
|
+
await waitFor(() =>
|
|
74
|
+
expect(screen.getByText("Rollout Plan")).toBeTruthy(),
|
|
75
|
+
);
|
|
76
|
+
expect(screen.getByText("Do the first thing.")).toBeTruthy();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("fires onBuildFromPlan from the primary action", async () => {
|
|
80
|
+
const onBuildFromPlan = vi.fn();
|
|
81
|
+
renderEditor({ onBuildFromPlan });
|
|
82
|
+
|
|
83
|
+
await waitFor(() =>
|
|
84
|
+
expect(screen.getByText("Build from plan")).toBeTruthy(),
|
|
85
|
+
);
|
|
86
|
+
fireEvent.click(screen.getByText("Build from plan"));
|
|
87
|
+
expect(onBuildFromPlan).toHaveBeenCalledTimes(1);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("degrades gracefully at narrow widths: truncating title and shrinkable toolbar", async () => {
|
|
91
|
+
// The document must reflow to whatever width the panel gives it — the
|
|
92
|
+
// toolbar wraps and its labels truncate rather than forcing a sideways
|
|
93
|
+
// scrollbar on the editor pane (the overflow-x-hidden body above clips,
|
|
94
|
+
// so anything that cannot shrink would be cut off, not scrollable).
|
|
95
|
+
renderEditor({ onBuildFromPlan: vi.fn() });
|
|
96
|
+
|
|
97
|
+
await waitFor(() =>
|
|
98
|
+
expect(screen.getByText("Rollout Plan")).toBeTruthy(),
|
|
99
|
+
);
|
|
100
|
+
expect(screen.getByText("Rollout Plan").className).toContain("truncate");
|
|
101
|
+
|
|
102
|
+
const buildLabel = screen.getByText("Build from plan");
|
|
103
|
+
expect(buildLabel.className).toContain("truncate");
|
|
104
|
+
const toolbar = screen.getByRole("tablist", { name: "Plan view" })
|
|
105
|
+
.parentElement!;
|
|
106
|
+
expect(toolbar.className).toContain("flex-wrap");
|
|
107
|
+
expect(toolbar.className).toContain("min-w-0");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("shows a pending label and disables the primary while the build starts", async () => {
|
|
111
|
+
renderEditor({ onBuildFromPlan: vi.fn(), buildDisabled: true });
|
|
112
|
+
|
|
113
|
+
await waitFor(() =>
|
|
114
|
+
expect(screen.getByText("Starting build…")).toBeTruthy(),
|
|
115
|
+
);
|
|
116
|
+
expect(
|
|
117
|
+
(screen.getByText("Starting build…").closest("button") as HTMLButtonElement)
|
|
118
|
+
.disabled,
|
|
119
|
+
).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("edits into the viewer-owned draft and shows the Edited indicator", async () => {
|
|
123
|
+
const setDraft = vi.fn();
|
|
124
|
+
const stigmer = stigmerMock();
|
|
125
|
+
const { rerender } = render(
|
|
126
|
+
<StigmerContext.Provider value={stigmer}>
|
|
127
|
+
<PlanEditor plan={plan} draft={draftMock({ setDraft })} />
|
|
128
|
+
</StigmerContext.Provider>,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
await waitFor(() => expect(screen.getByText("Edit")).toBeTruthy());
|
|
132
|
+
fireEvent.click(screen.getByText("Edit"));
|
|
133
|
+
|
|
134
|
+
const editor = screen.getByRole("textbox", { name: "Edit plan" });
|
|
135
|
+
fireEvent.change(editor, { target: { value: "# Edited" } });
|
|
136
|
+
expect(setDraft).toHaveBeenCalledWith("# Edited");
|
|
137
|
+
|
|
138
|
+
// Draft active → indicator + revert, and the editor shows the draft.
|
|
139
|
+
// Same client instance, so the content fetch is not re-triggered.
|
|
140
|
+
rerender(
|
|
141
|
+
<StigmerContext.Provider value={stigmer}>
|
|
142
|
+
<PlanEditor
|
|
143
|
+
plan={plan}
|
|
144
|
+
draft={draftMock({ draftText: "# Edited", isEdited: true, setDraft })}
|
|
145
|
+
/>
|
|
146
|
+
</StigmerContext.Provider>,
|
|
147
|
+
);
|
|
148
|
+
await waitFor(() => expect(screen.getByText("Edited")).toBeTruthy());
|
|
149
|
+
|
|
150
|
+
fireEvent.click(screen.getByText("Revert"));
|
|
151
|
+
expect(setDraft).toHaveBeenCalledWith(null);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("disables editing for truncated content", async () => {
|
|
155
|
+
renderEditor({ stigmer: stigmerMock({ truncated: true }) });
|
|
156
|
+
|
|
157
|
+
await waitFor(() => expect(screen.getByText("Edit")).toBeTruthy());
|
|
158
|
+
expect((screen.getByText("Edit") as HTMLButtonElement).disabled).toBe(true);
|
|
159
|
+
expect(screen.getByText(/Preview truncated/)).toBeTruthy();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("renders an error state with retry when the content fetch fails", async () => {
|
|
163
|
+
const failing = {
|
|
164
|
+
agentExecution: {
|
|
165
|
+
getArtifactContent: vi.fn().mockRejectedValue(new Error("boom")),
|
|
166
|
+
},
|
|
167
|
+
} as unknown as Stigmer;
|
|
168
|
+
|
|
169
|
+
renderEditor({ stigmer: failing });
|
|
170
|
+
|
|
171
|
+
await waitFor(() =>
|
|
172
|
+
expect(screen.getByText(/Couldn’t load the plan/)).toBeTruthy(),
|
|
173
|
+
);
|
|
174
|
+
expect(screen.getByText("Retry")).toBeTruthy();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe("PlanEditor — read-only (superseded plan)", () => {
|
|
179
|
+
it("renders the document with a superseded notice, no Edit and no Build", async () => {
|
|
180
|
+
renderEditor({
|
|
181
|
+
readOnly: true,
|
|
182
|
+
draft: undefined,
|
|
183
|
+
onBuildFromPlan: vi.fn(),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
await waitFor(() =>
|
|
187
|
+
expect(screen.getByText("Rollout Plan")).toBeTruthy(),
|
|
188
|
+
);
|
|
189
|
+
expect(screen.getByText("Superseded by a newer plan")).toBeTruthy();
|
|
190
|
+
expect(screen.queryByText("Edit")).toBeNull();
|
|
191
|
+
expect(screen.queryByText("Build from plan")).toBeNull();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("never applies the current plan's draft to a superseded plan", async () => {
|
|
195
|
+
// Defense in depth for the wiring contract (the host passes no draft for
|
|
196
|
+
// a read-only plan): even if a draft controller leaks through, read-only
|
|
197
|
+
// rendering shows the artifact text.
|
|
198
|
+
renderEditor({
|
|
199
|
+
readOnly: true,
|
|
200
|
+
draft: draftMock({ draftText: "# Someone else's draft", isEdited: true }),
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
await waitFor(() =>
|
|
204
|
+
expect(screen.getByText("Rollout Plan")).toBeTruthy(),
|
|
205
|
+
);
|
|
206
|
+
expect(screen.queryByText("Someone else's draft")).toBeNull();
|
|
207
|
+
expect(screen.queryByText("Edited")).toBeNull();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("keeps Rendered and Source views available", async () => {
|
|
211
|
+
renderEditor({ readOnly: true, draft: undefined });
|
|
212
|
+
|
|
213
|
+
await waitFor(() => expect(screen.getByText("Source")).toBeTruthy());
|
|
214
|
+
fireEvent.click(screen.getByText("Source"));
|
|
215
|
+
expect(screen.getByText(/# Rollout Plan/)).toBeTruthy();
|
|
216
|
+
});
|
|
217
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup } from "@testing-library/react";
|
|
3
|
+
import { PlanStreamingDocument } from "../PlanStreamingDocument";
|
|
4
|
+
|
|
5
|
+
afterEach(cleanup);
|
|
6
|
+
|
|
7
|
+
describe("PlanStreamingDocument — live plan tab view", () => {
|
|
8
|
+
it("is a busy article with a Writing status pill", () => {
|
|
9
|
+
render(
|
|
10
|
+
<PlanStreamingDocument displayText={"# Live Plan\n\nFirst section"} />,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const doc = screen.getByRole("article", { name: "Plan document" });
|
|
14
|
+
expect(doc.getAttribute("aria-busy")).toBe("true");
|
|
15
|
+
expect(screen.getByRole("status").textContent).toContain("Writing…");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("lifts the leading H1 into the header and renders the body as markdown", () => {
|
|
19
|
+
render(
|
|
20
|
+
<PlanStreamingDocument
|
|
21
|
+
displayText={"# Live Plan\n\n## Phase 1\n\n- step one"}
|
|
22
|
+
/>,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const doc = screen.getByRole("article", { name: "Plan document" });
|
|
26
|
+
expect(doc.querySelector("header")!.textContent).toContain("Live Plan");
|
|
27
|
+
// The lifted title does not repeat inside the prose body.
|
|
28
|
+
expect(doc.querySelectorAll("h1")).toHaveLength(0);
|
|
29
|
+
expect(doc.textContent).toContain("step one");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("renders headerless while the H1 line is still streaming in", () => {
|
|
33
|
+
// extractLeadingH1 requires the heading to terminate (newline or end);
|
|
34
|
+
// a lone "#" has no title yet — the document simply shows the raw text
|
|
35
|
+
// until the title resolves. No fabricated placeholder header.
|
|
36
|
+
render(<PlanStreamingDocument displayText={"# Re"} />);
|
|
37
|
+
|
|
38
|
+
const doc = screen.getByRole("article", { name: "Plan document" });
|
|
39
|
+
// "# Re" parses as a (partial) title, lifted into the header.
|
|
40
|
+
expect(doc.querySelector("header")!.textContent).toContain("Re");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("offers no Edit or Build controls — a plan in flight cannot be acted on", () => {
|
|
44
|
+
render(<PlanStreamingDocument displayText={"# Live Plan\n\nBody"} />);
|
|
45
|
+
|
|
46
|
+
expect(screen.queryByText("Edit")).toBeNull();
|
|
47
|
+
expect(screen.queryByText("Build from plan")).toBeNull();
|
|
48
|
+
expect(screen.queryByRole("tablist")).toBeNull();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { SessionPanelChip } from "../SessionPanelChip";
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// The chip is the panel's always-mounted toggle. Collapsed, it carries the
|
|
7
|
+
// pending-item count (arrivals never auto-open the panel); open, it is a bare
|
|
8
|
+
// hide affordance. Execution status deliberately never appears here (or in
|
|
9
|
+
// any other viewer chrome) — the thread itself communicates run state.
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
afterEach(cleanup);
|
|
13
|
+
|
|
14
|
+
describe("SessionPanelChip", () => {
|
|
15
|
+
it("collapsed: offers Show panel and carries the pending count", () => {
|
|
16
|
+
render(
|
|
17
|
+
<SessionPanelChip isOpen={false} onToggle={vi.fn()} badgeCount={3} />,
|
|
18
|
+
);
|
|
19
|
+
const button = screen.getByRole("button", { name: "Show panel" });
|
|
20
|
+
expect(button.getAttribute("aria-expanded")).toBe("false");
|
|
21
|
+
expect(button.textContent).toContain("3");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("open: offers Hide panel without the count (the panel carries it)", () => {
|
|
25
|
+
render(<SessionPanelChip isOpen onToggle={vi.fn()} badgeCount={3} />);
|
|
26
|
+
const button = screen.getByRole("button", { name: "Hide panel" });
|
|
27
|
+
expect(button.getAttribute("aria-expanded")).toBe("true");
|
|
28
|
+
expect(button.textContent).not.toContain("3");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("renders as a bare toggle when nothing is pending", () => {
|
|
32
|
+
render(<SessionPanelChip isOpen={false} onToggle={vi.fn()} />);
|
|
33
|
+
expect(screen.getByRole("button", { name: "Show panel" }).textContent).toBe("");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("invokes onToggle on click", () => {
|
|
37
|
+
const onToggle = vi.fn();
|
|
38
|
+
render(<SessionPanelChip isOpen={false} onToggle={onToggle} />);
|
|
39
|
+
fireEvent.click(screen.getByRole("button", { name: "Show panel" }));
|
|
40
|
+
expect(onToggle).toHaveBeenCalledTimes(1);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -39,10 +39,6 @@ vi.mock("../../composer", async (importOriginal) => {
|
|
|
39
39
|
};
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
vi.mock("../inspector/SessionInspector", () => ({
|
|
43
|
-
SessionInspector: () => <div data-testid="inspector-probe" />,
|
|
44
|
-
}));
|
|
45
|
-
|
|
46
42
|
const stubWorkspace = {
|
|
47
43
|
entries: [],
|
|
48
44
|
hasEntries: false,
|
|
@@ -134,6 +130,15 @@ vi.mock("../useSessionPageFlow", () => ({
|
|
|
134
130
|
useSessionPageFlow: () => stubSessionPageFlow,
|
|
135
131
|
}));
|
|
136
132
|
|
|
133
|
+
vi.mock("../../hooks", () => ({
|
|
134
|
+
useStigmer: () => ({
|
|
135
|
+
agentExecution: {
|
|
136
|
+
uploadAttachment: vi.fn(),
|
|
137
|
+
getArtifactContent: vi.fn(),
|
|
138
|
+
},
|
|
139
|
+
}),
|
|
140
|
+
}));
|
|
141
|
+
|
|
137
142
|
import { SessionViewer } from "../SessionViewer";
|
|
138
143
|
|
|
139
144
|
beforeEach(() => {
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, act, fireEvent } from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Wiring-contract test for transcript click-to-open: the onFilePathClick
|
|
6
|
+
// SessionViewer hands to MessageThread resolves a tool-call path, writes the
|
|
7
|
+
// open-editor store, AND expands the session panel — so the resolved file
|
|
8
|
+
// surfaces in the WorkspaceSurface. MessageThread and WorkspaceSurface render
|
|
9
|
+
// as prop-capturing probes; the store + SessionPanelRegion wire between them
|
|
10
|
+
// is the subject under test.
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
type CapturedProps = Record<string, unknown>;
|
|
14
|
+
|
|
15
|
+
const threadProps: CapturedProps[] = [];
|
|
16
|
+
vi.mock("../../execution/MessageThread", () => ({
|
|
17
|
+
MessageThread: (props: CapturedProps) => {
|
|
18
|
+
threadProps.push(props);
|
|
19
|
+
return <div data-testid="thread-probe" />;
|
|
20
|
+
},
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
const surfaceProps: CapturedProps[] = [];
|
|
24
|
+
vi.mock("../../workspace/WorkspaceSurface", () => ({
|
|
25
|
+
WorkspaceSurface: (props: CapturedProps) => {
|
|
26
|
+
surfaceProps.push(props);
|
|
27
|
+
return <div data-testid="surface-probe" />;
|
|
28
|
+
},
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
vi.mock("../../composer", async (importOriginal) => {
|
|
32
|
+
const actual = await importOriginal<typeof import("../../composer")>();
|
|
33
|
+
return {
|
|
34
|
+
...actual,
|
|
35
|
+
SessionComposer: () => <div data-testid="composer-probe" />,
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const gitEntry = {
|
|
40
|
+
id: "e1",
|
|
41
|
+
name: "acme/app",
|
|
42
|
+
type: "git" as const,
|
|
43
|
+
gitUrl: "https://github.com/acme/app.git",
|
|
44
|
+
gitBranch: "main",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const stubWorkspace = {
|
|
48
|
+
entries: [gitEntry],
|
|
49
|
+
hasEntries: true,
|
|
50
|
+
toInput: vi.fn().mockReturnValue([]),
|
|
51
|
+
addGitRepo: vi.fn(),
|
|
52
|
+
addLocalPath: vi.fn(),
|
|
53
|
+
remove: vi.fn(),
|
|
54
|
+
clear: vi.fn(),
|
|
55
|
+
clearLocal: vi.fn(),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const stubConv = {
|
|
59
|
+
session: { spec: {} },
|
|
60
|
+
isLoading: false,
|
|
61
|
+
loadError: null,
|
|
62
|
+
completedExecutions: [],
|
|
63
|
+
activeStreamExecution: null,
|
|
64
|
+
pendingUserMessage: null,
|
|
65
|
+
workspaceEntries: [],
|
|
66
|
+
fileChangeSets: [],
|
|
67
|
+
submitFileDecision: vi.fn(),
|
|
68
|
+
submittingFileDecisionKeys: new Set<string>(),
|
|
69
|
+
fileDecisionErrors: new Map<string, Error>(),
|
|
70
|
+
sendError: null,
|
|
71
|
+
stopError: null,
|
|
72
|
+
streamError: null,
|
|
73
|
+
reconnectStream: vi.fn(),
|
|
74
|
+
submittingApprovalIds: new Set<string>(),
|
|
75
|
+
approvalErrors: new Map<string, Error>(),
|
|
76
|
+
isStoppable: false,
|
|
77
|
+
isStopping: false,
|
|
78
|
+
isSending: false,
|
|
79
|
+
canSendFollowUp: true,
|
|
80
|
+
isReconnecting: false,
|
|
81
|
+
connectTimedOut: false,
|
|
82
|
+
isSlow: false,
|
|
83
|
+
stop: vi.fn(),
|
|
84
|
+
retryLastSend: vi.fn(),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const stubSessionPageFlow = {
|
|
88
|
+
conv: stubConv,
|
|
89
|
+
harness: "native" as const,
|
|
90
|
+
executionTarget: undefined,
|
|
91
|
+
model: [undefined, vi.fn()] as const,
|
|
92
|
+
interactionMode: ["agent" as const, vi.fn()] as const,
|
|
93
|
+
agentRef: { org: "acme", slug: "support-bot" },
|
|
94
|
+
setAgentRef: vi.fn(),
|
|
95
|
+
resolution: null,
|
|
96
|
+
setResolution: vi.fn(),
|
|
97
|
+
isDefaultAgent: false,
|
|
98
|
+
mcpServerUsages: [],
|
|
99
|
+
setMcpServerUsages: vi.fn(),
|
|
100
|
+
skillRefs: [],
|
|
101
|
+
setSkillRefs: vi.fn(),
|
|
102
|
+
workspace: stubWorkspace,
|
|
103
|
+
sessionVariables: { entries: [], isEmpty: true, clear: vi.fn() },
|
|
104
|
+
autoApproveAll: false,
|
|
105
|
+
setAutoApproveAll: vi.fn(),
|
|
106
|
+
handleSubmit: vi.fn(),
|
|
107
|
+
submitError: null as Error | null,
|
|
108
|
+
displayExecution: null,
|
|
109
|
+
allExecutions: [],
|
|
110
|
+
sandboxWorkspaceRoot: "/home/daytona/workspace",
|
|
111
|
+
};
|
|
112
|
+
vi.mock("../useSessionPageFlow", () => ({
|
|
113
|
+
useSessionPageFlow: () => stubSessionPageFlow,
|
|
114
|
+
}));
|
|
115
|
+
|
|
116
|
+
vi.mock("../../hooks", () => ({
|
|
117
|
+
useStigmer: () => ({
|
|
118
|
+
agentExecution: {
|
|
119
|
+
uploadAttachment: vi.fn(),
|
|
120
|
+
getArtifactContent: vi.fn(),
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
}));
|
|
124
|
+
|
|
125
|
+
import { SessionViewer } from "../SessionViewer";
|
|
126
|
+
|
|
127
|
+
/** The onFilePathClick handler threaded down to the (mocked) MessageThread. */
|
|
128
|
+
function capturedOnFilePathClick(): (path: string) => boolean {
|
|
129
|
+
const props = threadProps.at(-1);
|
|
130
|
+
return props?.onFilePathClick as (path: string) => boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** The latest selectedFile the (mocked) WorkspaceSurface received, if rendered. */
|
|
134
|
+
function latestSurfaceSelectedFile(): unknown {
|
|
135
|
+
return surfaceProps.at(-1)?.selectedFile ?? null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
beforeEach(() => {
|
|
139
|
+
threadProps.length = 0;
|
|
140
|
+
surfaceProps.length = 0;
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
afterEach(() => {
|
|
144
|
+
cleanup();
|
|
145
|
+
vi.clearAllMocks();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe("SessionViewer — transcript click-to-open wiring", () => {
|
|
149
|
+
it("threads an onFilePathClick to the message thread", () => {
|
|
150
|
+
render(<SessionViewer sessionId="ses_1" org="acme" />);
|
|
151
|
+
expect(typeof capturedOnFilePathClick()).toBe("function");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("opens a resolvable tool-call path in the session panel", () => {
|
|
155
|
+
render(<SessionViewer sessionId="ses_1" org="acme" />);
|
|
156
|
+
// Before any click: panel collapsed — the surface is not rendered.
|
|
157
|
+
expect(surfaceProps.length).toBe(0);
|
|
158
|
+
|
|
159
|
+
let handled: boolean | undefined;
|
|
160
|
+
act(() => {
|
|
161
|
+
handled = capturedOnFilePathClick()("/home/daytona/workspace/src/main.go");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
expect(handled).toBe(true);
|
|
165
|
+
// Opening expands the panel and routes the resolved selection into the
|
|
166
|
+
// surface — the SAME selection a tree click yields.
|
|
167
|
+
expect(latestSurfaceSelectedFile()).toEqual({
|
|
168
|
+
entryId: "e1",
|
|
169
|
+
path: "src/main.go",
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("declines a platform path (returns false) and opens nothing", () => {
|
|
174
|
+
render(<SessionViewer sessionId="ses_1" org="acme" />);
|
|
175
|
+
|
|
176
|
+
let handled: boolean | undefined;
|
|
177
|
+
act(() => {
|
|
178
|
+
handled = capturedOnFilePathClick()(".stigmer/skills/x.md");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
expect(handled).toBe(false);
|
|
182
|
+
// The panel stays collapsed: no surface rendered.
|
|
183
|
+
expect(surfaceProps.length).toBe(0);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("collapses back to full-width chat via the chip without losing the editor group", () => {
|
|
187
|
+
render(<SessionViewer sessionId="ses_1" org="acme" />);
|
|
188
|
+
act(() => {
|
|
189
|
+
capturedOnFilePathClick()("/home/daytona/workspace/src/main.go");
|
|
190
|
+
});
|
|
191
|
+
expect(latestSurfaceSelectedFile()).toEqual({ entryId: "e1", path: "src/main.go" });
|
|
192
|
+
|
|
193
|
+
// Chip is the hide affordance while open …
|
|
194
|
+
fireEvent.click(screen.getByRole("button", { name: "Hide panel" }));
|
|
195
|
+
const countBefore = surfaceProps.length;
|
|
196
|
+
|
|
197
|
+
// … and the show affordance while collapsed; the editor group survives.
|
|
198
|
+
fireEvent.click(screen.getByRole("button", { name: "Show panel" }));
|
|
199
|
+
expect(surfaceProps.length).toBeGreaterThan(countBefore);
|
|
200
|
+
expect(latestSurfaceSelectedFile()).toEqual({
|
|
201
|
+
entryId: "e1",
|
|
202
|
+
path: "src/main.go",
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|