@stigmer/react 3.1.2 → 3.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/composer/SessionComposer.d.ts +37 -5
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +13 -5
- package/composer/SessionComposer.js.map +1 -1
- package/execution/ArtifactApplyButton.d.ts +25 -0
- package/execution/ArtifactApplyButton.d.ts.map +1 -0
- package/execution/ArtifactApplyButton.js +21 -0
- package/execution/ArtifactApplyButton.js.map +1 -0
- package/execution/ArtifactContentBody.d.ts +36 -0
- package/execution/ArtifactContentBody.d.ts.map +1 -0
- package/execution/ArtifactContentBody.js +62 -0
- package/execution/ArtifactContentBody.js.map +1 -0
- package/execution/ArtifactDocument.d.ts +48 -0
- package/execution/ArtifactDocument.d.ts.map +1 -0
- package/execution/ArtifactDocument.js +89 -0
- package/execution/ArtifactDocument.js.map +1 -0
- package/execution/ArtifactPreviewModal.d.ts +9 -9
- package/execution/ArtifactPreviewModal.d.ts.map +1 -1
- package/execution/ArtifactPreviewModal.js +18 -145
- package/execution/ArtifactPreviewModal.js.map +1 -1
- package/execution/ArtifactRow.d.ts +49 -0
- package/execution/ArtifactRow.d.ts.map +1 -0
- package/execution/ArtifactRow.js +56 -0
- package/execution/ArtifactRow.js.map +1 -0
- package/execution/ArtifactsWidget.d.ts +6 -9
- package/execution/ArtifactsWidget.d.ts.map +1 -1
- package/execution/ArtifactsWidget.js +15 -19
- package/execution/ArtifactsWidget.js.map +1 -1
- package/execution/FileChangeProgressBar.d.ts +47 -0
- package/execution/FileChangeProgressBar.d.ts.map +1 -0
- package/execution/FileChangeProgressBar.js +49 -0
- package/execution/FileChangeProgressBar.js.map +1 -0
- package/execution/FileChangesView.d.ts.map +1 -1
- package/execution/FileChangesView.js +19 -4
- package/execution/FileChangesView.js.map +1 -1
- package/execution/FileReviewAtoms.d.ts +27 -0
- package/execution/FileReviewAtoms.d.ts.map +1 -0
- package/execution/FileReviewAtoms.js +64 -0
- package/execution/FileReviewAtoms.js.map +1 -0
- package/execution/FileReviewCard.d.ts +2 -2
- package/execution/FileReviewCard.d.ts.map +1 -1
- package/execution/FileReviewCard.js +32 -62
- package/execution/FileReviewCard.js.map +1 -1
- package/execution/MessageEntry.d.ts +0 -9
- package/execution/MessageEntry.d.ts.map +1 -1
- package/execution/MessageEntry.js +1 -11
- package/execution/MessageEntry.js.map +1 -1
- package/execution/MessageThread.d.ts +3 -4
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +25 -11
- package/execution/MessageThread.js.map +1 -1
- package/execution/PlanArtifactCard.d.ts +16 -10
- package/execution/PlanArtifactCard.d.ts.map +1 -1
- package/execution/PlanArtifactCard.js +31 -7
- package/execution/PlanArtifactCard.js.map +1 -1
- package/execution/PlanCompletionCard.d.ts +4 -4
- package/execution/PlanCompletionCard.d.ts.map +1 -1
- package/execution/PlanCompletionCard.js +4 -4
- package/execution/PlanCompletionCard.js.map +1 -1
- package/execution/PlanStreamingCard.d.ts +7 -2
- package/execution/PlanStreamingCard.d.ts.map +1 -1
- package/execution/PlanStreamingCard.js +11 -6
- package/execution/PlanStreamingCard.js.map +1 -1
- package/execution/index.d.ts +12 -2
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +6 -1
- package/execution/index.js.map +1 -1
- package/execution/use-build-from-plan-hotkey.d.ts +1 -1
- package/execution/use-build-from-plan-hotkey.js +1 -1
- package/execution/useArtifactContent.d.ts +6 -1
- package/execution/useArtifactContent.d.ts.map +1 -1
- package/execution/useArtifactContent.js +7 -2
- package/execution/useArtifactContent.js.map +1 -1
- package/execution/useArtifactCopy.d.ts +51 -0
- package/execution/useArtifactCopy.d.ts.map +1 -0
- package/execution/useArtifactCopy.js +75 -0
- package/execution/useArtifactCopy.js.map +1 -0
- package/execution/useArtifactDownload.d.ts.map +1 -1
- package/execution/useArtifactDownload.js +17 -5
- package/execution/useArtifactDownload.js.map +1 -1
- package/execution/useArtifactInspection.d.ts +85 -0
- package/execution/useArtifactInspection.d.ts.map +1 -0
- package/execution/useArtifactInspection.js +143 -0
- package/execution/useArtifactInspection.js.map +1 -0
- package/execution/useCreateAgentExecution.d.ts +16 -3
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js +1 -0
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/index.d.ts +4 -4
- package/index.d.ts.map +1 -1
- package/index.js +3 -3
- package/index.js.map +1 -1
- package/internal/MermaidDiagram.d.ts +47 -0
- package/internal/MermaidDiagram.d.ts.map +1 -0
- package/internal/MermaidDiagram.js +118 -0
- package/internal/MermaidDiagram.js.map +1 -0
- package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
- package/internal/file-tree/FileTreeNode.js +2 -2
- package/internal/file-tree/FileTreeNode.js.map +1 -1
- package/internal/markdown-components.d.ts.map +1 -1
- package/internal/markdown-components.js +34 -0
- package/internal/markdown-components.js.map +1 -1
- package/internal/mermaid-loader.d.ts +26 -0
- package/internal/mermaid-loader.d.ts.map +1 -0
- package/internal/mermaid-loader.js +34 -0
- package/internal/mermaid-loader.js.map +1 -0
- package/internal/mermaid-theme-css.d.ts +41 -0
- package/internal/mermaid-theme-css.d.ts.map +1 -0
- package/internal/mermaid-theme-css.js +179 -0
- package/internal/mermaid-theme-css.js.map +1 -0
- package/internal/slug.d.ts +9 -5
- package/internal/slug.d.ts.map +1 -1
- package/internal/slug.js +10 -6
- package/internal/slug.js.map +1 -1
- package/library/detect-plan-artifact.d.ts +27 -9
- package/library/detect-plan-artifact.d.ts.map +1 -1
- package/library/detect-plan-artifact.js +26 -6
- package/library/detect-plan-artifact.js.map +1 -1
- package/library/index.d.ts +1 -1
- package/library/index.d.ts.map +1 -1
- package/library/index.js +1 -1
- package/library/index.js.map +1 -1
- package/package.json +5 -4
- package/session/PlanEditor.js +1 -1
- package/session/PlanEditor.js.map +1 -1
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +122 -30
- package/session/SessionViewer.js.map +1 -1
- package/session/artifact-document.d.ts +13 -0
- package/session/artifact-document.d.ts.map +1 -0
- package/session/artifact-document.js +22 -0
- package/session/artifact-document.js.map +1 -0
- package/session/facets/ArtifactsTab.d.ts +30 -4
- package/session/facets/ArtifactsTab.d.ts.map +1 -1
- package/session/facets/ArtifactsTab.js +32 -10
- package/session/facets/ArtifactsTab.js.map +1 -1
- package/session/index.d.ts +2 -1
- package/session/index.d.ts.map +1 -1
- package/session/index.js +4 -1
- package/session/index.js.map +1 -1
- package/session/plan-document.d.ts +14 -2
- package/session/plan-document.d.ts.map +1 -1
- package/session/plan-document.js +14 -3
- package/session/plan-document.js.map +1 -1
- package/session/usePlanDraft.d.ts +1 -1
- package/session/useSessionArtifacts.d.ts +17 -0
- package/session/useSessionArtifacts.d.ts.map +1 -1
- package/session/useSessionArtifacts.js +16 -5
- package/session/useSessionArtifacts.js.map +1 -1
- package/session/useSessionConversation.d.ts +26 -3
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +51 -6
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionPageFlow.d.ts.map +1 -1
- package/session/useSessionPageFlow.js +1 -0
- package/session/useSessionPageFlow.js.map +1 -1
- package/session/useSessionPanel.d.ts +20 -0
- package/session/useSessionPanel.d.ts.map +1 -1
- package/session/useSessionPanel.js +13 -0
- package/session/useSessionPanel.js.map +1 -1
- package/session/useSessionRailViews.d.ts +13 -1
- package/session/useSessionRailViews.d.ts.map +1 -1
- package/session/useSessionRailViews.js +5 -3
- package/session/useSessionRailViews.js.map +1 -1
- package/src/composer/SessionComposer.tsx +76 -7
- package/src/composer/__tests__/SessionComposer-editing.test.tsx +128 -0
- package/src/execution/ArtifactApplyButton.tsx +79 -0
- package/src/execution/ArtifactContentBody.tsx +255 -0
- package/src/execution/ArtifactDocument.tsx +344 -0
- package/src/execution/ArtifactPreviewModal.tsx +49 -415
- package/src/execution/ArtifactRow.tsx +165 -0
- package/src/execution/ArtifactsWidget.tsx +24 -33
- package/src/execution/FileChangeProgressBar.tsx +121 -0
- package/src/execution/FileChangesView.tsx +18 -4
- package/src/execution/FileReviewAtoms.tsx +89 -0
- package/src/execution/FileReviewCard.tsx +57 -90
- package/src/execution/MessageEntry.tsx +0 -62
- package/src/execution/MessageThread.tsx +29 -15
- package/src/execution/PlanArtifactCard.tsx +130 -52
- package/src/execution/PlanCompletionCard.tsx +5 -5
- package/src/execution/PlanStreamingCard.tsx +15 -11
- package/src/execution/__tests__/ArtifactContentBody.test.tsx +131 -0
- package/src/execution/__tests__/ArtifactDocument.test.tsx +100 -0
- package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +6 -6
- package/src/execution/__tests__/ArtifactRow.test.tsx +139 -0
- package/src/execution/__tests__/ArtifactsWidget.test.tsx +78 -0
- package/src/execution/__tests__/FileChangeProgressBar.test.tsx +125 -0
- package/src/execution/__tests__/FileReviewCard.test.tsx +66 -7
- package/src/execution/__tests__/MessageThread.test.tsx +1 -1
- package/src/execution/__tests__/PlanArtifactCard.test.tsx +52 -18
- package/src/execution/__tests__/PlanCompletionCard.test.tsx +2 -2
- package/src/execution/__tests__/PlanStreamingCard.test.tsx +11 -4
- package/src/execution/__tests__/buildThreadItems-plan-completion.test.ts +104 -8
- package/src/execution/__tests__/message-entry.test.tsx +56 -39
- package/src/execution/__tests__/useArtifactCopy.test.tsx +90 -0
- package/src/execution/__tests__/useArtifactDownload.test.tsx +2 -0
- package/src/execution/__tests__/useArtifactInspection.test.tsx +151 -0
- package/src/execution/__tests__/useCreateAgentExecution.test.tsx +35 -0
- package/src/execution/index.ts +20 -2
- package/src/execution/use-build-from-plan-hotkey.ts +1 -1
- package/src/execution/useArtifactContent.ts +7 -0
- package/src/execution/useArtifactCopy.ts +109 -0
- package/src/execution/useArtifactDownload.ts +17 -5
- package/src/execution/useArtifactInspection.ts +252 -0
- package/src/execution/useCreateAgentExecution.ts +17 -3
- package/src/index.ts +17 -2
- package/src/internal/MermaidDiagram.tsx +164 -0
- package/src/internal/__tests__/a11y/mermaid-theme.a11y.test.tsx +122 -0
- package/src/internal/__tests__/markdown-components.test.tsx +93 -1
- package/src/internal/__tests__/mermaid-diagram.test.tsx +282 -0
- package/src/internal/file-tree/FileTreeNode.tsx +13 -2
- package/src/internal/markdown-components.tsx +36 -1
- package/src/internal/mermaid-loader.ts +44 -0
- package/src/internal/mermaid-theme-css.ts +178 -0
- package/src/internal/slug.ts +10 -6
- package/src/library/__tests__/detect-plan-artifact.test.ts +21 -0
- package/src/library/detect-plan-artifact.ts +32 -10
- package/src/library/index.ts +2 -0
- package/src/session/PlanEditor.tsx +1 -1
- package/src/session/SessionViewer.tsx +227 -51
- package/src/session/__tests__/PlanEditor.test.tsx +4 -4
- package/src/session/__tests__/PlanStreamingDocument.test.tsx +1 -1
- package/src/session/__tests__/SessionViewer-artifactTabs.test.tsx +210 -0
- package/src/session/__tests__/SessionViewer-editResubmit.test.tsx +233 -0
- package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +57 -0
- package/src/session/__tests__/useSessionArtifacts.test.ts +57 -0
- package/src/session/__tests__/useSessionConversation.test.tsx +216 -1
- package/src/session/__tests__/useSessionPanel.test.ts +70 -1
- package/src/session/artifact-document.ts +23 -0
- package/src/session/facets/ArtifactsTab.tsx +66 -18
- package/src/session/facets/__tests__/ArtifactsTab.test.tsx +111 -47
- package/src/session/index.ts +5 -1
- package/src/session/plan-document.ts +14 -3
- package/src/session/usePlanDraft.ts +1 -1
- package/src/session/useSessionArtifacts.ts +16 -5
- package/src/session/useSessionConversation.ts +80 -10
- package/src/session/useSessionPageFlow.ts +1 -0
- package/src/session/useSessionPanel.ts +41 -0
- package/src/session/useSessionRailViews.tsx +21 -1
- package/src/workspace/EditorTabs.tsx +38 -9
- package/src/workspace/ExplorerTree.tsx +10 -4
- package/src/workspace/FileViewer.tsx +27 -6
- package/src/workspace/WorkspaceContentSearch.tsx +44 -20
- package/src/workspace/WorkspaceEditor.tsx +19 -16
- package/src/workspace/WorkspaceFileSearch.tsx +51 -28
- package/src/workspace/WorkspaceSurface.tsx +106 -48
- package/src/workspace/__tests__/EditorTabs.test.tsx +4 -2
- package/src/workspace/__tests__/ExplorerTree.test.tsx +52 -0
- package/src/workspace/__tests__/FileViewer.test.tsx +18 -0
- package/src/workspace/__tests__/WorkspaceContentSearch.test.tsx +38 -5
- package/src/workspace/__tests__/WorkspaceEditor-initialPanel.test.tsx +19 -0
- package/src/workspace/__tests__/WorkspaceFileSearch.test.tsx +27 -6
- package/src/workspace/__tests__/WorkspaceSurface.test.tsx +70 -0
- package/src/workspace/__tests__/a11y/harness.tsx +140 -0
- package/src/workspace/__tests__/a11y/mock-capabilities.ts +138 -0
- package/src/workspace/__tests__/a11y/workspace-editor.a11y.test.tsx +65 -0
- package/src/workspace/__tests__/a11y/workspace-explorer-search.a11y.test.tsx +112 -0
- package/src/workspace/__tests__/a11y/workspace-viewer-tabs.a11y.test.tsx +69 -0
- package/styles.css +1 -1
- package/workspace/EditorTabs.d.ts +21 -3
- package/workspace/EditorTabs.d.ts.map +1 -1
- package/workspace/EditorTabs.js +18 -6
- package/workspace/EditorTabs.js.map +1 -1
- package/workspace/ExplorerTree.js +8 -4
- package/workspace/ExplorerTree.js.map +1 -1
- package/workspace/FileViewer.d.ts.map +1 -1
- package/workspace/FileViewer.js +19 -8
- package/workspace/FileViewer.js.map +1 -1
- package/workspace/WorkspaceContentSearch.d.ts.map +1 -1
- package/workspace/WorkspaceContentSearch.js +23 -2
- package/workspace/WorkspaceContentSearch.js.map +1 -1
- package/workspace/WorkspaceEditor.d.ts.map +1 -1
- package/workspace/WorkspaceEditor.js +8 -10
- package/workspace/WorkspaceEditor.js.map +1 -1
- package/workspace/WorkspaceFileSearch.d.ts.map +1 -1
- package/workspace/WorkspaceFileSearch.js +23 -3
- package/workspace/WorkspaceFileSearch.js.map +1 -1
- package/workspace/WorkspaceSurface.d.ts.map +1 -1
- package/workspace/WorkspaceSurface.js +54 -18
- package/workspace/WorkspaceSurface.js.map +1 -1
- package/execution/ArtifactCard.d.ts +0 -81
- package/execution/ArtifactCard.d.ts.map +0 -1
- package/execution/ArtifactCard.js +0 -130
- package/execution/ArtifactCard.js.map +0 -1
- package/src/execution/ArtifactCard.tsx +0 -320
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { memo, useState } from "react";
|
|
3
|
+
import { memo, useState, type ReactNode } from "react";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
5
|
import type { ExecutionArtifact } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
|
|
6
6
|
import { ArtifactPreviewModal } from "./ArtifactPreviewModal.js";
|
|
7
7
|
import { formatArtifactSize } from "./artifact-utils.js";
|
|
8
|
+
import { useArtifactCopy } from "./useArtifactCopy.js";
|
|
8
9
|
import { useArtifactDownload } from "./useArtifactDownload.js";
|
|
9
10
|
import { useBuildFromPlanHotkey } from "./use-build-from-plan-hotkey.js";
|
|
10
11
|
|
|
@@ -21,20 +22,20 @@ export interface PlanArtifactCardProps {
|
|
|
21
22
|
*/
|
|
22
23
|
readonly title?: string;
|
|
23
24
|
/**
|
|
24
|
-
* Organization slug. Required for the
|
|
25
|
+
* Organization slug. Required for the open-in-modal fallback (the shared
|
|
25
26
|
* {@link ArtifactPreviewModal} uses it for its detection/apply pipeline and
|
|
26
|
-
* to fetch the content). When omitted,
|
|
27
|
-
* "Build
|
|
27
|
+
* to fetch the content). When omitted, that fallback is hidden — the
|
|
28
|
+
* prominent "Build" action and the download icon remain.
|
|
28
29
|
*/
|
|
29
30
|
readonly org?: string;
|
|
30
|
-
/** Called when the user clicks "Build
|
|
31
|
+
/** Called when the user clicks "Build". Hidden when omitted. */
|
|
31
32
|
readonly onImplement?: () => void;
|
|
32
33
|
/**
|
|
33
34
|
* Opens the plan in the session panel's plan document tab — the
|
|
34
35
|
* side-by-side review/refine surface. When provided it REPLACES the
|
|
35
|
-
* modal-based
|
|
36
|
-
*
|
|
37
|
-
* modal
|
|
36
|
+
* modal-based open fallback (one review affordance, not two). When
|
|
37
|
+
* omitted (hosts without a session panel) the same "Open plan" icon
|
|
38
|
+
* opens the preview modal instead.
|
|
38
39
|
*/
|
|
39
40
|
readonly onOpenPlan?: () => void;
|
|
40
41
|
/** Disables the primary CTA (e.g., while an execution is active). */
|
|
@@ -52,9 +53,15 @@ export interface PlanArtifactCardProps {
|
|
|
52
53
|
* the panel carries the document.
|
|
53
54
|
*
|
|
54
55
|
* Anatomy: a document-style identity block (plan icon, the plan's title, a
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
56
|
+
* `<name> · size` meta line) with the actions trailing — open, copy, and
|
|
57
|
+
* download as icon-only secondaries (labelled via `aria-label`/`title`) and
|
|
58
|
+
* one prominent, themeable primary, **Build** — so the next step is
|
|
59
|
+
* unmistakable.
|
|
60
|
+
*
|
|
61
|
+
* Action-label rule (shared with `PlanStreamingCard`, which keeps its
|
|
62
|
+
* labelled "Open plan"): a lone secondary keeps its text label; a row of
|
|
63
|
+
* secondaries competing with a primary collapses to icons, so the primary
|
|
64
|
+
* carries the card's one visible verb.
|
|
58
65
|
*
|
|
59
66
|
* Only the LATEST plan's card carries `onImplement`; a superseded plan's card
|
|
60
67
|
* is review-only (its `onOpenPlan` opens the document read-only), so a stale
|
|
@@ -76,6 +83,7 @@ export const PlanArtifactCard = memo(function PlanArtifactCard({
|
|
|
76
83
|
}: PlanArtifactCardProps) {
|
|
77
84
|
const [previewOpen, setPreviewOpen] = useState(false);
|
|
78
85
|
const handleKeyDown = useBuildFromPlanHotkey(onImplement, disabled);
|
|
86
|
+
const { copy, isCopying, copied } = useArtifactCopy(executionId);
|
|
79
87
|
const { download, isDownloading } = useArtifactDownload(executionId);
|
|
80
88
|
|
|
81
89
|
return (
|
|
@@ -103,53 +111,41 @@ export const PlanArtifactCard = memo(function PlanArtifactCard({
|
|
|
103
111
|
</div>
|
|
104
112
|
</div>
|
|
105
113
|
|
|
106
|
-
<div className="ml-auto flex flex-wrap items-center gap-x-
|
|
114
|
+
<div className="ml-auto flex flex-wrap items-center gap-x-1.5 gap-y-1.5">
|
|
107
115
|
{onOpenPlan ? (
|
|
108
|
-
<
|
|
109
|
-
|
|
116
|
+
<IconAction
|
|
117
|
+
label="Open plan"
|
|
110
118
|
onClick={onOpenPlan}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
FOCUS_RING,
|
|
114
|
-
)}
|
|
115
|
-
>
|
|
116
|
-
<ExpandIcon />
|
|
117
|
-
Open plan
|
|
118
|
-
</button>
|
|
119
|
+
icon={<EyeIcon />}
|
|
120
|
+
/>
|
|
119
121
|
) : (
|
|
120
122
|
org && (
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
+
<IconAction
|
|
124
|
+
label="Open plan"
|
|
123
125
|
onClick={() => setPreviewOpen(true)}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
FOCUS_RING,
|
|
127
|
-
)}
|
|
128
|
-
>
|
|
129
|
-
<ExpandIcon />
|
|
130
|
-
Open full
|
|
131
|
-
</button>
|
|
126
|
+
icon={<EyeIcon />}
|
|
127
|
+
/>
|
|
132
128
|
)
|
|
133
129
|
)}
|
|
134
|
-
<
|
|
135
|
-
|
|
130
|
+
<IconAction
|
|
131
|
+
label={copied ? "Copied" : isCopying ? "Copying…" : "Copy plan"}
|
|
132
|
+
onClick={() => copy(artifact.storageKey)}
|
|
133
|
+
disabled={isCopying}
|
|
134
|
+
icon={copied ? <CheckIcon /> : <CopyIcon />}
|
|
135
|
+
/>
|
|
136
|
+
<IconAction
|
|
137
|
+
label={isDownloading ? "Preparing…" : `Download ${artifact.name}`}
|
|
136
138
|
onClick={() => download(artifact.storageKey, artifact.name)}
|
|
137
139
|
disabled={isDownloading}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
FOCUS_RING,
|
|
141
|
-
)}
|
|
142
|
-
>
|
|
143
|
-
<DownloadIcon />
|
|
144
|
-
{isDownloading ? "Preparing…" : "Download"}
|
|
145
|
-
</button>
|
|
140
|
+
icon={<DownloadIcon />}
|
|
141
|
+
/>
|
|
146
142
|
{onImplement && (
|
|
147
143
|
<button
|
|
148
144
|
type="button"
|
|
149
145
|
disabled={disabled}
|
|
150
146
|
onClick={onImplement}
|
|
151
147
|
className={cn(
|
|
152
|
-
"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5",
|
|
148
|
+
"ml-1.5 inline-flex items-center gap-1.5 rounded-md px-3 py-1.5",
|
|
153
149
|
"text-xs font-medium transition-colors",
|
|
154
150
|
"bg-primary text-primary-foreground hover:bg-primary-hover",
|
|
155
151
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
@@ -157,13 +153,18 @@ export const PlanArtifactCard = memo(function PlanArtifactCard({
|
|
|
157
153
|
)}
|
|
158
154
|
>
|
|
159
155
|
<ImplementIcon />
|
|
160
|
-
{buildPending ? "Starting build…" : "Build
|
|
156
|
+
{buildPending ? "Starting build…" : "Build"}
|
|
161
157
|
</button>
|
|
162
158
|
)}
|
|
163
159
|
</div>
|
|
164
160
|
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
<div role="status" aria-live="polite" aria-atomic="true" className="sr-only">
|
|
162
|
+
{copied && "Plan copied to clipboard"}
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
{/* The modal open fallback reuses the shared artifact preview popup —
|
|
166
|
+
the single place that fetches/renders plan content (with Copy +
|
|
167
|
+
Source/Rendered). */}
|
|
167
168
|
{org && previewOpen && (
|
|
168
169
|
<ArtifactPreviewModal
|
|
169
170
|
artifact={artifact}
|
|
@@ -182,6 +183,41 @@ export const PlanArtifactCard = memo(function PlanArtifactCard({
|
|
|
182
183
|
const FOCUS_RING =
|
|
183
184
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:rounded-sm";
|
|
184
185
|
|
|
186
|
+
/**
|
|
187
|
+
* An icon-only secondary action. The accessible name doubles as the hover
|
|
188
|
+
* tooltip (native `title` — the codebase's tooltip pattern), and the padded
|
|
189
|
+
* hit area keeps the target comfortable despite the compact glyph.
|
|
190
|
+
*/
|
|
191
|
+
function IconAction({
|
|
192
|
+
label,
|
|
193
|
+
onClick,
|
|
194
|
+
icon,
|
|
195
|
+
disabled,
|
|
196
|
+
}: {
|
|
197
|
+
readonly label: string;
|
|
198
|
+
readonly onClick: () => void;
|
|
199
|
+
readonly icon: ReactNode;
|
|
200
|
+
readonly disabled?: boolean;
|
|
201
|
+
}) {
|
|
202
|
+
return (
|
|
203
|
+
<button
|
|
204
|
+
type="button"
|
|
205
|
+
aria-label={label}
|
|
206
|
+
title={label}
|
|
207
|
+
onClick={onClick}
|
|
208
|
+
disabled={disabled}
|
|
209
|
+
className={cn(
|
|
210
|
+
"inline-flex items-center rounded-md p-1.5 text-muted-foreground",
|
|
211
|
+
"transition-colors hover:bg-muted hover:text-foreground",
|
|
212
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
213
|
+
FOCUS_RING,
|
|
214
|
+
)}
|
|
215
|
+
>
|
|
216
|
+
{icon}
|
|
217
|
+
</button>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
185
221
|
function PlanIcon() {
|
|
186
222
|
return (
|
|
187
223
|
<svg
|
|
@@ -223,8 +259,8 @@ function ImplementIcon() {
|
|
|
223
259
|
function DownloadIcon() {
|
|
224
260
|
return (
|
|
225
261
|
<svg
|
|
226
|
-
width="
|
|
227
|
-
height="
|
|
262
|
+
width="13"
|
|
263
|
+
height="13"
|
|
228
264
|
viewBox="0 0 12 12"
|
|
229
265
|
fill="none"
|
|
230
266
|
stroke="currentColor"
|
|
@@ -241,21 +277,63 @@ function DownloadIcon() {
|
|
|
241
277
|
);
|
|
242
278
|
}
|
|
243
279
|
|
|
244
|
-
function
|
|
280
|
+
function CopyIcon() {
|
|
281
|
+
return (
|
|
282
|
+
<svg
|
|
283
|
+
width="13"
|
|
284
|
+
height="13"
|
|
285
|
+
viewBox="0 0 12 12"
|
|
286
|
+
fill="none"
|
|
287
|
+
stroke="currentColor"
|
|
288
|
+
strokeWidth="1.2"
|
|
289
|
+
strokeLinecap="round"
|
|
290
|
+
strokeLinejoin="round"
|
|
291
|
+
className="shrink-0"
|
|
292
|
+
aria-hidden="true"
|
|
293
|
+
>
|
|
294
|
+
<rect x="4" y="4" width="6.5" height="6.5" rx="1" />
|
|
295
|
+
<path d="M8 4V2.5C8 1.95 7.55 1.5 7 1.5H2.5C1.95 1.5 1.5 1.95 1.5 2.5V7C1.5 7.55 1.95 8 2.5 8H4" />
|
|
296
|
+
</svg>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** A check glyph — transient confirmation that the copy succeeded. */
|
|
301
|
+
function CheckIcon() {
|
|
245
302
|
return (
|
|
246
303
|
<svg
|
|
247
|
-
width="
|
|
248
|
-
height="
|
|
304
|
+
width="13"
|
|
305
|
+
height="13"
|
|
249
306
|
viewBox="0 0 12 12"
|
|
250
307
|
fill="none"
|
|
251
308
|
stroke="currentColor"
|
|
252
309
|
strokeWidth="1.5"
|
|
253
310
|
strokeLinecap="round"
|
|
254
311
|
strokeLinejoin="round"
|
|
312
|
+
className="shrink-0 text-success"
|
|
313
|
+
aria-hidden="true"
|
|
314
|
+
>
|
|
315
|
+
<path d="M2 6.5L4.5 9L10 3" />
|
|
316
|
+
</svg>
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** An eye glyph — "view/open the plan to read it". */
|
|
321
|
+
function EyeIcon() {
|
|
322
|
+
return (
|
|
323
|
+
<svg
|
|
324
|
+
width="13"
|
|
325
|
+
height="13"
|
|
326
|
+
viewBox="0 0 16 16"
|
|
327
|
+
fill="none"
|
|
328
|
+
stroke="currentColor"
|
|
329
|
+
strokeWidth="1.5"
|
|
330
|
+
strokeLinecap="round"
|
|
331
|
+
strokeLinejoin="round"
|
|
255
332
|
className="shrink-0"
|
|
256
333
|
aria-hidden="true"
|
|
257
334
|
>
|
|
258
|
-
<path d="
|
|
335
|
+
<path d="M1.5 8S4 3.5 8 3.5s6.5 4.5 6.5 4.5-2.5 4.5-6.5 4.5S1.5 8 1.5 8Z" />
|
|
336
|
+
<circle cx="8" cy="8" r="1.75" />
|
|
259
337
|
</svg>
|
|
260
338
|
);
|
|
261
339
|
}
|
|
@@ -6,7 +6,7 @@ import { useBuildFromPlanHotkey } from "./use-build-from-plan-hotkey.js";
|
|
|
6
6
|
|
|
7
7
|
/** Props for {@link PlanCompletionCard}. */
|
|
8
8
|
export interface PlanCompletionCardProps {
|
|
9
|
-
/** Called when the user clicks the "Build
|
|
9
|
+
/** Called when the user clicks the "Build" button. */
|
|
10
10
|
readonly onImplement?: () => void;
|
|
11
11
|
/** Disables the CTA button (e.g., while an execution is active). */
|
|
12
12
|
readonly disabled?: boolean;
|
|
@@ -20,9 +20,9 @@ export interface PlanCompletionCardProps {
|
|
|
20
20
|
* a plan whose upload failed). When a plan artifact exists, the richer
|
|
21
21
|
* `PlanArtifactCard` is shown instead.
|
|
22
22
|
*
|
|
23
|
-
* Mirrors `PlanArtifactCard`'s hierarchy — one prominent, themeable "Build
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* Mirrors `PlanArtifactCard`'s hierarchy — one prominent, themeable "Build"
|
|
24
|
+
* primary action with the same card-scoped `Cmd/Ctrl+Enter` accelerator — so
|
|
25
|
+
* the two cards feel like the same affordance. The consumer wires the action
|
|
26
26
|
* to switch the interaction mode to Agent and submit the implement turn.
|
|
27
27
|
*
|
|
28
28
|
* Renders nothing when `onImplement` is not provided, so the card is fully
|
|
@@ -67,7 +67,7 @@ export const PlanCompletionCard = memo(function PlanCompletionCard({
|
|
|
67
67
|
)}
|
|
68
68
|
>
|
|
69
69
|
<ImplementIcon />
|
|
70
|
-
Build
|
|
70
|
+
Build
|
|
71
71
|
</button>
|
|
72
72
|
</div>
|
|
73
73
|
);
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { memo } from "react";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
|
-
import { PLAN_ARTIFACT_NAME } from "../library/detect-plan-artifact.js";
|
|
6
5
|
import { formatArtifactSize } from "./artifact-utils.js";
|
|
7
6
|
|
|
8
7
|
/** Props for {@link PlanStreamingCard}. */
|
|
@@ -34,8 +33,13 @@ export interface PlanStreamingCardProps {
|
|
|
34
33
|
* The live counterpart of {@link PlanArtifactCard} — a streaming Plan turn's
|
|
35
34
|
* compact stand-in in the thread while the plan document renders live in the
|
|
36
35
|
* session panel's plan tab. Same anatomy as the completed card (icon, title,
|
|
37
|
-
* `
|
|
38
|
-
* the same card settling, not a new element appearing.
|
|
36
|
+
* a `Writing… · size` meta, trailing actions) so the completion handoff reads
|
|
37
|
+
* as the same card settling, not a new element appearing.
|
|
38
|
+
*
|
|
39
|
+
* The meta line deliberately omits a filename: the artifact's name is derived
|
|
40
|
+
* from the plan's title, which is still streaming in, so a name shown here
|
|
41
|
+
* would change under the user when the plan settles. The completed card
|
|
42
|
+
* introduces the final `<slug>_<id>.plan.md` name once it exists.
|
|
39
43
|
*
|
|
40
44
|
* Deliberately action-light: "Open plan" is the only affordance. Download and
|
|
41
45
|
* Build require the published artifact, which does not exist until the turn
|
|
@@ -72,8 +76,6 @@ export const PlanStreamingCard = memo(function PlanStreamingCard({
|
|
|
72
76
|
{title ?? "Writing plan…"}
|
|
73
77
|
</div>
|
|
74
78
|
<div className="text-[0.65rem] text-muted-foreground-faint">
|
|
75
|
-
{PLAN_ARTIFACT_NAME}
|
|
76
|
-
<span aria-hidden="true"> · </span>
|
|
77
79
|
<span role="status">Writing…</span>
|
|
78
80
|
<span aria-hidden="true"> · </span>
|
|
79
81
|
<span className="tabular-nums">{formatArtifactSize(sizeBytes)}</span>
|
|
@@ -89,7 +91,7 @@ export const PlanStreamingCard = memo(function PlanStreamingCard({
|
|
|
89
91
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:rounded-sm",
|
|
90
92
|
)}
|
|
91
93
|
>
|
|
92
|
-
<
|
|
94
|
+
<EyeIcon />
|
|
93
95
|
Open plan
|
|
94
96
|
</button>
|
|
95
97
|
)}
|
|
@@ -117,12 +119,13 @@ function PlanIcon({ className }: { readonly className?: string }) {
|
|
|
117
119
|
);
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
|
|
122
|
+
/** An eye glyph — "view/open the plan to read it" (matches PlanArtifactCard). */
|
|
123
|
+
function EyeIcon() {
|
|
121
124
|
return (
|
|
122
125
|
<svg
|
|
123
|
-
width="
|
|
124
|
-
height="
|
|
125
|
-
viewBox="0 0
|
|
126
|
+
width="11"
|
|
127
|
+
height="11"
|
|
128
|
+
viewBox="0 0 16 16"
|
|
126
129
|
fill="none"
|
|
127
130
|
stroke="currentColor"
|
|
128
131
|
strokeWidth="1.5"
|
|
@@ -131,7 +134,8 @@ function ExpandIcon() {
|
|
|
131
134
|
className="shrink-0"
|
|
132
135
|
aria-hidden="true"
|
|
133
136
|
>
|
|
134
|
-
<path d="
|
|
137
|
+
<path d="M1.5 8S4 3.5 8 3.5s6.5 4.5 6.5 4.5-2.5 4.5-6.5 4.5S1.5 8 1.5 8Z" />
|
|
138
|
+
<circle cx="8" cy="8" r="1.75" />
|
|
135
139
|
</svg>
|
|
136
140
|
);
|
|
137
141
|
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup } from "@testing-library/react";
|
|
3
|
+
import { create } from "@bufbuild/protobuf";
|
|
4
|
+
import { ExecutionArtifactSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
|
|
5
|
+
import { ExecutionArtifactKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
6
|
+
import { ArtifactContentBody } from "../ArtifactContentBody";
|
|
7
|
+
import type { SkillPackageDetection } from "../../library/detect-skill-package";
|
|
8
|
+
|
|
9
|
+
const NOT_SKILL: SkillPackageDetection = { detected: false };
|
|
10
|
+
|
|
11
|
+
function fileArtifact(name: string) {
|
|
12
|
+
return create(ExecutionArtifactSchema, {
|
|
13
|
+
name,
|
|
14
|
+
kind: ExecutionArtifactKind.FILE,
|
|
15
|
+
sizeBytes: 64n,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function dirArtifact(name: string, entries: string[]) {
|
|
20
|
+
return create(ExecutionArtifactSchema, {
|
|
21
|
+
name,
|
|
22
|
+
kind: ExecutionArtifactKind.DIRECTORY,
|
|
23
|
+
sizeBytes: 512n,
|
|
24
|
+
entries,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
afterEach(cleanup);
|
|
29
|
+
|
|
30
|
+
describe("ArtifactContentBody — file states", () => {
|
|
31
|
+
it("renders a loading skeleton", () => {
|
|
32
|
+
render(
|
|
33
|
+
<ArtifactContentBody
|
|
34
|
+
artifact={fileArtifact("notes.txt")}
|
|
35
|
+
content={null}
|
|
36
|
+
contentType={null}
|
|
37
|
+
isLoading
|
|
38
|
+
error={null}
|
|
39
|
+
isTruncated={false}
|
|
40
|
+
skillDetection={NOT_SKILL}
|
|
41
|
+
/>,
|
|
42
|
+
);
|
|
43
|
+
expect(screen.getByLabelText("Loading content")).toBeTruthy();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("renders an error message", () => {
|
|
47
|
+
render(
|
|
48
|
+
<ArtifactContentBody
|
|
49
|
+
artifact={fileArtifact("notes.txt")}
|
|
50
|
+
content={null}
|
|
51
|
+
contentType={null}
|
|
52
|
+
isLoading={false}
|
|
53
|
+
error={new Error("boom")}
|
|
54
|
+
isTruncated={false}
|
|
55
|
+
skillDetection={NOT_SKILL}
|
|
56
|
+
/>,
|
|
57
|
+
);
|
|
58
|
+
expect(screen.getByText("boom")).toBeTruthy();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("renders the binary / unavailable state when content is null and settled", () => {
|
|
62
|
+
render(
|
|
63
|
+
<ArtifactContentBody
|
|
64
|
+
artifact={fileArtifact("image.bin")}
|
|
65
|
+
content={null}
|
|
66
|
+
contentType={null}
|
|
67
|
+
isLoading={false}
|
|
68
|
+
error={null}
|
|
69
|
+
isTruncated={false}
|
|
70
|
+
skillDetection={NOT_SKILL}
|
|
71
|
+
/>,
|
|
72
|
+
);
|
|
73
|
+
expect(screen.getByText(/Content not available for preview/)).toBeTruthy();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("renders text content", () => {
|
|
77
|
+
render(
|
|
78
|
+
<ArtifactContentBody
|
|
79
|
+
artifact={fileArtifact("notes.txt")}
|
|
80
|
+
content={"hello from the artifact"}
|
|
81
|
+
contentType={"text/plain"}
|
|
82
|
+
isLoading={false}
|
|
83
|
+
error={null}
|
|
84
|
+
isTruncated={false}
|
|
85
|
+
skillDetection={NOT_SKILL}
|
|
86
|
+
/>,
|
|
87
|
+
);
|
|
88
|
+
expect(screen.getByText(/hello from the artifact/)).toBeTruthy();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("ArtifactContentBody — directory state", () => {
|
|
93
|
+
it("lists directory entries", () => {
|
|
94
|
+
render(
|
|
95
|
+
<ArtifactContentBody
|
|
96
|
+
artifact={dirArtifact("pack", ["SKILL.md", "run.sh"])}
|
|
97
|
+
content={null}
|
|
98
|
+
contentType={null}
|
|
99
|
+
isLoading={false}
|
|
100
|
+
error={null}
|
|
101
|
+
isTruncated={false}
|
|
102
|
+
skillDetection={NOT_SKILL}
|
|
103
|
+
/>,
|
|
104
|
+
);
|
|
105
|
+
expect(screen.getByText("Files (2)")).toBeTruthy();
|
|
106
|
+
expect(screen.getByText("SKILL.md")).toBeTruthy();
|
|
107
|
+
expect(screen.getByText("run.sh")).toBeTruthy();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("shows the skill banner when a skill package is detected", () => {
|
|
111
|
+
const detection = {
|
|
112
|
+
detected: true,
|
|
113
|
+
skillName: "My Skill",
|
|
114
|
+
skillDescription: "does things",
|
|
115
|
+
fileCount: 2,
|
|
116
|
+
} as unknown as SkillPackageDetection;
|
|
117
|
+
render(
|
|
118
|
+
<ArtifactContentBody
|
|
119
|
+
artifact={dirArtifact("pack", ["SKILL.md"])}
|
|
120
|
+
content={null}
|
|
121
|
+
contentType={null}
|
|
122
|
+
isLoading={false}
|
|
123
|
+
error={null}
|
|
124
|
+
isTruncated={false}
|
|
125
|
+
skillDetection={detection}
|
|
126
|
+
/>,
|
|
127
|
+
);
|
|
128
|
+
expect(screen.getByText("My Skill")).toBeTruthy();
|
|
129
|
+
expect(screen.getByText("does things")).toBeTruthy();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, waitFor } from "@testing-library/react";
|
|
3
|
+
import { create } from "@bufbuild/protobuf";
|
|
4
|
+
import type { Stigmer } from "@stigmer/sdk";
|
|
5
|
+
import { ExecutionArtifactSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
|
|
6
|
+
import { ExecutionArtifactKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
7
|
+
import { StigmerContext } from "../../context";
|
|
8
|
+
import { ArtifactDocument } from "../ArtifactDocument";
|
|
9
|
+
|
|
10
|
+
function fileArtifact(name: string) {
|
|
11
|
+
return create(ExecutionArtifactSchema, {
|
|
12
|
+
name,
|
|
13
|
+
kind: ExecutionArtifactKind.FILE,
|
|
14
|
+
sizeBytes: 64n,
|
|
15
|
+
storageKey: `artifacts/aex_1/${name}`,
|
|
16
|
+
contentHash: "hash-1",
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function dirArtifact(name: string) {
|
|
21
|
+
return create(ExecutionArtifactSchema, {
|
|
22
|
+
name,
|
|
23
|
+
kind: ExecutionArtifactKind.DIRECTORY,
|
|
24
|
+
sizeBytes: 512n,
|
|
25
|
+
storageKey: `artifacts/aex_1/${name}`,
|
|
26
|
+
entries: ["a.txt", "b.txt"],
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function stigmerReturning(text: string): Stigmer {
|
|
31
|
+
return {
|
|
32
|
+
agentExecution: {
|
|
33
|
+
getArtifactContent: vi.fn().mockResolvedValue({
|
|
34
|
+
content: new TextEncoder().encode(text),
|
|
35
|
+
contentType: "text/plain",
|
|
36
|
+
truncated: false,
|
|
37
|
+
}),
|
|
38
|
+
},
|
|
39
|
+
} as unknown as Stigmer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function renderDoc(stigmer: Stigmer, artifact = fileArtifact("notes.txt")) {
|
|
43
|
+
return render(
|
|
44
|
+
<StigmerContext.Provider value={stigmer}>
|
|
45
|
+
<ArtifactDocument
|
|
46
|
+
artifact={artifact}
|
|
47
|
+
executionId="aex_1"
|
|
48
|
+
org="acme"
|
|
49
|
+
isTerminal
|
|
50
|
+
/>
|
|
51
|
+
</StigmerContext.Provider>,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const AGENT_YAML = [
|
|
56
|
+
"apiVersion: agentic.stigmer.ai/v1",
|
|
57
|
+
"kind: Agent",
|
|
58
|
+
"metadata:",
|
|
59
|
+
" name: my-agent",
|
|
60
|
+
"spec: {}",
|
|
61
|
+
"",
|
|
62
|
+
].join("\n");
|
|
63
|
+
|
|
64
|
+
afterEach(cleanup);
|
|
65
|
+
|
|
66
|
+
describe("ArtifactDocument", () => {
|
|
67
|
+
it("renders the toolbar (name + Download) and the fetched content", async () => {
|
|
68
|
+
renderDoc(stigmerReturning("hello document"));
|
|
69
|
+
|
|
70
|
+
expect(screen.getByRole("article", { name: "Artifact notes.txt" })).toBeTruthy();
|
|
71
|
+
expect(screen.getByText("notes.txt")).toBeTruthy();
|
|
72
|
+
expect(screen.getByText("Download")).toBeTruthy();
|
|
73
|
+
await waitFor(() => expect(screen.getByText(/hello document/)).toBeTruthy());
|
|
74
|
+
expect(screen.getByText("Copy")).toBeTruthy();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("surfaces resource detection and an Apply CTA for a detected Agent", async () => {
|
|
78
|
+
renderDoc(stigmerReturning(AGENT_YAML), fileArtifact("agent.yaml"));
|
|
79
|
+
|
|
80
|
+
await waitFor(() => expect(screen.getByText("Agent detected")).toBeTruthy());
|
|
81
|
+
expect(screen.getByText("Apply to acme")).toBeTruthy();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("renders a directory artifact with a Download ZIP action and no Copy", () => {
|
|
85
|
+
render(
|
|
86
|
+
<StigmerContext.Provider value={stigmerReturning("")}>
|
|
87
|
+
<ArtifactDocument
|
|
88
|
+
artifact={dirArtifact("pack")}
|
|
89
|
+
executionId="aex_1"
|
|
90
|
+
org="acme"
|
|
91
|
+
isTerminal
|
|
92
|
+
/>
|
|
93
|
+
</StigmerContext.Provider>,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
expect(screen.getByText("Download ZIP")).toBeTruthy();
|
|
97
|
+
expect(screen.queryByText("Copy")).toBeNull();
|
|
98
|
+
expect(screen.getByText("Files (2)")).toBeTruthy();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -26,8 +26,8 @@ function withStigmer(children: ReactNode) {
|
|
|
26
26
|
|
|
27
27
|
afterEach(cleanup);
|
|
28
28
|
|
|
29
|
-
describe("ArtifactPreviewContent — Build
|
|
30
|
-
it("renders a 'Build
|
|
29
|
+
describe("ArtifactPreviewContent — Build action", () => {
|
|
30
|
+
it("renders a 'Build' button only when onImplement is provided", () => {
|
|
31
31
|
const { rerender } = render(
|
|
32
32
|
withStigmer(
|
|
33
33
|
<ArtifactPreviewContent
|
|
@@ -39,7 +39,7 @@ describe("ArtifactPreviewContent — Build from plan action", () => {
|
|
|
39
39
|
/>,
|
|
40
40
|
),
|
|
41
41
|
);
|
|
42
|
-
expect(screen.queryByText("Build
|
|
42
|
+
expect(screen.queryByText("Build")).toBeNull();
|
|
43
43
|
|
|
44
44
|
rerender(
|
|
45
45
|
withStigmer(
|
|
@@ -53,10 +53,10 @@ describe("ArtifactPreviewContent — Build from plan action", () => {
|
|
|
53
53
|
/>,
|
|
54
54
|
),
|
|
55
55
|
);
|
|
56
|
-
expect(screen.getByText("Build
|
|
56
|
+
expect(screen.getByText("Build")).toBeTruthy();
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
it("calls onImplement then closes the modal when 'Build
|
|
59
|
+
it("calls onImplement then closes the modal when 'Build' is clicked", () => {
|
|
60
60
|
const calls: string[] = [];
|
|
61
61
|
const onImplement = vi.fn(() => calls.push("implement"));
|
|
62
62
|
const onClose = vi.fn(() => calls.push("close"));
|
|
@@ -74,7 +74,7 @@ describe("ArtifactPreviewContent — Build from plan action", () => {
|
|
|
74
74
|
),
|
|
75
75
|
);
|
|
76
76
|
|
|
77
|
-
fireEvent.click(screen.getByText("Build
|
|
77
|
+
fireEvent.click(screen.getByText("Build"));
|
|
78
78
|
|
|
79
79
|
expect(onImplement).toHaveBeenCalledTimes(1);
|
|
80
80
|
expect(onClose).toHaveBeenCalledTimes(1);
|