@burdenoff/fluidgrids-fe-sdk 2026.1023.5 → 2026.1024.2

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 (36) hide show
  1. package/dist/{ExecutionDetailPage-FDX242B5.js → ExecutionDetailPage-RF5NR2ND.js} +7 -7
  2. package/dist/{ExecutionsListPage-DYKEZ4KN.js → ExecutionsListPage-L447L5LX.js} +7 -7
  3. package/dist/{WorkflowBuilder-NITC5SKO.js → WorkflowBuilder-AWCE62C7.js} +9 -9
  4. package/dist/{WorkflowsList-7IHK3HTY.js → WorkflowsList-QFVCVGRY.js} +7 -7
  5. package/dist/{chunk-VMVY65DC.js → chunk-3GHYYFBA.js} +5 -5
  6. package/dist/{chunk-PZP5UYE7.js → chunk-4H7MRDNX.js} +77 -5
  7. package/dist/{chunk-XCQROWIZ.js → chunk-65DVJILQ.js} +2 -2
  8. package/dist/{chunk-EB344COE.js → chunk-7PC3ZBYU.js} +3 -3
  9. package/dist/{chunk-UKMXGBU3.js → chunk-7ST3XB36.js} +1 -1
  10. package/dist/{chunk-KAOLPDNX.js → chunk-DSBU5MH4.js} +1 -1
  11. package/dist/{chunk-3VF67N3X.js → chunk-GCOWR7J4.js} +1 -1
  12. package/dist/{chunk-CZURRUDX.js → chunk-GD7CSKFD.js} +4 -1
  13. package/dist/{chunk-TRYYDUCJ.js → chunk-GV76OCBP.js} +4 -4
  14. package/dist/{chunk-BT3ABHVR.js → chunk-I2DSSBIK.js} +5 -5
  15. package/dist/{chunk-2QTKDR23.js → chunk-INHBJPVE.js} +1 -1
  16. package/dist/{chunk-JG7UFL5M.js → chunk-IZMYI6QO.js} +621 -64
  17. package/dist/{chunk-7XBCGNUG.js → chunk-L37ZAZE4.js} +2 -2
  18. package/dist/{chunk-HAYSY6TZ.js → chunk-OQEQ3AIA.js} +1 -1
  19. package/dist/{chunk-OKWH2CHF.js → chunk-PMQECGSD.js} +3 -3
  20. package/dist/{chunk-LLPLOWMA.js → chunk-QIAXIFLW.js} +2 -2
  21. package/dist/{chunk-J3XDX5HG.js → chunk-S5ZKDRBV.js} +2 -2
  22. package/dist/{chunk-MR4OTF7D.js → chunk-T7X3HDNX.js} +2 -2
  23. package/dist/{chunk-HZC4TPPI.js → chunk-VZQSCUD4.js} +3 -3
  24. package/dist/{chunk-IULOX3NQ.js → chunk-XFVNTWM2.js} +1 -1
  25. package/dist/{chunk-NJZAWUGC.js → chunk-XXAVXVBW.js} +1 -1
  26. package/dist/executions.js +9 -9
  27. package/dist/history.js +4 -4
  28. package/dist/index.d.ts +800 -25
  29. package/dist/index.js +31 -31
  30. package/dist/nodes.js +2 -2
  31. package/dist/observability.js +4 -4
  32. package/dist/settings.js +4 -4
  33. package/dist/templates.js +8 -8
  34. package/dist/workflow-builder.js +10 -10
  35. package/dist/workflows-list.js +7 -7
  36. package/package.json +1 -1
@@ -2,16 +2,16 @@ import { deepClone, applyTemplateInputs } from './chunk-GEMNVFPK.js';
2
2
  import { Input as Input$1 } 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
