@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.
Files changed (285) hide show
  1. package/composer/SessionComposer.d.ts +37 -5
  2. package/composer/SessionComposer.d.ts.map +1 -1
  3. package/composer/SessionComposer.js +13 -5
  4. package/composer/SessionComposer.js.map +1 -1
  5. package/execution/ArtifactApplyButton.d.ts +25 -0
  6. package/execution/ArtifactApplyButton.d.ts.map +1 -0
  7. package/execution/ArtifactApplyButton.js +21 -0
  8. package/execution/ArtifactApplyButton.js.map +1 -0
  9. package/execution/ArtifactContentBody.d.ts +36 -0
  10. package/execution/ArtifactContentBody.d.ts.map +1 -0
  11. package/execution/ArtifactContentBody.js +62 -0
  12. package/execution/ArtifactContentBody.js.map +1 -0
  13. package/execution/ArtifactDocument.d.ts +48 -0
  14. package/execution/ArtifactDocument.d.ts.map +1 -0
  15. package/execution/ArtifactDocument.js +89 -0
  16. package/execution/ArtifactDocument.js.map +1 -0
  17. package/execution/ArtifactPreviewModal.d.ts +9 -9
  18. package/execution/ArtifactPreviewModal.d.ts.map +1 -1
  19. package/execution/ArtifactPreviewModal.js +18 -145
  20. package/execution/ArtifactPreviewModal.js.map +1 -1
  21. package/execution/ArtifactRow.d.ts +49 -0
  22. package/execution/ArtifactRow.d.ts.map +1 -0
  23. package/execution/ArtifactRow.js +56 -0
  24. package/execution/ArtifactRow.js.map +1 -0
  25. package/execution/ArtifactsWidget.d.ts +6 -9
  26. package/execution/ArtifactsWidget.d.ts.map +1 -1
  27. package/execution/ArtifactsWidget.js +15 -19
  28. package/execution/ArtifactsWidget.js.map +1 -1
  29. package/execution/FileChangeProgressBar.d.ts +47 -0
  30. package/execution/FileChangeProgressBar.d.ts.map +1 -0
  31. package/execution/FileChangeProgressBar.js +49 -0
  32. package/execution/FileChangeProgressBar.js.map +1 -0
  33. package/execution/FileChangesView.d.ts.map +1 -1
  34. package/execution/FileChangesView.js +19 -4
  35. package/execution/FileChangesView.js.map +1 -1
  36. package/execution/FileReviewAtoms.d.ts +27 -0
  37. package/execution/FileReviewAtoms.d.ts.map +1 -0
  38. package/execution/FileReviewAtoms.js +64 -0
  39. package/execution/FileReviewAtoms.js.map +1 -0
  40. package/execution/FileReviewCard.d.ts +2 -2
  41. package/execution/FileReviewCard.d.ts.map +1 -1
  42. package/execution/FileReviewCard.js +32 -62
  43. package/execution/FileReviewCard.js.map +1 -1
  44. package/execution/MessageEntry.d.ts +0 -9
  45. package/execution/MessageEntry.d.ts.map +1 -1
  46. package/execution/MessageEntry.js +1 -11
  47. package/execution/MessageEntry.js.map +1 -1
  48. package/execution/MessageThread.d.ts +3 -4
  49. package/execution/MessageThread.d.ts.map +1 -1
  50. package/execution/MessageThread.js +25 -11
  51. package/execution/MessageThread.js.map +1 -1
  52. package/execution/PlanArtifactCard.d.ts +16 -10
  53. package/execution/PlanArtifactCard.d.ts.map +1 -1
  54. package/execution/PlanArtifactCard.js +31 -7
  55. package/execution/PlanArtifactCard.js.map +1 -1
  56. package/execution/PlanCompletionCard.d.ts +4 -4
  57. package/execution/PlanCompletionCard.d.ts.map +1 -1
  58. package/execution/PlanCompletionCard.js +4 -4
  59. package/execution/PlanCompletionCard.js.map +1 -1
  60. package/execution/PlanStreamingCard.d.ts +7 -2
  61. package/execution/PlanStreamingCard.d.ts.map +1 -1
  62. package/execution/PlanStreamingCard.js +11 -6
  63. package/execution/PlanStreamingCard.js.map +1 -1
  64. package/execution/index.d.ts +12 -2
  65. package/execution/index.d.ts.map +1 -1
  66. package/execution/index.js +6 -1
  67. package/execution/index.js.map +1 -1
  68. package/execution/use-build-from-plan-hotkey.d.ts +1 -1
  69. package/execution/use-build-from-plan-hotkey.js +1 -1
  70. package/execution/useArtifactContent.d.ts +6 -1
  71. package/execution/useArtifactContent.d.ts.map +1 -1
  72. package/execution/useArtifactContent.js +7 -2
  73. package/execution/useArtifactContent.js.map +1 -1
  74. package/execution/useArtifactCopy.d.ts +51 -0
  75. package/execution/useArtifactCopy.d.ts.map +1 -0
  76. package/execution/useArtifactCopy.js +75 -0
  77. package/execution/useArtifactCopy.js.map +1 -0
  78. package/execution/useArtifactDownload.d.ts.map +1 -1
  79. package/execution/useArtifactDownload.js +17 -5
  80. package/execution/useArtifactDownload.js.map +1 -1
  81. package/execution/useArtifactInspection.d.ts +85 -0
  82. package/execution/useArtifactInspection.d.ts.map +1 -0
  83. package/execution/useArtifactInspection.js +143 -0
  84. package/execution/useArtifactInspection.js.map +1 -0
  85. package/execution/useCreateAgentExecution.d.ts +16 -3
  86. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  87. package/execution/useCreateAgentExecution.js +1 -0
  88. package/execution/useCreateAgentExecution.js.map +1 -1
  89. package/index.d.ts +4 -4
  90. package/index.d.ts.map +1 -1
  91. package/index.js +3 -3
  92. package/index.js.map +1 -1
  93. package/internal/MermaidDiagram.d.ts +47 -0
  94. package/internal/MermaidDiagram.d.ts.map +1 -0
  95. package/internal/MermaidDiagram.js +118 -0
  96. package/internal/MermaidDiagram.js.map +1 -0
  97. package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
  98. package/internal/file-tree/FileTreeNode.js +2 -2
  99. package/internal/file-tree/FileTreeNode.js.map +1 -1
  100. package/internal/markdown-components.d.ts.map +1 -1
  101. package/internal/markdown-components.js +34 -0
  102. package/internal/markdown-components.js.map +1 -1
  103. package/internal/mermaid-loader.d.ts +26 -0
  104. package/internal/mermaid-loader.d.ts.map +1 -0
  105. package/internal/mermaid-loader.js +34 -0
  106. package/internal/mermaid-loader.js.map +1 -0
  107. package/internal/mermaid-theme-css.d.ts +41 -0
  108. package/internal/mermaid-theme-css.d.ts.map +1 -0
  109. package/internal/mermaid-theme-css.js +179 -0
  110. package/internal/mermaid-theme-css.js.map +1 -0
  111. package/internal/slug.d.ts +9 -5
  112. package/internal/slug.d.ts.map +1 -1
  113. package/internal/slug.js +10 -6
  114. package/internal/slug.js.map +1 -1
  115. package/library/detect-plan-artifact.d.ts +27 -9
  116. package/library/detect-plan-artifact.d.ts.map +1 -1
  117. package/library/detect-plan-artifact.js +26 -6
  118. package/library/detect-plan-artifact.js.map +1 -1
  119. package/library/index.d.ts +1 -1
  120. package/library/index.d.ts.map +1 -1
  121. package/library/index.js +1 -1
  122. package/library/index.js.map +1 -1
  123. package/package.json +5 -4
  124. package/session/PlanEditor.js +1 -1
  125. package/session/PlanEditor.js.map +1 -1
  126. package/session/SessionViewer.d.ts.map +1 -1
  127. package/session/SessionViewer.js +122 -30
  128. package/session/SessionViewer.js.map +1 -1
  129. package/session/artifact-document.d.ts +13 -0
  130. package/session/artifact-document.d.ts.map +1 -0
  131. package/session/artifact-document.js +22 -0
  132. package/session/artifact-document.js.map +1 -0
  133. package/session/facets/ArtifactsTab.d.ts +30 -4
  134. package/session/facets/ArtifactsTab.d.ts.map +1 -1
  135. package/session/facets/ArtifactsTab.js +32 -10
  136. package/session/facets/ArtifactsTab.js.map +1 -1
  137. package/session/index.d.ts +2 -1
  138. package/session/index.d.ts.map +1 -1
  139. package/session/index.js +4 -1
  140. package/session/index.js.map +1 -1
  141. package/session/plan-document.d.ts +14 -2
  142. package/session/plan-document.d.ts.map +1 -1
  143. package/session/plan-document.js +14 -3
  144. package/session/plan-document.js.map +1 -1
  145. package/session/usePlanDraft.d.ts +1 -1
  146. package/session/useSessionArtifacts.d.ts +17 -0
  147. package/session/useSessionArtifacts.d.ts.map +1 -1
  148. package/session/useSessionArtifacts.js +16 -5
  149. package/session/useSessionArtifacts.js.map +1 -1
  150. package/session/useSessionConversation.d.ts +26 -3
  151. package/session/useSessionConversation.d.ts.map +1 -1
  152. package/session/useSessionConversation.js +51 -6
  153. package/session/useSessionConversation.js.map +1 -1
  154. package/session/useSessionPageFlow.d.ts.map +1 -1
  155. package/session/useSessionPageFlow.js +1 -0
  156. package/session/useSessionPageFlow.js.map +1 -1
  157. package/session/useSessionPanel.d.ts +20 -0
  158. package/session/useSessionPanel.d.ts.map +1 -1
  159. package/session/useSessionPanel.js +13 -0
  160. package/session/useSessionPanel.js.map +1 -1
  161. package/session/useSessionRailViews.d.ts +13 -1
  162. package/session/useSessionRailViews.d.ts.map +1 -1
  163. package/session/useSessionRailViews.js +5 -3
  164. package/session/useSessionRailViews.js.map +1 -1
  165. package/src/composer/SessionComposer.tsx +76 -7
  166. package/src/composer/__tests__/SessionComposer-editing.test.tsx +128 -0
  167. package/src/execution/ArtifactApplyButton.tsx +79 -0
  168. package/src/execution/ArtifactContentBody.tsx +255 -0
  169. package/src/execution/ArtifactDocument.tsx +344 -0
  170. package/src/execution/ArtifactPreviewModal.tsx +49 -415
  171. package/src/execution/ArtifactRow.tsx +165 -0
  172. package/src/execution/ArtifactsWidget.tsx +24 -33
  173. package/src/execution/FileChangeProgressBar.tsx +121 -0
  174. package/src/execution/FileChangesView.tsx +18 -4
  175. package/src/execution/FileReviewAtoms.tsx +89 -0
  176. package/src/execution/FileReviewCard.tsx +57 -90
  177. package/src/execution/MessageEntry.tsx +0 -62
  178. package/src/execution/MessageThread.tsx +29 -15
  179. package/src/execution/PlanArtifactCard.tsx +130 -52
  180. package/src/execution/PlanCompletionCard.tsx +5 -5
  181. package/src/execution/PlanStreamingCard.tsx +15 -11
  182. package/src/execution/__tests__/ArtifactContentBody.test.tsx +131 -0
  183. package/src/execution/__tests__/ArtifactDocument.test.tsx +100 -0
  184. package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +6 -6
  185. package/src/execution/__tests__/ArtifactRow.test.tsx +139 -0
  186. package/src/execution/__tests__/ArtifactsWidget.test.tsx +78 -0
  187. package/src/execution/__tests__/FileChangeProgressBar.test.tsx +125 -0
  188. package/src/execution/__tests__/FileReviewCard.test.tsx +66 -7
  189. package/src/execution/__tests__/MessageThread.test.tsx +1 -1
  190. package/src/execution/__tests__/PlanArtifactCard.test.tsx +52 -18
  191. package/src/execution/__tests__/PlanCompletionCard.test.tsx +2 -2
  192. package/src/execution/__tests__/PlanStreamingCard.test.tsx +11 -4
  193. package/src/execution/__tests__/buildThreadItems-plan-completion.test.ts +104 -8
  194. package/src/execution/__tests__/message-entry.test.tsx +56 -39
  195. package/src/execution/__tests__/useArtifactCopy.test.tsx +90 -0
  196. package/src/execution/__tests__/useArtifactDownload.test.tsx +2 -0
  197. package/src/execution/__tests__/useArtifactInspection.test.tsx +151 -0
  198. package/src/execution/__tests__/useCreateAgentExecution.test.tsx +35 -0
  199. package/src/execution/index.ts +20 -2
  200. package/src/execution/use-build-from-plan-hotkey.ts +1 -1
  201. package/src/execution/useArtifactContent.ts +7 -0
  202. package/src/execution/useArtifactCopy.ts +109 -0
  203. package/src/execution/useArtifactDownload.ts +17 -5
  204. package/src/execution/useArtifactInspection.ts +252 -0
  205. package/src/execution/useCreateAgentExecution.ts +17 -3
  206. package/src/index.ts +17 -2
  207. package/src/internal/MermaidDiagram.tsx +164 -0
  208. package/src/internal/__tests__/a11y/mermaid-theme.a11y.test.tsx +122 -0
  209. package/src/internal/__tests__/markdown-components.test.tsx +93 -1
  210. package/src/internal/__tests__/mermaid-diagram.test.tsx +282 -0
  211. package/src/internal/file-tree/FileTreeNode.tsx +13 -2
  212. package/src/internal/markdown-components.tsx +36 -1
  213. package/src/internal/mermaid-loader.ts +44 -0
  214. package/src/internal/mermaid-theme-css.ts +178 -0
  215. package/src/internal/slug.ts +10 -6
  216. package/src/library/__tests__/detect-plan-artifact.test.ts +21 -0
  217. package/src/library/detect-plan-artifact.ts +32 -10
  218. package/src/library/index.ts +2 -0
  219. package/src/session/PlanEditor.tsx +1 -1
  220. package/src/session/SessionViewer.tsx +227 -51
  221. package/src/session/__tests__/PlanEditor.test.tsx +4 -4
  222. package/src/session/__tests__/PlanStreamingDocument.test.tsx +1 -1
  223. package/src/session/__tests__/SessionViewer-artifactTabs.test.tsx +210 -0
  224. package/src/session/__tests__/SessionViewer-editResubmit.test.tsx +233 -0
  225. package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +57 -0
  226. package/src/session/__tests__/useSessionArtifacts.test.ts +57 -0
  227. package/src/session/__tests__/useSessionConversation.test.tsx +216 -1
  228. package/src/session/__tests__/useSessionPanel.test.ts +70 -1
  229. package/src/session/artifact-document.ts +23 -0
  230. package/src/session/facets/ArtifactsTab.tsx +66 -18
  231. package/src/session/facets/__tests__/ArtifactsTab.test.tsx +111 -47
  232. package/src/session/index.ts +5 -1
  233. package/src/session/plan-document.ts +14 -3
  234. package/src/session/usePlanDraft.ts +1 -1
  235. package/src/session/useSessionArtifacts.ts +16 -5
  236. package/src/session/useSessionConversation.ts +80 -10
  237. package/src/session/useSessionPageFlow.ts +1 -0
  238. package/src/session/useSessionPanel.ts +41 -0
  239. package/src/session/useSessionRailViews.tsx +21 -1
  240. package/src/workspace/EditorTabs.tsx +38 -9
  241. package/src/workspace/ExplorerTree.tsx +10 -4
  242. package/src/workspace/FileViewer.tsx +27 -6
  243. package/src/workspace/WorkspaceContentSearch.tsx +44 -20
  244. package/src/workspace/WorkspaceEditor.tsx +19 -16
  245. package/src/workspace/WorkspaceFileSearch.tsx +51 -28
  246. package/src/workspace/WorkspaceSurface.tsx +106 -48
  247. package/src/workspace/__tests__/EditorTabs.test.tsx +4 -2
  248. package/src/workspace/__tests__/ExplorerTree.test.tsx +52 -0
  249. package/src/workspace/__tests__/FileViewer.test.tsx +18 -0
  250. package/src/workspace/__tests__/WorkspaceContentSearch.test.tsx +38 -5
  251. package/src/workspace/__tests__/WorkspaceEditor-initialPanel.test.tsx +19 -0
  252. package/src/workspace/__tests__/WorkspaceFileSearch.test.tsx +27 -6
  253. package/src/workspace/__tests__/WorkspaceSurface.test.tsx +70 -0
  254. package/src/workspace/__tests__/a11y/harness.tsx +140 -0
  255. package/src/workspace/__tests__/a11y/mock-capabilities.ts +138 -0
  256. package/src/workspace/__tests__/a11y/workspace-editor.a11y.test.tsx +65 -0
  257. package/src/workspace/__tests__/a11y/workspace-explorer-search.a11y.test.tsx +112 -0
  258. package/src/workspace/__tests__/a11y/workspace-viewer-tabs.a11y.test.tsx +69 -0
  259. package/styles.css +1 -1
  260. package/workspace/EditorTabs.d.ts +21 -3
  261. package/workspace/EditorTabs.d.ts.map +1 -1
  262. package/workspace/EditorTabs.js +18 -6
  263. package/workspace/EditorTabs.js.map +1 -1
  264. package/workspace/ExplorerTree.js +8 -4
  265. package/workspace/ExplorerTree.js.map +1 -1
  266. package/workspace/FileViewer.d.ts.map +1 -1
  267. package/workspace/FileViewer.js +19 -8
  268. package/workspace/FileViewer.js.map +1 -1
  269. package/workspace/WorkspaceContentSearch.d.ts.map +1 -1
  270. package/workspace/WorkspaceContentSearch.js +23 -2
  271. package/workspace/WorkspaceContentSearch.js.map +1 -1
  272. package/workspace/WorkspaceEditor.d.ts.map +1 -1
  273. package/workspace/WorkspaceEditor.js +8 -10
  274. package/workspace/WorkspaceEditor.js.map +1 -1
  275. package/workspace/WorkspaceFileSearch.d.ts.map +1 -1
  276. package/workspace/WorkspaceFileSearch.js +23 -3
  277. package/workspace/WorkspaceFileSearch.js.map +1 -1
  278. package/workspace/WorkspaceSurface.d.ts.map +1 -1
  279. package/workspace/WorkspaceSurface.js +54 -18
  280. package/workspace/WorkspaceSurface.js.map +1 -1
  281. package/execution/ArtifactCard.d.ts +0 -81
  282. package/execution/ArtifactCard.d.ts.map +0 -1
  283. package/execution/ArtifactCard.js +0 -130
  284. package/execution/ArtifactCard.js.map +0 -1
  285. package/src/execution/ArtifactCard.tsx +0 -320
