@stigmer/react 3.0.9-dev.20260616060535 → 3.1.0
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/execution/ApprovalCard.d.ts +47 -3
- package/execution/ApprovalCard.d.ts.map +1 -1
- package/execution/ApprovalCard.js +138 -70
- package/execution/ApprovalCard.js.map +1 -1
- package/execution/ApprovalContext.d.ts +72 -0
- package/execution/ApprovalContext.d.ts.map +1 -0
- package/execution/ApprovalContext.js +41 -0
- package/execution/ApprovalContext.js.map +1 -0
- package/execution/ArtifactCard.d.ts.map +1 -1
- package/execution/ArtifactCard.js +3 -1
- package/execution/ArtifactCard.js.map +1 -1
- package/execution/ArtifactPreviewModal.d.ts.map +1 -1
- package/execution/ArtifactPreviewModal.js +5 -3
- package/execution/ArtifactPreviewModal.js.map +1 -1
- package/execution/EmptyChangeNotice.d.ts +38 -0
- package/execution/EmptyChangeNotice.d.ts.map +1 -0
- package/execution/EmptyChangeNotice.js +27 -0
- package/execution/EmptyChangeNotice.js.map +1 -0
- package/execution/FileChangesView.d.ts +81 -0
- package/execution/FileChangesView.d.ts.map +1 -0
- package/execution/FileChangesView.js +164 -0
- package/execution/FileChangesView.js.map +1 -0
- package/execution/FilePathLink.d.ts +24 -4
- package/execution/FilePathLink.d.ts.map +1 -1
- package/execution/FilePathLink.js +31 -9
- package/execution/FilePathLink.js.map +1 -1
- package/execution/FileReviewCard.d.ts +111 -0
- package/execution/FileReviewCard.d.ts.map +1 -0
- package/execution/FileReviewCard.js +558 -0
- package/execution/FileReviewCard.js.map +1 -0
- package/execution/FileReviewContext.d.ts +35 -0
- package/execution/FileReviewContext.d.ts.map +1 -0
- package/execution/FileReviewContext.js +23 -0
- package/execution/FileReviewContext.js.map +1 -0
- package/execution/FileReviewDock.d.ts +73 -0
- package/execution/FileReviewDock.d.ts.map +1 -0
- package/execution/FileReviewDock.js +65 -0
- package/execution/FileReviewDock.js.map +1 -0
- package/execution/McpToolDetail.d.ts.map +1 -1
- package/execution/McpToolDetail.js +38 -5
- package/execution/McpToolDetail.js.map +1 -1
- package/execution/MessageThread.d.ts +42 -3
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +263 -18
- package/execution/MessageThread.js.map +1 -1
- package/execution/PlanArtifactCard.d.ts.map +1 -1
- package/execution/PlanArtifactCard.js +3 -1
- package/execution/PlanArtifactCard.js.map +1 -1
- package/execution/PlanCompletionCard.d.ts +1 -1
- package/execution/PlanCompletionCard.js +1 -1
- package/execution/ResultView.d.ts +19 -1
- package/execution/ResultView.d.ts.map +1 -1
- package/execution/ResultView.js +141 -40
- package/execution/ResultView.js.map +1 -1
- package/execution/SubAgentSection.d.ts.map +1 -1
- package/execution/SubAgentSection.js +45 -6
- package/execution/SubAgentSection.js.map +1 -1
- package/execution/TerminalSession.d.ts +34 -0
- package/execution/TerminalSession.d.ts.map +1 -0
- package/execution/TerminalSession.js +34 -0
- package/execution/TerminalSession.js.map +1 -0
- package/execution/TodoCard.d.ts +53 -0
- package/execution/TodoCard.d.ts.map +1 -0
- package/execution/TodoCard.js +77 -0
- package/execution/TodoCard.js.map +1 -0
- package/execution/ToolArgsView.d.ts +7 -1
- package/execution/ToolArgsView.d.ts.map +1 -1
- package/execution/ToolArgsView.js +10 -9
- package/execution/ToolArgsView.js.map +1 -1
- package/execution/ToolCallDetail.d.ts +10 -1
- package/execution/ToolCallDetail.d.ts.map +1 -1
- package/execution/ToolCallDetail.js +66 -19
- package/execution/ToolCallDetail.js.map +1 -1
- package/execution/ToolCallGroup.d.ts +28 -18
- package/execution/ToolCallGroup.d.ts.map +1 -1
- package/execution/ToolCallGroup.js +39 -148
- package/execution/ToolCallGroup.js.map +1 -1
- package/execution/ToolCallItem.d.ts +36 -8
- package/execution/ToolCallItem.d.ts.map +1 -1
- package/execution/ToolCallItem.js +140 -21
- package/execution/ToolCallItem.js.map +1 -1
- package/execution/ToolRunGroup.d.ts +48 -0
- package/execution/ToolRunGroup.d.ts.map +1 -0
- package/execution/ToolRunGroup.js +168 -0
- package/execution/ToolRunGroup.js.map +1 -0
- package/execution/approval-provenance.d.ts +2 -0
- package/execution/approval-provenance.d.ts.map +1 -0
- package/execution/approval-provenance.js +6 -0
- package/execution/approval-provenance.js.map +1 -0
- package/execution/artifact-utils.d.ts +0 -10
- package/execution/artifact-utils.d.ts.map +1 -1
- package/execution/artifact-utils.js +0 -17
- package/execution/artifact-utils.js.map +1 -1
- package/execution/execution-phases.d.ts +1 -6
- package/execution/execution-phases.d.ts.map +1 -1
- package/execution/execution-phases.js +4 -14
- package/execution/execution-phases.js.map +1 -1
- package/execution/file-path-resolver.d.ts +28 -0
- package/execution/file-path-resolver.d.ts.map +1 -1
- package/execution/file-path-resolver.js +24 -0
- package/execution/file-path-resolver.js.map +1 -1
- package/execution/file-review-status.d.ts +127 -0
- package/execution/file-review-status.d.ts.map +1 -0
- package/execution/file-review-status.js +135 -0
- package/execution/file-review-status.js.map +1 -0
- package/execution/index.d.ts +39 -5
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +23 -3
- package/execution/index.js.map +1 -1
- package/execution/segment-tool-calls.d.ts +46 -0
- package/execution/segment-tool-calls.d.ts.map +1 -0
- package/execution/segment-tool-calls.js +71 -0
- package/execution/segment-tool-calls.js.map +1 -0
- package/execution/tool-categories.d.ts +62 -1
- package/execution/tool-categories.d.ts.map +1 -1
- package/execution/tool-categories.js +96 -2
- package/execution/tool-categories.js.map +1 -1
- package/execution/tool-presenter.d.ts +38 -2
- package/execution/tool-presenter.d.ts.map +1 -1
- package/execution/tool-presenter.js +23 -1
- package/execution/tool-presenter.js.map +1 -1
- package/execution/tool-rendering-primitives.d.ts.map +1 -1
- package/execution/tool-rendering-primitives.js +3 -2
- package/execution/tool-rendering-primitives.js.map +1 -1
- package/execution/useArtifactDownload.d.ts +44 -0
- package/execution/useArtifactDownload.d.ts.map +1 -0
- package/execution/useArtifactDownload.js +80 -0
- package/execution/useArtifactDownload.js.map +1 -0
- package/execution/useArtifactDownloadUrl.d.ts +67 -0
- package/execution/useArtifactDownloadUrl.d.ts.map +1 -0
- package/execution/useArtifactDownloadUrl.js +59 -0
- package/execution/useArtifactDownloadUrl.js.map +1 -0
- package/execution/useFileChangeContent.d.ts +61 -0
- package/execution/useFileChangeContent.d.ts.map +1 -0
- package/execution/useFileChangeContent.js +89 -0
- package/execution/useFileChangeContent.js.map +1 -0
- package/execution/useFileReview.d.ts +89 -0
- package/execution/useFileReview.d.ts.map +1 -0
- package/execution/useFileReview.js +106 -0
- package/execution/useFileReview.js.map +1 -0
- package/execution/useSubmitApproval.d.ts +17 -2
- package/execution/useSubmitApproval.d.ts.map +1 -1
- package/execution/useSubmitApproval.js +27 -23
- package/execution/useSubmitApproval.js.map +1 -1
- package/execution/useToolOutputContent.d.ts +44 -0
- package/execution/useToolOutputContent.d.ts.map +1 -0
- package/execution/useToolOutputContent.js +31 -0
- package/execution/useToolOutputContent.js.map +1 -0
- package/index.d.ts +6 -6
- package/index.d.ts.map +1 -1
- package/index.js +3 -3
- package/index.js.map +1 -1
- package/internal/ApprovalPeekBar.d.ts +23 -0
- package/internal/ApprovalPeekBar.d.ts.map +1 -0
- package/internal/ApprovalPeekBar.js +25 -0
- package/internal/ApprovalPeekBar.js.map +1 -0
- package/internal/BoundedContent.d.ts +49 -0
- package/internal/BoundedContent.d.ts.map +1 -0
- package/internal/BoundedContent.js +50 -0
- package/internal/BoundedContent.js.map +1 -0
- package/internal/DecisionButton.d.ts +47 -0
- package/internal/DecisionButton.d.ts.map +1 -0
- package/internal/DecisionButton.js +42 -0
- package/internal/DecisionButton.js.map +1 -0
- package/internal/InCardDecisionError.d.ts +31 -0
- package/internal/InCardDecisionError.d.ts.map +1 -0
- package/internal/InCardDecisionError.js +23 -0
- package/internal/InCardDecisionError.js.map +1 -0
- package/internal/RevealToggle.d.ts +36 -0
- package/internal/RevealToggle.d.ts.map +1 -0
- package/internal/RevealToggle.js +32 -0
- package/internal/RevealToggle.js.map +1 -0
- package/internal/VirtualizedThread.d.ts +7 -1
- package/internal/VirtualizedThread.d.ts.map +1 -1
- package/internal/VirtualizedThread.js +7 -3
- package/internal/VirtualizedThread.js.map +1 -1
- package/internal/useAutoDisclosure.d.ts +34 -0
- package/internal/useAutoDisclosure.d.ts.map +1 -0
- package/internal/useAutoDisclosure.js +53 -0
- package/internal/useAutoDisclosure.js.map +1 -0
- package/internal/useElapsedSince.d.ts +30 -0
- package/internal/useElapsedSince.d.ts.map +1 -0
- package/internal/useElapsedSince.js +70 -0
- package/internal/useElapsedSince.js.map +1 -0
- package/internal/useIsOverflowing.d.ts +30 -0
- package/internal/useIsOverflowing.d.ts.map +1 -0
- package/internal/useIsOverflowing.js +47 -0
- package/internal/useIsOverflowing.js.map +1 -0
- package/internal/useIsTextTruncated.d.ts +24 -0
- package/internal/useIsTextTruncated.d.ts.map +1 -0
- package/internal/useIsTextTruncated.js +41 -0
- package/internal/useIsTextTruncated.js.map +1 -0
- package/internal/useKeyedSubmission.d.ts +49 -0
- package/internal/useKeyedSubmission.d.ts.map +1 -0
- package/internal/useKeyedSubmission.js +67 -0
- package/internal/useKeyedSubmission.js.map +1 -0
- package/package.json +4 -4
- package/session/SessionViewer.d.ts +2 -2
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +10 -4
- package/session/SessionViewer.js.map +1 -1
- package/session/index.d.ts +2 -0
- package/session/index.d.ts.map +1 -1
- package/session/index.js +1 -0
- package/session/index.js.map +1 -1
- package/session/inspector/ChangesTab.d.ts +8 -5
- package/session/inspector/ChangesTab.d.ts.map +1 -1
- package/session/inspector/ChangesTab.js +11 -8
- package/session/inspector/ChangesTab.js.map +1 -1
- package/session/inspector/SessionInspector.d.ts +4 -4
- package/session/inspector/SessionInspector.d.ts.map +1 -1
- package/session/inspector/SessionInspector.js +4 -4
- package/session/inspector/SessionInspector.js.map +1 -1
- package/session/inspector/index.d.ts +0 -2
- package/session/inspector/index.d.ts.map +1 -1
- package/session/inspector/index.js +0 -1
- package/session/inspector/index.js.map +1 -1
- package/session/inspector/useSessionInspector.d.ts +8 -4
- package/session/inspector/useSessionInspector.d.ts.map +1 -1
- package/session/inspector/useSessionInspector.js +27 -20
- package/session/inspector/useSessionInspector.js.map +1 -1
- package/session/useSessionConversation.d.ts +37 -3
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +18 -2
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionFileChanges.d.ts +74 -0
- package/session/useSessionFileChanges.d.ts.map +1 -0
- package/session/useSessionFileChanges.js +178 -0
- package/session/useSessionFileChanges.js.map +1 -0
- package/src/__tests__/styles-border-layer-invariant.test.ts +105 -0
- package/src/execution/ApprovalCard.tsx +368 -230
- package/src/execution/ApprovalContext.ts +109 -0
- package/src/execution/ArtifactCard.tsx +10 -6
- package/src/execution/ArtifactPreviewModal.tsx +12 -6
- package/src/execution/EmptyChangeNotice.tsx +62 -0
- package/src/execution/FileChangesView.tsx +383 -0
- package/src/execution/FilePathLink.tsx +65 -12
- package/src/execution/FileReviewCard.tsx +1118 -0
- package/src/execution/FileReviewContext.ts +53 -0
- package/src/execution/FileReviewDock.tsx +157 -0
- package/src/execution/McpToolDetail.tsx +98 -32
- package/src/execution/MessageThread.tsx +374 -9
- package/src/execution/PlanArtifactCard.tsx +9 -6
- package/src/execution/PlanCompletionCard.tsx +1 -1
- package/src/execution/ResultView.tsx +333 -162
- package/src/execution/SubAgentSection.tsx +58 -11
- package/src/execution/TerminalSession.tsx +91 -0
- package/src/execution/TodoCard.tsx +186 -0
- package/src/execution/ToolArgsView.tsx +28 -17
- package/src/execution/ToolCallDetail.tsx +137 -26
- package/src/execution/ToolCallGroup.tsx +68 -296
- package/src/execution/ToolCallItem.tsx +265 -67
- package/src/execution/ToolRunGroup.tsx +302 -0
- package/src/execution/__tests__/ApprovalCard.test.tsx +395 -5
- package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +0 -1
- package/src/execution/__tests__/EmptyChangeNotice.test.tsx +24 -0
- package/src/execution/__tests__/FileChangesView.test.tsx +296 -0
- package/src/execution/__tests__/FilePathLink.test.tsx +92 -0
- package/src/execution/__tests__/FileReviewCard.test.tsx +1044 -0
- package/src/execution/__tests__/FileReviewDock.test.tsx +134 -0
- package/src/execution/__tests__/McpToolDetail.test.tsx +79 -0
- package/src/execution/__tests__/MessageThread.test.tsx +324 -0
- package/src/execution/__tests__/PlanArtifactCard.test.tsx +37 -16
- package/src/execution/__tests__/ResultView.test.tsx +280 -2
- package/src/execution/__tests__/SubAgentSection.test.tsx +180 -0
- package/src/execution/__tests__/TerminalSession.test.tsx +72 -0
- package/src/execution/__tests__/TodoCard.test.tsx +102 -0
- package/src/execution/__tests__/ToolArgsView.test.tsx +48 -0
- package/src/execution/__tests__/ToolCallDetail.test.tsx +195 -0
- package/src/execution/__tests__/ToolCallGroup.test.tsx +132 -0
- package/src/execution/__tests__/ToolCallItem.test.tsx +521 -0
- package/src/execution/__tests__/ToolRunGroup.test.tsx +137 -0
- package/src/execution/__tests__/blanked-narration.test.tsx +149 -0
- package/src/execution/__tests__/buildThreadItems-approvals.test.ts +304 -0
- package/src/execution/__tests__/buildThreadItems-file-review.test.ts +331 -0
- package/src/execution/__tests__/file-path-resolver.test.ts +44 -0
- package/src/execution/__tests__/file-review-status.test.ts +343 -0
- package/src/execution/__tests__/segment-tool-calls.test.ts +124 -0
- package/src/execution/__tests__/subagent-file-review-badge.test.tsx +273 -0
- package/src/execution/__tests__/thread-keys.test.ts +195 -0
- package/src/execution/__tests__/tool-presenter.test.tsx +95 -0
- package/src/execution/__tests__/useArtifactDownload.test.tsx +67 -0
- package/src/execution/__tests__/useArtifactDownloadUrl.test.tsx +51 -0
- package/src/execution/__tests__/useFileChangeContent.test.tsx +240 -0
- package/src/execution/__tests__/useFileReview.test.tsx +236 -0
- package/src/execution/__tests__/useSubmitApproval.test.tsx +165 -0
- package/src/execution/__tests__/useToolOutputContent.test.tsx +82 -0
- package/src/execution/approval-provenance.ts +8 -0
- package/src/execution/artifact-utils.ts +0 -18
- package/src/execution/execution-phases.ts +4 -16
- package/src/execution/file-path-resolver.ts +38 -0
- package/src/execution/file-review-status.ts +219 -0
- package/src/execution/index.ts +84 -4
- package/src/execution/segment-tool-calls.ts +101 -0
- package/src/execution/tool-categories.ts +128 -1
- package/src/execution/tool-presenter.ts +58 -3
- package/src/execution/tool-rendering-primitives.tsx +11 -14
- package/src/execution/useArtifactDownload.ts +109 -0
- package/src/execution/useArtifactDownloadUrl.ts +116 -0
- package/src/execution/useFileChangeContent.ts +149 -0
- package/src/execution/useFileReview.ts +197 -0
- package/src/execution/useSubmitApproval.ts +49 -27
- package/src/execution/useToolOutputContent.ts +70 -0
- package/src/index.ts +60 -1
- package/src/internal/ApprovalPeekBar.tsx +73 -0
- package/src/internal/BoundedContent.tsx +97 -0
- package/src/internal/DecisionButton.tsx +127 -0
- package/src/internal/InCardDecisionError.tsx +47 -0
- package/src/internal/RevealToggle.tsx +91 -0
- package/src/internal/VirtualizedThread.tsx +28 -2
- package/src/internal/__tests__/ApprovalPeekBar.test.tsx +34 -0
- package/src/internal/__tests__/BoundedContent.test.tsx +92 -0
- package/src/internal/__tests__/DecisionButton.test.tsx +107 -0
- package/src/internal/__tests__/RevealToggle.test.tsx +61 -0
- package/src/internal/__tests__/useAutoDisclosure.test.ts +74 -0
- package/src/internal/__tests__/useElapsedSince.test.tsx +68 -0
- package/src/internal/__tests__/useIsOverflowing.test.ts +36 -0
- package/src/internal/__tests__/useKeyedSubmission.test.tsx +126 -0
- package/src/internal/useAutoDisclosure.ts +58 -0
- package/src/internal/useElapsedSince.ts +72 -0
- package/src/internal/useIsOverflowing.ts +61 -0
- package/src/internal/useIsTextTruncated.ts +55 -0
- package/src/internal/useKeyedSubmission.ts +101 -0
- package/src/session/SessionViewer.tsx +21 -4
- package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +198 -0
- package/src/session/__tests__/audienceWiring.test.tsx +4 -0
- package/src/session/__tests__/useSessionFileChanges.test.ts +303 -0
- package/src/session/index.ts +3 -0
- package/src/session/inspector/ChangesTab.tsx +20 -17
- package/src/session/inspector/SessionInspector.tsx +12 -10
- package/src/session/inspector/__tests__/ArtifactsTab.test.tsx +0 -1
- package/src/session/inspector/__tests__/ChangesTab.test.tsx +38 -0
- package/src/session/inspector/__tests__/useSessionInspector.test.ts +46 -61
- package/src/session/inspector/index.ts +0 -3
- package/src/session/inspector/useSessionInspector.ts +27 -20
- package/src/session/useSessionConversation.ts +80 -3
- package/src/session/useSessionFileChanges.ts +207 -0
- package/src/styles.css +35 -10
- package/src/version-history/DiffViewer.tsx +10 -7
- package/src/version-history/UnifiedDiffView.tsx +66 -0
- package/src/version-history/__tests__/DiffViewer.test.tsx +57 -0
- package/src/version-history/__tests__/UnifiedDiffView.test.tsx +50 -0
- package/src/version-history/__tests__/computeDiff.test.ts +35 -0
- package/src/version-history/__tests__/parseUnifiedDiff.test.ts +82 -0
- package/src/version-history/computeDiff.ts +6 -37
- package/src/version-history/diff-hunks.ts +63 -0
- package/src/version-history/index.ts +1 -0
- package/src/version-history/parseUnifiedDiff.ts +34 -0
- package/src/workflow/WorkflowExecutionApprovalCard.tsx +49 -24
- package/src/workflow/WorkflowExecutionTimeline.tsx +15 -4
- package/src/workflow/WorkflowExecutionTimelineEvent.tsx +8 -3
- package/src/workflow/WorkflowExecutionViewer.tsx +72 -28
- package/src/workflow/WorkflowFileReviewList.tsx +152 -0
- package/src/workflow/WorkflowTaskApprovalCard.tsx +37 -85
- package/src/workflow/__tests__/ExecutionInspector.test.tsx +30 -2
- package/src/workflow/__tests__/WorkflowExecutionApprovalCard.test.tsx +129 -0
- package/src/workflow/__tests__/WorkflowFileReviewList.test.tsx +129 -0
- package/src/workflow/__tests__/WorkflowTaskApprovalCard.test.tsx +43 -11
- package/src/workflow/__tests__/useWorkflowExecutionActions.test.tsx +276 -0
- package/src/workflow/execution-inspector/ExecutionInspector.tsx +42 -18
- package/src/workflow/index.ts +5 -0
- package/src/workflow/useWorkflowExecutionActions.ts +177 -29
- package/styles.css +1 -1
- package/version-history/DiffViewer.d.ts.map +1 -1
- package/version-history/DiffViewer.js +9 -3
- package/version-history/DiffViewer.js.map +1 -1
- package/version-history/UnifiedDiffView.d.ts +29 -0
- package/version-history/UnifiedDiffView.d.ts.map +1 -0
- package/version-history/UnifiedDiffView.js +36 -0
- package/version-history/UnifiedDiffView.js.map +1 -0
- package/version-history/computeDiff.d.ts +3 -1
- package/version-history/computeDiff.d.ts.map +1 -1
- package/version-history/computeDiff.js +5 -35
- package/version-history/computeDiff.js.map +1 -1
- package/version-history/diff-hunks.d.ts +21 -0
- package/version-history/diff-hunks.d.ts.map +1 -0
- package/version-history/diff-hunks.js +58 -0
- package/version-history/diff-hunks.js.map +1 -0
- package/version-history/index.d.ts +1 -0
- package/version-history/index.d.ts.map +1 -1
- package/version-history/index.js +1 -0
- package/version-history/index.js.map +1 -1
- package/version-history/parseUnifiedDiff.d.ts +22 -0
- package/version-history/parseUnifiedDiff.d.ts.map +1 -0
- package/version-history/parseUnifiedDiff.js +34 -0
- package/version-history/parseUnifiedDiff.js.map +1 -0
- package/workflow/WorkflowExecutionApprovalCard.d.ts +8 -1
- package/workflow/WorkflowExecutionApprovalCard.d.ts.map +1 -1
- package/workflow/WorkflowExecutionApprovalCard.js +17 -3
- package/workflow/WorkflowExecutionApprovalCard.js.map +1 -1
- package/workflow/WorkflowExecutionTimeline.d.ts +11 -2
- package/workflow/WorkflowExecutionTimeline.d.ts.map +1 -1
- package/workflow/WorkflowExecutionTimeline.js +2 -2
- package/workflow/WorkflowExecutionTimeline.js.map +1 -1
- package/workflow/WorkflowExecutionTimelineEvent.d.ts +4 -1
- package/workflow/WorkflowExecutionTimelineEvent.d.ts.map +1 -1
- package/workflow/WorkflowExecutionTimelineEvent.js +2 -2
- package/workflow/WorkflowExecutionTimelineEvent.js.map +1 -1
- package/workflow/WorkflowExecutionViewer.d.ts.map +1 -1
- package/workflow/WorkflowExecutionViewer.js +18 -10
- package/workflow/WorkflowExecutionViewer.js.map +1 -1
- package/workflow/WorkflowFileReviewList.d.ts +46 -0
- package/workflow/WorkflowFileReviewList.d.ts.map +1 -0
- package/workflow/WorkflowFileReviewList.js +48 -0
- package/workflow/WorkflowFileReviewList.js.map +1 -0
- package/workflow/WorkflowTaskApprovalCard.d.ts +8 -1
- package/workflow/WorkflowTaskApprovalCard.d.ts.map +1 -1
- package/workflow/WorkflowTaskApprovalCard.js +12 -19
- package/workflow/WorkflowTaskApprovalCard.js.map +1 -1
- package/workflow/execution-inspector/ExecutionInspector.d.ts +21 -4
- package/workflow/execution-inspector/ExecutionInspector.d.ts.map +1 -1
- package/workflow/execution-inspector/ExecutionInspector.js +6 -3
- package/workflow/execution-inspector/ExecutionInspector.js.map +1 -1
- package/workflow/index.d.ts +1 -0
- package/workflow/index.d.ts.map +1 -1
- package/workflow/index.js +1 -0
- package/workflow/index.js.map +1 -1
- package/workflow/useWorkflowExecutionActions.d.ts +62 -4
- package/workflow/useWorkflowExecutionActions.d.ts.map +1 -1
- package/workflow/useWorkflowExecutionActions.js +94 -18
- package/workflow/useWorkflowExecutionActions.js.map +1 -1
- package/session/inspector/PlanTab.d.ts +0 -15
- package/session/inspector/PlanTab.d.ts.map +0 -1
- package/session/inspector/PlanTab.js +0 -19
- package/session/inspector/PlanTab.js.map +0 -1
- package/src/session/inspector/PlanTab.tsx +0 -31
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import type { ReactNode } from "react";
|
|
5
|
+
import { cn } from "@stigmer/theme";
|
|
6
|
+
import { useIsOverflowing } from "./useIsOverflowing";
|
|
7
|
+
import { RevealToggle } from "./RevealToggle";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The single shared height budget for a bounded tool-card block. Tall enough
|
|
11
|
+
* to show a screenshot or a handful of diff lines at a glance, short enough
|
|
12
|
+
* that a turn of several cards still scans as a timeline rather than a wall.
|
|
13
|
+
* One constant so the settled edit/write diff and the HITL approval gate diff
|
|
14
|
+
* are visually identical — change it here to retune both at once.
|
|
15
|
+
*/
|
|
16
|
+
export const PREVIEW_MAX_HEIGHT = "max-h-48";
|
|
17
|
+
|
|
18
|
+
/** Props for {@link BoundedContent}. */
|
|
19
|
+
export interface BoundedContentProps {
|
|
20
|
+
/** The content to bound — a diff (the only consumer today). */
|
|
21
|
+
readonly children: ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* Tailwind `max-h-*` clamp applied while collapsed. Defaults to the shared
|
|
24
|
+
* {@link PREVIEW_MAX_HEIGHT} budget; override only with deliberate reason.
|
|
25
|
+
*/
|
|
26
|
+
readonly maxHeightClass?: string;
|
|
27
|
+
/** Stable hook for e2e/visual targeting on the reveal control. */
|
|
28
|
+
readonly cursorTarget?: string;
|
|
29
|
+
/** Additional CSS class names for the root container. */
|
|
30
|
+
readonly className?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Bounds a visual block to one shared height budget with a bottom fade and an
|
|
35
|
+
* in-place {@link RevealToggle} — the "clamp + fade + Show more" shape used by
|
|
36
|
+
* the settled edit/write diff and the approval gate diff.
|
|
37
|
+
*
|
|
38
|
+
* This is the **pixel-clamp** truncation mechanism, for content that has no
|
|
39
|
+
* internal cap of its own (a diff table). Text and terminal output instead use
|
|
40
|
+
* the **line-count** mechanism ({@link CollapsiblePre} / {@link
|
|
41
|
+
* CollapsibleCode}); both render the same {@link RevealToggle}, so the two
|
|
42
|
+
* mechanisms share one visual language without ever double-bounding the same
|
|
43
|
+
* block. Never wrap content that already self-truncates in this.
|
|
44
|
+
*
|
|
45
|
+
* The control and fade appear only when {@link useIsOverflowing} reports the
|
|
46
|
+
* content is actually clipped (or the user expanded it). The fade uses
|
|
47
|
+
* `from-background`, matching every surface that consumes this today; a surface
|
|
48
|
+
* on a different token must supply its own fade.
|
|
49
|
+
*
|
|
50
|
+
* @internal Not part of the public API.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```tsx
|
|
54
|
+
* <BoundedContent><FileChangeDiff change={change} /></BoundedContent>
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function BoundedContent({
|
|
58
|
+
children,
|
|
59
|
+
maxHeightClass = PREVIEW_MAX_HEIGHT,
|
|
60
|
+
cursorTarget,
|
|
61
|
+
className,
|
|
62
|
+
}: BoundedContentProps) {
|
|
63
|
+
const [expanded, setExpanded] = useState(false);
|
|
64
|
+
|
|
65
|
+
// The clamp is applied while collapsed; measurement is only meaningful then.
|
|
66
|
+
const clamped = !expanded;
|
|
67
|
+
const { ref, isOverflowing } = useIsOverflowing<HTMLDivElement>(clamped);
|
|
68
|
+
|
|
69
|
+
// The control appears only when there is something hidden — either the
|
|
70
|
+
// content overflows now, or it was expanded (so "Show less" stays reachable).
|
|
71
|
+
const showControl = isOverflowing || expanded;
|
|
72
|
+
const showFade = clamped && isOverflowing;
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<div className={className}>
|
|
76
|
+
<div
|
|
77
|
+
ref={ref}
|
|
78
|
+
className={cn("relative overflow-hidden", clamped && maxHeightClass)}
|
|
79
|
+
>
|
|
80
|
+
{children}
|
|
81
|
+
{showFade && (
|
|
82
|
+
<div
|
|
83
|
+
aria-hidden="true"
|
|
84
|
+
className="pointer-events-none absolute inset-x-0 bottom-0 h-8 bg-gradient-to-t from-background to-transparent"
|
|
85
|
+
/>
|
|
86
|
+
)}
|
|
87
|
+
</div>
|
|
88
|
+
{showControl && (
|
|
89
|
+
<RevealToggle
|
|
90
|
+
expanded={expanded}
|
|
91
|
+
onToggle={() => setExpanded((v) => !v)}
|
|
92
|
+
cursorTarget={cursorTarget}
|
|
93
|
+
/>
|
|
94
|
+
)}
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@stigmer/theme";
|
|
4
|
+
|
|
5
|
+
/** The visual weight of a {@link DecisionButton}. */
|
|
6
|
+
export type DecisionVariant = "primary" | "ghost" | "danger";
|
|
7
|
+
|
|
8
|
+
/** Props for {@link DecisionButton}. */
|
|
9
|
+
export interface DecisionButtonProps {
|
|
10
|
+
/** Visible button text; also used as the accessible name. */
|
|
11
|
+
readonly label: string;
|
|
12
|
+
/**
|
|
13
|
+
* Visual weight. `primary` is the quiet neutral chip (the single recommended
|
|
14
|
+
* action), `ghost` is the lowest-weight neutral action, and `danger` is a
|
|
15
|
+
* ghost that reveals a destructive cue on hover/focus.
|
|
16
|
+
*/
|
|
17
|
+
readonly variant: DecisionVariant;
|
|
18
|
+
/** Invoked on click. */
|
|
19
|
+
readonly onClick: () => void;
|
|
20
|
+
/** True while ANY decision RPC is in flight — disables every button. */
|
|
21
|
+
readonly isSubmitting?: boolean;
|
|
22
|
+
/** True when THIS button's RPC is the one in flight — shows the spinner. */
|
|
23
|
+
readonly isActive?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Disables the button independent of {@link isSubmitting} — e.g. an action that
|
|
26
|
+
* is structurally unavailable (approving an incomplete, unreviewable diff).
|
|
27
|
+
*/
|
|
28
|
+
readonly disabled?: boolean;
|
|
29
|
+
/** Extra classes (e.g. `ml-auto` to visually separate a demoted action). */
|
|
30
|
+
readonly className?: string;
|
|
31
|
+
/** Stable hook for e2e/visual targeting (`data-cursor-target`). */
|
|
32
|
+
readonly cursorTarget?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Id of an element that explains this button — wired to `aria-describedby` so a
|
|
35
|
+
* disabled, structurally-unavailable action (e.g. approving an unreviewable
|
|
36
|
+
* change) is associated with the notice stating why.
|
|
37
|
+
*/
|
|
38
|
+
readonly ariaDescribedby?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const BASE = cn(
|
|
42
|
+
"inline-flex items-center justify-center gap-1.5 rounded-md px-3 py-1.5",
|
|
43
|
+
"text-xs font-medium transition-colors",
|
|
44
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
45
|
+
// Disabled dimming is ELEMENT opacity (allowed), never a `bg-token/NN` opacity
|
|
46
|
+
// modifier — each preset keeps full control of the token colors themselves.
|
|
47
|
+
"disabled:cursor-not-allowed disabled:opacity-50 disabled:pointer-events-none",
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Quiet, Cursor-grade, token-only.
|
|
51
|
+
const VARIANT: Record<DecisionVariant, string> = {
|
|
52
|
+
// Neutral chip: the single primary action. It reads as primary by fill-weight
|
|
53
|
+
// and border against the ghost actions — NOT by hue — so the hierarchy holds
|
|
54
|
+
// without color being the only channel (a11y).
|
|
55
|
+
primary:
|
|
56
|
+
"border border-border bg-accent text-accent-foreground hover:bg-accent-hover",
|
|
57
|
+
// The canonical SDK ghost: no fill at rest, neutral wash on hover.
|
|
58
|
+
ghost: "text-muted-foreground hover:text-foreground hover:bg-accent-hover",
|
|
59
|
+
// Quiet at rest; the destructive cue is revealed on hover AND keyboard focus
|
|
60
|
+
// (parity for keyboard users — not hover-only). A persistent red button is
|
|
61
|
+
// unnecessary: rejecting is the safe action, and a destructive tool gate is
|
|
62
|
+
// already flagged by the card's accent.
|
|
63
|
+
danger: cn(
|
|
64
|
+
"text-muted-foreground",
|
|
65
|
+
"hover:bg-destructive-subtle hover:text-destructive",
|
|
66
|
+
"focus-visible:bg-destructive-subtle focus-visible:text-destructive",
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The shared decision-action button for every approval surface — the agent
|
|
72
|
+
* tool gate ({@link ApprovalCard}) and the two workflow approval cards
|
|
73
|
+
* ({@link WorkflowTaskApprovalCard}, {@link WorkflowExecutionApprovalCard}).
|
|
74
|
+
*
|
|
75
|
+
* One source of truth for the quiet, Cursor-grade button system so the three
|
|
76
|
+
* surfaces can never visually drift, and so the spinner and base chrome live in
|
|
77
|
+
* exactly one place. Every visual property flows through `--stgm-*` tokens.
|
|
78
|
+
*
|
|
79
|
+
* @internal Not part of the public API.
|
|
80
|
+
*/
|
|
81
|
+
export function DecisionButton({
|
|
82
|
+
label,
|
|
83
|
+
variant,
|
|
84
|
+
onClick,
|
|
85
|
+
isSubmitting = false,
|
|
86
|
+
isActive = false,
|
|
87
|
+
disabled = false,
|
|
88
|
+
className,
|
|
89
|
+
cursorTarget,
|
|
90
|
+
ariaDescribedby,
|
|
91
|
+
}: DecisionButtonProps) {
|
|
92
|
+
return (
|
|
93
|
+
<button
|
|
94
|
+
type="button"
|
|
95
|
+
disabled={isSubmitting || disabled}
|
|
96
|
+
onClick={onClick}
|
|
97
|
+
aria-label={label}
|
|
98
|
+
aria-describedby={ariaDescribedby}
|
|
99
|
+
data-cursor-target={cursorTarget}
|
|
100
|
+
className={cn(BASE, VARIANT[variant], className)}
|
|
101
|
+
>
|
|
102
|
+
{isActive && isSubmitting ? <SpinnerIcon /> : null}
|
|
103
|
+
{label}
|
|
104
|
+
</button>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Inline SVG icon
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
function SpinnerIcon() {
|
|
113
|
+
return (
|
|
114
|
+
<svg
|
|
115
|
+
width="10"
|
|
116
|
+
height="10"
|
|
117
|
+
viewBox="0 0 12 12"
|
|
118
|
+
fill="none"
|
|
119
|
+
stroke="currentColor"
|
|
120
|
+
strokeWidth="1.5"
|
|
121
|
+
className="animate-spin"
|
|
122
|
+
aria-hidden="true"
|
|
123
|
+
>
|
|
124
|
+
<path d="M6 1.5A4.5 4.5 0 1 1 1.5 6" strokeLinecap="round" />
|
|
125
|
+
</svg>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/** Props for {@link InCardDecisionError}. */
|
|
4
|
+
export interface InCardDecisionErrorProps {
|
|
5
|
+
/** The failure to surface. Its `message` is shown verbatim (the actionable part). */
|
|
6
|
+
readonly error: Error;
|
|
7
|
+
/**
|
|
8
|
+
* The verb phrase completing "Couldn't ___" — e.g. `"submit decision"` or
|
|
9
|
+
* `"save"`. Kept short; the server `message` carries the detail.
|
|
10
|
+
*/
|
|
11
|
+
readonly leadIn: string;
|
|
12
|
+
/** Stable hook for e2e/visual targeting (`data-cursor-target`). */
|
|
13
|
+
readonly cursorTarget: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The shared in-card failure notice for a decision that did not take — used by
|
|
18
|
+
* every decision surface that surfaces errors beside the control that failed:
|
|
19
|
+
* the agent tool gate ({@link ApprovalCard}/{@link ApprovalCardBody}) and the
|
|
20
|
+
* file-review card ({@link FileReviewCard}, whole-set and per-file).
|
|
21
|
+
*
|
|
22
|
+
* One source of truth so those surfaces can never drift in tone, copy shape, or
|
|
23
|
+
* accessibility. `role="alert"` (matching the thread's other inline failures)
|
|
24
|
+
* announces it the moment it appears, since it is a dynamic reaction to the
|
|
25
|
+
* reviewer's action — by the time it renders the optimistic verdict has already
|
|
26
|
+
* reverted, and this explains why. The server `message` is shown verbatim
|
|
27
|
+
* because it is the actionable part (e.g. a digest mismatch means the captured
|
|
28
|
+
* content moved on); the lead-in stays short. Every visual property flows
|
|
29
|
+
* through `--stgm-*` tokens.
|
|
30
|
+
*
|
|
31
|
+
* @internal Not part of the public API.
|
|
32
|
+
*/
|
|
33
|
+
export function InCardDecisionError({
|
|
34
|
+
error,
|
|
35
|
+
leadIn,
|
|
36
|
+
cursorTarget,
|
|
37
|
+
}: InCardDecisionErrorProps) {
|
|
38
|
+
return (
|
|
39
|
+
<p
|
|
40
|
+
role="alert"
|
|
41
|
+
className="text-[11px] text-destructive"
|
|
42
|
+
data-cursor-target={cursorTarget}
|
|
43
|
+
>
|
|
44
|
+
Couldn’t {leadIn} — {error.message}
|
|
45
|
+
</p>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@stigmer/theme";
|
|
4
|
+
|
|
5
|
+
/** Props for {@link RevealToggle}. */
|
|
6
|
+
export interface RevealToggleProps {
|
|
7
|
+
/** Whether the governed content is currently fully revealed. */
|
|
8
|
+
readonly expanded: boolean;
|
|
9
|
+
/** Flips the reveal state. */
|
|
10
|
+
readonly onToggle: () => void;
|
|
11
|
+
/** Label shown while collapsed. Defaults to `"Show more"`. */
|
|
12
|
+
readonly moreLabel?: string;
|
|
13
|
+
/** Label shown while expanded. Defaults to `"Show less"`. */
|
|
14
|
+
readonly lessLabel?: string;
|
|
15
|
+
/** Stable hook for e2e/visual targeting (`data-cursor-target`). */
|
|
16
|
+
readonly cursorTarget?: string;
|
|
17
|
+
/** Additional CSS class names for the control. */
|
|
18
|
+
readonly className?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The single "reveal more / less" control for every bounded block on the
|
|
23
|
+
* execution surface — the diff/gate clamp ({@link BoundedContent}) and the
|
|
24
|
+
* line-count text/terminal blocks ({@link CollapsiblePre} / {@link
|
|
25
|
+
* CollapsibleCode}).
|
|
26
|
+
*
|
|
27
|
+
* It is the Cursor-style affordance the design calls for: a light, centered
|
|
28
|
+
* chevron at the bottom-middle (down to reveal, up to collapse) with a quiet
|
|
29
|
+
* label. Extracting it is what guarantees one disclosure *language* across two
|
|
30
|
+
* truncation *mechanisms* (pixel clamp for visual blocks, line count for text)
|
|
31
|
+
* — the surfaces can never visually drift because they render the same control.
|
|
32
|
+
*
|
|
33
|
+
* Presentational only: it owns no state. The caller holds the reveal state and
|
|
34
|
+
* passes `expanded` + `onToggle`. It carries `aria-expanded` for the block it
|
|
35
|
+
* governs; the owning tool-call row's header chevron (when present) carries the
|
|
36
|
+
* row-level disclosure, so the two axes never collide.
|
|
37
|
+
*
|
|
38
|
+
* @internal Not part of the public API.
|
|
39
|
+
*/
|
|
40
|
+
export function RevealToggle({
|
|
41
|
+
expanded,
|
|
42
|
+
onToggle,
|
|
43
|
+
moreLabel = "Show more",
|
|
44
|
+
lessLabel = "Show less",
|
|
45
|
+
cursorTarget,
|
|
46
|
+
className,
|
|
47
|
+
}: RevealToggleProps) {
|
|
48
|
+
return (
|
|
49
|
+
<button
|
|
50
|
+
type="button"
|
|
51
|
+
onClick={onToggle}
|
|
52
|
+
aria-expanded={expanded}
|
|
53
|
+
data-cursor-target={cursorTarget}
|
|
54
|
+
className={cn(
|
|
55
|
+
"mt-1 flex w-full items-center justify-center gap-1 rounded py-0.5",
|
|
56
|
+
"text-xs font-medium text-muted-foreground transition-colors hover:text-foreground",
|
|
57
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
58
|
+
className,
|
|
59
|
+
)}
|
|
60
|
+
>
|
|
61
|
+
<span>{expanded ? lessLabel : moreLabel}</span>
|
|
62
|
+
<ChevronIcon expanded={expanded} />
|
|
63
|
+
</button>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Inline SVG icon
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
function ChevronIcon({ expanded }: { expanded: boolean }) {
|
|
72
|
+
return (
|
|
73
|
+
<svg
|
|
74
|
+
width="10"
|
|
75
|
+
height="10"
|
|
76
|
+
viewBox="0 0 10 10"
|
|
77
|
+
fill="none"
|
|
78
|
+
stroke="currentColor"
|
|
79
|
+
strokeWidth="1.5"
|
|
80
|
+
strokeLinecap="round"
|
|
81
|
+
strokeLinejoin="round"
|
|
82
|
+
className={cn(
|
|
83
|
+
"shrink-0 transition-transform duration-150",
|
|
84
|
+
expanded && "rotate-180",
|
|
85
|
+
)}
|
|
86
|
+
aria-hidden="true"
|
|
87
|
+
>
|
|
88
|
+
<path d="M2.5 3.5L5 6.5L7.5 3.5" />
|
|
89
|
+
</svg>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -18,8 +18,11 @@ import {
|
|
|
18
18
|
} from "../execution/MessageThread";
|
|
19
19
|
import { FilePathContext, type FilePathContextValue } from "../execution/FilePathContext";
|
|
20
20
|
import { SandboxContext, type SandboxContextValue } from "../execution/SandboxContext";
|
|
21
|
+
import { ApprovalContext, type ApprovalContextValue } from "../execution/ApprovalContext";
|
|
22
|
+
import { FileReviewContext, type FileReviewContextValue } from "../execution/FileReviewContext";
|
|
21
23
|
import { DevProfiler, useDomNodeCount } from "./dev";
|
|
22
24
|
import { JumpToLatestButton } from "./JumpToLatestButton";
|
|
25
|
+
import { ApprovalPeekBar } from "./ApprovalPeekBar";
|
|
23
26
|
import { ThreadItemWrapper } from "./ThreadItemWrapper";
|
|
24
27
|
|
|
25
28
|
// ---------------------------------------------------------------------------
|
|
@@ -35,8 +38,12 @@ export interface VirtualizedThreadProps {
|
|
|
35
38
|
comment?: string,
|
|
36
39
|
) => void;
|
|
37
40
|
readonly submittingApprovalIds?: ReadonlySet<string>;
|
|
41
|
+
readonly approvalErrors?: ReadonlyMap<string, Error>;
|
|
38
42
|
readonly filePathCtx: FilePathContextValue;
|
|
39
43
|
readonly sandboxCtx: SandboxContextValue;
|
|
44
|
+
readonly approvalCtx: ApprovalContextValue;
|
|
45
|
+
readonly fileReviewCtx: FileReviewContextValue;
|
|
46
|
+
readonly unresolvedApprovalCount: number;
|
|
40
47
|
readonly onBuildFromPlan?: () => void;
|
|
41
48
|
readonly org?: string;
|
|
42
49
|
readonly planActionsDisabled?: boolean;
|
|
@@ -98,8 +105,12 @@ export function VirtualizedThread({
|
|
|
98
105
|
formatToolCallSummary,
|
|
99
106
|
onApprovalSubmit,
|
|
100
107
|
submittingApprovalIds,
|
|
108
|
+
approvalErrors,
|
|
101
109
|
filePathCtx,
|
|
102
110
|
sandboxCtx,
|
|
111
|
+
approvalCtx,
|
|
112
|
+
fileReviewCtx,
|
|
113
|
+
unresolvedApprovalCount,
|
|
103
114
|
onBuildFromPlan,
|
|
104
115
|
org,
|
|
105
116
|
planActionsDisabled,
|
|
@@ -135,6 +146,7 @@ export function VirtualizedThread({
|
|
|
135
146
|
formatToolCallSummary,
|
|
136
147
|
onApprovalSubmit,
|
|
137
148
|
submittingApprovalIds,
|
|
149
|
+
approvalErrors,
|
|
138
150
|
onBuildFromPlan,
|
|
139
151
|
org,
|
|
140
152
|
planActionsDisabled,
|
|
@@ -142,13 +154,15 @@ export function VirtualizedThread({
|
|
|
142
154
|
onRetryExecution,
|
|
143
155
|
onEditMessage,
|
|
144
156
|
}),
|
|
145
|
-
[formatToolCallSummary, onApprovalSubmit, submittingApprovalIds, onBuildFromPlan, org, planActionsDisabled, onRetrySend, onRetryExecution, onEditMessage],
|
|
157
|
+
[formatToolCallSummary, onApprovalSubmit, submittingApprovalIds, approvalErrors, onBuildFromPlan, org, planActionsDisabled, onRetrySend, onRetryExecution, onEditMessage],
|
|
146
158
|
);
|
|
147
159
|
|
|
148
160
|
return (
|
|
149
161
|
<>
|
|
150
162
|
<SandboxContext.Provider value={sandboxCtx}>
|
|
151
163
|
<FilePathContext.Provider value={filePathCtx}>
|
|
164
|
+
<ApprovalContext.Provider value={approvalCtx}>
|
|
165
|
+
<FileReviewContext.Provider value={fileReviewCtx}>
|
|
152
166
|
<DevProfiler id="MessageThread:virtualized">
|
|
153
167
|
<Virtuoso
|
|
154
168
|
ref={virtuosoRef}
|
|
@@ -174,9 +188,21 @@ export function VirtualizedThread({
|
|
|
174
188
|
)}
|
|
175
189
|
/>
|
|
176
190
|
</DevProfiler>
|
|
191
|
+
</FileReviewContext.Provider>
|
|
192
|
+
</ApprovalContext.Provider>
|
|
177
193
|
</FilePathContext.Provider>
|
|
178
194
|
</SandboxContext.Provider>
|
|
179
|
-
|
|
195
|
+
{/* The peek bar takes the jump button's slot while approvals are pending,
|
|
196
|
+
so the two never overlap — a gate is the louder of the two signals. */}
|
|
197
|
+
<JumpToLatestButton
|
|
198
|
+
onClick={jumpToLatest}
|
|
199
|
+
visible={!isAtBottom && unresolvedApprovalCount === 0}
|
|
200
|
+
/>
|
|
201
|
+
<ApprovalPeekBar
|
|
202
|
+
visible={!isAtBottom && unresolvedApprovalCount > 0}
|
|
203
|
+
count={unresolvedApprovalCount}
|
|
204
|
+
onClick={jumpToLatest}
|
|
205
|
+
/>
|
|
180
206
|
</>
|
|
181
207
|
);
|
|
182
208
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach, vi } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { ApprovalPeekBar } from "../ApprovalPeekBar";
|
|
4
|
+
|
|
5
|
+
afterEach(cleanup);
|
|
6
|
+
|
|
7
|
+
describe("ApprovalPeekBar", () => {
|
|
8
|
+
it("is interactive and labelled when visible", () => {
|
|
9
|
+
render(<ApprovalPeekBar visible count={1} onClick={() => {}} />);
|
|
10
|
+
const btn = screen.getByRole("button");
|
|
11
|
+
expect(btn.getAttribute("aria-hidden")).toBe("false");
|
|
12
|
+
expect(btn.getAttribute("tabindex")).toBe("0");
|
|
13
|
+
expect(screen.getByText("1 approval needed")).toBeTruthy();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("is inert (mounted for transitions) when not visible", () => {
|
|
17
|
+
render(<ApprovalPeekBar visible={false} count={2} onClick={() => {}} />);
|
|
18
|
+
const btn = screen.getByRole("button", { hidden: true });
|
|
19
|
+
expect(btn.getAttribute("aria-hidden")).toBe("true");
|
|
20
|
+
expect(btn.getAttribute("tabindex")).toBe("-1");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("pluralizes the count", () => {
|
|
24
|
+
render(<ApprovalPeekBar visible count={3} onClick={() => {}} />);
|
|
25
|
+
expect(screen.getByText("3 approvals needed")).toBeTruthy();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("invokes onClick (jump-to-latest) when pressed", () => {
|
|
29
|
+
const onClick = vi.fn();
|
|
30
|
+
render(<ApprovalPeekBar visible count={1} onClick={onClick} />);
|
|
31
|
+
fireEvent.click(screen.getByRole("button"));
|
|
32
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
// happy-dom does not compute layout, so the real overflow measurement always
|
|
5
|
+
// reports false there (scrollHeight === clientHeight === 0). We mock the hook so
|
|
6
|
+
// the overflow-driven branches (fade + reveal control) are deterministically
|
|
7
|
+
// testable here; the *rendered* clamp/fade is exercised against a real browser
|
|
8
|
+
// in test/e2e/tests/interactive-approval/tool-card-ux.spec.ts.
|
|
9
|
+
let mockOverflowing = false;
|
|
10
|
+
vi.mock("../useIsOverflowing", () => ({
|
|
11
|
+
useIsOverflowing: () => ({ ref: { current: null }, isOverflowing: mockOverflowing }),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const { BoundedContent, PREVIEW_MAX_HEIGHT } = await import("../BoundedContent");
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
mockOverflowing = false;
|
|
18
|
+
});
|
|
19
|
+
afterEach(cleanup);
|
|
20
|
+
|
|
21
|
+
/** The collapsed clamp element: `overflow-hidden` + the shared budget. */
|
|
22
|
+
function clampEl(container: HTMLElement): Element | null {
|
|
23
|
+
return container.querySelector(`.overflow-hidden.${PREVIEW_MAX_HEIGHT}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe("BoundedContent", () => {
|
|
27
|
+
it("renders its children", () => {
|
|
28
|
+
render(
|
|
29
|
+
<BoundedContent>
|
|
30
|
+
<p>diff body</p>
|
|
31
|
+
</BoundedContent>,
|
|
32
|
+
);
|
|
33
|
+
expect(screen.getByText("diff body")).toBeTruthy();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("clamps the body to the single shared budget while collapsed", () => {
|
|
37
|
+
const { container } = render(
|
|
38
|
+
<BoundedContent>
|
|
39
|
+
<p>diff body</p>
|
|
40
|
+
</BoundedContent>,
|
|
41
|
+
);
|
|
42
|
+
expect(clampEl(container)).not.toBeNull();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("shows no reveal control when the content fits the budget", () => {
|
|
46
|
+
mockOverflowing = false;
|
|
47
|
+
render(
|
|
48
|
+
<BoundedContent>
|
|
49
|
+
<p>short</p>
|
|
50
|
+
</BoundedContent>,
|
|
51
|
+
);
|
|
52
|
+
expect(screen.queryByRole("button")).toBeNull();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("shows 'Show more' when the content overflows and expands it in place to 'Show less', dropping the clamp", () => {
|
|
56
|
+
mockOverflowing = true;
|
|
57
|
+
const { container } = render(
|
|
58
|
+
<BoundedContent>
|
|
59
|
+
<p>long body</p>
|
|
60
|
+
</BoundedContent>,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// Collapsed: clamped, with a "Show more" reveal that owns the disclosure.
|
|
64
|
+
const control = screen.getByRole("button", { name: /Show more/ });
|
|
65
|
+
expect(control.getAttribute("aria-expanded")).toBe("false");
|
|
66
|
+
expect(clampEl(container)).not.toBeNull();
|
|
67
|
+
|
|
68
|
+
// Expanding removes the clamp (natural height) and flips the control.
|
|
69
|
+
fireEvent.click(control);
|
|
70
|
+
expect(
|
|
71
|
+
screen.getByRole("button", { name: /Show less/ }).getAttribute("aria-expanded"),
|
|
72
|
+
).toBe("true");
|
|
73
|
+
expect(clampEl(container)).toBeNull();
|
|
74
|
+
|
|
75
|
+
// Collapsing restores the clamp.
|
|
76
|
+
fireEvent.click(screen.getByRole("button", { name: /Show less/ }));
|
|
77
|
+
expect(screen.getByRole("button", { name: /Show more/ })).toBeTruthy();
|
|
78
|
+
expect(clampEl(container)).not.toBeNull();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("forwards the e2e cursor-target hook onto the control", () => {
|
|
82
|
+
mockOverflowing = true;
|
|
83
|
+
render(
|
|
84
|
+
<BoundedContent cursorTarget="file-diff-expand">
|
|
85
|
+
<p>body</p>
|
|
86
|
+
</BoundedContent>,
|
|
87
|
+
);
|
|
88
|
+
expect(
|
|
89
|
+
screen.getByRole("button").getAttribute("data-cursor-target"),
|
|
90
|
+
).toBe("file-diff-expand");
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach, vi } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { DecisionButton } from "../DecisionButton";
|
|
4
|
+
|
|
5
|
+
afterEach(cleanup);
|
|
6
|
+
|
|
7
|
+
// happy-dom cannot resolve `@layer`/the cascade, so these assert the class
|
|
8
|
+
// CONTRACT (the quiet, token-only treatment), not rendered pixels. The rendered
|
|
9
|
+
// outcome is covered by the e2e computed-style guard in tool-card-ux.spec.ts.
|
|
10
|
+
describe("DecisionButton", () => {
|
|
11
|
+
it("renders the label as the accessible name and invokes onClick", () => {
|
|
12
|
+
const onClick = vi.fn();
|
|
13
|
+
render(<DecisionButton label="Approve" variant="primary" onClick={onClick} />);
|
|
14
|
+
const btn = screen.getByRole("button", { name: "Approve" });
|
|
15
|
+
fireEvent.click(btn);
|
|
16
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("primary is a neutral chip (bordered fill), never the loud success green", () => {
|
|
20
|
+
render(<DecisionButton label="Approve" variant="primary" onClick={() => {}} />);
|
|
21
|
+
const cls = screen.getByRole("button").className;
|
|
22
|
+
expect(cls).toContain("border");
|
|
23
|
+
expect(cls).toContain("bg-accent");
|
|
24
|
+
expect(cls).toContain("text-accent-foreground");
|
|
25
|
+
expect(cls).not.toContain("bg-success");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("ghost carries no fill at rest (lowest weight)", () => {
|
|
29
|
+
render(<DecisionButton label="Skip" variant="ghost" onClick={() => {}} />);
|
|
30
|
+
const cls = screen.getByRole("button").className;
|
|
31
|
+
expect(cls).toContain("text-muted-foreground");
|
|
32
|
+
// No resting background — only a neutral hover wash.
|
|
33
|
+
expect(cls).not.toMatch(/(?:^|\s)bg-/);
|
|
34
|
+
expect(cls).toContain("hover:bg-accent-hover");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("danger is a quiet ghost that reveals the destructive cue on hover AND focus, never the loud red fill", () => {
|
|
38
|
+
render(<DecisionButton label="Reject" variant="danger" onClick={() => {}} />);
|
|
39
|
+
const cls = screen.getByRole("button").className;
|
|
40
|
+
expect(cls).toContain("text-muted-foreground");
|
|
41
|
+
expect(cls).not.toMatch(/(?:^|\s)bg-/); // transparent at rest
|
|
42
|
+
expect(cls).toContain("hover:text-destructive");
|
|
43
|
+
expect(cls).toContain("hover:bg-destructive-subtle");
|
|
44
|
+
// Keyboard parity: the cue is not hover-only.
|
|
45
|
+
expect(cls).toContain("focus-visible:text-destructive");
|
|
46
|
+
expect(cls).not.toContain("bg-destructive text-destructive-foreground");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("uses NO `bg-token/NN` opacity modifiers in any variant", () => {
|
|
50
|
+
for (const variant of ["primary", "ghost", "danger"] as const) {
|
|
51
|
+
const { unmount } = render(
|
|
52
|
+
<DecisionButton label="x" variant={variant} onClick={() => {}} />,
|
|
53
|
+
);
|
|
54
|
+
const cls = screen.getByRole("button").className;
|
|
55
|
+
// No utility ends in `/<digits>` (e.g. bg-success/90, hover:bg-destructive/10).
|
|
56
|
+
expect(cls).not.toMatch(/\/\d+(?:\s|$)/);
|
|
57
|
+
unmount();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("disables every button while a submission is in flight", () => {
|
|
62
|
+
render(
|
|
63
|
+
<DecisionButton label="Approve" variant="primary" onClick={() => {}} isSubmitting />,
|
|
64
|
+
);
|
|
65
|
+
expect((screen.getByRole("button") as HTMLButtonElement).disabled).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("shows the spinner only on the active button that is submitting", () => {
|
|
69
|
+
const { rerender } = render(
|
|
70
|
+
<DecisionButton
|
|
71
|
+
label="Approve"
|
|
72
|
+
variant="primary"
|
|
73
|
+
onClick={() => {}}
|
|
74
|
+
isSubmitting
|
|
75
|
+
isActive
|
|
76
|
+
/>,
|
|
77
|
+
);
|
|
78
|
+
expect(screen.getByRole("button").querySelector("svg.animate-spin")).toBeTruthy();
|
|
79
|
+
|
|
80
|
+
// In-flight, but a DIFFERENT button is the active one — no spinner here.
|
|
81
|
+
rerender(
|
|
82
|
+
<DecisionButton
|
|
83
|
+
label="Approve"
|
|
84
|
+
variant="primary"
|
|
85
|
+
onClick={() => {}}
|
|
86
|
+
isSubmitting
|
|
87
|
+
isActive={false}
|
|
88
|
+
/>,
|
|
89
|
+
);
|
|
90
|
+
expect(screen.getByRole("button").querySelector("svg.animate-spin")).toBeNull();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("forwards className and the data-cursor-target hook", () => {
|
|
94
|
+
render(
|
|
95
|
+
<DecisionButton
|
|
96
|
+
label="Approve all"
|
|
97
|
+
variant="ghost"
|
|
98
|
+
onClick={() => {}}
|
|
99
|
+
className="ml-auto"
|
|
100
|
+
cursorTarget="approve-all-button"
|
|
101
|
+
/>,
|
|
102
|
+
);
|
|
103
|
+
const btn = screen.getByRole("button");
|
|
104
|
+
expect(btn.className).toContain("ml-auto");
|
|
105
|
+
expect(btn.getAttribute("data-cursor-target")).toBe("approve-all-button");
|
|
106
|
+
});
|
|
107
|
+
});
|