@stigmer/react 3.12.2 → 3.12.3

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 (151) hide show
  1. package/access/ManageAccessDialog.d.ts.map +1 -1
  2. package/access/ManageAccessDialog.js +3 -19
  3. package/access/ManageAccessDialog.js.map +1 -1
  4. package/agent-instance/CreateAgentInstanceDialog.d.ts.map +1 -1
  5. package/agent-instance/CreateAgentInstanceDialog.js +7 -20
  6. package/agent-instance/CreateAgentInstanceDialog.js.map +1 -1
  7. package/attachment/AttachmentImageLightbox.d.ts.map +1 -1
  8. package/attachment/AttachmentImageLightbox.js +8 -23
  9. package/attachment/AttachmentImageLightbox.js.map +1 -1
  10. package/channel/ChannelConversationsDialog.d.ts.map +1 -1
  11. package/channel/ChannelConversationsDialog.js +3 -19
  12. package/channel/ChannelConversationsDialog.js.map +1 -1
  13. package/channel/ChannelCredentialsDialog.d.ts.map +1 -1
  14. package/channel/ChannelCredentialsDialog.js +3 -19
  15. package/channel/ChannelCredentialsDialog.js.map +1 -1
  16. package/channel/ChannelTemplatesDialog.d.ts.map +1 -1
  17. package/channel/ChannelTemplatesDialog.js +4 -20
  18. package/channel/ChannelTemplatesDialog.js.map +1 -1
  19. package/channel/ConnectSlackDialog.d.ts.map +1 -1
  20. package/channel/ConnectSlackDialog.js +3 -21
  21. package/channel/ConnectSlackDialog.js.map +1 -1
  22. package/channel/ConnectWhatsAppDialog.d.ts.map +1 -1
  23. package/channel/ConnectWhatsAppDialog.js +3 -21
  24. package/channel/ConnectWhatsAppDialog.js.map +1 -1
  25. package/conversation/ConversationAttentionBanner.d.ts +23 -6
  26. package/conversation/ConversationAttentionBanner.d.ts.map +1 -1
  27. package/conversation/ConversationAttentionBanner.js +25 -10
  28. package/conversation/ConversationAttentionBanner.js.map +1 -1
  29. package/conversation/ConversationListPane.d.ts.map +1 -1
  30. package/conversation/ConversationListPane.js +25 -6
  31. package/conversation/ConversationListPane.js.map +1 -1
  32. package/conversation/ConversationsWorkbench.d.ts.map +1 -1
  33. package/conversation/ConversationsWorkbench.js +1 -1
  34. package/conversation/ConversationsWorkbench.js.map +1 -1
  35. package/execution/ApprovalCard.d.ts.map +1 -1
  36. package/execution/ApprovalCard.js +13 -3
  37. package/execution/ApprovalCard.js.map +1 -1
  38. package/execution/ArtifactPreviewModal.d.ts.map +1 -1
  39. package/execution/ArtifactPreviewModal.js +11 -17
  40. package/execution/ArtifactPreviewModal.js.map +1 -1
  41. package/execution/ToolCallItem.d.ts +3 -1
  42. package/execution/ToolCallItem.d.ts.map +1 -1
  43. package/execution/ToolCallItem.js +19 -7
  44. package/execution/ToolCallItem.js.map +1 -1
  45. package/execution/index.d.ts +1 -1
  46. package/execution/index.d.ts.map +1 -1
  47. package/execution/index.js +1 -1
  48. package/execution/index.js.map +1 -1
  49. package/execution/tool-categories.d.ts +51 -0
  50. package/execution/tool-categories.d.ts.map +1 -1
  51. package/execution/tool-categories.js +103 -1
  52. package/execution/tool-categories.js.map +1 -1
  53. package/execution/tool-presenter.d.ts +15 -0
  54. package/execution/tool-presenter.d.ts.map +1 -1
  55. package/execution/tool-presenter.js +7 -2
  56. package/execution/tool-presenter.js.map +1 -1
  57. package/index.d.ts +1 -1
  58. package/index.d.ts.map +1 -1
  59. package/index.js +1 -1
  60. package/index.js.map +1 -1
  61. package/internal/DialogShell.d.ts +77 -0
  62. package/internal/DialogShell.d.ts.map +1 -0
  63. package/internal/DialogShell.js +77 -0
  64. package/internal/DialogShell.js.map +1 -0
  65. package/manifest/ApplyManifestDialog.d.ts.map +1 -1
  66. package/manifest/ApplyManifestDialog.js +7 -17
  67. package/manifest/ApplyManifestDialog.js.map +1 -1
  68. package/manifest/EditResourceYamlDialog.d.ts.map +1 -1
  69. package/manifest/EditResourceYamlDialog.js +3 -19
  70. package/manifest/EditResourceYamlDialog.js.map +1 -1
  71. package/mcp-server/McpServerConnectDialog.d.ts.map +1 -1
  72. package/mcp-server/McpServerConnectDialog.js +8 -20
  73. package/mcp-server/McpServerConnectDialog.js.map +1 -1
  74. package/mcp-server/McpServerDetailView.d.ts.map +1 -1
  75. package/mcp-server/McpServerDetailView.js +4 -15
  76. package/mcp-server/McpServerDetailView.js.map +1 -1
  77. package/package.json +4 -4
  78. package/resource-detail/ConfirmDialog.d.ts +2 -3
  79. package/resource-detail/ConfirmDialog.d.ts.map +1 -1
  80. package/resource-detail/ConfirmDialog.js +8 -20
  81. package/resource-detail/ConfirmDialog.js.map +1 -1
  82. package/sharing/ShareAgentDialog.d.ts.map +1 -1
  83. package/sharing/ShareAgentDialog.js +2 -20
  84. package/sharing/ShareAgentDialog.js.map +1 -1
  85. package/skill/SkillDiffDialog.d.ts.map +1 -1
  86. package/skill/SkillDiffDialog.js +5 -21
  87. package/skill/SkillDiffDialog.js.map +1 -1
  88. package/src/access/ManageAccessDialog.tsx +7 -27
  89. package/src/agent-instance/CreateAgentInstanceDialog.tsx +14 -27
  90. package/src/attachment/AttachmentImageLightbox.tsx +13 -36
  91. package/src/channel/ChannelConversationsDialog.tsx +8 -27
  92. package/src/channel/ChannelCredentialsDialog.tsx +8 -28
  93. package/src/channel/ChannelTemplatesDialog.tsx +10 -29
  94. package/src/channel/ConnectSlackDialog.tsx +8 -30
  95. package/src/channel/ConnectWhatsAppDialog.tsx +8 -30
  96. package/src/conversation/ConversationAttentionBanner.tsx +64 -19
  97. package/src/conversation/ConversationListPane.tsx +42 -17
  98. package/src/conversation/ConversationsWorkbench.tsx +1 -0
  99. package/src/conversation/__tests__/ConversationAttentionBanner.test.tsx +76 -2
  100. package/src/conversation/__tests__/ConversationListPane.test.tsx +15 -4
  101. package/src/execution/ApprovalCard.tsx +23 -2
  102. package/src/execution/ArtifactPreviewModal.tsx +15 -27
  103. package/src/execution/ToolCallItem.tsx +19 -8
  104. package/src/execution/__tests__/intent-title.test.tsx +221 -0
  105. package/src/execution/index.ts +3 -0
  106. package/src/execution/tool-categories.ts +123 -1
  107. package/src/execution/tool-presenter.ts +22 -1
  108. package/src/index.ts +3 -0
  109. package/src/internal/DialogShell.tsx +169 -0
  110. package/src/internal/__tests__/DialogShell.test.tsx +223 -0
  111. package/src/manifest/ApplyManifestDialog.tsx +8 -26
  112. package/src/manifest/EditResourceYamlDialog.tsx +4 -31
  113. package/src/mcp-server/McpServerConnectDialog.tsx +15 -29
  114. package/src/mcp-server/McpServerDetailView.tsx +12 -24
  115. package/src/resource-detail/ConfirmDialog.tsx +9 -30
  116. package/src/sharing/ShareAgentDialog.tsx +7 -29
  117. package/src/skill/SkillDiffDialog.tsx +13 -36
  118. package/src/workflow/ViewYamlDialog.tsx +18 -37
  119. package/src/workflow/WorkflowArchitectDialog.tsx +11 -43
  120. package/src/workflow/WorkflowExplainDialog.tsx +11 -37
  121. package/src/workflow/WorkflowGraphFullscreenDialog.tsx +14 -26
  122. package/src/workflow/WorkflowRunDialog.tsx +21 -48
  123. package/src/workflow/execution-comparison/ExecutionComparisonPicker.tsx +14 -31
  124. package/src/workflow/instance/CreateWorkflowInstanceDialog.tsx +14 -27
  125. package/src/workflow/templates/WorkflowTemplatePreview.tsx +18 -28
  126. package/styles.css +1 -1
  127. package/workflow/ViewYamlDialog.d.ts.map +1 -1
  128. package/workflow/ViewYamlDialog.js +10 -29
  129. package/workflow/ViewYamlDialog.js.map +1 -1
  130. package/workflow/WorkflowArchitectDialog.d.ts.map +1 -1
  131. package/workflow/WorkflowArchitectDialog.js +5 -24
  132. package/workflow/WorkflowArchitectDialog.js.map +1 -1
  133. package/workflow/WorkflowExplainDialog.d.ts.map +1 -1
  134. package/workflow/WorkflowExplainDialog.js +5 -21
  135. package/workflow/WorkflowExplainDialog.js.map +1 -1
  136. package/workflow/WorkflowGraphFullscreenDialog.d.ts.map +1 -1
  137. package/workflow/WorkflowGraphFullscreenDialog.js +11 -17
  138. package/workflow/WorkflowGraphFullscreenDialog.js.map +1 -1
  139. package/workflow/WorkflowRunDialog.d.ts.map +1 -1
  140. package/workflow/WorkflowRunDialog.js +13 -29
  141. package/workflow/WorkflowRunDialog.js.map +1 -1
  142. package/workflow/execution-comparison/ExecutionComparisonPicker.d.ts.map +1 -1
  143. package/workflow/execution-comparison/ExecutionComparisonPicker.js +8 -26
  144. package/workflow/execution-comparison/ExecutionComparisonPicker.js.map +1 -1
  145. package/workflow/instance/CreateWorkflowInstanceDialog.d.ts.map +1 -1
  146. package/workflow/instance/CreateWorkflowInstanceDialog.js +7 -20
  147. package/workflow/instance/CreateWorkflowInstanceDialog.js.map +1 -1
  148. package/workflow/templates/WorkflowTemplatePreview.d.ts +1 -1
  149. package/workflow/templates/WorkflowTemplatePreview.d.ts.map +1 -1
  150. package/workflow/templates/WorkflowTemplatePreview.js +10 -22
  151. package/workflow/templates/WorkflowTemplatePreview.js.map +1 -1
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useRef } from "react";
3
+ import { useCallback } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
+ import { DialogShell } from "../internal/DialogShell.js";
5
6
  import type { ConfirmState } from "./types.js";
