@burdenoff/fluidgrids-fe-sdk 2026.1028.3 → 2026.1028.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,15 +3,15 @@ import { Input } from './chunk-IEVSFCFS.js';
3
3
  import { Dialog as Dialog$1, DialogContent as DialogContent$1, DialogHeader as DialogHeader$1, DialogTitle as DialogTitle$1, DialogDescription as DialogDescription$1, DialogFooter } from './chunk-YXMFRQYV.js';
4
4
  import { Badge as Badge$1, Button } from './chunk-BQXVT32O.js';
5
5
  import { QuotaExhaustedDialog } from './chunk-P2NRMJ7Z.js';
6
- import { EntityTagPicker } from './chunk-MODO24HY.js';
7
- import { useWorkflowVersions, useRestoreWorkflowVersion, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-63BP6O6E.js';
6
+ import { EntityTagPicker, useWorkflowProposal, WorkflowProposalCard } from './chunk-JAKWVVVQ.js';
7
+ import { useRestoreWorkflowVersion, useWorkflowVersions, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-63BP6O6E.js';
8
8
  import { FluidGridsDocsLink } from './chunk-4VJ3MDOV.js';
9
9
  import { NODE_CONFIGS, createConfiguredNode, resolveOutputHandles, resolveInputHandles, isNodeConfigsInitialized, useFlowStore, getNodeDetail, useSaveStatusStore, liveInternalOperations, resolveNodeOperation, readDisplayOptions, parseParameterValue, canAutosave, STICKY_NOTE_TYPE, isNodeTrigger, resolveUiSpec, RUNS_QUOTA_NAME, throwIfResultError, BILLING_PLANS_PATH, emitUpgradeClicked, FluidGridsContext, emitWorkflowCreated, emitTemplateUsed, ensureNodeConfigsLoaded, registerWorkflowNodeTypes, emitNodeInstalled, useNodeExecution, onNodeTypesUpdate, NODE_TYPES, getNodeMetadata, useIsValidConnection, refetchStoreNodes, AVAILABLE_NODES, useInsertNode, NODE_TYPE_DRAG_DATA_FORMAT, NODE_OPERATION_DRAG_DATA_FORMAT, fieldsFromValue, useFluidGridsAnalytics, useSaveWorkflow, workflowFingerprint, buildStickyNode, useNodeRegistryRevision, fieldsFromIO, isParamVisible, ConnectionSelector, structuredParameterType, nodeAccent, NodeTypeIcon, NODES_METADATA, useConnections, ConnectionFormDialog, SdkPermissionButton, FLUIDGRIDS_PERMISSIONS, saveIndicatorOf, inAppLeaveTarget, declaredOutputFields, subscribeWorkflowSaved, pathToExpression, ExpressionEditor, formatParameterValue, leaveNeedsConfirmation, buildOperationSeed, snapshotForSave, editStateOf, isImageIcon, NODES, synchronizeOperationUpdate, NodePropertyPanel, openRunQuotaUpgradePrompt, describeOperationError, isTriggerOperation, canRenderOperationPanel, resolveOperationName, AUTOSAVE_RETRY_DELAYS_MS, AUTOSAVE_DEBOUNCE_MS } from './chunk-5LKZ4PNK.js';
10
10
  import { cn } from './chunk-JL7CASN5.js';
11
11
  import { getGlobalStoreClient } from './chunk-PXIFUMLA.js';
12
12
  import { getFluidGridsConfig } from './chunk-CY6KX4RG.js';
13
13
  import { GetMyNodeInstallationsDocument, BrowseWorkflowTemplatesDocument, InstallWorkflowNodeDocument, InstallPurchasedNodeDocument, BrowseWorkflowNodesDocument, UninstallWorkflowNodeDocument, CreateStoreOrderDocument, GetNodeInstallableVersionsDocument } from './chunk-WBH2XEWO.js';
14
- import { WorkspaceRunsQuotaDocument, NodeSchemaIntrospectionDocument, useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, WorkflowGenerationTemplatesDocument, WorkflowGenerationDocument, GenerateWorkflowFromPromptDocument, WorkflowEnvironmentsDocument, WorkflowPromotionRequestsDocument, DeployWorkflowVersionToDevDocument, RequestWorkflowPromotionDocument, ApproveWorkflowPromotionDocument, RejectWorkflowPromotionDocument, CancelWorkflowPromotionDocument, UpdateWorkflowEnvironmentPolicyDocument, WorkflowGitRepositoryDocument, WorkflowGitPushesDocument, WorkflowGitCommitsDocument, LinkWorkflowGitRepositoryDocument, UnlinkWorkflowGitRepositoryDocument, PushWorkflowToGitDocument, WorkflowGitDiffDocument, RestoreWorkflowFromGitDocument, WorkflowAuthoringDocument, PreviewScheduleDocument, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument } from './chunk-JBLYBD2B.js';
14
+ import { WorkspaceRunsQuotaDocument, NodeSchemaIntrospectionDocument, useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, WorkflowGenerationTemplatesDocument, WorkflowEnvironmentsDocument, WorkflowPromotionRequestsDocument, DeployWorkflowVersionToDevDocument, RequestWorkflowPromotionDocument, ApproveWorkflowPromotionDocument, RejectWorkflowPromotionDocument, CancelWorkflowPromotionDocument, UpdateWorkflowEnvironmentPolicyDocument, WorkflowGitRepositoryDocument, WorkflowGitPushesDocument, WorkflowGitCommitsDocument, LinkWorkflowGitRepositoryDocument, UnlinkWorkflowGitRepositoryDocument, PushWorkflowToGitDocument, WorkflowGitDiffDocument, RestoreWorkflowFromGitDocument, WorkflowAuthoringDocument, PreviewScheduleDocument, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument } from './chunk-JBLYBD2B.js';
15
15
  import { useTr } from './chunk-UULVIT25.js';
16
16
  import { getFluidGridsClient, waitForFluidGridsClient, getFluidGridsClientSafe } from './chunk-NTWG3CJX.js';
17
17
  import { __export } from './chunk-62GWEYJ5.js';
@@ -54902,10 +54902,30 @@ function GitSyncPanel({
54902
54902
  }
54903
54903
  );
54904
54904
  }
54905
+ function useAiWorkflowTemplates(enabled) {
54906
+ const [templates, setTemplates] = useState(
54907
+ []
54908
+ );
54909
+ useEffect(() => {
54910
+ if (!enabled) return;
54911
+ let active = true;
54912
+ getFluidGridsClient().query({
54913
+ query: WorkflowGenerationTemplatesDocument,
54914
+ fetchPolicy: "cache-first"
54915
+ }).then((result) => {
54916
+ if (active)
54917
+ setTemplates(result.data?.workflowGenerationTemplates ?? []);
54918
+ }).catch(() => {
54919
+ if (active) setTemplates([]);
54920
+ });
54921
+ return () => {
54922
+ active = false;
54923
+ };
54924
+ }, [enabled]);
54925
+ return templates;
54926
+ }
54905
54927
 
54906
54928
  // src/builder/lib/ai-draft.ts
54907
- var AI_DRAFT_POLL_INTERVAL_MS = 2500;
54908
- var AI_DRAFT_MAX_WAIT_MS = 6 * 60 * 1e3;
54909
54929
  var AI_DRAFT_MAX_PROMPT_LENGTH = 4e3;
54910
54930
  function isRecord6(value) {
54911
54931
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -54978,168 +54998,28 @@ function enrichDraftNodes(nodes, seed) {
54978
54998
  };
54979
54999
  });
