@burdenoff/fluidgrids-fe-sdk 2026.1026.5 → 2026.1027.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-QWMR2CST.js → ExecutionDetailPage-CRGWMW6J.js} +7 -7
  2. package/dist/{ExecutionsListPage-TYYC3WT7.js → ExecutionsListPage-P4I6FJX3.js} +7 -7
  3. package/dist/{WorkflowBuilder-U636SQC4.js → WorkflowBuilder-ACDC2MQF.js} +9 -9
  4. package/dist/{WorkflowsList-XPARJ4SL.js → WorkflowsList-ZO7KPIG7.js} +7 -7
  5. package/dist/{chunk-NLK5WXH5.js → chunk-2PQZZ6Y4.js} +2 -2
  6. package/dist/{chunk-IQLMFGAQ.js → chunk-3UA5H5CT.js} +1 -1
  7. package/dist/{chunk-W57KG66V.js → chunk-5BF6XLMJ.js} +1 -1
  8. package/dist/{chunk-WU3ZNMXL.js → chunk-AUAF6E4Z.js} +4 -4
  9. package/dist/{chunk-FJMKFZ6L.js → chunk-BK4OICYX.js} +5 -5
  10. package/dist/{chunk-U5HHDK3P.js → chunk-BQWEOKPY.js} +2 -2
  11. package/dist/{chunk-DIZ7NKG2.js → chunk-FBJS2N4N.js} +113 -5
  12. package/dist/{chunk-6HMCSUQM.js → chunk-FJBCC4UZ.js} +1008 -28
  13. package/dist/{chunk-3ZGCACWA.js → chunk-IKGKMOBR.js} +1 -1
  14. package/dist/{chunk-KVVM3C2R.js → chunk-JQ66KPU6.js} +2 -2
  15. package/dist/{chunk-BTVYWLPL.js → chunk-JVBJKIV2.js} +1 -1
  16. package/dist/{chunk-JOE4LAR4.js → chunk-KEURSYHK.js} +1 -1
  17. package/dist/{chunk-4QV27HMI.js → chunk-KQ76OJWI.js} +3 -3
  18. package/dist/{chunk-PKE3R6N3.js → chunk-LXQXMWJ6.js} +1 -1
  19. package/dist/{chunk-NHGKPOJZ.js → chunk-MRL6QLBD.js} +1 -1
  20. package/dist/{chunk-KQWIQJ7E.js → chunk-PGEG5FCW.js} +2 -2
  21. package/dist/{chunk-L2S5V4YS.js → chunk-QRJ7SYR2.js} +11 -3
  22. package/dist/{chunk-SO7UZG76.js → chunk-RBAYIRXR.js} +5 -5
  23. package/dist/{chunk-IGBCNXSB.js → chunk-VSQDJY7J.js} +3 -3
  24. package/dist/{chunk-4JR52R3N.js → chunk-WYTARFHH.js} +3 -3
  25. package/dist/{chunk-IOAU6GYE.js → chunk-X7IMNKTG.js} +1 -1
  26. package/dist/executions.js +9 -9
  27. package/dist/history.js +4 -4
  28. package/dist/index.d.ts +1127 -8
  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-3ZGCACWA.js';
