@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
@@ -102,7 +102,15 @@ export function FileTreeNode({
102
102
  );
103
103
 
104
104
  return (
105
- <li role="treeitem" aria-expanded={isFolder ? expanded : undefined}>
105
+ <li
106
+ role="treeitem"
107
+ // Depth conveys tree nesting to assistive tech (indentation is visual
108
+ // only). aria-level is 1-based; `depth` is 0-based. aria-expanded stays on
109
+ // the treeitem (the <li>) — its child group is nested here, so this is the
110
+ // element a tree-aware screen reader reads expandability from.
111
+ aria-level={depth + 1}
112
+ aria-expanded={isFolder ? expanded : undefined}
113
+ >
106
114
  <button
107
115
  type="button"
108
116
  onClick={handleClick}
@@ -123,7 +131,10 @@ export function FileTreeNode({
123
131
  aria-current={isSelected ? "true" : undefined}
124
132
  >
125
133
  {isFolder && (
126
- <span className="text-[10px] text-muted-foreground-subtle">
134
+ <span
135
+ aria-hidden="true"
136
+ className="text-[10px] text-muted-foreground-subtle"
137
+ >
127
138
  {expanded ? "▼" : "▶"}
128
139
  </span>
129
140
  )}
@@ -1,10 +1,12 @@
1
- import type { ComponentProps, JSX } from "react";
1
+ import { isValidElement, type ComponentProps, type JSX, type ReactNode } from "react";
2
2
  import type { Components } from "react-markdown";
3
3
  import remarkGfm from "remark-gfm";
4
4
  import { cn } from "@stigmer/theme";
5
5
  import { highlightToReact } from "./code-highlight.js";
6
+ import { MermaidDiagram } from "./MermaidDiagram.js";
6
7
 
7
8
  const LANGUAGE_CLASS_PREFIX = "language-";
9
+ const MERMAID_LANGUAGE_CLASS = "language-mermaid";
8
10
 
9
11
  type MdProps<T extends keyof JSX.IntrinsicElements> = ComponentProps<T>;
10
12
 
@@ -119,6 +121,30 @@ export function extractLeadingH1(markdown: string): {
119
121
  return { title: match[1], body: trimmed.slice(match[0].length) };
120
122
  }
121
123
 
124
+ /**
125
+ * Returns the mermaid source when a `pre` element's children are a single
126
+ * fenced code block explicitly tagged `mermaid`, or `null` otherwise.
127
+ *
128
+ * Both renderers (Streamdown for chat, react-markdown for artifacts/skills)
129
+ * hand `pre` exactly one `<code>` child whose `className` carries the fence's
130
+ * info string as `language-*` and whose children are the raw source text.
131
+ * Anything that deviates from that shape — a different language, a missing
132
+ * tag, non-string children — is not a mermaid fence and keeps the ordinary
133
+ * code-block rendering. Only the explicit `mermaid` tag qualifies:
134
+ * inspecting fence bodies to guess diagram intent is the kind of fuzzy
135
+ * heuristic this codebase avoids.
136
+ */
137
+ function extractMermaidSource(children: ReactNode): string | null {
138
+ if (!isValidElement(children)) return null;
139
+ const { className, children: code } = children.props as {
140
+ className?: unknown;
141
+ children?: unknown;
142
+ };
143
+ if (typeof className !== "string") return null;
144
+ if (!className.split(/\s+/).includes(MERMAID_LANGUAGE_CLASS)) return null;
145
+ return typeof code === "string" ? code : null;
146
+ }
147
+
122
148
  /**
123
149
  * Styled react-markdown component overrides for SDK markdown surfaces.
124
150
  *
@@ -208,6 +234,15 @@ export const MARKDOWN_COMPONENTS: Components = {
208
234
  },
209
235
 
210
236
  pre({ children, ...props }: MdProps<"pre">) {
237
+ // A ```mermaid fence renders as a diagram, not a code block. The check
238
+ // lives here (not in the `code` override) because the diagram must
239
+ // replace the <pre> wrapper too — a block-level diagram container inside
240
+ // <pre> is invalid HTML and would inherit code-block chrome.
241
+ const mermaidSource = extractMermaidSource(children);
242
+ if (mermaidSource !== null) {
243
+ return <MermaidDiagram chart={mermaidSource} />;
244
+ }
245
+
211
246
  return (
212
247
  <pre
213
248
  className="mb-3 last:mb-0 overflow-x-auto rounded-md bg-muted p-3"
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Lazy loader for the `mermaid` library, isolated in its own module — the
3
+ * same seam pattern as `workflow/layout/elk-layout-engine.ts` — so
4
+ * {@link file://./MermaidDiagram.tsx} stays a pure presentation component and
5
+ * tests can mock the load outcome (resolve, reject) without touching module
6
+ * registries.
7
+ *
8
+ * `mermaid` is an optional peer dependency (DD-013): it is large, so it must
9
+ * never sit on the synchronous bundle path, and a host that has not installed
10
+ * it must degrade gracefully (the caller falls back to a code block).
11
+ */
12
+
13
+ /**
14
+ * The mermaid API surface the SDK consumes, typed from the package itself so
15
+ * it stays in lockstep with the installed version.
16
+ */
17
+ export type MermaidModule = typeof import("mermaid").default;
18
+
19
+ /**
20
+ * Holds the in-flight import promise so concurrent diagrams share one load.
21
+ * A failed import clears the cache: rejections are often transient (a
22
+ * chunk-load hiccup mid-deploy), and caching one would break every diagram
23
+ * for the rest of the session.
24
+ */
25
+ let mermaidImport: Promise<MermaidModule> | null = null;
26
+
27
+ /**
28
+ * Dynamically imports `mermaid`, sharing one in-flight import across callers.
29
+ *
30
+ * @returns The mermaid default export.
31
+ * @throws If `mermaid` cannot be imported (not installed, or the async chunk
32
+ * failed to load). Callers treat this as "diagrams unavailable" and render
33
+ * the fenced source as a plain code block.
34
+ */
35
+ export function loadMermaid(): Promise<MermaidModule> {
36
+ mermaidImport ??= import("mermaid").then(
37
+ (module) => module.default,
38
+ (error: unknown) => {
39
+ mermaidImport = null;
40
+ throw error;
41
+ },
42
+ );
43
+ return mermaidImport;
44
+ }
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Token-driven `themeCSS` for Mermaid diagram interiors.
3
+ *
4
+ * Mermaid's built-in `default`/`dark` themes color a diagram's SVG interior
5
+ * with a fixed palette that ignores the host's `--stgm-*` preset (Corporate,
6
+ * Fintech, ...). The obvious fix — feeding resolved tokens into Mermaid's
7
+ * `themeVariables` — is blocked: our tokens are authored in `oklch()`, and
8
+ * Mermaid's color engine (`khroma`) parses only hex/rgb/hsl/keyword, so it
9
+ * throws or NaNs on `oklch(...)`. (Mermaid also gates every `themeVariables`
10
+ * value through the allowlist `^[\d "#%(),.;A-Za-z]+$`, which excludes `-` and
11
+ * would blank any `var(--...)`.)
12
+ *
13
+ * Instead we hand Mermaid this CSS via `initialize({ themeCSS })`. Mermaid
14
+ * injects it into the diagram's own `<style>` block, scoped under the render
15
+ * id (`#stgm-mermaid-N`), *after* the theme palette it computes. Because every
16
+ * selector in that block is id-prefixed, our rules and the palette's rules sit
17
+ * at equal specificity, so ours win by source order — and `!important` makes
18
+ * that bulletproof against the few palette rules that are themselves important
19
+ * or inlined. Crucially, `themeCSS` never passes through khroma, so the
20
+ * `oklch()`-backed `var(--stgm-*)` references flow straight to the browser,
21
+ * which resolves them against the `.stgm` scope the SVG lives in. That is why
22
+ * interiors track the active preset *and* color mode live, with no JS color
23
+ * reading and no re-render on theme change.
24
+ *
25
+ * `initialize({ theme })` still selects the built-in `default`/`dark` theme as
26
+ * the base, so any element or diagram type we do not target here degrades to
27
+ * Mermaid's stock palette — never a blank or mis-colored diagram.
28
+ *
29
+ * Palette intent (subtle surfaces + brand accents — legibility over brand
30
+ * saturation): node/actor bodies sit on a muted surface with quiet
31
+ * token-colored borders and edges; labels use the standard foreground; the
32
+ * brand `--stgm-primary` is reserved for structural headers (subgraph/cluster
33
+ * titles, sequence loop/section titles, the sequence-number badge).
34
+ *
35
+ * Coverage is the two families agents emit overwhelmingly — flowchart and
36
+ * sequence — plus the shared marker/edge-label chrome. Both label render paths
37
+ * are handled: SVG `text`/`tspan` (via `fill`) and HTML labels inside
38
+ * `foreignObject` (via `color`).
39
+ */
40
+ export const MERMAID_THEME_CSS = `
41
+ /* Shared: edge arrowheads / markers */
42
+ .marker {
43
+ fill: var(--stgm-border) !important;
44
+ stroke: var(--stgm-border) !important;
45
+ }
46
+ .marker.cross {
47
+ stroke: var(--stgm-border) !important;
48
+ }
49
+
50
+ /* Flowchart: node bodies */
51
+ .node rect,
52
+ .node circle,
53
+ .node ellipse,
54
+ .node polygon,
55
+ .node path {
56
+ fill: var(--stgm-muted) !important;
57
+ stroke: var(--stgm-border) !important;
58
+ }
59
+ .node .neo-node {
60
+ stroke: var(--stgm-border) !important;
61
+ }
62
+ .node .katex path {
63
+ fill: var(--stgm-foreground) !important;
64
+ stroke: var(--stgm-foreground) !important;
65
+ }
66
+
67
+ /* Flowchart: node labels (SVG text and HTML spans) */
68
+ .label {
69
+ color: var(--stgm-foreground) !important;
70
+ }
71
+ .label text,
72
+ .label span {
73
+ fill: var(--stgm-foreground) !important;
74
+ color: var(--stgm-foreground) !important;
75
+ }
76
+
77
+ /* Flowchart: edges */
78
+ .edgePath .path,
79
+ .flowchart-link {
80
+ stroke: var(--stgm-border) !important;
81
+ }
82
+ .arrowheadPath,
83
+ .root .anchor path {
84
+ fill: var(--stgm-border) !important;
85
+ stroke: var(--stgm-border) !important;
86
+ }
87
+
88
+ /* Flowchart: edge labels sit on the diagram surface for legibility */
89
+ .edgeLabel,
90
+ .edgeLabel p,
91
+ .edgeLabel rect,
92
+ .labelBkg {
93
+ background-color: var(--stgm-card) !important;
94
+ fill: var(--stgm-card) !important;
95
+ }
96
+
97
+ /* Flowchart: subgraph clusters — muted surface, brand-accent title */
98
+ .cluster rect {
99
+ fill: var(--stgm-muted-subtle) !important;
100
+ stroke: var(--stgm-border) !important;
101
+ }
102
+ .cluster-label text,
103
+ .cluster text {
104
+ fill: var(--stgm-primary) !important;
105
+ }
106
+ .cluster-label span,
107
+ .cluster span {
108
+ color: var(--stgm-primary) !important;
109
+ }
110
+
111
+ /* Sequence: actors and lifelines */
112
+ .actor {
113
+ fill: var(--stgm-muted) !important;
114
+ stroke: var(--stgm-border) !important;
115
+ }
116
+ text.actor > tspan {
117
+ fill: var(--stgm-foreground) !important;
118
+ }
119
+ .actor-line {
120
+ stroke: var(--stgm-border) !important;
121
+ }
122
+
123
+ /* Sequence: messages */
124
+ .messageLine0,
125
+ .messageLine1 {
126
+ stroke: var(--stgm-border) !important;
127
+ }
128
+ [id$="-arrowhead"] path,
129
+ [id$="-crosshead"] path {
130
+ fill: var(--stgm-border) !important;
131
+ stroke: var(--stgm-border) !important;
132
+ }
133
+ .messageText {
134
+ fill: var(--stgm-foreground) !important;
135
+ }
136
+
137
+ /* Sequence: number badge — brand accent with its paired foreground */
138
+ [id$="-sequencenumber"] {
139
+ fill: var(--stgm-primary) !important;
140
+ }
141
+ .sequenceNumber {
142
+ fill: var(--stgm-primary-foreground) !important;
143
+ }
144
+
145
+ /* Sequence: alt/opt/loop boxes and labels */
146
+ .labelBox {
147
+ fill: var(--stgm-muted-subtle) !important;
148
+ stroke: var(--stgm-border) !important;
149
+ }
150
+ .labelText,
151
+ .labelText > tspan {
152
+ fill: var(--stgm-foreground) !important;
153
+ }
154
+ .loopText,
155
+ .loopText > tspan,
156
+ .sectionTitle,
157
+ .sectionTitle > tspan {
158
+ fill: var(--stgm-primary) !important;
159
+ }
160
+ .loopLine {
161
+ stroke: var(--stgm-border) !important;
162
+ }
163
+
164
+ /* Sequence: notes and activations */
165
+ .note {
166
+ fill: var(--stgm-muted-subtle) !important;
167
+ stroke: var(--stgm-border) !important;
168
+ }
169
+ .noteText,
170
+ .noteText > tspan {
171
+ fill: var(--stgm-foreground) !important;
172
+ }
173
+ .activation0,
174
+ .activation1 {
175
+ fill: var(--stgm-muted) !important;
176
+ stroke: var(--stgm-border) !important;
177
+ }
178
+ `;
@@ -7,23 +7,27 @@
7
7
  *
8
8
  * Rules (applied in order):
9
9
  * 1. Convert to lowercase
10
- * 2. Replace spaces with hyphens
10
+ * 2. Replace spaces and dots (namespace separators) with hyphens
11
11
  * 3. Strip everything except `a-z`, `0-9`, and `-`
12
12
  * 4. Collapse consecutive hyphens into one
13
13
  * 5. Trim leading / trailing hyphens
14
14
  *
15
+ * Dots are treated as namespace separators (e.g. `platform.sara` → `platform-sara`),
16
+ * matching the backend generators, so namespace-scoped names stay readable.
17
+ *
15
18
  * @example
16
19
  * ```ts
17
- * generateSlug("My Cool Agent") // "my-cool-agent"
18
- * generateSlug("Acme Corp!") // "acme-corp"
19
- * generateSlug(" --hello-- ") // "hello"
20
- * generateSlug("") // ""
20
+ * generateSlug("My Cool Agent") // "my-cool-agent"
21
+ * generateSlug("Acme Corp!") // "acme-corp"
22
+ * generateSlug(" --hello-- ") // "hello"
23
+ * generateSlug("platform.planton-arch") // "platform-planton-arch"
24
+ * generateSlug("") // ""
21
25
  * ```
22
26
  */
23
27
  export function generateSlug(name: string): string {
24
28
  return name
25
29
  .toLowerCase()
26
- .replace(/ /g, "-")
30
+ .replace(/[ .]/g, "-")
27
31
  .replace(/[^a-z0-9-]/g, "")
28
32
  .replace(/-{2,}/g, "-")
29
33
  .replace(/^-+|-+$/g, "");
@@ -9,6 +9,7 @@ import { ExecutionArtifactKind } from "@stigmer/protos/ai/stigmer/agentic/agente
9
9
  import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
10
10
  import {
11
11
  isPlanArtifact,
12
+ isPlanArtifactName,
12
13
  findPlanArtifact,
13
14
  findLatestSessionPlan,
14
15
  PLAN_ARTIFACT_NAME,
@@ -35,11 +36,31 @@ function executionWithId(id: string, ...artifacts: ReturnType<typeof artifact>[]
35
36
  });
36
37
  }
37
38
 
39
+ describe("isPlanArtifactName", () => {
40
+ it("accepts the legacy exact name and any *.plan.md", () => {
41
+ expect(isPlanArtifactName(PLAN_ARTIFACT_NAME)).toBe(true);
42
+ expect(isPlanArtifactName("feature_x.plan.md")).toBe(true);
43
+ // The runner's current shape: hyphenated slug + content-hash discriminator.
44
+ expect(isPlanArtifactName("fix-the-download-ux_a1b2c3d4.plan.md")).toBe(true);
45
+ expect(isPlanArtifactName("a1b2c3d4.plan.md")).toBe(true);
46
+ });
47
+
48
+ it("rejects non-plan filenames", () => {
49
+ expect(isPlanArtifactName("report.md")).toBe(false);
50
+ expect(isPlanArtifactName("plan.txt")).toBe(false);
51
+ expect(isPlanArtifactName("myplan.md")).toBe(false);
52
+ });
53
+ });
54
+
38
55
  describe("isPlanArtifact", () => {
39
56
  it("is true for a FILE artifact named plan.md", () => {
40
57
  expect(isPlanArtifact(artifact({ name: PLAN_ARTIFACT_NAME }))).toBe(true);
41
58
  });
42
59
 
60
+ it("is true for a FILE artifact named <slug>_<id>.plan.md", () => {
61
+ expect(isPlanArtifact(artifact({ name: "plan-card-ux-cleanup_a1b2c3d4.plan.md" }))).toBe(true);
62
+ });
63
+
43
64
  it("is false for a directory named plan.md", () => {
44
65
  expect(
45
66
  isPlanArtifact(artifact({ name: "plan.md", kind: ExecutionArtifactKind.DIRECTORY })),
@@ -3,26 +3,48 @@ import type { ExecutionArtifact } from "@stigmer/protos/ai/stigmer/agentic/agent
3
3
  import { ExecutionArtifactKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
4
4
 
5
5
  /**
6
- * The session's current plan: the published `plan.md` artifact of the most
7
- * recent execution that produced one, plus the execution it belongs to
8
- * (artifact content RPCs are execution-scoped).
6
+ * The session's current plan: the published plan artifact (a `*.plan.md` FILE)
7
+ * of the most recent execution that produced one, plus the execution it belongs
8
+ * to (artifact content RPCs are execution-scoped).
9
9
  */
10
10
  export interface SessionPlan {
11
11
  /** ID of the execution that published the plan. */
12
12
  readonly executionId: string;
13
- /** The published `plan.md` artifact. */
13
+ /** The published plan artifact (`*.plan.md`). */
14
14
  readonly artifact: ExecutionArtifact;
15
15
  }
16
16
 
17
17
  /**
18
- * Canonical filename a Plan-mode execution publishes its plan under. Kept in
19
- * sync with the runner's `publishPlanArtifact` (shared/plan-artifact.ts).
18
+ * Legacy plan filename. Detection-only: {@link isPlanArtifactName} still
19
+ * accepts this exact name so plans published before named artifacts existed
20
+ * keep working. The runner never freshly emits it (a titleless plan falls back
21
+ * to a bare `<id>.plan.md`). Kept in sync with the runner's
22
+ * `PLAN_ARTIFACT_NAME` (shared/plan-artifact.ts).
20
23
  */
21
24
  export const PLAN_ARTIFACT_NAME = "plan.md";
22
25
 
23
26
  /**
24
- * Returns `true` when an artifact is the published plan: a FILE artifact named
25
- * `plan.md`.
27
+ * Suffix every named plan artifact carries (`<slug>_<id>.plan.md`, or a bare
28
+ * `<id>.plan.md` when untitled). The runner derives the slug from the plan's
29
+ * title and the `<id>` from its content hash; detection keys on this suffix.
30
+ * Kept in sync with the runner's `PLAN_ARTIFACT_SUFFIX` (shared/plan-artifact.ts)
31
+ * — the runner and browser SDK have disjoint module graphs, so the constant is
32
+ * duplicated by design, mirroring {@link PLAN_ARTIFACT_NAME}.
33
+ */
34
+ export const PLAN_ARTIFACT_SUFFIX = ".plan.md";
35
+
36
+ /**
37
+ * Returns `true` when a filename is a plan's: the legacy exact `plan.md`, or
38
+ * any `*.plan.md`. Accepting the legacy name keeps plans published before named
39
+ * artifacts existed detectable.
40
+ */
41
+ export function isPlanArtifactName(name: string): boolean {
42
+ return name === PLAN_ARTIFACT_NAME || name.endsWith(PLAN_ARTIFACT_SUFFIX);
43
+ }
44
+
45
+ /**
46
+ * Returns `true` when an artifact is the published plan: a FILE artifact whose
47
+ * name satisfies {@link isPlanArtifactName}.
26
48
  *
27
49
  * Detection is by convention — the same lightweight, content-free approach used
28
50
  * for skill packages ({@link isSkillPackage}) — so the UI never needs an extra
@@ -32,7 +54,7 @@ export const PLAN_ARTIFACT_NAME = "plan.md";
32
54
  export function isPlanArtifact(artifact: ExecutionArtifact): boolean {
33
55
  return (
34
56
  artifact.kind === ExecutionArtifactKind.FILE &&
35
- artifact.name === PLAN_ARTIFACT_NAME
57
+ isPlanArtifactName(artifact.name)
36
58
  );
37
59
  }
38
60
 
@@ -56,7 +78,7 @@ export function findPlanArtifact(
56
78
 
57
79
  /**
58
80
  * Finds the session's LATEST plan across all executions — the plan the panel's
59
- * plan document tab edits and "Build from plan" implements. Executions are
81
+ * plan document tab edits and the "Build" action implements. Executions are
60
82
  * scanned newest-first (the array is chronological), mirroring the thread's
61
83
  * latest-plan-owns-the-build-action rule so every surface agrees on which
62
84
  * plan is current.
@@ -26,9 +26,11 @@ export type { SkillPackageDetection } from "./detect-skill-package.js";
26
26
 
27
27
  export {
28
28
  isPlanArtifact,
29
+ isPlanArtifactName,
29
30
  findPlanArtifact,
30
31
  findLatestSessionPlan,
31
32
  PLAN_ARTIFACT_NAME,
33
+ PLAN_ARTIFACT_SUFFIX,
32
34
  } from "./detect-plan-artifact.js";
33
35
  export type { SessionPlan } from "./detect-plan-artifact.js";
34
36
 
@@ -203,7 +203,7 @@ export function PlanEditor({
203
203
  >
204
204
  <ImplementIcon />
205
205
  <span className="truncate">
206
- {buildDisabled ? "Starting build…" : "Build from plan"}
206
+ {buildDisabled ? "Starting build…" : "Build"}
207
207
  </span>
208
208
  </button>
209
209
  )