@@ -1,22 +1,14 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { useCallback, useEffect, useRef } from "react";
4
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
5
  import { cn } from "@stigmer/theme";
7
- import { useArtifactContent } from "./useArtifactContent.js";
8
6
  import { useArtifactDownload } from "./useArtifactDownload.js";
9
- import { isTextArtifact, formatArtifactSize } from "./artifact-utils.js";
10
- import { ArtifactContentRenderer } from "./ArtifactContentRenderer.js";
11
- import { useDetectStigmerResource } from "../library/useDetectStigmerResource.js";
12
- import { useDetectSkillPackage } from "../library/useDetectSkillPackage.js";
13
- import type { SkillPackageDetection } from "../library/detect-skill-package.js";
14
- import {
15
- useApplyResource,
16
- type ApplyResourceResult,
17
- } from "../library/useApplyResource.js";
18
-
19
- const COPIED_FEEDBACK_MS = 2000;
7
+ import { formatArtifactSize } from "./artifact-utils.js";
8
+ import { ArtifactContentBody } from "./ArtifactContentBody.js";
9
+ import { ArtifactApplyButton } from "./ArtifactApplyButton.js";
10
+ import { useArtifactInspection } from "./useArtifactInspection.js";
11
+ import type { ApplyResourceResult } from "../library/useApplyResource.js";
20
12
 