6
- import { EntityTagPicker } from './chunk-IQLMFGAQ.js';
7
- import { useWorkflowVersions, useRestoreWorkflowVersion, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-NLK5WXH5.js';
5
+ import { QuotaExhaustedDialog } from './chunk-IKGKMOBR.js';
6
+ import { EntityTagPicker } from './chunk-3UA5H5CT.js';
7
+ import { useWorkflowVersions, useRestoreWorkflowVersion, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-2PQZZ6Y4.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, buildOperationSeed, isNodeTrigger, buildStickyNode, fieldsFromIO, ConnectionSelector, structuredParameterType, nodeAccent, NodeTypeIcon, NODES_METADATA, resolveNodeOperation, declaredOutputFields, subscribeWorkflowSaved, pathToExpression, ExpressionEditor, formatParameterValue, parseParameterValue, isNodeConfigsInitialized, resolveUiSpec, isImageIcon, NODES, synchronizeOperationUpdate, NodePropertyPanel, openRunQuotaUpgradePrompt, describeOperationError, isTriggerOperation, canRenderOperationPanel, resolveOperationName, resolveInputHandles } from './chunk-BTVYWLPL.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, useConnections, ConnectionFormDialog, useSaveWorkflow, SdkPermissionButton, FLUIDGRIDS_PERMISSIONS, STICKY_NOTE_TYPE, fieldsFromValue, getNodeDetail, useFluidGridsAnalytics, buildOperationSeed, isNodeTrigger, buildStickyNode, fieldsFromIO, ConnectionSelector, structuredParameterType, nodeAccent, NodeTypeIcon, NODES_METADATA, resolveNodeOperation, declaredOutputFields, subscribeWorkflowSaved, pathToExpression, ExpressionEditor, formatParameterValue, parseParameterValue, isNodeConfigsInitialized, resolveUiSpec, isImageIcon, NODES, synchronizeOperationUpdate, NodePropertyPanel, openRunQuotaUpgradePrompt, describeOperationError, isTriggerOperation, canRenderOperationPanel, resolveOperationName, resolveInputHandles } from './chunk-JVBJKIV2.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, WorkflowGenerationTemplatesDocument, WorkflowGenerationDocument, GenerateWorkflowFromPromptDocument, WorkflowEnvironmentsDocument, WorkflowPromotionRequestsDocument, DeployWorkflowVersionToDevDocument, RequestWorkflowPromotionDocument, ApproveWorkflowPromotionDocument, RejectWorkflowPromotionDocument, CancelWorkflowPromotionDocument, UpdateWorkflowEnvironmentPolicyDocument, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument } from './chunk-L2S5V4YS.js';
14
+ import { WorkspaceRunsQuotaDocument, NodeSchemaIntrospectionDocument, useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, WorkflowGenerationTemplatesDocument, WorkflowGenerationDocument, GenerateWorkflowFromPromptDocument, WorkflowEnvironmentsDocument, WorkflowPromotionRequestsDocument, DeployWorkflowVersionToDevDocument, RequestWorkflowPromotionDocument, ApproveWorkflowPromotionDocument, RejectWorkflowPromotionDocument, CancelWorkflowPromotionDocument, UpdateWorkflowEnvironmentPolicyDocument, WorkflowGitRepositoryDocument, WorkflowGitPushesDocument, WorkflowGitCommitsDocument, LinkWorkflowGitRepositoryDocument, UnlinkWorkflowGitRepositoryDocument, PushWorkflowToGitDocument, WorkflowGitDiffDocument, RestoreWorkflowFromGitDocument, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument } from './chunk-QRJ7SYR2.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';
@@ -25,7 +25,7 @@ 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, Loader2, History, GitCompareArrows, Play, RotateCcw, Trash2, Layers, Upload, Rocket, ArrowRight, Check, X, Undo2, Maximize2, Redo2, LayoutGrid, Keyboard, Plus, GitBranch, AlertCircle, Search, RefreshCw, CheckCircle2, Package, Info, Copy, BadgeCheck, DollarSign, Type, Code, Hash, List, Braces, Workflow, PlusCircle, ChevronDown, ChevronRight, AlertTriangle } from 'lucide-react';
28
+ import { Sparkles, Star, Download, Eye, Loader2, History, GitCompareArrows, Play, RotateCcw, Trash2, Layers, Upload, Rocket, ArrowRight, Check, X, Undo2, Plus, Link2, GitBranch, Unlink, GitCompare, ExternalLink, GitCommitHorizontal, Maximize2, Redo2, LayoutGrid, Keyboard, AlertCircle, Search, RefreshCw, CheckCircle2, Package, Info, Copy, BadgeCheck, DollarSign, Type, Code, Hash, List, Braces, Workflow, PlusCircle, ChevronDown, ChevronRight, 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((toast12) => {
6460
- addToRemoveQueue(toast12.id);
6459
+ state.toasts.forEach((toast13) => {
6460
+ addToRemoveQueue(toast13.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: toast12 } = useToast();
7836
+ const { toast: toast13 } = 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
- toast12({
7856
+ toast13({
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
- toast12,
7882
+ toast13,
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
- toast12({
7989
+ toast13({
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
- toast12({
7997
+ toast13({
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
- toast12({
8007
+ toast13({
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, toast12, tr, setEdges]
8020
+ [nodes, mapping, onMappingChange, toast13, 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
- toast12({
8077
+ toast13({
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
- toast12({
8109
+ toast13({
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: toast12 } = useToast();
8408
+ const { toast: toast13 } = 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
- toast12({
8532
+ toast13({
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
- toast12({
8543
+ toast13({
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
- toast12({
8562
+ toast13({
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
- toast12({
8574
+ toast13({
8575
8575
  title: tr(
8576
8576
  "fluidgrids.builder.mappingEditor.mappingTestFailedTitle",
8577
8577
  "Mapping test failed"
@@ -14384,8 +14384,9 @@ var TYPE_ICON = {
14384
14384
  object: "mdi:code-braces",
14385
14385
  any: "mdi:help-circle-outline"
14386
14386
  };
14387
+ var DEFAULT_EXPAND_DEPTH = 3;
14387
14388
  function FieldRow({ field, depth, draggable }) {
14388
- const [open, setOpen] = useState(depth < 1);
14389
+ const [open, setOpen] = useState(depth < DEFAULT_EXPAND_DEPTH);
14389
14390
  const hasChildren = !!field.children && field.children.length > 0;
14390
14391
  const handleDragStart = (e) => {
14391
14392
  e.dataTransfer.setData(FIELD_PATH_MIME, field.path);
@@ -20410,7 +20411,8 @@ function EnvironmentsPanel({
20410
20411
  ) : [
20411
20412
  state.promotedFrom ? `${tr("fluidgrids.builder.environments.from", "From")} ${envLabel(state.promotedFrom)}` : null,
20412
20413
  deployer,
20413
- relativeTime(state.deployedAt)
20414
+ relativeTime(state.deployedAt),
20415
+ state.gitCommitSha ? `Git ${state.gitCommitSha.slice(0, 7)}` : null
20414
20416
  ].filter(Boolean).join(" \xB7 ") }),
20415
20417
  /* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap gap-2", children: [
20416
20418
  state.environment === "DEV" && currentVersion && /* @__PURE__ */ jsxs(
@@ -20684,7 +20686,8 @@ function EnvironmentsPanel({
20684
20686
  /* @__PURE__ */ jsx("p", { className: "truncate text-muted-foreground", children: [
20685
20687
  nameOf(item.requestedById),
20686
20688
  item.reviewedById && item.reviewedById !== item.requestedById ? `${tr("fluidgrids.builder.environments.reviewedBy", "reviewed by")} ${nameOf(item.reviewedById)}` : null,
20687
- relativeTime(item.requestedAt)
20689
+ relativeTime(item.requestedAt),
20690
+ item.gitCommitSha ? `Git ${item.gitCommitSha.slice(0, 7)}` : null
20688
20691
  ].filter(Boolean).join(" \xB7 ") })
20689
20692
  ] }),
20690
20693
  /* @__PURE__ */ jsx(
@@ -20754,6 +20757,981 @@ function EnvironmentsPanel({
20754
20757
  )
20755
20758
  ] });
20756
20759
  }
20760
+ var LIST_LIMIT = 20;
20761
+ function messageOf2(error) {
20762
+ return error instanceof Error ? error.message : String(error);
20763
+ }
20764
+ function useWorkflowGitSync(workflowKey) {
20765
+ const [repository, setRepository] = useState(null);
20766
+ const [commits, setCommits] = useState([]);
20767
+ const [pushes, setPushes] = useState([]);
20768
+ const [loading, setLoading] = useState(false);
20769
+ const [error, setError] = useState(null);
20770
+ const [historyError, setHistoryError] = useState(null);
20771
+ const keyRef = useRef(workflowKey);
20772
+ keyRef.current = workflowKey;
20773
+ const refetch = useCallback(async () => {
20774
+ if (!workflowKey) return;
20775
+ setLoading(true);
20776
+ setError(null);
20777
+ setHistoryError(null);
20778
+ try {
20779
+ const client = getFluidGridsClient();
20780
+ const [linked, pushed] = await Promise.all([
20781
+ client.query({
20782
+ query: WorkflowGitRepositoryDocument,
20783
+ fetchPolicy: "network-only"
20784
+ }),
20785
+ client.query({
20786
+ query: WorkflowGitPushesDocument,
20787
+ variables: { workflowKey, limit: LIST_LIMIT },
20788
+ fetchPolicy: "network-only"
20789
+ })
20790
+ ]);
20791
+ throwIfResultError(linked);
20792
+ throwIfResultError(pushed);
20793
+ if (keyRef.current !== workflowKey) return;
20794
+ const link2 = linked.data?.workflowGitRepository ?? null;
20795
+ setRepository(link2);
20796
+ setPushes(pushed.data?.workflowGitPushes ?? []);
20797
+ if (!link2) {
20798
+ setCommits([]);
20799
+ return;
20800
+ }
20801
+ try {
20802
+ const history = await client.query({
20803
+ query: WorkflowGitCommitsDocument,
20804
+ variables: { workflowKey, limit: LIST_LIMIT },
20805
+ fetchPolicy: "network-only"
20806
+ });
20807
+ throwIfResultError(history);
20808
+ if (keyRef.current === workflowKey) {
20809
+ setCommits(history.data?.workflowGitCommits ?? []);
20810
+ }
20811
+ } catch (err) {
20812
+ if (keyRef.current === workflowKey) {
20813
+ setCommits([]);
20814
+ setHistoryError(messageOf2(err));
20815
+ }
20816
+ }
20817
+ } catch (err) {
20818
+ if (keyRef.current === workflowKey) setError(messageOf2(err));
20819
+ } finally {
20820
+ if (keyRef.current === workflowKey) setLoading(false);
20821
+ }
20822
+ }, [workflowKey]);
20823
+ useEffect(() => {
20824
+ setRepository(null);
20825
+ setCommits([]);
20826
+ setPushes([]);
20827
+ }, [workflowKey]);
20828
+ const link = useCallback(
20829
+ async (values) => {
20830
+ const result = await getFluidGridsClient().mutate({
20831
+ mutation: LinkWorkflowGitRepositoryDocument,
20832
+ variables: {
20833
+ input: {
20834
+ provider: "GITHUB",
20835
+ connectionId: values.connectionId,
20836
+ repository: values.repository.trim(),
20837
+ branch: values.branch.trim(),
20838
+ pathPrefix: values.pathPrefix.trim(),
20839
+ pushOnActivation: values.pushOnActivation,
20840
+ pushOnPromotion: values.pushOnPromotion
20841
+ }
20842
+ }
20843
+ });
20844
+ throwIfResultError(result);
20845
+ await refetch();
20846
+ },
20847
+ [refetch]
20848
+ );
20849
+ const unlink = useCallback(async () => {
20850
+ const result = await getFluidGridsClient().mutate({ mutation: UnlinkWorkflowGitRepositoryDocument });
20851
+ throwIfResultError(result);
20852
+ await refetch();
20853
+ }, [refetch]);
20854
+ const push = useCallback(
20855
+ async (version) => {
20856
+ const result = await getFluidGridsClient().mutate({
20857
+ mutation: PushWorkflowToGitDocument,
20858
+ variables: { workflowKey, version }
20859
+ });
20860
+ throwIfResultError(result);
20861
+ const pushed = result.data?.pushWorkflowToGit;
20862
+ if (!pushed) throw new Error("The push returned no result");
20863
+ await refetch();
20864
+ return pushed;
20865
+ },
20866
+ [workflowKey, refetch]
20867
+ );
20868
+ const diff = useCallback(
20869
+ async (commitSha, version) => {
20870
+ const result = await getFluidGridsClient().query({
20871
+ query: WorkflowGitDiffDocument,
20872
+ variables: { workflowKey, commitSha, version },
20873
+ fetchPolicy: "network-only"
20874
+ });
20875
+ throwIfResultError(result);
20876
+ const view = result.data?.workflowGitDiff;
20877
+ if (!view) throw new Error("The diff returned no result");
20878
+ return view;
20879
+ },
20880
+ [workflowKey]
20881
+ );
20882
+ const restore = useCallback(
20883
+ async (commitSha) => {
20884
+ const result = await getFluidGridsClient().mutate({
20885
+ mutation: RestoreWorkflowFromGitDocument,
20886
+ variables: { input: { workflowKey, commitSha } }
20887
+ });
20888
+ throwIfResultError(result);
20889
+ const restored = result.data?.restoreWorkflowFromGit;
20890
+ if (!restored) throw new Error("The restore returned no result");
20891
+ return restored;
20892
+ },
20893
+ [workflowKey]
20894
+ );
20895
+ return {
20896
+ repository,
20897
+ commits,
20898
+ pushes,
20899
+ loading,
20900
+ error,
20901
+ historyError,
20902
+ refetch,
20903
+ link,
20904
+ unlink,
20905
+ push,
20906
+ diff,
20907
+ restore
20908
+ };
20909
+ }
20910
+
20911
+ // src/builder/lib/workflow-git-sync.ts
20912
+ var GITHUB_PROVIDER_KEY = "github";
20913
+ var DEFAULT_GIT_BRANCH = "main";
20914
+ var DEFAULT_GIT_FOLDER = "fluidgrids/workflows";
20915
+ function githubConnections(connections) {
20916
+ return connections.filter(
20917
+ (connection) => connection.provider?.key?.toLowerCase() === GITHUB_PROVIDER_KEY
20918
+ ).sort((a, b) => {
20919
+ const activeA = a.status === "ACTIVE" ? 0 : 1;
20920
+ const activeB = b.status === "ACTIVE" ? 0 : 1;
20921
+ return activeA - activeB || a.name.localeCompare(b.name);
20922
+ });
20923
+ }
20924
+ function normalizeRepositoryInput(value) {
20925
+ return value.trim().replace(/^https?:\/\/github\.com\//i, "").replace(/\.git$/i, "").replace(/\/+$/, "");
20926
+ }
20927
+ function gitLinkFormProblem(form) {
20928
+ if (!form.connectionId.trim()) return "connection";
20929
+ if (!/^[A-Za-z0-9][A-Za-z0-9-]*\/[A-Za-z0-9._-]+$/.test(
20930
+ normalizeRepositoryInput(form.repository)
20931
+ ))
20932
+ return "repository";
20933
+ const branch = form.branch.trim();
20934
+ if (!branch || /\s|\.\.|^\/|\/$/.test(branch)) return "branch";
20935
+ const folder = form.pathPrefix.trim().replace(/^\/+|\/+$/g, "");
20936
+ if (folder.split("/").some((segment) => segment === ".." || segment === "."))
20937
+ return "pathPrefix";
20938
+ return null;
20939
+ }
20940
+ function gitFilePath(pathPrefix, workflowKey) {
20941
+ return pathPrefix ? `${pathPrefix}/${workflowKey}.json` : `${workflowKey}.json`;
20942
+ }
20943
+ function shortSha(sha) {
20944
+ return sha ? sha.slice(0, 7) : "";
20945
+ }
20946
+ function commitTitle(message) {
20947
+ const first = message.split("\n", 1)[0] ?? "";
20948
+ return first.trim() || "(no message)";
20949
+ }
20950
+ var EMPTY_FORM = {
20951
+ connectionId: "",
20952
+ repository: "",
20953
+ branch: DEFAULT_GIT_BRANCH,
20954
+ pathPrefix: DEFAULT_GIT_FOLDER,
20955
+ pushOnActivation: true,
20956
+ pushOnPromotion: true
20957
+ };
20958
+ function formatDiffValue2(value) {
20959
+ if (value === void 0 || value === null) return "\u2014";
20960
+ try {
20961
+ return JSON.stringify(value);
20962
+ } catch {
20963
+ return String(value);
20964
+ }
20965
+ }
20966
+ function GitSyncPanel({
20967
+ className,
20968
+ compact = false
20969
+ }) {
20970
+ const tr = useTr();
20971
+ const navigate = useNavigate();
20972
+ const params = useParams();
20973
+ const workflowKey = params?.id ?? "";
20974
+ const currentVersion = params?.version ?? "";
20975
+ const {
20976
+ repository,
20977
+ commits,
20978
+ pushes,
20979
+ loading,
20980
+ error,
20981
+ historyError,
20982
+ refetch,
20983
+ link,
20984
+ unlink,
20985
+ push,
20986
+ diff,
20987
+ restore
20988
+ } = useWorkflowGitSync(workflowKey);
20989
+ const {
20990
+ connections,
20991
+ loading: connectionsLoading,
20992
+ refetch: refetchConnections
20993
+ } = useConnections();
20994
+ const gitConnections = useMemo(
20995
+ () => githubConnections(connections),
20996
+ [connections]
20997
+ );
20998
+ const [busy, setBusy] = useState(null);
20999
+ const [editing, setEditing] = useState(false);
21000
+ const [form, setForm] = useState(EMPTY_FORM);
21001
+ const [showProblem, setShowProblem] = useState(false);
21002
+ const [newConnectionOpen, setNewConnectionOpen] = useState(false);
21003
+ const [comparison, setComparison] = useState(
21004
+ null
21005
+ );
21006
+ const [confirm, setConfirm] = useState(null);
21007
+ const config = getFluidGridsConfig();
21008
+ const actorInputs = useMemo(() => {
21009
+ const ids = /* @__PURE__ */ new Set();
21010
+ for (const item of pushes) ids.add(item.pushedById);
21011
+ if (repository) ids.add(repository.linkedById);
21012
+ return [...ids].map((actorId) => ({ actorId, actorType: "user" }));
21013
+ }, [pushes, repository]);
21014
+ const actorProfiles = useActorProfiles(actorInputs, {
21015
+ workspaceId: config.workspaceId || void 0,
21016
+ orgId: config.organizationId,
21017
+ authToken: config.authToken
21018
+ });
21019
+ const nameOf = (actorId) => {
21020
+ if (!actorId) return null;
21021
+ return actorProfiles[actorId]?.displayName ?? buildActorFallback({ actorId, actorType: "user" }).displayName;
21022
+ };
21023
+ const problem = gitLinkFormProblem(form);
21024
+ const problemText = (value) => {
21025
+ switch (value) {
21026
+ case "connection":
21027
+ return tr(
21028
+ "fluidgrids.builder.git.problemConnection",
21029
+ "Choose a GitHub connection"
21030
+ );
21031
+ case "repository":
21032
+ return tr(
21033
+ "fluidgrids.builder.git.problemRepository",
21034
+ "Enter the repository as owner/name"
21035
+ );
21036
+ case "branch":
21037
+ return tr(
21038
+ "fluidgrids.builder.git.problemBranch",
21039
+ "Enter a valid branch name"
21040
+ );
21041
+ default:
21042
+ return tr(
21043
+ "fluidgrids.builder.git.problemFolder",
21044
+ "The folder cannot contain . or .. segments"
21045
+ );
21046
+ }
21047
+ };
21048
+ const run = async (key, action, errorTitle) => {
21049
+ setBusy(key);
21050
+ try {
21051
+ await action();
21052
+ } catch (err) {
21053
+ toast.error(errorTitle, {
21054
+ description: err instanceof Error ? err.message : String(err)
21055
+ });
21056
+ } finally {
21057
+ setBusy(null);
21058
+ }
21059
+ };
21060
+ const startEditing = () => {
21061
+ setForm(
21062
+ repository ? {
21063
+ connectionId: repository.connectionId,
21064
+ repository: repository.repository,
21065
+ branch: repository.branch,
21066
+ pathPrefix: repository.pathPrefix,
21067
+ pushOnActivation: repository.pushOnActivation,
21068
+ pushOnPromotion: repository.pushOnPromotion
21069
+ } : {
21070
+ ...EMPTY_FORM,
21071
+ connectionId: gitConnections[0]?.id ?? ""
21072
+ }
21073
+ );
21074
+ setShowProblem(false);
21075
+ setEditing(true);
21076
+ };
21077
+ const handleLink = () => {
21078
+ if (problem) {
21079
+ setShowProblem(true);
21080
+ return;
21081
+ }
21082
+ void run(
21083
+ "link",
21084
+ async () => {
21085
+ await link(form);
21086
+ setEditing(false);
21087
+ toast.success(
21088
+ tr("fluidgrids.builder.git.linked", "Repository linked"),
21089
+ {
21090
+ description: `${form.repository.trim()} @ ${form.branch.trim()}`,
21091
+ dismissible: true
21092
+ }
21093
+ );
21094
+ },
21095
+ tr("fluidgrids.builder.git.linkError", "Could not link the repository")
21096
+ );
21097
+ };
21098
+ const handlePush = () => run(
21099
+ "push",
21100
+ async () => {
21101
+ const pushed = await push(currentVersion);
21102
+ toast.success(
21103
+ pushed.status === "UNCHANGED" ? tr(
21104
+ "fluidgrids.builder.git.unchanged",
21105
+ "Already up to date in Git"
21106
+ ) : `${pushed.version} ${tr("fluidgrids.builder.git.pushed", "pushed to Git")}`,
21107
+ {
21108
+ description: pushed.commitSha ? `${pushed.filePath} \xB7 ${shortSha(pushed.commitSha)}` : pushed.filePath,
21109
+ dismissible: true
21110
+ }
21111
+ );
21112
+ },
21113
+ tr("fluidgrids.builder.git.pushError", "Push to Git failed")
21114
+ );
21115
+ const handleCompare = (sha) => run(
21116
+ `diff:${sha}`,
21117
+ async () => {
21118
+ setComparison(await diff(sha, currentVersion));
21119
+ },
21120
+ tr("fluidgrids.builder.git.diffError", "Could not compare")
21121
+ );
21122
+ const handleConfirm = async () => {
21123
+ if (!confirm) return;
21124
+ const current = confirm;
21125
+ setConfirm(null);
21126
+ if (current.kind === "unlink") {
21127
+ await run(
21128
+ "unlink",
21129
+ async () => {
21130
+ await unlink();
21131
+ setComparison(null);
21132
+ toast.success(
21133
+ tr("fluidgrids.builder.git.unlinked", "Repository unlinked"),
21134
+ { dismissible: true }
21135
+ );
21136
+ },
21137
+ tr(
21138
+ "fluidgrids.builder.git.unlinkError",
21139
+ "Could not unlink the repository"
21140
+ )
21141
+ );
21142
+ return;
21143
+ }
21144
+ await run(
21145
+ `restore:${current.sha}`,
21146
+ async () => {
21147
+ const result = await restore(current.sha);
21148
+ if (!result.success || !result.workflow) {
21149
+ const reasons = [
21150
+ ...result.validation?.errors ?? [],
21151
+ result.message ?? ""
21152
+ ].filter(Boolean);
21153
+ throw new Error(reasons.join(" "));
21154
+ }
21155
+ toast.success(
21156
+ tr("fluidgrids.builder.git.restored", "Restored from Git"),
21157
+ {
21158
+ description: `${shortSha(current.sha)} \u2192 ${result.workflow.version}`,
21159
+ dismissible: true
21160
+ }
21161
+ );
21162
+ navigate(
21163
+ `../workflow/${result.workflow.workflowKey}/${result.workflow.version}`
21164
+ );
21165
+ },
21166
+ tr("fluidgrids.builder.git.restoreError", "Restore from Git failed")
21167
+ );
21168
+ };
21169
+ const pushStatusVariant = (status) => {
21170
+ if (status === "PUSHED") return "default";
21171
+ if (status === "FAILED") return "destructive";
21172
+ return "outline";
21173
+ };
21174
+ const triggerLabel = (trigger) => {
21175
+ if (trigger === "PROMOTION")
21176
+ return tr("fluidgrids.builder.git.triggerPromotion", "Promotion");
21177
+ if (trigger === "ACTIVATION")
21178
+ return tr("fluidgrids.builder.git.triggerActivation", "Publish");
21179
+ return tr("fluidgrids.builder.git.triggerManual", "Manual");
21180
+ };
21181
+ const linkForm = /* @__PURE__ */ jsxs(
21182
+ "div",
21183
+ {
21184
+ className: "space-y-3 rounded-lg border border-border p-3",
21185
+ "data-testid": "fluidgrids-git-link-form",
21186
+ children: [
21187
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
21188
+ /* @__PURE__ */ jsx(Label, { htmlFor: "fg-git-connection", className: "text-xs", children: tr("fluidgrids.builder.git.connection", "GitHub connection") }),
21189
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
21190
+ /* @__PURE__ */ jsxs(
21191
+ Select,
21192
+ {
21193
+ value: form.connectionId,
21194
+ onValueChange: (value) => setForm((prev) => ({ ...prev, connectionId: value })),
21195
+ children: [
21196
+ /* @__PURE__ */ jsx(SelectTrigger, { id: "fg-git-connection", className: "h-8 flex-1", children: /* @__PURE__ */ jsx(
21197
+ SelectValue,
21198
+ {
21199
+ placeholder: connectionsLoading ? tr("fluidgrids.builder.git.loading", "Loading...") : tr(
21200
+ "fluidgrids.builder.git.chooseConnection",
21201
+ "Choose a connection"
21202
+ )
21203
+ }
21204
+ ) }),
21205
+ /* @__PURE__ */ jsx(SelectContent, { className: "z-[120]", children: gitConnections.map((connection) => /* @__PURE__ */ jsxs(SelectItem, { value: connection.id, children: [
21206
+ connection.name,
21207
+ connection.status !== "ACTIVE" ? ` (${connection.status.toLowerCase()})` : ""
21208
+ ] }, connection.id)) })
21209
+ ]
21210
+ }
21211
+ ),
21212
+ /* @__PURE__ */ jsxs(
21213
+ Button$1,
21214
+ {
21215
+ variant: "outline",
21216
+ size: "sm",
21217
+ className: "h-8 gap-1 text-xs",
21218
+ onClick: () => setNewConnectionOpen(true),
21219
+ children: [
21220
+ /* @__PURE__ */ jsx(Plus, { className: "size-3.5" }),
21221
+ tr("fluidgrids.builder.git.newConnection", "New")
21222
+ ]
21223
+ }
21224
+ )
21225
+ ] }),
21226
+ !connectionsLoading && gitConnections.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: tr(
21227
+ "fluidgrids.builder.git.noConnections",
21228
+ "No GitHub connection yet. Create one; it needs access to the repository's contents (the repo scope)."
21229
+ ) })
21230
+ ] }),
21231
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
21232
+ /* @__PURE__ */ jsx(Label, { htmlFor: "fg-git-repository", className: "text-xs", children: tr("fluidgrids.builder.git.repository", "Repository") }),
21233
+ /* @__PURE__ */ jsx(
21234
+ Input,
21235
+ {
21236
+ id: "fg-git-repository",
21237
+ className: "h-8 font-mono text-xs",
21238
+ placeholder: "owner/name",
21239
+ value: form.repository,
21240
+ onChange: (event) => setForm((prev) => ({ ...prev, repository: event.target.value }))
21241
+ }
21242
+ )
21243
+ ] }),
21244
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
21245
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
21246
+ /* @__PURE__ */ jsx(Label, { htmlFor: "fg-git-branch", className: "text-xs", children: tr("fluidgrids.builder.git.branch", "Branch") }),
21247
+ /* @__PURE__ */ jsx(
21248
+ Input,
21249
+ {
21250
+ id: "fg-git-branch",
21251
+ className: "h-8 font-mono text-xs",
21252
+ value: form.branch,
21253
+ onChange: (event) => setForm((prev) => ({ ...prev, branch: event.target.value }))
21254
+ }
21255
+ )
21256
+ ] }),
21257
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
21258
+ /* @__PURE__ */ jsx(Label, { htmlFor: "fg-git-folder", className: "text-xs", children: tr("fluidgrids.builder.git.folder", "Folder") }),
21259
+ /* @__PURE__ */ jsx(
21260
+ Input,
21261
+ {
21262
+ id: "fg-git-folder",
21263
+ className: "h-8 font-mono text-xs",
21264
+ value: form.pathPrefix,
21265
+ onChange: (event) => setForm((prev) => ({ ...prev, pathPrefix: event.target.value }))
21266
+ }
21267
+ )
21268
+ ] })
21269
+ ] }),
21270
+ [
21271
+ [
21272
+ "pushOnActivation",
21273
+ tr(
21274
+ "fluidgrids.builder.git.pushOnActivation",
21275
+ "Commit when a version is published"
21276
+ )
21277
+ ],
21278
+ [
21279
+ "pushOnPromotion",
21280
+ tr(
21281
+ "fluidgrids.builder.git.pushOnPromotion",
21282
+ "Commit when a promotion applies"
21283
+ )
21284
+ ]
21285
+ ].map(([field, label]) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
21286
+ /* @__PURE__ */ jsx(Label, { htmlFor: `fg-git-${field}`, className: "text-xs font-normal", children: label }),
21287
+ /* @__PURE__ */ jsx(
21288
+ Switch,
21289
+ {
21290
+ id: `fg-git-${field}`,
21291
+ checked: form[field],
21292
+ onCheckedChange: (checked) => setForm((prev) => ({ ...prev, [field]: checked }))
21293
+ }
21294
+ )
21295
+ ] }, field)),
21296
+ showProblem && problem && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--color-status-error-text)]", children: problemText(problem) }),
21297
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
21298
+ /* @__PURE__ */ jsxs(
21299
+ Button$1,
21300
+ {
21301
+ size: "sm",
21302
+ className: "h-7 gap-1 text-xs",
21303
+ disabled: busy !== null,
21304
+ onClick: handleLink,
21305
+ children: [
21306
+ busy === "link" ? /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Link2, { className: "size-3.5" }),
21307
+ repository ? tr("fluidgrids.builder.git.saveLink", "Save") : tr("fluidgrids.builder.git.link", "Link repository")
21308
+ ]
21309
+ }
21310
+ ),
21311
+ repository && /* @__PURE__ */ jsx(
21312
+ Button$1,
21313
+ {
21314
+ size: "sm",
21315
+ variant: "ghost",
21316
+ className: "h-7 text-xs",
21317
+ disabled: busy !== null,
21318
+ onClick: () => setEditing(false),
21319
+ children: tr("fluidgrids.builder.git.cancel", "Cancel")
21320
+ }
21321
+ )
21322
+ ] }),
21323
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: tr(
21324
+ "fluidgrids.builder.git.linkHint",
21325
+ "The link applies to every workflow in this workspace. Linking checks that the connection can read the branch. Only members with publish rights can change it."
21326
+ ) })
21327
+ ]
21328
+ }
21329
+ );
21330
+ return /* @__PURE__ */ jsxs(
21331
+ Sheet,
21332
+ {
21333
+ onOpenChange: (open) => {
21334
+ if (!open) return;
21335
+ setComparison(null);
21336
+ void refetch();
21337
+ void refetchConnections();
21338
+ },
21339
+ children: [
21340
+ /* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
21341
+ Button$1,
21342
+ {
21343
+ "data-tour": "fluidgrids-builder-git",
21344
+ variant: "outline",
21345
+ size: compact ? "icon" : "sm",
21346
+ className: cn$1(!compact && "flex gap-1", className),
21347
+ "aria-label": tr("fluidgrids.builder.git.triggerButton", "Git"),
21348
+ disabled: !workflowKey,
21349
+ children: [
21350
+ /* @__PURE__ */ jsx(GitBranch, { className: "size-4" }),
21351
+ !compact && tr("fluidgrids.builder.git.triggerButton", "Git")
21352
+ ]
21353
+ }
21354
+ ) }),
21355
+ /* @__PURE__ */ jsxs(
21356
+ SheetContent,
21357
+ {
21358
+ side: "right",
21359
+ className: "w-[380px] sm:w-[460px] overflow-y-auto",
21360
+ "data-testid": "fluidgrids-git-panel",
21361
+ children: [
21362
+ /* @__PURE__ */ jsxs(SheetHeader, { children: [
21363
+ /* @__PURE__ */ jsxs(SheetTitle, { className: "flex items-center gap-2", children: [
21364
+ /* @__PURE__ */ jsx(GitBranch, { className: "size-5" }),
21365
+ tr("fluidgrids.builder.git.title", "Git sync")
21366
+ ] }),
21367
+ /* @__PURE__ */ jsx(SheetDescription, { children: tr(
21368
+ "fluidgrids.builder.git.description",
21369
+ "Keep this workflow in a GitHub repository: commit versions, read the file's history, compare a commit with this version or restore it as a new draft."
21370
+ ) })
21371
+ ] }),
21372
+ loading && !repository && pushes.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-8 text-sm text-muted-foreground", children: [
21373
+ /* @__PURE__ */ jsx(Loader2, { className: "mr-2 size-4 animate-spin" }),
21374
+ tr("fluidgrids.builder.git.loading", "Loading...")
21375
+ ] }) : error ? /* @__PURE__ */ jsxs("div", { className: "py-6 text-sm", children: [
21376
+ /* @__PURE__ */ jsx("p", { className: "text-[var(--color-status-error-text)]", children: error }),
21377
+ /* @__PURE__ */ jsx(
21378
+ Button$1,
21379
+ {
21380
+ variant: "outline",
21381
+ size: "sm",
21382
+ className: "mt-3",
21383
+ onClick: () => void refetch(),
21384
+ children: tr("fluidgrids.builder.git.retry", "Retry")
21385
+ }
21386
+ )
21387
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-5 pb-6", children: [
21388
+ /* @__PURE__ */ jsxs("section", { "aria-labelledby": "fg-git-repo", children: [
21389
+ /* @__PURE__ */ jsx(
21390
+ "h3",
21391
+ {
21392
+ id: "fg-git-repo",
21393
+ className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground",
21394
+ children: tr("fluidgrids.builder.git.repositoryTitle", "Repository")
21395
+ }
21396
+ ),
21397
+ !repository && !editing ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-dashed border-border p-3", children: [
21398
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: tr(
21399
+ "fluidgrids.builder.git.notLinked",
21400
+ "No repository is linked to this workspace yet."
21401
+ ) }),
21402
+ /* @__PURE__ */ jsxs(
21403
+ Button$1,
21404
+ {
21405
+ size: "sm",
21406
+ className: "mt-2 h-7 gap-1 text-xs",
21407
+ onClick: startEditing,
21408
+ children: [
21409
+ /* @__PURE__ */ jsx(Link2, { className: "size-3.5" }),
21410
+ tr("fluidgrids.builder.git.link", "Link repository")
21411
+ ]
21412
+ }
21413
+ )
21414
+ ] }) : editing ? linkForm : repository ? /* @__PURE__ */ jsxs(
21415
+ "div",
21416
+ {
21417
+ className: "rounded-lg border border-border p-3",
21418
+ "data-testid": "fluidgrids-git-linked",
21419
+ children: [
21420
+ /* @__PURE__ */ jsx("p", { className: "font-mono text-sm text-foreground", children: repository.repository }),
21421
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
21422
+ `${tr("fluidgrids.builder.git.branch", "Branch")} ${repository.branch}`,
21423
+ gitFilePath(repository.pathPrefix, workflowKey)
21424
+ ].join(" \xB7 ") }),
21425
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
21426
+ repository.pushOnActivation ? tr(
21427
+ "fluidgrids.builder.git.onPublish",
21428
+ "Commits on publish"
21429
+ ) : null,
21430
+ repository.pushOnPromotion ? tr(
21431
+ "fluidgrids.builder.git.onPromotion",
21432
+ "Commits on promotion"
21433
+ ) : null,
21434
+ nameOf(repository.linkedById)
21435
+ ].filter(Boolean).join(" \xB7 ") }),
21436
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap gap-2", children: [
21437
+ currentVersion && /* @__PURE__ */ jsxs(
21438
+ Button$1,
21439
+ {
21440
+ size: "sm",
21441
+ className: "h-7 gap-1 text-xs",
21442
+ disabled: busy !== null,
21443
+ onClick: () => void handlePush(),
21444
+ children: [
21445
+ busy === "push" ? /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Upload, { className: "size-3.5" }),
21446
+ tr("fluidgrids.builder.git.push", "Push"),
21447
+ " ",
21448
+ currentVersion
21449
+ ]
21450
+ }
21451
+ ),
21452
+ /* @__PURE__ */ jsx(
21453
+ Button$1,
21454
+ {
21455
+ size: "sm",
21456
+ variant: "outline",
21457
+ className: "h-7 text-xs",
21458
+ disabled: busy !== null,
21459
+ onClick: startEditing,
21460
+ children: tr("fluidgrids.builder.git.edit", "Edit link")
21461
+ }
21462
+ ),
21463
+ /* @__PURE__ */ jsxs(
21464
+ Button$1,
21465
+ {
21466
+ size: "sm",
21467
+ variant: "ghost",
21468
+ className: "h-7 gap-1 text-xs",
21469
+ disabled: busy !== null,
21470
+ onClick: () => setConfirm({ kind: "unlink" }),
21471
+ children: [
21472
+ /* @__PURE__ */ jsx(Unlink, { className: "size-3.5" }),
21473
+ tr("fluidgrids.builder.git.unlink", "Unlink")
21474
+ ]
21475
+ }
21476
+ )
21477
+ ] })
21478
+ ]
21479
+ }
21480
+ ) : null
21481
+ ] }),
21482
+ repository && /* @__PURE__ */ jsxs("section", { "aria-labelledby": "fg-git-commits", children: [
21483
+ /* @__PURE__ */ jsx(
21484
+ "h3",
21485
+ {
21486
+ id: "fg-git-commits",
21487
+ className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground",
21488
+ children: tr("fluidgrids.builder.git.commitsTitle", "Git history")
21489
+ }
21490
+ ),
21491
+ historyError ? /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--color-status-error-text)]", children: historyError }) : commits.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: tr(
21492
+ "fluidgrids.builder.git.commitsEmpty",
21493
+ "This workflow has no commits in the repository yet."
21494
+ ) }) : /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: commits.map((commit) => /* @__PURE__ */ jsxs(
21495
+ "li",
21496
+ {
21497
+ className: "rounded-lg border border-border p-2.5",
21498
+ "data-testid": "fluidgrids-git-commit",
21499
+ children: [
21500
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
21501
+ /* @__PURE__ */ jsx("p", { className: "min-w-0 truncate text-xs font-medium text-foreground", children: commitTitle(commit.message) }),
21502
+ commit.version && /* @__PURE__ */ jsx(
21503
+ Badge,
21504
+ {
21505
+ variant: "outline",
21506
+ className: "h-5 shrink-0 px-1.5 py-0 font-mono text-[10px]",
21507
+ children: commit.version
21508
+ }
21509
+ )
21510
+ ] }),
21511
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[11px] text-muted-foreground", children: [
21512
+ shortSha(commit.sha),
21513
+ commit.authorName,
21514
+ relativeTime(commit.authoredAt)
21515
+ ].filter(Boolean).join(" \xB7 ") }),
21516
+ /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex flex-wrap gap-1.5", children: [
21517
+ currentVersion && /* @__PURE__ */ jsxs(
21518
+ Button$1,
21519
+ {
21520
+ size: "sm",
21521
+ variant: "outline",
21522
+ className: "h-6 gap-1 px-2 text-[11px]",
21523
+ disabled: busy !== null,
21524
+ onClick: () => void handleCompare(commit.sha),
21525
+ children: [
21526
+ busy === `diff:${commit.sha}` ? /* @__PURE__ */ jsx(Loader2, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx(GitCompare, { className: "size-3" }),
21527
+ tr(
21528
+ "fluidgrids.builder.git.compare",
21529
+ "Compare with"
21530
+ ),
21531
+ " ",
21532
+ currentVersion
21533
+ ]
21534
+ }
21535
+ ),
21536
+ /* @__PURE__ */ jsxs(
21537
+ Button$1,
21538
+ {
21539
+ size: "sm",
21540
+ variant: "outline",
21541
+ className: "h-6 gap-1 px-2 text-[11px]",
21542
+ disabled: busy !== null,
21543
+ onClick: () => setConfirm({ kind: "restore", sha: commit.sha }),
21544
+ children: [
21545
+ busy === `restore:${commit.sha}` ? /* @__PURE__ */ jsx(Loader2, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx(RotateCcw, { className: "size-3" }),
21546
+ tr(
21547
+ "fluidgrids.builder.git.restore",
21548
+ "Restore as draft"
21549
+ )
21550
+ ]
21551
+ }
21552
+ ),
21553
+ commit.url && /* @__PURE__ */ jsx(
21554
+ Button$1,
21555
+ {
21556
+ asChild: true,
21557
+ size: "sm",
21558
+ variant: "ghost",
21559
+ className: "h-6 gap-1 px-2 text-[11px]",
21560
+ children: /* @__PURE__ */ jsxs(
21561
+ "a",
21562
+ {
21563
+ href: commit.url,
21564
+ target: "_blank",
21565
+ rel: "noopener noreferrer",
21566
+ children: [
21567
+ /* @__PURE__ */ jsx(ExternalLink, { className: "size-3" }),
21568
+ tr(
21569
+ "fluidgrids.builder.git.openCommit",
21570
+ "Open"
21571
+ )
21572
+ ]
21573
+ }
21574
+ )
21575
+ }
21576
+ )
21577
+ ] })
21578
+ ]
21579
+ },
21580
+ commit.sha
21581
+ )) }),
21582
+ comparison && /* @__PURE__ */ jsxs(
21583
+ "div",
21584
+ {
21585
+ className: "mt-3 space-y-2 border-t border-border pt-3",
21586
+ "data-testid": "fluidgrids-git-diff",
21587
+ children: [
21588
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
21589
+ shortSha(comparison.commitSha),
21590
+ " \u2192 ",
21591
+ comparison.version,
21592
+ ":",
21593
+ " ",
21594
+ comparison.entries.length,
21595
+ " ",
21596
+ tr("fluidgrids.builder.git.changes", "changes"),
21597
+ comparison.truncated ? "+" : ""
21598
+ ] }),
21599
+ comparison.entries.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: tr(
21600
+ "fluidgrids.builder.git.noChanges",
21601
+ "The commit matches this version"
21602
+ ) }) : /* @__PURE__ */ jsx("ul", { className: "max-h-64 space-y-1 overflow-y-auto", children: comparison.entries.map((entry) => /* @__PURE__ */ jsxs(
21603
+ "li",
21604
+ {
21605
+ className: "rounded border border-border p-2 text-xs",
21606
+ children: [
21607
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
21608
+ /* @__PURE__ */ jsx(
21609
+ Badge,
21610
+ {
21611
+ variant: entry.kind === "CHANGED" ? "secondary" : entry.kind === "ADDED" ? "default" : "destructive",
21612
+ className: "text-[10px]",
21613
+ children: entry.kind
21614
+ }
21615
+ ),
21616
+ /* @__PURE__ */ jsx("code", { className: "min-w-0 truncate font-mono", children: entry.path })
21617
+ ] }),
21618
+ entry.kind === "CHANGED" && /* @__PURE__ */ jsxs("div", { className: "mt-1 grid gap-1 text-[11px] text-muted-foreground", children: [
21619
+ /* @__PURE__ */ jsxs("span", { className: "truncate", children: [
21620
+ "\u2212 ",
21621
+ formatDiffValue2(entry.before)
21622
+ ] }),
21623
+ /* @__PURE__ */ jsxs("span", { className: "truncate", children: [
21624
+ "+ ",
21625
+ formatDiffValue2(entry.after)
21626
+ ] })
21627
+ ] })
21628
+ ]
21629
+ },
21630
+ `${entry.kind}-${entry.path}`
21631
+ )) })
21632
+ ]
21633
+ }
21634
+ )
21635
+ ] }),
21636
+ /* @__PURE__ */ jsxs("section", { "aria-labelledby": "fg-git-pushes", children: [
21637
+ /* @__PURE__ */ jsx(
21638
+ "h3",
21639
+ {
21640
+ id: "fg-git-pushes",
21641
+ className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground",
21642
+ children: tr("fluidgrids.builder.git.pushesTitle", "Pushes")
21643
+ }
21644
+ ),
21645
+ pushes.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: tr(
21646
+ "fluidgrids.builder.git.pushesEmpty",
21647
+ "Nothing was pushed from FluidGrids yet."
21648
+ ) }) : /* @__PURE__ */ jsx("ul", { className: "space-y-1.5", children: pushes.map((item) => /* @__PURE__ */ jsxs(
21649
+ "li",
21650
+ {
21651
+ className: "flex items-start justify-between gap-2 text-xs",
21652
+ "data-testid": "fluidgrids-git-push",
21653
+ children: [
21654
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
21655
+ /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1 text-foreground", children: [
21656
+ /* @__PURE__ */ jsx(GitCommitHorizontal, { className: "size-3.5 shrink-0 text-muted-foreground" }),
21657
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: item.version }),
21658
+ item.environment ? ` \u2192 ${item.environment}` : "",
21659
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
21660
+ "\xB7 ",
21661
+ triggerLabel(item.trigger)
21662
+ ] })
21663
+ ] }),
21664
+ /* @__PURE__ */ jsx("p", { className: "truncate text-muted-foreground", children: [
21665
+ shortSha(item.commitSha),
21666
+ nameOf(item.pushedById),
21667
+ relativeTime(item.pushedAt)
21668
+ ].filter(Boolean).join(" \xB7 ") }),
21669
+ item.errorMessage && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-[var(--color-status-error-text)]", children: item.errorMessage })
21670
+ ] }),
21671
+ /* @__PURE__ */ jsx(
21672
+ Badge,
21673
+ {
21674
+ variant: pushStatusVariant(item.status),
21675
+ className: "h-5 shrink-0 px-1.5 py-0 text-[10px]",
21676
+ children: item.status === "PUSHED" ? tr("fluidgrids.builder.git.statusPushed", "Pushed") : item.status === "UNCHANGED" ? tr(
21677
+ "fluidgrids.builder.git.statusUnchanged",
21678
+ "Up to date"
21679
+ ) : tr(
21680
+ "fluidgrids.builder.git.statusFailed",
21681
+ "Failed"
21682
+ )
21683
+ }
21684
+ )
21685
+ ]
21686
+ },
21687
+ item.id
21688
+ )) })
21689
+ ] })
21690
+ ] })
21691
+ ]
21692
+ }
21693
+ ),
21694
+ /* @__PURE__ */ jsx(
21695
+ ConnectionFormDialog,
21696
+ {
21697
+ open: newConnectionOpen,
21698
+ onClose: () => setNewConnectionOpen(false),
21699
+ providerKey: GITHUB_PROVIDER_KEY,
21700
+ defaultName: "GitHub",
21701
+ onConnectionCreated: (created) => {
21702
+ setNewConnectionOpen(false);
21703
+ setForm((prev) => ({ ...prev, connectionId: created.id }));
21704
+ void refetchConnections();
21705
+ }
21706
+ }
21707
+ ),
21708
+ /* @__PURE__ */ jsx(
21709
+ AlertDialog,
21710
+ {
21711
+ open: confirm !== null,
21712
+ onOpenChange: (open) => !open && setConfirm(null),
21713
+ children: /* @__PURE__ */ jsxs(AlertDialogContent, { className: "z-[120]", children: [
21714
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
21715
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: confirm?.kind === "unlink" ? tr("fluidgrids.builder.git.unlinkTitle", "Unlink repository?") : `${tr("fluidgrids.builder.git.restoreTitle", "Restore commit")} ${confirm?.kind === "restore" ? shortSha(confirm.sha) : ""}` }),
21716
+ /* @__PURE__ */ jsx(AlertDialogDescription, { children: confirm?.kind === "unlink" ? tr(
21717
+ "fluidgrids.builder.git.unlinkBody",
21718
+ "Publishing and promotions stop committing to Git for every workflow in this workspace. Commits already made stay in the repository."
21719
+ ) : tr(
21720
+ "fluidgrids.builder.git.restoreBody",
21721
+ "The workflow file at this commit becomes a new draft version. No existing version changes and nothing is activated."
21722
+ ) })
21723
+ ] }),
21724
+ /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
21725
+ /* @__PURE__ */ jsx(AlertDialogCancel, { children: tr("fluidgrids.builder.git.cancel", "Cancel") }),
21726
+ /* @__PURE__ */ jsx(AlertDialogAction, { onClick: () => void handleConfirm(), children: confirm?.kind === "unlink" ? tr("fluidgrids.builder.git.unlink", "Unlink") : tr("fluidgrids.builder.git.restore", "Restore as draft") })
21727
+ ] })
21728
+ ] })
21729
+ }
21730
+ )
21731
+ ]
21732
+ }
21733
+ );
21734
+ }
20757
21735
 
