@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
|
@@ -100,6 +100,11 @@ export interface UseArtifactContentReturn {
|
|
|
100
100
|
* When the same file is overwritten during execution, the `storageKey` stays
|
|
101
101
|
* stable but `contentHash` changes, triggering a re-fetch so the UI never
|
|
102
102
|
* shows stale content. Pass `undefined` or omit for backwards compatibility.
|
|
103
|
+
* @param cacheKey - Optional cross-mount cache key (DD-014). When set, a remount
|
|
104
|
+
* with the same key renders the previously-fetched content instantly and
|
|
105
|
+
* refetches in the background (no skeleton flash) — used by the artifact
|
|
106
|
+
* document so switching back to a recently-viewed tab is instant. Omit for the
|
|
107
|
+
* default fetch-on-mount behavior (card / modal / plan callers are unchanged).
|
|
103
108
|
*
|
|
104
109
|
* @example
|
|
105
110
|
* ```tsx
|
|
@@ -142,6 +147,7 @@ export function useArtifactContent(
|
|
|
142
147
|
storageKey: string | null,
|
|
143
148
|
entryPath?: string | null,
|
|
144
149
|
contentHash?: string,
|
|
150
|
+
cacheKey?: string,
|
|
145
151
|
): UseArtifactContentReturn {
|
|
146
152
|
const stigmer = useStigmer();
|
|
147
153
|
|
|
@@ -164,6 +170,7 @@ export function useArtifactContent(
|
|
|
164
170
|
: null,
|
|
165
171
|
[executionId, storageKey, entryPath, contentHash, stigmer],
|
|
166
172
|
EMPTY_ARTIFACT,
|
|
173
|
+
cacheKey ? { cacheKey } : undefined,
|
|
167
174
|
);
|
|
168
175
|
|
|
169
176
|
return {
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { create } from "@bufbuild/protobuf";
|
|
5
|
+
import { GetArtifactContentRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
|
|
6
|
+
import { useStigmer } from "../hooks.js";
|
|
7
|
+
import { toError } from "../internal/toError.js";
|
|
8
|
+
|
|
9
|
+
/** How long the transient `copied` flag stays true after a successful copy. */
|
|
10
|
+
const COPIED_FEEDBACK_MS = 2000;
|
|
11
|
+
|
|
12
|
+
/** Return value of {@link useArtifactCopy}. */
|
|
13
|
+
export interface UseArtifactCopyReturn {
|
|
14
|
+
/**
|
|
15
|
+
* Fetch the artifact's text at click time and write it to the clipboard.
|
|
16
|
+
* No-op when the execution id is unknown or a copy is already in flight.
|
|
17
|
+
* Resolves once the copy has been attempted.
|
|
18
|
+
*
|
|
19
|
+
* @param storageKey - Storage key of the artifact to copy.
|
|
20
|
+
*/
|
|
21
|
+
readonly copy: (storageKey: string) => Promise<void>;
|
|
22
|
+
|
|
23
|
+
/** `true` while the content is being fetched / written to the clipboard. */
|
|
24
|
+
readonly isCopying: boolean;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* `true` for a brief window after a successful copy (drives "Copied"
|
|
28
|
+
* affordance feedback), then resets automatically.
|
|
29
|
+
*/
|
|
30
|
+
readonly copied: boolean;
|
|
31
|
+
|
|
32
|
+
/** Error from the last failed copy attempt, or `null` when healthy. */
|
|
33
|
+
readonly error: Error | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Behavior hook that copies an execution artifact's text to the clipboard on
|
|
38
|
+
* demand.
|
|
39
|
+
*
|
|
40
|
+
* The imperative sibling of {@link useArtifactDownload}: it fetches content at
|
|
41
|
+
* click time via `getArtifactContent` (CORS-safe, through the Stigmer API)
|
|
42
|
+
* rather than requiring the caller to have loaded it. This suits content-free
|
|
43
|
+
* surfaces — a plan card that detects the artifact by convention and never
|
|
44
|
+
* fetches until the user acts — where a mount-time {@link useArtifactContent}
|
|
45
|
+
* would be wasteful.
|
|
46
|
+
*
|
|
47
|
+
* Returns an imperative `copy(storageKey)` action plus a transient `copied`
|
|
48
|
+
* flag for feedback, mirroring the modal's copy affordance so every surface
|
|
49
|
+
* shares one implementation.
|
|
50
|
+
*
|
|
51
|
+
* @param executionId - Execution that produced the artifacts, or `null` to
|
|
52
|
+
* disable (the action becomes a no-op).
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```tsx
|
|
56
|
+
* const { copy, isCopying, copied } = useArtifactCopy(executionId);
|
|
57
|
+
* <button onClick={() => copy(artifact.storageKey)} disabled={isCopying}>
|
|
58
|
+
* {copied ? "Copied" : "Copy"}
|
|
59
|
+
* </button>
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @see useArtifactDownload — imperative save-to-disk counterpart
|
|
63
|
+
* @see useArtifactContent — declarative content reader (preview rendering)
|
|
64
|
+
*/
|
|
65
|
+
export function useArtifactCopy(
|
|
66
|
+
executionId: string | null,
|
|
67
|
+
): UseArtifactCopyReturn {
|
|
68
|
+
const stigmer = useStigmer();
|
|
69
|
+
const [isCopying, setIsCopying] = useState(false);
|
|
70
|
+
const [copied, setCopied] = useState(false);
|
|
71
|
+
const [error, setError] = useState<Error | null>(null);
|
|
72
|
+
|
|
73
|
+
// Clear the pending "copied" timer on unmount so a resolved copy never sets
|
|
74
|
+
// state on an unmounted component.
|
|
75
|
+
const copiedTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
return () => {
|
|
78
|
+
if (copiedTimer.current !== null) clearTimeout(copiedTimer.current);
|
|
79
|
+
};
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
const copy = useCallback(
|
|
83
|
+
async (storageKey: string) => {
|
|
84
|
+
if (!executionId || !storageKey || isCopying) return;
|
|
85
|
+
setIsCopying(true);
|
|
86
|
+
setError(null);
|
|
87
|
+
try {
|
|
88
|
+
const result = await stigmer.agentExecution.getArtifactContent(
|
|
89
|
+
create(GetArtifactContentRequestSchema, { executionId, storageKey }),
|
|
90
|
+
);
|
|
91
|
+
const text = new TextDecoder().decode(result.content);
|
|
92
|
+
await navigator.clipboard.writeText(text);
|
|
93
|
+
setCopied(true);
|
|
94
|
+
if (copiedTimer.current !== null) clearTimeout(copiedTimer.current);
|
|
95
|
+
copiedTimer.current = setTimeout(() => setCopied(false), COPIED_FEEDBACK_MS);
|
|
96
|
+
} catch (err) {
|
|
97
|
+
setError(toError(err));
|
|
98
|
+
} finally {
|
|
99
|
+
setIsCopying(false);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
[executionId, stigmer, isCopying],
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return useMemo(
|
|
106
|
+
() => ({ copy, isCopying, copied, error }),
|
|
107
|
+
[copy, isCopying, copied, error],
|
|
108
|
+
);
|
|
109
|
+
}
|
|
@@ -65,7 +65,16 @@ export function useArtifactDownload(
|
|
|
65
65
|
setError(null);
|
|
66
66
|
try {
|
|
67
67
|
const result = await stigmer.agentExecution.getArtifactDownloadUrl(
|
|
68
|
-
create(GetArtifactDownloadUrlRequestSchema, {
|
|
68
|
+
create(GetArtifactDownloadUrlRequestSchema, {
|
|
69
|
+
executionId,
|
|
70
|
+
storageKey,
|
|
71
|
+
// This is a save-to-disk action, so mint a URL that forces a
|
|
72
|
+
// browser download (Content-Disposition: attachment). Browsers
|
|
73
|
+
// ignore the anchor's `download` attribute cross-origin, so the
|
|
74
|
+
// disposition must ride on the URL itself. Contrast
|
|
75
|
+
// useArtifactDownloadUrl, which stays inline for `<img src>`.
|
|
76
|
+
asAttachment: true,
|
|
77
|
+
}),
|
|
69
78
|
);
|
|
70
79
|
if (result.downloadUrl) {
|
|
71
80
|
triggerBrowserDownload(result.downloadUrl, fileName);
|
|
@@ -86,10 +95,13 @@ export function useArtifactDownload(
|
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
/**
|
|
89
|
-
* Trigger a browser download for a URL via a transient anchor click.
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
98
|
+
* Trigger a browser download for a URL via a transient anchor click.
|
|
99
|
+
*
|
|
100
|
+
* The URL minted for this flow carries `Content-Disposition: attachment`
|
|
101
|
+
* (requested via `asAttachment`), so the browser saves the file even
|
|
102
|
+
* cross-origin — where it would ignore the anchor's `download` attribute. The
|
|
103
|
+
* `download` hint is still set as a same-origin nicety. Falls back to a new tab
|
|
104
|
+
* in non-DOM environments.
|
|
93
105
|
*/
|
|
94
106
|
function triggerBrowserDownload(url: string, fileName?: string): void {
|
|
95
107
|
if (typeof document === "undefined") {
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
|
+
import type { ExecutionArtifact } 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 { useArtifactContent } from "./useArtifactContent.js";
|
|
7
|
+
import { isTextArtifact } from "./artifact-utils.js";
|
|
8
|
+
import { useDetectStigmerResource } from "../library/useDetectStigmerResource.js";
|
|
9
|
+
import { useDetectSkillPackage } from "../library/useDetectSkillPackage.js";
|
|
10
|
+
import type { SkillPackageDetection } from "../library/detect-skill-package.js";
|
|
11
|
+
import {
|
|
12
|
+
useApplyResource,
|
|
13
|
+
type ApplyResourceResult,
|
|
14
|
+
} from "../library/useApplyResource.js";
|
|
15
|
+
|
|
16
|
+
const COPIED_FEEDBACK_MS = 2000;
|
|
17
|
+
|
|
18
|
+
/** Options for {@link useArtifactInspection}. */
|
|
19
|
+
export interface UseArtifactInspectionOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Cross-mount cache key for the content fetch (DD-014). Set it so a remount
|
|
22
|
+
* with the same key renders the previously-fetched content instantly and
|
|
23
|
+
* refetches in the background — the artifact document passes it so switching
|
|
24
|
+
* back to a recently-viewed tab is instant. Omit for fetch-on-mount (the
|
|
25
|
+
* modal, which resets on every open).
|
|
26
|
+
*/
|
|
27
|
+
readonly cacheKey?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Called after a resource is successfully applied or a skill package pushed.
|
|
30
|
+
* The consumer uses it for post-apply behavior (toast, navigate to Library).
|
|
31
|
+
*/
|
|
32
|
+
readonly onApplied?: (result: ApplyResourceResult) => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Everything a chrome (the modal action bar, the document toolbar, the content
|
|
37
|
+
* body) needs to inspect and act on one artifact: fetched content, resource
|
|
38
|
+
* detection, the Apply/Push action, and clipboard copy.
|
|
39
|
+
*/
|
|
40
|
+
export interface ArtifactInspection {
|
|
41
|
+
/** Decoded text content, or `null` (directory / binary / loading / error). */
|
|
42
|
+
readonly content: string | null;
|
|
43
|
+
/** Server-detected content type, or `null`. */
|
|
44
|
+
readonly contentType: string | null;
|
|
45
|
+
/** Whether the fetched content was truncated by the server's size cap. */
|
|
46
|
+
readonly isTruncated: boolean;
|
|
47
|
+
/** `true` while the content request is in-flight (first load). */
|
|
48
|
+
readonly isLoading: boolean;
|
|
49
|
+
/** Content-fetch error, or `null`. */
|
|
50
|
+
readonly error: Error | null;
|
|
51
|
+
/** Re-fetch the artifact content. */
|
|
52
|
+
readonly refetch: () => void;
|
|
53
|
+
|
|
54
|
+
/** Whether the artifact is a directory (skill package candidate). */
|
|
55
|
+
readonly isDirectory: boolean;
|
|
56
|
+
/** Skill-package detection for directory artifacts. */
|
|
57
|
+
readonly skillDetection: SkillPackageDetection;
|
|
58
|
+
|
|
59
|
+
/** `true` when the artifact is a recognized Stigmer resource or skill. */
|
|
60
|
+
readonly isDetected: boolean;
|
|
61
|
+
/** `true` while detection is still resolving (content / skill fetch). */
|
|
62
|
+
readonly isDetecting: boolean;
|
|
63
|
+
/** Detection badge label (`"Agent detected"`, `"Skill · N files"`), or `null`. */
|
|
64
|
+
readonly detectionLabel: string | null;
|
|
65
|
+
|
|
66
|
+
/** Apply/Push CTA label (`"Apply to org"`, `"Push Skill to org"`), or `null`. */
|
|
67
|
+
readonly ctaLabel: string | null;
|
|
68
|
+
/** Apply the detected resource / push the detected skill. */
|
|
69
|
+
readonly apply: () => Promise<void>;
|
|
70
|
+
/** `true` while an apply/push is in-flight. */
|
|
71
|
+
readonly isApplying: boolean;
|
|
72
|
+
/** The successful apply result, or `null`. */
|
|
73
|
+
readonly applyResult: ApplyResourceResult | null;
|
|
74
|
+
/** Apply/push error, or `null`. */
|
|
75
|
+
readonly applyError: Error | null;
|
|
76
|
+
|
|
77
|
+
/** Copy the in-memory content to the clipboard. No-op without content. */
|
|
78
|
+
readonly copy: () => void;
|
|
79
|
+
/** `true` for a short window after a successful copy. */
|
|
80
|
+
readonly copied: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Headless behavior hook consolidating the artifact inspect-and-act pipeline
|
|
85
|
+
* that was previously inlined per artifact surface: content fetch,
|
|
86
|
+
* Agent/McpServer YAML detection, skill-package detection, the Apply/Push
|
|
87
|
+
* mutation, and in-memory clipboard copy.
|
|
88
|
+
*
|
|
89
|
+
* It is the single writer of this pipeline's derived state, consumed by every
|
|
90
|
+
* artifact chrome — the modal action bar and the editor-area
|
|
91
|
+
* `ArtifactDocument` toolbar (DD-003 headless-first). Because there is exactly
|
|
92
|
+
* one place computing `detectionLabel` / `ctaLabel` / the apply flow, the two
|
|
93
|
+
* surfaces cannot drift.
|
|
94
|
+
*
|
|
95
|
+
* All four underlying hooks are called unconditionally with null-gated
|
|
96
|
+
* arguments (their documented skip contract), so this composition is
|
|
97
|
+
* React-rules-clean regardless of the artifact kind.
|
|
98
|
+
*
|
|
99
|
+
* @param artifact - The artifact to inspect.
|
|
100
|
+
* @param executionId - Execution that produced it (content + skill fetching).
|
|
101
|
+
* @param org - Organization for the "Apply to [org]" / "Push Skill to [org]" CTA.
|
|
102
|
+
* @param options - {@link UseArtifactInspectionOptions}.
|
|
103
|
+
*/
|
|
104
|
+
export function useArtifactInspection(
|
|
105
|
+
artifact: ExecutionArtifact,
|
|
106
|
+
executionId: string,
|
|
107
|
+
org: string,
|
|
108
|
+
options?: UseArtifactInspectionOptions,
|
|
109
|
+
): ArtifactInspection {
|
|
110
|
+
const isDirectory = artifact.kind === ExecutionArtifactKind.DIRECTORY;
|
|
111
|
+
const canFetchContent = !isDirectory && isTextArtifact(artifact);
|
|
112
|
+
|
|
113
|
+
const {
|
|
114
|
+
content,
|
|
115
|
+
contentType,
|
|
116
|
+
isTruncated,
|
|
117
|
+
isLoading,
|
|
118
|
+
error,
|
|
119
|
+
refetch,
|
|
120
|
+
} = useArtifactContent(
|
|
121
|
+
canFetchContent ? executionId : null,
|
|
122
|
+
canFetchContent ? artifact.storageKey : null,
|
|
123
|
+
undefined,
|
|
124
|
+
artifact.contentHash || undefined,
|
|
125
|
+
options?.cacheKey,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const yamlDetection = useDetectStigmerResource(
|
|
129
|
+
canFetchContent ? content : null,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const { detection: skillDetection, isLoading: isSkillLoading } =
|
|
133
|
+
useDetectSkillPackage(
|
|
134
|
+
isDirectory ? artifact : null,
|
|
135
|
+
isDirectory ? executionId : null,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const isDetected = yamlDetection.detected || skillDetection.detected;
|
|
139
|
+
const isDetecting =
|
|
140
|
+
(canFetchContent && isLoading) || (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
|
+
let ctaLabel: string | null = null;
|
|
151
|
+
if (yamlDetection.detected) {
|
|
152
|
+
ctaLabel = `Apply to ${org}`;
|
|
153
|
+
} else if (skillDetection.detected) {
|
|
154
|
+
ctaLabel = `Push Skill to ${org}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// --- Apply / Push ---------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
const { applyYamlResource, pushSkillPackage, isApplying, error: applyError, clearError } =
|
|
160
|
+
useApplyResource();
|
|
161
|
+
const [applyResult, setApplyResult] = useState<ApplyResourceResult | null>(
|
|
162
|
+
null,
|
|
163
|
+
);
|
|
164
|
+
const onApplied = options?.onApplied;
|
|
165
|
+
|
|
166
|
+
const apply = useCallback(async () => {
|
|
167
|
+
clearError();
|
|
168
|
+
try {
|
|
169
|
+
let result: ApplyResourceResult;
|
|
170
|
+
if (yamlDetection.detected && content) {
|
|
171
|
+
result = await applyYamlResource(content, org);
|
|
172
|
+
} else if (skillDetection.detected) {
|
|
173
|
+
result = await pushSkillPackage({
|
|
174
|
+
org,
|
|
175
|
+
executionId,
|
|
176
|
+
storageKey: artifact.storageKey,
|
|
177
|
+
});
|
|
178
|
+
} else {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
setApplyResult(result);
|
|
182
|
+
onApplied?.(result);
|
|
183
|
+
} catch {
|
|
184
|
+
// error state is owned by useApplyResource
|
|
185
|
+
}
|
|
186
|
+
}, [
|
|
187
|
+
yamlDetection.detected,
|
|
188
|
+
skillDetection.detected,
|
|
189
|
+
content,
|
|
190
|
+
org,
|
|
191
|
+
executionId,
|
|
192
|
+
artifact.storageKey,
|
|
193
|
+
applyYamlResource,
|
|
194
|
+
pushSkillPackage,
|
|
195
|
+
clearError,
|
|
196
|
+
onApplied,
|
|
197
|
+
]);
|
|
198
|
+
|
|
199
|
+
// --- Copy -----------------------------------------------------------------
|
|
200
|
+
|
|
201
|
+
const [copied, setCopied] = useState(false);
|
|
202
|
+
const copy = useCallback(() => {
|
|
203
|
+
if (!content) return;
|
|
204
|
+
void navigator.clipboard.writeText(content).then(() => {
|
|
205
|
+
setCopied(true);
|
|
206
|
+
setTimeout(() => setCopied(false), COPIED_FEEDBACK_MS);
|
|
207
|
+
});
|
|
208
|
+
}, [content]);
|
|
209
|
+
|
|
210
|
+
return useMemo(
|
|
211
|
+
() => ({
|
|
212
|
+
content,
|
|
213
|
+
contentType,
|
|
214
|
+
isTruncated,
|
|
215
|
+
isLoading,
|
|
216
|
+
error,
|
|
217
|
+
refetch,
|
|
218
|
+
isDirectory,
|
|
219
|
+
skillDetection,
|
|
220
|
+
isDetected,
|
|
221
|
+
isDetecting,
|
|
222
|
+
detectionLabel,
|
|
223
|
+
ctaLabel,
|
|
224
|
+
apply,
|
|
225
|
+
isApplying,
|
|
226
|
+
applyResult,
|
|
227
|
+
applyError,
|
|
228
|
+
copy,
|
|
229
|
+
copied,
|
|
230
|
+
}),
|
|
231
|
+
[
|
|
232
|
+
content,
|
|
233
|
+
contentType,
|
|
234
|
+
isTruncated,
|
|
235
|
+
isLoading,
|
|
236
|
+
error,
|
|
237
|
+
refetch,
|
|
238
|
+
isDirectory,
|
|
239
|
+
skillDetection,
|
|
240
|
+
isDetected,
|
|
241
|
+
isDetecting,
|
|
242
|
+
detectionLabel,
|
|
243
|
+
ctaLabel,
|
|
244
|
+
apply,
|
|
245
|
+
isApplying,
|
|
246
|
+
applyResult,
|
|
247
|
+
applyError,
|
|
248
|
+
copy,
|
|
249
|
+
copied,
|
|
250
|
+
],
|
|
251
|
+
);
|
|
252
|
+
}
|
|
@@ -55,11 +55,13 @@ export interface CreateAgentExecutionInput {
|
|
|
55
55
|
*/
|
|
56
56
|
readonly interactionMode?: "agent" | "plan";
|
|
57
57
|
/**
|
|
58
|
-
* Marks this execution as a
|
|
58
|
+
* Marks this execution as a Build-from-plan turn: the user approved a
|
|
59
59
|
* plan from a prior Plan-mode execution and asked the agent to implement
|
|
60
60
|
* it. The runner injects the implement-plan directive (pointing at the
|
|
61
|
-
* attached approved plan when present); the thread
|
|
62
|
-
*
|
|
61
|
+
* attached approved plan when present); the thread hides the turn's
|
|
62
|
+
* machine-written message entirely — the plan card above it is the
|
|
63
|
+
* visible cause. Surfaces without that treatment (the CLI, history)
|
|
64
|
+
* show the message text as-is.
|
|
63
65
|
*
|
|
64
66
|
* Maps to `ExecutionConfig.build_from_plan` in the proto.
|
|
65
67
|
*/
|
|
@@ -91,6 +93,17 @@ export interface CreateAgentExecutionInput {
|
|
|
91
93
|
* Populated by the drag-to-reference feature in SessionComposer.
|
|
92
94
|
*/
|
|
93
95
|
readonly workspaceFileRefs?: string[];
|
|
96
|
+
/**
|
|
97
|
+
* ID of the execution this one supersedes via edit-and-resubmit.
|
|
98
|
+
*
|
|
99
|
+
* Set when the user stopped an in-flight turn, edited its message, and
|
|
100
|
+
* resubmitted. Chat threads hide the superseded execution so the edited
|
|
101
|
+
* message replaces the original in place; history surfaces keep the full
|
|
102
|
+
* record. Maps to `AgentExecutionSpec.supersedes_execution_id`.
|
|
103
|
+
*
|
|
104
|
+
* Display-level only — the runner does not rewind model context.
|
|
105
|
+
*/
|
|
106
|
+
readonly supersedesExecutionId?: string;
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
/** Resolved output of {@link UseCreateAgentExecutionReturn.create}. */
|
|
@@ -197,6 +210,7 @@ export function useCreateAgentExecution(): UseCreateAgentExecutionReturn {
|
|
|
197
210
|
runtimeEnv: input.runtimeEnv,
|
|
198
211
|
attachments: input.attachments,
|
|
199
212
|
workspaceFileRefs: input.workspaceFileRefs,
|
|
213
|
+
supersedesExecutionId: input.supersedesExecutionId,
|
|
200
214
|
});
|
|
201
215
|
|
|
202
216
|
return {
|
package/src/index.ts
CHANGED
|
@@ -129,6 +129,7 @@ export {
|
|
|
129
129
|
useSessionExecutions,
|
|
130
130
|
useSessionConversation,
|
|
131
131
|
useSessionArtifacts,
|
|
132
|
+
artifactKey,
|
|
132
133
|
useSessionWriteBacks,
|
|
133
134
|
useSessionFileChanges,
|
|
134
135
|
useSessionUsage,
|
|
@@ -154,6 +155,7 @@ export {
|
|
|
154
155
|
PlanStreamingDocument,
|
|
155
156
|
PLAN_DOCUMENT_ENTRY_ID,
|
|
156
157
|
PLAN_DOCUMENT_PATH,
|
|
158
|
+
ARTIFACT_DOCUMENT_ENTRY_ID,
|
|
157
159
|
SetupTab,
|
|
158
160
|
} from "./session/index.js";
|
|
159
161
|
export type {
|
|
@@ -283,12 +285,16 @@ export {
|
|
|
283
285
|
ApprovalCardBody,
|
|
284
286
|
FileReviewCard,
|
|
285
287
|
FileReviewDock,
|
|
288
|
+
FileChangeProgressBar,
|
|
286
289
|
ApprovalContext,
|
|
287
290
|
useApproval,
|
|
288
291
|
FileReviewContext,
|
|
289
292
|
useFileReviewRowState,
|
|
290
|
-
|
|
293
|
+
ArtifactRow,
|
|
291
294
|
ArtifactContentRenderer,
|
|
295
|
+
ArtifactContentBody,
|
|
296
|
+
ArtifactDocument,
|
|
297
|
+
useArtifactInspection,
|
|
292
298
|
ArtifactPreviewContent,
|
|
293
299
|
ArtifactPreviewModal,
|
|
294
300
|
ArtifactsWidget,
|
|
@@ -307,6 +313,7 @@ export {
|
|
|
307
313
|
useArtifactContent,
|
|
308
314
|
useArtifactDownloadUrl,
|
|
309
315
|
useArtifactDownload,
|
|
316
|
+
useArtifactCopy,
|
|
310
317
|
useToolOutputContent,
|
|
311
318
|
useFileChangeContent,
|
|
312
319
|
execIdFromStorageKey,
|
|
@@ -375,11 +382,16 @@ export type {
|
|
|
375
382
|
ApprovalCardBodyProps,
|
|
376
383
|
FileReviewCardProps,
|
|
377
384
|
FileReviewDockProps,
|
|
385
|
+
FileChangeProgressBarProps,
|
|
378
386
|
ApprovalContextValue,
|
|
379
387
|
UseApprovalResult,
|
|
380
388
|
FileReviewContextValue,
|
|
381
|
-
|
|
389
|
+
ArtifactRowProps,
|
|
382
390
|
ArtifactContentRendererProps,
|
|
391
|
+
ArtifactContentBodyProps,
|
|
392
|
+
ArtifactDocumentProps,
|
|
393
|
+
ArtifactInspection,
|
|
394
|
+
UseArtifactInspectionOptions,
|
|
383
395
|
ArtifactRenderMode,
|
|
384
396
|
ArtifactPreviewContentProps,
|
|
385
397
|
ArtifactPreviewModalProps,
|
|
@@ -397,6 +409,7 @@ export type {
|
|
|
397
409
|
UseArtifactDownloadUrlReturn,
|
|
398
410
|
UseArtifactDownloadUrlOptions,
|
|
399
411
|
UseArtifactDownloadReturn,
|
|
412
|
+
UseArtifactCopyReturn,
|
|
400
413
|
UseToolOutputContentReturn,
|
|
401
414
|
ToolOutputRefLike,
|
|
402
415
|
UseFileChangeContentReturn,
|
|
@@ -980,10 +993,12 @@ export {
|
|
|
980
993
|
detectSkillPackage,
|
|
981
994
|
useDetectSkillPackage,
|
|
982
995
|
isPlanArtifact,
|
|
996
|
+
isPlanArtifactName,
|
|
983
997
|
findPlanArtifact,
|
|
984
998
|
findLatestSessionPlan,
|
|
985
999
|
findStreamingPlan,
|
|
986
1000
|
PLAN_ARTIFACT_NAME,
|
|
1001
|
+
PLAN_ARTIFACT_SUFFIX,
|
|
987
1002
|
parseResourceYaml,
|
|
988
1003
|
serializeAgentYaml,
|
|
989
1004
|
serializeMcpServerYaml,
|