@stigmer/react 3.1.3 → 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 +31 -0
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +12 -4
- 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 +4 -4
- package/execution/ArtifactPreviewModal.d.ts.map +1 -1
- package/execution/ArtifactPreviewModal.js +15 -142
- 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/index.d.ts +8 -2
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +4 -1
- package/execution/index.js.map +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/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 +11 -0
- 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 +3 -3
- package/index.d.ts.map +1 -1
- package/index.js +2 -2
- 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/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/package.json +5 -4
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +91 -18
- 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/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 +17 -1
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +32 -4
- 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 +69 -1
- 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 +39 -405
- package/src/execution/ArtifactRow.tsx +165 -0
- package/src/execution/ArtifactsWidget.tsx +24 -33
- package/src/execution/__tests__/ArtifactContentBody.test.tsx +131 -0
- package/src/execution/__tests__/ArtifactDocument.test.tsx +100 -0
- package/src/execution/__tests__/ArtifactRow.test.tsx +139 -0
- package/src/execution/__tests__/ArtifactsWidget.test.tsx +78 -0
- package/src/execution/__tests__/message-entry.test.tsx +50 -2
- package/src/execution/__tests__/useArtifactInspection.test.tsx +151 -0
- package/src/execution/__tests__/useCreateAgentExecution.test.tsx +35 -0
- package/src/execution/index.ts +14 -2
- package/src/execution/useArtifactContent.ts +7 -0
- package/src/execution/useArtifactInspection.ts +252 -0
- package/src/execution/useCreateAgentExecution.ts +12 -0
- package/src/index.ts +11 -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/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/session/SessionViewer.tsx +177 -33
- package/src/session/__tests__/SessionViewer-artifactTabs.test.tsx +210 -0
- package/src/session/__tests__/SessionViewer-editResubmit.test.tsx +233 -0
- package/src/session/__tests__/useSessionArtifacts.test.ts +57 -0
- package/src/session/__tests__/useSessionConversation.test.tsx +172 -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/useSessionArtifacts.ts +16 -5
- package/src/session/useSessionConversation.ts +48 -6
- package/src/session/useSessionPageFlow.ts +1 -0
- package/src/session/useSessionPanel.ts +41 -0
- package/src/session/useSessionRailViews.tsx +21 -1
- package/styles.css +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,81 +0,0 @@
|
|
|
1
|
-
import type { ExecutionArtifact } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
|
|
2
|
-
/** Props for {@link ArtifactCard}. */
|
|
3
|
-
export interface ArtifactCardProps {
|
|
4
|
-
/** The execution artifact to render. */
|
|
5
|
-
readonly artifact: ExecutionArtifact;
|
|
6
|
-
/** ID of the execution that produced this artifact — used for content fetching. */
|
|
7
|
-
readonly executionId: string;
|
|
8
|
-
/**
|
|
9
|
-
* Organization slug — used for skill package detection which fetches
|
|
10
|
-
* `SKILL.md` frontmatter via the artifact content RPC.
|
|
11
|
-
*/
|
|
12
|
-
readonly org: string;
|
|
13
|
-
/**
|
|
14
|
-
* Called when the user clicks "Preview". The consumer is responsible
|
|
15
|
-
* for rendering a preview UI (e.g., {@link ArtifactPreviewModal}).
|
|
16
|
-
*
|
|
17
|
-
* When omitted, the Preview action is not rendered.
|
|
18
|
-
*/
|
|
19
|
-
readonly onPreview?: (artifact: ExecutionArtifact) => void;
|
|
20
|
-
/**
|
|
21
|
-
* When `true`, another artifact in the list shares the same display
|
|
22
|
-
* `name` but has a different `sandbox_path`. The card renders the
|
|
23
|
-
* parent directory from `sandbox_path` as a muted subtitle for
|
|
24
|
-
* disambiguation.
|
|
25
|
-
*
|
|
26
|
-
* Set by {@link ArtifactsWidget} via {@link useSessionArtifacts}.
|
|
27
|
-
* Defaults to `false`.
|
|
28
|
-
*/
|
|
29
|
-
readonly hasNameCollision?: boolean;
|
|
30
|
-
/** Additional CSS classes for the root element. */
|
|
31
|
-
readonly className?: string;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Renders a single execution artifact as a compact summary card with
|
|
35
|
-
* automatic Stigmer resource detection and a download link.
|
|
36
|
-
*
|
|
37
|
-
* The card's role is **signal and navigate**: it tells the user what
|
|
38
|
-
* the artifact is (file/directory, size, detected resource type) and
|
|
39
|
-
* provides two actions — Preview and Download. The Apply/Push CTA
|
|
40
|
-
* lives exclusively in {@link ArtifactPreviewModal} so the user must
|
|
41
|
-
* review content before acting (review-before-apply pattern).
|
|
42
|
-
*
|
|
43
|
-
* Internally orchestrates the detection pipeline:
|
|
44
|
-
*
|
|
45
|
-
* - **FILE artifacts**: Fetches text content via {@link useArtifactContent},
|
|
46
|
-
* then detects Agent/McpServer YAML via {@link useDetectStigmerResource}.
|
|
47
|
-
* Only text files under 256 KB are fetched for detection.
|
|
48
|
-
*
|
|
49
|
-
* - **DIRECTORY artifacts**: Detects skill packages via
|
|
50
|
-
* {@link useDetectSkillPackage} (checks for `SKILL.md` in the ZIP
|
|
51
|
-
* archive entries).
|
|
52
|
-
*
|
|
53
|
-
* Detection results are surfaced as a type badge ("Agent detected",
|
|
54
|
-
* "Skill · N files"). Non-Stigmer artifacts render with file info
|
|
55
|
-
* and a download link only — no error messages or "not detected"
|
|
56
|
-
* indicators (detection is silent).
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```tsx
|
|
60
|
-
* const { artifacts } = useExecutionArtifacts(execution);
|
|
61
|
-
*
|
|
62
|
-
* {artifacts.map((artifact) => (
|
|
63
|
-
* <ArtifactCard
|
|
64
|
-
* key={artifact.storageKey}
|
|
65
|
-
* artifact={artifact}
|
|
66
|
-
* executionId={execution.metadata!.id}
|
|
67
|
-
* org={activeOrg}
|
|
68
|
-
* onPreview={(a) => setPreviewArtifact(a)}
|
|
69
|
-
* />
|
|
70
|
-
* ))}
|
|
71
|
-
* ```
|
|
72
|
-
*
|
|
73
|
-
* @see {@link ArtifactPreviewModal} — full preview with Apply/Push CTA
|
|
74
|
-
* @see {@link ArtifactsWidget} — container that composes cards + modal
|
|
75
|
-
* @see {@link useExecutionArtifacts} — extracts artifact metadata from an execution
|
|
76
|
-
* @see {@link useArtifactContent} — content-fetching hook (headless alternative)
|
|
77
|
-
* @see {@link useDetectStigmerResource} — YAML resource detection (headless)
|
|
78
|
-
* @see {@link useDetectSkillPackage} — skill package detection (headless)
|
|
79
|
-
*/
|
|
80
|
-
export declare function ArtifactCard({ artifact, executionId, org, onPreview, hasNameCollision, className, }: ArtifactCardProps): import("react/jsx-runtime").JSX.Element;
|
|
81
|
-
//# sourceMappingURL=ArtifactCard.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ArtifactCard.d.ts","sourceRoot":"","sources":["../../src/execution/ArtifactCard.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kEAAkE,CAAC;AAiB1G,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,mFAAmF;IACnF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC3D;;;;;;;;OAQG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,mDAAmD;IACnD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,WAAW,EACX,GAAG,EACH,SAAS,EACT,gBAAwB,EACxB,SAAS,GACV,EAAE,iBAAiB,2CA+HnB"}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { ExecutionArtifactKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
4
|
-
import { cn } from "@stigmer/theme";
|
|
5
|
-
import { useArtifactContent } from "./useArtifactContent.js";
|
|
6
|
-
import { useArtifactDownload } from "./useArtifactDownload.js";
|
|
7
|
-
import { isTextArtifact, formatArtifactSize } from "./artifact-utils.js";
|
|
8
|
-
import { useDetectStigmerResource } from "../library/useDetectStigmerResource.js";
|
|
9
|
-
import { useDetectSkillPackage } from "../library/useDetectSkillPackage.js";
|
|
10
|
-
/**
|
|
11
|
-
* Artifacts larger than this threshold skip the content fetch used for
|
|
12
|
-
* resource detection. Stigmer resource YAML files are typically under
|
|
13
|
-
* 10 KB — 256 KB is generous without risking large payloads for log
|
|
14
|
-
* files or binary-adjacent text content.
|
|
15
|
-
*/
|
|
16
|
-
const MAX_DETECTION_SIZE = 256 * 1024;
|
|
17
|
-
/**
|
|
18
|
-
* Renders a single execution artifact as a compact summary card with
|
|
19
|
-
* automatic Stigmer resource detection and a download link.
|
|
20
|
-
*
|
|
21
|
-
* The card's role is **signal and navigate**: it tells the user what
|
|
22
|
-
* the artifact is (file/directory, size, detected resource type) and
|
|
23
|
-
* provides two actions — Preview and Download. The Apply/Push CTA
|
|
24
|
-
* lives exclusively in {@link ArtifactPreviewModal} so the user must
|
|
25
|
-
* review content before acting (review-before-apply pattern).
|
|
26
|
-
*
|
|
27
|
-
* Internally orchestrates the detection pipeline:
|
|
28
|
-
*
|
|
29
|
-
* - **FILE artifacts**: Fetches text content via {@link useArtifactContent},
|
|
30
|
-
* then detects Agent/McpServer YAML via {@link useDetectStigmerResource}.
|
|
31
|
-
* Only text files under 256 KB are fetched for detection.
|
|
32
|
-
*
|
|
33
|
-
* - **DIRECTORY artifacts**: Detects skill packages via
|
|
34
|
-
* {@link useDetectSkillPackage} (checks for `SKILL.md` in the ZIP
|
|
35
|
-
* archive entries).
|
|
36
|
-
*
|
|
37
|
-
* Detection results are surfaced as a type badge ("Agent detected",
|
|
38
|
-
* "Skill · N files"). Non-Stigmer artifacts render with file info
|
|
39
|
-
* and a download link only — no error messages or "not detected"
|
|
40
|
-
* indicators (detection is silent).
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* ```tsx
|
|
44
|
-
* const { artifacts } = useExecutionArtifacts(execution);
|
|
45
|
-
*
|
|
46
|
-
* {artifacts.map((artifact) => (
|
|
47
|
-
* <ArtifactCard
|
|
48
|
-
* key={artifact.storageKey}
|
|
49
|
-
* artifact={artifact}
|
|
50
|
-
* executionId={execution.metadata!.id}
|
|
51
|
-
* org={activeOrg}
|
|
52
|
-
* onPreview={(a) => setPreviewArtifact(a)}
|
|
53
|
-
* />
|
|
54
|
-
* ))}
|
|
55
|
-
* ```
|
|
56
|
-
*
|
|
57
|
-
* @see {@link ArtifactPreviewModal} — full preview with Apply/Push CTA
|
|
58
|
-
* @see {@link ArtifactsWidget} — container that composes cards + modal
|
|
59
|
-
* @see {@link useExecutionArtifacts} — extracts artifact metadata from an execution
|
|
60
|
-
* @see {@link useArtifactContent} — content-fetching hook (headless alternative)
|
|
61
|
-
* @see {@link useDetectStigmerResource} — YAML resource detection (headless)
|
|
62
|
-
* @see {@link useDetectSkillPackage} — skill package detection (headless)
|
|
63
|
-
*/
|
|
64
|
-
export function ArtifactCard({ artifact, executionId, org, onPreview, hasNameCollision = false, className, }) {
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
|
-
// Detection orchestration
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
const isDirectory = artifact.kind === ExecutionArtifactKind.DIRECTORY;
|
|
69
|
-
const canDetectYaml = !isDirectory &&
|
|
70
|
-
isTextArtifact(artifact) &&
|
|
71
|
-
Number(artifact.sizeBytes) < MAX_DETECTION_SIZE;
|
|
72
|
-
const { content, isLoading: isContentLoading } = useArtifactContent(canDetectYaml ? executionId : null, canDetectYaml ? artifact.storageKey : null, undefined, artifact.contentHash || undefined);
|
|
73
|
-
const yamlDetection = useDetectStigmerResource(canDetectYaml ? content : null);
|
|
74
|
-
const { detection: skillDetection, isLoading: isSkillLoading } = useDetectSkillPackage(isDirectory ? artifact : null, isDirectory ? executionId : null);
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
// Derived detection state
|
|
77
|
-
// ---------------------------------------------------------------------------
|
|
78
|
-
const isDetected = yamlDetection.detected || skillDetection.detected;
|
|
79
|
-
const isDetecting = (canDetectYaml && isContentLoading) || (isDirectory && isSkillLoading);
|
|
80
|
-
let detectionLabel = null;
|
|
81
|
-
if (yamlDetection.detected) {
|
|
82
|
-
detectionLabel = `${yamlDetection.displayName} detected`;
|
|
83
|
-
}
|
|
84
|
-
else if (skillDetection.detected) {
|
|
85
|
-
const count = skillDetection.fileCount;
|
|
86
|
-
detectionLabel = `Skill \u00B7 ${count} ${count === 1 ? "file" : "files"}`;
|
|
87
|
-
}
|
|
88
|
-
const canPreview = !!onPreview && (isTextArtifact(artifact) || isDirectory);
|
|
89
|
-
const { download, isDownloading } = useArtifactDownload(executionId);
|
|
90
|
-
const parentDir = hasNameCollision && artifact.sandboxPath
|
|
91
|
-
? parentDirectory(artifact.sandboxPath)
|
|
92
|
-
: null;
|
|
93
|
-
// ---------------------------------------------------------------------------
|
|
94
|
-
// Render
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
return (_jsxs("div", { role: "article", "aria-label": artifact.name, className: cn("rounded-md border border-border p-3", className), children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: isDirectory ? _jsx(FolderIcon, {}) : _jsx(FileIcon, {}) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "truncate text-sm font-medium text-foreground", children: [artifact.name, isDirectory && "/"] }), parentDir && (_jsx("div", { className: "truncate text-xs text-muted-foreground", children: parentDir })), _jsx("div", { className: "text-xs tabular-nums text-muted-foreground", children: formatArtifactSize(artifact.sizeBytes) })] })] }), isDetecting && !isDetected && (_jsx("div", { className: "mt-1.5 h-5 w-28 animate-pulse rounded-full bg-muted", "aria-hidden": "true" })), detectionLabel && (_jsx("span", { className: "mt-1.5 inline-flex items-center rounded-full bg-primary-subtle px-2 py-0.5 text-xs font-medium text-primary", children: detectionLabel })), _jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1", children: [canPreview && (_jsx("button", { type: "button", onClick: () => onPreview(artifact), className: cn("text-xs font-medium text-primary transition-colors hover:text-primary-muted", FOCUS_RING_CLASSES), children: "Preview" })), _jsxs("button", { type: "button", onClick: () => download(artifact.storageKey, artifact.name), disabled: isDownloading, className: cn("inline-flex items-center gap-1 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground disabled:opacity-50", FOCUS_RING_CLASSES), children: [_jsx(DownloadIcon, {}), isDownloading ? "Preparing…" : isDirectory ? "Download ZIP" : "Download"] })] })] }));
|
|
97
|
-
}
|
|
98
|
-
// ---------------------------------------------------------------------------
|
|
99
|
-
// Helpers
|
|
100
|
-
// ---------------------------------------------------------------------------
|
|
101
|
-
/**
|
|
102
|
-
* Extracts a human-readable parent directory label from a sandbox path.
|
|
103
|
-
* Given `/workspace/configs/agent.yaml` returns `configs/`.
|
|
104
|
-
* Returns `null` when the path has no meaningful parent segment.
|
|
105
|
-
*/
|
|
106
|
-
function parentDirectory(sandboxPath) {
|
|
107
|
-
const lastSlash = sandboxPath.lastIndexOf("/");
|
|
108
|
-
if (lastSlash <= 0)
|
|
109
|
-
return null;
|
|
110
|
-
const parent = sandboxPath.slice(0, lastSlash);
|
|
111
|
-
const segment = parent.slice(parent.lastIndexOf("/") + 1);
|
|
112
|
-
return segment ? `${segment}/` : null;
|
|
113
|
-
}
|
|
114
|
-
// ---------------------------------------------------------------------------
|
|
115
|
-
// Shared style constants
|
|
116
|
-
// ---------------------------------------------------------------------------
|
|
117
|
-
const FOCUS_RING_CLASSES = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:rounded-sm";
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
// Inline SVG icons
|
|
120
|
-
// ---------------------------------------------------------------------------
|
|
121
|
-
function FileIcon() {
|
|
122
|
-
return (_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M8 1H4C3.45 1 3 1.45 3 2V12C3 12.55 3.45 13 4 13H10C10.55 13 11 12.55 11 12V4L8 1Z" }), _jsx("path", { d: "M8 1V4H11" })] }));
|
|
123
|
-
}
|
|
124
|
-
function FolderIcon() {
|
|
125
|
-
return (_jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M13 11C13 11.55 12.55 12 12 12H2C1.45 12 1 11.55 1 11V3C1 2.45 1.45 2 2 2H5L7 4H12C12.55 4 13 4.45 13 5V11Z" }) }));
|
|
126
|
-
}
|
|
127
|
-
function DownloadIcon() {
|
|
128
|
-
return (_jsxs("svg", { width: "10", height: "10", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", className: "shrink-0", "aria-hidden": "true", children: [_jsx("path", { d: "M6 1.5V8.5" }), _jsx("path", { d: "M3 6L6 9L9 6" }), _jsx("path", { d: "M2 10.5H10" })] }));
|
|
129
|
-
}
|
|
130
|
-
//# sourceMappingURL=ArtifactCard.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ArtifactCard.js","sourceRoot":"","sources":["../../src/execution/ArtifactCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import type { ExecutionArtifact } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
|
|
4
|
-
import { ExecutionArtifactKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
5
|
-
import { cn } from "@stigmer/theme";
|
|
6
|
-
import { useArtifactContent } from "./useArtifactContent.js";
|
|
7
|
-
import { useArtifactDownload } from "./useArtifactDownload.js";
|
|
8
|
-
import { isTextArtifact, formatArtifactSize } from "./artifact-utils.js";
|
|
9
|
-
import { useDetectStigmerResource } from "../library/useDetectStigmerResource.js";
|
|
10
|
-
import { useDetectSkillPackage } from "../library/useDetectSkillPackage.js";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Artifacts larger than this threshold skip the content fetch used for
|
|
14
|
-
* resource detection. Stigmer resource YAML files are typically under
|
|
15
|
-
* 10 KB — 256 KB is generous without risking large payloads for log
|
|
16
|
-
* files or binary-adjacent text content.
|
|
17
|
-
*/
|
|
18
|
-
const MAX_DETECTION_SIZE = 256 * 1024;
|
|
19
|
-
|
|
20
|
-
/** Props for {@link ArtifactCard}. */
|
|
21
|
-
export interface ArtifactCardProps {
|
|
22
|
-
/** The execution artifact to render. */
|
|
23
|
-
readonly artifact: ExecutionArtifact;
|
|
24
|
-
/** ID of the execution that produced this artifact — used for content fetching. */
|
|
25
|
-
readonly executionId: string;
|
|
26
|
-
/**
|
|
27
|
-
* Organization slug — used for skill package detection which fetches
|
|
28
|
-
* `SKILL.md` frontmatter via the artifact content RPC.
|
|
29
|
-
*/
|
|
30
|
-
readonly org: string;
|
|
31
|
-
/**
|
|
32
|
-
* Called when the user clicks "Preview". The consumer is responsible
|
|
33
|
-
* for rendering a preview UI (e.g., {@link ArtifactPreviewModal}).
|
|
34
|
-
*
|
|
35
|
-
* When omitted, the Preview action is not rendered.
|
|
36
|
-
*/
|
|
37
|
-
readonly onPreview?: (artifact: ExecutionArtifact) => void;
|
|
38
|
-
/**
|
|
39
|
-
* When `true`, another artifact in the list shares the same display
|
|
40
|
-
* `name` but has a different `sandbox_path`. The card renders the
|
|
41
|
-
* parent directory from `sandbox_path` as a muted subtitle for
|
|
42
|
-
* disambiguation.
|
|
43
|
-
*
|
|
44
|
-
* Set by {@link ArtifactsWidget} via {@link useSessionArtifacts}.
|
|
45
|
-
* Defaults to `false`.
|
|
46
|
-
*/
|
|
47
|
-
readonly hasNameCollision?: boolean;
|
|
48
|
-
/** Additional CSS classes for the root element. */
|
|
49
|
-
readonly className?: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Renders a single execution artifact as a compact summary card with
|
|
54
|
-
* automatic Stigmer resource detection and a download link.
|
|
55
|
-
*
|
|
56
|
-
* The card's role is **signal and navigate**: it tells the user what
|
|
57
|
-
* the artifact is (file/directory, size, detected resource type) and
|
|
58
|
-
* provides two actions — Preview and Download. The Apply/Push CTA
|
|
59
|
-
* lives exclusively in {@link ArtifactPreviewModal} so the user must
|
|
60
|
-
* review content before acting (review-before-apply pattern).
|
|
61
|
-
*
|
|
62
|
-
* Internally orchestrates the detection pipeline:
|
|
63
|
-
*
|
|
64
|
-
* - **FILE artifacts**: Fetches text content via {@link useArtifactContent},
|
|
65
|
-
* then detects Agent/McpServer YAML via {@link useDetectStigmerResource}.
|
|
66
|
-
* Only text files under 256 KB are fetched for detection.
|
|
67
|
-
*
|
|
68
|
-
* - **DIRECTORY artifacts**: Detects skill packages via
|
|
69
|
-
* {@link useDetectSkillPackage} (checks for `SKILL.md` in the ZIP
|
|
70
|
-
* archive entries).
|
|
71
|
-
*
|
|
72
|
-
* Detection results are surfaced as a type badge ("Agent detected",
|
|
73
|
-
* "Skill · N files"). Non-Stigmer artifacts render with file info
|
|
74
|
-
* and a download link only — no error messages or "not detected"
|
|
75
|
-
* indicators (detection is silent).
|
|
76
|
-
*
|
|
77
|
-
* @example
|
|
78
|
-
* ```tsx
|
|
79
|
-
* const { artifacts } = useExecutionArtifacts(execution);
|
|
80
|
-
*
|
|
81
|
-
* {artifacts.map((artifact) => (
|
|
82
|
-
* <ArtifactCard
|
|
83
|
-
* key={artifact.storageKey}
|
|
84
|
-
* artifact={artifact}
|
|
85
|
-
* executionId={execution.metadata!.id}
|
|
86
|
-
* org={activeOrg}
|
|
87
|
-
* onPreview={(a) => setPreviewArtifact(a)}
|
|
88
|
-
* />
|
|
89
|
-
* ))}
|
|
90
|
-
* ```
|
|
91
|
-
*
|
|
92
|
-
* @see {@link ArtifactPreviewModal} — full preview with Apply/Push CTA
|
|
93
|
-
* @see {@link ArtifactsWidget} — container that composes cards + modal
|
|
94
|
-
* @see {@link useExecutionArtifacts} — extracts artifact metadata from an execution
|
|
95
|
-
* @see {@link useArtifactContent} — content-fetching hook (headless alternative)
|
|
96
|
-
* @see {@link useDetectStigmerResource} — YAML resource detection (headless)
|
|
97
|
-
* @see {@link useDetectSkillPackage} — skill package detection (headless)
|
|
98
|
-
*/
|
|
99
|
-
export function ArtifactCard({
|
|
100
|
-
artifact,
|
|
101
|
-
executionId,
|
|
102
|
-
org,
|
|
103
|
-
onPreview,
|
|
104
|
-
hasNameCollision = false,
|
|
105
|
-
className,
|
|
106
|
-
}: ArtifactCardProps) {
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
// Detection orchestration
|
|
109
|
-
// ---------------------------------------------------------------------------
|
|
110
|
-
|
|
111
|
-
const isDirectory = artifact.kind === ExecutionArtifactKind.DIRECTORY;
|
|
112
|
-
const canDetectYaml =
|
|
113
|
-
!isDirectory &&
|
|
114
|
-
isTextArtifact(artifact) &&
|
|
115
|
-
Number(artifact.sizeBytes) < MAX_DETECTION_SIZE;
|
|
116
|
-
|
|
117
|
-
const { content, isLoading: isContentLoading } = useArtifactContent(
|
|
118
|
-
canDetectYaml ? executionId : null,
|
|
119
|
-
canDetectYaml ? artifact.storageKey : null,
|
|
120
|
-
undefined,
|
|
121
|
-
artifact.contentHash || undefined,
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
const yamlDetection = useDetectStigmerResource(
|
|
125
|
-
canDetectYaml ? content : null,
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
const { detection: skillDetection, isLoading: isSkillLoading } =
|
|
129
|
-
useDetectSkillPackage(
|
|
130
|
-
isDirectory ? artifact : null,
|
|
131
|
-
isDirectory ? executionId : null,
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
// Derived detection state
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
|
-
|
|
138
|
-
const isDetected = yamlDetection.detected || skillDetection.detected;
|
|
139
|
-
const isDetecting =
|
|
140
|
-
(canDetectYaml && isContentLoading) || (isDirectory && isSkillLoading);
|
|
141
|
-
|
|
142
|
-
let detectionLabel: string | null = null;
|
|
143
|
-
if (yamlDetection.detected) {
|
|
144
|
-
detectionLabel = `${yamlDetection.displayName} detected`;
|
|
145
|
-
} else if (skillDetection.detected) {
|
|
146
|
-
const count = skillDetection.fileCount;
|
|
147
|
-
detectionLabel = `Skill \u00B7 ${count} ${count === 1 ? "file" : "files"}`;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const canPreview = !!onPreview && (isTextArtifact(artifact) || isDirectory);
|
|
151
|
-
|
|
152
|
-
const { download, isDownloading } = useArtifactDownload(executionId);
|
|
153
|
-
|
|
154
|
-
const parentDir =
|
|
155
|
-
hasNameCollision && artifact.sandboxPath
|
|
156
|
-
? parentDirectory(artifact.sandboxPath)
|
|
157
|
-
: null;
|
|
158
|
-
|
|
159
|
-
// ---------------------------------------------------------------------------
|
|
160
|
-
// Render
|
|
161
|
-
// ---------------------------------------------------------------------------
|
|
162
|
-
|
|
163
|
-
return (
|
|
164
|
-
<div
|
|
165
|
-
role="article"
|
|
166
|
-
aria-label={artifact.name}
|
|
167
|
-
className={cn("rounded-md border border-border p-3", className)}
|
|
168
|
-
>
|
|
169
|
-
{/* Header: icon + name + size */}
|
|
170
|
-
<div className="flex items-start gap-2">
|
|
171
|
-
<span className="mt-0.5 shrink-0 text-muted-foreground">
|
|
172
|
-
{isDirectory ? <FolderIcon /> : <FileIcon />}
|
|
173
|
-
</span>
|
|
174
|
-
<div className="min-w-0 flex-1">
|
|
175
|
-
<div className="truncate text-sm font-medium text-foreground">
|
|
176
|
-
{artifact.name}
|
|
177
|
-
{isDirectory && "/"}
|
|
178
|
-
</div>
|
|
179
|
-
{parentDir && (
|
|
180
|
-
<div className="truncate text-xs text-muted-foreground">
|
|
181
|
-
{parentDir}
|
|
182
|
-
</div>
|
|
183
|
-
)}
|
|
184
|
-
<div className="text-xs tabular-nums text-muted-foreground">
|
|
185
|
-
{formatArtifactSize(artifact.sizeBytes)}
|
|
186
|
-
</div>
|
|
187
|
-
</div>
|
|
188
|
-
</div>
|
|
189
|
-
|
|
190
|
-
{/* Detection badge */}
|
|
191
|
-
{isDetecting && !isDetected && (
|
|
192
|
-
<div
|
|
193
|
-
className="mt-1.5 h-5 w-28 animate-pulse rounded-full bg-muted"
|
|
194
|
-
aria-hidden="true"
|
|
195
|
-
/>
|
|
196
|
-
)}
|
|
197
|
-
{detectionLabel && (
|
|
198
|
-
<span className="mt-1.5 inline-flex items-center rounded-full bg-primary-subtle px-2 py-0.5 text-xs font-medium text-primary">
|
|
199
|
-
{detectionLabel}
|
|
200
|
-
</span>
|
|
201
|
-
)}
|
|
202
|
-
|
|
203
|
-
{/* Secondary actions: Preview + Download */}
|
|
204
|
-
<div className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-1">
|
|
205
|
-
{canPreview && (
|
|
206
|
-
<button
|
|
207
|
-
type="button"
|
|
208
|
-
onClick={() => onPreview!(artifact)}
|
|
209
|
-
className={cn(
|
|
210
|
-
"text-xs font-medium text-primary transition-colors hover:text-primary-muted",
|
|
211
|
-
FOCUS_RING_CLASSES,
|
|
212
|
-
)}
|
|
213
|
-
>
|
|
214
|
-
Preview
|
|
215
|
-
</button>
|
|
216
|
-
)}
|
|
217
|
-
<button
|
|
218
|
-
type="button"
|
|
219
|
-
onClick={() => download(artifact.storageKey, artifact.name)}
|
|
220
|
-
disabled={isDownloading}
|
|
221
|
-
className={cn(
|
|
222
|
-
"inline-flex items-center gap-1 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground disabled:opacity-50",
|
|
223
|
-
FOCUS_RING_CLASSES,
|
|
224
|
-
)}
|
|
225
|
-
>
|
|
226
|
-
<DownloadIcon />
|
|
227
|
-
{isDownloading ? "Preparing…" : isDirectory ? "Download ZIP" : "Download"}
|
|
228
|
-
</button>
|
|
229
|
-
</div>
|
|
230
|
-
|
|
231
|
-
</div>
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// ---------------------------------------------------------------------------
|
|
236
|
-
// Helpers
|
|
237
|
-
// ---------------------------------------------------------------------------
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Extracts a human-readable parent directory label from a sandbox path.
|
|
241
|
-
* Given `/workspace/configs/agent.yaml` returns `configs/`.
|
|
242
|
-
* Returns `null` when the path has no meaningful parent segment.
|
|
243
|
-
*/
|
|
244
|
-
function parentDirectory(sandboxPath: string): string | null {
|
|
245
|
-
const lastSlash = sandboxPath.lastIndexOf("/");
|
|
246
|
-
if (lastSlash <= 0) return null;
|
|
247
|
-
const parent = sandboxPath.slice(0, lastSlash);
|
|
248
|
-
const segment = parent.slice(parent.lastIndexOf("/") + 1);
|
|
249
|
-
return segment ? `${segment}/` : null;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// ---------------------------------------------------------------------------
|
|
253
|
-
// Shared style constants
|
|
254
|
-
// ---------------------------------------------------------------------------
|
|
255
|
-
|
|
256
|
-
const FOCUS_RING_CLASSES =
|
|
257
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:rounded-sm";
|
|
258
|
-
|
|
259
|
-
// ---------------------------------------------------------------------------
|
|
260
|
-
// Inline SVG icons
|
|
261
|
-
// ---------------------------------------------------------------------------
|
|
262
|
-
|
|
263
|
-
function FileIcon() {
|
|
264
|
-
return (
|
|
265
|
-
<svg
|
|
266
|
-
width="14"
|
|
267
|
-
height="14"
|
|
268
|
-
viewBox="0 0 14 14"
|
|
269
|
-
fill="none"
|
|
270
|
-
stroke="currentColor"
|
|
271
|
-
strokeWidth="1.5"
|
|
272
|
-
strokeLinecap="round"
|
|
273
|
-
strokeLinejoin="round"
|
|
274
|
-
aria-hidden="true"
|
|
275
|
-
>
|
|
276
|
-
<path d="M8 1H4C3.45 1 3 1.45 3 2V12C3 12.55 3.45 13 4 13H10C10.55 13 11 12.55 11 12V4L8 1Z" />
|
|
277
|
-
<path d="M8 1V4H11" />
|
|
278
|
-
</svg>
|
|
279
|
-
);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function FolderIcon() {
|
|
283
|
-
return (
|
|
284
|
-
<svg
|
|
285
|
-
width="14"
|
|
286
|
-
height="14"
|
|
287
|
-
viewBox="0 0 14 14"
|
|
288
|
-
fill="none"
|
|
289
|
-
stroke="currentColor"
|
|
290
|
-
strokeWidth="1.5"
|
|
291
|
-
strokeLinecap="round"
|
|
292
|
-
strokeLinejoin="round"
|
|
293
|
-
aria-hidden="true"
|
|
294
|
-
>
|
|
295
|
-
<path d="M13 11C13 11.55 12.55 12 12 12H2C1.45 12 1 11.55 1 11V3C1 2.45 1.45 2 2 2H5L7 4H12C12.55 4 13 4.45 13 5V11Z" />
|
|
296
|
-
</svg>
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function DownloadIcon() {
|
|
301
|
-
return (
|
|
302
|
-
<svg
|
|
303
|
-
width="10"
|
|
304
|
-
height="10"
|
|
305
|
-
viewBox="0 0 12 12"
|
|
306
|
-
fill="none"
|
|
307
|
-
stroke="currentColor"
|
|
308
|
-
strokeWidth="1.5"
|
|
309
|
-
strokeLinecap="round"
|
|
310
|
-
strokeLinejoin="round"
|
|
311
|
-
className="shrink-0"
|
|
312
|
-
aria-hidden="true"
|
|
313
|
-
>
|
|
314
|
-
<path d="M6 1.5V8.5" />
|
|
315
|
-
<path d="M3 6L6 9L9 6" />
|
|
316
|
-
<path d="M2 10.5H10" />
|
|
317
|
-
</svg>
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
|