54980
55000
  }
54981
- function summarizeDraftNodes(definitionJson) {
54982
- return draftToCanvas(definitionJson).nodes.map((node2) => {
54983
- const data = isRecord6(node2.data) ? node2.data : {};
54984
- const label = stringOr(
54985
- node2.label,
54986
- stringOr(data.label, node2.type ?? node2.id)
54987
- );
54988
- return { id: node2.id, type: node2.type ?? "", label };
54989
- });
54990
- }
54991
- function shortNodeType(type) {
54992
- const afterColon = type.includes(":") ? type.split(":").pop() : type;
54993
- const afterSlash = afterColon?.includes("/") ? afterColon.split("/").pop() : afterColon;
54994
- return afterSlash ?? type;
54995
- }
54996
- function shouldKeepPolling(status, startedAt, now = Date.now()) {
54997
- return status === "PROCESSING" && now - startedAt < AI_DRAFT_MAX_WAIT_MS;
54998
- }
54999
55001
  function canvasHasWork(nodes) {
55000
55002
  return nodes.length > 1;
55001
55003
  }
55002
-
55003
- // src/builder/hooks/use-ai-workflow-draft.ts
55004
- function messageOf3(error62) {
55005
- return error62 instanceof Error ? error62.message : String(error62);
55006
- }
55007
- function useAiWorkflowDraft() {
55008
- const [generation, setGeneration] = useState(
55009
- null
55010
- );
55011
- const [isGenerating, setIsGenerating] = useState(false);
55012
- const [error62, setError] = useState(null);
55013
- const [timedOut, setTimedOut] = useState(false);
55014
- const runRef = useRef(0);
55015
- const timerRef = useRef(null);
55016
- const clearTimer = useCallback(() => {
55017
- if (timerRef.current) clearTimeout(timerRef.current);
55018
- timerRef.current = null;
55019
- }, []);
55020
- const reset = useCallback(() => {
55021
- runRef.current++;
55022
- clearTimer();
55023
- setGeneration(null);
55024
- setIsGenerating(false);
55025
- setError(null);
55026
- setTimedOut(false);
55027
- }, [clearTimer]);
55028
- useEffect(
55029
- () => () => {
55030
- runRef.current++;
55031
- clearTimer();
55032
- },
55033
- [clearTimer]
55034
- );
55035
- const poll = useCallback(
55036
- async (run, id, startedAt) => {
55037
- if (run !== runRef.current) return;
55038
- try {
55039
- const result = throwIfResultError(
55040
- await getFluidGridsClient().query({
55041
- query: WorkflowGenerationDocument,
55042
- variables: { id },
55043
- fetchPolicy: "network-only"
55044
- })
55045
- );
55046
- if (run !== runRef.current) return;
55047
- const next = result.data?.workflowGeneration ?? null;
55048
- if (next) setGeneration(next);
55049
- const status = next ? String(next.status) : void 0;
55050
- if (shouldKeepPolling(status, startedAt)) {
55051
- timerRef.current = setTimeout(
55052
- () => void poll(run, id, startedAt),
55053
- AI_DRAFT_POLL_INTERVAL_MS
55054
- );
55055
- return;
55056
- }
55057
- if (status === "PROCESSING") setTimedOut(true);
55058
- setIsGenerating(false);
55059
- } catch (pollError) {
55060
- if (run !== runRef.current) return;
55061
- setError(messageOf3(pollError));
55062
- setIsGenerating(false);
55063
- }
55064
- },
55065
- []
55066
- );
55067
- const start = useCallback(
55068
- async (input2) => {
55069
- const run = ++runRef.current;
55070
- clearTimer();
55071
- setGeneration(null);
55072
- setError(null);
55073
- setTimedOut(false);
55074
- setIsGenerating(true);
55075
- try {
55076
- const result = throwIfResultError(
55077
- await getFluidGridsClient().mutate({
55078
- mutation: GenerateWorkflowFromPromptDocument,
55079
- variables: {
55080
- input: {
55081
- prompt: input2.prompt,
55082
- ...input2.template ? { template: input2.template } : {},
55083
- ...input2.baseDefinitionJson ? { baseDefinitionJson: input2.baseDefinitionJson } : {},
55084
- ...input2.workflowId ? { workflowId: input2.workflowId } : {}
55085
- }
55086
- }
55087
- })
55088
- );
55089
- const id = result.data?.generateWorkflowFromPrompt?.id;
55090
- if (!id) throw new Error("The generation did not start");
55091
- if (run !== runRef.current) return;
55092
- timerRef.current = setTimeout(
55093
- () => void poll(run, id, Date.now()),
55094
- AI_DRAFT_POLL_INTERVAL_MS
55095
- );
55096
- } catch (startError) {
55097
- if (run !== runRef.current) return;
55098
- setError(messageOf3(startError));
55099
- setIsGenerating(false);
55100
- }
55101
- },
55102
- [clearTimer, poll]
55103
- );
55104
- return { generation, isGenerating, error: error62, timedOut, start, reset };
55105
- }
55106
- function useAiWorkflowTemplates(enabled) {
55107
- const [templates, setTemplates] = useState(
55108
- []
55109
- );
55110
- useEffect(() => {
55111
- if (!enabled) return;
55112
- let active = true;
55113
- getFluidGridsClient().query({
55114
- query: WorkflowGenerationTemplatesDocument,
55115
- fetchPolicy: "cache-first"
55116
- }).then((result) => {
55117
- if (active)
55118
- setTemplates(result.data?.workflowGenerationTemplates ?? []);
55119
- }).catch(() => {
55120
- if (active) setTemplates([]);
55121
- });
55122
- return () => {
55123
- active = false;
55124
- };
55125
- }, [enabled]);
55126
- return templates;
55127
- }
55128
55004
  var NO_TEMPLATE = "__none__";