6
7
 
7
8
  export interface ConfirmDialogProps {
@@ -16,9 +17,8 @@ export interface ConfirmDialogProps {
16
17
  /**
17
18
  * Accessible confirmation dialog for destructive actions.
18
19
  *
19
- * Uses the native `<dialog>` element with `showModal()` for built-in
20
- * focus trapping, Escape key handling, and backdrop. Styled via
21
- * `--stgm-*` design tokens.
20
+ * Renders on {@link DialogShell} (native `<dialog>` + `showModal()` focus
21
+ * trapping, Escape, token backdrop). Styled via `--stgm-*` design tokens.
22
22
  *
23
23
  * Pairs with {@link useConfirmAction} which manages the imperative
24
24
  * `confirm()` → Promise pattern.
@@ -51,22 +51,9 @@ export function ConfirmDialog({
51
51
  onConfirm,
52
52
  onCancel,
53
53
  }: ConfirmDialogProps) {
54
- const dialogRef = useRef<HTMLDialogElement>(null);
55
-
56
- useEffect(() => {
57
- const dialog = dialogRef.current;
58
- if (!dialog) return;
59
- if (state && !dialog.open) {
60
- dialog.showModal();
61
- } else if (!state && dialog.open) {
62
- dialog.close();
63
- }
64
- }, [state]);
65
-
66
- const handleDialogCancel = useCallback(
67
- (e: React.SyntheticEvent) => {
68
- e.preventDefault();
69
- onCancel();
54
+ const handleOpenChange = useCallback(
55
+ (open: boolean) => {
56
+ if (!open) onCancel();
70
57
  },
71
58
  [onCancel],
72
59
  );
@@ -76,15 +63,7 @@ export function ConfirmDialog({
76
63
  const isDestructive = state.variant === "destructive";
77
64
 
78
65
  return (
79
- <dialog
80
- ref={dialogRef}
81
- onCancel={handleDialogCancel}
82
- className={cn(
83
- "stg:fixed stg:inset-0 stg:z-50 stg:m-auto stg:w-full stg:max-w-sm stg:rounded-lg stg:border stg:border-border stg:bg-popover stg:p-0 stg:text-popover-foreground stg:shadow-lg",
84
- "stg:backdrop:bg-backdrop",
85
- "stg:open:animate-in stg:open:fade-in-0 stg:open:zoom-in-95",
86
- )}
87
- >
66
+ <DialogShell open onOpenChange={handleOpenChange} width="sm" aria-label={state.title}>
88
67
  <div className="stg:flex stg:flex-col stg:gap-4 stg:p-6">
89
68
  <div className="stg:flex stg:flex-col stg:gap-1.5">
90
69
  <h3 className="stg:text-base stg:font-semibold stg:text-foreground">
@@ -122,6 +101,6 @@ export function ConfirmDialog({
122
101
  </button>
123
102
  </div>
124
103
  </div>
125
- </dialog>
104
+ </DialogShell>
126
105
  );
127
106
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useCallback, useId, useMemo, useRef, useState, type FormEvent } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
+ import { DialogShell } from "../internal/DialogShell.js";
5
6
  import {
6
7
  MAX_ALLOWED_ORIGINS,
7
8
  StigmerError,
@@ -137,8 +138,6 @@ export function ShareAgentDialog({
137
138
  onSharingChanged,
138
139
  modal = true,
139
140
  }: ShareAgentDialogProps) {
140
- const dialogRef = useRef<HTMLDialogElement>(null);
141
-
142
141
  // Instance-scoped title id (oss#593): a reusable component must not
143
142
  // hardcode DOM ids — hosts legitimately mount this dialog more than once
144
143
  // per page (e.g. zone-cached detail pages), and duplicate ids break the
@@ -146,36 +145,15 @@ export function ShareAgentDialog({
146
145
  const titleId = useId();
147
146
 
148
147
  const handleClose = useCallback(() => {
149
- dialogRef.current?.close();
150
148
  onOpenChange(false);
151
149
  }, [onOpenChange]);
152
150
 
153
- // Sync native dialog open state (matches the SDK dialog convention).
154
- // Non-modal hosts pass `open` as a plain attribute instead — the dialog
155
- // renders in-flow with no top layer to manage.
156
- const prevOpenRef = useRef(false);
157
- if (modal && open !== prevOpenRef.current) {
158
- prevOpenRef.current = open;
159
- if (open) {
160
- requestAnimationFrame(() => {
161
- if (dialogRef.current && !dialogRef.current.open) {
162
- dialogRef.current.showModal();
163
- }
164
- });
165
- } else if (dialogRef.current?.open) {
166
- dialogRef.current.close();
167
- }
168
- }
169
-
170
151
  return (
171
- <dialog
172
- ref={dialogRef}
173
- open={modal ? undefined : open}
174
- onClose={handleClose}
175
- className={cn(
176
- "stg:w-full stg:max-w-lg stg:rounded-xl stg:border stg:border-border stg:bg-popover stg:p-0 stg:shadow-xl",
177
- modal ? "stg:fixed stg:inset-0 stg:m-auto stg:backdrop:bg-backdrop" : "stg:relative",
178
- )}
152
+ <DialogShell
153
+ open={open}
154
+ onOpenChange={onOpenChange}
155
+ modal={modal}
156
+ width="lg"
179
157
  aria-labelledby={titleId}
180
158
  >
181
159
  {/* Body mounts only while open so its draft state resets per
@@ -191,7 +169,7 @@ export function ShareAgentDialog({
191
169
  titleId={titleId}
192
170
  />
193
171
  )}
194
- </dialog>
172
+ </DialogShell>
195
173
  );
196
174
  }
197
175
 
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useRef } from "react";
3
+ import { useCallback } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
+ import { DialogShell } from "../internal/DialogShell.js";
5
6
  import { useSkillDiff } from "./useSkillDiff.js";
6
7
  import { MultiFileDiffView } from "../version-history/MultiFileDiffView.js";
7
8
  import { ErrorMessage } from "../error/ErrorMessage.js";
@@ -57,31 +58,9 @@ export interface SkillDiffDialogProps {
57
58
  * ```
58
59
  */
59
60
  export function SkillDiffDialog({ state, onClose }: SkillDiffDialogProps) {
60
- const dialogRef = useRef<HTMLDialogElement>(null);
61
-
62
- useEffect(() => {
63
- const dialog = dialogRef.current;
64
- if (!dialog) return;
65
- if (state && !dialog.open) {
66
- dialog.showModal();
67
- } else if (!state && dialog.open) {
68
- dialog.close();
69
- }
70
- }, [state]);
71
-
72
- const handleDialogCancel = useCallback(
73
- (e: React.SyntheticEvent) => {
74
- e.preventDefault();
75
- onClose();
76
- },
77
- [onClose],
78
- );
79
-
80
- const handleBackdropClick = useCallback(
81
- (e: React.MouseEvent<HTMLDialogElement>) => {
82
- if (e.target === e.currentTarget) {
83
- onClose();
84
- }
61
+ const handleOpenChange = useCallback(
62
+ (open: boolean) => {
63
+ if (!open) onClose();
85
64
  },
86
65
  [onClose],
87
66
  );
@@ -94,15 +73,13 @@ export function SkillDiffDialog({ state, onClose }: SkillDiffDialogProps) {
94
73
  if (!state) return null;
95
74
 
96
75
  return (
97
- <dialog
98
- ref={dialogRef}
99
- onCancel={handleDialogCancel}
100
- onClick={handleBackdropClick}
101
- className={cn(
102
- "stg:fixed stg:inset-0 stg:z-50 stg:m-auto stg:h-[85vh] stg:w-full stg:max-w-4xl stg:rounded-lg stg:border stg:border-border stg:bg-popover stg:p-0 stg:text-popover-foreground stg:shadow-lg",
103
- "stg:backdrop:bg-backdrop",
104
- "stg:open:animate-in stg:open:fade-in-0 stg:open:zoom-in-95",
105
- )}
76
+ <DialogShell
77
+ open
78
+ onOpenChange={handleOpenChange}
79
+ width="4xl"
80
+ dismissOnBackdrop
81
+ className="stg:h-[85vh]"
82
+ aria-label="Skill version diff"
106
83
  >
107
84
  <div className="stg:flex stg:h-full stg:flex-col">
108
85
  {/* Header */}
@@ -142,7 +119,7 @@ export function SkillDiffDialog({ state, onClose }: SkillDiffDialogProps) {
142
119
  ) : null}
143
120
  </div>
144
121
  </div>
145
- </dialog>
122
+ </DialogShell>
146
123
  );
147
124
  }
148
125
 
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
 
3
- import { memo, useCallback, useEffect, useRef } from "react";
4
- import { cn } from "@stigmer/theme";
3
+ import { memo, useCallback } from "react";
5
4
  import type { WorkflowGraphModel } from "./workflow-graph-model.js";
6
5
  import { taskToYaml } from "./inspector/task-to-yaml.js";
6
+ import { DialogShell } from "../internal/DialogShell.js";
7
7
  import { useCopyFeedback } from "../internal/useCopyFeedback.js";
8
8
 
9
9
  /** Props for {@link ViewYamlDialog}. */
@@ -32,7 +32,6 @@ export const ViewYamlDialog = memo(function ViewYamlDialog({
32
32
  graph,
33
33
  onClose,
34
34
  }: ViewYamlDialogProps) {
35
- const dialogRef = useRef<HTMLDialogElement>(null);
36
35
  const { copy, copied } = useCopyFeedback();
37
36
 
38
37
  const node = nodeId && graph
@@ -41,45 +40,27 @@ export const ViewYamlDialog = memo(function ViewYamlDialog({
41
40
 
42
41
  const yaml = node ? taskToYaml(node) : "";
43
42
 
44
- useEffect(() => {
45
- const dialog = dialogRef.current;
46
- if (!dialog) return;
47
- if (nodeId && node) {
48
- if (!dialog.open) dialog.showModal();
49
- } else {
50
- if (dialog.open) dialog.close();
51
- }
52
- }, [nodeId, node]);
53
-
54
- useEffect(() => {
55
- const dialog = dialogRef.current;
56
- if (!dialog) return;
57
- const handleClose = () => onClose();
58
- dialog.addEventListener("close", handleClose);
59
- return () => dialog.removeEventListener("close", handleClose);
60
- }, [onClose]);
43
+ const handleOpenChange = useCallback(
44
+ (open: boolean) => {
45
+ if (!open) onClose();
46
+ },
47
+ [onClose],
48
+ );
61
49
 
62
50
  const handleCopy = useCallback(() => {
63
51
  void copy(yaml);
64
52
  }, [copy, yaml]);
65
53
 
66
- const handleBackdropClick = useCallback(
67
- (e: React.MouseEvent<HTMLDialogElement>) => {
68
- if (e.target === dialogRef.current) {
69
- onClose();
70
- }
71
- },
72
- [onClose],
73
- );
74
-
75
54
  return (
76
- <dialog
77
- ref={dialogRef}
78
- className={cn(
79
- "stgm stg:m-auto stg:max-h-[80vh] stg:w-full stg:max-w-lg stg:rounded-lg stg:border stg:border-[var(--stgm-border,#e5e5e5)] stg:bg-[var(--stgm-background,#fff)] stg:p-0 stg:shadow-xl",
80
- "stg:backdrop:bg-backdrop",
81
- )}
82
- onClick={handleBackdropClick}
55
+ <DialogShell
56
+ open={Boolean(nodeId && node)}
57
+ onOpenChange={handleOpenChange}
58
+ width="lg"
59
+ dismissOnBackdrop
60
+ // `stgm` re-establishes the theme scope (this dialog renders from the
61
+ // workflow canvas); the var-fallback styling of the body predates the
62
+ // token utilities and stays untouched here.
63
+ className="stgm stg:max-h-[80vh] stg:bg-[var(--stgm-background,#fff)] stg:border-[var(--stgm-border,#e5e5e5)]"
83
64
  aria-label={node ? `YAML for ${node.taskName}` : "View YAML"}
84
65
  >
85
66
  {node && (
@@ -121,7 +102,7 @@ export const ViewYamlDialog = memo(function ViewYamlDialog({
121
102
  </div>
122
103
  </div>
123
104
  )}
124
- </dialog>
105
+ </DialogShell>
125
106
  );
126
107
  });
127
108
 
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useId, useRef, useState } from "react";
3
+ import { useCallback, useEffect, useId, useState } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
+ import { DialogShell } from "../internal/DialogShell.js";
5
6
  import {
6
7
  useWorkflowArchitectFlow,
7
8
  type ArchitectPhase,
@@ -64,8 +65,6 @@ export function WorkflowArchitectDialog({
64
65
  onSuccess,
65
66
  onError,
66
67
  }: WorkflowArchitectDialogProps) {
67
- const dialogRef = useRef<HTMLDialogElement>(null);
68
-
69
68
  const handleSuccess = useCallback(
70
69
  (successOrg: string, slug: string) => {
71
70
  onOpenChange(false);
@@ -80,54 +79,23 @@ export function WorkflowArchitectDialog({
80
79
  onError,
81
80
  });
82
81
 
82
+ // Each opening starts from a clean prompt.
83
83
  useEffect(() => {
84
- const dialog = dialogRef.current;
85
- if (!dialog) return;
86
- if (open && !dialog.open) {
87
- flow.reset();
88
- dialog.showModal();
89
- } else if (!open && dialog.open) {
90
- dialog.close();
91
- }
84
+ if (open) flow.reset();
92
85
  }, [open, flow.reset]);
93
86
 
94
- const handleDialogCancel = useCallback(
95
- (e: React.SyntheticEvent) => {
96
- e.preventDefault();
97
- onOpenChange(false);
98
- },
99
- [onOpenChange],
100
- );
101
-
102
- const handleBackdropClick = useCallback(
103
- (e: React.MouseEvent<HTMLDialogElement>) => {
104
- if (e.target === dialogRef.current) {
105
- onOpenChange(false);
106
- }
107
- },
108
- [onOpenChange],
109
- );
110
-
111
- const isWorking =
112
- flow.phase === "starting" ||
113
- flow.phase === "streaming" ||
114
- flow.phase === "applying";
115
-
116
87
  return (
117
- <dialog
118
- ref={dialogRef}
119
- onCancel={handleDialogCancel}
120
- onClick={handleBackdropClick}
121
- className={cn(
122
- "stg:fixed stg:inset-0 stg:z-50 stg:m-auto stg:w-full stg:max-w-3xl stg:rounded-lg stg:border stg:border-border stg:bg-popover stg:p-0 stg:text-popover-foreground stg:shadow-lg",
123
- "stg:backdrop:bg-backdrop",
124
- "stg:open:animate-in stg:open:fade-in-0 stg:open:zoom-in-95",
125
- )}
88
+ <DialogShell
89
+ open={open}
90
+ onOpenChange={onOpenChange}
91
+ width="3xl"
92
+ dismissOnBackdrop
93
+ aria-label="Workflow Architect"
126
94
  >
127
95
  <div className="stg:flex stg:flex-col">
128
96
  <DialogPhase flow={flow} onClose={() => onOpenChange(false)} />
129
97
  </div>
130
- </dialog>
98
+ </DialogShell>
131
99
  );
132
100
  }
133
101
 
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useRef } from "react";
3
+ import { useCallback, useEffect } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
+ import { DialogShell } from "../internal/DialogShell.js";
5
6
  import { useExplainWorkflowFlow, type ExplainPhase } from "./useExplainWorkflowFlow.js";
6
7
  import { MessageThread } from "../execution/MessageThread.js";
7
8
  import { SpinnerIcon } from "../internal/SpinnerIcon.js";
@@ -36,8 +37,6 @@ export function WorkflowExplainDialog({
36
37
  org,
37
38
  currentYaml,
38
39
  }: WorkflowExplainDialogProps) {
39
- const dialogRef = useRef<HTMLDialogElement>(null);
40
-
41
40
  const flow = useExplainWorkflowFlow({
42
41
  org,
43
42
  currentYaml,
@@ -46,36 +45,14 @@ export function WorkflowExplainDialog({
46
45
  },
47
46
  });
48
47
 
49
- // Show/hide dialog
48
+ // Each opening starts a fresh explanation.
50
49
  useEffect(() => {
51
- const dialog = dialogRef.current;
52
- if (!dialog) return;
53
- if (open && !dialog.open) {
50
+ if (open) {
54
51
  flow.reset();
55
- dialog.showModal();
56
52
  flow.explain();
57
- } else if (!open && dialog.open) {
58
- dialog.close();
59
53
  }
60
54
  }, [open, flow.reset, flow.explain]);
61
55
 
62
- const handleDialogCancel = useCallback(
63
- (e: React.SyntheticEvent) => {
64
- e.preventDefault();
65
- onOpenChange(false);
66
- },
67
- [onOpenChange],
68
- );
69
-
70
- const handleBackdropClick = useCallback(
71
- (e: React.MouseEvent<HTMLDialogElement>) => {
72
- if (e.target === dialogRef.current) {
73
- onOpenChange(false);
74
- }
75
- },
76
- [onOpenChange],
77
- );
78
-
79
56
  const { copy, copied } = useCopyFeedback();
80
57
  const handleCopy = useCallback(() => {
81
58
  if (flow.explanation) {
@@ -84,15 +61,12 @@ export function WorkflowExplainDialog({
84
61
  }, [copy, flow.explanation]);
85
62
 
86
63
  return (
87
- <dialog
88
- ref={dialogRef}
89
- onCancel={handleDialogCancel}
90
- onClick={handleBackdropClick}
91
- className={cn(
92
- "stg:fixed stg:inset-0 stg:z-50 stg:m-auto stg:w-full stg:max-w-2xl stg:rounded-lg stg:border stg:border-border stg:bg-popover stg:p-0 stg:text-popover-foreground stg:shadow-lg",
93
- "stg:backdrop:bg-backdrop",
94
- "stg:open:animate-in stg:open:fade-in-0 stg:open:zoom-in-95",
95
- )}
64
+ <DialogShell
65
+ open={open}
66
+ onOpenChange={onOpenChange}
67
+ width="2xl"
68
+ dismissOnBackdrop
69
+ aria-label="Workflow Explanation"
96
70
  >
97
71
  <div className="stg:flex stg:flex-col">
98
72
  {/* Header */}
@@ -206,7 +180,7 @@ export function WorkflowExplainDialog({
206
180
  </div>
207
181
  </div>
208
182
  </div>
209
- </dialog>
183
+ </DialogShell>
210
184
  );
211
185
  }
212
186
 
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useRef } from "react";
3
+ import { useCallback } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
+ import { DialogShell } from "../internal/DialogShell.js";
5
6
  import type { Workflow } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
6
7
  import { WorkflowOverviewGraph } from "./WorkflowOverviewGraph.js";
7
8
 
@@ -38,23 +39,9 @@ export function WorkflowGraphFullscreenDialog({
38
39
  onClose,
39
40
  onOpenInEditor,
40
41
  }: WorkflowGraphFullscreenDialogProps) {
41
- const dialogRef = useRef<HTMLDialogElement>(null);
42
-
43
- useEffect(() => {
44
- const dialog = dialogRef.current;
45
- if (!dialog) return;
46
-
47
- if (open && !dialog.open) {
48
- dialog.showModal();
49
- } else if (!open && dialog.open) {
50
- dialog.close();
51
- }
52
- }, [open]);
53
-
54
- const handleCancel = useCallback(
55
- (e: React.SyntheticEvent) => {
56
- e.preventDefault();
57
- onClose();
42
+ const handleOpenChange = useCallback(
43
+ (nextOpen: boolean) => {
44
+ if (!nextOpen) onClose();
58
45
  },
59
46
  [onClose],
60
47
  );
@@ -62,14 +49,15 @@ export function WorkflowGraphFullscreenDialog({
62
49
  const workflowName = workflow?.metadata?.name || "Task Flow";
63
50
 
64
51
  return (
65
- <dialog
66
- ref={dialogRef}
67
- onCancel={handleCancel}
52
+ <DialogShell
53
+ open={open}
54
+ onOpenChange={handleOpenChange}
68
55
  aria-label={`${workflowName} — full view`}
69
- className={cn(
70
- "stg:fixed stg:inset-0 stg:z-50 stg:m-auto stg:h-[90vh] stg:w-[95vw] stg:rounded-lg stg:border stg:border-border stg:bg-background stg:p-0 stg:text-foreground stg:shadow-2xl stg:outline-none",
71
- "stg:[&::backdrop]:bg-black/60",
72
- )}
56
+ // Near-full-viewport outlier: free-form sizing and the background
57
+ // surface for the graph canvas. The previously hardcoded ::backdrop
58
+ // color (bg-black/60 — it predated and escaped the #652 fence via the
59
+ // arbitrary-variant spelling) now rides the shell's token backdrop.
60
+ className="stg:h-[90vh] stg:w-[95vw] stg:max-w-none stg:bg-background stg:text-foreground stg:outline-none"
73
61
  >
74
62
  {open && (
75
63
  <div className="stg:flex stg:h-full stg:flex-col">
@@ -101,7 +89,7 @@ export function WorkflowGraphFullscreenDialog({
101
89
  </div>
102
90
  </div>
103
91
  )}
104
- </dialog>
92
+ </DialogShell>
105
93
  );
106
94
  }
107
95
 
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useRef } from "react";
3
+ import { useCallback, useEffect } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
+ import { DialogShell } from "../internal/DialogShell.js";
5
6
  import type { Workflow } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
6
7
  import type { WorkflowInstance } from "@stigmer/protos/ai/stigmer/agentic/workflowinstance/v1/api_pb";
7
8
  import { useRunWorkflowFlow } from "./useRunWorkflowFlow.js";
@@ -82,8 +83,6 @@ export function WorkflowRunDialog({
82
83
  onSuccess,
83
84
  onError,
84
85
  }: WorkflowRunDialogProps) {
85
- const dialogRef = useRef<HTMLDialogElement>(null);
86
-
87
86
  const handleSuccess = useCallback(
88
87
  (executionId: string) => {
89
88
  onOpenChange(false);
@@ -100,25 +99,19 @@ export function WorkflowRunDialog({
100
99
  onError,
101
100
  });
102
101
 
103
- // Sync open state with the native dialog
102
+ // Each opening starts from a clean form.
104
103
  useEffect(() => {
105
- const dialog = dialogRef.current;
106
- if (!dialog) return;
107
- if (open && !dialog.open) {
108
- flow.reset();
109
- // Preselection must follow reset (reset clears the selection back to
110
- // the server-resolved default). Guarded against stale ids: an instance
111
- // deleted between the caller capturing the id and this open falls back
112
- // to the default option instead of a <select> value with no option.
113
- if (
114
- initialInstanceId &&
115
- instances.some((i) => i.metadata?.id === initialInstanceId)
116
- ) {
117
- flow.setSelectedInstanceId(initialInstanceId);
118
- }
119
- dialog.showModal();
120
- } else if (!open && dialog.open) {
121
- dialog.close();
104
+ if (!open) return;
105
+ flow.reset();
106
+ // Preselection must follow reset (reset clears the selection back to
107
+ // the server-resolved default). Guarded against stale ids: an instance
108
+ // deleted between the caller capturing the id and this open falls back
109
+ // to the default option instead of a <select> value with no option.
110
+ if (
111
+ initialInstanceId &&
112
+ instances.some((i) => i.metadata?.id === initialInstanceId)
113
+ ) {
114
+ flow.setSelectedInstanceId(initialInstanceId);
122
115
  }
123
116
  }, [
124
117
  open,
@@ -128,36 +121,16 @@ export function WorkflowRunDialog({
128
121
  instances,
129
122
  ]);
130
123
 
131
- const handleDialogCancel = useCallback(
132
- (e: React.SyntheticEvent) => {
133
- e.preventDefault();
134
- onOpenChange(false);
135
- },
136
- [onOpenChange],
137
- );
138
-
139
- const handleBackdropClick = useCallback(
140
- (e: React.MouseEvent<HTMLDialogElement>) => {
141
- if (e.target === dialogRef.current) {
142
- onOpenChange(false);
143
- }
144
- },
145
- [onOpenChange],
146
- );
147
-
148
124
  const workflowName =
149
125
  workflow.metadata?.name || workflow.metadata?.slug || "Workflow";
150
126
 
151
127
  return (
152
- <dialog
153
- ref={dialogRef}
154
- onCancel={handleDialogCancel}
155
- onClick={handleBackdropClick}
156
- className={cn(
157
- "stg:fixed stg:inset-0 stg:z-50 stg:m-auto stg:w-full stg:max-w-lg stg:rounded-lg stg:border stg:border-border stg:bg-popover stg:p-0 stg:text-popover-foreground stg:shadow-lg",
158
- "stg:backdrop:bg-backdrop",
159
- "stg:open:animate-in stg:open:fade-in-0 stg:open:zoom-in-95",
160
- )}
128
+ <DialogShell
129
+ open={open}
130
+ onOpenChange={onOpenChange}
131
+ width="lg"
132
+ dismissOnBackdrop
133
+ aria-label={`Run ${workflowName}`}
161
134
  >
162
135
  <div className="stg:flex stg:flex-col">
163
136
  {/* Header */}
@@ -231,7 +204,7 @@ export function WorkflowRunDialog({
231
204
  </button>
232
205
  </div>
233
206
  </div>
234
- </dialog>
207
+ </DialogShell>
235
208
  );
236
209
  }
237
210