20758
21736
  // src/builder/lib/ai-draft.ts
20759
21737
  var AI_DRAFT_POLL_INTERVAL_MS = 2500;
@@ -20853,7 +21831,7 @@ function canvasHasWork(nodes) {
20853
21831
  }
20854
21832
 
20855
21833
  // src/builder/hooks/use-ai-workflow-draft.ts
20856
- function messageOf2(error) {
21834
+ function messageOf3(error) {
20857
21835
  return error instanceof Error ? error.message : String(error);
20858
21836
  }
20859
21837
  function useAiWorkflowDraft() {
@@ -20910,7 +21888,7 @@ function useAiWorkflowDraft() {
20910
21888
  setIsGenerating(false);
20911
21889
  } catch (pollError) {
20912
21890
  if (run !== runRef.current) return;
20913
- setError(messageOf2(pollError));
21891
+ setError(messageOf3(pollError));
20914
21892
  setIsGenerating(false);
20915
21893
  }
20916
21894
  },
@@ -20947,7 +21925,7 @@ function useAiWorkflowDraft() {
20947
21925
  );
20948
21926
  } catch (startError) {
20949
21927
  if (run !== runRef.current) return;
20950
- setError(messageOf2(startError));
21928
+ setError(messageOf3(startError));
20951
21929
  setIsGenerating(false);
20952
21930
  }
