@burdenoff/fluidgrids-fe-sdk 2026.1111.12 → 2026.1111.14
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-CDkJeeu2.js → ExecutionDetailPage-itor_whl.js} +4 -4
- package/dist/{FluidGridsProvider-CDZjKmfD.js → FluidGridsProvider-B30VcLdX.js} +236 -17
- package/dist/{HistoryPage-DnVGN_72.js → HistoryPage-DueEld8g.js} +1 -1
- package/dist/{InsightsPage-CGRpZtGu.js → InsightsPage-DQ7KCR9h.js} +2 -2
- package/dist/{ManagedAiPolicyAuthority-DbJBledZ.js → ManagedAiPolicyAuthority-C0YPTJMS.js} +2 -2
- package/dist/{MissingNodesInstallModal-YI57ImRx.js → MissingNodesInstallModal-Cj31oMDX.js} +1 -1
- package/dist/{TemplateConfigurationModal-BimyvVH6.js → TemplateConfigurationModal-BAPCG8y-.js} +1 -1
- package/dist/{TemplateDetailPanel-CkwB6vrW.js → TemplateDetailPanel-CZ1uxEQL.js} +1 -1
- package/dist/{TemplatesPage-ZK6cMWur.js → TemplatesPage-DWdgZnIH.js} +4 -4
- package/dist/{WorkflowSearchRedirect-BHtdF64q.js → WorkflowSearchRedirect-C8vhSZK9.js} +32 -49
- package/dist/{WorkflowShareDialog-ByTSWwwR.js → WorkflowShareDialog-DnBQA3V6.js} +1 -1
- package/dist/{WorkflowsList-B9F2e8M6.js → WorkflowsList-74jjJFsJ.js} +4 -4
- package/dist/{agentTools-Dgrdql-W.js → agentTools-D5AW7zHn.js} +2 -2
- package/dist/canvasNoteTools-BqQTTCll.js +136 -0
- package/dist/executions.js +1 -1
- package/dist/{flow-builder-CukxgO65.js → flow-builder-BR8p5tXG.js} +520 -65
- package/dist/history.js +1 -1
- package/dist/{index-BES5GtJJ.d.ts → index-CLqh2u28.d.ts} +73 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +440 -19
- package/dist/{node-detail-view-DRk8Ln5L.js → node-detail-view-Dnhqdn8C.js} +3 -3
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +1 -1
- package/dist/{quick-config-panel-40_1utiL.js → quick-config-panel-C4tSpCrt.js} +2 -2
- package/dist/settings.js +1 -1
- package/dist/templates.js +1 -1
- package/dist/{useExecutions-BqoD7XWi.js → useExecutions-CNEN1ENQ.js} +1 -1
- package/dist/{useObservability-Cs-5_NHh.js → useObservability-CmFFXmKb.js} +1 -1
- package/dist/{useRunHistory-CEL-CIz-.js → useRunHistory-DhU_mrzF.js} +1 -1
- package/dist/{useWorkflows-Bwpvfdgv.js → useWorkflows-D01LC39Y.js} +1 -1
- package/dist/workflow-builder.js +2 -2
- package/dist/workflows-list.js +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { A as nodeAccent, B as NODE_TYPE_DRAG_DATA_FORMAT, G as isTriggerOperation, H as useFlowStore, M as isImageIcon, T as resolveInputHandles, U as useIsValidConnection, V as STICKY_NOTE_TYPE, g as toCodeMirrorDiagnostics, j as NodeTypeIcon, r as CODE_WORKFLOW_NODE_TYPE, z as NODE_OPERATION_DRAG_DATA_FORMAT } from "./node-operation-selection--oy-yEq_.js";
|
|
3
|
-
import { $ as
|
|
3
|
+
import { $ as isStringOptionList, $n as usePaletteGates, $t as inAppLeaveTarget, A as StoreNodeProductsByTypeDocument, An as isQuotaExhaustedFailure, At as useSaveStatusStore, B as useRunGuard, Bt as shouldReleaseForIdle, C as GetMyNodeInstallationsDocument, Ct as useSaveWorkflow, D as InstallWorkflowNodeDocument, E as InstallPurchasedNodeDocument, En as RUNS_QUOTA_NAME, F as getNodeMetadata, Ft as leaseCallQueue, G as nodeSubtitle, Gt as sessionBeforeReload, H as asRecord, Ht as EDIT_SESSION_ID, I as buildOperationSeed, It as leaseCallSequence, J as paramsFromOperation, Jt as AUTOSAVE_DEBOUNCE_MS, K as nodeTitle, Kt as rememberOwnStatusChange, Lt as phaseAfterAcquire, M as UninstallOrganizationPrivateNodeDocument, Mn as useNodeRegistryRevision, Mt as useEditLeaseStore, N as UninstallWorkflowNodeDocument, Nt as bannerOf, On as collectExtensions, Ot as refusalOfQuota, Pn as loadDefinitionJson, Pt as isLocked, R as useInsertNode, Rt as phaseAfterTakeOver, S as CreateStoreOrderDocument, St as useFlowValidator, T as InstallOrganizationPrivateNodeDocument, Tn as BILLING_PLANS_PATH, U as coerceNumber, Un as FLUIDGRIDS_FLAGS, Ut as forgetSessionForReload, V as workflowParameterIssues, Vt as tickAction, Wt as rememberSessionForReload, X as useNodeConfig, Xn as useDurableParityEnabled, Xt as canAutosave, Yn as useDurableFunctionNodesEnabled, Yt as AUTOSAVE_RETRY_DELAYS_MS, Zn as useFluidGridsFlag, _t as leavingLosesChanges, a as NODES_METADATA, ar as matchesAdapterProvider, at as casesFromRules, b as BrowseWorkflowNodesDocument, bn as emitWorkflowCreated, bt as watchLeavingLosesChanges, c as ensureInstalledNodeTypesLoaded, cn as structuredParameterType, cr as getNodeDetail, d as ensureRegistryNodeTypesLoaded, dn as isParamVisible, dr as useBuilderReadOnly, dt as useGuardedNavigate, en as saveFailureActionOf, er as usePhaseCFlags, et as optionListText, f as isNodeConfigsInitialized, fn as useFluidGridsAnalytics, ft as useManualSave, g as registerWorkflowNodeTypes, gt as editingLeaseLocked, ht as editStateFor, i as AVAILABLE_NODES, ir as isOptionalAdapter, it as SwitchRulesEditor, jn as useCanvasEdges, jt as useEditLeaseLocked, k as OrganizationPrivateNodesDocument, kt as saveIndicatorOf, l as ensureNodeConfigsLoaded, lr as buildStickyNode, lt as boundFieldsOf, m as refetchStoreNodes, mn as emitNodeInstalled, mt as departingSave, nn as useNodeExecution, nr as useWorkflowTestsEnabled, nt as resolveLoadOptionsSpec, o as NODE_CONFIGS, on as formatParameterValue, or as newConnectionProviderKey, ot as FieldTree, p as onNodeTypesUpdate, pn as openRunQuotaUpgradePrompt, pr as getGlobalStoreClient, pt as autosaveAllowedFor, q as optionText, qt as rememberRevision, rr as connectionsForAdapter, rt as ConditionTreeBuilder, s as NODE_TYPES, sn as parseParameterValue, sr as replaceAdapterConnections, st as parseMappingValue, t as FluidGridsContext, tn as workflowFingerprint, tt as optionListValues, ur as BuilderReadOnlyProvider, ut as missingRequiredParameters, v as scheduleNodePaletteLoad, vn as emitTemplateUsed, vt as savedEverythingBeforeLeaving, w as GetNodeInstallableVersionsDocument, x as BrowseWorkflowTemplatesDocument, xt as isFlowComplete, yn as emitUpgradeClicked, yt as touchesSavedFields, z as MissingParametersDialog, zt as releasesOnClose } from "./FluidGridsProvider-B30VcLdX.js";
|
|
4
4
|
import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
|
|
5
5
|
import { Bi as describeOperationError, Br as WorkflowGitRepositoryDocument, Ct as LinkWorkflowGitRepositoryDocument, Dr as WorkflowDefinitionDocument, Ei as WorkspaceRunsQuotaDocument, Ht as PreviewScheduleDocument, Ir as WorkflowGenerationTemplatesDocument, J as DeleteWorkflowRolloutDocument, Kt as PushWorkflowToGitDocument, Li as emitFluidGridsEvent, Lr as WorkflowGitCommitsDocument, Mt as NodeSchemaIntrospectionDocument, Ni as waitForFluidGridsClient, Nr as WorkflowEnvironmentsDocument, Oi as getFluidGridsClient, Pt as NodeUsageStatusDocument, Qt as RemoveNodeFromWorkflowsDocument, Rr as WorkflowGitDiffDocument, Si as WorkflowVersioningDocument, Ui as throwIfResultError, Vn as TakeOverWorkflowEditLeaseDocument, Wr as WorkflowPromotionRequestsDocument, Xn as UnlinkWorkflowGitRepositoryDocument, Yt as RejectWorkflowPromotionDocument, Z as DeployWorkflowVersionToDevDocument, Zt as ReleaseWorkflowEditLeaseDocument, cr as UpdateWorkflowEnvironmentPolicyDocument, i as ActivateWorkflowVersionCheckedDocument, jn as SetWorkflowRolloutDocument, jr as WorkflowEditLeaseDocument, ki as getFluidGridsClientSafe, mr as WebhookTestListenerDocument, n as AcquireWorkflowEditLeaseDocument, nn as RequestWorkflowPromotionDocument, oa as __exportAll, on as RestoreWorkflowFromGitDocument, qi as getFluidGridsConfig, u as ApproveWorkflowPromotionDocument, wr as WorkflowCallInputsDocument, wt as ListenForWebhookTestEventDocument, x as CancelWorkflowPromotionDocument, xi as WorkflowVersionDrainageDocument, yr as WorkflowAuthoringDocument, zn as StopWebhookTestListenerDocument, zr as WorkflowGitPushesDocument } from "./node-endpoint-configs-DHFGA5MQ.js";
|
|
6
6
|
import { D as describeSchedule, H as pauseSchedule, _ as useResource, c as FieldError, d as NativeSelect, et as triggerSchedule, nt as unpauseSchedule, p as StatusPill, r as describeRule, s as isAgentNodeType, v as backfillSchedule } from "./agent-form-BvMWhQ42.js";
|
|
@@ -9,9 +9,9 @@ import { a as useConnections, r as useAgentsUsingConnection, t as useAgentDefini
|
|
|
9
9
|
import { F as normaliseAssertRules, I as operatorSpec, L as readAssertRulesSetting, M as ASSERT_OPERATORS, N as describeAssertRuleProblem, P as emptyAssertRuleRow, R as rowsFromRules, _ as buildNodeDisplayMap, a as TEST_STATUS_TONE_CLASS, b as EntityTagPicker, c as draftToInput, d as isTestRunOpen, f as nextSortOrder, g as testStatusTone, h as testStatusLabel, i as useWorkflowTests, l as emptyTestCaseDraft, m as testRunSummaryText, n as useWorkflowProposal, p as pinnedOutputsFromCanvas, s as draftFromTestCase, t as WorkflowProposalCard, u as enabledCaseIds, z as ruleFromRow } from "./WorkflowProposalCard-8Vr56kQa.js";
|
|
10
10
|
import { t as cn$1 } from "./cn-QqidFIgr.js";
|
|
11
11
|
import { i as Button$2, t as PublishCustomNodeAction } from "./PublishCustomNodeAction-D2F7oN0S.js";
|
|
12
|
-
import { O as CreateWorkflowRefusalDialog, b as lineOfNode, h as fileBaseName, i as useWorkflowCodeExport, n as downloadWorkflowCodeExport, t as WorkflowShareDialog, v as isLosslessReport, w as sortFidelityItems, x as orderedExportFiles, y as isLossyFidelityItem } from "./WorkflowShareDialog-
|
|
12
|
+
import { O as CreateWorkflowRefusalDialog, b as lineOfNode, h as fileBaseName, i as useWorkflowCodeExport, n as downloadWorkflowCodeExport, t as WorkflowShareDialog, v as isLosslessReport, w as sortFidelityItems, x as orderedExportFiles, y as isLossyFidelityItem } from "./WorkflowShareDialog-DnBQA3V6.js";
|
|
13
13
|
import { t as FluidGridsDocsLink } from "./FluidGridsDocsLink-JxkBpOeb.js";
|
|
14
|
-
import { a as useDeactivateWorkflow, d as useRestoreWorkflowVersion, o as useDeleteWorkflow, t as useActivateWorkflow, v as useWorkflowVersions } from "./useWorkflows-
|
|
14
|
+
import { a as useDeactivateWorkflow, d as useRestoreWorkflowVersion, o as useDeleteWorkflow, t as useActivateWorkflow, v as useWorkflowVersions } from "./useWorkflows-D01LC39Y.js";
|
|
15
15
|
import { n as VersionLifecycleBar, r as countLifecycle, t as VersionDiffDialog } from "./VersionDiffDialog-Dq2xlPFI.js";
|
|
16
16
|
import { n as useRequestedProposal } from "./useRequestedProposal-B7uPXfZB.js";
|
|
17
17
|
import { t as AiCreditBalanceSlot } from "./aiCreditsBridge-BuE92tKo.js";
|
|
@@ -21,7 +21,7 @@ import { Link, useNavigate, useParams } from "react-router";
|
|
|
21
21
|
import { useMutation, useQuery } from "@apollo/client/react";
|
|
22
22
|
import { gql } from "@apollo/client/core";
|
|
23
23
|
import { create } from "zustand";
|
|
24
|
-
import { Background, Controls, EdgeLabelRenderer, Handle, MarkerType, MiniMap, Panel, Position, ReactFlow, ReactFlowProvider, addEdge, getBezierPath, useEdgesState, useNodes, useNodesState, useOnSelectionChange, useReactFlow, useStore } from "@xyflow/react";
|
|
24
|
+
import { Background, Controls, EdgeLabelRenderer, Handle, MarkerType, MiniMap, NodeToolbar, Panel, Position, ReactFlow, ReactFlowProvider, ViewportPortal, addEdge, getBezierPath, useEdgesState, useInternalNode, useNodes, useNodesState, useOnSelectionChange, useReactFlow, useStore } from "@xyflow/react";
|
|
25
25
|
import { Icon } from "@iconify/react";
|
|
26
26
|
import ReactMarkdown from "react-markdown";
|
|
27
27
|
import { cn } from "@burdenoff/fe-libs/utils";
|
|
@@ -10395,6 +10395,19 @@ function useImportWorkflowTemplate() {
|
|
|
10395
10395
|
|
|
10396
10396
|
// src/utils/workflow-node-check.ts
|
|
10397
10397
|
/**
|
|
10398
|
+
* Workflow node dependency checking.
|
|
10399
|
+
*
|
|
10400
|
+
* Shared logic for detecting node types used by a workflow that are not
|
|
10401
|
+
* installed in the current workspace, resolving them to store products, and
|
|
10402
|
+
* classifying them as installed / missing (free installable) / missingPaid /
|
|
10403
|
+
* unavailable (not offered by the store, so nothing can install them).
|
|
10404
|
+
*
|
|
10405
|
+
* Used by:
|
|
10406
|
+
* - Template import flow (desktop-sidebar-fragment)
|
|
10407
|
+
* - Shared-workflow copy flow (WorkflowItem)
|
|
10408
|
+
* - AI-assistant-created workflow open flow (WorkflowItem)
|
|
10409
|
+
*/
|
|
10410
|
+
/**
|
|
10398
10411
|
* Canvas-only node types: they live on the React Flow canvas but are never
|
|
10399
10412
|
* executed, never registered in NODE_CONFIGS and never published to the store.
|
|
10400
10413
|
*
|
|
@@ -10418,50 +10431,22 @@ function getWorkflowNodeTypes(nodes) {
|
|
|
10418
10431
|
return [...new Set(nodes.map((n) => n.type).filter((t) => Boolean(t)).filter((t) => !CANVAS_ONLY_NODE_TYPES.has(t)))];
|
|
10419
10432
|
}
|
|
10420
10433
|
/**
|
|
10421
|
-
*
|
|
10422
|
-
*
|
|
10423
|
-
* returns 100 anyway. The old single request for 200 rows silently dropped
|
|
10424
|
-
* every node past the first 100 (BOFF-11402).
|
|
10434
|
+
* Node types per store lookup: global-store-svc answers for at most 100 at a
|
|
10435
|
+
* time. A workflow misses far fewer; more are asked in parallel requests.
|
|
10425
10436
|
*/
|
|
10426
|
-
const
|
|
10437
|
+
const STORE_NODE_LOOKUP_BATCH_SIZE = 100;
|
|
10438
|
+
/** The store context of FluidGrids node products, as the Node Store browses them. */
|
|
10439
|
+
const STORE_NODE_PRODUCT_CONTEXT = "fluidgrids";
|
|
10427
10440
|
/**
|
|
10428
|
-
*
|
|
10429
|
-
*
|
|
10441
|
+
* The store products that offer any of `nodeTypes`, at most
|
|
10442
|
+
* `STORE_NODE_LOOKUP_BATCH_SIZE` types per request, the requests in parallel.
|
|
10443
|
+
* Blank and repeated types are asked once.
|
|
10430
10444
|
*/
|
|
10431
|
-
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
|
|
10435
|
-
|
|
10436
|
-
type: "NODE",
|
|
10437
|
-
compatibleWith: "fluidgrids"
|
|
10438
|
-
},
|
|
10439
|
-
sortBy: "DOWNLOADS",
|
|
10440
|
-
pagination: {
|
|
10441
|
-
limit: 100,
|
|
10442
|
-
offset
|
|
10443
|
-
}
|
|
10444
|
-
} };
|
|
10445
|
-
}
|
|
10446
|
-
/**
|
|
10447
|
-
* Fetch every FluidGrids-compatible node product from the store.
|
|
10448
|
-
*
|
|
10449
|
-
* `browseStore` pages by `offset`; its `cursor` input is ignored by the
|
|
10450
|
-
* server. The offset advances by the number of rows actually returned so a
|
|
10451
|
-
* lower server cap still walks the whole catalog. Rows are de-duplicated by
|
|
10452
|
-
* product id in case a page boundary moves while the catalog changes.
|
|
10453
|
-
*/
|
|
10454
|
-
async function fetchAllStoreNodes(fetchPage) {
|
|
10455
|
-
const byId = /* @__PURE__ */ new Map();
|
|
10456
|
-
let offset = 0;
|
|
10457
|
-
for (let page = 0; page < 50; page++) {
|
|
10458
|
-
const browse = await fetchPage(storeNodePageVariables(offset));
|
|
10459
|
-
const products = browse?.products ?? [];
|
|
10460
|
-
for (const product of products) if (!byId.has(product.id)) byId.set(product.id, product);
|
|
10461
|
-
if (!browse?.hasMore || products.length === 0) break;
|
|
10462
|
-
offset += products.length;
|
|
10463
|
-
}
|
|
10464
|
-
return [...byId.values()];
|
|
10445
|
+
async function fetchStoreNodeProducts(nodeTypes, fetchProducts) {
|
|
10446
|
+
const wanted = [...new Set(nodeTypes.filter(Boolean))];
|
|
10447
|
+
const batches = [];
|
|
10448
|
+
for (let i = 0; i < wanted.length; i += 100) batches.push(wanted.slice(i, i + 100));
|
|
10449
|
+
return (await Promise.all(batches.map(fetchProducts))).flatMap((products) => [...products ?? []]);
|
|
10465
10450
|
}
|
|
10466
10451
|
function publishedAtMs(product) {
|
|
10467
10452
|
const ms = product.publishedAt ? Date.parse(product.publishedAt) : NaN;
|
|
@@ -10503,18 +10488,22 @@ function buildStoreNodeTypeMap(products) {
|
|
|
10503
10488
|
return nodeTypeToProduct;
|
|
10504
10489
|
}
|
|
10505
10490
|
/**
|
|
10506
|
-
*
|
|
10507
|
-
*
|
|
10491
|
+
* Ask the global store which products offer `nodeTypes` and map each type to the
|
|
10492
|
+
* best one (see compareStoreNodeCandidates). Only the asked types travel: the
|
|
10493
|
+
* check used to page through the whole node catalog for them (BOFF-11754).
|
|
10508
10494
|
*/
|
|
10509
|
-
async function fetchStoreNodeTypeMap() {
|
|
10495
|
+
async function fetchStoreNodeTypeMap(nodeTypes) {
|
|
10510
10496
|
const storeClient = getGlobalStoreClient();
|
|
10511
|
-
if (!storeClient) return /* @__PURE__ */ new Map();
|
|
10512
|
-
return buildStoreNodeTypeMap(await
|
|
10497
|
+
if (!storeClient || nodeTypes.length === 0) return /* @__PURE__ */ new Map();
|
|
10498
|
+
return buildStoreNodeTypeMap(await fetchStoreNodeProducts(nodeTypes, async (batch) => {
|
|
10513
10499
|
return (await storeClient.query({
|
|
10514
|
-
query:
|
|
10515
|
-
variables
|
|
10500
|
+
query: StoreNodeProductsByTypeDocument,
|
|
10501
|
+
variables: {
|
|
10502
|
+
nodeTypes: batch,
|
|
10503
|
+
compatibleWith: STORE_NODE_PRODUCT_CONTEXT
|
|
10504
|
+
},
|
|
10516
10505
|
fetchPolicy: "cache-first"
|
|
10517
|
-
})).data?.
|
|
10506
|
+
})).data?.storeNodeProductsByType;
|
|
10518
10507
|
}));
|
|
10519
10508
|
}
|
|
10520
10509
|
/**
|
|
@@ -10551,10 +10540,11 @@ function requiredNodeEntry(type, registered, product) {
|
|
|
10551
10540
|
async function buildRequiredNodeTypes(nodes) {
|
|
10552
10541
|
const workflowNodeTypes = getWorkflowNodeTypes(nodes);
|
|
10553
10542
|
if (workflowNodeTypes.length === 0) return [];
|
|
10554
|
-
|
|
10555
|
-
|
|
10543
|
+
const isRegistered = (type) => NODE_CONFIGS.some((c) => c.type === type);
|
|
10544
|
+
await ensureRegistryNodeTypesLoaded(workflowNodeTypes);
|
|
10545
|
+
const missingTypes = workflowNodeTypes.filter((type) => !isRegistered(type));
|
|
10556
10546
|
let nodeTypeToProduct = /* @__PURE__ */ new Map();
|
|
10557
|
-
if (missingTypes.length > 0) nodeTypeToProduct = await fetchStoreNodeTypeMap();
|
|
10547
|
+
if (missingTypes.length > 0) [, nodeTypeToProduct] = await Promise.all([ensureInstalledNodeTypesLoaded(missingTypes), fetchStoreNodeTypeMap(missingTypes)]);
|
|
10558
10548
|
return workflowNodeTypes.map((type) => {
|
|
10559
10549
|
const registered = NODE_CONFIGS.find((c) => c.type === type);
|
|
10560
10550
|
return requiredNodeEntry(type, registered ? { version: registered.defaultData.version } : void 0, nodeTypeToProduct.get(type));
|
|
@@ -10582,7 +10572,7 @@ function buildNodeCheck(requiredNodeTypes, installedAppIds) {
|
|
|
10582
10572
|
}
|
|
10583
10573
|
|
|
10584
10574
|
// src/hooks/store/useTemplateImportFlow.tsx
|
|
10585
|
-
const LazyTemplateConfigurationModal = lazy(() => import("./TemplateConfigurationModal-
|
|
10575
|
+
const LazyTemplateConfigurationModal = lazy(() => import("./TemplateConfigurationModal-BAPCG8y-.js").then((m) => ({ default: m.TemplateConfigurationModal })));
|
|
10586
10576
|
/**
|
|
10587
10577
|
* "Use this template", the same everywhere it is offered: the builder's
|
|
10588
10578
|
* template panel, the Templates page and the template preview page
|
|
@@ -12759,7 +12749,7 @@ function TemplatesGrid({ templates, loading, hasMore, onLoadMore, onTemplateClic
|
|
|
12759
12749
|
}
|
|
12760
12750
|
|
|
12761
12751
|
// src/builder/components/flow-builder/components/templates/TemplatesModal.tsx
|
|
12762
|
-
const LazyTemplateDetailPanel = lazy(() => import("./TemplateDetailPanel-
|
|
12752
|
+
const LazyTemplateDetailPanel = lazy(() => import("./TemplateDetailPanel-CZ1uxEQL.js").then((m) => ({ default: m.TemplateDetailPanel })));
|
|
12763
12753
|
/**
|
|
12764
12754
|
* Modal for browsing and selecting workflow templates
|
|
12765
12755
|
* Features:
|
|
@@ -20702,6 +20692,448 @@ function WebhookRunFollower() {
|
|
|
20702
20692
|
return null;
|
|
20703
20693
|
}
|
|
20704
20694
|
|
|
20695
|
+
// src/agentMarks/plainText.ts
|
|
20696
|
+
/**
|
|
20697
|
+
* ★ BOFF-11584: the text of a callout or note is model output, and the model
|
|
20698
|
+
* has read workflow content people wrote. It is shown as plain text only:
|
|
20699
|
+
* React renders it as a text node (never `dangerouslySetInnerHTML`, never a
|
|
20700
|
+
* markdown renderer), so markup, links and images in it stay inert characters.
|
|
20701
|
+
*
|
|
20702
|
+
* This keeps it short and printable as well: control characters (other than
|
|
20703
|
+
* line breaks) and bidirectional controls, which can disguise what a line
|
|
20704
|
+
* says, are removed; runs of blank lines collapse; the result is clamped.
|
|
20705
|
+
*/
|
|
20706
|
+
const BIDI_CONTROLS = /* @__PURE__ */ new Set([
|
|
20707
|
+
1564,
|
|
20708
|
+
8206,
|
|
20709
|
+
8207,
|
|
20710
|
+
8234,
|
|
20711
|
+
8235,
|
|
20712
|
+
8236,
|
|
20713
|
+
8237,
|
|
20714
|
+
8238,
|
|
20715
|
+
8294,
|
|
20716
|
+
8295,
|
|
20717
|
+
8296,
|
|
20718
|
+
8297
|
|
20719
|
+
]);
|
|
20720
|
+
function isDroppedCharacter(code) {
|
|
20721
|
+
if (code === 10) return false;
|
|
20722
|
+
if (code <= 31) return true;
|
|
20723
|
+
if (code >= 127 && code <= 159) return true;
|
|
20724
|
+
return BIDI_CONTROLS.has(code);
|
|
20725
|
+
}
|
|
20726
|
+
function toPlainText(text, max) {
|
|
20727
|
+
let kept = "";
|
|
20728
|
+
for (const character of text.replace(/\r\n?/g, "\n").replace(/\t/g, " ")) if (!isDroppedCharacter(character.codePointAt(0) ?? 0)) kept += character;
|
|
20729
|
+
const cleaned = kept.replace(/ {2,}/g, " ").replace(/ *\n */g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
20730
|
+
if (cleaned.length <= max) return cleaned;
|
|
20731
|
+
return `${cleaned.slice(0, max - 1).trimEnd()}…`;
|
|
20732
|
+
}
|
|
20733
|
+
|
|
20734
|
+
// src/agentMarks/marksStore.ts
|
|
20735
|
+
/** The text of a mark is model output: kept as plain text, never markup. */
|
|
20736
|
+
const CALLOUT_LABEL_MAX = 200;
|
|
20737
|
+
const NOTE_MESSAGE_MAX = 280;
|
|
20738
|
+
/** A workflow holds at most this many notes, resolved ones included. */
|
|
20739
|
+
const MAX_NOTES_PER_WORKFLOW = 20;
|
|
20740
|
+
let sequence = 0;
|
|
20741
|
+
const nextId = (prefix) => {
|
|
20742
|
+
sequence += 1;
|
|
20743
|
+
return `${prefix}${sequence}`;
|
|
20744
|
+
};
|
|
20745
|
+
const useAgentMarksStore = create()((set, get) => ({
|
|
20746
|
+
callouts: [],
|
|
20747
|
+
notes: [],
|
|
20748
|
+
nextNumber: {},
|
|
20749
|
+
openNoteId: null,
|
|
20750
|
+
flash: null,
|
|
20751
|
+
actions: {
|
|
20752
|
+
addCallout: ({ label, target, element, route }) => {
|
|
20753
|
+
const callout = {
|
|
20754
|
+
id: nextId("c"),
|
|
20755
|
+
label: toPlainText(label, 200),
|
|
20756
|
+
target,
|
|
20757
|
+
route,
|
|
20758
|
+
element: element ? new WeakRef(element) : null
|
|
20759
|
+
};
|
|
20760
|
+
set((state) => ({ callouts: [...state.callouts, callout].slice(-3) }));
|
|
20761
|
+
return callout;
|
|
20762
|
+
},
|
|
20763
|
+
rebindCallout: (id, element) => set((state) => ({ callouts: state.callouts.map((callout) => callout.id === id ? {
|
|
20764
|
+
...callout,
|
|
20765
|
+
element: new WeakRef(element)
|
|
20766
|
+
} : callout) })),
|
|
20767
|
+
removeCallout: (id) => set((state) => ({ callouts: state.callouts.filter((callout) => callout.id !== id) })),
|
|
20768
|
+
clearCallouts: () => {
|
|
20769
|
+
const count = get().callouts.length;
|
|
20770
|
+
if (count > 0) set({ callouts: [] });
|
|
20771
|
+
return count;
|
|
20772
|
+
},
|
|
20773
|
+
dropCalloutsOffRoute: (route) => {
|
|
20774
|
+
const { callouts } = get();
|
|
20775
|
+
const kept = callouts.filter((callout) => callout.route === route);
|
|
20776
|
+
if (kept.length !== callouts.length) set({ callouts: kept });
|
|
20777
|
+
return callouts.length - kept.length;
|
|
20778
|
+
},
|
|
20779
|
+
addNote: (input) => {
|
|
20780
|
+
const { notes, nextNumber } = get();
|
|
20781
|
+
if (notes.filter((note) => note.workflowKey === input.workflowKey).length >= 20) return null;
|
|
20782
|
+
const number = nextNumber[input.workflowKey] ?? 1;
|
|
20783
|
+
const note = {
|
|
20784
|
+
id: nextId("n"),
|
|
20785
|
+
number,
|
|
20786
|
+
workflowKey: input.workflowKey,
|
|
20787
|
+
nodeId: input.nodeId,
|
|
20788
|
+
nodeLabel: toPlainText(input.nodeLabel, 80),
|
|
20789
|
+
paramKey: input.paramKey ?? null,
|
|
20790
|
+
kind: input.kind,
|
|
20791
|
+
message: toPlainText(input.message, 280),
|
|
20792
|
+
resolved: false
|
|
20793
|
+
};
|
|
20794
|
+
set({
|
|
20795
|
+
notes: [...notes, note],
|
|
20796
|
+
nextNumber: {
|
|
20797
|
+
...nextNumber,
|
|
20798
|
+
[input.workflowKey]: number + 1
|
|
20799
|
+
}
|
|
20800
|
+
});
|
|
20801
|
+
return note;
|
|
20802
|
+
},
|
|
20803
|
+
setResolved: (id, resolved) => set((state) => ({ notes: state.notes.map((note) => note.id === id ? {
|
|
20804
|
+
...note,
|
|
20805
|
+
resolved
|
|
20806
|
+
} : note) })),
|
|
20807
|
+
removeNote: (id) => set((state) => ({
|
|
20808
|
+
notes: state.notes.filter((note) => note.id !== id),
|
|
20809
|
+
openNoteId: state.openNoteId === id ? null : state.openNoteId
|
|
20810
|
+
})),
|
|
20811
|
+
clearNotes: (workflowKey) => {
|
|
20812
|
+
const { notes, openNoteId } = get();
|
|
20813
|
+
const kept = workflowKey === void 0 ? [] : notes.filter((note) => note.workflowKey !== workflowKey);
|
|
20814
|
+
const removed = notes.length - kept.length;
|
|
20815
|
+
if (removed > 0) set({
|
|
20816
|
+
notes: kept,
|
|
20817
|
+
openNoteId: kept.some((note) => note.id === openNoteId) ? openNoteId : null
|
|
20818
|
+
});
|
|
20819
|
+
return removed;
|
|
20820
|
+
},
|
|
20821
|
+
openNote: (id) => set({ openNoteId: id }),
|
|
20822
|
+
flashNode: (nodeId) => {
|
|
20823
|
+
sequence += 1;
|
|
20824
|
+
set({ flash: {
|
|
20825
|
+
nodeId,
|
|
20826
|
+
token: sequence
|
|
20827
|
+
} });
|
|
20828
|
+
}
|
|
20829
|
+
}
|
|
20830
|
+
}));
|
|
20831
|
+
/** Reset everything (tests, and sign-out if a host wants it). */
|
|
20832
|
+
function resetAgentMarks() {
|
|
20833
|
+
useAgentMarksStore.setState({
|
|
20834
|
+
callouts: [],
|
|
20835
|
+
notes: [],
|
|
20836
|
+
nextNumber: {},
|
|
20837
|
+
openNoteId: null,
|
|
20838
|
+
flash: null
|
|
20839
|
+
});
|
|
20840
|
+
}
|
|
20841
|
+
|
|
20842
|
+
// src/builder/agentNotes/notesKey.ts
|
|
20843
|
+
/**
|
|
20844
|
+
* ★ BOFF-11584: which workflow a canvas note belongs to. A saved workflow is
|
|
20845
|
+
* known by its key across versions; a new one has no key until its first
|
|
20846
|
+
* save, so its in-memory id stands in.
|
|
20847
|
+
*/
|
|
20848
|
+
function notesKeyOf(workflow) {
|
|
20849
|
+
return workflow.workflowKey || workflow.id || "unsaved";
|
|
20850
|
+
}
|
|
20851
|
+
|
|
20852
|
+
// src/builder/agentNotes/CanvasNotesLayer.tsx
|
|
20853
|
+
/**
|
|
20854
|
+
* ★ BOFF-11584: the assistant's notes on the builder canvas
|
|
20855
|
+
* (`fg_canvas_annotate`). Rendered as a child of `<ReactFlow>`, outside every
|
|
20856
|
+
* node component: the nodes know nothing about notes.
|
|
20857
|
+
*
|
|
20858
|
+
* - Numbered pins sit above their node (`NodeToolbar`), so they follow it
|
|
20859
|
+
* through drags, pans and zooms. A pin opens the note's card: the message,
|
|
20860
|
+
* the parameter it is about, "Open node" and "Mark resolved".
|
|
20861
|
+
* - A dashed ring marks each node with an open note (`ViewportPortal`, in
|
|
20862
|
+
* canvas coordinates), and a brief ring shows the node the assistant just
|
|
20863
|
+
* brought into view.
|
|
20864
|
+
* - The notes list (top left; the FluidGrids mark holds the top right)
|
|
20865
|
+
* holds every note, including orphaned ones whose node was deleted: those
|
|
20866
|
+
* are never moved onto another node.
|
|
20867
|
+
*
|
|
20868
|
+
* Everything here is `data-agent="deny"`: an agent cannot read or press its
|
|
20869
|
+
* own notes. Messages render as React text, never as markup.
|
|
20870
|
+
*/
|
|
20871
|
+
const FLASH_MS = 1800;
|
|
20872
|
+
const RING_PAD = 8;
|
|
20873
|
+
function pinClass(note, open) {
|
|
20874
|
+
return cn("flex size-6 items-center justify-center rounded-full text-xs font-semibold shadow-md transition-transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1", note.resolved ? "bg-muted text-muted-foreground" : note.kind === "fix" ? "bg-destructive text-destructive-foreground" : "bg-primary text-primary-foreground", open && "scale-110 ring-2 ring-ring ring-offset-1 ring-offset-background");
|
|
20875
|
+
}
|
|
20876
|
+
/** A ring around a node, drawn in canvas coordinates. */
|
|
20877
|
+
const NodeRing = ({ nodeId, tone }) => {
|
|
20878
|
+
const node = useInternalNode(nodeId);
|
|
20879
|
+
const width = node?.measured.width ?? 0;
|
|
20880
|
+
const height = node?.measured.height ?? 0;
|
|
20881
|
+
if (!node || width === 0 || height === 0) return null;
|
|
20882
|
+
const { x, y } = node.internals.positionAbsolute;
|
|
20883
|
+
return /* @__PURE__ */ jsx("div", {
|
|
20884
|
+
"aria-hidden": "true",
|
|
20885
|
+
"data-testid": `fg-canvas-note-ring-${tone}`,
|
|
20886
|
+
className: cn("pointer-events-none absolute left-0 top-0 rounded-2xl border-2", tone === "fix" && "border-dashed border-destructive", tone === "info" && "border-dashed border-primary", tone === "flash" && "border-primary ring-4 ring-primary/30 motion-safe:animate-pulse"),
|
|
20887
|
+
style: {
|
|
20888
|
+
transform: `translate(${x - RING_PAD}px, ${y - RING_PAD}px)`,
|
|
20889
|
+
width: width + 16,
|
|
20890
|
+
height: height + 16
|
|
20891
|
+
}
|
|
20892
|
+
});
|
|
20893
|
+
};
|
|
20894
|
+
/** The node the assistant just brought into view, for a moment. */
|
|
20895
|
+
const FlashRing = () => {
|
|
20896
|
+
const flash = useAgentMarksStore((state) => state.flash);
|
|
20897
|
+
const [shown, setShown] = useState(null);
|
|
20898
|
+
useEffect(() => {
|
|
20899
|
+
if (!flash) return;
|
|
20900
|
+
setShown(flash.token);
|
|
20901
|
+
const timer = setTimeout(() => setShown(null), FLASH_MS);
|
|
20902
|
+
return () => clearTimeout(timer);
|
|
20903
|
+
}, [flash]);
|
|
20904
|
+
if (!flash || shown !== flash.token) return null;
|
|
20905
|
+
return /* @__PURE__ */ jsx(NodeRing, {
|
|
20906
|
+
nodeId: flash.nodeId,
|
|
20907
|
+
tone: "flash"
|
|
20908
|
+
});
|
|
20909
|
+
};
|
|
20910
|
+
const CanvasNoteCard = ({ note, nodeOnCanvas }) => {
|
|
20911
|
+
const tr = useTr();
|
|
20912
|
+
const { openNote, setResolved } = useAgentMarksStore((state) => state.actions);
|
|
20913
|
+
const openNodePanel = () => {
|
|
20914
|
+
const { workflow, actions } = useFlowStore.getState();
|
|
20915
|
+
const node = workflow.nodes.find((candidate) => candidate.id === note.nodeId);
|
|
20916
|
+
if (!node?.type) return;
|
|
20917
|
+
actions.ndv.open({
|
|
20918
|
+
nodeId: node.id,
|
|
20919
|
+
type: node.type
|
|
20920
|
+
});
|
|
20921
|
+
openNote(null);
|
|
20922
|
+
};
|
|
20923
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
20924
|
+
"data-testid": "fg-canvas-note-card",
|
|
20925
|
+
className: "nowheel w-64 cursor-default rounded-lg border border-border bg-popover p-3 text-left text-xs text-foreground shadow-lg",
|
|
20926
|
+
children: [
|
|
20927
|
+
/* @__PURE__ */ jsxs("div", {
|
|
20928
|
+
className: "mb-1.5 flex items-center justify-between gap-2",
|
|
20929
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
20930
|
+
className: "flex items-center gap-1.5 font-semibold",
|
|
20931
|
+
children: [/* @__PURE__ */ jsx(Sparkles, {
|
|
20932
|
+
"aria-hidden": "true",
|
|
20933
|
+
className: "size-3.5 text-primary"
|
|
20934
|
+
}), note.kind === "fix" ? tr("fluidgrids.agentMarks.noteFixTitle", "Note {{number}} · To fix", { number: note.number }) : tr("fluidgrids.agentMarks.noteInfoTitle", "Note {{number}}", { number: note.number })]
|
|
20935
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
20936
|
+
type: "button",
|
|
20937
|
+
"aria-label": tr("fluidgrids.agentMarks.closeNote", "Close note"),
|
|
20938
|
+
onClick: () => openNote(null),
|
|
20939
|
+
className: "-m-1 rounded p-1 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
20940
|
+
children: /* @__PURE__ */ jsx(X, {
|
|
20941
|
+
"aria-hidden": "true",
|
|
20942
|
+
className: "size-3.5"
|
|
20943
|
+
})
|
|
20944
|
+
})]
|
|
20945
|
+
}),
|
|
20946
|
+
/* @__PURE__ */ jsxs("p", {
|
|
20947
|
+
className: "mb-1 truncate text-muted-foreground",
|
|
20948
|
+
children: [note.nodeLabel, note.paramKey ? ` · ${tr("fluidgrids.agentMarks.noteParam", "Field: {{param}}", { param: note.paramKey })}` : ""]
|
|
20949
|
+
}),
|
|
20950
|
+
/* @__PURE__ */ jsx("p", {
|
|
20951
|
+
"data-testid": "fg-canvas-note-message",
|
|
20952
|
+
className: "whitespace-pre-line break-words text-sm text-foreground",
|
|
20953
|
+
children: note.message
|
|
20954
|
+
}),
|
|
20955
|
+
!nodeOnCanvas && /* @__PURE__ */ jsx("p", {
|
|
20956
|
+
className: "mt-1.5 text-muted-foreground",
|
|
20957
|
+
children: tr("fluidgrids.agentMarks.noteOrphaned", "This node was removed from the workflow.")
|
|
20958
|
+
}),
|
|
20959
|
+
/* @__PURE__ */ jsxs("div", {
|
|
20960
|
+
className: "mt-2.5 flex justify-end gap-2",
|
|
20961
|
+
children: [nodeOnCanvas && /* @__PURE__ */ jsx("button", {
|
|
20962
|
+
type: "button",
|
|
20963
|
+
onClick: openNodePanel,
|
|
20964
|
+
className: "rounded-md border border-border px-2 py-1 font-medium hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
20965
|
+
children: tr("fluidgrids.agentMarks.openNode", "Open node")
|
|
20966
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
20967
|
+
type: "button",
|
|
20968
|
+
"data-testid": "fg-canvas-note-resolve",
|
|
20969
|
+
onClick: () => setResolved(note.id, !note.resolved),
|
|
20970
|
+
className: "rounded-md bg-primary px-2 py-1 font-medium text-primary-foreground hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
20971
|
+
children: note.resolved ? tr("fluidgrids.agentMarks.reopenNote", "Reopen") : tr("fluidgrids.agentMarks.resolveNote", "Mark resolved")
|
|
20972
|
+
})]
|
|
20973
|
+
})
|
|
20974
|
+
]
|
|
20975
|
+
});
|
|
20976
|
+
};
|
|
20977
|
+
/** The pins above one node, and the open card. */
|
|
20978
|
+
const NodePins = ({ nodeId, notes }) => {
|
|
20979
|
+
const tr = useTr();
|
|
20980
|
+
const openNoteId = useAgentMarksStore((state) => state.openNoteId);
|
|
20981
|
+
const openNote = useAgentMarksStore((state) => state.actions.openNote);
|
|
20982
|
+
const open = notes.find((note) => note.id === openNoteId);
|
|
20983
|
+
return /* @__PURE__ */ jsx(NodeToolbar, {
|
|
20984
|
+
nodeId,
|
|
20985
|
+
isVisible: true,
|
|
20986
|
+
position: Position.Top,
|
|
20987
|
+
align: "end",
|
|
20988
|
+
offset: 6,
|
|
20989
|
+
"data-agent": "deny",
|
|
20990
|
+
"data-testid": "fg-canvas-note-pins",
|
|
20991
|
+
className: "nodrag nopan",
|
|
20992
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
20993
|
+
className: "flex flex-col items-end gap-1.5",
|
|
20994
|
+
children: [open && /* @__PURE__ */ jsx(CanvasNoteCard, {
|
|
20995
|
+
note: open,
|
|
20996
|
+
nodeOnCanvas: true
|
|
20997
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
20998
|
+
className: "flex gap-1",
|
|
20999
|
+
children: notes.map((note) => /* @__PURE__ */ jsx("button", {
|
|
21000
|
+
type: "button",
|
|
21001
|
+
"data-testid": "fg-canvas-note-pin",
|
|
21002
|
+
"aria-expanded": note.id === openNoteId,
|
|
21003
|
+
"aria-label": tr("fluidgrids.agentMarks.notePin", "Assistant note {{number}}", { number: note.number }),
|
|
21004
|
+
onClick: () => openNote(note.id === openNoteId ? null : note.id),
|
|
21005
|
+
className: pinClass(note, note.id === openNoteId),
|
|
21006
|
+
children: note.resolved ? /* @__PURE__ */ jsx(Check, {
|
|
21007
|
+
"aria-hidden": "true",
|
|
21008
|
+
className: "size-3.5"
|
|
21009
|
+
}) : note.number
|
|
21010
|
+
}, note.id))
|
|
21011
|
+
})]
|
|
21012
|
+
})
|
|
21013
|
+
});
|
|
21014
|
+
};
|
|
21015
|
+
/** Every note on this workflow, orphaned ones included. */
|
|
21016
|
+
const NotesList = ({ notes, live }) => {
|
|
21017
|
+
const tr = useTr();
|
|
21018
|
+
const [expanded, setExpanded] = useState(false);
|
|
21019
|
+
const { fitView } = useReactFlow();
|
|
21020
|
+
const { openNote, removeNote, flashNode } = useAgentMarksStore((state) => state.actions);
|
|
21021
|
+
const openCount = notes.filter((note) => !note.resolved).length;
|
|
21022
|
+
const orphaned = notes.filter((note) => !live.has(note.nodeId)).length;
|
|
21023
|
+
const show = (note) => {
|
|
21024
|
+
if (!live.has(note.nodeId)) return;
|
|
21025
|
+
fitView({
|
|
21026
|
+
nodes: [{ id: note.nodeId }],
|
|
21027
|
+
duration: 400,
|
|
21028
|
+
padding: .6,
|
|
21029
|
+
maxZoom: 1.25
|
|
21030
|
+
}).then((done) => {
|
|
21031
|
+
if (done) flashNode(note.nodeId);
|
|
21032
|
+
});
|
|
21033
|
+
openNote(note.id);
|
|
21034
|
+
};
|
|
21035
|
+
return /* @__PURE__ */ jsx(Panel, {
|
|
21036
|
+
position: "top-left",
|
|
21037
|
+
"data-agent": "deny",
|
|
21038
|
+
"data-testid": "fg-canvas-notes-list",
|
|
21039
|
+
className: "nodrag nopan nowheel",
|
|
21040
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
21041
|
+
className: "w-64 rounded-lg border border-border bg-popover text-xs text-foreground shadow-md",
|
|
21042
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
21043
|
+
type: "button",
|
|
21044
|
+
"aria-expanded": expanded,
|
|
21045
|
+
onClick: () => setExpanded((value) => !value),
|
|
21046
|
+
className: "flex w-full items-center justify-between gap-2 rounded-lg px-3 py-2 font-medium hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
21047
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
21048
|
+
className: "flex items-center gap-1.5",
|
|
21049
|
+
children: [/* @__PURE__ */ jsx(Sparkles, {
|
|
21050
|
+
"aria-hidden": "true",
|
|
21051
|
+
className: "size-3.5 text-primary"
|
|
21052
|
+
}), tr("fluidgrids.agentMarks.notesHeading", "Assistant notes")]
|
|
21053
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
21054
|
+
className: "text-muted-foreground",
|
|
21055
|
+
children: [tr("fluidgrids.agentMarks.notesOpenCount", "{{count}} open", { count: openCount }), orphaned > 0 ? ` · ${tr("fluidgrids.agentMarks.notesOrphanedCount", "{{count}} removed", { count: orphaned })}` : ""]
|
|
21056
|
+
})]
|
|
21057
|
+
}), expanded && /* @__PURE__ */ jsx("ul", {
|
|
21058
|
+
className: "max-h-64 overflow-y-auto border-t border-border py-1",
|
|
21059
|
+
children: notes.map((note) => {
|
|
21060
|
+
const onCanvas = live.has(note.nodeId);
|
|
21061
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
21062
|
+
"data-testid": "fg-canvas-notes-item",
|
|
21063
|
+
className: "flex items-start gap-2 px-3 py-1.5",
|
|
21064
|
+
children: [
|
|
21065
|
+
/* @__PURE__ */ jsx("span", {
|
|
21066
|
+
className: cn(pinClass(note, false), "size-5 shrink-0"),
|
|
21067
|
+
children: note.resolved ? /* @__PURE__ */ jsx(Check, {
|
|
21068
|
+
"aria-hidden": "true",
|
|
21069
|
+
className: "size-3"
|
|
21070
|
+
}) : note.number
|
|
21071
|
+
}),
|
|
21072
|
+
/* @__PURE__ */ jsxs("button", {
|
|
21073
|
+
type: "button",
|
|
21074
|
+
disabled: !onCanvas,
|
|
21075
|
+
onClick: () => show(note),
|
|
21076
|
+
className: "min-w-0 flex-1 text-left disabled:cursor-default",
|
|
21077
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
21078
|
+
className: "block truncate font-medium",
|
|
21079
|
+
children: note.nodeLabel
|
|
21080
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
21081
|
+
className: cn("block truncate text-muted-foreground", note.resolved && "line-through"),
|
|
21082
|
+
children: onCanvas ? note.message : tr("fluidgrids.agentMarks.noteOrphaned", "This node was removed from the workflow.")
|
|
21083
|
+
})]
|
|
21084
|
+
}),
|
|
21085
|
+
!onCanvas && /* @__PURE__ */ jsx("button", {
|
|
21086
|
+
type: "button",
|
|
21087
|
+
"aria-label": tr("fluidgrids.agentMarks.removeNote", "Remove note"),
|
|
21088
|
+
onClick: () => removeNote(note.id),
|
|
21089
|
+
className: "shrink-0 rounded p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
21090
|
+
children: /* @__PURE__ */ jsx(X, {
|
|
21091
|
+
"aria-hidden": "true",
|
|
21092
|
+
className: "size-3.5"
|
|
21093
|
+
})
|
|
21094
|
+
})
|
|
21095
|
+
]
|
|
21096
|
+
}, note.id);
|
|
21097
|
+
})
|
|
21098
|
+
})]
|
|
21099
|
+
})
|
|
21100
|
+
});
|
|
21101
|
+
};
|
|
21102
|
+
const CanvasNotesLayer = () => {
|
|
21103
|
+
const workflowKey = useFlowStore((state) => notesKeyOf(state.workflow));
|
|
21104
|
+
const nodeIds = useFlowStore(useShallow((state) => state.workflow.nodes.map((node) => node.id)));
|
|
21105
|
+
const allNotes = useAgentMarksStore((state) => state.notes);
|
|
21106
|
+
const notes = useMemo(() => allNotes.filter((note) => note.workflowKey === workflowKey), [allNotes, workflowKey]);
|
|
21107
|
+
const live = useMemo(() => new Set(nodeIds), [nodeIds]);
|
|
21108
|
+
const byNode = useMemo(() => {
|
|
21109
|
+
const groups = /* @__PURE__ */ new Map();
|
|
21110
|
+
for (const note of notes) {
|
|
21111
|
+
if (!live.has(note.nodeId)) continue;
|
|
21112
|
+
groups.set(note.nodeId, [...groups.get(note.nodeId) ?? [], note]);
|
|
21113
|
+
}
|
|
21114
|
+
return [...groups.entries()];
|
|
21115
|
+
}, [notes, live]);
|
|
21116
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21117
|
+
/* @__PURE__ */ jsxs(ViewportPortal, { children: [byNode.map(([nodeId, nodeNotes]) => {
|
|
21118
|
+
const openNotes = nodeNotes.filter((note) => !note.resolved);
|
|
21119
|
+
if (openNotes.length === 0) return null;
|
|
21120
|
+
const tone = openNotes.some((note) => note.kind === "fix") ? "fix" : "info";
|
|
21121
|
+
return /* @__PURE__ */ jsx(NodeRing, {
|
|
21122
|
+
nodeId,
|
|
21123
|
+
tone
|
|
21124
|
+
}, nodeId);
|
|
21125
|
+
}), /* @__PURE__ */ jsx(FlashRing, {})] }),
|
|
21126
|
+
byNode.map(([nodeId, nodeNotes]) => /* @__PURE__ */ jsx(NodePins, {
|
|
21127
|
+
nodeId,
|
|
21128
|
+
notes: nodeNotes
|
|
21129
|
+
}, nodeId)),
|
|
21130
|
+
notes.length > 0 && /* @__PURE__ */ jsx(NotesList, {
|
|
21131
|
+
notes,
|
|
21132
|
+
live
|
|
21133
|
+
})
|
|
21134
|
+
] });
|
|
21135
|
+
};
|
|
21136
|
+
|
|
20705
21137
|
// src/builder/lib/autosave-runner.ts
|
|
20706
21138
|
function createAutosaveRunner(deps) {
|
|
20707
21139
|
let timer;
|
|
@@ -27143,8 +27575,8 @@ function EditLeaseBanner() {
|
|
|
27143
27575
|
}
|
|
27144
27576
|
|
|
27145
27577
|
// src/builder/components/flow-builder/flow-builder.tsx
|
|
27146
|
-
const LazyNodeDetailView = lazy(() => import("./node-detail-view-
|
|
27147
|
-
const LazyQuickConfigPanel = lazy(() => import("./quick-config-panel-
|
|
27578
|
+
const LazyNodeDetailView = lazy(() => import("./node-detail-view-Dnhqdn8C.js").then((m) => ({ default: m.NodeDetailView })));
|
|
27579
|
+
const LazyQuickConfigPanel = lazy(() => import("./quick-config-panel-C4tSpCrt.js").then((m) => ({ default: m.QuickConfigPanel })));
|
|
27148
27580
|
const edgeTypes = { deletable: CustomDeletableEdge };
|
|
27149
27581
|
const edgeOptions = {
|
|
27150
27582
|
type: "deletable",
|
|
@@ -27204,7 +27636,7 @@ const FlowBuilder = ({ readOnly = false }) => {
|
|
|
27204
27636
|
const [agentTools, setAgentTools] = useState([]);
|
|
27205
27637
|
useEffect(() => {
|
|
27206
27638
|
let cancelled = false;
|
|
27207
|
-
import("./agentTools-
|
|
27639
|
+
import("./agentTools-D5AW7zHn.js").then(({ createBuilderAgentTools }) => {
|
|
27208
27640
|
if (cancelled) return;
|
|
27209
27641
|
setAgentTools(createBuilderAgentTools({ durableFunctionsEnabled: () => paletteGatesRef.current }));
|
|
27210
27642
|
}).catch((error) => {
|
|
@@ -27251,7 +27683,30 @@ const FlowBuilder = ({ readOnly = false }) => {
|
|
|
27251
27683
|
}
|
|
27252
27684
|
return merged;
|
|
27253
27685
|
}, [nodeTypesVersion, nodes]);
|
|
27254
|
-
const { getNodes } = useReactFlow();
|
|
27686
|
+
const { getNodes, fitView } = useReactFlow();
|
|
27687
|
+
const fitViewRef = useRef(fitView);
|
|
27688
|
+
useEffect(() => {
|
|
27689
|
+
fitViewRef.current = fitView;
|
|
27690
|
+
}, [fitView]);
|
|
27691
|
+
const [noteTools, setNoteTools] = useState([]);
|
|
27692
|
+
useEffect(() => {
|
|
27693
|
+
let cancelled = false;
|
|
27694
|
+
import("./canvasNoteTools-BqQTTCll.js").then(({ createCanvasNoteTools }) => {
|
|
27695
|
+
if (cancelled) return;
|
|
27696
|
+
setNoteTools(createCanvasNoteTools({ focusNode: (nodeId) => fitViewRef.current({
|
|
27697
|
+
nodes: [{ id: nodeId }],
|
|
27698
|
+
duration: 400,
|
|
27699
|
+
padding: .6,
|
|
27700
|
+
maxZoom: 1.25
|
|
27701
|
+
}) }));
|
|
27702
|
+
}).catch((error) => {
|
|
27703
|
+
console.error("[FlowBuilder] Assistant note tools failed to load:", error);
|
|
27704
|
+
});
|
|
27705
|
+
return () => {
|
|
27706
|
+
cancelled = true;
|
|
27707
|
+
};
|
|
27708
|
+
}, []);
|
|
27709
|
+
useAgentTools(noteTools);
|
|
27255
27710
|
const { handleOnEdgeDropConnectEnd, floatingMenuWrapperRef, handleAddConnectedNode } = useAddNodeOnEdgeDrop();
|
|
27256
27711
|
const deleteKeyCode = useDeleteKeyCode();
|
|
27257
27712
|
useNodeSelectedEvent();
|
|
@@ -27321,7 +27776,7 @@ const FlowBuilder = ({ readOnly = false }) => {
|
|
|
27321
27776
|
className: "relative flex-1 rounded-b-xl border border-card-foreground/10 border-t-0 overflow-hidden mx-4 mb-1",
|
|
27322
27777
|
children: [
|
|
27323
27778
|
/* @__PURE__ */ jsx(CanvasControls, {}),
|
|
27324
|
-
/* @__PURE__ */
|
|
27779
|
+
/* @__PURE__ */ jsxs(ReactFlow, {
|
|
27325
27780
|
proOptions: { hideAttribution: true },
|
|
27326
27781
|
onInit: ({ fitView }) => fitView({
|
|
27327
27782
|
minZoom: 1,
|
|
@@ -27354,7 +27809,7 @@ const FlowBuilder = ({ readOnly = false }) => {
|
|
|
27354
27809
|
maxZoom: 1
|
|
27355
27810
|
},
|
|
27356
27811
|
defaultEdgeOptions: edgeOptions,
|
|
27357
|
-
children: /* @__PURE__ */ jsx(Background, { gap: 24 })
|
|
27812
|
+
children: [/* @__PURE__ */ jsx(Background, { gap: 24 }), /* @__PURE__ */ jsx(CanvasNotesLayer, {})]
|
|
27358
27813
|
}),
|
|
27359
27814
|
/* @__PURE__ */ jsx(FluidGridsMark, {}),
|
|
27360
27815
|
nodes.length === 0 && !editingBlocked && /* @__PURE__ */ jsx("div", {
|
|
@@ -27544,4 +27999,4 @@ const FlowBuilderPage = ({ workflow, readOnly = false }) => {
|
|
|
27544
27999
|
] });
|
|
27545
28000
|
};
|
|
27546
28001
|
|
|
27547
|
-
export {
|
|
28002
|
+
export { NodeParamsForm as $, DialogFooter$1 as A, isRunControlNodeType as At, getWorkflowNodeTypes as B, templatePreview as C, Input$1 as Ct, DialogClose as D, filterPaletteNodes as Dt, Dialog$1 as E, WORKFLOW_TEST_NODE_TYPES as Et, DialogTrigger$1 as F, NodeEditorTabBody as G, useWorkflowTemplates as H, computeAttachPosition as I, RunnerStatusPill as J, NodeSettingsPanel as K, useTemplateImportFlow as L, DialogOverlay as M, DialogPortal as N, DialogContent$1 as O, isDurableFunctionNodeType as Ot, DialogTitle$1 as P, useRunners as Q, buildNodeCheck as R, TemplateCard as S, CardTitle$1 as St, badgeVariants as T, RUN_CONTROL_NODE_TYPES as Tt, useInstalledNodes as U, useImportWorkflowTemplate as V, NodeExecutionActions as W, runnerInstallCommand as X, RUNNER_INSTALL_SCRIPT_URL as Y, useRunnerActions as Z, resetAgentMarks as _, Card$1 as _t, useAiWorkflowTemplates as a, CodeEditorField as at, JsonView as b, CardFooter as bt, RunsUsageMeter as c, ReadOnlyFieldset as ct, readRunsQuota as d, SchedulePanel as dt, useGlobalStoreClient as et, runsQuotaLevel as f, NodeEditorTabs as ft, notesKeyOf as g, FluidGridsMark as gt, useWorkflowReloadStore as h, useNodeSelectedEvent as ht, enrichDraftNodes as i, PopoverTrigger$1 as it, DialogHeader$1 as j, DialogDescription$1 as k, isNodeTypeHiddenFromPalette as kt, RunsUsageMeterView as l, useNodeEditorTab as lt, releaseLeaseKeptForReload as m, NodeInputFields as mt, AI_DRAFT_MAX_PROMPT_LENGTH as n, Popover$1 as nt, BUILDER_FULLSCREEN_ATTRIBUTE as o, ConnectionSelector as ot, summarizeRunsQuota as p, TabButton as pt, RunOnSection as q, draftToCanvas as r, PopoverContent$1 as rt, BUILDER_FULLSCREEN_EVENT as s, ConnectionFormDialog as st, FlowBuilderPage as t, NDV_Z_INDEX as tt, useRunsQuota as u, SCHEDULE_NODE_TYPE as ut, useAgentMarksStore as v, CardContent$1 as vt, Badge$1 as w, DURABLE_FUNCTION_NODE_TYPES as wt, TableView as x, CardHeader$1 as xt, toPlainText as y, CardDescription$1 as yt, buildRequiredNodeTypes as z };
|