@burdenoff/fluidgrids-fe-sdk 2026.1111.16 → 2026.1111.18

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 (119) hide show
  1. package/README.md +41 -0
  2. package/dist/AiStudioPage-BHVEU4Wp.js +774 -0
  3. package/dist/ApprovalInboxPage-dbBDSJ-f.js +558 -0
  4. package/dist/DataSinkDashboardPage-1h2QsMzH.js +270 -0
  5. package/dist/DataTablesPage-BZy2z0LB.js +1690 -0
  6. package/dist/EntityTagPicker-BqRNsLCQ.js +135 -0
  7. package/dist/{ExecutionDetailPage-D-0_POAf.js → ExecutionDetailPage-BdcZmLT4.js} +799 -4420
  8. package/dist/ExecutionDetailPage-DvQ667OE.d.ts +10 -0
  9. package/dist/ExecutionsListPage-BCUJoAIE.js +2742 -0
  10. package/dist/{FailureTriagePanel-BHiduL7A.js → FailureTriagePanel-B9hDQymi.js} +4 -97
  11. package/dist/{FluidGridsProvider-DmvFADFa.js → FluidGridsProvider-dvZSNtSW.js} +10 -350
  12. package/dist/HistoryPage-BLoRpJ1R.d.ts +6 -0
  13. package/dist/{HistoryPage-BbUgxx1I.js → HistoryPage-U0tqupBN.js} +6 -4
  14. package/dist/HumanRequestCard-D-ooVPDn.js +439 -0
  15. package/dist/{InsightsPage-s6f5bU-8.js → InsightsPage-CALo16eB.js} +17 -328
  16. package/dist/ManagedAiPolicyAuthority-BxwJ3ptd.js +40 -0
  17. package/dist/{ManagedAiPolicyAuthority-CGux_DaV.d.ts → ManagedAiPolicyAuthority-DM3t3wNr.d.ts} +1 -5
  18. package/dist/ManagedAiPolicyAuthorityContext-CNkxIOZ1.js +10 -0
  19. package/dist/{MissingNodesInstallModal-B-9gHy2K.js → MissingNodesInstallModal-CMybsI8h.js} +3 -2
  20. package/dist/{NodesPage-ClvhUfbh.js → NodesPage-Bf5PhfBK.js} +7 -3
  21. package/dist/NodesPage-DEbnz9iU.d.ts +6 -0
  22. package/dist/{PublishCustomNodeAction-D2F7oN0S.js → PublishCustomNodeAction-5XXnSA3H.js} +3 -45
  23. package/dist/QuotaExhaustedDialog-7IFSaLyi.js +54 -0
  24. package/dist/RunnerStatusPill-Ctg4TJcP.js +187 -0
  25. package/dist/RunnersPage-CLCOCclc.js +467 -0
  26. package/dist/{ManagedAiPolicyAuthority-DsSaz-vg.js → SettingsPage-CJLZvv4J.js} +743 -773
  27. package/dist/SettingsPage-DMHQFj5-.d.ts +9 -0
  28. package/dist/TagFilterSelect-DCBOH6Ou.js +41 -0
  29. package/dist/TaskQueuesPage-9VgSnLdL.js +951 -0
  30. package/dist/TemplateCard-DsC0KNH1.js +1416 -0
  31. package/dist/{TemplateConfigurationModal-D_Mvgxqa.js → TemplateConfigurationModal-BhGWIzpm.js} +3 -2
  32. package/dist/{TemplateDetailPanel-DfFfUcOe.js → TemplateDetailPanel-D4cUVr-m.js} +3 -3
  33. package/dist/TemplatePreview-FayF6jxh.js +231 -0
  34. package/dist/TemplatesPage-BG3H-6v6.d.ts +10 -0
  35. package/dist/{TemplatesPage-Cku13bav.js → TemplatesPage-CsDNT54N.js} +11 -228
  36. package/dist/VariablesPage-67WR_lVE.js +949 -0
  37. package/dist/{VersionDiffDialog-Dq2xlPFI.js → VersionDiffDialog-DSN0mU0f.js} +2 -1
  38. package/dist/WorkflowBuilder-CPnXEL5J.js +555 -0
  39. package/dist/{WorkflowSearchRedirect-CaoOOpEl.js → WorkflowBuilder-YXRysvZc.js} +19 -585
  40. package/dist/{WorkflowProposalCard-8Vr56kQa.js → WorkflowProposalCard-qrEeNyYG.js} +4 -405
  41. package/dist/WorkflowSearchRedirect-DwDNZ_q5.js +36 -0
  42. package/dist/WorkflowShareDialog-mbIwRQUY.js +168 -0
  43. package/dist/WorkflowsList-CcNDQge5.d.ts +6 -0
  44. package/dist/{WorkflowsList-B2j1Pehb.js → WorkflowsList-DiiGyZkT.js} +12 -6
  45. package/dist/agent-form-Bfvm32j1.js +83 -0
  46. package/dist/{agentTools-DJ5o2ekg.js → agentTools-8s_25Q_F.js} +3 -2
  47. package/dist/{ai-usage-D5p7ZeRR.js → ai-usage-glCwHyQz.js} +1 -1
  48. package/dist/api-BrCHB5oA.js +224 -0
  49. package/dist/api-C8e0iV-Q.js +55 -0
  50. package/dist/button-DG7y_FVJ.js +48 -0
  51. package/dist/{canvasNoteTools-D3zUdEeg.js → canvasNoteTools-B6pYARgU.js} +2 -2
  52. package/dist/client-C9XSzSXr.js +1147 -0
  53. package/dist/code-editor-field-9-GdVE_8.js +111 -0
  54. package/dist/{codec-client-DstqyzTN.js → codec-client-CJ95Rmqo.js} +1 -1
  55. package/dist/constants-B2lhWNoN.js +202 -0
  56. package/dist/constants-XdvUxeWK.d.ts +162 -0
  57. package/dist/dialog-Cjb4lB4D.js +105 -0
  58. package/dist/executions.d.ts +1 -1
  59. package/dist/executions.js +2 -1
  60. package/dist/feature-flags-DM5myWaD.js +296 -0
  61. package/dist/flags.d.ts +3 -0
  62. package/dist/flags.js +4 -0
  63. package/dist/{flow-builder-DEzT8TQu.js → flow-builder-CZDKRc6f.js} +662 -10716
  64. package/dist/{node-endpoint-configs-DHFGA5MQ.js → graphql-operation-types-kX-MMjgW.js} +1 -1194
  65. package/dist/{graphql-types-XcDLtFwU.js → graphql-types-CDKVWH2R.js} +2 -202
  66. package/dist/history.d.ts +1 -1
  67. package/dist/history.js +1 -1
  68. package/dist/{index-DRf25-D3.d.ts → index-6JR2vD9J.d.ts} +33 -215
  69. package/dist/index-72XFgvbn.d.ts +132 -0
  70. package/dist/index-BcdtKBM-.d.ts +56 -0
  71. package/dist/{index-B3BWqafN.d.ts → index-DjiFgEQl.d.ts} +5 -131
  72. package/dist/index.d.ts +13 -9
  73. package/dist/index.js +52 -5300
  74. package/dist/{node-detail-view-LNqeIqN-.js → node-detail-view-CV4ZRwqh.js} +13 -8
  75. package/dist/node-editor-tab-body-DU-dh35X.js +174 -0
  76. package/dist/node-endpoint-configs-B5bjNMDW.js +37 -0
  77. package/dist/node-registry-refresh-Dy0uRg9f.js +84 -0
  78. package/dist/nodes.d.ts +1 -1
  79. package/dist/nodes.js +1 -1
  80. package/dist/observability.d.ts +1 -1
  81. package/dist/observability.js +2 -1
  82. package/dist/popover-BJHvrFGR.js +472 -0
  83. package/dist/{quick-config-panel-D3ogsaFL.js → quick-config-panel-ws3pLvbt.js} +3 -2
  84. package/dist/rolldown-runtime-hDbaFkMy.js +18 -0
  85. package/dist/run-display-BycKYSU0.js +46 -0
  86. package/dist/run-list-links-ByYC0OlV.js +98 -0
  87. package/dist/schedule-panel-C0mfGQSD.js +7542 -0
  88. package/dist/settings.d.ts +2 -1
  89. package/dist/settings.js +2 -1
  90. package/dist/shared-CMfn30xT.js +196 -0
  91. package/dist/sidebar-node-editor-CR1PiZO-.js +177 -0
  92. package/dist/templates.d.ts +1 -1
  93. package/dist/templates.js +2 -1
  94. package/dist/theme-provider-B3mIuz2d.js +11 -0
  95. package/dist/{useAgentDefinitions-NFtLytDR.js → useAgentDefinitions-B0dam3_o.js} +3 -397
  96. package/dist/useConnections-Cs7LCWEM.js +401 -0
  97. package/dist/useDurableParity-CzUQMFJ3.js +415 -0
  98. package/dist/useEntityTags-DhnsZcTl.js +236 -0
  99. package/dist/{useExecutions-DAzeWHS6.js → useExecutions-9aLuOt1c.js} +4 -3
  100. package/dist/{useObservability-COCZ0hUS.js → useObservability-CfEw3ZFD.js} +2 -2
  101. package/dist/{useRunHistory-CsYUbouO.js → useRunHistory-B1xJDwl1.js} +4 -3
  102. package/dist/{WorkflowShareDialog-6RRDe-_v.js → useWorkflowCodeExport-Bu2QYJzA.js} +7 -210
  103. package/dist/{useWorkflows-w4Dx2fF2.js → useWorkflows-BlVVu8NM.js} +3 -2
  104. package/dist/utils-uaL5UVxb.js +65 -0
  105. package/dist/workflow-builder.d.ts +1 -1
  106. package/dist/workflow-builder.js +4 -2
  107. package/dist/workflow-host.d.ts +6 -0
  108. package/dist/workflow-host.js +9 -0
  109. package/dist/workflow-pages.d.ts +55 -0
  110. package/dist/workflow-pages.js +113 -0
  111. package/dist/workflows-list.d.ts +1 -1
  112. package/dist/workflows-list.js +1 -1
  113. package/package.json +13 -1
  114. package/dist/ExecutionDetailPage-MvsnOnE1.d.ts +0 -4
  115. package/dist/HistoryPage-BqUizaFv.d.ts +0 -3
  116. package/dist/NodesPage-C97-9K2Y.d.ts +0 -3
  117. package/dist/TemplatesPage-D0qnXzHh.d.ts +0 -4
  118. package/dist/WorkflowsList-0d5GSKdh.d.ts +0 -3
  119. package/dist/agent-form-BvMWhQ42.js +0 -496