- import { QuotaExhaustedDialog } from './chunk-3VF67N3X.js';
6
- import { EntityTagPicker } from './chunk-IULOX3NQ.js';
7
- import { useWorkflowVersions, useRestoreWorkflowVersion, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-J3XDX5HG.js';
5
+ import { QuotaExhaustedDialog } from './chunk-GCOWR7J4.js';
6
+ import { EntityTagPicker } from './chunk-XFVNTWM2.js';
7
+ import { useWorkflowVersions, useRestoreWorkflowVersion, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-S5ZKDRBV.js';
8
8
  import { FluidGridsDocsLink } from './chunk-4VJ3MDOV.js';
9
- import { RUNS_QUOTA_NAME, throwIfResultError, useFlowStore, BILLING_PLANS_PATH, emitUpgradeClicked, FluidGridsContext, emitWorkflowCreated, emitTemplateUsed, ensureNodeConfigsLoaded, NODE_CONFIGS, registerWorkflowNodeTypes, emitNodeInstalled, useNodeExecution, onNodeTypesUpdate, NODE_TYPES, getNodeMetadata, useIsValidConnection, refetchStoreNodes, AVAILABLE_NODES, useInsertNode, NODE_TYPE_DRAG_DATA_FORMAT, NODE_OPERATION_DRAG_DATA_FORMAT, useSaveWorkflow, SdkPermissionButton, FLUIDGRIDS_PERMISSIONS, STICKY_NOTE_TYPE, fieldsFromValue, getNodeDetail, useFluidGridsAnalytics, isNodeTrigger, buildStickyNode, fieldsFromIO, ConnectionSelector, structuredParameterType, nodeAccent, NodeTypeIcon, NODES_METADATA, resolveNodeOperation, declaredOutputFields, pathToExpression, ExpressionEditor, formatParameterValue, parseParameterValue, isNodeConfigsInitialized, resolveUiSpec, isImageIcon, NODES, synchronizeOperationUpdate, NodePropertyPanel, openRunQuotaUpgradePrompt, describeOperationError, isTriggerOperation, canRenderOperationPanel, resolveOperationName, resolveInputHandles } from './chunk-MR4OTF7D.js';
9
+ import { RUNS_QUOTA_NAME, throwIfResultError, useFlowStore, BILLING_PLANS_PATH, emitUpgradeClicked, FluidGridsContext, emitWorkflowCreated, emitTemplateUsed, ensureNodeConfigsLoaded, NODE_CONFIGS, registerWorkflowNodeTypes, emitNodeInstalled, useNodeExecution, onNodeTypesUpdate, NODE_TYPES, getNodeMetadata, useIsValidConnection, refetchStoreNodes, AVAILABLE_NODES, useInsertNode, NODE_TYPE_DRAG_DATA_FORMAT, NODE_OPERATION_DRAG_DATA_FORMAT, useSaveWorkflow, SdkPermissionButton, FLUIDGRIDS_PERMISSIONS, STICKY_NOTE_TYPE, fieldsFromValue, getNodeDetail, useFluidGridsAnalytics, buildOperationSeed, isNodeTrigger, buildStickyNode, fieldsFromIO, ConnectionSelector, structuredParameterType, nodeAccent, NodeTypeIcon, NODES_METADATA, resolveNodeOperation, declaredOutputFields, pathToExpression, ExpressionEditor, formatParameterValue, parseParameterValue, isNodeConfigsInitialized, resolveUiSpec, isImageIcon, NODES, synchronizeOperationUpdate, NodePropertyPanel, openRunQuotaUpgradePrompt, describeOperationError, isTriggerOperation, canRenderOperationPanel, resolveOperationName, resolveInputHandles } from './chunk-T7X3HDNX.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, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument } from './chunk-CZURRUDX.js';
14
+ import { WorkspaceRunsQuotaDocument, NodeSchemaIntrospectionDocument, useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, WorkflowGenerationTemplatesDocument, WorkflowGenerationDocument, GenerateWorkflowFromPromptDocument, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument } from './chunk-GD7CSKFD.js';
15
15
  import { useTr } from './chunk-UULVIT25.js';
16
16
  import { getFluidGridsClient, waitForFluidGridsClient, getFluidGridsClientSafe } from './chunk-NTWG3CJX.js';
17
17
  import * as React3 from 'react';
@@ -19,13 +19,13 @@ import React3__default, { forwardRef, lazy, memo, createContext, useState, useRe
19
19
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
20
20
  import { gql } from '@apollo/client';
21
21
  import { cn as cn$1 } from '@burdenoff/fe-libs/utils';
22
- import { Card as Card$1, Badge, CardContent as CardContent$1, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Button as Button$1, Input, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, TooltipProvider, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, DropdownMenuItem, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Popover, PopoverTrigger, PopoverContent, Tooltip, TooltipTrigger, TooltipContent, Toaster, Tabs, TabsList, TabsTrigger, TabsContent, CardHeader as CardHeader$1, CardTitle as CardTitle$1, CardDescription as CardDescription$1, ScrollArea, Label, DropdownMenuLabel, DropdownMenuSeparator, Textarea } from '@burdenoff/fe-libs/ui';
22
+ import { Card as Card$1, Badge, CardContent as CardContent$1, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Button as Button$1, Input, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, Label, Textarea, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, TooltipProvider, DropdownMenuItem, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Popover, PopoverTrigger, PopoverContent, Tooltip, TooltipTrigger, TooltipContent, Toaster, Tabs, TabsList, TabsTrigger, TabsContent, CardHeader as CardHeader$1, CardTitle as CardTitle$1, CardDescription as CardDescription$1, ScrollArea, DropdownMenuLabel, DropdownMenuSeparator } from '@burdenoff/fe-libs/ui';
23
23
  import { Link, useNavigate, useParams } from 'react-router';
24
24
  import { useEventBus } from '@burdenoff/fe-libs/shared/events';
25
25
  import { useQuery, useMutation } from '@apollo/client/react';
26
26
  import { gql as gql$1 } from '@apollo/client/core';
27
27
  import 'jszip';
28
- import { Sparkles, Star, Download, Eye, History, GitCompareArrows, Loader2, Play, RotateCcw, Trash2, Maximize2, Undo2, Redo2, LayoutGrid, Keyboard, Plus, GitBranch, AlertCircle, Search, RefreshCw, CheckCircle2, Package, Info, Copy, BadgeCheck, DollarSign, Type, Code, Hash, List, Braces, Workflow, PlusCircle, ChevronDown, ChevronRight, Layers, X, AlertTriangle } from 'lucide-react';
28
+ import { Sparkles, Star, Download, Eye, Loader2, History, GitCompareArrows, Play, RotateCcw, Trash2, Maximize2, Undo2, Redo2, LayoutGrid, Keyboard, Plus, GitBranch, AlertCircle, Search, RefreshCw, CheckCircle2, Package, Info, Copy, BadgeCheck, DollarSign, Type, Code, Hash, List, Braces, Workflow, PlusCircle, ChevronDown, ChevronRight, Layers, X, AlertTriangle } from 'lucide-react';
29
29
  import { Icon } from '@iconify/react';
30
30
  import { toast } from 'sonner';
31
31
  import { useShallow, shallow } from 'zustand/shallow';
@@ -6456,8 +6456,8 @@ var reducer = (state, action) => {
6456
6456
  if (toastId) {
6457
6457
  addToRemoveQueue(toastId);
6458
6458
  } else {
6459
- state.toasts.forEach((toast10) => {
6460
- addToRemoveQueue(toast10.id);
6459
+ state.toasts.forEach((toast11) => {
6460
+ addToRemoveQueue(toast11.id);
6461
6461
  });
6462
6462
  }
6463
6463
  return {
@@ -7833,7 +7833,7 @@ function VisualMapperContent({
7833
7833
  mapping,
7834
7834
  onMappingChange
7835
7835
  }) {
7836
- const { toast: toast10 } = useToast();
7836
+ const { toast: toast11 } = useToast();
7837
7837
  const tr = useTr();
7838
7838
  const reactFlowWrapper = useRef(null);
7839
7839
  const [selectedSourceNode, setSelectedSourceNode] = useState(null);
@@ -7853,7 +7853,7 @@ function VisualMapperContent({
7853
7853
  targetOperation
7854
7854
  );
7855
7855
  if (generatedNodes.length === 0) {
7856
- toast10({
7856
+ toast11({
7857
7857
  title: tr(
7858
7858
  "fluidgrids.builder.visualMapper.noFieldsFoundTitle",
7859
7859
  "No fields found"
@@ -7879,7 +7879,7 @@ function VisualMapperContent({
7879
7879
  targetNode,
7880
7880
  targetOperation,
7881
7881
  setNodes,
7882
- toast10,
7882
+ toast11,
7883
7883
  tr
7884
7884
  ]);
7885
7885
  useEffect(() => {
@@ -7986,7 +7986,7 @@ function VisualMapperContent({
7986
7986
  }
7987
7987
  onMappingChange(updatedMapping);
7988
7988
  if (isCompatible) {
7989
- toast10({
7989
+ toast11({
7990
7990
  title: tr(
7991
7991
  "fluidgrids.builder.visualMapper.mappingCreatedTitle",
7992
7992
  "Mapping created"
@@ -7994,7 +7994,7 @@ function VisualMapperContent({
7994
7994
  description: `Mapped ${sourcePath} (${sourceType}) to ${targetPath} (${targetType})`
7995
7995
  });
7996
7996
  } else {
7997
- toast10({
7997
+ toast11({
7998
7998
  title: tr(
7999
7999
  "fluidgrids.builder.visualMapper.typeMismatchTitle",
8000
8000
  "Type mismatch warning"
@@ -8004,7 +8004,7 @@ function VisualMapperContent({
8004
8004
  });
8005
8005
  }
8006
8006
  } else {
8007
- toast10({
8007
+ toast11({
8008
8008
  title: tr(
8009
8009
  "fluidgrids.builder.visualMapper.invalidMappingTitle",
8010
8010
  "Invalid mapping"
@@ -8017,7 +8017,7 @@ function VisualMapperContent({
8017
8017
  });
8018
8018
  }
8019
8019
  },
8020
- [nodes, mapping, onMappingChange, toast10, tr, setEdges]
8020
+ [nodes, mapping, onMappingChange, toast11, tr, setEdges]
8021
8021
  );
8022
8022
  const onEdgeDelete = useCallback(
8023
8023
  (edge) => {
@@ -8074,7 +8074,7 @@ function VisualMapperContent({
8074
8074
  const handleSaveMappingDialog = (expression, targetFieldName) => {
8075
8075
  const targetPath = targetFieldName || selectedTargetNode?.data?.path;
8076
8076
  if (!targetPath) {
8077
- toast10({
8077
+ toast11({
8078
8078
  title: tr(
8079
8079
  "fluidgrids.builder.visualMapper.noTargetFieldTitle",
8080
8080
  "No target field specified"
@@ -8106,7 +8106,7 @@ function VisualMapperContent({
8106
8106
  }
8107
8107
  onMappingChange(updatedMapping);
8108
8108
  const mappingType = detectMappingType(expression);
8109
- toast10({
8109
+ toast11({
8110
8110
  title: `${mappingType.charAt(0).toUpperCase() + mappingType.slice(1)} mapping created`,
8111
8111
  description: `${tr("fluidgrids.builder.visualMapper.appliedMappingTo", "Applied mapping to")} ${targetPath}`
8112
8112
  });
@@ -8405,7 +8405,7 @@ function MappingEditor({
8405
8405
  setEdges,
8406
8406
  edgeId
8407
8407
  }) {
8408
- const { toast: toast10 } = useToast();
8408
+ const { toast: toast11 } = useToast();
8409
8409
  const tr = useTr();
8410
8410
  const [mappingJson, setMappingJson] = useState({});
8411
8411
  const [jsonDataString, setJsonDataString] = useState(
@@ -8529,7 +8529,7 @@ function MappingEditor({
8529
8529
  const handleSaveMapping = () => {
8530
8530
  try {
8531
8531
  checkJsonValidity();
8532
- toast10({
8532
+ toast11({
8533
8533
  title: tr(
8534
8534
  "fluidgrids.builder.mappingEditor.mappingSavedTitle",
8535
8535
  "Mapping saved"
@@ -8540,7 +8540,7 @@ function MappingEditor({
8540
8540
  )
8541
8541
  });
8542
8542
  } catch (error) {
8543
- toast10({
8543
+ toast11({
8544
8544
  title: tr(
8545
8545
  "fluidgrids.builder.mappingEditor.errorSavingTitle",
8546
8546
  "Error saving mapping"
@@ -8559,7 +8559,7 @@ function MappingEditor({
8559
8559
  const parsedData = checkJsonValidity();
8560
8560
  const result = await applyMapping(parsedData, sampleInput);
8561
8561
  setPreviewOutput(result);
8562
- toast10({
8562
+ toast11({
8563
8563
  title: tr(
8564
8564
  "fluidgrids.builder.mappingEditor.mappingTestedTitle",
8565
8565
  "Mapping tested"
@@ -8571,7 +8571,7 @@ function MappingEditor({
8571
8571
  });
8572
8572
  } catch (error) {
8573
8573
  console.error("Test mapping error:", error);
8574
- toast10({
8574
+ toast11({
8575
8575
  title: tr(
8576
8576
  "fluidgrids.builder.mappingEditor.mappingTestFailedTitle",
8577
8577
  "Mapping test failed"
@@ -19726,6 +19726,559 @@ function VersionHistoryPanel({ className }) {
19726
19726
  )
19727
19727
  ] });
19728
19728
  }
19729
+
19730
+ // src/builder/lib/ai-draft.ts
19731
+ var AI_DRAFT_POLL_INTERVAL_MS = 2500;
19732
+ var AI_DRAFT_MAX_WAIT_MS = 6 * 60 * 1e3;
19733
+ var AI_DRAFT_MAX_PROMPT_LENGTH = 4e3;
19734
+ function isRecord4(value) {
19735
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19736
+ }
19737
+ function stringOr(value, fallback) {
19738
+ return typeof value === "string" && value.trim() !== "" ? value : fallback;
19739
+ }
19740
+ function draftToCanvas(definitionJson) {
19741
+ const data = isRecord4(definitionJson) && isRecord4(definitionJson.data) ? definitionJson.data : {};
19742
+ const rawNodes = Array.isArray(data.nodes) ? data.nodes : [];
19743
+ const rawEdges = Array.isArray(data.edges) ? data.edges : [];
19744
+ const nodes = rawNodes.filter(isRecord4).flatMap((raw) => {
19745
+ if (typeof raw.id !== "string" || typeof raw.type !== "string") return [];
19746
+ const position = isRecord4(raw.position) ? {
19747
+ x: typeof raw.position.x === "number" ? raw.position.x : 0,
19748
+ y: typeof raw.position.y === "number" ? raw.position.y : 0
19749
+ } : { x: 0, y: 0 };
19750
+ const nodeData = isRecord4(raw.data) ? raw.data : {};
19751
+ const node = {
19752
+ ...raw,
19753
+ id: raw.id,
19754
+ type: raw.type,
19755
+ position,
19756
+ data: nodeData,
19757
+ selected: false
19758
+ };
19759
+ return [node];
19760
+ });
19761
+ const ids = new Set(nodes.map((node) => node.id));
19762
+ const edges = rawEdges.filter(isRecord4).flatMap((raw) => {
19763
+ if (typeof raw.source !== "string" || typeof raw.target !== "string") {
19764
+ return [];
19765
+ }
19766
+ if (!ids.has(raw.source) || !ids.has(raw.target)) return [];
19767
+ const edge = {
19768
+ id: stringOr(raw.id, `e-${raw.source}-${raw.target}`),
19769
+ source: raw.source,
19770
+ target: raw.target,
19771
+ type: stringOr(raw.type, "deletable"),
19772
+ ...typeof raw.sourceHandle === "string" ? { sourceHandle: raw.sourceHandle } : {},
19773
+ ...typeof raw.targetHandle === "string" ? { targetHandle: raw.targetHandle } : {}
19774
+ };
19775
+ return [edge];
19776
+ });
19777
+ return { nodes, edges };
19778
+ }
19779
+ function enrichDraftNodes(nodes, seed) {
19780
+ return nodes.map((node) => {
19781
+ const data = isRecord4(node.data) ? node.data : {};
19782
+ const rootOperation = node.operation_name;
19783
+ let operationName;
19784
+ if (typeof data.operation_name === "string") {
19785
+ operationName = data.operation_name;
19786
+ } else if (typeof rootOperation === "string") {
19787
+ operationName = rootOperation;
19788
+ }
19789
+ if (!node.type || !operationName) return node;
19790
+ const seeded = seed(node.type, operationName, data);
19791
+ if (!seeded) return node;
19792
+ const seededArgs = isRecord4(seeded.args) ? seeded.args : {};
19793
+ const defaults = isRecord4(seededArgs.state) ? seededArgs.state : {};
19794
+ const draftArgs = isRecord4(data.args) ? data.args : {};
19795
+ const draftState = isRecord4(draftArgs.state) ? draftArgs.state : {};
19796
+ return {
19797
+ ...node,
19798
+ data: {
19799
+ ...seeded,
19800
+ args: { ...seededArgs, state: { ...defaults, ...draftState } }
19801
+ }
19802
+ };
19803
+ });
19804
+ }
19805
+ function summarizeDraftNodes(definitionJson) {
19806
+ return draftToCanvas(definitionJson).nodes.map((node) => {
19807
+ const data = isRecord4(node.data) ? node.data : {};
19808
+ const label = stringOr(
19809
+ node.label,
19810
+ stringOr(data.label, node.type ?? node.id)
19811
+ );
19812
+ return { id: node.id, type: node.type ?? "", label };
19813
+ });
19814
+ }
19815
+ function shortNodeType(type) {
19816
+ const afterColon = type.includes(":") ? type.split(":").pop() : type;
19817
+ const afterSlash = afterColon?.includes("/") ? afterColon.split("/").pop() : afterColon;
19818
+ return afterSlash ?? type;
19819
+ }
19820
+ function shouldKeepPolling(status, startedAt, now = Date.now()) {
19821
+ return status === "PROCESSING" && now - startedAt < AI_DRAFT_MAX_WAIT_MS;
19822
+ }
19823
+ function canvasHasWork(nodes) {
19824
+ return nodes.length > 1;
19825
+ }
19826
+
19827
+ // src/builder/hooks/use-ai-workflow-draft.ts
19828
+ function messageOf(error) {
19829
+ return error instanceof Error ? error.message : String(error);
19830
+ }
19831
+ function useAiWorkflowDraft() {
19832
+ const [generation, setGeneration] = useState(
19833
+ null
19834
+ );
19835
+ const [isGenerating, setIsGenerating] = useState(false);
19836
+ const [error, setError] = useState(null);
19837
+ const [timedOut, setTimedOut] = useState(false);
19838
+ const runRef = useRef(0);
19839
+ const timerRef = useRef(null);
19840
+ const clearTimer = useCallback(() => {
19841
+ if (timerRef.current) clearTimeout(timerRef.current);
19842
+ timerRef.current = null;
19843
+ }, []);
19844
+ const reset = useCallback(() => {
19845
+ runRef.current++;
19846
+ clearTimer();
19847
+ setGeneration(null);
19848
+ setIsGenerating(false);
19849
+ setError(null);
19850
+ setTimedOut(false);
19851
+ }, [clearTimer]);
19852
+ useEffect(
19853
+ () => () => {
19854
+ runRef.current++;
19855
+ clearTimer();
19856
+ },
19857
+ [clearTimer]
19858
+ );
19859
+ const poll = useCallback(
19860
+ async (run, id, startedAt) => {
19861
+ if (run !== runRef.current) return;
19862
+ try {
19863
+ const result = throwIfResultError(
19864
+ await getFluidGridsClient().query({
19865
+ query: WorkflowGenerationDocument,
19866
+ variables: { id },
19867
+ fetchPolicy: "network-only"
19868
+ })
19869
+ );
19870
+ if (run !== runRef.current) return;
19871
+ const next = result.data?.workflowGeneration ?? null;
19872
+ if (next) setGeneration(next);
19873
+ const status = next ? String(next.status) : void 0;
19874
+ if (shouldKeepPolling(status, startedAt)) {
19875
+ timerRef.current = setTimeout(
19876
+ () => void poll(run, id, startedAt),
19877
+ AI_DRAFT_POLL_INTERVAL_MS
19878
+ );
19879
+ return;
19880
+ }
19881
+ if (status === "PROCESSING") setTimedOut(true);
19882
+ setIsGenerating(false);
19883
+ } catch (pollError) {
19884
+ if (run !== runRef.current) return;
19885
+ setError(messageOf(pollError));
19886
+ setIsGenerating(false);
19887
+ }
19888
+ },
19889
+ []
19890
+ );
19891
+ const start = useCallback(
19892
+ async (input) => {
19893
+ const run = ++runRef.current;
19894
+ clearTimer();
19895
+ setGeneration(null);
19896
+ setError(null);
19897
+ setTimedOut(false);
19898
+ setIsGenerating(true);
19899
+ try {
19900
+ const result = throwIfResultError(
19901
+ await getFluidGridsClient().mutate({
19902
+ mutation: GenerateWorkflowFromPromptDocument,
19903
+ variables: {
19904
+ input: {
19905
+ prompt: input.prompt,
19906
+ ...input.template ? { template: input.template } : {},
19907
+ ...input.baseDefinitionJson ? { baseDefinitionJson: input.baseDefinitionJson } : {},
19908
+ ...input.workflowId ? { workflowId: input.workflowId } : {}
19909
+ }
19910
+ }
19911
+ })
19912
+ );
19913
+ const id = result.data?.generateWorkflowFromPrompt?.id;
19914
+ if (!id) throw new Error("The generation did not start");
19915
+ if (run !== runRef.current) return;
19916
+ timerRef.current = setTimeout(
19917
+ () => void poll(run, id, Date.now()),
19918
+ AI_DRAFT_POLL_INTERVAL_MS
19919
+ );
19920
+ } catch (startError) {
19921
+ if (run !== runRef.current) return;
19922
+ setError(messageOf(startError));
19923
+ setIsGenerating(false);
19924
+ }
19925
+ },
19926
+ [clearTimer, poll]
19927
+ );
19928
+ return { generation, isGenerating, error, timedOut, start, reset };
19929
+ }
19930
+ function useAiWorkflowTemplates(enabled) {
19931
+ const [templates, setTemplates] = useState(
19932
+ []
19933
+ );
19934
+ useEffect(() => {
19935
+ if (!enabled) return;
19936
+ let active = true;
19937
+ getFluidGridsClient().query({
19938
+ query: WorkflowGenerationTemplatesDocument,
19939
+ fetchPolicy: "cache-first"
19940
+ }).then((result) => {
19941
+ if (active)
19942
+ setTemplates(result.data?.workflowGenerationTemplates ?? []);
19943
+ }).catch(() => {
19944
+ if (active) setTemplates([]);
19945
+ });
19946
+ return () => {
19947
+ active = false;
19948
+ };
19949
+ }, [enabled]);
19950
+ return templates;
19951
+ }
19952
+ var NO_TEMPLATE = "__none__";
19953
+ function AiDraftPanel({
19954
+ className,
19955
+ compact = false
19956
+ }) {
19957
+ const tr = useTr();
19958
+ const { id: workflowKey } = useParams();
19959
+ const { fitView } = useReactFlow();
19960
+ const [open, setOpen] = useState(false);
19961
+ const [prompt, setPrompt] = useState("");
19962
+ const [template, setTemplate] = useState(NO_TEMPLATE);
19963
+ const [refining, setRefining] = useState(false);
19964
+ const [confirmReplace, setConfirmReplace] = useState(false);
19965
+ const templates = useAiWorkflowTemplates(open);
19966
+ const { generation, isGenerating, error, timedOut, start, reset } = useAiWorkflowDraft();
19967
+ const [nodes, setNodes, setEdges] = useFlowStore(
19968
+ useShallow((s) => [
19969
+ s.workflow.nodes,
19970
+ s.actions.nodes.setNodes,
19971
+ s.actions.edges.setEdges
19972
+ ])
19973
+ );
19974
+ const status = generation ? String(generation.status) : void 0;
19975
+ const draft = status === "COMPLETED" ? generation?.draft ?? null : null;
19976
+ const failed = status === "FAILED";
19977
+ const draftNodes = useMemo(
19978
+ () => draft ? summarizeDraftNodes(draft.definitionJson) : [],
19979
+ [draft]
19980
+ );
19981
+ const trimmed = prompt.trim();
19982
+ const handleGenerate = useCallback(() => {
19983
+ if (!trimmed) return;
19984
+ void start({
19985
+ prompt: trimmed,
19986
+ ...template !== NO_TEMPLATE ? { template } : {},
19987
+ ...refining && draft ? { baseDefinitionJson: draft.definitionJson } : {},
19988
+ ...workflowKey ? { workflowId: workflowKey } : {}
19989
+ });
19990
+ }, [draft, refining, start, template, trimmed, workflowKey]);
19991
+ const applyDraft = useCallback(() => {
19992
+ if (!draft) return;
19993
+ const canvas = draftToCanvas(draft.definitionJson);
19994
+ const nodes2 = enrichDraftNodes(canvas.nodes, buildOperationSeed);
19995
+ registerWorkflowNodeTypes(nodes2);
19996
+ setNodes(nodes2, { historyKind: "ai-draft:apply" });
19997
+ setEdges(canvas.edges, { skipHistory: true });
19998
+ setConfirmReplace(false);
19999
+ setOpen(false);
20000
+ reset();
20001
+ setPrompt("");
20002
+ setRefining(false);
20003
+ setTimeout(() => void fitView({ padding: 0.2, duration: 300 }), 50);
20004
+ toast.success(
20005
+ tr(
20006
+ "fluidgrids.builder.aiDraft.appliedToast",
20007
+ "Draft added to the canvas"
20008
+ ),
20009
+ {
20010
+ description: tr(
20011
+ "fluidgrids.builder.aiDraft.appliedToastDescription",
20012
+ "Review each node's settings, then save the workflow."
20013
+ )
20014
+ }
20015
+ );
20016
+ }, [draft, fitView, reset, setEdges, setNodes, tr]);
20017
+ const handleAccept = useCallback(() => {
20018
+ if (canvasHasWork(nodes)) setConfirmReplace(true);
20019
+ else applyDraft();
20020
+ }, [applyDraft, nodes]);
20021
+ const handleRefine = useCallback(() => {
20022
+ setRefining(true);
20023
+ setPrompt("");
20024
+ }, []);
20025
+ const handleReject = useCallback(() => {
20026
+ reset();
20027
+ setRefining(false);
20028
+ }, [reset]);
20029
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
20030
+ /* @__PURE__ */ jsxs(Sheet, { open, onOpenChange: setOpen, children: [
20031
+ /* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
20032
+ Button$1,
20033
+ {
20034
+ variant: "outline",
20035
+ size: compact ? "icon" : "sm",
20036
+ "data-tour": "fluidgrids-builder-ai-draft",
20037
+ "data-testid": "ai-draft-open",
20038
+ "aria-label": tr(
20039
+ "fluidgrids.builder.aiDraft.openButton",
20040
+ "Generate with AI"
20041
+ ),
20042
+ title: tr(
20043
+ "fluidgrids.builder.aiDraft.openButton",
20044
+ "Generate with AI"
20045
+ ),
20046
+ className: cn$1("flex gap-2", className),
20047
+ children: [
20048
+ /* @__PURE__ */ jsx(Sparkles, { className: "size-4" }),
20049
+ !compact && tr("fluidgrids.builder.aiDraft.openButton", "Generate with AI")
20050
+ ]
20051
+ }
20052
+ ) }),
20053
+ /* @__PURE__ */ jsxs(
20054
+ SheetContent,
20055
+ {
20056
+ side: "right",
20057
+ className: "flex w-full flex-col gap-4 overflow-y-auto sm:max-w-lg",
20058
+ children: [
20059
+ /* @__PURE__ */ jsxs(SheetHeader, { children: [
20060
+ /* @__PURE__ */ jsx(SheetTitle, { children: tr("fluidgrids.builder.aiDraft.title", "Generate with AI") }),
20061
+ /* @__PURE__ */ jsx(SheetDescription, { children: tr(
20062
+ "fluidgrids.builder.aiDraft.description",
20063
+ "Describe what the workflow should do. You review the draft before anything changes on the canvas."
20064
+ ) })
20065
+ ] }),
20066
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 px-4", children: [
20067
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
20068
+ /* @__PURE__ */ jsx(Label, { htmlFor: "ai-draft-prompt", children: refining ? tr(
20069
+ "fluidgrids.builder.aiDraft.refineLabel",
20070
+ "What should change in this draft?"
20071
+ ) : tr(
20072
+ "fluidgrids.builder.aiDraft.promptLabel",
20073
+ "Describe the workflow"
20074
+ ) }),
20075
+ /* @__PURE__ */ jsx(
20076
+ Textarea,
20077
+ {
20078
+ id: "ai-draft-prompt",
20079
+ "data-testid": "ai-draft-prompt",
20080
+ rows: 5,
20081
+ maxLength: AI_DRAFT_MAX_PROMPT_LENGTH,
20082
+ value: prompt,
20083
+ disabled: isGenerating,
20084
+ placeholder: tr(
20085
+ "fluidgrids.builder.aiDraft.promptPlaceholder",
20086
+ "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."
20087
+ ),
20088
+ onChange: (event) => setPrompt(event.target.value)
20089
+ }
20090
+ ),
20091
+ /* @__PURE__ */ jsxs("span", { className: "self-end text-xs text-muted-foreground", children: [
20092
+ prompt.length,
20093
+ "/",
20094
+ AI_DRAFT_MAX_PROMPT_LENGTH
20095
+ ] })
20096
+ ] }),
20097
+ !refining && templates.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
20098
+ /* @__PURE__ */ jsx(Label, { htmlFor: "ai-draft-template", children: tr(
20099
+ "fluidgrids.builder.aiDraft.templateLabel",
20100
+ "Starting pattern (optional)"
20101
+ ) }),
20102
+ /* @__PURE__ */ jsxs(
20103
+ Select,
20104
+ {
20105
+ value: template,
20106
+ onValueChange: setTemplate,
20107
+ disabled: isGenerating,
20108
+ children: [
20109
+ /* @__PURE__ */ jsx(SelectTrigger, { id: "ai-draft-template", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
20110
+ /* @__PURE__ */ jsxs(SelectContent, { className: "z-[130]", children: [
20111
+ /* @__PURE__ */ jsx(SelectItem, { value: NO_TEMPLATE, children: tr(
20112
+ "fluidgrids.builder.aiDraft.noTemplate",
20113
+ "No pattern"
20114
+ ) }),
20115
+ templates.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.id, children: option.name }, option.id))
20116
+ ] })
20117
+ ]
20118
+ }
20119
+ )
20120
+ ] }),
20121
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
20122
+ /* @__PURE__ */ jsxs(
20123
+ Button$1,
20124
+ {
20125
+ "data-testid": "ai-draft-generate",
20126
+ onClick: handleGenerate,
20127
+ disabled: !trimmed || isGenerating,
20128
+ className: "flex gap-2",
20129
+ children: [
20130
+ isGenerating ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Sparkles, { className: "size-4" }),
20131
+ refining ? tr(
20132
+ "fluidgrids.builder.aiDraft.refineButton",
20133
+ "Refine draft"
20134
+ ) : tr("fluidgrids.builder.aiDraft.generateButton", "Generate")
20135
+ ]
20136
+ }
20137
+ ),
20138
+ refining && !isGenerating && /* @__PURE__ */ jsx(Button$1, { variant: "ghost", onClick: () => setRefining(false), children: tr("fluidgrids.builder.aiDraft.cancelRefine", "Cancel") })
20139
+ ] }),
20140
+ isGenerating && /* @__PURE__ */ jsx(
20141
+ "p",
20142
+ {
20143
+ className: "text-sm text-muted-foreground",
20144
+ "data-testid": "ai-draft-progress",
20145
+ children: tr(
20146
+ "fluidgrids.builder.aiDraft.generating",
20147
+ "Designing your workflow. This usually takes under a minute."
20148
+ )
20149
+ }
20150
+ ),
20151
+ (error || timedOut) && /* @__PURE__ */ jsx(
20152
+ "div",
20153
+ {
20154
+ role: "alert",
20155
+ 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)]",
20156
+ children: timedOut ? tr(
20157
+ "fluidgrids.builder.aiDraft.timedOut",
20158
+ "The draft is taking longer than expected. Try again in a moment."
20159
+ ) : error
20160
+ }
20161
+ ),
20162
+ failed && /* @__PURE__ */ jsxs(
20163
+ "div",
20164
+ {
20165
+ role: "alert",
20166
+ "data-testid": "ai-draft-failed",
20167
+ 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)]",
20168
+ children: [
20169
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: generation?.message ?? tr(
20170
+ "fluidgrids.builder.aiDraft.failed",
20171
+ "The workflow could not be generated."
20172
+ ) }),
20173
+ (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)) }),
20174
+ /* @__PURE__ */ jsx("span", { children: tr(
20175
+ "fluidgrids.builder.aiDraft.failedHint",
20176
+ "Try describing the steps more concretely, then generate again."
20177
+ ) })
20178
+ ]
20179
+ }
20180
+ ),
20181
+ draft && /* @__PURE__ */ jsxs(
20182
+ "div",
20183
+ {
20184
+ "data-testid": "ai-draft-result",
20185
+ className: "flex flex-col gap-3 rounded-lg border border-border bg-card p-3",
20186
+ children: [
20187
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
20188
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-card-foreground", children: draft.name }),
20189
+ draft.explanation && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: draft.explanation })
20190
+ ] }),
20191
+ /* @__PURE__ */ jsx(
20192
+ "ol",
20193
+ {
20194
+ className: "flex flex-col gap-1.5",
20195
+ "data-testid": "ai-draft-nodes",
20196
+ children: draftNodes.map((node, index) => /* @__PURE__ */ jsxs(
20197
+ "li",
20198
+ {
20199
+ className: "flex items-center justify-between gap-2 text-sm",
20200
+ children: [
20201
+ /* @__PURE__ */ jsxs("span", { className: "truncate text-card-foreground", children: [
20202
+ index + 1,
20203
+ ". ",
20204
+ node.label
20205
+ ] }),
20206
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "shrink-0", children: shortNodeType(node.type) })
20207
+ ]
20208
+ },
20209
+ node.id
20210
+ ))
20211
+ }
20212
+ ),
20213
+ (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: [
20214
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: tr(
20215
+ "fluidgrids.builder.aiDraft.warningsTitle",
20216
+ "Still to configure:"
20217
+ ) }),
20218
+ /* @__PURE__ */ jsx("ul", { className: "list-disc pl-4", children: generation?.validation?.warnings.map((message) => /* @__PURE__ */ jsx("li", { children: message }, message)) })
20219
+ ] }),
20220
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
20221
+ /* @__PURE__ */ jsx(
20222
+ Button$1,
20223
+ {
20224
+ "data-testid": "ai-draft-accept",
20225
+ onClick: handleAccept,
20226
+ className: "flex gap-2",
20227
+ children: tr("fluidgrids.builder.aiDraft.accept", "Add to canvas")
20228
+ }
20229
+ ),
20230
+ /* @__PURE__ */ jsx(
20231
+ Button$1,
20232
+ {
20233
+ variant: "outline",
20234
+ "data-testid": "ai-draft-refine",
20235
+ onClick: handleRefine,
20236
+ children: tr("fluidgrids.builder.aiDraft.refine", "Refine")
20237
+ }
20238
+ ),
20239
+ /* @__PURE__ */ jsx(
20240
+ Button$1,
20241
+ {
20242
+ variant: "ghost",
20243
+ "data-testid": "ai-draft-reject",
20244
+ onClick: handleReject,
20245
+ children: tr("fluidgrids.builder.aiDraft.reject", "Discard")
20246
+ }
20247
+ )
20248
+ ] })
20249
+ ]
20250
+ }
20251
+ )
20252
+ ] })
20253
+ ]
20254
+ }
20255
+ )
20256
+ ] }),
20257
+ /* @__PURE__ */ jsx(AlertDialog, { open: confirmReplace, onOpenChange: setConfirmReplace, children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
20258
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
20259
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: tr(
20260
+ "fluidgrids.builder.aiDraft.replaceTitle",
20261
+ "Replace the current canvas?"
20262
+ ) }),
20263
+ /* @__PURE__ */ jsx(AlertDialogDescription, { children: tr(
20264
+ "fluidgrids.builder.aiDraft.replaceDescription",
20265
+ "The draft replaces the nodes on the canvas. You can undo this, and nothing is saved until you save the workflow."
20266
+ ) })
20267
+ ] }),
20268
+ /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
20269
+ /* @__PURE__ */ jsx(AlertDialogCancel, { children: tr("fluidgrids.builder.aiDraft.replaceCancel", "Keep canvas") }),
20270
+ /* @__PURE__ */ jsx(
20271
+ AlertDialogAction,
20272
+ {
20273
+ "data-testid": "ai-draft-replace-confirm",
20274
+ onClick: applyDraft,
20275
+ children: tr("fluidgrids.builder.aiDraft.replaceConfirm", "Replace")
20276
+ }
20277
+ )
20278
+ ] })
20279
+ ] }) })
20280
+ ] });
20281
+ }
19729
20282
  var ActivateWorkflowButton = ({
19730
20283
  className
19731
20284
  }) => {
@@ -20071,6 +20624,7 @@ var FlowBuilder = ({ readOnly = false }) => {
20071
20624
  ] }),
20072
20625
  name !== "" && /* @__PURE__ */ jsxs(Fragment, { children: [
20073
20626
  /* @__PURE__ */ jsxs("div", { className: "hidden shrink-0 lg:inline-flex items-center gap-2", children: [
20627
+ !isReadOnly && /* @__PURE__ */ jsx(AiDraftPanel, {}),
20074
20628
  /* @__PURE__ */ jsx(VersionHistoryPanel, {}),
20075
20629
  /* @__PURE__ */ jsx(SaveFlowButton, {}),
20076
20630
  /* @__PURE__ */ jsx(ActivateWorkflowButton, {}),
@@ -20091,47 +20645,50 @@ var FlowBuilder = ({ readOnly = false }) => {
20091
20645
  }
20092
20646
  )
20093
20647
  ] }),
20094
- /* @__PURE__ */ jsx("div", { className: "shrink-0 lg:hidden", children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
20095
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
20096
- Button$1,
20097
- {
20098
- variant: "ghost",
20099
- size: "icon",
20100
- className: "text-card-foreground",
20101
- "data-tour": "fluidgrids-builder-actions-menu",
20102
- children: /* @__PURE__ */ jsx(Icon, { icon: "mynaui:dots-vertical", className: "size-5" })
20103
- }
20104
- ) }),
20105
- /* @__PURE__ */ jsxs(
20106
- DropdownMenuContent,
20107
- {
20108
- align: "end",
20109
- className: "flex flex-col gap-1.5 p-2 w-56",
20110
- children: [
20111
- /* @__PURE__ */ jsx(VersionHistoryPanel, { className: "w-full justify-start" }),
20112
- /* @__PURE__ */ jsx(SaveFlowButton, { className: "w-full justify-start" }),
20113
- /* @__PURE__ */ jsx(ActivateWorkflowButton, { className: "w-full justify-start" }),
20114
- /* @__PURE__ */ jsx(
20115
- RunWithViewRuns,
20116
- {
20117
- onViewRuns: handleViewRuns,
20118
- canViewRuns: !!id,
20119
- className: "w-full",
20120
- runButtonClassName: "flex-1 justify-start rounded-l-lg"
20121
- }
20122
- ),
20123
- /* @__PURE__ */ jsx(
20124
- FluidGridsDocsLink,
20125
- {
20126
- topic: "builder",
20127
- className: "w-full rounded-md px-3 py-2",
20128
- children: tr("fluidgrids.docs.builderGuide", "Builder guide")
20129
- }
20130
- )
20131
- ]
20132
- }
20133
- )
20134
- ] }) })
20648
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1 lg:hidden", children: [
20649
+ !isReadOnly && /* @__PURE__ */ jsx(AiDraftPanel, { compact: true }),
20650
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
20651
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
20652
+ Button$1,
20653
+ {
20654
+ variant: "ghost",
20655
+ size: "icon",
20656
+ className: "text-card-foreground",
20657
+ "data-tour": "fluidgrids-builder-actions-menu",
20658
+ children: /* @__PURE__ */ jsx(Icon, { icon: "mynaui:dots-vertical", className: "size-5" })
20659
+ }
20660
+ ) }),
20661
+ /* @__PURE__ */ jsxs(
20662
+ DropdownMenuContent,
20663
+ {
20664
+ align: "end",
20665
+ className: "flex flex-col gap-1.5 p-2 w-56",
20666
+ children: [
20667
+ /* @__PURE__ */ jsx(VersionHistoryPanel, { className: "w-full justify-start" }),
20668
+ /* @__PURE__ */ jsx(SaveFlowButton, { className: "w-full justify-start" }),
20669
+ /* @__PURE__ */ jsx(ActivateWorkflowButton, { className: "w-full justify-start" }),
20670
+ /* @__PURE__ */ jsx(
20671
+ RunWithViewRuns,
20672
+ {
20673
+ onViewRuns: handleViewRuns,
20674
+ canViewRuns: !!id,
20675
+ className: "w-full",
20676
+ runButtonClassName: "flex-1 justify-start rounded-l-lg"
20677
+ }
20678
+ ),
20679
+ /* @__PURE__ */ jsx(
20680
+ FluidGridsDocsLink,
20681
+ {
20682
+ topic: "builder",
20683
+ className: "w-full rounded-md px-3 py-2",
20684
+ children: tr("fluidgrids.docs.builderGuide", "Builder guide")
20685
+ }
20686
+ )
20687
+ ]
20688
+ }
20689
+ )
20690
+ ] })
20691
+ ] })
20135
20692
  ] })
20136
20693
  ] }),
20137
20694
  /* @__PURE__ */ jsxs(