@stigmer/react 3.0.9-dev.20260615153829 → 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/code-highlight.d.ts +24 -0
- package/internal/code-highlight.d.ts.map +1 -0
- package/internal/code-highlight.js +115 -0
- package/internal/code-highlight.js.map +1 -0
- package/internal/markdown-components.d.ts.map +1 -1
- package/internal/markdown-components.js +13 -2
- package/internal/markdown-components.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 +7 -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__/ArtifactContentRenderer.test.tsx +15 -0
- 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__/message-entry.test.tsx +16 -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__/code-highlight.test.tsx +59 -0
- package/src/internal/__tests__/markdown-components.test.tsx +68 -2
- 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/code-highlight.tsx +120 -0
- package/src/internal/markdown-components.tsx +20 -3
- 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 +123 -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,61 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach, vi } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { RevealToggle } from "../RevealToggle";
|
|
4
|
+
|
|
5
|
+
afterEach(cleanup);
|
|
6
|
+
|
|
7
|
+
describe("RevealToggle", () => {
|
|
8
|
+
it("shows the default 'Show more' label and a collapsed aria-expanded when collapsed", () => {
|
|
9
|
+
render(<RevealToggle expanded={false} onToggle={() => {}} />);
|
|
10
|
+
const btn = screen.getByRole("button", { name: /Show more/ });
|
|
11
|
+
expect(btn.getAttribute("aria-expanded")).toBe("false");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("shows the default 'Show less' label and an expanded aria-expanded when expanded", () => {
|
|
15
|
+
render(<RevealToggle expanded onToggle={() => {}} />);
|
|
16
|
+
const btn = screen.getByRole("button", { name: /Show less/ });
|
|
17
|
+
expect(btn.getAttribute("aria-expanded")).toBe("true");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("honours custom more/less labels (e.g. the line-count primitives)", () => {
|
|
21
|
+
const { rerender } = render(
|
|
22
|
+
<RevealToggle
|
|
23
|
+
expanded={false}
|
|
24
|
+
onToggle={() => {}}
|
|
25
|
+
moreLabel="Show all 42 lines"
|
|
26
|
+
lessLabel="Show less"
|
|
27
|
+
/>,
|
|
28
|
+
);
|
|
29
|
+
expect(screen.getByText("Show all 42 lines")).toBeTruthy();
|
|
30
|
+
|
|
31
|
+
rerender(
|
|
32
|
+
<RevealToggle
|
|
33
|
+
expanded
|
|
34
|
+
onToggle={() => {}}
|
|
35
|
+
moreLabel="Show all 42 lines"
|
|
36
|
+
lessLabel="Show less"
|
|
37
|
+
/>,
|
|
38
|
+
);
|
|
39
|
+
expect(screen.getByText("Show less")).toBeTruthy();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("invokes onToggle on click", () => {
|
|
43
|
+
const onToggle = vi.fn();
|
|
44
|
+
render(<RevealToggle expanded={false} onToggle={onToggle} />);
|
|
45
|
+
fireEvent.click(screen.getByRole("button"));
|
|
46
|
+
expect(onToggle).toHaveBeenCalledTimes(1);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("forwards the e2e cursor-target hook onto the control", () => {
|
|
50
|
+
render(
|
|
51
|
+
<RevealToggle
|
|
52
|
+
expanded={false}
|
|
53
|
+
onToggle={() => {}}
|
|
54
|
+
cursorTarget="tool-detail-expand"
|
|
55
|
+
/>,
|
|
56
|
+
);
|
|
57
|
+
expect(
|
|
58
|
+
screen.getByRole("button").getAttribute("data-cursor-target"),
|
|
59
|
+
).toBe("tool-detail-expand");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
+
import { render, cleanup } from "@testing-library/react";
|
|
3
|
+
import { resolveLanguage, highlightToReact } from "../code-highlight";
|
|
4
|
+
|
|
5
|
+
afterEach(cleanup);
|
|
6
|
+
|
|
7
|
+
describe("resolveLanguage", () => {
|
|
8
|
+
it.each([
|
|
9
|
+
["a registered name", "go", "go"],
|
|
10
|
+
["uppercase is normalized", "GO", "go"],
|
|
11
|
+
["surrounding whitespace is trimmed", " yaml ", "yaml"],
|
|
12
|
+
["a registered alias (ts → typescript)", "ts", "ts"],
|
|
13
|
+
["a registered alias (tsx)", "tsx", "tsx"],
|
|
14
|
+
["a registered alias (html → xml)", "html", "html"],
|
|
15
|
+
["a registered alias (sh → bash)", "sh", "sh"],
|
|
16
|
+
])("resolves %s", (_label, input, expected) => {
|
|
17
|
+
expect(resolveLanguage(input)).toBe(expected);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it.each([
|
|
21
|
+
["an unregistered language", "hcl"],
|
|
22
|
+
["a nonsense language", "klingon"],
|
|
23
|
+
["an empty string", ""],
|
|
24
|
+
["whitespace only", " "],
|
|
25
|
+
["undefined", undefined],
|
|
26
|
+
])("returns null for %s", (_label, input) => {
|
|
27
|
+
expect(resolveLanguage(input)).toBeNull();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("highlightToReact", () => {
|
|
32
|
+
it("produces hljs token spans for a known language", () => {
|
|
33
|
+
const node = highlightToReact("func main() {}", "go");
|
|
34
|
+
expect(node).not.toBeNull();
|
|
35
|
+
|
|
36
|
+
const { container } = render(<code>{node}</code>);
|
|
37
|
+
// `func` is a Go keyword → at least one tokenized span must appear.
|
|
38
|
+
expect(
|
|
39
|
+
container.querySelectorAll('span[class*="hljs-"]').length,
|
|
40
|
+
).toBeGreaterThan(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("highlights via an alias (ts)", () => {
|
|
44
|
+
const node = highlightToReact("const x: number = 1;", "ts");
|
|
45
|
+
expect(node).not.toBeNull();
|
|
46
|
+
|
|
47
|
+
const { container } = render(<code>{node}</code>);
|
|
48
|
+
expect(
|
|
49
|
+
container.querySelectorAll('span[class*="hljs-"]').length,
|
|
50
|
+
).toBeGreaterThan(0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it.each([
|
|
54
|
+
["an unregistered language", "hcl"],
|
|
55
|
+
["no language", undefined],
|
|
56
|
+
])("returns null for %s (caller falls back to flat)", (_label, language) => {
|
|
57
|
+
expect(highlightToReact("anything at all", language)).toBeNull();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
1
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
+
import { render, cleanup } from "@testing-library/react";
|
|
3
|
+
import type { ComponentType, ReactNode } from "react";
|
|
4
|
+
import {
|
|
5
|
+
MARKDOWN_COMPONENTS,
|
|
6
|
+
unwrapEnclosingMarkdownFence,
|
|
7
|
+
} from "../markdown-components";
|
|
8
|
+
|
|
9
|
+
afterEach(cleanup);
|
|
10
|
+
|
|
11
|
+
/** The shared `code` override, typed for direct rendering in tests. */
|
|
12
|
+
const CodeComponent = MARKDOWN_COMPONENTS.code as ComponentType<{
|
|
13
|
+
className?: string;
|
|
14
|
+
children?: ReactNode;
|
|
15
|
+
}>;
|
|
3
16
|
|
|
4
17
|
describe("unwrapEnclosingMarkdownFence", () => {
|
|
5
18
|
describe("unwraps a whole-message markdown fence", () => {
|
|
@@ -51,3 +64,56 @@ describe("unwrapEnclosingMarkdownFence", () => {
|
|
|
51
64
|
expect(unwrapEnclosingMarkdownFence(once)).toBe(once);
|
|
52
65
|
});
|
|
53
66
|
});
|
|
67
|
+
|
|
68
|
+
describe("MARKDOWN_COMPONENTS.code (shared highlight seam)", () => {
|
|
69
|
+
it("syntax-highlights a fenced block with a known language", () => {
|
|
70
|
+
const { container } = render(
|
|
71
|
+
<CodeComponent className="language-go">
|
|
72
|
+
{"func main() {}"}
|
|
73
|
+
</CodeComponent>,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const code = container.querySelector("code");
|
|
77
|
+
expect(code).not.toBeNull();
|
|
78
|
+
expect(code!.className).toContain("hljs");
|
|
79
|
+
expect(code!.className).toContain("language-go");
|
|
80
|
+
expect(
|
|
81
|
+
container.querySelectorAll('span[class*="hljs-"]').length,
|
|
82
|
+
).toBeGreaterThan(0);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("falls back to flat text for an unknown language (no token spans)", () => {
|
|
86
|
+
const { container } = render(
|
|
87
|
+
<CodeComponent className="language-hcl">
|
|
88
|
+
{'resource "x" {}'}
|
|
89
|
+
</CodeComponent>,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const code = container.querySelector("code");
|
|
93
|
+
expect(code).not.toBeNull();
|
|
94
|
+
expect(code!.textContent).toContain('resource "x" {}');
|
|
95
|
+
expect(container.querySelectorAll('span[class*="hljs-"]').length).toBe(0);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("falls back to flat for non-string children (e.g. a streaming caret)", () => {
|
|
99
|
+
const { container } = render(
|
|
100
|
+
<CodeComponent className="language-go">
|
|
101
|
+
<span data-testid="caret">x</span>
|
|
102
|
+
</CodeComponent>,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Renders children untouched, no throw, no tokenization attempted.
|
|
106
|
+
expect(container.querySelector('[data-testid="caret"]')).not.toBeNull();
|
|
107
|
+
expect(container.querySelectorAll('span[class*="hljs-"]').length).toBe(0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("leaves inline code unhighlighted and unboxed by hljs", () => {
|
|
111
|
+
const { container } = render(<CodeComponent>{"inlineToken"}</CodeComponent>);
|
|
112
|
+
|
|
113
|
+
const code = container.querySelector("code");
|
|
114
|
+
expect(code).not.toBeNull();
|
|
115
|
+
expect(code!.className).not.toContain("hljs");
|
|
116
|
+
expect(code!.className).toContain("bg-muted");
|
|
117
|
+
expect(code!.textContent).toBe("inlineToken");
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { act, renderHook } from "@testing-library/react";
|
|
3
|
+
import { useAutoDisclosure } from "../useAutoDisclosure";
|
|
4
|
+
|
|
5
|
+
describe("useAutoDisclosure", () => {
|
|
6
|
+
it("starts open when autoOpen is true", () => {
|
|
7
|
+
const { result } = renderHook(() => useAutoDisclosure(true));
|
|
8
|
+
expect(result.current[0]).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("starts closed when autoOpen is false", () => {
|
|
12
|
+
const { result } = renderHook(() => useAutoDisclosure(false));
|
|
13
|
+
expect(result.current[0]).toBe(false);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("honours initialOpen over autoOpen for the first render", () => {
|
|
17
|
+
const { result } = renderHook(() =>
|
|
18
|
+
useAutoDisclosure(false, { initialOpen: true }),
|
|
19
|
+
);
|
|
20
|
+
expect(result.current[0]).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("tracks autoOpen while the user has not toggled", () => {
|
|
24
|
+
const { result, rerender } = renderHook(
|
|
25
|
+
({ autoOpen }) => useAutoDisclosure(autoOpen),
|
|
26
|
+
{ initialProps: { autoOpen: true } },
|
|
27
|
+
);
|
|
28
|
+
expect(result.current[0]).toBe(true);
|
|
29
|
+
|
|
30
|
+
rerender({ autoOpen: false });
|
|
31
|
+
expect(result.current[0]).toBe(false);
|
|
32
|
+
|
|
33
|
+
rerender({ autoOpen: true });
|
|
34
|
+
expect(result.current[0]).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("freezes on the user's choice once toggled, ignoring autoOpen", () => {
|
|
38
|
+
const { result, rerender } = renderHook(
|
|
39
|
+
({ autoOpen }) => useAutoDisclosure(autoOpen),
|
|
40
|
+
{ initialProps: { autoOpen: true } },
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// User collapses while the section is auto-open.
|
|
44
|
+
act(() => result.current[1]());
|
|
45
|
+
expect(result.current[0]).toBe(false);
|
|
46
|
+
|
|
47
|
+
// Auto signal flips both ways — user's collapse must persist.
|
|
48
|
+
rerender({ autoOpen: false });
|
|
49
|
+
expect(result.current[0]).toBe(false);
|
|
50
|
+
rerender({ autoOpen: true });
|
|
51
|
+
expect(result.current[0]).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("toggle flips from a user-opened state too", () => {
|
|
55
|
+
const { result } = renderHook(() => useAutoDisclosure(false));
|
|
56
|
+
expect(result.current[0]).toBe(false);
|
|
57
|
+
|
|
58
|
+
act(() => result.current[1]());
|
|
59
|
+
expect(result.current[0]).toBe(true);
|
|
60
|
+
|
|
61
|
+
act(() => result.current[1]());
|
|
62
|
+
expect(result.current[0]).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("keeps a stable toggle identity across renders", () => {
|
|
66
|
+
const { result, rerender } = renderHook(
|
|
67
|
+
({ autoOpen }) => useAutoDisclosure(autoOpen),
|
|
68
|
+
{ initialProps: { autoOpen: false } },
|
|
69
|
+
);
|
|
70
|
+
const first = result.current[1];
|
|
71
|
+
rerender({ autoOpen: true });
|
|
72
|
+
expect(result.current[1]).toBe(first);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { renderHook, act, cleanup } from "@testing-library/react";
|
|
3
|
+
import { useElapsedSince, formatElapsed } from "../useElapsedSince";
|
|
4
|
+
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
vi.useFakeTimers();
|
|
7
|
+
vi.setSystemTime(new Date("2026-07-02T12:00:30Z"));
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
cleanup();
|
|
12
|
+
vi.useRealTimers();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe("useElapsedSince", () => {
|
|
16
|
+
it("returns the elapsed ms since the timestamp", () => {
|
|
17
|
+
const { result } = renderHook(() =>
|
|
18
|
+
useElapsedSince("2026-07-02T12:00:00Z"),
|
|
19
|
+
);
|
|
20
|
+
expect(result.current).toBe(30_000);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("ticks forward once per second", () => {
|
|
24
|
+
const { result } = renderHook(() =>
|
|
25
|
+
useElapsedSince("2026-07-02T12:00:00Z"),
|
|
26
|
+
);
|
|
27
|
+
act(() => {
|
|
28
|
+
vi.advanceTimersByTime(3_000);
|
|
29
|
+
});
|
|
30
|
+
expect(result.current).toBe(33_000);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("clamps to zero when the timestamp is ahead of the client clock", () => {
|
|
34
|
+
const { result } = renderHook(() =>
|
|
35
|
+
useElapsedSince("2026-07-02T12:00:35Z"),
|
|
36
|
+
);
|
|
37
|
+
expect(result.current).toBe(0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("returns null for an empty timestamp", () => {
|
|
41
|
+
const { result } = renderHook(() => useElapsedSince(""));
|
|
42
|
+
expect(result.current).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("returns null for an unparseable timestamp", () => {
|
|
46
|
+
const { result } = renderHook(() => useElapsedSince("not-a-date"));
|
|
47
|
+
expect(result.current).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("tears down its interval on unmount", () => {
|
|
51
|
+
const { unmount } = renderHook(() =>
|
|
52
|
+
useElapsedSince("2026-07-02T12:00:00Z"),
|
|
53
|
+
);
|
|
54
|
+
unmount();
|
|
55
|
+
expect(vi.getTimerCount()).toBe(0);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("formatElapsed", () => {
|
|
60
|
+
it("formats each magnitude", () => {
|
|
61
|
+
expect(formatElapsed(500)).toBe("just now");
|
|
62
|
+
expect(formatElapsed(42_000)).toBe("42s");
|
|
63
|
+
expect(formatElapsed(3 * 60_000 + 12_000)).toBe("3m 12s");
|
|
64
|
+
expect(formatElapsed(5 * 60_000)).toBe("5m");
|
|
65
|
+
expect(formatElapsed(65 * 60_000)).toBe("1h 5m");
|
|
66
|
+
expect(formatElapsed(120 * 60_000)).toBe("2h");
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
+
import { createElement } from "react";
|
|
3
|
+
import { render, cleanup } from "@testing-library/react";
|
|
4
|
+
import { useIsOverflowing } from "../useIsOverflowing";
|
|
5
|
+
|
|
6
|
+
afterEach(cleanup);
|
|
7
|
+
|
|
8
|
+
function Probe({
|
|
9
|
+
enabled,
|
|
10
|
+
onState,
|
|
11
|
+
}: {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
onState: (overflowing: boolean) => void;
|
|
14
|
+
}) {
|
|
15
|
+
const { ref, isOverflowing } = useIsOverflowing<HTMLDivElement>(enabled);
|
|
16
|
+
onState(isOverflowing);
|
|
17
|
+
return createElement("div", { ref }, "content");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// happy-dom does not compute layout (scrollHeight === clientHeight === 0), so the
|
|
21
|
+
// deterministic contract here is the `enabled` gate and the not-overflowing
|
|
22
|
+
// baseline; the clipped (overflowing) case is verified against a real browser in
|
|
23
|
+
// the e2e layer, mirroring the SDK's documented unit/e2e split.
|
|
24
|
+
describe("useIsOverflowing", () => {
|
|
25
|
+
it("reports not-overflowing when disabled (observer never attached)", () => {
|
|
26
|
+
let state = true;
|
|
27
|
+
render(createElement(Probe, { enabled: false, onState: (s) => (state = s) }));
|
|
28
|
+
expect(state).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("reports not-overflowing for content that fits its container", () => {
|
|
32
|
+
let state = true;
|
|
33
|
+
render(createElement(Probe, { enabled: true, onState: (s) => (state = s) }));
|
|
34
|
+
expect(state).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { renderHook, act } from "@testing-library/react";
|
|
3
|
+
import { useKeyedSubmission } from "../useKeyedSubmission";
|
|
4
|
+
|
|
5
|
+
describe("useKeyedSubmission", () => {
|
|
6
|
+
it("tracks a key while its operation is in flight, then releases it", async () => {
|
|
7
|
+
let resolve!: () => void;
|
|
8
|
+
const { result } = renderHook(() => useKeyedSubmission<void>());
|
|
9
|
+
|
|
10
|
+
let pending: Promise<void>;
|
|
11
|
+
act(() => {
|
|
12
|
+
pending = result.current.run("k-1", () => new Promise<void>((r) => (resolve = r)));
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
expect(result.current.submittingKeys.has("k-1")).toBe(true);
|
|
16
|
+
|
|
17
|
+
await act(async () => {
|
|
18
|
+
resolve();
|
|
19
|
+
await pending;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
expect(result.current.submittingKeys.size).toBe(0);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("records a failure keyed, releases the in-flight key, then rethrows", async () => {
|
|
26
|
+
const { result } = renderHook(() => useKeyedSubmission<void>());
|
|
27
|
+
|
|
28
|
+
await act(async () => {
|
|
29
|
+
await expect(
|
|
30
|
+
result.current.run("k-1", () => Promise.reject(new Error("boom"))),
|
|
31
|
+
).rejects.toThrow("boom");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
expect(result.current.errorsByKey.get("k-1")?.message).toBe("boom");
|
|
35
|
+
expect(result.current.submittingKeys.size).toBe(0);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("coerces a non-Error rejection into an Error", async () => {
|
|
39
|
+
const { result } = renderHook(() => useKeyedSubmission<void>());
|
|
40
|
+
|
|
41
|
+
await act(async () => {
|
|
42
|
+
await expect(
|
|
43
|
+
result.current.run("k-1", () => Promise.reject("plain string")),
|
|
44
|
+
).rejects.toBe("plain string");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const recorded = result.current.errorsByKey.get("k-1");
|
|
48
|
+
expect(recorded).toBeInstanceOf(Error);
|
|
49
|
+
expect(recorded?.message).toBe("plain string");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("keys two failures independently — one never clobbers another", async () => {
|
|
53
|
+
const { result } = renderHook(() => useKeyedSubmission<void>());
|
|
54
|
+
|
|
55
|
+
await act(async () => {
|
|
56
|
+
await expect(
|
|
57
|
+
result.current.run("k-a", () => Promise.reject(new Error("fail-a"))),
|
|
58
|
+
).rejects.toThrow("fail-a");
|
|
59
|
+
});
|
|
60
|
+
await act(async () => {
|
|
61
|
+
await expect(
|
|
62
|
+
result.current.run("k-b", () => Promise.reject(new Error("fail-b"))),
|
|
63
|
+
).rejects.toThrow("fail-b");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(result.current.errorsByKey.get("k-a")?.message).toBe("fail-a");
|
|
67
|
+
expect(result.current.errorsByKey.get("k-b")?.message).toBe("fail-b");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("clears a key's prior error at retry-start, leaving siblings untouched", async () => {
|
|
71
|
+
const { result } = renderHook(() => useKeyedSubmission<void>());
|
|
72
|
+
|
|
73
|
+
await act(async () => {
|
|
74
|
+
await expect(
|
|
75
|
+
result.current.run("k-a", () => Promise.reject(new Error("fail-a"))),
|
|
76
|
+
).rejects.toThrow("fail-a");
|
|
77
|
+
});
|
|
78
|
+
await act(async () => {
|
|
79
|
+
await expect(
|
|
80
|
+
result.current.run("k-b", () => Promise.reject(new Error("fail-b"))),
|
|
81
|
+
).rejects.toThrow("fail-b");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Retrying k-a succeeds: its error clears at run-start; k-b's persists.
|
|
85
|
+
await act(async () => {
|
|
86
|
+
await result.current.run("k-a", () => Promise.resolve());
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(result.current.errorsByKey.has("k-a")).toBe(false);
|
|
90
|
+
expect(result.current.errorsByKey.get("k-b")?.message).toBe("fail-b");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("returns the resolved value on success", async () => {
|
|
94
|
+
const { result } = renderHook(() => useKeyedSubmission<number>());
|
|
95
|
+
|
|
96
|
+
let value: number | undefined;
|
|
97
|
+
await act(async () => {
|
|
98
|
+
value = await result.current.run("k-1", () => Promise.resolve(42));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(value).toBe(42);
|
|
102
|
+
expect(result.current.errorsByKey.size).toBe(0);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("clearErrors resets every recorded error", async () => {
|
|
106
|
+
const { result } = renderHook(() => useKeyedSubmission<void>());
|
|
107
|
+
|
|
108
|
+
await act(async () => {
|
|
109
|
+
await expect(
|
|
110
|
+
result.current.run("k-a", () => Promise.reject(new Error("fail-a"))),
|
|
111
|
+
).rejects.toThrow("fail-a");
|
|
112
|
+
});
|
|
113
|
+
expect(result.current.errorsByKey.size).toBe(1);
|
|
114
|
+
|
|
115
|
+
act(() => result.current.clearErrors());
|
|
116
|
+
expect(result.current.errorsByKey.size).toBe(0);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("keeps a stable return reference across renders when nothing changes", () => {
|
|
120
|
+
const { result, rerender } = renderHook(() => useKeyedSubmission<void>());
|
|
121
|
+
|
|
122
|
+
const first = result.current;
|
|
123
|
+
rerender();
|
|
124
|
+
expect(result.current).toBe(first);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Fragment, type ReactNode } from "react";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createLowlight } from "lowlight";
|
|
4
|
+
import { toJsxRuntime } from "hast-util-to-jsx-runtime";
|
|
5
|
+
|
|
6
|
+
// Dependency licensing (DD-012): `lowlight` and `hast-util-to-jsx-runtime` are
|
|
7
|
+
// MIT; `highlight.js` is BSD-3-Clause. BSD-3-Clause is a permissive, OSI-approved
|
|
8
|
+
// license, compatible with MIT/Apache-2.0, that imposes no obligations on SDK
|
|
9
|
+
// consumers beyond attribution — so it satisfies DD-012's "MIT or Apache-2.0
|
|
10
|
+
// compatible" rule. Recorded here so the choice is auditable at the point of use.
|
|
11
|
+
import bash from "highlight.js/lib/languages/bash";
|
|
12
|
+
import css from "highlight.js/lib/languages/css";
|
|
13
|
+
import dockerfile from "highlight.js/lib/languages/dockerfile";
|
|
14
|
+
import go from "highlight.js/lib/languages/go";
|
|
15
|
+
import java from "highlight.js/lib/languages/java";
|
|
16
|
+
import javascript from "highlight.js/lib/languages/javascript";
|
|
17
|
+
import json from "highlight.js/lib/languages/json";
|
|
18
|
+
import markdown from "highlight.js/lib/languages/markdown";
|
|
19
|
+
import python from "highlight.js/lib/languages/python";
|
|
20
|
+
import rust from "highlight.js/lib/languages/rust";
|
|
21
|
+
import sql from "highlight.js/lib/languages/sql";
|
|
22
|
+
import typescript from "highlight.js/lib/languages/typescript";
|
|
23
|
+
import xml from "highlight.js/lib/languages/xml";
|
|
24
|
+
import yaml from "highlight.js/lib/languages/yaml";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Syntax highlighter for fenced code blocks in SDK markdown surfaces.
|
|
28
|
+
*
|
|
29
|
+
* This is the single highlighting engine behind the shared `code` override in
|
|
30
|
+
* {@link file://./markdown-components.tsx} — so chat (Streamdown) and the
|
|
31
|
+
* artifact/skill viewers (react-markdown) colorize code identically.
|
|
32
|
+
*
|
|
33
|
+
* The engine emits highlight.js token classes (`hljs-keyword`, `hljs-string`,
|
|
34
|
+
* …) which `styles.css` maps onto the `--stgm-syntax-*` theme tokens. Colors
|
|
35
|
+
* are therefore 100% token-driven: they track the host's preset and color-mode
|
|
36
|
+
* with no hardcoded values, exactly like the CodeMirror YAML editor.
|
|
37
|
+
*
|
|
38
|
+
* **Eager, not lazy.** Highlighting is on the core path (almost every
|
|
39
|
+
* `SessionViewer` consumer renders agent messages containing code), so DD-013's
|
|
40
|
+
* lazy pattern (for rarely-used heavy deps) does not apply. The grammars are
|
|
41
|
+
* imported eagerly; because this module is only reachable through the markdown
|
|
42
|
+
* components, normal tree-shaking still keeps it out of bundles that never
|
|
43
|
+
* render markdown.
|
|
44
|
+
*
|
|
45
|
+
* **Curated grammar set.** Only the languages agents commonly emit are
|
|
46
|
+
* registered, to keep the payload small. Anything else falls back to flat
|
|
47
|
+
* rendering (see {@link resolveLanguage}) — a deterministic choice, never
|
|
48
|
+
* highlight.js auto-detection, consistent with this codebase's avoidance of
|
|
49
|
+
* fuzzy heuristics.
|
|
50
|
+
*/
|
|
51
|
+
const lowlight = createLowlight({
|
|
52
|
+
bash,
|
|
53
|
+
css,
|
|
54
|
+
dockerfile,
|
|
55
|
+
go,
|
|
56
|
+
java,
|
|
57
|
+
javascript,
|
|
58
|
+
json,
|
|
59
|
+
markdown,
|
|
60
|
+
python,
|
|
61
|
+
rust,
|
|
62
|
+
sql,
|
|
63
|
+
typescript,
|
|
64
|
+
xml,
|
|
65
|
+
yaml,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Fence info-strings agents use that aren't already registered as grammar
|
|
69
|
+
// aliases by highlight.js. `xml` is highlight.js's grammar for HTML markup.
|
|
70
|
+
lowlight.registerAlias({
|
|
71
|
+
bash: ["sh", "shell", "zsh", "console"],
|
|
72
|
+
dockerfile: ["docker"],
|
|
73
|
+
javascript: ["js", "jsx", "mjs", "cjs"],
|
|
74
|
+
markdown: ["md"],
|
|
75
|
+
python: ["py"],
|
|
76
|
+
typescript: ["ts", "tsx"],
|
|
77
|
+
xml: ["html"],
|
|
78
|
+
yaml: ["yml"],
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const jsxRuntime = { Fragment, jsx, jsxs } as const;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Resolves a fence info-string to a registered highlight.js language name, or
|
|
85
|
+
* `null` when no grammar is registered for it (the caller then renders the code
|
|
86
|
+
* flat). Case-insensitive and whitespace-tolerant; never guesses.
|
|
87
|
+
*
|
|
88
|
+
* @param language - The raw language from a `language-*` class (e.g. `"go"`).
|
|
89
|
+
* @returns The normalized, registered language name, or `null`.
|
|
90
|
+
*/
|
|
91
|
+
export function resolveLanguage(language: string | undefined): string | null {
|
|
92
|
+
if (!language) return null;
|
|
93
|
+
const name = language.trim().toLowerCase();
|
|
94
|
+
return name && lowlight.registered(name) ? name : null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Highlights `code` for the given fence `language` and returns themed React
|
|
99
|
+
* nodes — token `<span>`s carrying `hljs-*` classes that `styles.css` maps to
|
|
100
|
+
* `--stgm-syntax-*`.
|
|
101
|
+
*
|
|
102
|
+
* Returns `null` (so the caller can fall back to flat rendering) when the
|
|
103
|
+
* language has no registered grammar or tokenization throws. Highlighting is
|
|
104
|
+
* synchronous: there is no loading state to handle.
|
|
105
|
+
*
|
|
106
|
+
* @param code - The raw source text of the fenced block.
|
|
107
|
+
* @param language - The fence language (e.g. `"ts"`); unknown → `null`.
|
|
108
|
+
*/
|
|
109
|
+
export function highlightToReact(
|
|
110
|
+
code: string,
|
|
111
|
+
language: string | undefined,
|
|
112
|
+
): ReactNode | null {
|
|
113
|
+
const resolved = resolveLanguage(language);
|
|
114
|
+
if (resolved === null) return null;
|
|
115
|
+
try {
|
|
116
|
+
return toJsxRuntime(lowlight.highlight(resolved, code), jsxRuntime);
|
|
117
|
+
} catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -2,6 +2,9 @@ import type { ComponentProps, JSX } from "react";
|
|
|
2
2
|
import type { Components } from "react-markdown";
|
|
3
3
|
import remarkGfm from "remark-gfm";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { highlightToReact } from "./code-highlight";
|
|
6
|
+
|
|
7
|
+
const LANGUAGE_CLASS_PREFIX = "language-";
|
|
5
8
|
|
|
6
9
|
type MdProps<T extends keyof JSX.IntrinsicElements> = ComponentProps<T>;
|
|
7
10
|
|
|
@@ -163,15 +166,29 @@ export const MARKDOWN_COMPONENTS: Components = {
|
|
|
163
166
|
|
|
164
167
|
code({ children, className: codeClassName, ...props }: MdProps<"code">) {
|
|
165
168
|
const isBlock =
|
|
166
|
-
typeof codeClassName === "string" &&
|
|
169
|
+
typeof codeClassName === "string" &&
|
|
170
|
+
codeClassName.startsWith(LANGUAGE_CLASS_PREFIX);
|
|
167
171
|
|
|
168
172
|
if (isBlock) {
|
|
173
|
+
// Tokenize here, in the one component both renderers (Streamdown for
|
|
174
|
+
// chat, react-markdown for artifacts/skills) share, so highlighting is
|
|
175
|
+
// identical everywhere. Highlight only plain-string children — anything
|
|
176
|
+
// else (e.g. a streaming caret node) falls back to flat rendering.
|
|
177
|
+
const language = codeClassName.slice(LANGUAGE_CLASS_PREFIX.length);
|
|
178
|
+
const highlighted =
|
|
179
|
+
typeof children === "string"
|
|
180
|
+
? highlightToReact(children, language)
|
|
181
|
+
: null;
|
|
182
|
+
|
|
169
183
|
return (
|
|
170
184
|
<code
|
|
171
|
-
className={cn(
|
|
185
|
+
className={cn(
|
|
186
|
+
"hljs font-mono text-xs text-foreground",
|
|
187
|
+
codeClassName,
|
|
188
|
+
)}
|
|
172
189
|
{...props}
|
|
173
190
|
>
|
|
174
|
-
{children}
|
|
191
|
+
{highlighted ?? children}
|
|
175
192
|
</code>
|
|
176
193
|
);
|
|
177
194
|
}
|