55129
55005
  function AiDraftPanel({
55130
55006
  className,
55131
55007
  compact = false
55132
55008
  }) {
55133
55009
  const tr = useTr();
55134
- const { id: workflowKey } = useParams();
55010
+ const params = useParams();
55011
+ const workflowKey = params.id;
55012
+ const openVersion = params.version;
55013
+ const navigate = useGuardedNavigate();
55135
55014
  const { fitView } = useReactFlow();
55136
55015
  const [open2, setOpen] = useState(false);
55137
55016
  const [prompt, setPrompt] = useState("");
55138
55017
  const [template, setTemplate] = useState(NO_TEMPLATE);
55139
- const [refining, setRefining] = useState(false);
55140
55018
  const [confirmReplace, setConfirmReplace] = useState(false);
55019
+ const [reverting, setReverting] = useState(false);
55141
55020
  const templates = useAiWorkflowTemplates(open2);
55142
- const { generation, isGenerating, error: error62, timedOut, start, reset } = useAiWorkflowDraft();
55021
+ const proposal = useWorkflowProposal();
55022
+ const { restoreVersion } = useRestoreWorkflowVersion();
55143
55023
  const [nodes, setNodes, setEdges] = useFlowStore(
55144
55024
  useShallow((s) => [
55145
55025
  s.workflow.nodes,
@@ -55147,35 +55027,100 @@ function AiDraftPanel({
55147
55027
  s.actions.edges.setEdges
55148
55028
  ])
55149
55029
  );
55150
- const status = generation ? String(generation.status) : void 0;
55151
- const draft = status === "COMPLETED" ? generation?.draft ?? null : null;
55152
- const failed = status === "FAILED";
55153
- const draftNodes = useMemo(
55154
- () => draft ? summarizeDraftNodes(draft.definitionJson) : [],
55155
- [draft]
55156
- );
55030
+ const current = proposal.proposal;
55031
+ const status = current ? String(current.status) : void 0;
55157
55032
  const trimmed = prompt.trim();
55033
+ const unsavedEdits = Boolean(workflowKey) && open2 && leavingLosesChanges();
55158
55034
  const handleGenerate = useCallback(() => {
55159
55035
  if (!trimmed) return;
55160
- void start({
55036
+ void proposal.start({
55037
+ kind: "change",
55161
55038
  prompt: trimmed,
55162
- ...template !== NO_TEMPLATE ? { template } : {},
55163
- ...refining && draft ? { baseDefinitionJson: draft.definitionJson } : {},
55164
- ...workflowKey ? { workflowId: workflowKey } : {}
55039
+ ...workflowKey ? { workflowKey } : {},
55040
+ ...workflowKey && openVersion ? { baseVersion: openVersion } : {},
55041
+ ...template !== NO_TEMPLATE ? { template } : {}
55165
55042
  });
55166
- }, [draft, refining, start, template, trimmed, workflowKey]);
55167
- const applyDraft = useCallback(() => {
55168
- if (!draft) return;
55169
- const canvas = draftToCanvas(draft.definitionJson);
55170
- const nodes2 = enrichDraftNodes(canvas.nodes, buildOperationSeed);
55171
- registerWorkflowNodeTypes(nodes2);
55172
- setNodes(nodes2, { historyKind: "ai-draft:apply" });
55043
+ }, [openVersion, proposal, template, trimmed, workflowKey]);
55044
+ const openWorkflow = useCallback(
55045
+ (key, version2) => {
55046
+ setOpen(false);
55047
+ proposal.reset();
55048
+ setPrompt("");
55049
+ navigate(
55050
+ `../workflow/${encodeURIComponent(key)}/${encodeURIComponent(version2)}`
55051
+ );
55052
+ },
55053
+ [navigate, proposal]
55054
+ );
55055
+ const handleApply = useCallback(async () => {
55056
+ const applied = await proposal.apply();
55057
+ const appliedStatus = applied ? String(applied.status) : void 0;
55058
+ if (appliedStatus === "APPLIED" && applied?.appliedWorkflowKey) {
55059
+ toast.success(
55060
+ tr(
55061
+ "fluidgrids.builder.aiDraft.appliedVersionToast",
55062
+ "Saved as draft {{version}}",
55063
+ { version: applied.appliedVersion ?? "" }
55064
+ ),
55065
+ {
55066
+ description: tr(
55067
+ "fluidgrids.builder.aiDraft.appliedVersionDescription",
55068
+ "Nothing is live yet. Review it, run a test, then activate it yourself."
55069
+ )
55070
+ }
55071
+ );
55072
+ } else if (appliedStatus === "STALE") {
55073
+ toast.warning(
55074
+ tr(
55075
+ "fluidgrids.builder.aiDraft.staleToast",
55076
+ "The workflow changed after this proposal was made. Ask again."
55077
+ )
55078
+ );
55079
+ }
55080
+ }, [proposal, tr]);
55081
+ const handleRevert = useCallback(
55082
+ async (key, version2) => {
55083
+ setReverting(true);
55084
+ try {
55085
+ const result = await restoreVersion(key, version2);
55086
+ if (!result?.success || !result.workflow) {
55087
+ throw new Error(result?.message || "Failed to restore version");
55088
+ }
55089
+ toast.success(
55090
+ tr(
55091
+ "fluidgrids.builder.aiDraft.revertedToast",
55092
+ "Restored {{version}} as {{newVersion}}",
55093
+ { version: version2, newVersion: result.workflow.version }
55094
+ )
55095
+ );
55096
+ openWorkflow(result.workflow.workflowKey, result.workflow.version);
55097
+ } catch (error62) {
55098
+ toast.error(
55099
+ tr(
55100
+ "fluidgrids.builder.aiDraft.revertFailed",
55101
+ "Could not revert the change"
55102
+ ),
55103
+ {
55104
+ description: error62 instanceof Error ? error62.message : String(error62)
55105
+ }
55106
+ );
55107
+ } finally {
55108
+ setReverting(false);
55109
+ }
55110
+ },
55111
+ [openWorkflow, restoreVersion, tr]
55112
+ );
55113
+ const putOnCanvas = useCallback(() => {
55114
+ if (!current?.definitionJson) return;
55115
+ const canvas = draftToCanvas(current.definitionJson);
55116
+ const canvasNodes = enrichDraftNodes(canvas.nodes, buildOperationSeed);
55117
+ registerWorkflowNodeTypes(canvasNodes);
55118
+ setNodes(canvasNodes, { historyKind: "ai-draft:apply" });
55173
55119
  setEdges(canvas.edges, { skipHistory: true });
55174
55120
  setConfirmReplace(false);
55175
55121
  setOpen(false);
55176
- reset();
55122
+ proposal.reset();
55177
55123
  setPrompt("");
55178
- setRefining(false);
55179
55124
  setTimeout(() => void fitView({ padding: 0.2, duration: 300 }), 50);
55180
55125
  toast.success(
55181
55126
  tr(
@@ -55189,19 +55134,18 @@ function AiDraftPanel({
55189
55134
  )
55190
55135
  }
55191
55136
  );
55192
- }, [draft, fitView, reset, setEdges, setNodes, tr]);
55193
- const handleAccept = useCallback(() => {
55137
+ }, [current, fitView, proposal, setEdges, setNodes, tr]);
55138
+ const handlePutOnCanvas = useCallback(() => {
55194
55139
  if (canvasHasWork(nodes)) setConfirmReplace(true);
55195
- else applyDraft();
55196
- }, [applyDraft, nodes]);
55197
- const handleRefine = useCallback(() => {
55198
- setRefining(true);
55199
- setPrompt("");
55200
- }, []);
55201
- const handleReject = useCallback(() => {
55202
- reset();
55203
- setRefining(false);
55204
- }, [reset]);
55140
+ else putOnCanvas();
55141
+ }, [nodes, putOnCanvas]);
55142
+ const handleReject = useCallback(async () => {
55143
+ await proposal.reject();
55144
+ }, [proposal]);
55145
+ const startOver = useCallback(() => {
55146
+ proposal.reset();
55147
+ }, [proposal]);
55148
+ const finished = status !== void 0 && status !== "PROCESSING" && status !== "PROPOSED";
55205
55149
  return /* @__PURE__ */ jsxs(Fragment, { children: [
55206
55150
  /* @__PURE__ */ jsxs(Sheet, { open: open2, onOpenChange: setOpen, children: [
55207
55151
  /* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
@@ -55234,16 +55178,19 @@ function AiDraftPanel({
55234
55178
  children: [
55235
55179
  /* @__PURE__ */ jsxs(SheetHeader, { children: [
55236
55180
  /* @__PURE__ */ jsx(SheetTitle, { children: tr("fluidgrids.builder.aiDraft.title", "Generate with AI") }),
55237
- /* @__PURE__ */ jsx(SheetDescription, { children: tr(
55181
+ /* @__PURE__ */ jsx(SheetDescription, { children: workflowKey ? tr(
55182
+ "fluidgrids.builder.aiDraft.changeDescription",
55183
+ "Describe what should change. You review the proposed changes before a new draft version is saved."
55184
+ ) : tr(
55238
55185
  "fluidgrids.builder.aiDraft.description",
55239
55186
  "Describe what the workflow should do. You review the draft before anything changes on the canvas."
55240
55187
  ) })
55241
55188
  ] }),
55242
55189
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 px-4", children: [
55243
55190
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
55244
- /* @__PURE__ */ jsx(Label, { htmlFor: "ai-draft-prompt", children: refining ? tr(
55245
- "fluidgrids.builder.aiDraft.refineLabel",
55246
- "What should change in this draft?"
55191
+ /* @__PURE__ */ jsx(Label, { htmlFor: "ai-draft-prompt", children: workflowKey ? tr(
55192
+ "fluidgrids.builder.aiDraft.changeLabel",
55193
+ "What should change?"
55247
55194
  ) : tr(
55248
55195
  "fluidgrids.builder.aiDraft.promptLabel",
55249
55196
  "Describe the workflow"
@@ -55256,7 +55203,7 @@ function AiDraftPanel({
55256
55203
  rows: 5,
55257
55204
  maxLength: AI_DRAFT_MAX_PROMPT_LENGTH,
55258
55205
  value: prompt,
55259
- disabled: isGenerating,
55206
+ disabled: proposal.isWorking,
55260
55207
  placeholder: tr(
55261
55208
  "fluidgrids.builder.aiDraft.promptPlaceholder",
55262
55209
  "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."
@@ -55270,7 +55217,7 @@ function AiDraftPanel({
55270
55217
  AI_DRAFT_MAX_PROMPT_LENGTH
55271
55218
  ] })
55272
55219
  ] }),
55273
- !refining && templates.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
55220
+ !workflowKey && templates.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
55274
55221
  /* @__PURE__ */ jsx(Label, { htmlFor: "ai-draft-template", children: tr(
55275
55222
  "fluidgrids.builder.aiDraft.templateLabel",
55276
55223
  "Starting pattern (optional)"
@@ -55280,7 +55227,7 @@ function AiDraftPanel({
55280
55227
  {
55281
55228
  value: template,
55282
55229
  onValueChange: setTemplate,
55283
- disabled: isGenerating,
55230
+ disabled: proposal.isWorking,
55284
55231
  children: [
55285
55232
  /* @__PURE__ */ jsx(SelectTrigger, { id: "ai-draft-template", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
55286
55233
  /* @__PURE__ */ jsxs(SelectContent, { className: "z-[130]", children: [
@@ -55294,137 +55241,80 @@ function AiDraftPanel({
55294
55241
  }
55295
55242
  )
55296
55243
  ] }),
55244
+ unsavedEdits && /* @__PURE__ */ jsx(
55245
+ "p",
55246
+ {
55247
+ className: "text-xs text-muted-foreground",
55248
+ "data-testid": "ai-draft-unsaved-note",
55249
+ children: tr(
55250
+ "fluidgrids.builder.aiDraft.unsavedNote",
55251
+ "The proposal is based on the saved version. Save first if it should include your latest edits."
55252
+ )
55253
+ }
55254
+ ),
55297
55255
  /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
55298
55256
  /* @__PURE__ */ jsxs(
55299
55257
  Button$1,
55300
55258
  {
55301
55259
  "data-testid": "ai-draft-generate",
55302
55260
  onClick: handleGenerate,
55303
- disabled: !trimmed || isGenerating,
55261
+ disabled: !trimmed || proposal.isWorking,
55304
55262
  className: "flex gap-2",
55305
55263
  children: [
55306
- isGenerating ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Sparkles, { className: "size-4" }),
55307
- refining ? tr(
55308
- "fluidgrids.builder.aiDraft.refineButton",
55309
- "Refine draft"
55310
- ) : tr("fluidgrids.builder.aiDraft.generateButton", "Generate")
55264
+ proposal.isWorking ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Sparkles, { className: "size-4" }),
55265
+ tr("fluidgrids.builder.aiDraft.generateButton", "Generate")
55311
55266
  ]
55312
55267
  }
55313
55268
  ),
55314
- refining && !isGenerating && /* @__PURE__ */ jsx(Button$1, { variant: "ghost", onClick: () => setRefining(false), children: tr("fluidgrids.builder.aiDraft.cancelRefine", "Cancel") })
55269
+ finished && /* @__PURE__ */ jsx(
55270
+ Button$1,
55271
+ {
55272
+ variant: "ghost",
55273
+ "data-testid": "ai-draft-start-over",
55274
+ onClick: startOver,
55275
+ children: tr("fluidgrids.builder.aiDraft.startOver", "Start over")
55276
+ }
55277
+ )
55315
55278
  ] }),
55316
- isGenerating && /* @__PURE__ */ jsx(
55317
- "p",
55318
- {
55319
- className: "text-sm text-muted-foreground",
55320
- "data-testid": "ai-draft-progress",
55321
- children: tr(
55322
- "fluidgrids.builder.aiDraft.generating",
55323
- "Designing your workflow. This usually takes under a minute."
55324
- )
55325
- }
55326
- ),
55327
- (error62 || timedOut) && /* @__PURE__ */ jsx(
55279
+ (proposal.error || proposal.timedOut) && /* @__PURE__ */ jsx(
55328
55280
  "div",
55329
55281
  {
55330
55282
  role: "alert",
55331
55283
  className: "rounded-md border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bg-subtle)] p-3 text-sm text-[var(--color-status-error-text)]",
55332
- children: timedOut ? tr(
55284
+ children: proposal.timedOut ? tr(
55333
55285
  "fluidgrids.builder.aiDraft.timedOut",
55334
55286
  "The draft is taking longer than expected. Try again in a moment."
55335
- ) : error62
55336
- }
55337
- ),
55338
- failed && /* @__PURE__ */ jsxs(
55339
- "div",
55340
- {
55341
- role: "alert",
55342
- "data-testid": "ai-draft-failed",
55343
- className: "flex flex-col gap-2 rounded-md border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bg-subtle)] p-3 text-sm text-[var(--color-status-error-text)]",
55344
- children: [
55345
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: generation?.message ?? tr(
55346
- "fluidgrids.builder.aiDraft.failed",
55347
- "The workflow could not be generated."
55348
- ) }),
55349
- (generation?.validation?.errors ?? []).length > 0 && /* @__PURE__ */ jsx("ul", { className: "list-disc pl-4", children: generation?.validation?.errors.map((message) => /* @__PURE__ */ jsx("li", { children: message }, message)) }),
55350
- /* @__PURE__ */ jsx("span", { children: tr(
55351
- "fluidgrids.builder.aiDraft.failedHint",
55352
- "Try describing the steps more concretely, then generate again."
55353
- ) })
55354
- ]
55287
+ ) : proposal.error
55355
55288
  }
55356
55289
  ),
55357
- draft && /* @__PURE__ */ jsxs(
55358
- "div",
55359
- {
55360
- "data-testid": "ai-draft-result",
55361
- className: "flex flex-col gap-3 rounded-lg border border-border bg-card p-3",
55362
- children: [
55363
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
55364
- /* @__PURE__ */ jsx("span", { className: "font-semibold text-card-foreground", children: draft.name }),
55365
- draft.explanation && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: draft.explanation })
55366
- ] }),
55367
- /* @__PURE__ */ jsx(
55368
- "ol",
55369
- {
55370
- className: "flex flex-col gap-1.5",
55371
- "data-testid": "ai-draft-nodes",
55372
- children: draftNodes.map((node2, index) => /* @__PURE__ */ jsxs(
55373
- "li",
55374
- {
55375
- className: "flex items-center justify-between gap-2 text-sm",
55376
- children: [
55377
- /* @__PURE__ */ jsxs("span", { className: "truncate text-card-foreground", children: [
55378
- index + 1,
55379
- ". ",
55380
- node2.label
55381
- ] }),
55382
- /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "shrink-0", children: shortNodeType(node2.type) })
55383
- ]
55384
- },
55385
- node2.id
55386
- ))
55387
- }
55388
- ),
55389
- (generation?.validation?.warnings ?? []).length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-[var(--color-status-warning-border)] bg-[var(--color-status-warning-bg-subtle)] p-2 text-xs text-[var(--color-status-warning-text)]", children: [
55390
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: tr(
55391
- "fluidgrids.builder.aiDraft.warningsTitle",
55392
- "Still to configure:"
55393
- ) }),
55394
- /* @__PURE__ */ jsx("ul", { className: "list-disc pl-4", children: generation?.validation?.warnings.map((message) => /* @__PURE__ */ jsx("li", { children: message }, message)) })
55395
- ] }),
55396
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
55397
- /* @__PURE__ */ jsx(
55398
- Button$1,
55399
- {
55400
- "data-testid": "ai-draft-accept",
55401
- onClick: handleAccept,
55402
- className: "flex gap-2",
55403
- children: tr("fluidgrids.builder.aiDraft.accept", "Add to canvas")
55404
- }
55405
- ),
55406
- /* @__PURE__ */ jsx(
55407
- Button$1,
55408
- {
55409
- variant: "outline",
55410
- "data-testid": "ai-draft-refine",
55411
- onClick: handleRefine,
55412
- children: tr("fluidgrids.builder.aiDraft.refine", "Refine")
55413
- }
55414
- ),
55415
- /* @__PURE__ */ jsx(
55416
- Button$1,
55417
- {
55418
- variant: "ghost",
55419
- "data-testid": "ai-draft-reject",
55420
- onClick: handleReject,
55421
- children: tr("fluidgrids.builder.aiDraft.reject", "Discard")
55422
- }
55423
- )
55424
- ] })
55425
- ]
55426
- }
55427
- )
55290
+ current && /* @__PURE__ */ jsxs("div", { "data-testid": "ai-draft-result", children: [
55291
+ /* @__PURE__ */ jsx(
55292
+ WorkflowProposalCard,
55293
+ {
55294
+ proposal: current,
55295
+ isDeciding: proposal.isDeciding || reverting,
55296
+ onApply: () => void handleApply(),
55297
+ onReject: () => void handleReject(),
55298
+ onCancel: () => void proposal.cancel(),
55299
+ onOpenWorkflow: openWorkflow,
55300
+ onRevert: (key, version2) => void handleRevert(key, version2)
55301
+ }
55302
+ ),
55303
+ status === "PROPOSED" && !workflowKey && /* @__PURE__ */ jsx(
55304
+ Button$1,
55305
+ {
55306
+ variant: "ghost",
55307
+ size: "sm",
55308
+ className: "mt-2",
55309
+ "data-testid": "ai-draft-accept",
55310
+ onClick: handlePutOnCanvas,
55311
+ children: tr(
55312
+ "fluidgrids.builder.aiDraft.putOnCanvas",
55313
+ "Add to canvas without saving"
55314
+ )
55315
+ }
55316
+ )
55317
+ ] })
55428
55318
  ] })
55429
55319
  ]
55430
55320
  }
@@ -55447,7 +55337,7 @@ function AiDraftPanel({
55447
55337
  AlertDialogAction,
55448
55338
  {
55449
55339
  "data-testid": "ai-draft-replace-confirm",
55450
- onClick: applyDraft,
55340
+ onClick: putOnCanvas,
55451
55341
  children: tr("fluidgrids.builder.aiDraft.replaceConfirm", "Replace")
55452
55342
  }
55453
55343
  )
@@ -1,5 +1,5 @@
1
1
  import { isGuidedTourIntent, pickGuidedTour, withoutGuidedTourIntent, isCreateIntent, CreateWorkflowRefusalDialog, decideMissingWorkflow } from './chunk-CSBKB5EX.js';
2
- import { useInstalledNodes, buildNodeCheck, FlowBuilderPage, buildRequiredNodeTypes } from './chunk-227PQY53.js';
2
+ import { useInstalledNodes, buildNodeCheck, FlowBuilderPage, buildRequiredNodeTypes } from './chunk-JH6YDVT5.js';
3
3
  import { useLazyWorkflowDefinition, useCreateWorkflow } from './chunk-63BP6O6E.js';
4
4
  import { useFlowStore, refetchStoreNodes, BILLING_PLANS_PATH, mergeStickyNotes, blankWorkflowCreateInput, classifyCreateRefusal } from './chunk-5LKZ4PNK.js';
5
5
  import { useTr } from './chunk-UULVIT25.js';
@@ -1,4 +1,4 @@
1
- import { useImportWorkflowTemplate, FlowBuilderPage, useWorkflowTemplates, TemplateCard } from './chunk-227PQY53.js';
1
+ import { useImportWorkflowTemplate, FlowBuilderPage, useWorkflowTemplates, TemplateCard } from './chunk-JH6YDVT5.js';
2
2
  import { QuotaExhaustedDialog } from './chunk-P2NRMJ7Z.js';
3
3
  import { useWorkflowDefinitions, useDuplicateWorkflow } from './chunk-63BP6O6E.js';
4
4
  import { templatesControls } from './chunk-D3UWK7GY.js';
@@ -1,10 +1,10 @@
1
1
  import './chunk-AD3BXTJS.js';
2
2
  export { ExecutionsListPage } from './chunk-H7CJXSMI.js';
3
- export { ExecutionDetailPage } from './chunk-ZWLEOEIP.js';
3
+ export { ExecutionDetailPage } from './chunk-CPEIOFYR.js';
4
4
  import './chunk-SSJIK6WS.js';
5
5
  import './chunk-UUUDSVC7.js';
6
6
  import './chunk-5ERAJC2I.js';
7
- import './chunk-MODO24HY.js';
7
+ import './chunk-JAKWVVVQ.js';
8
8
  import './chunk-QGXA2USP.js';
9
9
  import './chunk-63BP6O6E.js';
10
10
  import './chunk-D3UWK7GY.js';