20953
21931
  },
@@ -21655,6 +22633,7 @@ var FlowBuilder = ({ readOnly = false }) => {
21655
22633
  !isReadOnly && /* @__PURE__ */ jsx(AiDraftPanel, {}),
21656
22634
  /* @__PURE__ */ jsx(VersionHistoryPanel, {}),
21657
22635
  /* @__PURE__ */ jsx(EnvironmentsPanel, {}),
22636
+ /* @__PURE__ */ jsx(GitSyncPanel, {}),
21658
22637
  /* @__PURE__ */ jsx(SaveFlowButton, {}),
21659
22638
  /* @__PURE__ */ jsx(ActivateWorkflowButton, {}),
21660
22639
  /* @__PURE__ */ jsx(
@@ -21677,6 +22656,7 @@ var FlowBuilder = ({ readOnly = false }) => {
21677
22656
  /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1 lg:hidden", children: [
21678
22657
  !isReadOnly && /* @__PURE__ */ jsx(AiDraftPanel, { compact: true }),
21679
22658
  /* @__PURE__ */ jsx(EnvironmentsPanel, { compact: true }),
22659
+ /* @__PURE__ */ jsx(GitSyncPanel, { compact: true }),
21680
22660
  /* @__PURE__ */ jsxs(DropdownMenu, { children: [
21681
22661
  /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
21682
22662
  Button$1,