@stigmer/react 3.0.5 → 3.0.7-dev.20260611143057
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/AgentDetailView.d.ts.map +1 -1
- package/agent/AgentDetailView.js +1 -1
- package/agent/AgentDetailView.js.map +1 -1
- package/agent-instance/AgentInstanceDetailPanel.d.ts.map +1 -1
- package/agent-instance/AgentInstanceDetailPanel.js +2 -13
- package/agent-instance/AgentInstanceDetailPanel.js.map +1 -1
- package/agent-instance/AgentInstanceList.d.ts.map +1 -1
- package/agent-instance/AgentInstanceList.js +2 -13
- package/agent-instance/AgentInstanceList.js.map +1 -1
- package/agent-instance/CreateAgentInstanceDialog.d.ts.map +1 -1
- package/agent-instance/CreateAgentInstanceDialog.js +1 -1
- package/agent-instance/CreateAgentInstanceDialog.js.map +1 -1
- package/composer/SessionComposer.d.ts +14 -0
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +15 -9
- package/composer/SessionComposer.js.map +1 -1
- package/index.d.ts +3 -3
- package/index.d.ts.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/library/InstanceVisibilitySelector.d.ts +30 -23
- package/library/InstanceVisibilitySelector.d.ts.map +1 -1
- package/library/InstanceVisibilitySelector.js +22 -145
- package/library/InstanceVisibilitySelector.js.map +1 -1
- package/library/ResourceVisibilityControl.d.ts +23 -6
- package/library/ResourceVisibilityControl.d.ts.map +1 -1
- package/library/ResourceVisibilityControl.js +38 -9
- package/library/ResourceVisibilityControl.js.map +1 -1
- package/library/ScopeToggle.d.ts +1 -1
- package/library/ScopeToggle.js +1 -1
- package/library/VisibilityOptionRow.d.ts +52 -0
- package/library/VisibilityOptionRow.d.ts.map +1 -0
- package/library/VisibilityOptionRow.js +92 -0
- package/library/VisibilityOptionRow.js.map +1 -0
- package/library/VisibilitySelector.d.ts +98 -0
- package/library/VisibilitySelector.d.ts.map +1 -0
- package/library/VisibilitySelector.js +193 -0
- package/library/VisibilitySelector.js.map +1 -0
- package/library/index.d.ts +4 -2
- package/library/index.d.ts.map +1 -1
- package/library/index.js +2 -1
- package/library/index.js.map +1 -1
- package/library/useUpdateVisibility.d.ts +5 -4
- package/library/useUpdateVisibility.d.ts.map +1 -1
- package/library/useUpdateVisibility.js +5 -4
- package/library/useUpdateVisibility.js.map +1 -1
- package/library/visibilityLevels.d.ts +96 -0
- package/library/visibilityLevels.d.ts.map +1 -0
- package/library/visibilityLevels.js +97 -0
- package/library/visibilityLevels.js.map +1 -0
- package/mcp-server/McpServerDetailView.d.ts +1 -11
- package/mcp-server/McpServerDetailView.d.ts.map +1 -1
- package/mcp-server/McpServerDetailView.js +3 -6
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/package.json +4 -4
- package/resource-detail/types.d.ts +1 -1
- package/session/NewSessionViewer.d.ts +32 -1
- package/session/NewSessionViewer.d.ts.map +1 -1
- package/session/NewSessionViewer.js +20 -9
- package/session/NewSessionViewer.js.map +1 -1
- package/session/SessionViewer.d.ts +24 -1
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +18 -12
- package/session/SessionViewer.js.map +1 -1
- package/session/audience.d.ts +21 -0
- package/session/audience.d.ts.map +1 -0
- package/session/audience.js +2 -0
- package/session/audience.js.map +1 -0
- package/session/index.d.ts +2 -0
- package/session/index.d.ts.map +1 -1
- package/session/index.js.map +1 -1
- package/session/runtime-env.d.ts +47 -0
- package/session/runtime-env.d.ts.map +1 -0
- package/session/runtime-env.js +20 -0
- package/session/runtime-env.js.map +1 -0
- package/session/useNewSessionFlow.d.ts +25 -0
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +20 -8
- package/session/useNewSessionFlow.js.map +1 -1
- package/session/useSessionPageFlow.d.ts +27 -2
- package/session/useSessionPageFlow.d.ts.map +1 -1
- package/session/useSessionPageFlow.js +34 -13
- package/session/useSessionPageFlow.js.map +1 -1
- package/skill/SkillDetailView.d.ts.map +1 -1
- package/skill/SkillDetailView.js +1 -1
- package/skill/SkillDetailView.js.map +1 -1
- package/src/agent/AgentDetailView.tsx +1 -0
- package/src/agent-instance/AgentInstanceDetailPanel.tsx +7 -32
- package/src/agent-instance/AgentInstanceList.tsx +7 -32
- package/src/agent-instance/CreateAgentInstanceDialog.tsx +1 -0
- package/src/composer/SessionComposer.tsx +30 -8
- package/src/composer/__tests__/SessionComposer-lockAgent.test.tsx +150 -0
- package/src/index.ts +10 -2
- package/src/library/InstanceVisibilitySelector.tsx +44 -283
- package/src/library/ResourceVisibilityControl.tsx +54 -8
- package/src/library/ScopeToggle.tsx +1 -1
- package/src/library/VisibilityOptionRow.tsx +244 -0
- package/src/library/VisibilitySelector.tsx +436 -0
- package/src/library/__tests__/VisibilitySelector.test.tsx +256 -0
- package/src/library/index.ts +13 -2
- package/src/library/useUpdateVisibility.ts +5 -4
- package/src/library/visibilityLevels.ts +174 -0
- package/src/mcp-server/McpServerDetailView.tsx +10 -35
- package/src/resource-detail/types.ts +1 -1
- package/src/session/NewSessionViewer.tsx +61 -12
- package/src/session/SessionViewer.tsx +51 -15
- package/src/session/__tests__/audienceWiring.test.tsx +274 -0
- package/src/session/__tests__/useNewSessionFlow.test.tsx +122 -0
- package/src/session/__tests__/useSessionPageFlow.runtimeEnv.test.tsx +170 -0
- package/src/session/audience.ts +20 -0
- package/src/session/index.ts +3 -0
- package/src/session/runtime-env.ts +57 -0
- package/src/session/useNewSessionFlow.ts +44 -9
- package/src/session/useSessionPageFlow.ts +65 -17
- package/src/skill/SkillDetailView.tsx +1 -0
- package/src/workflow/WorkflowDetailView.tsx +1 -0
- package/src/workflow/instance/CreateWorkflowInstanceDialog.tsx +1 -0
- package/src/workflow/instance/WorkflowInstanceDetailPanel.tsx +7 -32
- package/src/workflow/instance/WorkflowInstanceList.tsx +7 -32
- package/styles.css +1 -1
- package/workflow/WorkflowDetailView.d.ts.map +1 -1
- package/workflow/WorkflowDetailView.js +1 -1
- package/workflow/WorkflowDetailView.js.map +1 -1
- package/workflow/instance/CreateWorkflowInstanceDialog.d.ts.map +1 -1
- package/workflow/instance/CreateWorkflowInstanceDialog.js +1 -1
- package/workflow/instance/CreateWorkflowInstanceDialog.js.map +1 -1
- package/workflow/instance/WorkflowInstanceDetailPanel.d.ts.map +1 -1
- package/workflow/instance/WorkflowInstanceDetailPanel.js +2 -13
- package/workflow/instance/WorkflowInstanceDetailPanel.js.map +1 -1
- package/workflow/instance/WorkflowInstanceList.d.ts.map +1 -1
- package/workflow/instance/WorkflowInstanceList.js +2 -13
- package/workflow/instance/WorkflowInstanceList.js.map +1 -1
- package/library/VisibilityToggle.d.ts +0 -53
- package/library/VisibilityToggle.d.ts.map +0 -1
- package/library/VisibilityToggle.js +0 -100
- package/library/VisibilityToggle.js.map +0 -1
- package/src/library/VisibilityToggle.tsx +0 -280
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
3
|
+
import type { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
4
|
+
import {
|
|
5
|
+
VisibilitySelector,
|
|
6
|
+
type VisibilitySelectorMode,
|
|
7
|
+
} from "./VisibilitySelector";
|
|
8
|
+
import { INSTANCE_VISIBILITY_LEVELS } from "./visibilityLevels";
|
|
6
9
|
|
|
7
10
|
/** Props for {@link InstanceVisibilitySelector}. */
|
|
8
11
|
export interface InstanceVisibilitySelectorProps {
|
|
9
12
|
/** Current visibility of the instance. */
|
|
10
13
|
readonly visibility: ApiResourceVisibility;
|
|
11
14
|
/**
|
|
12
|
-
* Called when the user
|
|
13
|
-
*
|
|
15
|
+
* Called when the user selects (and, for escalations in `"manage"` mode,
|
|
16
|
+
* confirms) a visibility change. Escalating to Organization shows a light
|
|
17
|
+
* inline confirm; escalating to Public opens a blocking confirm dialog.
|
|
14
18
|
*/
|
|
15
19
|
readonly onVisibilityChange: (v: ApiResourceVisibility) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Presentation + confirmation mode, forwarded to {@link VisibilitySelector}.
|
|
22
|
+
* Use `"create"` when picking an initial value inside a create dialog
|
|
23
|
+
* (inline list, applies immediately); defaults to `"manage"` (popover with
|
|
24
|
+
* escalation confirmation) for live instances.
|
|
25
|
+
*/
|
|
26
|
+
readonly mode?: VisibilitySelectorMode;
|
|
16
27
|
/** Shows a spinner/disabled state while the RPC is in flight. */
|
|
17
28
|
readonly isPending?: boolean;
|
|
18
29
|
/** Disables all interaction (e.g., when the user lacks can_edit). */
|
|
@@ -21,303 +32,53 @@ export interface InstanceVisibilitySelectorProps {
|
|
|
21
32
|
readonly className?: string;
|
|
22
33
|
}
|
|
23
34
|
|
|
24
|
-
const OPTIONS: readonly {
|
|
25
|
-
readonly value: ApiResourceVisibility;
|
|
26
|
-
readonly label: string;
|
|
27
|
-
readonly description: string;
|
|
28
|
-
}[] = [
|
|
29
|
-
{
|
|
30
|
-
value: ApiResourceVisibility.visibility_private,
|
|
31
|
-
label: "Private",
|
|
32
|
-
description: "Only you can access",
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
value: ApiResourceVisibility.visibility_org,
|
|
36
|
-
label: "Organization",
|
|
37
|
-
description: "All org members can view executions",
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
value: ApiResourceVisibility.visibility_public,
|
|
41
|
-
label: "Public",
|
|
42
|
-
description: "All authenticated users can view",
|
|
43
|
-
},
|
|
44
|
-
];
|
|
45
|
-
|
|
46
35
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
36
|
+
* Visibility selector for instances (AgentInstance, WorkflowInstance):
|
|
37
|
+
* {@link VisibilitySelector} preconfigured with the instance level set
|
|
38
|
+
* (Private / Organization / Public — platform is excluded by design to
|
|
39
|
+
* preserve tenant isolation).
|
|
49
40
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* shows an inline confirmation prompt since expanding access is
|
|
54
|
-
* consequential.
|
|
41
|
+
* For workflow instances, ORG visibility has cascading effects: all org
|
|
42
|
+
* members automatically see all executions via FGA inheritance (zero
|
|
43
|
+
* per-execution tuples needed).
|
|
55
44
|
*
|
|
56
|
-
* For
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* flow through `--stgm-*` design tokens.
|
|
45
|
+
* For live instances, prefer {@link ResourceVisibilityControl} (which adds
|
|
46
|
+
* the read-only {@link VisibilityBadge} fallback for non-editors). This thin
|
|
47
|
+
* preset is used directly when picking an initial value inside a create
|
|
48
|
+
* dialog, where `mode="create"` renders an inline list instead of a popover
|
|
49
|
+
* (a portaled popover would stack beneath a native `<dialog>`'s top layer).
|
|
62
50
|
*
|
|
63
51
|
* @example
|
|
64
52
|
* ```tsx
|
|
65
|
-
* const
|
|
66
|
-
*
|
|
67
|
-
* instance.metadata.id,
|
|
53
|
+
* const [visibility, setVisibility] = useState(
|
|
54
|
+
* ApiResourceVisibility.visibility_private,
|
|
68
55
|
* );
|
|
69
56
|
*
|
|
70
57
|
* <InstanceVisibilitySelector
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
58
|
+
* mode="create"
|
|
59
|
+
* visibility={visibility}
|
|
60
|
+
* onVisibilityChange={setVisibility}
|
|
74
61
|
* />
|
|
75
62
|
* ```
|
|
76
63
|
*/
|
|
77
64
|
export function InstanceVisibilitySelector({
|
|
78
65
|
visibility,
|
|
79
66
|
onVisibilityChange,
|
|
67
|
+
mode,
|
|
80
68
|
isPending = false,
|
|
81
69
|
disabled = false,
|
|
82
70
|
className,
|
|
83
71
|
}: InstanceVisibilitySelectorProps) {
|
|
84
|
-
const [confirming, setConfirming] = useState<ApiResourceVisibility | null>(
|
|
85
|
-
null,
|
|
86
|
-
);
|
|
87
|
-
const optionRefs = useRef<(HTMLButtonElement | null)[]>([]);
|
|
88
|
-
const effectivelyDisabled = disabled || isPending;
|
|
89
|
-
|
|
90
|
-
const isEscalation = useCallback(
|
|
91
|
-
(target: ApiResourceVisibility) => {
|
|
92
|
-
const order = [
|
|
93
|
-
ApiResourceVisibility.visibility_private,
|
|
94
|
-
ApiResourceVisibility.visibility_org,
|
|
95
|
-
ApiResourceVisibility.visibility_public,
|
|
96
|
-
];
|
|
97
|
-
return order.indexOf(target) > order.indexOf(visibility);
|
|
98
|
-
},
|
|
99
|
-
[visibility],
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
const handleSelect = useCallback(
|
|
103
|
-
(value: ApiResourceVisibility) => {
|
|
104
|
-
if (value === visibility) return;
|
|
105
|
-
|
|
106
|
-
if (isEscalation(value)) {
|
|
107
|
-
setConfirming(value);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
onVisibilityChange(value);
|
|
112
|
-
},
|
|
113
|
-
[visibility, onVisibilityChange, isEscalation],
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
const confirmChange = useCallback(() => {
|
|
117
|
-
if (confirming === null) return;
|
|
118
|
-
setConfirming(null);
|
|
119
|
-
onVisibilityChange(confirming);
|
|
120
|
-
}, [confirming, onVisibilityChange]);
|
|
121
|
-
|
|
122
|
-
const cancelConfirm = useCallback(() => {
|
|
123
|
-
setConfirming(null);
|
|
124
|
-
}, []);
|
|
125
|
-
|
|
126
|
-
const handleKeyDown = useCallback(
|
|
127
|
-
(e: React.KeyboardEvent<HTMLButtonElement>, index: number) => {
|
|
128
|
-
let nextIndex: number | null = null;
|
|
129
|
-
|
|
130
|
-
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
|
|
131
|
-
e.preventDefault();
|
|
132
|
-
nextIndex = (index + 1) % OPTIONS.length;
|
|
133
|
-
} else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
|
|
134
|
-
e.preventDefault();
|
|
135
|
-
nextIndex = (index - 1 + OPTIONS.length) % OPTIONS.length;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (nextIndex !== null) {
|
|
139
|
-
optionRefs.current[nextIndex]?.focus();
|
|
140
|
-
handleSelect(OPTIONS[nextIndex].value);
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
[handleSelect],
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
const confirmingOption = confirming
|
|
147
|
-
? OPTIONS.find((o) => o.value === confirming)
|
|
148
|
-
: null;
|
|
149
|
-
|
|
150
|
-
return (
|
|
151
|
-
<div className={cn("inline-flex flex-col gap-1.5", className)}>
|
|
152
|
-
<div
|
|
153
|
-
role="radiogroup"
|
|
154
|
-
aria-label="Instance visibility"
|
|
155
|
-
aria-disabled={effectivelyDisabled || undefined}
|
|
156
|
-
className={cn(
|
|
157
|
-
"inline-flex rounded-md bg-muted p-0.5",
|
|
158
|
-
effectivelyDisabled && "pointer-events-none opacity-50",
|
|
159
|
-
)}
|
|
160
|
-
>
|
|
161
|
-
{OPTIONS.map((option, index) => {
|
|
162
|
-
const isSelected = visibility === option.value;
|
|
163
|
-
|
|
164
|
-
return (
|
|
165
|
-
<button
|
|
166
|
-
key={option.value}
|
|
167
|
-
ref={(el) => {
|
|
168
|
-
optionRefs.current[index] = el;
|
|
169
|
-
}}
|
|
170
|
-
type="button"
|
|
171
|
-
role="radio"
|
|
172
|
-
aria-checked={isSelected}
|
|
173
|
-
aria-label={`${option.label}: ${option.description}`}
|
|
174
|
-
tabIndex={isSelected ? 0 : -1}
|
|
175
|
-
disabled={effectivelyDisabled}
|
|
176
|
-
onClick={() => handleSelect(option.value)}
|
|
177
|
-
onKeyDown={(e) => handleKeyDown(e, index)}
|
|
178
|
-
className={cn(
|
|
179
|
-
"inline-flex cursor-pointer items-center gap-1 rounded-sm px-2.5 py-1 text-xs font-medium transition-colors",
|
|
180
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
181
|
-
isSelected
|
|
182
|
-
? getSelectedStyle(option.value)
|
|
183
|
-
: "text-muted-foreground hover:text-foreground",
|
|
184
|
-
)}
|
|
185
|
-
>
|
|
186
|
-
{isPending && isSelected ? (
|
|
187
|
-
<span
|
|
188
|
-
className="inline-block size-3 animate-spin rounded-full border-2 border-current border-t-transparent"
|
|
189
|
-
aria-hidden="true"
|
|
190
|
-
/>
|
|
191
|
-
) : (
|
|
192
|
-
getIcon(option.value)
|
|
193
|
-
)}
|
|
194
|
-
{option.label}
|
|
195
|
-
</button>
|
|
196
|
-
);
|
|
197
|
-
})}
|
|
198
|
-
</div>
|
|
199
|
-
|
|
200
|
-
{/* Description of current state */}
|
|
201
|
-
{!confirming && (
|
|
202
|
-
<p className="text-[0.65rem] text-muted-foreground">
|
|
203
|
-
{OPTIONS.find((o) => o.value === visibility)?.description}
|
|
204
|
-
</p>
|
|
205
|
-
)}
|
|
206
|
-
|
|
207
|
-
{/* Confirmation prompt for escalation */}
|
|
208
|
-
{confirming !== null && confirmingOption && (
|
|
209
|
-
<div
|
|
210
|
-
className={cn(
|
|
211
|
-
"flex items-center gap-2 rounded-md border px-3 py-1.5 text-xs",
|
|
212
|
-
confirming === ApiResourceVisibility.visibility_public
|
|
213
|
-
? "border-amber-200 bg-amber-50 dark:border-amber-800/50 dark:bg-amber-950/30"
|
|
214
|
-
: "border-blue-200 bg-blue-50 dark:border-blue-800/50 dark:bg-blue-950/30",
|
|
215
|
-
)}
|
|
216
|
-
role="alert"
|
|
217
|
-
>
|
|
218
|
-
<span
|
|
219
|
-
className={cn(
|
|
220
|
-
confirming === ApiResourceVisibility.visibility_public
|
|
221
|
-
? "text-amber-800 dark:text-amber-200"
|
|
222
|
-
: "text-blue-800 dark:text-blue-200",
|
|
223
|
-
)}
|
|
224
|
-
>
|
|
225
|
-
{confirming === ApiResourceVisibility.visibility_public
|
|
226
|
-
? "Make visible to all authenticated users?"
|
|
227
|
-
: "Make visible to all org members?"}
|
|
228
|
-
</span>
|
|
229
|
-
<button
|
|
230
|
-
type="button"
|
|
231
|
-
onClick={confirmChange}
|
|
232
|
-
className={cn(
|
|
233
|
-
"rounded px-2 py-0.5 text-xs font-medium text-white",
|
|
234
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
235
|
-
confirming === ApiResourceVisibility.visibility_public
|
|
236
|
-
? "bg-amber-600 hover:bg-amber-700 dark:bg-amber-600 dark:hover:bg-amber-500"
|
|
237
|
-
: "bg-blue-600 hover:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-500",
|
|
238
|
-
)}
|
|
239
|
-
>
|
|
240
|
-
Confirm
|
|
241
|
-
</button>
|
|
242
|
-
<button
|
|
243
|
-
type="button"
|
|
244
|
-
onClick={cancelConfirm}
|
|
245
|
-
className={cn(
|
|
246
|
-
"rounded px-2 py-0.5 text-xs font-medium",
|
|
247
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
248
|
-
confirming === ApiResourceVisibility.visibility_public
|
|
249
|
-
? "text-amber-700 hover:text-amber-900 dark:text-amber-300 dark:hover:text-amber-100"
|
|
250
|
-
: "text-blue-700 hover:text-blue-900 dark:text-blue-300 dark:hover:text-blue-100",
|
|
251
|
-
)}
|
|
252
|
-
>
|
|
253
|
-
Cancel
|
|
254
|
-
</button>
|
|
255
|
-
</div>
|
|
256
|
-
)}
|
|
257
|
-
</div>
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// ---------------------------------------------------------------------------
|
|
262
|
-
// Helpers
|
|
263
|
-
// ---------------------------------------------------------------------------
|
|
264
|
-
|
|
265
|
-
function getSelectedStyle(value: ApiResourceVisibility): string {
|
|
266
|
-
switch (value) {
|
|
267
|
-
case ApiResourceVisibility.visibility_private:
|
|
268
|
-
return "bg-amber-50 text-amber-800 shadow-sm dark:bg-amber-900/30 dark:text-amber-300";
|
|
269
|
-
case ApiResourceVisibility.visibility_org:
|
|
270
|
-
return "bg-blue-100 text-blue-800 shadow-sm dark:bg-blue-900/40 dark:text-blue-300";
|
|
271
|
-
case ApiResourceVisibility.visibility_public:
|
|
272
|
-
return "bg-emerald-100 text-emerald-800 shadow-sm dark:bg-emerald-900/40 dark:text-emerald-300";
|
|
273
|
-
default:
|
|
274
|
-
return "bg-background text-foreground shadow-sm";
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function getIcon(value: ApiResourceVisibility) {
|
|
279
|
-
switch (value) {
|
|
280
|
-
case ApiResourceVisibility.visibility_private:
|
|
281
|
-
return <LockIcon className="size-3" />;
|
|
282
|
-
case ApiResourceVisibility.visibility_org:
|
|
283
|
-
return <UsersIcon className="size-3" />;
|
|
284
|
-
case ApiResourceVisibility.visibility_public:
|
|
285
|
-
return <GlobeIcon className="size-3" />;
|
|
286
|
-
default:
|
|
287
|
-
return null;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// ---------------------------------------------------------------------------
|
|
292
|
-
// Icons
|
|
293
|
-
// ---------------------------------------------------------------------------
|
|
294
|
-
|
|
295
|
-
function LockIcon({ className }: { readonly className?: string }) {
|
|
296
|
-
return (
|
|
297
|
-
<svg className={className} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
298
|
-
<rect x="3.5" y="7" width="9" height="7" rx="1.5" />
|
|
299
|
-
<path d="M5.5 7V5a2.5 2.5 0 0 1 5 0v2" />
|
|
300
|
-
</svg>
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function UsersIcon({ className }: { readonly className?: string }) {
|
|
305
|
-
return (
|
|
306
|
-
<svg className={className} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
307
|
-
<circle cx="6" cy="5" r="2.5" />
|
|
308
|
-
<path d="M2 13c0-2.21 1.79-4 4-4s4 1.79 4 4" />
|
|
309
|
-
<circle cx="11.5" cy="5.5" r="2" />
|
|
310
|
-
<path d="M14 13c0-1.66-1.12-3-2.5-3-.5 0-1 .14-1.4.4" />
|
|
311
|
-
</svg>
|
|
312
|
-
);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function GlobeIcon({ className }: { readonly className?: string }) {
|
|
316
72
|
return (
|
|
317
|
-
<
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
73
|
+
<VisibilitySelector
|
|
74
|
+
visibility={visibility}
|
|
75
|
+
options={INSTANCE_VISIBILITY_LEVELS}
|
|
76
|
+
onVisibilityChange={onVisibilityChange}
|
|
77
|
+
mode={mode}
|
|
78
|
+
isPending={isPending}
|
|
79
|
+
disabled={disabled}
|
|
80
|
+
ariaLabel="Instance visibility"
|
|
81
|
+
className={className}
|
|
82
|
+
/>
|
|
322
83
|
);
|
|
323
84
|
}
|
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback } from "react";
|
|
4
4
|
import type { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
5
|
+
import { useDeploymentMode } from "../deployment-mode";
|
|
5
6
|
import { PermissionGate } from "../iam-policy/PermissionGate";
|
|
6
|
-
import {
|
|
7
|
+
import { useSsoProvider } from "../identity-provider/useSsoProvider";
|
|
8
|
+
import { VisibilityBadge, VisibilitySelector } from "./VisibilitySelector";
|
|
9
|
+
import {
|
|
10
|
+
blueprintVisibilityLevels,
|
|
11
|
+
INSTANCE_VISIBILITY_LEVELS,
|
|
12
|
+
} from "./visibilityLevels";
|
|
7
13
|
import {
|
|
8
14
|
useUpdateVisibility,
|
|
9
15
|
type VisibilityResourceKind,
|
|
@@ -24,6 +30,11 @@ const FGA_KIND: Record<VisibilityResourceKind, string> = {
|
|
|
24
30
|
workflowInstance: "workflow_instance",
|
|
25
31
|
};
|
|
26
32
|
|
|
33
|
+
const INSTANCE_KINDS: ReadonlySet<VisibilityResourceKind> = new Set([
|
|
34
|
+
"agentInstance",
|
|
35
|
+
"workflowInstance",
|
|
36
|
+
]);
|
|
37
|
+
|
|
27
38
|
/** Props for {@link ResourceVisibilityControl}. */
|
|
28
39
|
export interface ResourceVisibilityControlProps {
|
|
29
40
|
/** Resource kind, selecting both the updateVisibility RPC and the FGA type. */
|
|
@@ -32,6 +43,13 @@ export interface ResourceVisibilityControlProps {
|
|
|
32
43
|
readonly resourceId: string;
|
|
33
44
|
/** Current visibility of the resource. */
|
|
34
45
|
readonly visibility: ApiResourceVisibility;
|
|
46
|
+
/**
|
|
47
|
+
* Slug of the organization that OWNS the resource (`metadata.org`).
|
|
48
|
+
* Used to look up whether the org operates an IdentityProvider, which
|
|
49
|
+
* gates the Platform option for blueprints. When omitted, Platform is
|
|
50
|
+
* simply not offered (the other levels need no org context).
|
|
51
|
+
*/
|
|
52
|
+
readonly org?: string;
|
|
35
53
|
/**
|
|
36
54
|
* Called after a successful visibility change so the host can refresh the
|
|
37
55
|
* resource (e.g. `refetch`) and reflect the new state.
|
|
@@ -46,24 +64,50 @@ export interface ResourceVisibilityControlProps {
|
|
|
46
64
|
*
|
|
47
65
|
* Behavior:
|
|
48
66
|
* - Always renders a legible state: a read-only {@link VisibilityBadge}
|
|
49
|
-
* (
|
|
67
|
+
* (all four levels) is shown to viewers without `can_edit` and while the
|
|
50
68
|
* permission check is in flight — never a silent blank.
|
|
51
|
-
* - Upgrades to the interactive {@link
|
|
69
|
+
* - Upgrades to the interactive {@link VisibilitySelector} for users with
|
|
52
70
|
* `can_edit`, persisting changes via {@link useUpdateVisibility} and invoking
|
|
53
71
|
* {@link ResourceVisibilityControlProps.onChanged} on success.
|
|
54
72
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
73
|
+
* Offered levels are kind- and context-aware (`visibilityLevels.ts`):
|
|
74
|
+
* - Blueprints (agent/workflow/skill/mcp_server): Private / Organization /
|
|
75
|
+
* Public, plus Platform when the deployment is `cloud` AND the owning org
|
|
76
|
+
* operates an IdentityProvider (checked via {@link useSsoProvider}, the
|
|
77
|
+
* only permission-free IdP lookup — blueprint owners editing visibility
|
|
78
|
+
* are not necessarily org admins). In `local` mode (OSS Go backend) the
|
|
79
|
+
* set collapses to Private / Public.
|
|
80
|
+
* - Instances: Private / Organization / Public — platform is excluded by
|
|
81
|
+
* design to preserve tenant isolation.
|
|
82
|
+
*
|
|
83
|
+
* The backend remains the enforcer (`ValidateVisibilityStep` rejects
|
|
84
|
+
* platform without an IdP); the gate here only prevents offering an option
|
|
85
|
+
* that is guaranteed to fail.
|
|
58
86
|
*/
|
|
59
87
|
export function ResourceVisibilityControl({
|
|
60
88
|
kind,
|
|
61
89
|
resourceId,
|
|
62
90
|
visibility,
|
|
91
|
+
org,
|
|
63
92
|
onChanged,
|
|
64
93
|
className,
|
|
65
94
|
}: ResourceVisibilityControlProps) {
|
|
66
95
|
const { updateVisibility, isPending } = useUpdateVisibility(kind, resourceId);
|
|
96
|
+
const deploymentMode = useDeploymentMode();
|
|
97
|
+
|
|
98
|
+
const isInstance = INSTANCE_KINDS.has(kind);
|
|
99
|
+
// The IdP lookup only matters for blueprints in cloud mode; passing null
|
|
100
|
+
// makes the hook a stable no-op everywhere else.
|
|
101
|
+
const idpLookupOrg =
|
|
102
|
+
!isInstance && deploymentMode === "cloud" ? (org ?? null) : null;
|
|
103
|
+
const { ssoProvider } = useSsoProvider(idpLookupOrg);
|
|
104
|
+
|
|
105
|
+
const options = isInstance
|
|
106
|
+
? INSTANCE_VISIBILITY_LEVELS
|
|
107
|
+
: blueprintVisibilityLevels({
|
|
108
|
+
deploymentMode,
|
|
109
|
+
hasIdentityProvider: ssoProvider !== null,
|
|
110
|
+
});
|
|
67
111
|
|
|
68
112
|
const handleChange = useCallback(
|
|
69
113
|
async (next: ApiResourceVisibility) => {
|
|
@@ -72,7 +116,7 @@ export function ResourceVisibilityControl({
|
|
|
72
116
|
onChanged?.();
|
|
73
117
|
} catch {
|
|
74
118
|
// The RPC error is captured in useUpdateVisibility's `error` state;
|
|
75
|
-
// swallow here so the
|
|
119
|
+
// swallow here so the selector's promise settles without an unhandled
|
|
76
120
|
// rejection. Surfacing a toast is the host app's concern.
|
|
77
121
|
}
|
|
78
122
|
},
|
|
@@ -88,10 +132,12 @@ export function ResourceVisibilityControl({
|
|
|
88
132
|
fallback={badge}
|
|
89
133
|
loading={badge}
|
|
90
134
|
>
|
|
91
|
-
<
|
|
135
|
+
<VisibilitySelector
|
|
92
136
|
visibility={visibility}
|
|
137
|
+
options={options}
|
|
93
138
|
onVisibilityChange={handleChange}
|
|
94
139
|
isPending={isPending}
|
|
140
|
+
ariaLabel={isInstance ? "Instance visibility" : "Resource visibility"}
|
|
95
141
|
className={className}
|
|
96
142
|
/>
|
|
97
143
|
</PermissionGate>
|
|
@@ -48,7 +48,7 @@ const OPTIONS: readonly {
|
|
|
48
48
|
*
|
|
49
49
|
* Renders as a WAI-ARIA Radio Group with roving tabindex and
|
|
50
50
|
* arrow-key navigation. Follows the same visual pattern as
|
|
51
|
-
* {@link
|
|
51
|
+
* {@link VisibilitySelector}.
|
|
52
52
|
*
|
|
53
53
|
* The component is controlled — the consumer owns the `value` state
|
|
54
54
|
* and handles persistence (e.g., localStorage).
|