@@ -0,0 +1,774 @@
1
+ "use client";
2
+ import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
+ import { t as ThemeProvider } from "./theme-provider-B3mIuz2d.js";
4
+ import { a as useAiWorkflowTemplates, i as enrichDraftNodes, n as AI_DRAFT_MAX_PROMPT_LENGTH, r as draftToCanvas, t as FlowBuilderPage } from "./flow-builder-CZDKRc6f.js";
5
+ import { Et as classifyCreateRefusal, I as buildOperationSeed, Tt as builderPathOf, u as ensureNodeTypesLoaded } from "./FluidGridsProvider-dvZSNtSW.js";
6
+ import { _ as throwIfResultError, n as getFluidGridsClient } from "./client-C9XSzSXr.js";
7
+ import { a as notifyFluidGridsAiCreditsUpdated, t as AiCreditBalanceSlot } from "./aiCreditsBridge-BuE92tKo.js";
8
+ import { Pr as WorkflowGenerationDocument, y as CancelWorkflowGenerationDocument, yt as GenerateWorkflowFromPromptDocument } from "./graphql-operation-types-kX-MMjgW.js";
9
+ import { n as useCreateWorkflow } from "./useWorkflows-BlVVu8NM.js";
10
+ import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
11
+ import { f as BILLING_PLANS_PATH } from "./fluidgridsEvents-CNdcrUNN.js";
12
+ import { T as CreateWorkflowRefusalDialog } from "./useWorkflowCodeExport-Bu2QYJzA.js";
13
+ import { s as useFluidGridsPermission, t as FLUIDGRIDS_PERMISSIONS } from "./constants-B2lhWNoN.js";
14
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
15
+ import { useNavigate } from "react-router";
16
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
17
+ import { toast } from "sonner";
18
+ import { Badge, Button, Card, CardContent, CardHeader, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea } from "@burdenoff/fe-libs/ui";
19
+ import { ErrorBoundary } from "@burdenoff/fe-libs/shared/components";
20
+ import { v4 } from "uuid";
21
+ import "@xyflow/react/dist/style.css";
22
+ import { CheckCircle2, Lightbulb, Loader2, MessageCircleQuestion, Pencil, RefreshCw, Sparkles, Square, TriangleAlert } from "lucide-react";
23
+
24
+ // src/hooks/useWorkflowGeneration.ts
25
+ /**
26
+ * useWorkflowGeneration — drives one natural-language workflow generation for
27
+ * AI Studio (BOFF-4089): start it with `generateWorkflowFromPrompt`, poll
28
+ * `workflowGeneration(id)` while it is PROCESSING, and cancel it.
29
+ *
30
+ * A generation never saves anything. Its draft is shown for review and saved
31
+ * as a new DRAFT workflow only when the user chooses "Edit in builder".
32
+ */
33
+ /** How often a PROCESSING generation is polled. */
34
+ const GENERATION_POLL_INTERVAL_MS = 3e3;
35
+ /** Stop polling after this long; the server closes a stuck generation after 20 minutes. */
36
+ const GENERATION_MAX_WAIT_MS = 36e4;
37
+ function messageOf(error) {
38
+ return error instanceof Error ? error.message : String(error);
39
+ }
40
+ function useWorkflowGeneration() {
41
+ const [generation, setGeneration] = useState(null);
42
+ const [isWorking, setIsWorking] = useState(false);
43
+ const [error, setError] = useState(null);
44
+ const [timedOut, setTimedOut] = useState(false);
45
+ const runRef = useRef(0);
46
+ const timerRef = useRef(null);
47
+ const idRef = useRef(null);
48
+ const clearTimer = useCallback(() => {
49
+ if (timerRef.current) clearTimeout(timerRef.current);
50
+ timerRef.current = null;
51
+ }, []);
52
+ useEffect(() => () => {
53
+ runRef.current++;
54
+ clearTimer();
55
+ }, [clearTimer]);
56
+ const reset = useCallback(() => {
57
+ runRef.current++;
58
+ clearTimer();
59
+ idRef.current = null;
60
+ setGeneration(null);
61
+ setIsWorking(false);
62
+ setError(null);
63
+ setTimedOut(false);
64
+ }, [clearTimer]);
65
+ const poll = useCallback(async (run, id, startedAt) => {
66
+ if (run !== runRef.current) return;
67
+ try {
68
+ const result = throwIfResultError(await getFluidGridsClient().query({
69
+ query: WorkflowGenerationDocument,
70
+ variables: { id },
71
+ fetchPolicy: "network-only"
72
+ }));
73
+ if (run !== runRef.current) return;
74
+ const next = result.data?.workflowGeneration ?? null;
75
+ if (next) setGeneration(next);
76
+ if (next && String(next.status) === "PROCESSING") {
77
+ if (Date.now() - startedAt < 36e4) {
78
+ timerRef.current = setTimeout(() => void poll(run, id, startedAt), GENERATION_POLL_INTERVAL_MS);
79
+ return;
80
+ }
81
+ setTimedOut(true);
82
+ } else notifyFluidGridsAiCreditsUpdated();
83
+ setIsWorking(false);
84
+ } catch (pollError) {
85
+ if (run !== runRef.current) return;
86
+ setError(messageOf(pollError));
87
+ setIsWorking(false);
88
+ }
89
+ }, []);
90
+ return {
91
+ generation,
92
+ isWorking,
93
+ error,
94
+ timedOut,
95
+ start: useCallback(async (input) => {
96
+ const run = ++runRef.current;
97
+ clearTimer();
98
+ setGeneration(null);
99
+ setError(null);
100
+ setTimedOut(false);
101
+ setIsWorking(true);
102
+ try {
103
+ const result = throwIfResultError(await getFluidGridsClient().mutate({
104
+ mutation: GenerateWorkflowFromPromptDocument,
105
+ variables: { input: {
106
+ prompt: input.prompt,
107
+ ...input.template ? { template: input.template } : {},
108
+ ...input.baseDefinitionJson ? { baseDefinitionJson: input.baseDefinitionJson } : {}
109
+ } }
110
+ }));
111
+ if (run !== runRef.current) return;
112
+ const id = result.data?.generateWorkflowFromPrompt.id;
113
+ if (!id) throw new Error("The generation did not start");
114
+ idRef.current = id;
115
+ await poll(run, id, Date.now());
116
+ } catch (startError) {
117
+ if (run !== runRef.current) return;
118
+ setError(messageOf(startError));
119
+ setIsWorking(false);
120
+ }
121
+ }, [clearTimer, poll]),
122
+ cancel: useCallback(async () => {
123
+ const id = idRef.current;
124
+ const run = ++runRef.current;
125
+ clearTimer();
126
+ if (!id) {
127
+ setIsWorking(false);
128
+ return;
129
+ }
130
+ try {
131
+ const result = throwIfResultError(await getFluidGridsClient().mutate({
132
+ mutation: CancelWorkflowGenerationDocument,
133
+ variables: { id }
134
+ }));
135
+ if (run !== runRef.current) return;
136
+ const cancelled = result.data?.cancelWorkflowGeneration;
137
+ if (cancelled) setGeneration((current) => current ? {
138
+ ...current,
139
+ status: cancelled.status,
140
+ message: cancelled.message
141
+ } : current);
142
+ } catch (cancelError) {
143
+ if (run !== runRef.current) return;
144
+ setError(messageOf(cancelError));
145
+ } finally {
146
+ if (run === runRef.current) setIsWorking(false);
147
+ }
148
+ }, [clearTimer]),
149
+ reset
150
+ };
151
+ }
152
+
153
+ // src/modules/ai-studio/lib/ai-studio.ts
154
+ /** The version every workflow saved from AI Studio starts at. */
155
+ const AI_STUDIO_DRAFT_VERSION = "v1";
156
+ const AI_STUDIO_EXAMPLES = [
157
+ {
158
+ id: "payment-to-crm",
159
+ labelKey: "fluidgrids.aiStudio.examples.paymentToCrm.label",
160
+ label: "Payment to CRM and chat",
161
+ promptKey: "fluidgrids.aiStudio.examples.paymentToCrm.prompt",
162
+ prompt: "When a new Stripe payment succeeds, add the customer to HubSpot and post a summary to Slack #sales."
163
+ },
164
+ {
165
+ id: "daily-digest",
166
+ labelKey: "fluidgrids.aiStudio.examples.dailyDigest.label",
167
+ label: "Daily order digest",
168
+ promptKey: "fluidgrids.aiStudio.examples.dailyDigest.prompt",
169
+ prompt: "Every weekday at 9am, fetch new orders from our API, keep the ones over 500 and post a summary to a webhook. Log the rest."
170
+ },
171
+ {
172
+ id: "webhook-triage",
173
+ labelKey: "fluidgrids.aiStudio.examples.webhookTriage.label",
174
+ label: "Triage incoming requests",
175
+ promptKey: "fluidgrids.aiStudio.examples.webhookTriage.prompt",
176
+ prompt: "When a webhook receives a support request, check the priority field. Send urgent requests to our escalation API and log everything else."
177
+ }
178
+ ];
179
+ const AI_STUDIO_REFINEMENTS = [
180
+ {
181
+ id: "error-handling",
182
+ labelKey: "fluidgrids.aiStudio.refinements.errorHandling",
183
+ label: "Add error handling",
184
+ instruction: "Add error handling: when a step that calls an external service fails, route the failure to a separate branch that logs the error instead of stopping silently."
185
+ },
186
+ {
187
+ id: "notify-on-failure",
188
+ labelKey: "fluidgrids.aiStudio.refinements.notifyOnFailure",
189
+ label: "Notify on failure",
190
+ instruction: "Notify someone when the workflow fails: add a step that sends the error details to a notification webhook."
191
+ },
192
+ {
193
+ id: "log-result",
194
+ labelKey: "fluidgrids.aiStudio.refinements.logResult",
195
+ label: "Log the result",
196
+ instruction: "Add a final step that logs a short summary of what the workflow did."
197
+ }
198
+ ];
199
+ function clamp(text) {
200
+ return text.length > 4e3 ? text.slice(0, AI_DRAFT_MAX_PROMPT_LENGTH) : text;
201
+ }
202
+ /**
203
+ * The request for a refinement of the current draft: the original request
204
+ * plus the change. The draft itself goes to the server as
205
+ * `baseDefinitionJson`, so the model edits it rather than starting over.
206
+ */
207
+ function buildRefinementPrompt(originalPrompt, instruction) {
208
+ return clamp(`${originalPrompt.trim()}\n\nChange: ${instruction.trim()}`);
209
+ }
210
+ /**
211
+ * The request after the user answered some of the model's clarifying
212
+ * questions. Unanswered questions are left out; null when nothing was
213
+ * answered, so the caller keeps the button disabled.
214
+ */
215
+ function buildAnsweredPrompt(originalPrompt, questions, answers) {
216
+ const lines = questions.flatMap((question, index) => {
217
+ const answer = (answers[index] ?? "").trim();
218
+ return answer === "" ? [] : [`- ${question} ${answer}`];
219
+ });
220
+ if (lines.length === 0) return null;
221
+ return clamp(`${originalPrompt.trim()}\n\nAnswers to your questions:\n${lines.join("\n")}`);
222
+ }
223
+ function isRecord(value) {
224
+ return typeof value === "object" && value !== null && !Array.isArray(value);
225
+ }
226
+ /** The draft's nodes as readable steps; entries without an id or type are skipped. */
227
+ function draftSteps(definitionJson) {
228
+ const data = isRecord(definitionJson) && isRecord(definitionJson.data) ? definitionJson.data : {};
229
+ return (Array.isArray(data.nodes) ? data.nodes : []).filter(isRecord).flatMap((node) => {
230
+ if (typeof node.id !== "string" || typeof node.type !== "string") return [];
231
+ const nodeData = isRecord(node.data) ? node.data : {};
232
+ let label = node.id;
233
+ if (typeof nodeData.label === "string" && nodeData.label.trim() !== "") label = nodeData.label;
234
+ else if (typeof node.label === "string" && node.label.trim() !== "") label = node.label;
235
+ return [{
236
+ id: node.id,
237
+ label,
238
+ type: node.type
239
+ }];
240
+ });
241
+ }
242
+ /**
243
+ * The `createWorkflowDefinition` input that saves a draft as a new DRAFT
244
+ * workflow under `key`. Same shape the server writes when a proposal is
245
+ * applied as a new workflow: the generated definition with its key replaced.
246
+ * Nothing is activated.
247
+ */
248
+ function draftCreateInput(draft, key) {
249
+ const name = draft.name.trim() || "Generated workflow";
250
+ const description = draft.description.trim() || name;
251
+ return {
252
+ workflowKey: key,
253
+ version: "v1",
254
+ name,
255
+ description,
256
+ definitionJson: {
257
+ ...draft.definitionJson,
258
+ key,
259
+ name,
260
+ description
261
+ }
262
+ };
263
+ }
264
+ /** The page phase for a generation status (the wire enum's string). */
265
+ function phaseOf(status, working) {
266
+ if (working || status === "PROCESSING") return "generating";
267
+ if (status === "COMPLETED") return "ready";
268
+ if (status === "FAILED") return "failed";
269
+ if (status === "CANCELLED") return "cancelled";
270
+ return "idle";
271
+ }
272
+
273
+ // src/modules/ai-studio/pages/AiStudioPage.tsx
274
+ /**
275
+ * AI Studio (BOFF-4089) — describe an automation in plain English and review
276
+ * the workflow draft the model builds for it, before anything is saved.
277
+ *
278
+ * - Left: the request, optional starting pattern, starter examples, the
279
+ * AI-credit balance, one-click refinements of the current draft and the
280
+ * model's clarifying questions (answer them and regenerate).
281
+ * - Right: the draft on the real builder canvas in read-only mode, its
282
+ * explanation, the settings still to fill in, and the two ways forward:
283
+ * "Edit in builder" saves the draft as a new DRAFT workflow (never
284
+ * activated) and opens it; "Regenerate" asks again from scratch.
285
+ *
286
+ * Generation runs on workflow-svc (`generateWorkflowFromPrompt`, polled via
287
+ * `workflowGeneration`); this page only drives and renders it.
288
+ */
289
+ var AiStudioPage_exports = /* @__PURE__ */ __exportAll({ AiStudioPage: () => AiStudioPage });
290
+ const NO_TEMPLATE = "__none__";
291
+ /** The read-only canvas for a draft, with the palette metadata a drop would give each node. */
292
+ function DraftCanvas({ generationId, name, definitionJson }) {
293
+ const tr = useTr();
294
+ const [workflow, setWorkflow] = useState(null);
295
+ useEffect(() => {
296
+ let active = true;
297
+ const build = (seeded) => {
298
+ const canvas = draftToCanvas(definitionJson);
299
+ return {
300
+ id: generationId,
301
+ workflowKey: generationId,
302
+ version: "preview",
303
+ name,
304
+ nodes: seeded ? enrichDraftNodes(canvas.nodes, buildOperationSeed) : canvas.nodes,
305
+ edges: canvas.edges,
306
+ sidebar: {
307
+ active: "none",
308
+ panels: { nodeProperties: { selectedNode: null } }
309
+ }
310
+ };
311
+ };
312
+ ensureNodeTypesLoaded(draftToCanvas(definitionJson).nodes.map((node) => node.type)).then(() => {
313
+ if (active) setWorkflow(build(true));
314
+ }).catch(() => {
315
+ if (active) setWorkflow(build(false));
316
+ });
317
+ return () => {
318
+ active = false;
319
+ };
320
+ }, [
321
+ definitionJson,
322
+ generationId,
323
+ name
324
+ ]);
325
+ if (!workflow) return /* @__PURE__ */ jsxs("div", {
326
+ className: "flex h-full items-center justify-center gap-2 text-sm text-muted-foreground",
327
+ children: [/* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }), tr("fluidgrids.aiStudio.preview.loading", "Loading preview…")]
328
+ });
329
+ return /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(ErrorBoundary, {
330
+ fallback: () => /* @__PURE__ */ jsx("div", {
331
+ className: "flex h-full items-center justify-center p-6 text-center text-sm text-muted-foreground",
332
+ children: tr("fluidgrids.aiStudio.preview.renderError", "The preview could not be drawn. The draft can still be opened in the builder.")
333
+ }),
334
+ children: /* @__PURE__ */ jsx(FlowBuilderPage, {
335
+ workflow,
336
+ readOnly: true
337
+ })
338
+ }) });
339
+ }
340
+ function AiStudioPage() {
341
+ const tr = useTr();
342
+ const navigate = useNavigate();
343
+ const generation = useWorkflowGeneration();
344
+ const templates = useAiWorkflowTemplates(true);
345
+ const { createWorkflow } = useCreateWorkflow();
346
+ const { allowed: canGenerate } = useFluidGridsPermission(FLUIDGRIDS_PERMISSIONS.AI_WORKFLOW_CREATE);
347
+ const { allowed: canCreate } = useFluidGridsPermission(FLUIDGRIDS_PERMISSIONS.WORKFLOW_CREATE);
348
+ const [prompt, setPrompt] = useState("");
349
+ const [template, setTemplate] = useState(NO_TEMPLATE);
350
+ const [answers, setAnswers] = useState({});
351
+ const [saving, setSaving] = useState(false);
352
+ const [refusal, setRefusal] = useState(null);
353
+ const requestRef = useRef(null);
354
+ const current = generation.generation;
355
+ const status = current ? String(current.status) : void 0;
356
+ const phase = phaseOf(status, generation.isWorking);
357
+ const draft = phase === "ready" ? current?.draft ?? null : null;
358
+ const questions = draft?.questions ?? [];
359
+ const warnings = current?.validation?.warnings ?? [];
360
+ const errors = current?.validation?.errors ?? [];
361
+ const steps = useMemo(() => draft ? draftSteps(draft.definitionJson) : [], [draft]);
362
+ const trimmed = prompt.trim();
363
+ const working = phase === "generating";
364
+ const answeredPrompt = requestRef.current ? buildAnsweredPrompt(requestRef.current.prompt, questions, answers) : null;
365
+ const run = useCallback((input) => {
366
+ setAnswers({});
367
+ generation.start(input);
368
+ }, [generation]);
369
+ const handleGenerate = useCallback(() => {
370
+ if (!trimmed) return;
371
+ const request = {
372
+ prompt: trimmed,
373
+ ...template !== NO_TEMPLATE ? { template } : {}
374
+ };
375
+ requestRef.current = request;
376
+ run(request);
377
+ }, [
378
+ run,
379
+ template,
380
+ trimmed
381
+ ]);
382
+ const handleRegenerate = useCallback(() => {
383
+ if (!requestRef.current) return;
384
+ run(requestRef.current);
385
+ }, [run]);
386
+ const handleRefine = useCallback((instruction) => {
387
+ if (!draft || !requestRef.current) return;
388
+ const refined = buildRefinementPrompt(requestRef.current.prompt, instruction);
389
+ requestRef.current = {
390
+ ...requestRef.current,
391
+ prompt: refined
392
+ };
393
+ setPrompt(refined);
394
+ run({
395
+ prompt: refined,
396
+ baseDefinitionJson: draft.definitionJson
397
+ });
398
+ }, [draft, run]);
399
+ const handleAnswer = useCallback(() => {
400
+ if (!draft || !requestRef.current || !answeredPrompt) return;
401
+ requestRef.current = {
402
+ ...requestRef.current,
403
+ prompt: answeredPrompt
404
+ };
405
+ setPrompt(answeredPrompt);
406
+ run({
407
+ prompt: answeredPrompt,
408
+ baseDefinitionJson: draft.definitionJson
409
+ });
410
+ }, [
411
+ answeredPrompt,
412
+ draft,
413
+ run
414
+ ]);
415
+ const handleEditInBuilder = useCallback(async () => {
416
+ if (!draft) return;
417
+ setSaving(true);
418
+ let reported = null;
419
+ try {
420
+ const created = await createWorkflow(draftCreateInput({
421
+ name: draft.name,
422
+ description: draft.description,
423
+ definitionJson: draft.definitionJson
424
+ }, v4()), { onError: (error) => {
425
+ reported = error;
426
+ } });
427
+ if (!created?.workflowKey || !created.version) {
428
+ setRefusal(classifyCreateRefusal(reported));
429
+ return;
430
+ }
431
+ toast.success(tr("fluidgrids.aiStudio.savedToast", "Draft saved as a new workflow"), { description: tr("fluidgrids.aiStudio.savedToastDescription", "Nothing is live yet. Fill in the settings, run a test, then activate it.") });
432
+ navigate(builderPathOf(created, ".."));
433
+ } finally {
434
+ setSaving(false);
435
+ }
436
+ }, [
437
+ createWorkflow,
438
+ draft,
439
+ navigate,
440
+ tr
441
+ ]);
442
+ const showExamples = phase === "idle" && !requestRef.current;
443
+ return /* @__PURE__ */ jsxs("div", {
444
+ className: "min-h-full bg-background-primary p-4 sm:p-6",
445
+ "data-testid": "ai-studio-page",
446
+ children: [
447
+ /* @__PURE__ */ jsxs("header", {
448
+ className: "mb-6 flex flex-col gap-1",
449
+ children: [/* @__PURE__ */ jsxs("div", {
450
+ className: "flex items-center gap-3",
451
+ children: [
452
+ /* @__PURE__ */ jsx(Sparkles, {
453
+ className: "size-6 text-action-primary-bg",
454
+ "aria-hidden": true
455
+ }),
456
+ /* @__PURE__ */ jsx("h1", {
457
+ className: "text-2xl font-semibold text-text-primary sm:text-3xl",
458
+ children: tr("fluidgrids.aiStudio.title", "AI Studio")
459
+ }),
460
+ /* @__PURE__ */ jsx(Badge, {
461
+ variant: "outline",
462
+ children: tr("fluidgrids.aiStudio.beta", "Beta")
463
+ })
464
+ ]
465
+ }), /* @__PURE__ */ jsx("p", {
466
+ className: "text-sm text-text-secondary",
467
+ children: tr("fluidgrids.aiStudio.subtitle", "Describe your automation in plain English. AI builds a draft workflow you review before anything is saved.")
468
+ })]
469
+ }),
470
+ /* @__PURE__ */ jsxs("div", {
471
+ className: "grid gap-6 lg:grid-cols-[minmax(0,2fr)_minmax(0,3fr)]",
472
+ children: [/* @__PURE__ */ jsxs(Card, {
473
+ className: "h-fit",
474
+ children: [/* @__PURE__ */ jsx(CardHeader, {
475
+ className: "pb-2",
476
+ children: /* @__PURE__ */ jsx("h2", {
477
+ className: "text-base font-semibold text-text-primary",
478
+ children: tr("fluidgrids.aiStudio.describeTitle", "Describe your automation")
479
+ })
480
+ }), /* @__PURE__ */ jsxs(CardContent, {
481
+ className: "flex flex-col gap-4",
482
+ children: [
483
+ /* @__PURE__ */ jsxs("div", {
484
+ className: "flex flex-col gap-1.5",
485
+ children: [
486
+ /* @__PURE__ */ jsx(Label, {
487
+ htmlFor: "ai-studio-prompt",
488
+ className: "sr-only",
489
+ children: tr("fluidgrids.aiStudio.promptLabel", "What should the workflow do?")
490
+ }),
491
+ /* @__PURE__ */ jsx(Textarea, {
492
+ id: "ai-studio-prompt",
493
+ "data-testid": "ai-studio-prompt",
494
+ rows: 6,
495
+ maxLength: AI_DRAFT_MAX_PROMPT_LENGTH,
496
+ value: prompt,
497
+ disabled: working,
498
+ placeholder: tr("fluidgrids.aiStudio.promptPlaceholder", "When a new Stripe payment succeeds, add the customer to HubSpot and post a summary to Slack #sales."),
499
+ onChange: (event) => setPrompt(event.target.value)
500
+ }),
501
+ /* @__PURE__ */ jsxs("span", {
502
+ className: "self-end text-xs text-text-secondary",
503
+ children: [
504
+ prompt.length,
505
+ "/",
506
+ AI_DRAFT_MAX_PROMPT_LENGTH
507
+ ]
508
+ })
509
+ ]
510
+ }),
511
+ templates.length > 0 && /* @__PURE__ */ jsxs("div", {
512
+ className: "flex flex-col gap-1.5",
513
+ children: [/* @__PURE__ */ jsx(Label, {
514
+ htmlFor: "ai-studio-template",
515
+ children: tr("fluidgrids.aiStudio.templateLabel", "Starting pattern (optional)")
516
+ }), /* @__PURE__ */ jsxs(Select, {
517
+ value: template,
518
+ onValueChange: setTemplate,
519
+ disabled: working,
520
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
521
+ id: "ai-studio-template",
522
+ "data-testid": "ai-studio-template",
523
+ children: /* @__PURE__ */ jsx(SelectValue, {})
524
+ }), /* @__PURE__ */ jsxs(SelectContent, { children: [/* @__PURE__ */ jsx(SelectItem, {
525
+ value: NO_TEMPLATE,
526
+ children: tr("fluidgrids.aiStudio.noTemplate", "No pattern")
527
+ }), templates.map((option) => /* @__PURE__ */ jsx(SelectItem, {
528
+ value: option.id,
529
+ children: option.name
530
+ }, option.id))] })]
531
+ })]
532
+ }),
533
+ /* @__PURE__ */ jsx(AiCreditBalanceSlot, { testId: "ai-studio-credit-balance" }),
534
+ /* @__PURE__ */ jsxs("div", {
535
+ className: "flex flex-wrap gap-2",
536
+ children: [/* @__PURE__ */ jsxs(Button, {
537
+ "data-testid": "ai-studio-generate",
538
+ onClick: handleGenerate,
539
+ disabled: !trimmed || working || !canGenerate,
540
+ title: canGenerate ? void 0 : tr("fluidgrids.aiStudio.noPermission", "Your role cannot generate workflows with AI"),
541
+ className: "flex grow gap-2",
542
+ children: [working ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Sparkles, { className: "size-4" }), working ? tr("fluidgrids.aiStudio.generating", "Generating…") : tr("fluidgrids.aiStudio.generate", "Generate workflow")]
543
+ }), working && /* @__PURE__ */ jsxs(Button, {
544
+ variant: "outline",
545
+ "data-testid": "ai-studio-cancel",
546
+ onClick: () => void generation.cancel(),
547
+ className: "flex gap-2",
548
+ children: [/* @__PURE__ */ jsx(Square, { className: "size-4" }), tr("fluidgrids.aiStudio.stop", "Stop")]
549
+ })]
550
+ }),
551
+ showExamples && /* @__PURE__ */ jsxs("section", {
552
+ className: "flex flex-col gap-2",
553
+ "data-testid": "ai-studio-examples",
554
+ children: [/* @__PURE__ */ jsxs("h3", {
555
+ className: "flex items-center gap-2 text-sm font-medium text-text-primary",
556
+ children: [/* @__PURE__ */ jsx(Lightbulb, {
557
+ className: "size-4",
558
+ "aria-hidden": true
559
+ }), tr("fluidgrids.aiStudio.examplesTitle", "Try an example")]
560
+ }), /* @__PURE__ */ jsx("div", {
561
+ className: "flex flex-wrap gap-2",
562
+ children: AI_STUDIO_EXAMPLES.map((example) => /* @__PURE__ */ jsx(Button, {
563
+ variant: "outline",
564
+ size: "sm",
565
+ "data-testid": `ai-studio-example-${example.id}`,
566
+ onClick: () => setPrompt(tr(example.promptKey, example.prompt)),
567
+ children: tr(example.labelKey, example.label)
568
+ }, example.id))
569
+ })]
570
+ }),
571
+ draft && /* @__PURE__ */ jsxs("section", {
572
+ className: "flex flex-col gap-2",
573
+ "data-testid": "ai-studio-suggestions",
574
+ children: [/* @__PURE__ */ jsxs("h3", {
575
+ className: "flex items-center gap-2 text-sm font-medium text-text-primary",
576
+ children: [/* @__PURE__ */ jsx(Lightbulb, {
577
+ className: "size-4",
578
+ "aria-hidden": true
579
+ }), tr("fluidgrids.aiStudio.suggestionsTitle", "Suggestions")]
580
+ }), /* @__PURE__ */ jsx("div", {
581
+ className: "flex flex-wrap gap-2",
582
+ children: AI_STUDIO_REFINEMENTS.map((refinement) => /* @__PURE__ */ jsx(Button, {
583
+ variant: "outline",
584
+ size: "sm",
585
+ disabled: !canGenerate,
586
+ "data-testid": `ai-studio-refine-${refinement.id}`,
587
+ onClick: () => handleRefine(refinement.instruction),
588
+ children: tr(refinement.labelKey, refinement.label)
589
+ }, refinement.id))
590
+ })]
591
+ }),
592
+ questions.length > 0 && /* @__PURE__ */ jsxs("section", {
593
+ className: "flex flex-col gap-3",
594
+ "data-testid": "ai-studio-questions",
595
+ children: [
596
+ /* @__PURE__ */ jsxs("h3", {
597
+ className: "flex items-center gap-2 text-sm font-medium text-text-primary",
598
+ children: [/* @__PURE__ */ jsx(MessageCircleQuestion, {
599
+ className: "size-4",
600
+ "aria-hidden": true
601
+ }), tr("fluidgrids.aiStudio.questionsTitle", "AI clarifying questions")]
602
+ }),
603
+ /* @__PURE__ */ jsx("p", {
604
+ className: "text-xs text-text-secondary",
605
+ children: tr("fluidgrids.aiStudio.questionsHint", "The draft uses sensible defaults. Answer any of these to refine it.")
606
+ }),
607
+ /* @__PURE__ */ jsx("ol", {
608
+ className: "flex flex-col gap-3",
609
+ children: questions.map((question, index) => /* @__PURE__ */ jsxs("li", {
610
+ className: "flex flex-col gap-1.5",
611
+ children: [/* @__PURE__ */ jsxs(Label, {
612
+ htmlFor: `ai-studio-answer-${index}`,
613
+ className: "flex gap-2 font-normal text-text-primary",
614
+ children: [/* @__PURE__ */ jsx("span", {
615
+ className: "flex size-5 shrink-0 items-center justify-center rounded-full bg-action-primary-bg text-xs text-action-primary-text",
616
+ children: index + 1
617
+ }), /* @__PURE__ */ jsx("span", { children: question })]
618
+ }), /* @__PURE__ */ jsx(Input, {
619
+ id: `ai-studio-answer-${index}`,
620
+ "data-testid": `ai-studio-answer-${index}`,
621
+ value: answers[index] ?? "",
622
+ placeholder: tr("fluidgrids.aiStudio.answerPlaceholder", "Your answer (optional)"),
623
+ onChange: (event) => setAnswers((previous) => ({
624
+ ...previous,
625
+ [index]: event.target.value
626
+ }))
627
+ })]
628
+ }, question))
629
+ }),
630
+ /* @__PURE__ */ jsxs(Button, {
631
+ variant: "secondary",
632
+ "data-testid": "ai-studio-answer-submit",
633
+ disabled: !answeredPrompt || !canGenerate,
634
+ onClick: handleAnswer,
635
+ className: "flex gap-2 self-start",
636
+ children: [/* @__PURE__ */ jsx(RefreshCw, { className: "size-4" }), tr("fluidgrids.aiStudio.answerSubmit", "Update the draft with my answers")]
637
+ })
638
+ ]
639
+ })
640
+ ]
641
+ })]
642
+ }), /* @__PURE__ */ jsxs(Card, {
643
+ className: "flex min-w-0 flex-col",
644
+ "data-testid": "ai-studio-draft",
645
+ children: [/* @__PURE__ */ jsxs(CardHeader, {
646
+ className: "flex flex-row flex-wrap items-center justify-between gap-2 pb-2",
647
+ children: [/* @__PURE__ */ jsxs("div", {
648
+ className: "flex min-w-0 items-center gap-2",
649
+ children: [/* @__PURE__ */ jsx(Badge, {
650
+ variant: "secondary",
651
+ children: tr("fluidgrids.aiStudio.draftBadge", "Draft")
652
+ }), /* @__PURE__ */ jsx("h2", {
653
+ className: "truncate text-base font-semibold text-text-primary",
654
+ "data-testid": "ai-studio-draft-name",
655
+ children: draft?.name ?? (working ? tr("fluidgrids.aiStudio.draftGenerating", "Your workflow is being generated") : tr("fluidgrids.aiStudio.draftEmpty", "No draft yet"))
656
+ })]
657
+ }), draft && /* @__PURE__ */ jsx(Badge, {
658
+ variant: "outline",
659
+ "data-testid": "ai-studio-step-count",
660
+ children: tr("fluidgrids.aiStudio.stepCount", "{{count}} steps", { count: draft.nodeCount })
661
+ })]
662
+ }), /* @__PURE__ */ jsxs(CardContent, {
663
+ className: "flex flex-col gap-4",
664
+ children: [
665
+ /* @__PURE__ */ jsx("div", {
666
+ className: "relative h-[360px] w-full overflow-hidden rounded-lg border border-border-secondary sm:h-[440px]",
667
+ children: draft && current ? /* @__PURE__ */ jsx(DraftCanvas, {
668
+ generationId: current.id,
669
+ name: draft.name,
670
+ definitionJson: draft.definitionJson
671
+ }) : /* @__PURE__ */ jsx("div", {
672
+ className: "flex h-full flex-col items-center justify-center gap-3 p-6 text-center text-sm text-text-secondary",
673
+ "data-testid": `ai-studio-state-${phase}`,
674
+ children: working ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Loader2, { className: "size-6 animate-spin" }), /* @__PURE__ */ jsx("p", { children: tr("fluidgrids.aiStudio.generatingHint", "Picking nodes, connecting them and checking the result. This usually takes under a minute.") })] }) : phase === "failed" ? /* @__PURE__ */ jsxs(Fragment, { children: [
675
+ /* @__PURE__ */ jsx(TriangleAlert, { className: "size-6 text-status-error-text" }),
676
+ /* @__PURE__ */ jsx("p", {
677
+ role: "alert",
678
+ className: "text-status-error-text",
679
+ children: current?.message ?? tr("fluidgrids.aiStudio.failed", "The draft could not be generated.")
680
+ }),
681
+ errors.length > 0 && /* @__PURE__ */ jsx("ul", {
682
+ className: "max-w-md list-disc text-left text-xs",
683
+ children: errors.slice(0, 5).map((entry) => /* @__PURE__ */ jsx("li", { children: entry }, entry))
684
+ })
685
+ ] }) : phase === "cancelled" ? /* @__PURE__ */ jsx("p", { children: tr("fluidgrids.aiStudio.cancelled", "Generation stopped. Change the request or generate again.") }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Sparkles, {
686
+ className: "size-6",
687
+ "aria-hidden": true
688
+ }), /* @__PURE__ */ jsx("p", { children: tr("fluidgrids.aiStudio.emptyHint", "Your draft appears here: a trigger and the steps that follow, ready to review.") })] })
689
+ })
690
+ }),
691
+ (generation.error || generation.timedOut) && /* @__PURE__ */ jsx("div", {
692
+ role: "alert",
693
+ className: "rounded-md border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text",
694
+ "data-testid": "ai-studio-error",
695
+ children: generation.timedOut ? tr("fluidgrids.aiStudio.timedOut", "The draft is taking longer than expected. Try again in a moment.") : generation.error
696
+ }),
697
+ draft && /* @__PURE__ */ jsxs(Fragment, { children: [
698
+ draft.explanation && /* @__PURE__ */ jsx("p", {
699
+ className: "text-sm text-text-secondary",
700
+ "data-testid": "ai-studio-explanation",
701
+ children: draft.explanation
702
+ }),
703
+ steps.length > 0 && /* @__PURE__ */ jsx("ol", {
704
+ className: "flex flex-col gap-1 text-sm",
705
+ "data-testid": "ai-studio-steps",
706
+ children: steps.map((step, index) => /* @__PURE__ */ jsxs("li", {
707
+ className: "flex items-center gap-2 text-text-primary",
708
+ children: [
709
+ /* @__PURE__ */ jsx(CheckCircle2, {
710
+ className: "size-4 shrink-0 text-status-success-text",
711
+ "aria-hidden": true
712
+ }),
713
+ /* @__PURE__ */ jsxs("span", {
714
+ className: "text-text-secondary",
715
+ children: [index + 1, "."]
716
+ }),
717
+ /* @__PURE__ */ jsx("span", {
718
+ className: "truncate",
719
+ children: step.label
720
+ })
721
+ ]
722
+ }, step.id))
723
+ }),
724
+ warnings.length > 0 && /* @__PURE__ */ jsxs("div", {
725
+ className: "rounded-md border border-status-warning-border bg-status-warning-bg-subtle p-3 text-sm text-status-warning-text",
726
+ "data-testid": "ai-studio-warnings",
727
+ children: [/* @__PURE__ */ jsx("p", {
728
+ className: "font-medium",
729
+ children: tr("fluidgrids.aiStudio.warningsTitle", "Fill in before activating")
730
+ }), /* @__PURE__ */ jsx("ul", {
731
+ className: "mt-1 list-disc pl-5 text-xs",
732
+ children: warnings.map((warning) => /* @__PURE__ */ jsx("li", { children: warning }, warning))
733
+ })]
734
+ })
735
+ ] }),
736
+ /* @__PURE__ */ jsxs("div", {
737
+ className: "flex flex-wrap items-center gap-2",
738
+ children: [
739
+ /* @__PURE__ */ jsxs(Button, {
740
+ "data-testid": "ai-studio-edit-in-builder",
741
+ disabled: !draft || saving || working || !canCreate,
742
+ onClick: () => void handleEditInBuilder(),
743
+ className: "flex gap-2",
744
+ children: [saving ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Pencil, { className: "size-4" }), tr("fluidgrids.aiStudio.editInBuilder", "Edit in builder")]
745
+ }),
746
+ /* @__PURE__ */ jsxs(Button, {
747
+ variant: "outline",
748
+ "data-testid": "ai-studio-regenerate",
749
+ disabled: !requestRef.current || working || !canGenerate,
750
+ onClick: handleRegenerate,
751
+ className: "flex gap-2",
752
+ children: [/* @__PURE__ */ jsx(RefreshCw, { className: "size-4" }), tr("fluidgrids.aiStudio.regenerate", "Regenerate")]
753
+ }),
754
+ /* @__PURE__ */ jsx("span", {
755
+ className: "text-xs text-text-secondary sm:ml-auto",
756
+ children: tr("fluidgrids.aiStudio.betaNote", "AI Studio is in beta. Outputs may vary.")
757
+ })
758
+ ]
759
+ })
760
+ ]
761
+ })]
762
+ })]
763
+ }),
764
+ /* @__PURE__ */ jsx(CreateWorkflowRefusalDialog, {
765
+ surface: "builder-create",
766
+ refusal,
767
+ onClose: () => setRefusal(null),
768
+ onUpgrade: () => navigate(BILLING_PLANS_PATH)
769
+ })
770
+ ]
771
+ });
772
+ }
773
+
774
+ export { AiStudioPage_exports as n, AiStudioPage as t };