@burdenoff/fluidgrids-fe-sdk 2026.1029.7 → 2026.1030.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ExecutionDetailPage-GWBKNRLH.js +22 -0
- package/dist/{ExecutionsListPage-7NMW6S5A.js → ExecutionsListPage-XWHKPZ4I.js} +8 -8
- package/dist/{WorkflowBuilder-RXVUBDZJ.js → WorkflowBuilder-G7BNJ2PN.js} +11 -11
- package/dist/{WorkflowsList-5U74DYQ3.js → WorkflowsList-MJ7PDJSS.js} +8 -8
- package/dist/{chunk-BZTVAUOQ.js → chunk-2QHNITN3.js} +3 -3
- package/dist/{chunk-OJPJ2QXD.js → chunk-4TSFUGBA.js} +2 -2
- package/dist/{chunk-DEGCZCHP.js → chunk-64ZGBHEP.js} +2 -2
- package/dist/{chunk-IDGDMJMJ.js → chunk-7QNSQMAX.js} +3 -3
- package/dist/{chunk-RZR3BDDZ.js → chunk-ARQDZCJH.js} +162 -99
- package/dist/{chunk-4MHWFZJV.js → chunk-B26HQYWR.js} +1 -1
- package/dist/{chunk-NL4JQFFA.js → chunk-BTQJL6D7.js} +1 -1
- package/dist/{chunk-6SZXW2BF.js → chunk-C3XYKJ46.js} +32 -15
- package/dist/{chunk-Z4YQTXCD.js → chunk-CNDA4WB7.js} +4 -4
- package/dist/{chunk-ZE5KS2YL.js → chunk-FLN3KOG5.js} +2 -2
- package/dist/{chunk-YTUIGIVP.js → chunk-GQTEHWWY.js} +12 -8
- package/dist/{chunk-TYYSYSAL.js → chunk-H4AZBUWM.js} +1 -1
- package/dist/{chunk-7CTIVBBA.js → chunk-HGLZOGGQ.js} +4 -4
- package/dist/{chunk-FFOZ5VUJ.js → chunk-IZCDTR5O.js} +1 -1
- package/dist/{chunk-HAFUSBYP.js → chunk-JFAGVOWU.js} +8 -8
- package/dist/{chunk-US4LPOB3.js → chunk-LAV7QQ7Q.js} +1 -1
- package/dist/{chunk-KANARCPR.js → chunk-MJHWM6J6.js} +3 -3
- package/dist/{chunk-VIZ7W5WJ.js → chunk-QZS6DTJX.js} +5 -5
- package/dist/{chunk-SZTKLZZR.js → chunk-R5VDESMI.js} +4 -4
- package/dist/{chunk-E7UAY3JJ.js → chunk-SJBJ3D7A.js} +1 -1
- package/dist/{chunk-AXMEATLG.js → chunk-TBRZKV2D.js} +2 -2
- package/dist/{chunk-EGIEFGOV.js → chunk-WHTA6KAS.js} +822 -62
- package/dist/{chunk-K4BF7PAW.js → chunk-Y6IEOGIU.js} +301 -30
- package/dist/{chunk-6TMLIQHB.js → chunk-Z3FYBDE3.js} +30 -1
- package/dist/{chunk-LAVHDFHI.js → chunk-Z6I6A47T.js} +1 -1
- package/dist/executions.js +12 -12
- package/dist/history.js +4 -4
- package/dist/index.d.ts +210 -3
- package/dist/index.js +35 -35
- package/dist/nodes.js +2 -2
- package/dist/observability.js +6 -6
- package/dist/settings.js +6 -6
- package/dist/templates.js +10 -10
- package/dist/{workflow-builder-CZFczcE7.d.ts → workflow-builder-BMGVSwpy.d.ts} +7 -0
- package/dist/workflow-builder.d.ts +1 -1
- package/dist/workflow-builder.js +12 -12
- package/dist/workflows-list.js +8 -8
- package/package.json +3 -3
- package/dist/ExecutionDetailPage-L7LZYFOT.js +0 -22
|
@@ -2,18 +2,18 @@ import { deepClone, applyTemplateInputs } from './chunk-GEMNVFPK.js';
|
|
|
2
2
|
import { Input } from './chunk-IEVSFCFS.js';
|
|
3
3
|
import { Dialog as Dialog$1, DialogContent as DialogContent$1, DialogHeader as DialogHeader$1, DialogTitle as DialogTitle$1, DialogDescription as DialogDescription$1, DialogFooter } from './chunk-YXMFRQYV.js';
|
|
4
4
|
import { Badge as Badge$1, Button } from './chunk-BQXVT32O.js';
|
|
5
|
-
import { QuotaExhaustedDialog, useWorkflowCodeExport, orderedExportFiles, fileBaseName, downloadWorkflowCodeExport, isLosslessReport, sortFidelityItems, isLossyFidelityItem, lineOfNode } from './chunk-
|
|
6
|
-
import { EntityTagPicker, useWorkflowProposal, WorkflowProposalCard, useRunHistory, RunHistoryView, SignalRunDialog } from './chunk-
|
|
5
|
+
import { QuotaExhaustedDialog, useWorkflowCodeExport, orderedExportFiles, fileBaseName, downloadWorkflowCodeExport, isLosslessReport, sortFidelityItems, isLossyFidelityItem, lineOfNode } from './chunk-64ZGBHEP.js';
|
|
6
|
+
import { EntityTagPicker, useWorkflowProposal, WorkflowProposalCard, useRunHistory, RunHistoryView, SignalRunDialog } from './chunk-7QNSQMAX.js';
|
|
7
7
|
import { AiCreditBalanceSlot } from './chunk-Y53CVZVV.js';
|
|
8
|
-
import { useRestoreWorkflowVersion, useWorkflowVersions, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-
|
|
9
|
-
import { isAgentNodeType, AgentDefinitionSelectField } from './chunk-
|
|
8
|
+
import { useRestoreWorkflowVersion, useWorkflowVersions, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-4TSFUGBA.js';
|
|
9
|
+
import { isAgentNodeType, AgentDefinitionSelectField } from './chunk-B26HQYWR.js';
|
|
10
10
|
import { FluidGridsDocsLink } from './chunk-4VJ3MDOV.js';
|
|
11
|
-
import { NODE_CONFIGS, createConfiguredNode, resolveOutputHandles, resolveInputHandles, isNodeConfigsInitialized, useFlowStore, getNodeDetail, useSaveStatusStore, isNodeTypeHiddenFromPalette, liveInternalOperations, resolveNodeOperation, readDisplayOptions, parseParameterValue, canAutosave, STICKY_NOTE_TYPE, isNodeTrigger, resolveUiSpec, RUNS_QUOTA_NAME, throwIfResultError, BILLING_PLANS_PATH, emitUpgradeClicked, FluidGridsContext, emitWorkflowCreated, emitTemplateUsed, ensureNodeConfigsLoaded, registerWorkflowNodeTypes, emitNodeInstalled, useNodeExecution, useDurableFunctionNodesEnabled, onNodeTypesUpdate, NODE_TYPES, getNodeMetadata, useIsValidConnection, refetchStoreNodes, AVAILABLE_NODES, useInsertNode, NODE_TYPE_DRAG_DATA_FORMAT, NODE_OPERATION_DRAG_DATA_FORMAT, CODE_WORKFLOW_NODE_TYPE, SdkPermissionButton,
|
|
11
|
+
import { leaseCallQueue, NODE_CONFIGS, createConfiguredNode, resolveOutputHandles, resolveInputHandles, isNodeConfigsInitialized, useFlowStore, getNodeDetail, useSaveStatusStore, isNodeTypeHiddenFromPalette, liveInternalOperations, resolveNodeOperation, readDisplayOptions, parseParameterValue, canAutosave, STICKY_NOTE_TYPE, isNodeTrigger, resolveUiSpec, RUNS_QUOTA_NAME, throwIfResultError, BILLING_PLANS_PATH, emitUpgradeClicked, FluidGridsContext, emitWorkflowCreated, emitTemplateUsed, ensureNodeConfigsLoaded, useEditLeaseStore, useEditLeaseLocked, registerWorkflowNodeTypes, emitNodeInstalled, useNodeExecution, useDurableFunctionNodesEnabled, onNodeTypesUpdate, NODE_TYPES, getNodeMetadata, useIsValidConnection, BuilderReadOnlyProvider, refetchStoreNodes, AVAILABLE_NODES, EDIT_SESSION_ID, useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS, useInsertNode, NODE_TYPE_DRAG_DATA_FORMAT, NODE_OPERATION_DRAG_DATA_FORMAT, CODE_WORKFLOW_NODE_TYPE, SdkPermissionButton, bannerOf, useSaveWorkflow, useBuilderReadOnly, fieldsFromValue, filterPaletteNodes, useFluidGridsAnalytics, sessionBeforeReload, forgetSessionForReload, releasesOnClose, editStateOf, workflowFingerprint, isLocked, buildStickyNode, useNodeRegistryRevision, fieldsFromIO, readCodeWorkflowState, useCodeWorkflowInterface, clearCodeWorkflowDefinitionCache, isParamVisible, ConnectionSelector, structuredParameterType, nodeAccent, NodeTypeIcon, NODES_METADATA, shouldReleaseForIdle, tickAction, rememberSessionForReload, useDurableParityEnabled, useConnections, ConnectionFormDialog, useFluidGridsFlag, FLUIDGRIDS_FLAGS, saveIndicatorOf, inAppLeaveTarget, declaredOutputFields, subscribeWorkflowSaved, pathToExpression, diagnosticsByPath, interfaceHandlers, declaredSignals, ExpressionEditor, toCodeMirrorDiagnostics, formatParameterValue, leaveNeedsConfirmation, phaseAfterAcquire, buildOperationSeed, describeOperationError, rememberOwnStatusChange, snapshotForSave, childRunIdOf, defaultSourcePath, projectLanguage, changedSourceFiles, isEditableSourcePath, sourceLanguageOf, interfaceFields, isImageIcon, NODES, synchronizeOperationUpdate, NodePropertyPanel, loadDefinitionJson, openRunQuotaUpgradePrompt, isTriggerOperation, canRenderOperationPanel, runPreparationOf, resolveOperationName, savesInPlace, AUTOSAVE_RETRY_DELAYS_MS, AUTOSAVE_DEBOUNCE_MS, rememberRevision, phaseAfterTakeOver } from './chunk-Y6IEOGIU.js';
|
|
12
12
|
import { cn } from './chunk-JL7CASN5.js';
|
|
13
13
|
import { getGlobalStoreClient } from './chunk-PXIFUMLA.js';
|
|
14
14
|
import { getFluidGridsConfig } from './chunk-6AMNEX2Y.js';
|
|
15
15
|
import { GetMyNodeInstallationsDocument, BrowseWorkflowTemplatesDocument, InstallWorkflowNodeDocument, InstallPurchasedNodeDocument, BrowseWorkflowNodesDocument, UninstallWorkflowNodeDocument, CreateStoreOrderDocument, GetNodeInstallableVersionsDocument, OrganizationPrivateNodesDocument, InstallOrganizationPrivateNodeDocument, UninstallOrganizationPrivateNodeDocument } from './chunk-JUUYPCFY.js';
|
|
16
|
-
import { WorkspaceRunsQuotaDocument, NodeSchemaIntrospectionDocument, useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, WorkflowGenerationTemplatesDocument, WorkflowEnvironmentsDocument, WorkflowPromotionRequestsDocument, DeployWorkflowVersionToDevDocument, RequestWorkflowPromotionDocument, ApproveWorkflowPromotionDocument, RejectWorkflowPromotionDocument, CancelWorkflowPromotionDocument, UpdateWorkflowEnvironmentPolicyDocument, WorkflowGitRepositoryDocument, WorkflowGitPushesDocument, WorkflowGitCommitsDocument, LinkWorkflowGitRepositoryDocument, UnlinkWorkflowGitRepositoryDocument, PushWorkflowToGitDocument, WorkflowGitDiffDocument, RestoreWorkflowFromGitDocument, WorkflowAuthoringDocument, NodeStatesDocument, PreviewScheduleDocument, WorkflowVersioningDocument, WorkflowVersionDrainageDocument, ActivateWorkflowVersionCheckedDocument, WorkflowCodeSourceDocument, WorkflowEditLeaseStatusDocument, BuildWorkflowCodeDocument, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument, SetWorkflowRolloutDocument, DeleteWorkflowRolloutDocument } from './chunk-
|
|
16
|
+
import { WorkspaceRunsQuotaDocument, ReleaseWorkflowEditLeaseDocument, NodeSchemaIntrospectionDocument, useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, AcquireWorkflowEditLeaseDocument, WorkflowGenerationTemplatesDocument, WorkflowEnvironmentsDocument, WorkflowPromotionRequestsDocument, DeployWorkflowVersionToDevDocument, RequestWorkflowPromotionDocument, ApproveWorkflowPromotionDocument, RejectWorkflowPromotionDocument, CancelWorkflowPromotionDocument, UpdateWorkflowEnvironmentPolicyDocument, WorkflowGitRepositoryDocument, WorkflowGitPushesDocument, WorkflowGitCommitsDocument, LinkWorkflowGitRepositoryDocument, UnlinkWorkflowGitRepositoryDocument, PushWorkflowToGitDocument, WorkflowGitDiffDocument, RestoreWorkflowFromGitDocument, WorkflowAuthoringDocument, NodeStatesDocument, PreviewScheduleDocument, WorkflowEditLeaseDocument, WorkflowVersioningDocument, WorkflowVersionDrainageDocument, ActivateWorkflowVersionCheckedDocument, WorkflowDefinitionDocument, WorkflowCodeSourceDocument, WorkflowEditLeaseStatusDocument, BuildWorkflowCodeDocument, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument, SetWorkflowRolloutDocument, DeleteWorkflowRolloutDocument, TakeOverWorkflowEditLeaseDocument } from './chunk-GQTEHWWY.js';
|
|
17
17
|
import { useTr } from './chunk-UULVIT25.js';
|
|
18
18
|
import { getFluidGridsClient, waitForFluidGridsClient, getFluidGridsClientSafe } from './chunk-NTWG3CJX.js';
|
|
19
19
|
import { __export } from './chunk-62GWEYJ5.js';
|
|
@@ -30,6 +30,7 @@ import { useQuery, useMutation } from '@apollo/client/react';
|
|
|
30
30
|
import { gql as gql$1 } from '@apollo/client/core';
|
|
31
31
|
import 'jszip';
|
|
32
32
|
import { Sparkles, Star, Download, Eye, Maximize2, Undo2, Redo2, LayoutGrid, Keyboard, Plus, Trash2, GitBranch, Loader2, AlertCircle, Search, RefreshCw, History, GitCompareArrows, Play, RotateCcw, Layers, Upload, Rocket, ArrowRight, Check, X, Link2, Unlink, GitCompare, ExternalLink, GitCommitHorizontal, CircleHelp, CheckCircle2, Package, SlidersHorizontal, Info, Copy, BadgeCheck, DollarSign, Type, Code, Hash, List, Braces, Workflow, PlusCircle, ChevronDown, ChevronRight, AlertTriangle } from 'lucide-react';
|
|
33
|
+
import { create } from 'zustand';
|
|
33
34
|
import { Icon } from '@iconify/react';
|
|
34
35
|
import { toast } from 'sonner';
|
|
35
36
|
import { useShallow, shallow } from 'zustand/shallow';
|
|
@@ -43,7 +44,6 @@ import { ThemeProvider as ThemeProvider$1 } from '@burdenoff/fe-libs/shared/prov
|
|
|
43
44
|
import '@xyflow/react/dist/style.css';
|
|
44
45
|
import { edgeColor, nodeCategoryColor } from '@burdenoff/fe-libs/shared/utils';
|
|
45
46
|
import { buildSchema, GraphQLObjectType, GraphQLInputObjectType, GraphQLEnumType, GraphQLInterfaceType, GraphQLUnionType, GraphQLNonNull, GraphQLList } from 'graphql';
|
|
46
|
-
import { create } from 'zustand';
|
|
47
47
|
import { nanoid as nanoid$1 } from 'nanoid/non-secure';
|
|
48
48
|
import { defineAgentTool, AgentToolError, useAgentTools } from '@burdenoff/fe-libs/shared/agent-tools';
|
|
49
49
|
import CodeMirror, { EditorView } from '@uiw/react-codemirror';
|
|
@@ -21676,6 +21676,266 @@ function FluidGridsGlyph() {
|
|
|
21676
21676
|
}
|
|
21677
21677
|
);
|
|
21678
21678
|
}
|
|
21679
|
+
var useWorkflowReloadStore = create()((set2) => ({
|
|
21680
|
+
nonce: 0,
|
|
21681
|
+
request: () => set2((state) => ({ nonce: state.nonce + 1 }))
|
|
21682
|
+
}));
|
|
21683
|
+
function requestWorkflowReload() {
|
|
21684
|
+
useWorkflowReloadStore.getState().request();
|
|
21685
|
+
}
|
|
21686
|
+
var TICK_MS = 5e3;
|
|
21687
|
+
var UNAVAILABLE = {
|
|
21688
|
+
available: false,
|
|
21689
|
+
held: false,
|
|
21690
|
+
heldBySelf: false
|
|
21691
|
+
};
|
|
21692
|
+
var leaseCalls = leaseCallQueue();
|
|
21693
|
+
function sessionStore() {
|
|
21694
|
+
try {
|
|
21695
|
+
return typeof window === "undefined" ? void 0 : window.sessionStorage;
|
|
21696
|
+
} catch {
|
|
21697
|
+
return void 0;
|
|
21698
|
+
}
|
|
21699
|
+
}
|
|
21700
|
+
function openWorkflow() {
|
|
21701
|
+
const { workflow } = useFlowStore.getState();
|
|
21702
|
+
const workflowKey = workflow.workflowKey || workflow.id;
|
|
21703
|
+
if (!workflow.dbId || !workflowKey) return null;
|
|
21704
|
+
return { workflowKey, version: workflow.version ?? "" };
|
|
21705
|
+
}
|
|
21706
|
+
function snapshotOpen() {
|
|
21707
|
+
const open2 = openWorkflow();
|
|
21708
|
+
if (!open2) return null;
|
|
21709
|
+
return { ...open2, generation: useFlowStore.getState().loadGeneration };
|
|
21710
|
+
}
|
|
21711
|
+
function stillOpen(snapshot) {
|
|
21712
|
+
const now = openWorkflow();
|
|
21713
|
+
return now?.workflowKey === snapshot.workflowKey && now.version === snapshot.version && useFlowStore.getState().loadGeneration === snapshot.generation && useEditLeaseStore.getState().workflowKey === snapshot.workflowKey;
|
|
21714
|
+
}
|
|
21715
|
+
function discardStaleAnswer(snapshot, answer) {
|
|
21716
|
+
if (!answer.heldBySelf) return;
|
|
21717
|
+
if (useEditLeaseStore.getState().workflowKey === snapshot.workflowKey) return;
|
|
21718
|
+
void releaseLease(snapshot.workflowKey);
|
|
21719
|
+
}
|
|
21720
|
+
function acquireLease(workflowKey, version2, resumeSessionId) {
|
|
21721
|
+
return leaseCalls.run(workflowKey, async () => {
|
|
21722
|
+
try {
|
|
21723
|
+
const { data } = throwIfResultError(
|
|
21724
|
+
await getFluidGridsClient().mutate({
|
|
21725
|
+
mutation: AcquireWorkflowEditLeaseDocument,
|
|
21726
|
+
variables: {
|
|
21727
|
+
input: {
|
|
21728
|
+
workflowKey,
|
|
21729
|
+
sessionId: EDIT_SESSION_ID,
|
|
21730
|
+
version: version2 || null,
|
|
21731
|
+
resumeSessionId
|
|
21732
|
+
}
|
|
21733
|
+
}
|
|
21734
|
+
})
|
|
21735
|
+
);
|
|
21736
|
+
return data?.acquireWorkflowEditLease ?? UNAVAILABLE;
|
|
21737
|
+
} catch {
|
|
21738
|
+
return UNAVAILABLE;
|
|
21739
|
+
}
|
|
21740
|
+
});
|
|
21741
|
+
}
|
|
21742
|
+
async function peekLease(workflowKey) {
|
|
21743
|
+
try {
|
|
21744
|
+
const { data } = throwIfResultError(
|
|
21745
|
+
await getFluidGridsClient().query({
|
|
21746
|
+
query: WorkflowEditLeaseDocument,
|
|
21747
|
+
variables: { workflowKey, sessionId: EDIT_SESSION_ID },
|
|
21748
|
+
fetchPolicy: "network-only"
|
|
21749
|
+
})
|
|
21750
|
+
);
|
|
21751
|
+
return data?.workflowEditLease ?? UNAVAILABLE;
|
|
21752
|
+
} catch {
|
|
21753
|
+
return UNAVAILABLE;
|
|
21754
|
+
}
|
|
21755
|
+
}
|
|
21756
|
+
function sendRelease(workflowKey, keepalive) {
|
|
21757
|
+
return getFluidGridsClient().mutate({
|
|
21758
|
+
mutation: ReleaseWorkflowEditLeaseDocument,
|
|
21759
|
+
variables: { workflowKey, sessionId: EDIT_SESSION_ID },
|
|
21760
|
+
...keepalive ? { context: { fetchOptions: { keepalive: true } } } : {}
|
|
21761
|
+
}).then(() => void 0).catch(() => void 0);
|
|
21762
|
+
}
|
|
21763
|
+
function releaseLease(workflowKey) {
|
|
21764
|
+
return leaseCalls.run(workflowKey, () => sendRelease(workflowKey, false));
|
|
21765
|
+
}
|
|
21766
|
+
function recordAcquired(answer) {
|
|
21767
|
+
const lease = useEditLeaseStore.getState();
|
|
21768
|
+
lease.actions.record(phaseAfterAcquire(lease.phase, answer), answer);
|
|
21769
|
+
}
|
|
21770
|
+
async function takeOverEditLease() {
|
|
21771
|
+
const open2 = snapshotOpen();
|
|
21772
|
+
if (!open2) return;
|
|
21773
|
+
const answer = await leaseCalls.run(open2.workflowKey, async () => {
|
|
21774
|
+
try {
|
|
21775
|
+
const { data } = throwIfResultError(
|
|
21776
|
+
await getFluidGridsClient().mutate({
|
|
21777
|
+
mutation: TakeOverWorkflowEditLeaseDocument,
|
|
21778
|
+
variables: {
|
|
21779
|
+
input: {
|
|
21780
|
+
workflowKey: open2.workflowKey,
|
|
21781
|
+
sessionId: EDIT_SESSION_ID,
|
|
21782
|
+
version: open2.version || null
|
|
21783
|
+
}
|
|
21784
|
+
}
|
|
21785
|
+
})
|
|
21786
|
+
);
|
|
21787
|
+
return data?.takeOverWorkflowEditLease ?? UNAVAILABLE;
|
|
21788
|
+
} catch {
|
|
21789
|
+
return UNAVAILABLE;
|
|
21790
|
+
}
|
|
21791
|
+
});
|
|
21792
|
+
if (!stillOpen(open2)) {
|
|
21793
|
+
discardStaleAnswer(open2, answer);
|
|
21794
|
+
return;
|
|
21795
|
+
}
|
|
21796
|
+
const next = phaseAfterTakeOver(answer);
|
|
21797
|
+
useEditLeaseStore.getState().actions.record(next, answer);
|
|
21798
|
+
if (next === "reloading") requestWorkflowReload();
|
|
21799
|
+
}
|
|
21800
|
+
async function resumeEditLease() {
|
|
21801
|
+
const open2 = snapshotOpen();
|
|
21802
|
+
if (!open2) return;
|
|
21803
|
+
const answer = await acquireLease(open2.workflowKey, open2.version, null);
|
|
21804
|
+
if (!stillOpen(open2)) {
|
|
21805
|
+
discardStaleAnswer(open2, answer);
|
|
21806
|
+
return;
|
|
21807
|
+
}
|
|
21808
|
+
recordAcquired(answer);
|
|
21809
|
+
}
|
|
21810
|
+
function releaseLeaseKeptForReload() {
|
|
21811
|
+
const lease = useEditLeaseStore.getState();
|
|
21812
|
+
const workflowKey = lease.workflowKey;
|
|
21813
|
+
if (lease.phase !== "reloading" || !workflowKey) return;
|
|
21814
|
+
void releaseLease(workflowKey);
|
|
21815
|
+
lease.actions.reset();
|
|
21816
|
+
}
|
|
21817
|
+
function reloadLatestVersion() {
|
|
21818
|
+
const lease = useEditLeaseStore.getState();
|
|
21819
|
+
if (lease.phase === "holding") lease.actions.record("reloading");
|
|
21820
|
+
requestWorkflowReload();
|
|
21821
|
+
}
|
|
21822
|
+
function useEditLease(enabled) {
|
|
21823
|
+
const workflowKey = useFlowStore(
|
|
21824
|
+
(s) => s.workflow.dbId ? s.workflow.workflowKey || s.workflow.id : ""
|
|
21825
|
+
);
|
|
21826
|
+
useEffect(() => {
|
|
21827
|
+
const lease = useEditLeaseStore.getState();
|
|
21828
|
+
if (!enabled || !workflowKey) {
|
|
21829
|
+
lease.actions.reset();
|
|
21830
|
+
return;
|
|
21831
|
+
}
|
|
21832
|
+
lease.actions.begin(workflowKey);
|
|
21833
|
+
let disposed = false;
|
|
21834
|
+
let inFlight = false;
|
|
21835
|
+
let lastRenewAt = 0;
|
|
21836
|
+
let lastPeekAt = 0;
|
|
21837
|
+
let lastInputAt = Date.now();
|
|
21838
|
+
const visible = () => typeof document === "undefined" || document.visibilityState !== "hidden";
|
|
21839
|
+
const phase = () => useEditLeaseStore.getState().phase;
|
|
21840
|
+
const version2 = () => useFlowStore.getState().workflow.version ?? "";
|
|
21841
|
+
const renew = async (resumeSessionId = null) => {
|
|
21842
|
+
if (inFlight) return;
|
|
21843
|
+
inFlight = true;
|
|
21844
|
+
lastRenewAt = Date.now();
|
|
21845
|
+
try {
|
|
21846
|
+
const answer = await acquireLease(
|
|
21847
|
+
workflowKey,
|
|
21848
|
+
version2(),
|
|
21849
|
+
resumeSessionId
|
|
21850
|
+
);
|
|
21851
|
+
if (!disposed && phase() !== "idle") recordAcquired(answer);
|
|
21852
|
+
} finally {
|
|
21853
|
+
inFlight = false;
|
|
21854
|
+
}
|
|
21855
|
+
};
|
|
21856
|
+
const peek = async () => {
|
|
21857
|
+
if (inFlight) return;
|
|
21858
|
+
inFlight = true;
|
|
21859
|
+
lastPeekAt = Date.now();
|
|
21860
|
+
try {
|
|
21861
|
+
const answer = await peekLease(workflowKey);
|
|
21862
|
+
if (disposed || !answer.available) return;
|
|
21863
|
+
const current = useEditLeaseStore.getState();
|
|
21864
|
+
current.actions.record(current.phase, answer);
|
|
21865
|
+
} finally {
|
|
21866
|
+
inFlight = false;
|
|
21867
|
+
}
|
|
21868
|
+
};
|
|
21869
|
+
const tick = () => {
|
|
21870
|
+
if (disposed) return;
|
|
21871
|
+
const now = Date.now();
|
|
21872
|
+
if (shouldReleaseForIdle(phase(), lastInputAt, now)) {
|
|
21873
|
+
useEditLeaseStore.getState().actions.record("idle");
|
|
21874
|
+
void releaseLease(workflowKey);
|
|
21875
|
+
return;
|
|
21876
|
+
}
|
|
21877
|
+
const action = tickAction(
|
|
21878
|
+
phase(),
|
|
21879
|
+
{ lastRenewAt, lastPeekAt, visible: visible() },
|
|
21880
|
+
now
|
|
21881
|
+
);
|
|
21882
|
+
if (action === "renew") void renew();
|
|
21883
|
+
else if (action === "peek") void peek();
|
|
21884
|
+
};
|
|
21885
|
+
const resume = sessionBeforeReload(sessionStore(), workflowKey);
|
|
21886
|
+
forgetSessionForReload(sessionStore(), workflowKey);
|
|
21887
|
+
void renew(resume);
|
|
21888
|
+
const timer = setInterval(tick, TICK_MS);
|
|
21889
|
+
let reloadSeen = false;
|
|
21890
|
+
const unsubscribeFlow = useFlowStore.subscribe((state, previous) => {
|
|
21891
|
+
if (state.loadGeneration !== previous.loadGeneration) reloadSeen = true;
|
|
21892
|
+
if (reloadSeen && state.workflow.dbId && phase() === "reloading") {
|
|
21893
|
+
reloadSeen = false;
|
|
21894
|
+
void renew();
|
|
21895
|
+
}
|
|
21896
|
+
});
|
|
21897
|
+
const onVisible = () => {
|
|
21898
|
+
if (!visible()) return;
|
|
21899
|
+
lastRenewAt = 0;
|
|
21900
|
+
lastPeekAt = 0;
|
|
21901
|
+
tick();
|
|
21902
|
+
};
|
|
21903
|
+
const onInput = () => {
|
|
21904
|
+
lastInputAt = Date.now();
|
|
21905
|
+
};
|
|
21906
|
+
const onPageHide = () => {
|
|
21907
|
+
if (phase() !== "holding") return;
|
|
21908
|
+
rememberSessionForReload(sessionStore(), workflowKey, EDIT_SESSION_ID);
|
|
21909
|
+
void sendRelease(workflowKey, true);
|
|
21910
|
+
};
|
|
21911
|
+
const onPageShow = (event) => {
|
|
21912
|
+
if (event.persisted) void renew(EDIT_SESSION_ID);
|
|
21913
|
+
};
|
|
21914
|
+
const inputEvents = ["pointerdown", "keydown", "wheel"];
|
|
21915
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
21916
|
+
window.addEventListener("online", onVisible);
|
|
21917
|
+
window.addEventListener("pagehide", onPageHide);
|
|
21918
|
+
window.addEventListener("pageshow", onPageShow);
|
|
21919
|
+
for (const name of inputEvents) {
|
|
21920
|
+
window.addEventListener(name, onInput, { capture: true, passive: true });
|
|
21921
|
+
}
|
|
21922
|
+
return () => {
|
|
21923
|
+
disposed = true;
|
|
21924
|
+
clearInterval(timer);
|
|
21925
|
+
unsubscribeFlow();
|
|
21926
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
21927
|
+
window.removeEventListener("online", onVisible);
|
|
21928
|
+
window.removeEventListener("pagehide", onPageHide);
|
|
21929
|
+
window.removeEventListener("pageshow", onPageShow);
|
|
21930
|
+
for (const name of inputEvents) {
|
|
21931
|
+
window.removeEventListener(name, onInput, { capture: true });
|
|
21932
|
+
}
|
|
21933
|
+
const current = useEditLeaseStore.getState();
|
|
21934
|
+
if (releasesOnClose(current.phase)) void releaseLease(workflowKey);
|
|
21935
|
+
if (current.phase !== "reloading") current.actions.reset();
|
|
21936
|
+
};
|
|
21937
|
+
}, [enabled, workflowKey]);
|
|
21938
|
+
}
|
|
21679
21939
|
function findEdges(node2, connectedEdges) {
|
|
21680
21940
|
const outgoingEdges = connectedEdges.filter(
|
|
21681
21941
|
(edge) => edge.source === node2.id
|
|
@@ -21752,6 +22012,9 @@ function useFlowValidator(onValidate) {
|
|
|
21752
22012
|
}
|
|
21753
22013
|
|
|
21754
22014
|
// src/builder/lib/save-guards.ts
|
|
22015
|
+
function editingLeaseLocked() {
|
|
22016
|
+
return isLocked(useEditLeaseStore.getState().phase);
|
|
22017
|
+
}
|
|
21755
22018
|
function autosaveAllowedFor(workflow) {
|
|
21756
22019
|
return useSaveStatusStore.getState().autosaveEnabled && canAutosave(workflow);
|
|
21757
22020
|
}
|
|
@@ -21768,7 +22031,7 @@ function editStateFor(state) {
|
|
|
21768
22031
|
}
|
|
21769
22032
|
function departingSave(state) {
|
|
21770
22033
|
const status = useSaveStatusStore.getState();
|
|
21771
|
-
if (status.autosavePaused) return null;
|
|
22034
|
+
if (status.autosavePaused || editingLeaseLocked()) return null;
|
|
21772
22035
|
if (editStateFor(state) !== "dirty") return null;
|
|
21773
22036
|
if (!autosaveAllowedFor(state.workflow)) return null;
|
|
21774
22037
|
const { nodes, edges } = state.workflow;
|
|
@@ -21784,7 +22047,9 @@ function unsavedState() {
|
|
|
21784
22047
|
return {
|
|
21785
22048
|
// Checked fresh: the dirty flag trails the last edit by a moment.
|
|
21786
22049
|
dirty: status.dirty || editStateFor(flow) === "dirty",
|
|
21787
|
-
|
|
22050
|
+
// A paused autosave (a refused save), or a page someone else is editing
|
|
22051
|
+
// from, does not save on the way out either.
|
|
22052
|
+
autosaveAllowed: autosaveAllowedFor(flow.workflow) && !status.autosavePaused && !editingLeaseLocked(),
|
|
21788
22053
|
incomplete: status.incomplete || !isFlowComplete(flow.workflow.nodes, flow.workflow.edges),
|
|
21789
22054
|
failed: status.failure !== null
|
|
21790
22055
|
};
|
|
@@ -21814,6 +22079,10 @@ function watchLeavingLosesChanges(onChange, delayMs) {
|
|
|
21814
22079
|
if (timer) clearTimeout(timer);
|
|
21815
22080
|
timer = setTimeout(settle2, delayMs);
|
|
21816
22081
|
};
|
|
22082
|
+
const settleNow = () => {
|
|
22083
|
+
if (timer) clearTimeout(timer);
|
|
22084
|
+
settle2();
|
|
22085
|
+
};
|
|
21817
22086
|
settle2();
|
|
21818
22087
|
const unsubscribeFlow = useFlowStore.subscribe((state, previous) => {
|
|
21819
22088
|
if (!touchesSavedFields(state, previous)) return;
|
|
@@ -21821,9 +22090,13 @@ function watchLeavingLosesChanges(onChange, delayMs) {
|
|
|
21821
22090
|
settleLater();
|
|
21822
22091
|
});
|
|
21823
22092
|
const unsubscribeStatus = useSaveStatusStore.subscribe(settleLater);
|
|
22093
|
+
const unsubscribeLease = useEditLeaseStore.subscribe((state, previous) => {
|
|
22094
|
+
if (isLocked(state.phase) !== isLocked(previous.phase)) settleNow();
|
|
22095
|
+
});
|
|
21824
22096
|
return () => {
|
|
21825
22097
|
unsubscribeFlow();
|
|
21826
22098
|
unsubscribeStatus();
|
|
22099
|
+
unsubscribeLease();
|
|
21827
22100
|
if (timer) clearTimeout(timer);
|
|
21828
22101
|
};
|
|
21829
22102
|
}
|
|
@@ -31501,6 +31774,7 @@ function CustomDeletableEdge(props) {
|
|
|
31501
31774
|
markerEnd
|
|
31502
31775
|
} = props;
|
|
31503
31776
|
const { setEdges, getEdges, getNode } = useReactFlow();
|
|
31777
|
+
const readOnly = useBuilderReadOnly();
|
|
31504
31778
|
const tr = useTr();
|
|
31505
31779
|
const nodeStatuses = useFlowStore(
|
|
31506
31780
|
useShallow((s) => s.execution.nodeStatuses)
|
|
@@ -31522,6 +31796,7 @@ function CustomDeletableEdge(props) {
|
|
|
31522
31796
|
targetPosition
|
|
31523
31797
|
});
|
|
31524
31798
|
useEffect(() => {
|
|
31799
|
+
if (readOnly) return;
|
|
31525
31800
|
const edges = getEdges();
|
|
31526
31801
|
const currentEdge = edges?.find((e) => e.id === id);
|
|
31527
31802
|
if (currentEdge && !currentEdge.conditions) {
|
|
@@ -31534,10 +31809,13 @@ function CustomDeletableEdge(props) {
|
|
|
31534
31809
|
});
|
|
31535
31810
|
});
|
|
31536
31811
|
}
|
|
31537
|
-
}, [id, setEdges, getEdges]);
|
|
31538
|
-
const [
|
|
31539
|
-
const [
|
|
31540
|
-
const [
|
|
31812
|
+
}, [id, setEdges, getEdges, readOnly]);
|
|
31813
|
+
const [mapperRequested, setShowModal] = useState(false);
|
|
31814
|
+
const [conditionRequested, setConditionShowModal] = useState(false);
|
|
31815
|
+
const [hovered, setIsHovered] = useState(false);
|
|
31816
|
+
const showModal = mapperRequested && !readOnly;
|
|
31817
|
+
const showConditionModal = conditionRequested && !readOnly;
|
|
31818
|
+
const isHovered = hovered && !readOnly;
|
|
31541
31819
|
const [sourceSchema, setSourceSchema] = useState(null);
|
|
31542
31820
|
const [targetSchema, setTargetSchema] = useState(null);
|
|
31543
31821
|
const [isLoadingSchemas, setIsLoadingSchemas] = useState(false);
|
|
@@ -33122,7 +33400,8 @@ function ControlBtn({
|
|
|
33122
33400
|
function CanvasControls() {
|
|
33123
33401
|
const tr = useTr();
|
|
33124
33402
|
const searchParams = new URLSearchParams(window.location.search);
|
|
33125
|
-
const
|
|
33403
|
+
const leaseLocked = useEditLeaseLocked();
|
|
33404
|
+
const isReadOnly = searchParams.get("readonly") === "true" || leaseLocked;
|
|
33126
33405
|
const [showMinimap, setShowMinimap] = useState(false);
|
|
33127
33406
|
const [edgesHidden, setEdgesHidden] = useState(false);
|
|
33128
33407
|
const [shortcutsHelpOpen, setShortcutsHelpOpen] = useState(false);
|
|
@@ -34569,6 +34848,24 @@ function nodeSubtitle(node2) {
|
|
|
34569
34848
|
const data = asRecord(node2?.data);
|
|
34570
34849
|
return typeof data?.operation_name === "string" ? data.operation_name : void 0;
|
|
34571
34850
|
}
|
|
34851
|
+
function leaveUnchanged() {
|
|
34852
|
+
}
|
|
34853
|
+
function readOnlyNodeConfig(api) {
|
|
34854
|
+
return {
|
|
34855
|
+
...api,
|
|
34856
|
+
writeFixed: leaveUnchanged,
|
|
34857
|
+
writeMapping: leaveUnchanged,
|
|
34858
|
+
handleConnectionsChange: leaveUnchanged,
|
|
34859
|
+
toggleMode: leaveUnchanged,
|
|
34860
|
+
// A field dropped on a read-only form is refused, not opened as a link.
|
|
34861
|
+
handleDropOnParam: () => (e) => e.preventDefault(),
|
|
34862
|
+
writeErrorConfig: leaveUnchanged,
|
|
34863
|
+
writeNotes: leaveUnchanged,
|
|
34864
|
+
writeExecutionSettings: leaveUnchanged,
|
|
34865
|
+
writeNodeDataField: leaveUnchanged,
|
|
34866
|
+
readOnly: true
|
|
34867
|
+
};
|
|
34868
|
+
}
|
|
34572
34869
|
function runOnOf(executionSettings) {
|
|
34573
34870
|
const runOn = asRecord(executionSettings?.runOn);
|
|
34574
34871
|
return runOn?.kind === "runner" && typeof runOn.runnerId === "string" ? { kind: "runner", runnerId: runOn.runnerId } : null;
|
|
@@ -34577,6 +34874,7 @@ function useNodeConfig(target, options) {
|
|
|
34577
34874
|
const nodes = useFlowStore((s) => s.workflow.nodes);
|
|
34578
34875
|
const edges = useFlowStore((s) => s.workflow.edges);
|
|
34579
34876
|
const workflowKey = useFlowStore((s) => s.workflow.workflowKey);
|
|
34877
|
+
const readOnly = useBuilderReadOnly();
|
|
34580
34878
|
const executionLogs = useFlowStore((s) => s.execution.logs);
|
|
34581
34879
|
const currentRunId = useFlowStore((s) => s.execution.currentRunId);
|
|
34582
34880
|
const nodeId2 = target?.nodeId;
|
|
@@ -34841,7 +35139,7 @@ function useNodeConfig(target, options) {
|
|
|
34841
35139
|
setModeByField((m) => ({ ...m, [field]: "expression" }));
|
|
34842
35140
|
}
|
|
34843
35141
|
};
|
|
34844
|
-
|
|
35142
|
+
const api = {
|
|
34845
35143
|
node: node2,
|
|
34846
35144
|
nodes,
|
|
34847
35145
|
incoming,
|
|
@@ -34882,8 +35180,10 @@ function useNodeConfig(target, options) {
|
|
|
34882
35180
|
executeOnce,
|
|
34883
35181
|
runOn,
|
|
34884
35182
|
writeExecutionSettings,
|
|
34885
|
-
writeNodeDataField
|
|
35183
|
+
writeNodeDataField,
|
|
35184
|
+
readOnly: false
|
|
34886
35185
|
};
|
|
35186
|
+
return readOnly ? readOnlyNodeConfig(api) : api;
|
|
34887
35187
|
}
|
|
34888
35188
|
|
|
34889
35189
|
// src/builder/agentTools.ts
|
|
@@ -35993,7 +36293,7 @@ function CodeEditorField({
|
|
|
35993
36293
|
placeholder,
|
|
35994
36294
|
label,
|
|
35995
36295
|
onChange,
|
|
35996
|
-
readOnly = false,
|
|
36296
|
+
readOnly: readOnlyProp = false,
|
|
35997
36297
|
diagnostics,
|
|
35998
36298
|
minHeight = "180px",
|
|
35999
36299
|
maxHeight = "420px",
|
|
@@ -36002,6 +36302,8 @@ function CodeEditorField({
|
|
|
36002
36302
|
}) {
|
|
36003
36303
|
const dark = useIsDarkTheme();
|
|
36004
36304
|
const withLint = diagnostics !== void 0;
|
|
36305
|
+
const builderReadOnly = useBuilderReadOnly();
|
|
36306
|
+
const readOnly = readOnlyProp || builderReadOnly;
|
|
36005
36307
|
const extensions = useMemo(
|
|
36006
36308
|
() => [
|
|
36007
36309
|
languageExtension(language),
|
|
@@ -36298,11 +36600,6 @@ function useWebhookTestListener(target, onReceived) {
|
|
|
36298
36600
|
}, [workflowKey, version2, triggerNodeId, stopOpenListener]);
|
|
36299
36601
|
return { listener, busy, error: error62, listen, stop };
|
|
36300
36602
|
}
|
|
36301
|
-
var BuilderReadOnlyContext = createContext(false);
|
|
36302
|
-
var BuilderReadOnlyProvider = BuilderReadOnlyContext.Provider;
|
|
36303
|
-
function useBuilderReadOnly() {
|
|
36304
|
-
return useContext(BuilderReadOnlyContext);
|
|
36305
|
-
}
|
|
36306
36603
|
var SECTION_LABEL2 = "text-[11px] font-semibold uppercase tracking-[0.12em] text-muted-foreground";
|
|
36307
36604
|
function isSafeHttpUrl(value) {
|
|
36308
36605
|
try {
|
|
@@ -39717,6 +40014,21 @@ function Field({
|
|
|
39717
40014
|
children
|
|
39718
40015
|
] });
|
|
39719
40016
|
}
|
|
40017
|
+
function ReadOnlyFieldset({
|
|
40018
|
+
className,
|
|
40019
|
+
children
|
|
40020
|
+
}) {
|
|
40021
|
+
const readOnly = useBuilderReadOnly();
|
|
40022
|
+
return /* @__PURE__ */ jsx(
|
|
40023
|
+
"fieldset",
|
|
40024
|
+
{
|
|
40025
|
+
disabled: readOnly,
|
|
40026
|
+
"data-readonly": readOnly ? "true" : void 0,
|
|
40027
|
+
className: cn$1("m-0 min-w-0 border-0 p-0", className),
|
|
40028
|
+
children
|
|
40029
|
+
}
|
|
40030
|
+
);
|
|
40031
|
+
}
|
|
39720
40032
|
function useMediaQuery(query) {
|
|
39721
40033
|
const [matches, setMatches] = React4.useState(false);
|
|
39722
40034
|
React4.useEffect(() => {
|
|
@@ -40910,7 +41222,7 @@ function NodeDetailView() {
|
|
|
40910
41222
|
const [pinNodeData, { loading: pinning }] = usePinNodeDataMutation();
|
|
40911
41223
|
const [unpinNodeData, { loading: unpinning }] = useUnpinNodeDataMutation();
|
|
40912
41224
|
const togglePin = async () => {
|
|
40913
|
-
if (!workflowDbId || !ndv?.nodeId) return;
|
|
41225
|
+
if (!workflowDbId || !ndv?.nodeId || config2.readOnly) return;
|
|
40914
41226
|
try {
|
|
40915
41227
|
if (isPinned) {
|
|
40916
41228
|
await unpinNodeData({
|
|
@@ -41282,21 +41594,21 @@ function NodeDetailView() {
|
|
|
41282
41594
|
{
|
|
41283
41595
|
className: "min-h-0 flex-1 space-y-4 overflow-auto p-4",
|
|
41284
41596
|
"data-testid": "ndv-params",
|
|
41285
|
-
children: /* @__PURE__ */ jsx(
|
|
41597
|
+
children: /* @__PURE__ */ jsx(ReadOnlyFieldset, { className: "space-y-4", children: /* @__PURE__ */ jsx(
|
|
41286
41598
|
NodeParamsForm,
|
|
41287
41599
|
{
|
|
41288
41600
|
config: config2,
|
|
41289
41601
|
nodeType: node2?.type,
|
|
41290
41602
|
testIdPrefix: "ndv"
|
|
41291
41603
|
}
|
|
41292
|
-
)
|
|
41604
|
+
) })
|
|
41293
41605
|
}
|
|
41294
41606
|
) : /* @__PURE__ */ jsx(
|
|
41295
41607
|
"div",
|
|
41296
41608
|
{
|
|
41297
41609
|
className: "min-h-0 flex-1 overflow-auto p-4",
|
|
41298
41610
|
"data-testid": "ndv-settings",
|
|
41299
|
-
children: /* @__PURE__ */ jsx(NodeSettingsPanel, { config: config2 })
|
|
41611
|
+
children: /* @__PURE__ */ jsx(ReadOnlyFieldset, { children: /* @__PURE__ */ jsx(NodeSettingsPanel, { config: config2 }) })
|
|
41300
41612
|
}
|
|
41301
41613
|
)
|
|
41302
41614
|
]
|
|
@@ -41340,7 +41652,7 @@ function NodeDetailView() {
|
|
|
41340
41652
|
{
|
|
41341
41653
|
type: "button",
|
|
41342
41654
|
onClick: togglePin,
|
|
41343
|
-
disabled: pinning || unpinning,
|
|
41655
|
+
disabled: pinning || unpinning || config2.readOnly,
|
|
41344
41656
|
"data-testid": "ndv-pin-toggle",
|
|
41345
41657
|
title: tr(
|
|
41346
41658
|
isPinned ? "fluidgrids.builder.nodeDetailView.unpinHint" : "fluidgrids.builder.nodeDetailView.pinHint",
|
|
@@ -41983,14 +42295,14 @@ function QuickConfigPanelInner({
|
|
|
41983
42295
|
setShowMapper(true);
|
|
41984
42296
|
}
|
|
41985
42297
|
},
|
|
41986
|
-
children: /* @__PURE__ */ jsx(
|
|
42298
|
+
children: /* @__PURE__ */ jsx(ReadOnlyFieldset, { className: "space-y-4", children: /* @__PURE__ */ jsx(
|
|
41987
42299
|
NodeParamsForm,
|
|
41988
42300
|
{
|
|
41989
42301
|
config: config2,
|
|
41990
42302
|
nodeType: node2.type,
|
|
41991
42303
|
testIdPrefix: "qc"
|
|
41992
42304
|
}
|
|
41993
|
-
)
|
|
42305
|
+
) })
|
|
41994
42306
|
}
|
|
41995
42307
|
),
|
|
41996
42308
|
/* @__PURE__ */ jsx(
|
|
@@ -42115,10 +42427,11 @@ function useAutosave(readOnly) {
|
|
|
42115
42427
|
});
|
|
42116
42428
|
};
|
|
42117
42429
|
const autosave = createAutosaveRunner({
|
|
42118
|
-
mayStart: () => !status().autosavePaused && autosaveAllowedFor(useFlowStore.getState().workflow),
|
|
42430
|
+
mayStart: () => !status().autosavePaused && !editingLeaseLocked() && autosaveAllowedFor(useFlowStore.getState().workflow),
|
|
42119
42431
|
gate: () => {
|
|
42120
42432
|
const { workflow } = useFlowStore.getState();
|
|
42121
42433
|
if (status().autosavePaused || !status().dirty) return "skip";
|
|
42434
|
+
if (editingLeaseLocked()) return "skip";
|
|
42122
42435
|
if (!autosaveAllowedFor(workflow)) return "skip";
|
|
42123
42436
|
const complete = isFlowComplete(workflow.nodes, workflow.edges);
|
|
42124
42437
|
actions.setIncomplete(!complete);
|
|
@@ -42175,9 +42488,18 @@ function useAutosave(readOnly) {
|
|
|
42175
42488
|
}
|
|
42176
42489
|
}
|
|
42177
42490
|
);
|
|
42491
|
+
const unsubscribeLease = useEditLeaseStore.subscribe((state, previous) => {
|
|
42492
|
+
if (!isLocked(previous.phase) || isLocked(state.phase)) return;
|
|
42493
|
+
if (status().failure?.conflict?.reason === "EDIT_LEASE_HELD") {
|
|
42494
|
+
actions.clearFailure();
|
|
42495
|
+
actions.setAutosavePaused(false);
|
|
42496
|
+
}
|
|
42497
|
+
scheduleCheck();
|
|
42498
|
+
});
|
|
42178
42499
|
return () => {
|
|
42179
42500
|
unsubscribeFlow();
|
|
42180
42501
|
unsubscribeStatus();
|
|
42502
|
+
unsubscribeLease();
|
|
42181
42503
|
if (checkTimer) clearTimeout(checkTimer);
|
|
42182
42504
|
autosave.cancel();
|
|
42183
42505
|
saveDeparting(useFlowStore.getState());
|
|
@@ -42249,6 +42571,125 @@ function useLeaveGuards(readOnly) {
|
|
|
42249
42571
|
};
|
|
42250
42572
|
}, [readOnly]);
|
|
42251
42573
|
}
|
|
42574
|
+
function useActorName(actorId) {
|
|
42575
|
+
const config2 = getFluidGridsConfig();
|
|
42576
|
+
const actors = useMemo(
|
|
42577
|
+
() => actorId ? [{ actorId, actorType: "user" }] : [],
|
|
42578
|
+
[actorId]
|
|
42579
|
+
);
|
|
42580
|
+
const profiles = useActorProfiles(actors, {
|
|
42581
|
+
workspaceId: config2.workspaceId || void 0,
|
|
42582
|
+
orgId: config2.organizationId,
|
|
42583
|
+
authToken: config2.authToken
|
|
42584
|
+
});
|
|
42585
|
+
if (!actorId) return null;
|
|
42586
|
+
return profiles[actorId]?.displayName ?? buildActorFallback({ actorId, actorType: "user" }).displayName;
|
|
42587
|
+
}
|
|
42588
|
+
function SaveConflictDialog() {
|
|
42589
|
+
const tr = useTr();
|
|
42590
|
+
const open2 = useSaveStatusStore((s) => s.conflictOpen);
|
|
42591
|
+
const conflict = useSaveStatusStore((s) => s.failure?.conflict);
|
|
42592
|
+
const statusActions = useSaveStatusStore((s) => s.actions);
|
|
42593
|
+
const dbId = useFlowStore((s) => s.workflow.dbId);
|
|
42594
|
+
const { saveToBackend } = useSaveWorkflow();
|
|
42595
|
+
const name = useActorName(conflict?.updatedById);
|
|
42596
|
+
const [busy, setBusy] = useState(false);
|
|
42597
|
+
if (!conflict || conflict.reason !== "STALE_REVISION") return null;
|
|
42598
|
+
const someone = name ?? tr("fluidgrids.builder.saveConflict.someone", "Someone else");
|
|
42599
|
+
const stillDraft = conflict.currentStatus === "DRAFT";
|
|
42600
|
+
const canOverwrite = stillDraft && conflict.currentRevision !== null && !!dbId;
|
|
42601
|
+
const run = (action) => async () => {
|
|
42602
|
+
setBusy(true);
|
|
42603
|
+
try {
|
|
42604
|
+
await action();
|
|
42605
|
+
} finally {
|
|
42606
|
+
setBusy(false);
|
|
42607
|
+
}
|
|
42608
|
+
};
|
|
42609
|
+
const loadLatest = () => {
|
|
42610
|
+
const flow = useFlowStore.getState();
|
|
42611
|
+
statusActions.setBaseline({
|
|
42612
|
+
fingerprint: workflowFingerprint(flow.workflow),
|
|
42613
|
+
generation: flow.loadGeneration
|
|
42614
|
+
});
|
|
42615
|
+
statusActions.setConflictOpen(false);
|
|
42616
|
+
reloadLatestVersion();
|
|
42617
|
+
};
|
|
42618
|
+
const keepAsNewVersion = run(() => saveToBackend({ asNewVersion: true }));
|
|
42619
|
+
const overwrite = run(() => {
|
|
42620
|
+
if (dbId && conflict.currentRevision !== null) {
|
|
42621
|
+
rememberRevision(dbId, conflict.currentRevision);
|
|
42622
|
+
}
|
|
42623
|
+
return saveToBackend();
|
|
42624
|
+
});
|
|
42625
|
+
return /* @__PURE__ */ jsx(
|
|
42626
|
+
AlertDialog,
|
|
42627
|
+
{
|
|
42628
|
+
open: open2,
|
|
42629
|
+
onOpenChange: (next) => {
|
|
42630
|
+
if (!next && !busy) statusActions.setConflictOpen(false);
|
|
42631
|
+
},
|
|
42632
|
+
children: /* @__PURE__ */ jsxs(AlertDialogContent, { "data-testid": "fluidgrids-save-conflict-dialog", children: [
|
|
42633
|
+
/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
|
|
42634
|
+
/* @__PURE__ */ jsx(AlertDialogTitle, { children: tr(
|
|
42635
|
+
"fluidgrids.builder.saveConflict.title",
|
|
42636
|
+
"This workflow changed after you opened it"
|
|
42637
|
+
) }),
|
|
42638
|
+
/* @__PURE__ */ jsx(AlertDialogDescription, { children: stillDraft ? tr(
|
|
42639
|
+
"fluidgrids.builder.saveConflict.changedDescription",
|
|
42640
|
+
"{{name}} saved changes to this version, so your save was stopped to keep them. Choose what to keep.",
|
|
42641
|
+
{ name: someone }
|
|
42642
|
+
) : tr(
|
|
42643
|
+
"fluidgrids.builder.saveConflict.publishedDescription",
|
|
42644
|
+
"This version is no longer a draft, so your changes were not saved into it. Keep them as a new version, or load the latest one."
|
|
42645
|
+
) })
|
|
42646
|
+
] }),
|
|
42647
|
+
/* @__PURE__ */ jsxs(AlertDialogFooter, { className: "flex-wrap gap-2", children: [
|
|
42648
|
+
/* @__PURE__ */ jsx(AlertDialogCancel, { disabled: busy, children: tr("fluidgrids.builder.saveConflict.decideLater", "Decide later") }),
|
|
42649
|
+
/* @__PURE__ */ jsx(
|
|
42650
|
+
Button$2,
|
|
42651
|
+
{
|
|
42652
|
+
variant: "outline",
|
|
42653
|
+
disabled: busy,
|
|
42654
|
+
"data-testid": "fluidgrids-save-conflict-load-latest",
|
|
42655
|
+
onClick: loadLatest,
|
|
42656
|
+
children: tr(
|
|
42657
|
+
"fluidgrids.builder.saveConflict.loadLatest",
|
|
42658
|
+
"Discard mine and load latest"
|
|
42659
|
+
)
|
|
42660
|
+
}
|
|
42661
|
+
),
|
|
42662
|
+
canOverwrite ? /* @__PURE__ */ jsx(
|
|
42663
|
+
Button$2,
|
|
42664
|
+
{
|
|
42665
|
+
variant: "outline",
|
|
42666
|
+
disabled: busy,
|
|
42667
|
+
"data-testid": "fluidgrids-save-conflict-overwrite",
|
|
42668
|
+
onClick: () => void overwrite(),
|
|
42669
|
+
children: tr(
|
|
42670
|
+
"fluidgrids.builder.saveConflict.overwrite",
|
|
42671
|
+
"Overwrite their changes"
|
|
42672
|
+
)
|
|
42673
|
+
}
|
|
42674
|
+
) : null,
|
|
42675
|
+
/* @__PURE__ */ jsx(
|
|
42676
|
+
Button$2,
|
|
42677
|
+
{
|
|
42678
|
+
"data-agent": "confirm",
|
|
42679
|
+
disabled: busy,
|
|
42680
|
+
"data-testid": "fluidgrids-save-conflict-keep",
|
|
42681
|
+
onClick: () => void keepAsNewVersion(),
|
|
42682
|
+
children: tr(
|
|
42683
|
+
"fluidgrids.builder.saveConflict.keepAsNewVersion",
|
|
42684
|
+
"Keep mine as a new version"
|
|
42685
|
+
)
|
|
42686
|
+
}
|
|
42687
|
+
)
|
|
42688
|
+
] })
|
|
42689
|
+
] })
|
|
42690
|
+
}
|
|
42691
|
+
);
|
|
42692
|
+
}
|
|
42252
42693
|
function LeaveChangesDialog() {
|
|
42253
42694
|
const tr = useTr();
|
|
42254
42695
|
const navigate = useNavigate();
|
|
@@ -42339,14 +42780,18 @@ function SaveQuotaDialog() {
|
|
|
42339
42780
|
}
|
|
42340
42781
|
);
|
|
42341
42782
|
}
|
|
42342
|
-
function BuilderSaveController({
|
|
42783
|
+
function BuilderSaveController({
|
|
42784
|
+
readOnly,
|
|
42785
|
+
locked = false
|
|
42786
|
+
}) {
|
|
42343
42787
|
useAutosave(readOnly);
|
|
42344
|
-
useSaveShortcut(readOnly);
|
|
42788
|
+
useSaveShortcut(readOnly || locked);
|
|
42345
42789
|
useLeaveGuards(readOnly);
|
|
42346
42790
|
if (readOnly) return null;
|
|
42347
42791
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42348
42792
|
/* @__PURE__ */ jsx(LeaveChangesDialog, {}),
|
|
42349
|
-
/* @__PURE__ */ jsx(SaveQuotaDialog, {})
|
|
42793
|
+
/* @__PURE__ */ jsx(SaveQuotaDialog, {}),
|
|
42794
|
+
/* @__PURE__ */ jsx(SaveConflictDialog, {})
|
|
42350
42795
|
] });
|
|
42351
42796
|
}
|
|
42352
42797
|
|
|
@@ -42444,6 +42889,34 @@ function FailedStatus({ className, compact }) {
|
|
|
42444
42889
|
const tr = useTr();
|
|
42445
42890
|
const manualSave = useManualSave();
|
|
42446
42891
|
const message = useSaveStatusStore((s) => s.failure?.message);
|
|
42892
|
+
const staleConflict = useSaveStatusStore(
|
|
42893
|
+
(s) => s.failure?.conflict?.reason === "STALE_REVISION"
|
|
42894
|
+
);
|
|
42895
|
+
const setConflictOpen = useSaveStatusStore((s) => s.actions.setConflictOpen);
|
|
42896
|
+
if (staleConflict) {
|
|
42897
|
+
return /* @__PURE__ */ jsxs(
|
|
42898
|
+
StatusFrame,
|
|
42899
|
+
{
|
|
42900
|
+
state: "conflict",
|
|
42901
|
+
title: message,
|
|
42902
|
+
tone: "text-status-warning-text",
|
|
42903
|
+
className,
|
|
42904
|
+
children: [
|
|
42905
|
+
/* @__PURE__ */ jsx(Icon, { icon: "mynaui:git-branch", className: "size-3.5" }),
|
|
42906
|
+
/* @__PURE__ */ jsx(Label3, { compact, children: tr("fluidgrids.builder.saveStatus.conflict", "Changed elsewhere") }),
|
|
42907
|
+
/* @__PURE__ */ jsx(
|
|
42908
|
+
"button",
|
|
42909
|
+
{
|
|
42910
|
+
type: "button",
|
|
42911
|
+
onClick: () => setConflictOpen(true),
|
|
42912
|
+
className: "ml-1 rounded px-1 font-medium underline-offset-2 hover:underline",
|
|
42913
|
+
children: tr("fluidgrids.builder.saveStatus.resolve", "Resolve")
|
|
42914
|
+
}
|
|
42915
|
+
)
|
|
42916
|
+
]
|
|
42917
|
+
}
|
|
42918
|
+
);
|
|
42919
|
+
}
|
|
42447
42920
|
return /* @__PURE__ */ jsxs(
|
|
42448
42921
|
StatusFrame,
|
|
42449
42922
|
{
|
|
@@ -43055,6 +43528,7 @@ var ActivateWorkflowButton = ({
|
|
|
43055
43528
|
});
|
|
43056
43529
|
if (result) {
|
|
43057
43530
|
setWorkflow({ status: result.status });
|
|
43531
|
+
rememberOwnStatusChange(result.id, result.revision);
|
|
43058
43532
|
if (result.status === "ACTIVE") {
|
|
43059
43533
|
toast.success("Workflow activated", {
|
|
43060
43534
|
description: `Version ${result.version} is now ACTIVE. Future saves will create a new draft version.`,
|
|
@@ -43071,6 +43545,7 @@ var ActivateWorkflowButton = ({
|
|
|
43071
43545
|
const result = await deactivateWorkflow(dbId);
|
|
43072
43546
|
if (result) {
|
|
43073
43547
|
setWorkflow({ status: result.status });
|
|
43548
|
+
rememberOwnStatusChange(result.id, result.revision);
|
|
43074
43549
|
toast.success("Workflow deactivated", {
|
|
43075
43550
|
description: `Version ${result.version} is now a DRAFT. Saves will update in-place.`,
|
|
43076
43551
|
dismissible: true
|
|
@@ -43114,6 +43589,24 @@ var ActivateWorkflowButton = ({
|
|
|
43114
43589
|
);
|
|
43115
43590
|
};
|
|
43116
43591
|
|
|
43592
|
+
// src/builder/hooks/refresh-open-revision.ts
|
|
43593
|
+
async function refreshOpenRevision() {
|
|
43594
|
+
const dbId = useFlowStore.getState().workflow.dbId;
|
|
43595
|
+
if (!dbId) return;
|
|
43596
|
+
try {
|
|
43597
|
+
const { data } = await getFluidGridsClient().query(
|
|
43598
|
+
{
|
|
43599
|
+
query: WorkflowDefinitionDocument,
|
|
43600
|
+
variables: { id: dbId },
|
|
43601
|
+
fetchPolicy: "network-only"
|
|
43602
|
+
}
|
|
43603
|
+
);
|
|
43604
|
+
const stored = data?.workflowDefinition;
|
|
43605
|
+
rememberOwnStatusChange(stored?.id, stored?.revision);
|
|
43606
|
+
} catch {
|
|
43607
|
+
}
|
|
43608
|
+
}
|
|
43609
|
+
|
|
43117
43610
|
// src/utils/workflow-definition-diff.ts
|
|
43118
43611
|
function isRecord5(value) {
|
|
43119
43612
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -44029,10 +44522,12 @@ function VersionHistoryPanel({
|
|
|
44029
44522
|
});
|
|
44030
44523
|
if (version2 === currentVersion) {
|
|
44031
44524
|
setWorkflow({ status: "ACTIVE" });
|
|
44525
|
+
rememberOwnStatusChange(result.id, result.revision);
|
|
44032
44526
|
} else {
|
|
44033
44527
|
const refreshed = await refetch();
|
|
44034
44528
|
const current = refreshed.find((v) => v.version === currentVersion);
|
|
44035
44529
|
if (current?.status) setWorkflow({ status: current.status });
|
|
44530
|
+
rememberOwnStatusChange(current?.id, current?.revision);
|
|
44036
44531
|
}
|
|
44037
44532
|
await refetch();
|
|
44038
44533
|
} catch (error62) {
|
|
@@ -44062,7 +44557,11 @@ function VersionHistoryPanel({
|
|
|
44062
44557
|
dismissible: true
|
|
44063
44558
|
});
|
|
44064
44559
|
if (version2 === currentVersion) setWorkflow({ status: result.status });
|
|
44065
|
-
await Promise.all([
|
|
44560
|
+
await Promise.all([
|
|
44561
|
+
refetch(),
|
|
44562
|
+
refetchVersioning(),
|
|
44563
|
+
refreshOpenRevision()
|
|
44564
|
+
]);
|
|
44066
44565
|
} catch (error62) {
|
|
44067
44566
|
toast.error(
|
|
44068
44567
|
tr(
|
|
@@ -44803,12 +45302,15 @@ function EnvironmentsPanel({
|
|
|
44803
45302
|
const pending = view?.pendingPromotions ?? [];
|
|
44804
45303
|
const policy = view?.policy;
|
|
44805
45304
|
const dev = environments.find((state) => state.environment === "DEV");
|
|
44806
|
-
const afterProdChange = (promotedVersion) => {
|
|
45305
|
+
const afterProdChange = async (promotedVersion) => {
|
|
44807
45306
|
if (promotedVersion === currentVersion) {
|
|
44808
45307
|
setWorkflow({ status: "ACTIVE" });
|
|
44809
45308
|
} else if (openStatus === "ACTIVE") {
|
|
44810
45309
|
setWorkflow({ status: "ARCHIVED" });
|
|
45310
|
+
} else {
|
|
45311
|
+
return;
|
|
44811
45312
|
}
|
|
45313
|
+
await refreshOpenRevision();
|
|
44812
45314
|
};
|
|
44813
45315
|
const run = async (key, action, errorTitle) => {
|
|
44814
45316
|
setBusy(key);
|
|
@@ -44863,7 +45365,8 @@ function EnvironmentsPanel({
|
|
|
44863
45365
|
`${request.version} ${tr("fluidgrids.builder.environments.promotedTo", "promoted to")} ${envLabel(current.target)}`,
|
|
44864
45366
|
{ dismissible: true }
|
|
44865
45367
|
);
|
|
44866
|
-
if (current.target === "PROD")
|
|
45368
|
+
if (current.target === "PROD")
|
|
45369
|
+
await afterProdChange(request.version);
|
|
44867
45370
|
}
|
|
44868
45371
|
},
|
|
44869
45372
|
tr("fluidgrids.builder.environments.promoteError", "Promotion failed")
|
|
@@ -44879,7 +45382,7 @@ function EnvironmentsPanel({
|
|
|
44879
45382
|
`${current.label} ${tr("fluidgrids.builder.environments.approved", "approved and deployed")}`,
|
|
44880
45383
|
{ dismissible: true }
|
|
44881
45384
|
);
|
|
44882
|
-
if (current.target === "PROD") afterProdChange(current.version);
|
|
45385
|
+
if (current.target === "PROD") await afterProdChange(current.version);
|
|
44883
45386
|
},
|
|
44884
45387
|
tr("fluidgrids.builder.environments.approveError", "Approval failed")
|
|
44885
45388
|
);
|
|
@@ -46697,7 +47200,7 @@ function AiDraftPanel({
|
|
|
46697
47200
|
...template !== NO_TEMPLATE ? { template } : {}
|
|
46698
47201
|
});
|
|
46699
47202
|
}, [openVersion, proposal, template, trimmed, workflowKey]);
|
|
46700
|
-
const
|
|
47203
|
+
const openWorkflow2 = useCallback(
|
|
46701
47204
|
(key, version2) => {
|
|
46702
47205
|
setOpen(false);
|
|
46703
47206
|
proposal.reset();
|
|
@@ -46749,7 +47252,7 @@ function AiDraftPanel({
|
|
|
46749
47252
|
{ version: version2, newVersion: result.workflow.version }
|
|
46750
47253
|
)
|
|
46751
47254
|
);
|
|
46752
|
-
|
|
47255
|
+
openWorkflow2(result.workflow.workflowKey, result.workflow.version);
|
|
46753
47256
|
} catch (error62) {
|
|
46754
47257
|
toast.error(
|
|
46755
47258
|
tr(
|
|
@@ -46764,7 +47267,7 @@ function AiDraftPanel({
|
|
|
46764
47267
|
setReverting(false);
|
|
46765
47268
|
}
|
|
46766
47269
|
},
|
|
46767
|
-
[
|
|
47270
|
+
[openWorkflow2, restoreVersion, tr]
|
|
46768
47271
|
);
|
|
46769
47272
|
const putOnCanvas = useCallback(() => {
|
|
46770
47273
|
if (!current?.definitionJson) return;
|
|
@@ -46953,7 +47456,7 @@ function AiDraftPanel({
|
|
|
46953
47456
|
onApply: () => void handleApply(),
|
|
46954
47457
|
onReject: () => void handleReject(),
|
|
46955
47458
|
onCancel: () => void proposal.cancel(),
|
|
46956
|
-
onOpenWorkflow:
|
|
47459
|
+
onOpenWorkflow: openWorkflow2,
|
|
46957
47460
|
onRevert: (key, version2) => void handleRevert(key, version2)
|
|
46958
47461
|
}
|
|
46959
47462
|
),
|
|
@@ -47879,6 +48382,236 @@ function CodeVersionNotice() {
|
|
|
47879
48382
|
}
|
|
47880
48383
|
);
|
|
47881
48384
|
}
|
|
48385
|
+
function actorOf(banner) {
|
|
48386
|
+
return banner.kind === "editing" || banner.kind === "taken" ? banner.actorId : null;
|
|
48387
|
+
}
|
|
48388
|
+
function unsavedWorkActions(tr, commands) {
|
|
48389
|
+
return [
|
|
48390
|
+
{
|
|
48391
|
+
key: "keep",
|
|
48392
|
+
label: tr(
|
|
48393
|
+
"fluidgrids.builder.editLease.keepAsNewVersion",
|
|
48394
|
+
"Keep my changes as a new version"
|
|
48395
|
+
),
|
|
48396
|
+
onClick: commands.keepMine,
|
|
48397
|
+
primary: true
|
|
48398
|
+
},
|
|
48399
|
+
{
|
|
48400
|
+
key: "take-over",
|
|
48401
|
+
label: tr(
|
|
48402
|
+
"fluidgrids.builder.editLease.discardAndTakeOver",
|
|
48403
|
+
"Discard my changes and take over"
|
|
48404
|
+
),
|
|
48405
|
+
onClick: commands.takeOver
|
|
48406
|
+
},
|
|
48407
|
+
{
|
|
48408
|
+
key: "latest",
|
|
48409
|
+
label: tr(
|
|
48410
|
+
"fluidgrids.builder.editLease.discardAndViewLatest",
|
|
48411
|
+
"Discard my changes and view latest"
|
|
48412
|
+
),
|
|
48413
|
+
onClick: commands.refresh
|
|
48414
|
+
}
|
|
48415
|
+
];
|
|
48416
|
+
}
|
|
48417
|
+
function unsavedHere(tr) {
|
|
48418
|
+
return tr(
|
|
48419
|
+
"fluidgrids.builder.editLease.unsavedHere",
|
|
48420
|
+
"Your latest changes are only on this screen."
|
|
48421
|
+
);
|
|
48422
|
+
}
|
|
48423
|
+
function editingView(self, someone, dirty, tr, commands) {
|
|
48424
|
+
const editing = self ? tr(
|
|
48425
|
+
"fluidgrids.builder.editLease.editingElsewhereSelf",
|
|
48426
|
+
"You are editing this workflow in another tab or window, so it is read-only here."
|
|
48427
|
+
) : tr(
|
|
48428
|
+
"fluidgrids.builder.editLease.editingElsewhere",
|
|
48429
|
+
"{{name}} is editing this workflow, so it is read-only for you.",
|
|
48430
|
+
{ name: someone }
|
|
48431
|
+
);
|
|
48432
|
+
if (dirty) {
|
|
48433
|
+
return {
|
|
48434
|
+
icon: "mynaui:lock",
|
|
48435
|
+
message: `${editing} ${unsavedHere(tr)}`,
|
|
48436
|
+
actions: unsavedWorkActions(tr, commands)
|
|
48437
|
+
};
|
|
48438
|
+
}
|
|
48439
|
+
return {
|
|
48440
|
+
icon: "mynaui:lock",
|
|
48441
|
+
message: editing,
|
|
48442
|
+
actions: [
|
|
48443
|
+
{
|
|
48444
|
+
key: "take-over",
|
|
48445
|
+
label: self ? tr("fluidgrids.builder.editLease.editHere", "Edit here") : tr("fluidgrids.builder.editLease.takeOver", "Take over"),
|
|
48446
|
+
onClick: commands.takeOver,
|
|
48447
|
+
primary: true
|
|
48448
|
+
},
|
|
48449
|
+
{
|
|
48450
|
+
key: "refresh",
|
|
48451
|
+
label: tr("fluidgrids.builder.editLease.refresh", "Refresh"),
|
|
48452
|
+
onClick: commands.refresh
|
|
48453
|
+
}
|
|
48454
|
+
]
|
|
48455
|
+
};
|
|
48456
|
+
}
|
|
48457
|
+
function takenView(self, someone, dirty, tr, commands) {
|
|
48458
|
+
const taken = self ? tr(
|
|
48459
|
+
"fluidgrids.builder.editLease.takenSelf",
|
|
48460
|
+
"You started editing this workflow in another tab or window."
|
|
48461
|
+
) : tr(
|
|
48462
|
+
"fluidgrids.builder.editLease.taken",
|
|
48463
|
+
"{{name}} took over editing this workflow.",
|
|
48464
|
+
{ name: someone }
|
|
48465
|
+
);
|
|
48466
|
+
if (dirty) {
|
|
48467
|
+
return {
|
|
48468
|
+
icon: "mynaui:lock",
|
|
48469
|
+
message: `${taken} ${unsavedHere(tr)}`,
|
|
48470
|
+
actions: unsavedWorkActions(tr, commands)
|
|
48471
|
+
};
|
|
48472
|
+
}
|
|
48473
|
+
return {
|
|
48474
|
+
icon: "mynaui:lock",
|
|
48475
|
+
message: taken,
|
|
48476
|
+
actions: [
|
|
48477
|
+
{
|
|
48478
|
+
key: "take-over",
|
|
48479
|
+
label: tr("fluidgrids.builder.editLease.takeOver", "Take over"),
|
|
48480
|
+
onClick: commands.takeOver,
|
|
48481
|
+
primary: true
|
|
48482
|
+
},
|
|
48483
|
+
{
|
|
48484
|
+
key: "latest",
|
|
48485
|
+
label: tr("fluidgrids.builder.editLease.viewLatest", "View latest"),
|
|
48486
|
+
onClick: commands.refresh
|
|
48487
|
+
}
|
|
48488
|
+
]
|
|
48489
|
+
};
|
|
48490
|
+
}
|
|
48491
|
+
function bannerView(banner, context, commands) {
|
|
48492
|
+
const { someone, dirty, tr } = context;
|
|
48493
|
+
switch (banner.kind) {
|
|
48494
|
+
case "editing":
|
|
48495
|
+
return editingView(banner.self, someone, dirty, tr, commands);
|
|
48496
|
+
case "taken":
|
|
48497
|
+
return takenView(banner.self, someone, dirty, tr, commands);
|
|
48498
|
+
case "free":
|
|
48499
|
+
return {
|
|
48500
|
+
icon: "mynaui:lock-open",
|
|
48501
|
+
message: tr(
|
|
48502
|
+
"fluidgrids.builder.editLease.free",
|
|
48503
|
+
"Nobody is editing this workflow now."
|
|
48504
|
+
),
|
|
48505
|
+
actions: [
|
|
48506
|
+
{
|
|
48507
|
+
key: "edit",
|
|
48508
|
+
label: tr("fluidgrids.builder.editLease.edit", "Edit"),
|
|
48509
|
+
onClick: commands.takeOver,
|
|
48510
|
+
primary: true
|
|
48511
|
+
}
|
|
48512
|
+
]
|
|
48513
|
+
};
|
|
48514
|
+
case "idle":
|
|
48515
|
+
return {
|
|
48516
|
+
icon: "mynaui:pause",
|
|
48517
|
+
message: tr(
|
|
48518
|
+
"fluidgrids.builder.editLease.idle",
|
|
48519
|
+
"Editing paused after 15 minutes without activity, so others can edit this workflow."
|
|
48520
|
+
),
|
|
48521
|
+
actions: [
|
|
48522
|
+
{
|
|
48523
|
+
key: "resume",
|
|
48524
|
+
label: tr("fluidgrids.builder.editLease.resume", "Resume editing"),
|
|
48525
|
+
onClick: commands.resume,
|
|
48526
|
+
primary: true
|
|
48527
|
+
}
|
|
48528
|
+
]
|
|
48529
|
+
};
|
|
48530
|
+
case "reloading":
|
|
48531
|
+
return {
|
|
48532
|
+
icon: "mynaui:spinner",
|
|
48533
|
+
message: tr(
|
|
48534
|
+
"fluidgrids.builder.editLease.reloading",
|
|
48535
|
+
"Loading the latest version\u2026"
|
|
48536
|
+
),
|
|
48537
|
+
actions: []
|
|
48538
|
+
};
|
|
48539
|
+
}
|
|
48540
|
+
}
|
|
48541
|
+
function EditLeaseBanner() {
|
|
48542
|
+
const tr = useTr();
|
|
48543
|
+
const lease = useEditLeaseStore(
|
|
48544
|
+
useShallow((s) => ({
|
|
48545
|
+
phase: s.phase,
|
|
48546
|
+
held: s.held,
|
|
48547
|
+
holderActorId: s.holderActorId,
|
|
48548
|
+
heldByCaller: s.heldByCaller,
|
|
48549
|
+
takenBy: s.takenBy,
|
|
48550
|
+
takenByCaller: s.takenByCaller
|
|
48551
|
+
}))
|
|
48552
|
+
);
|
|
48553
|
+
const banner = bannerOf(lease);
|
|
48554
|
+
const name = useActorName(banner ? actorOf(banner) : null);
|
|
48555
|
+
const workflowName = useFlowStore((s) => s.workflow.name);
|
|
48556
|
+
const statusDirty = useSaveStatusStore((s) => s.dirty);
|
|
48557
|
+
useSaveStatusStore((s) => s.baseline);
|
|
48558
|
+
const canvasDirty = useFlowStore((s) => editStateFor(s) === "dirty");
|
|
48559
|
+
const dirty = statusDirty || canvasDirty;
|
|
48560
|
+
const { saveToBackend } = useSaveWorkflow();
|
|
48561
|
+
const [busy, setBusy] = useState(false);
|
|
48562
|
+
if (!banner) return null;
|
|
48563
|
+
const run = (action) => () => {
|
|
48564
|
+
setBusy(true);
|
|
48565
|
+
void Promise.resolve().then(action).finally(() => setBusy(false));
|
|
48566
|
+
};
|
|
48567
|
+
const view = bannerView(
|
|
48568
|
+
banner,
|
|
48569
|
+
{
|
|
48570
|
+
someone: name ?? tr("fluidgrids.builder.editLease.someone", "Someone else"),
|
|
48571
|
+
dirty,
|
|
48572
|
+
tr
|
|
48573
|
+
},
|
|
48574
|
+
{
|
|
48575
|
+
takeOver: run(takeOverEditLease),
|
|
48576
|
+
refresh: reloadLatestVersion,
|
|
48577
|
+
keepMine: run(() => saveToBackend({ asNewVersion: true })),
|
|
48578
|
+
resume: run(resumeEditLease)
|
|
48579
|
+
}
|
|
48580
|
+
);
|
|
48581
|
+
return /* @__PURE__ */ jsxs(
|
|
48582
|
+
"div",
|
|
48583
|
+
{
|
|
48584
|
+
role: "status",
|
|
48585
|
+
"data-testid": "fluidgrids-edit-lease-banner",
|
|
48586
|
+
"data-state": banner.kind,
|
|
48587
|
+
className: "mx-4 mt-2 flex flex-wrap items-center gap-x-3 gap-y-2 rounded-t-xl border border-status-warning-border bg-status-warning-bg-subtle px-4 py-2 text-sm text-status-warning-text",
|
|
48588
|
+
children: [
|
|
48589
|
+
/* @__PURE__ */ jsx(
|
|
48590
|
+
Icon,
|
|
48591
|
+
{
|
|
48592
|
+
icon: view.icon,
|
|
48593
|
+
className: banner.kind === "reloading" ? "size-4 animate-spin" : "size-4",
|
|
48594
|
+
"aria-hidden": true
|
|
48595
|
+
}
|
|
48596
|
+
),
|
|
48597
|
+
workflowName ? /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: workflowName }) : null,
|
|
48598
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1", children: view.message }),
|
|
48599
|
+
view.actions.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: view.actions.map((action) => /* @__PURE__ */ jsx(
|
|
48600
|
+
Button$2,
|
|
48601
|
+
{
|
|
48602
|
+
size: "sm",
|
|
48603
|
+
variant: action.primary ? "default" : "outline",
|
|
48604
|
+
disabled: busy,
|
|
48605
|
+
"data-testid": `fluidgrids-edit-lease-${action.key}`,
|
|
48606
|
+
onClick: action.onClick,
|
|
48607
|
+
children: action.label
|
|
48608
|
+
},
|
|
48609
|
+
action.key
|
|
48610
|
+
)) }) : null
|
|
48611
|
+
]
|
|
48612
|
+
}
|
|
48613
|
+
);
|
|
48614
|
+
}
|
|
47882
48615
|
var edgeTypes2 = {
|
|
47883
48616
|
deletable: CustomDeletableEdge
|
|
47884
48617
|
};
|
|
@@ -47895,9 +48628,38 @@ var edgeOptions = {
|
|
|
47895
48628
|
strokeWidth: 1.5
|
|
47896
48629
|
}
|
|
47897
48630
|
};
|
|
48631
|
+
function canvasEditingProps(blocked2, editing) {
|
|
48632
|
+
if (blocked2) {
|
|
48633
|
+
return {
|
|
48634
|
+
nodesDraggable: false,
|
|
48635
|
+
nodesConnectable: false,
|
|
48636
|
+
elementsSelectable: false,
|
|
48637
|
+
multiSelectionKeyCode: null,
|
|
48638
|
+
deleteKeyCode: null
|
|
48639
|
+
};
|
|
48640
|
+
}
|
|
48641
|
+
return {
|
|
48642
|
+
...editing,
|
|
48643
|
+
nodesDraggable: true,
|
|
48644
|
+
nodesConnectable: true,
|
|
48645
|
+
elementsSelectable: true,
|
|
48646
|
+
// Ctrl/Cmd-click adds a node to the selection; box-select via Shift-drag
|
|
48647
|
+
// on empty canvas is React Flow's own default and needs no extra prop.
|
|
48648
|
+
multiSelectionKeyCode: ["Meta", "Control"]
|
|
48649
|
+
};
|
|
48650
|
+
}
|
|
48651
|
+
function useEditingBlocked(isReadOnly) {
|
|
48652
|
+
const { allowed: canEdit } = useFluidGridsPermission(
|
|
48653
|
+
FLUIDGRIDS_PERMISSIONS.WORKFLOW_UPDATE
|
|
48654
|
+
);
|
|
48655
|
+
useEditLease(!isReadOnly && canEdit);
|
|
48656
|
+
const leaseLocked = useEditLeaseLocked() && !isReadOnly;
|
|
48657
|
+
return { leaseLocked, editingBlocked: isReadOnly || leaseLocked };
|
|
48658
|
+
}
|
|
47898
48659
|
var FlowBuilder = ({ readOnly = false }) => {
|
|
47899
48660
|
const searchParams = new URLSearchParams(window.location.search);
|
|
47900
48661
|
const isReadOnly = readOnly || searchParams.get("readonly") === "true";
|
|
48662
|
+
const { leaseLocked, editingBlocked } = useEditingBlocked(isReadOnly);
|
|
47901
48663
|
const durableFunctionsEnabled = useDurableFunctionNodesEnabled();
|
|
47902
48664
|
const durableFunctionsEnabledRef = useRef(durableFunctionsEnabled);
|
|
47903
48665
|
useEffect(() => {
|
|
@@ -47909,7 +48671,7 @@ var FlowBuilder = ({ readOnly = false }) => {
|
|
|
47909
48671
|
}),
|
|
47910
48672
|
[]
|
|
47911
48673
|
);
|
|
47912
|
-
useAgentTools(agentTools, !
|
|
48674
|
+
useAgentTools(agentTools, !editingBlocked);
|
|
47913
48675
|
const [nodes, edges, onNodesChange, onEdgesChange, onConnect] = useFlowStore(
|
|
47914
48676
|
useShallow((s) => {
|
|
47915
48677
|
return [
|
|
@@ -48001,10 +48763,11 @@ var FlowBuilder = ({ readOnly = false }) => {
|
|
|
48001
48763
|
onNodesChange
|
|
48002
48764
|
]
|
|
48003
48765
|
);
|
|
48004
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full w-full overflow-hidden", children: [
|
|
48005
|
-
!
|
|
48766
|
+
return /* @__PURE__ */ jsx(BuilderReadOnlyProvider, { value: editingBlocked, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full w-full overflow-hidden", children: [
|
|
48767
|
+
!editingBlocked && /* @__PURE__ */ jsx(BuilderHeader, {}),
|
|
48006
48768
|
!isReadOnly && /* @__PURE__ */ jsx(CodeVersionNotice, {}),
|
|
48007
|
-
/* @__PURE__ */ jsx(
|
|
48769
|
+
leaseLocked && /* @__PURE__ */ jsx(EditLeaseBanner, {}),
|
|
48770
|
+
/* @__PURE__ */ jsx(BuilderSaveController, { readOnly: isReadOnly, locked: leaseLocked }),
|
|
48008
48771
|
/* @__PURE__ */ jsxs(
|
|
48009
48772
|
"div",
|
|
48010
48773
|
{
|
|
@@ -48038,21 +48801,19 @@ var FlowBuilder = ({ readOnly = false }) => {
|
|
|
48038
48801
|
edgeTypes: edgeTypes2,
|
|
48039
48802
|
edges,
|
|
48040
48803
|
onEdgesChange,
|
|
48041
|
-
|
|
48042
|
-
|
|
48043
|
-
|
|
48044
|
-
|
|
48804
|
+
...canvasEditingProps(editingBlocked, {
|
|
48805
|
+
onConnect,
|
|
48806
|
+
onConnectEnd: handleOnEdgeDropConnectEnd,
|
|
48807
|
+
onDrop,
|
|
48808
|
+
onDragOver,
|
|
48809
|
+
deleteKeyCode
|
|
48810
|
+
}),
|
|
48045
48811
|
onNodeDragStop: (_, node2) => {
|
|
48046
48812
|
autoAdjustNode(node2);
|
|
48047
48813
|
},
|
|
48048
48814
|
onPaneClick: closeQuickConfig,
|
|
48049
48815
|
onNodesDelete,
|
|
48050
48816
|
isValidConnection,
|
|
48051
|
-
nodesDraggable: !isReadOnly,
|
|
48052
|
-
nodesConnectable: !isReadOnly,
|
|
48053
|
-
elementsSelectable: !isReadOnly,
|
|
48054
|
-
multiSelectionKeyCode: isReadOnly ? null : ["Meta", "Control"],
|
|
48055
|
-
deleteKeyCode: isReadOnly ? null : deleteKeyCode,
|
|
48056
48817
|
snapGrid: [16, 16],
|
|
48057
48818
|
snapToGrid: true,
|
|
48058
48819
|
fitView: true,
|
|
@@ -48062,7 +48823,7 @@ var FlowBuilder = ({ readOnly = false }) => {
|
|
|
48062
48823
|
}
|
|
48063
48824
|
),
|
|
48064
48825
|
/* @__PURE__ */ jsx(FluidGridsMark, {}),
|
|
48065
|
-
nodes.length === 0 && !
|
|
48826
|
+
nodes.length === 0 && !editingBlocked && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3 text-center", children: [
|
|
48066
48827
|
/* @__PURE__ */ jsx("div", { className: "size-16 flex items-center justify-center rounded-full bg-primary/10 border-2 border-dashed border-primary/30", children: /* @__PURE__ */ jsx(
|
|
48067
48828
|
Icon,
|
|
48068
48829
|
{
|
|
@@ -48078,10 +48839,8 @@ var FlowBuilder = ({ readOnly = false }) => {
|
|
|
48078
48839
|
),
|
|
48079
48840
|
/* @__PURE__ */ jsx(ChatPanel, {}),
|
|
48080
48841
|
/* @__PURE__ */ jsx(LogsPanel, {}),
|
|
48081
|
-
/* @__PURE__ */
|
|
48082
|
-
|
|
48083
|
-
/* @__PURE__ */ jsx(QuickConfigPanel, {})
|
|
48084
|
-
] }),
|
|
48842
|
+
/* @__PURE__ */ jsx(NodeDetailView, {}),
|
|
48843
|
+
/* @__PURE__ */ jsx(QuickConfigPanel, {}),
|
|
48085
48844
|
!isReadOnly && /* @__PURE__ */ jsx(WebhookRunFollower, {}),
|
|
48086
48845
|
/* @__PURE__ */ jsx(
|
|
48087
48846
|
"div",
|
|
@@ -48093,7 +48852,7 @@ var FlowBuilder = ({ readOnly = false }) => {
|
|
|
48093
48852
|
children: /* @__PURE__ */ jsx("div", { ref: floatingMenuWrapperRef, className: "relative size-full", children: /* @__PURE__ */ jsx(AddNodeFloatingMenu, { onNodeAdd: handleAddConnectedNode }) })
|
|
48094
48853
|
}
|
|
48095
48854
|
)
|
|
48096
|
-
] });
|
|
48855
|
+
] }) });
|
|
48097
48856
|
};
|
|
48098
48857
|
function IntegrationReturnHandler() {
|
|
48099
48858
|
const { setNodes, getNodes } = useReactFlow();
|
|
@@ -48185,6 +48944,7 @@ var FlowBuilderPage = ({
|
|
|
48185
48944
|
const setWorkflow = useFlowStore((s) => s.actions.setWorkflow);
|
|
48186
48945
|
const searchParams = new URLSearchParams(window.location.search);
|
|
48187
48946
|
const isReadOnly = readOnly || searchParams.get("readonly") === "true";
|
|
48947
|
+
const leaseLocked = useEditLeaseLocked();
|
|
48188
48948
|
const resetWorkflow = useFlowStore((s) => s.actions.resetWorkflow);
|
|
48189
48949
|
const [isStoreReady, setIsStoreReady] = useState(false);
|
|
48190
48950
|
const syncedWorkflowRef = useRef(null);
|
|
@@ -48225,9 +48985,9 @@ var FlowBuilderPage = ({
|
|
|
48225
48985
|
/* @__PURE__ */ jsx(HistoricalRunLoader, {}),
|
|
48226
48986
|
/* @__PURE__ */ jsx("div", { className: "flex flex-col h-full", children: /* @__PURE__ */ jsxs("div", { className: "relative flex grow min-h-0 divide-x divide-card-foreground/10", children: [
|
|
48227
48987
|
/* @__PURE__ */ jsx("div", { className: "grow min-w-0 bg-card md:bg-transparent", children: /* @__PURE__ */ jsx(FlowBuilder, { readOnly: isReadOnly }) }),
|
|
48228
|
-
!isReadOnly && /* @__PURE__ */ jsx(SidebarModule, {})
|
|
48988
|
+
!isReadOnly && !leaseLocked && /* @__PURE__ */ jsx(SidebarModule, {})
|
|
48229
48989
|
] }) })
|
|
48230
48990
|
] });
|
|
48231
48991
|
};
|
|
48232
48992
|
|
|
48233
|
-
export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, FlowBuilderPage, FluidGridsMark, Popover, PopoverContent, PopoverTrigger, RUNNER_INSTALL_SCRIPT_URL, RunOnSection, RunnerStatusPill, RunsUsageMeter, RunsUsageMeterView, TemplateCard, buildNodeCheck, buildRequiredNodeTypes, external_exports, readRunsQuota, runnerInstallCommand, runsQuotaLevel, summarizeRunsQuota, useImportWorkflowTemplate, useInstalledNodes, useRunnerActions, useRunners, useRunsQuota, useWorkflowTemplates };
|
|
48993
|
+
export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, FlowBuilderPage, FluidGridsMark, Popover, PopoverContent, PopoverTrigger, RUNNER_INSTALL_SCRIPT_URL, RunOnSection, RunnerStatusPill, RunsUsageMeter, RunsUsageMeterView, TemplateCard, buildNodeCheck, buildRequiredNodeTypes, external_exports, readRunsQuota, releaseLeaseKeptForReload, runnerInstallCommand, runsQuotaLevel, summarizeRunsQuota, useImportWorkflowTemplate, useInstalledNodes, useRunnerActions, useRunners, useRunsQuota, useWorkflowReloadStore, useWorkflowTemplates };
|