21
13
  // ---------------------------------------------------------------------------
22
14
  // ArtifactPreviewContent — standalone content component
@@ -47,10 +39,10 @@ export interface ArtifactPreviewContentProps {
47
39
  */
48
40
  readonly onApplied?: (result: ApplyResourceResult) => void;
49
41
  /**
50
- * Optional plan-build action. When provided, a "Build from plan" primary
51
- * button appears in the action bar (used for plan artifacts: turn the
52
- * plan into an Agent run). Clicking it calls `onImplement` then closes
53
- * the modal. Omit for non-actionable artifacts.
42
+ * Optional plan-build action. When provided, a "Build" primary button
43
+ * appears in the action bar (used for plan artifacts: turn the plan into
44
+ * an Agent run). Clicking it calls `onImplement` then closes the modal.
45
+ * Omit for non-actionable artifacts.
54
46
  */
55
47
  readonly onImplement?: () => void;
56
48
  /** Additional CSS classes for the root container. */
@@ -66,7 +58,7 @@ export interface ArtifactPreviewContentProps {
66
58
  * it inside a `<dialog>`, modal, sheet, overlay, or inline context as
67
59
  * needed.
68
60
  *
69
- * Orchestrates the same detection pipeline as {@link ArtifactCard}:
61
+ * Orchestrates the shared detection pipeline (see {@link useArtifactInspection}):
70
62
  *
71
63
  * - **FILE artifacts**: Fetches text content via {@link useArtifactContent},
72
64
  * renders via {@link ArtifactContentRenderer} (markdown, YAML, JSON, or
@@ -96,7 +88,7 @@ export interface ArtifactPreviewContentProps {
96
88
  * ```
97
89
  *
98
90
  * @see {@link ArtifactPreviewModal} — wraps this component in a native `<dialog>`
99
- * @see {@link ArtifactCard} — compact card that triggers preview via `onPreview`
91
+ * @see {@link ArtifactRow} — dense list row that triggers preview via `onOpen`
100
92
  * @see {@link useArtifactContent} — content-fetching hook (headless alternative)
101
93
  * @see {@link useDetectStigmerResource} — YAML resource detection (headless)
102
94
  * @see {@link useDetectSkillPackage} — skill package detection (headless)
@@ -112,176 +104,56 @@ export function ArtifactPreviewContent({
112
104
  onImplement,
113
105
  className,
114
106
  }: ArtifactPreviewContentProps) {
115
- const isDirectory = artifact.kind === ExecutionArtifactKind.DIRECTORY;
116
- const canFetchContent = !isDirectory && isTextArtifact(artifact);
117
-
118
- // ---------------------------------------------------------------------------
119
- // Detection orchestration
120
- // ---------------------------------------------------------------------------
121
-
122
- const {
123
- content,
124
- contentType,
125
- isTruncated,
126
- isLoading: isContentLoading,
127
- error: contentError,
128
- } = useArtifactContent(
129
- canFetchContent ? executionId : null,
130
- canFetchContent ? artifact.storageKey : null,
131
- undefined,
132
- artifact.contentHash || undefined,
133
- );
134
-
135
- const yamlDetection = useDetectStigmerResource(
136
- canFetchContent ? content : null,
137
- );
138
-
139
- const { detection: skillDetection, isLoading: isSkillLoading } =
140
- useDetectSkillPackage(
141
- isDirectory ? artifact : null,
142
- isDirectory ? executionId : null,
143
- );
144
-
145
- // ---------------------------------------------------------------------------
146
- // Derived detection state
147
- // ---------------------------------------------------------------------------
148
-
149
- const isDetected = yamlDetection.detected || skillDetection.detected;
150
- const isDetecting =
151
- (canFetchContent && isContentLoading) || (isDirectory && isSkillLoading);
152
-
153
- let detectionLabel: string | null = null;
154
- if (yamlDetection.detected) {
155
- detectionLabel = `${yamlDetection.displayName} detected`;
156
- } else if (skillDetection.detected) {
157
- const count = skillDetection.fileCount;
158
- detectionLabel = `Skill \u00B7 ${count} ${count === 1 ? "file" : "files"}`;
159
- }
160
-
161
- // ---------------------------------------------------------------------------
162
- // Apply state
163
- // ---------------------------------------------------------------------------
164
-
165
- const {
166
- applyYamlResource,
167
- pushSkillPackage,
168
- isApplying,
169
- error: applyError,
170
- clearError,
171
- } = useApplyResource();
172
-
173
- const [applyResult, setApplyResult] = useState<ApplyResourceResult | null>(
174
- null,
175
- );
176
-
177
- const handleApply = useCallback(async () => {
178
- clearError();
179
- try {
180
- let result: ApplyResourceResult;
181
- if (yamlDetection.detected && content) {
182
- result = await applyYamlResource(content, org);
183
- } else if (skillDetection.detected) {
184
- result = await pushSkillPackage({
185
- org,
186
- executionId,
187
- storageKey: artifact.storageKey,
188
- });
189
- } else {
190
- return;
191
- }
192
- setApplyResult(result);
193
- onApplied?.(result);
194
- } catch {
195
- // error state managed by useApplyResource
196
- }
197
- }, [
198
- yamlDetection.detected,
199
- skillDetection.detected,
200
- content,
201
- org,
202
- executionId,
203
- artifact.storageKey,
204
- applyYamlResource,
205
- pushSkillPackage,
206
- clearError,
107
+ // The whole inspect-and-act pipeline (content, detection, apply/push, copy)
108
+ // lives in one headless hook, shared with the editor-area ArtifactDocument so
109
+ // the two surfaces can never drift.
110
+ const inspection = useArtifactInspection(artifact, executionId, org, {
207
111
  onApplied,
208
- ]);
209
-
210
- // ---------------------------------------------------------------------------
211
- // Copy to clipboard
212
- // ---------------------------------------------------------------------------
213
-
214
- const [copied, setCopied] = useState(false);
215
-
216
- const handleCopy = useCallback(() => {
217
- if (!content) return;
218
- navigator.clipboard.writeText(content).then(() => {
219
- setCopied(true);
220
- setTimeout(() => setCopied(false), COPIED_FEEDBACK_MS);
221
- });
222
- }, [content]);
223
-
224
- let ctaLabel: string | null = null;
225
- if (yamlDetection.detected) {
226
- ctaLabel = `Apply to ${org}`;
227
- } else if (skillDetection.detected) {
228
- ctaLabel = `Push Skill to ${org}`;
229
- }
230
-
231
- // "Build from plan" runs the plan; closing the modal lets the host's submit
232
- // pipeline take over (switch to Agent + send). Single combined handler keeps
233
- // the caller's contract simple ("just give me onImplement").
112
+ });
113
+
114
+ // "Build" runs the plan; closing the modal lets the host's submit pipeline
115
+ // take over (switch to Agent + send). Single combined handler keeps the
116
+ // caller's contract simple ("just give me onImplement").
234
117
  const handleImplement = useCallback(() => {
235
118
  onImplement?.();
236
119
  onClose();
237
120
  }, [onImplement, onClose]);
238
121
 
239
- // ---------------------------------------------------------------------------
240
- // Render
241
- // ---------------------------------------------------------------------------
242
-
243
122
  return (
244
123
  <div className={cn("flex max-h-[80vh] flex-col", className)}>
245
124
  <ContentHeader
246
125
  artifact={artifact}
247
- isDirectory={isDirectory}
248
- detectionLabel={detectionLabel}
249
- isDetecting={isDetecting}
126
+ isDirectory={inspection.isDirectory}
127
+ detectionLabel={inspection.detectionLabel}
128
+ isDetecting={inspection.isDetecting}
250
129
  onClose={onClose}
251
130
  />
252
131
 
253
- <div className="min-h-0 flex-1 overflow-y-auto border-t border-border">
254
- {isDirectory ? (
255
- <DirectoryContentView
256
- artifact={artifact}
257
- skillDetection={skillDetection}
258
- />
259
- ) : (
260
- <FileContentStateView
261
- artifact={artifact}
262
- content={content}
263
- contentType={contentType}
264
- isLoading={isContentLoading}
265
- error={contentError}
266
- isTruncated={isTruncated}
267
- />
268
- )}
269
- </div>
132
+ <ArtifactContentBody
133
+ artifact={artifact}
134
+ content={inspection.content}
135
+ contentType={inspection.contentType}
136
+ isLoading={inspection.isLoading}
137
+ error={inspection.error}
138
+ isTruncated={inspection.isTruncated}
139
+ skillDetection={inspection.skillDetection}
140
+ className="min-h-0 flex-1 overflow-y-auto border-t border-border"
141
+ />
270
142
 
271
143
  <ActionBar
272
144
  artifact={artifact}
273
145
  executionId={executionId}
274
- isDirectory={isDirectory}
275
- hasContent={content !== null}
276
- copied={copied}
277
- onCopy={handleCopy}
278
- isDetected={isDetected}
279
- ctaLabel={ctaLabel}
146
+ isDirectory={inspection.isDirectory}
147
+ hasContent={inspection.content !== null}
148
+ copied={inspection.copied}
149
+ onCopy={inspection.copy}
150
+ isDetected={inspection.isDetected}
151
+ ctaLabel={inspection.ctaLabel}
280
152
  isTerminal={isTerminal}
281
- isApplying={isApplying}
282
- applyResult={applyResult}
283
- applyError={applyError}
284
- onApply={handleApply}
153
+ isApplying={inspection.isApplying}
154
+ applyResult={inspection.applyResult}
155
+ applyError={inspection.applyError}
156
+ onApply={inspection.apply}
285
157
  onImplement={onImplement ? handleImplement : undefined}
286
158
  />
287
159
 
@@ -291,7 +163,7 @@ export function ArtifactPreviewContent({
291
163
  aria-atomic="true"
292
164
  className="sr-only"
293
165
  >
294
- {copied && "Content copied to clipboard"}
166
+ {inspection.copied && "Content copied to clipboard"}
295
167
  </div>
296
168
  </div>
297
169
  );
@@ -329,7 +201,7 @@ export interface ArtifactPreviewModalProps {
329
201
  readonly onApplied?: (result: ApplyResourceResult) => void;
330
202
  /**
331
203
  * Optional plan-build action (see {@link ArtifactPreviewContentProps.onImplement}).
332
- * When provided, a "Build from plan" primary button appears; clicking it calls
204
+ * When provided, a "Build" primary button appears; clicking it calls
333
205
  * `onImplement` then closes the modal.
334
206
  */
335
207
  readonly onImplement?: () => void;
@@ -368,7 +240,7 @@ export interface ArtifactPreviewModalProps {
368
240
  * ```
369
241
  *
370
242
  * @see {@link ArtifactPreviewContent} — the content component (use directly for non-dialog contexts)
371
- * @see {@link ArtifactCard} — compact card that triggers preview via `onPreview`
243
+ * @see {@link ArtifactRow} — dense list row that triggers preview via `onOpen`
372
244
  */
373
245
  export function ArtifactPreviewModal({
374
246
  artifact,
@@ -496,128 +368,6 @@ function ContentHeader({
496
368
  );
497
369
  }
498
370
 
499
- // ---------------------------------------------------------------------------
500
- // FileContentStateView (internal — loading/error/empty states + renderer)
501
- // ---------------------------------------------------------------------------
502
-
503
- const SKELETON_LINE_WIDTHS = [85, 72, 90, 65, 78, 88, 70, 82] as const;
504
-
505
- function FileContentStateView({
506
- artifact,
507
- content,
508
- contentType,
509
- isLoading,
510
- error,
511
- isTruncated,
512
- }: {
513
- readonly artifact: ExecutionArtifact;
514
- readonly content: string | null;
515
- readonly contentType: string | null;
516
- readonly isLoading: boolean;
517
- readonly error: Error | null;
518
- readonly isTruncated: boolean;
519
- }) {
520
- if (isLoading) {
521
- return (
522
- <div className="space-y-2 p-4" aria-busy="true" aria-label="Loading content">
523
- {SKELETON_LINE_WIDTHS.map((width, i) => (
524
- <div
525
- key={i}
526
- className="h-4 animate-pulse rounded bg-muted"
527
- style={{ width: `${width}%` }}
528
- aria-hidden="true"
529
- />
530
- ))}
531
- </div>
532
- );
533
- }
534
-
535
- if (error) {
536
- return (
537
- <div className="flex flex-col items-center justify-center gap-2 p-8 text-center">
538
- <ErrorAlertIcon />
539
- <p className="text-sm text-destructive">{error.message}</p>
540
- </div>
541
- );
542
- }
543
-
544
- if (content === null) {
545
- return (
546
- <div className="p-8 text-center text-sm text-muted-foreground">
547
- Content not available for preview.
548
- </div>
549
- );
550
- }
551
-
552
- return (
553
- <ArtifactContentRenderer
554
- content={content}
555
- fileName={artifact.name}
556
- contentType={contentType}
557
- isTruncated={isTruncated}
558
- />
559
- );
560
- }
561
-
562
- // ---------------------------------------------------------------------------
563
- // DirectoryContentView (internal)
564
- // ---------------------------------------------------------------------------
565
-
566
- function DirectoryContentView({
567
- artifact,
568
- skillDetection,
569
- }: {
570
- readonly artifact: ExecutionArtifact;
571
- readonly skillDetection: SkillPackageDetection;
572
- }) {
573
- const entries = artifact.entries;
574
-
575
- return (
576
- <div className="p-4">
577
- {skillDetection.detected && (
578
- <div className="mb-4 rounded-md bg-primary-subtle p-3">
579
- <p className="text-sm font-medium text-foreground">
580
- {skillDetection.skillName}
581
- </p>
582
- {skillDetection.skillDescription && (
583
- <p className="mt-1 text-xs text-muted-foreground">
584
- {skillDetection.skillDescription}
585
- </p>
586
- )}
587
- </div>
588
- )}
589
-
590
- {entries.length > 0 ? (
591
- <div>
592
- <h3 className="mb-2 text-xs font-medium text-muted-foreground">
593
- Files ({entries.length})
594
- </h3>
595
- <ul className="space-y-0.5" role="list">
596
- {entries.map((entry) => (
597
- <li
598
- key={entry}
599
- className="flex items-center gap-2 rounded-sm px-2 py-1 font-mono text-xs text-foreground"
600
- >
601
- <EntryIcon name={entry} />
602
- <span className="min-w-0 truncate">{entry}</span>
603
- </li>
604
- ))}
605
- </ul>
606
- </div>
607
- ) : (
608
- <p className="text-sm text-muted-foreground">
609
- File listing not available.
610
- </p>
611
- )}
612
- </div>
613
- );
614
- }
615
-
616
- function EntryIcon({ name }: { readonly name: string }) {
617
- if (name.endsWith("/")) return <FolderSmallIcon />;
618
- return <FileSmallIcon />;
619
- }
620
-
621
371
  // ---------------------------------------------------------------------------
622
372
  // ActionBar (internal)
623
373
  // ---------------------------------------------------------------------------
@@ -701,7 +451,7 @@ function ActionBar({
701
451
  )}
702
452
  >
703
453
  <ImplementIcon />
704
- Build from plan
454
+ Build
705
455
  </button>
706
456
  )}
707
457
  {applyResult ? (
@@ -727,7 +477,7 @@ function ActionBar({
727
477
  </span>
728
478
  ) : isDetected && ctaLabel ? (
729
479
  <span data-cursor-target="apply-resource-button">
730
- <ApplyButton
480
+ <ArtifactApplyButton
731
481
  label={ctaLabel}
732
482
  isTerminal={isTerminal}
733
483
  isApplying={isApplying}
@@ -740,46 +490,6 @@ function ActionBar({
740
490
  );
741
491
  }
742
492
 
743
- function ApplyButton({
744
- label,
745
- isTerminal,
746
- isApplying,
747
- onApply,
748
- }: {
749
- readonly label: string;
750
- readonly isTerminal: boolean;
751
- readonly isApplying: boolean;
752
- readonly onApply: () => void;
753
- }) {
754
- const canApply = isTerminal && !isApplying;
755
-
756
- return (
757
- <button
758
- type="button"
759
- onClick={canApply ? onApply : undefined}
760
- disabled={!canApply}
761
- aria-busy={isApplying}
762
- className={cn(
763
- "rounded-md px-4 py-1.5 text-xs font-medium transition-colors",
764
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
765
- "disabled:cursor-not-allowed",
766
- canApply
767
- ? "bg-primary text-primary-foreground hover:bg-primary-hover"
768
- : "bg-muted text-muted-foreground",
769
- )}
770
- >
771
- {isApplying ? (
772
- <span className="inline-flex items-center gap-1.5">
773
- <SpinnerIcon />
774
- Applying{"\u2026"}
775
- </span>
776
- ) : (
777
- label
778
- )}
779
- </button>
780
- );
781
- }
782
-
783
493
  // ---------------------------------------------------------------------------
784
494
  // Inline SVG icons (SDK pattern: no external icon dependency)
785
495
  // ---------------------------------------------------------------------------
@@ -840,45 +550,6 @@ function FolderIcon() {
840
550
  );
841
551
  }
842
552
 
843
- function FileSmallIcon() {
844
- return (
845
- <svg
846
- width="12"
847
- height="12"
848
- viewBox="0 0 14 14"
849
- fill="none"
850
- stroke="currentColor"
851
- strokeWidth="1.5"
852
- strokeLinecap="round"
853
- strokeLinejoin="round"
854
- className="shrink-0 text-muted-foreground"
855
- aria-hidden="true"
856
- >
857
- <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" />
858
- <path d="M8 1V4H11" />
859
- </svg>
860
- );
861
- }
862
-
863
- function FolderSmallIcon() {
864
- return (
865
- <svg
866
- width="12"
867
- height="12"
868
- viewBox="0 0 14 14"
869
- fill="none"
870
- stroke="currentColor"
871
- strokeWidth="1.5"
872
- strokeLinecap="round"
873
- strokeLinejoin="round"
874
- className="shrink-0 text-muted-foreground"
875
- aria-hidden="true"
876
- >
877
- <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" />
878
- </svg>
879
- );
880
- }
881
-
882
553
  function CopyIcon() {
883
554
  return (
884
555
  <svg
@@ -958,40 +629,3 @@ function CheckIcon() {
958
629
  );
959
630
  }
960
631
 
961
- function SpinnerIcon() {
962
- return (
963
- <svg
964
- width="12"
965
- height="12"
966
- viewBox="0 0 12 12"
967
- fill="none"
968
- stroke="currentColor"
969
- strokeWidth="1.5"
970
- className="shrink-0 animate-spin"
971
- aria-hidden="true"
972
- >
973
- <path d="M6 1.5A4.5 4.5 0 1 1 1.5 6" strokeLinecap="round" />
974
- </svg>
975
- );
976
- }
977
-
978
- function ErrorAlertIcon() {
979
- return (
980
- <svg
981
- width="16"
982
- height="16"
983
- viewBox="0 0 16 16"
984
- fill="none"
985
- stroke="currentColor"
986
- strokeWidth="1.5"
987
- strokeLinecap="round"
988
- strokeLinejoin="round"
989
- className="text-destructive"
990
- aria-hidden="true"
991
- >
992
- <circle cx="8" cy="8" r="6.5" />
993
- <path d="M8 5.5V8.5" />
994
- <circle cx="8" cy="11" r="0.5" fill="currentColor" stroke="none" />
995
- </svg>
996
- );
997
- }