@burdenoff/fluidgrids-fe-sdk 2026.1111.17 → 2026.1111.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/{AiStudioPage-kJHSsIcj.js → AiStudioPage-htYcXz43.js} +2 -2
  2. package/dist/{MissingNodesInstallModal-CMybsI8h.js → MissingNodesInstallModal-Dly7T2_7.js} +1 -1
  3. package/dist/{SettingsPage-CgkRlCM5.js → SettingsPage-CJLZvv4J.js} +1485 -1484
  4. package/dist/{TemplateCard-DsC0KNH1.js → TemplateCard-BdB087SP.js} +47 -11
  5. package/dist/{TemplateConfigurationModal-BhGWIzpm.js → TemplateConfigurationModal-CPVcTDSi.js} +1 -1
  6. package/dist/{TemplateDetailPanel-D4cUVr-m.js → TemplateDetailPanel-DruadMkg.js} +1 -1
  7. package/dist/{TemplatePreview-GthzCOSo.js → TemplatePreview-CNrYJXw2.js} +2 -2
  8. package/dist/{TemplatesPage-CsDNT54N.js → TemplatesPage-BU4tSqOo.js} +1 -1
  9. package/dist/{WorkflowBuilder-DEi_9YOb.js → WorkflowBuilder-BSPGcOfA.js} +2 -2
  10. package/dist/{WorkflowBuilder-iPqXgbYr.js → WorkflowBuilder-DMonxuoY.js} +4 -4
  11. package/dist/WorkflowShareDialog-mbIwRQUY.js +168 -0
  12. package/dist/{WorkflowsList-CA20dVIl.js → WorkflowsList-DiiGyZkT.js} +2 -1
  13. package/dist/{agentTools-CkRJB7r5.js → agentTools-C7pdNJqE.js} +2 -2
  14. package/dist/{canvasNoteTools-CQIc7Oxg.js → canvasNoteTools-CqiPTBa1.js} +1 -1
  15. package/dist/code-editor-field-9-GdVE_8.js +111 -0
  16. package/dist/{flow-builder-y2Y1iG4Y.js → flow-builder-CV5Zu_Hq.js} +246 -8639
  17. package/dist/{index-W2o__Nm6.d.ts → index-6JR2vD9J.d.ts} +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.js +15 -13
  20. package/dist/{node-detail-view-B91kXzIy.js → node-detail-view-ttMp6H6K.js} +4 -2
  21. package/dist/node-editor-tab-body-Bqb--pZy.js +174 -0
  22. package/dist/observability.d.ts +1 -1
  23. package/dist/popover-BbVOHsfO.js +472 -0
  24. package/dist/{quick-config-panel-DggPl1vY.js → quick-config-panel-Da4uOVSs.js} +2 -1
  25. package/dist/schedule-panel-DnFN933b.js +7542 -0
  26. package/dist/settings.js +1 -1
  27. package/dist/sidebar-node-editor-lcnIZxdR.js +177 -0
  28. package/dist/templates.js +2 -2
  29. package/dist/{useAgentDefinitions-Dpz0HDTq.js → useAgentDefinitions-B0dam3_o.js} +1 -396
  30. package/dist/useConnections-Cs7LCWEM.js +401 -0
  31. package/dist/{WorkflowShareDialog-B_gn968c.js → useWorkflowCodeExport-Bu2QYJzA.js} +4 -161
  32. package/dist/workflow-builder.js +2 -2
  33. package/dist/workflow-pages.d.ts +1 -1
  34. package/dist/workflow-pages.js +6 -6
  35. package/dist/workflows-list.js +1 -1
  36. package/package.json +1 -1
@@ -1052,8 +1052,34 @@ function buildNodeCheck(requiredNodeTypes, installedAppIds) {
1052
1052
  };
1053
1053
  }
1054
1054
 
1055
+ // src/utils/dismiss-toast.ts
1056
+ /** Resolves on the next zero-delay timer. */
1057
+ function nextTimer() {
1058
+ return new Promise((resolve) => setTimeout(resolve, 0));
1059
+ }
1060
+ /**
1061
+ * Dismiss a toast that may have been created a moment ago.
1062
+ *
1063
+ * sonner adds a toast to the screen on a zero-delay timer, but delivers a
1064
+ * dismissal on animation frames. A dismissal sent while the add is still
1065
+ * queued (the work finished fast, or the main thread was busy rendering) is
1066
+ * applied first, finds nothing to remove, and the toast then appears and
1067
+ * stays: "Loading template..." stuck on the template preview page
1068
+ * (BOFF-11153). Timers of equal delay fire in the order they were set, so
1069
+ * waiting one zero-delay timer runs after the queued add, and the dismissal
1070
+ * then lands on the toast.
1071
+ *
1072
+ * Where a toast is followed by another message, replace it through the same
1073
+ * `id` instead (`toast.success(…, { id })`); that goes through the same
1074
+ * queue as the add and needs no dismissal.
1075
+ */
1076
+ async function dismissToastWhenShown(id) {
1077
+ await nextTimer();
1078
+ toast.dismiss(id);
1079
+ }
1080
+
1055
1081
  // src/hooks/store/useTemplateImportFlow.tsx
1056
- const LazyTemplateConfigurationModal = lazy(() => import("./TemplateConfigurationModal-BhGWIzpm.js").then((m) => ({ default: m.TemplateConfigurationModal })));
1082
+ const LazyTemplateConfigurationModal = lazy(() => import("./TemplateConfigurationModal-CPVcTDSi.js").then((m) => ({ default: m.TemplateConfigurationModal })));
1057
1083
  /**
1058
1084
  * "Use this template", the same everywhere it is offered: the builder's
1059
1085
  * template panel, the Templates page and the template preview page
@@ -1109,9 +1135,11 @@ function useTemplateImportFlow({ onImported, onQuotaExhausted }) {
1109
1135
  name: overrides?.name,
1110
1136
  templateInputs: overrides?.templateInputs
1111
1137
  });
1112
- toast.dismiss("template-import");
1113
1138
  if (result.success && result.workflow) {
1114
- toast.success(tr("fluidgrids.builder.desktopSidebar.workflowCreatedToast", "Workflow created from template!"), { description: `"${result.workflow.name}" is ready to edit` });
1139
+ toast.success(tr("fluidgrids.builder.desktopSidebar.workflowCreatedToast", "Workflow created from template!"), {
1140
+ id: "template-import",
1141
+ description: `"${result.workflow.name}" is ready to edit`
1142
+ });
1115
1143
  if ((content.requiredCredentialTypes?.length ?? 0) > 0) toast.info(tr("fluidgrids.builder.desktopSidebar.credentialsNeededToast", "Credentials needed"), {
1116
1144
  description: `Configure ${content.requiredCredentialTypes?.join(", ")} in the Credentials page`,
1117
1145
  duration: 6e3
@@ -1120,14 +1148,20 @@ function useTemplateImportFlow({ onImported, onQuotaExhausted }) {
1120
1148
  return true;
1121
1149
  }
1122
1150
  if (onQuotaExhausted && isQuotaExhaustedFailure(result.error?.cause ?? result.error)) {
1151
+ dismissToastWhenShown("template-import");
1123
1152
  onQuotaExhausted();
1124
1153
  return false;
1125
1154
  }
1126
- toast.error(tr("fluidgrids.builder.desktopSidebar.importTemplateErrorToast", "Failed to import template"), { description: result.message || tr("fluidgrids.builder.desktopSidebar.importTemplateErrorFallback", "Unknown error during import") });
1155
+ toast.error(tr("fluidgrids.builder.desktopSidebar.importTemplateErrorToast", "Failed to import template"), {
1156
+ id: "template-import",
1157
+ description: result.message || tr("fluidgrids.builder.desktopSidebar.importTemplateErrorFallback", "Unknown error during import")
1158
+ });
1127
1159
  return false;
1128
1160
  } catch (err) {
1129
- toast.dismiss("template-import");
1130
- toast.error(tr("fluidgrids.builder.desktopSidebar.importFailedToast", "Import failed"), { description: err instanceof Error ? err.message : tr("fluidgrids.builder.desktopSidebar.importFailedFallback", "Unknown error") });
1161
+ toast.error(tr("fluidgrids.builder.desktopSidebar.importFailedToast", "Import failed"), {
1162
+ id: "template-import",
1163
+ description: err instanceof Error ? err.message : tr("fluidgrids.builder.desktopSidebar.importFailedFallback", "Unknown error")
1164
+ });
1131
1165
  return false;
1132
1166
  } finally {
1133
1167
  setInstallProgress(null);
@@ -1145,16 +1179,16 @@ function useTemplateImportFlow({ onImported, onQuotaExhausted }) {
1145
1179
  toast.loading(tr("fluidgrids.builder.desktopSidebar.loadingTemplateToast", "Loading template..."), { id: "template-load" });
1146
1180
  try {
1147
1181
  const full = await getTemplateDetail(template.id);
1148
- toast.dismiss("template-load");
1149
1182
  if (!full) {
1150
- toast.error(tr("fluidgrids.builder.desktopSidebar.templateDetailsErrorToast", "Failed to load template details"));
1183
+ toast.error(tr("fluidgrids.builder.desktopSidebar.templateDetailsErrorToast", "Failed to load template details"), { id: "template-load" });
1151
1184
  return;
1152
1185
  }
1153
1186
  const content = await resolveTemplateContent(full, buildRequiredNodeTypes);
1154
1187
  if (!content) {
1155
- toast.error(tr("fluidgrids.builder.desktopSidebar.invalidTemplateDataToast", "Template does not contain valid workflow data"));
1188
+ toast.error(tr("fluidgrids.builder.desktopSidebar.invalidTemplateDataToast", "Template does not contain valid workflow data"), { id: "template-load" });
1156
1189
  return;
1157
1190
  }
1191
+ await dismissToastWhenShown("template-load");
1158
1192
  const nodeCheck = buildNodeCheck(content.requiredNodeTypes ?? [], installedAppIds);
1159
1193
  if (needsTemplateConfiguration(content, nodeCheck)) {
1160
1194
  setPending({
@@ -1166,8 +1200,10 @@ function useTemplateImportFlow({ onImported, onQuotaExhausted }) {
1166
1200
  }
1167
1201
  await createCopy(full, content, nodeCheck);
1168
1202
  } catch (err) {
1169
- toast.dismiss("template-load");
1170
- toast.error(tr("fluidgrids.builder.desktopSidebar.genericErrorToast", "Something went wrong"), { description: err instanceof Error ? err.message : tr("fluidgrids.builder.desktopSidebar.genericErrorFallback", "Failed to process template") });
1203
+ toast.error(tr("fluidgrids.builder.desktopSidebar.genericErrorToast", "Something went wrong"), {
1204
+ id: "template-load",
1205
+ description: err instanceof Error ? err.message : tr("fluidgrids.builder.desktopSidebar.genericErrorFallback", "Failed to process template")
1206
+ });
1171
1207
  } finally {
1172
1208
  setBusy(false);
1173
1209
  }
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { a as DialogFooter, d as Input, i as DialogDescription, l as DialogTitle, o as DialogHeader, r as DialogContent, t as Dialog } from "./dialog-Cjb4lB4D.js";
3
3
  import { t as Button } from "./button-DG7y_FVJ.js";
4
- import { r as Badge } from "./TemplateCard-DsC0KNH1.js";
4
+ import { r as Badge } from "./TemplateCard-BdB087SP.js";
5
5
  import { t as Label } from "./label-CDy86rIZ.js";
6
6
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
7
7
  import { useEffect, useMemo, useState } from "react";
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { t as Button } from "./button-DG7y_FVJ.js";
3
- import { n as templatePreview, r as Badge } from "./TemplateCard-DsC0KNH1.js";
3
+ import { n as templatePreview, r as Badge } from "./TemplateCard-BdB087SP.js";
4
4
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
5
5
  import { i as SdkPermissionButton, t as FLUIDGRIDS_PERMISSIONS } from "./constants-B2lhWNoN.js";
6
6
  import { Icon } from "@iconify/react";
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
3
  import { t as ThemeProvider } from "./theme-provider-B3mIuz2d.js";
4
- import { t as FlowBuilderPage } from "./flow-builder-y2Y1iG4Y.js";
5
- import { a as useTemplateImportFlow, l as useImportWorkflowTemplate } from "./TemplateCard-DsC0KNH1.js";
4
+ import { t as FlowBuilderPage } from "./flow-builder-CV5Zu_Hq.js";
5
+ import { a as useTemplateImportFlow, l as useImportWorkflowTemplate } from "./TemplateCard-BdB087SP.js";
6
6
  import { t as QuotaExhaustedDialog } from "./QuotaExhaustedDialog-7IFSaLyi.js";
7
7
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
8
8
  import { i as SdkPermissionButton, t as FLUIDGRIDS_PERMISSIONS } from "./constants-B2lhWNoN.js";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
3
  import { h as useWorkflowDefinitions, l as useLazyWorkflowDefinition, s as useDuplicateWorkflow } from "./useWorkflows-BlVVu8NM.js";
4
- import { a as useTemplateImportFlow, t as TemplateCard, u as useWorkflowTemplates, y as useGlobalStoreClient } from "./TemplateCard-DsC0KNH1.js";
4
+ import { a as useTemplateImportFlow, t as TemplateCard, u as useWorkflowTemplates, y as useGlobalStoreClient } from "./TemplateCard-BdB087SP.js";
5
5
  import { YF as WorkflowStatus } from "./graphql-types-CDKVWH2R.js";
6
6
  import { i as SdkPermissionButton, t as FLUIDGRIDS_PERMISSIONS } from "./constants-B2lhWNoN.js";
7
7
  import { t as QuotaExhaustedDialog } from "./QuotaExhaustedDialog-7IFSaLyi.js";
@@ -3,10 +3,10 @@ import { hn as useCanvasEdges, n as FluidGridsProvider } from "./FluidGridsProvi
3
3
  import { H as useFlowStore } from "./node-operation-selection--oy-yEq_.js";
4
4
  import { t as Button } from "./button-DG7y_FVJ.js";
5
5
  import { d as Input } from "./dialog-Cjb4lB4D.js";
6
- import { b as Card } from "./TemplateCard-DsC0KNH1.js";
6
+ import { b as Card } from "./TemplateCard-BdB087SP.js";
7
7
  import { t as cn } from "./cn-QqidFIgr.js";
8
8
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
9
- import { B as FluidGridsMark, z as useNodeSelectedEvent } from "./flow-builder-y2Y1iG4Y.js";
9
+ import { S as FluidGridsMark, x as useNodeSelectedEvent } from "./flow-builder-CV5Zu_Hq.js";
10
10
  import { useCallback, useEffect, useRef, useState } from "react";
11
11
  import { Background, Controls, Handle, MarkerType, MiniMap, Position, ReactFlow, ReactFlowProvider, useReactFlow } from "@xyflow/react";
12
12
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
- import { h as useWorkflowReloadStore, m as releaseLeaseKeptForReload, t as FlowBuilderPage } from "./flow-builder-y2Y1iG4Y.js";
3
+ import { g as useWorkflowReloadStore, h as releaseLeaseKeptForReload, t as FlowBuilderPage } from "./flow-builder-CV5Zu_Hq.js";
4
4
  import { n as registerLiveCanvasHandler } from "./liveCanvas-D5ytXDp8.js";
5
5
  import { _ as throwIfResultError, n as getFluidGridsClient, x as getFluidGridsConfig } from "./client-C9XSzSXr.js";
6
6
  import { Er as WorkflowDefinitionDocument } from "./graphql-operation-types-kX-MMjgW.js";
@@ -9,10 +9,10 @@ import { At as useSaveStatusStore, Et as classifyCreateRefusal, J as paramsFromO
9
9
  import { F as mergeStickyNotes, H as useFlowStore } from "./node-operation-selection--oy-yEq_.js";
10
10
  import { t as canvasFromDefinition } from "./definition-canvas--3MPHoP-.js";
11
11
  import { l as useLazyWorkflowDefinition, n as useCreateWorkflow } from "./useWorkflows-BlVVu8NM.js";
12
- import { c as getWorkflowNodeTypes, o as buildNodeCheck, s as buildRequiredNodeTypes, v as useInstalledNodes } from "./TemplateCard-DsC0KNH1.js";
12
+ import { c as getWorkflowNodeTypes, o as buildNodeCheck, s as buildRequiredNodeTypes, v as useInstalledNodes } from "./TemplateCard-BdB087SP.js";
13
13
  import { c as withoutGuidedTourIntent, l as decideMissingWorkflow, o as isGuidedTourIntent, s as pickGuidedTour, u as isCreateIntent } from "./firstWorkflow-C_h0KMB2.js";
14
14
  import { f as BILLING_PLANS_PATH } from "./fluidgridsEvents-CNdcrUNN.js";
15
- import { O as CreateWorkflowRefusalDialog } from "./WorkflowShareDialog-B_gn968c.js";
15
+ import { T as CreateWorkflowRefusalDialog } from "./useWorkflowCodeExport-Bu2QYJzA.js";
16
16
  import { Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
17
17
  import { useLocation, useNavigate, useParams } from "react-router";
18
18
  import { Icon } from "@iconify/react";
@@ -1088,7 +1088,7 @@ function GuidedTourStarter({ ready }) {
1088
1088
  }
1089
1089
 
1090
1090
  // src/builder/app/workflow/work/page.tsx
1091
- const LazyMissingNodesInstallModal = lazy(() => import("./MissingNodesInstallModal-CMybsI8h.js").then((m) => ({ default: m.MissingNodesInstallModal })));
1091
+ const LazyMissingNodesInstallModal = lazy(() => import("./MissingNodesInstallModal-Dly7T2_7.js").then((m) => ({ default: m.MissingNodesInstallModal })));
1092
1092
  function WorkflowItem({ readonly = false }) {
1093
1093
  if (readonly) {
1094
1094
  const queryParams = new URLSearchParams(window.location.search);
@@ -0,0 +1,168 @@
1
+ "use client";
2
+ import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
+ import { n as getFluidGridsClient } from "./client-C9XSzSXr.js";
4
+ import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
5
+ import { JF as WorkflowShareRole, hF as WorkflowAccess, nI as WorkflowVisibility, qF as WorkflowSharePrincipalType } from "./graphql-types-CDKVWH2R.js";
6
+ import { Mn as ShareWorkflowDocument, li as WorkflowSharingDocument } from "./graphql-operation-types-kX-MMjgW.js";
7
+ import { useMemo } from "react";
8
+ import { jsx } from "react/jsx-runtime";
9
+ import { ResourceShareDialog, createSharePrincipalLister } from "@burdenoff/fe-libs/connections";
10
+
11
+ // src/utils/workflow-sharing.ts
12
+ /** Whether someone reaching a workflow this way may change who it is shared with. */
13
+ function canShareWorkflow(access) {
14
+ return access === "OWNER" || access === "EDITOR" || access === "ADMINISTRATOR";
15
+ }
16
+ /** Whether someone reaching a workflow this way may change or run it. */
17
+ function canEditWorkflow(access) {
18
+ return access !== "VIEWER";
19
+ }
20
+ const VISIBILITY_OUT = {
21
+ WORKSPACE: "WORKSPACE",
22
+ RESTRICTED: "RESTRICTED"
23
+ };
24
+ const VISIBILITY_IN = {
25
+ ["WORKSPACE"]: "WORKSPACE",
26
+ ["RESTRICTED"]: "RESTRICTED"
27
+ };
28
+ const PRINCIPAL_OUT = {
29
+ USER: "USER",
30
+ GROUP: "GROUP",
31
+ PROJECT: "PROJECT"
32
+ };
33
+ const PRINCIPAL_IN = {
34
+ ["USER"]: "USER",
35
+ ["GROUP"]: "GROUP",
36
+ ["PROJECT"]: "PROJECT"
37
+ };
38
+ const ROLE_OUT = {
39
+ VIEWER: "VIEWER",
40
+ EDITOR: "EDITOR"
41
+ };
42
+ const ROLE_IN = {
43
+ ["VIEWER"]: "VIEWER",
44
+ ["EDITOR"]: "EDITOR"
45
+ };
46
+ /** The service's answer as the dialog sees it; `name` is what the title shows. */
47
+ function toResourceSharing(sharing, name = sharing.workflowKey) {
48
+ const shares = sharing.shares.map((share) => ({
49
+ principalType: PRINCIPAL_IN[share.principalType],
50
+ principalId: share.principalId,
51
+ role: ROLE_IN[share.role]
52
+ }));
53
+ return {
54
+ id: sharing.workflowKey,
55
+ name,
56
+ ownerId: sharing.ownerId ?? "",
57
+ visibility: VISIBILITY_IN[sharing.visibility],
58
+ canManage: canShareWorkflow(sharing.myAccess),
59
+ administrator: sharing.canTransferOwnership,
60
+ shares
61
+ };
62
+ }
63
+ /** The workflow service behind the generic Share dialog. */
64
+ function workflowSharingApi(client, listPrincipals) {
65
+ return {
66
+ async getSharing(workflowKey) {
67
+ const result = await client.query({
68
+ query: WorkflowSharingDocument,
69
+ variables: { workflowKey },
70
+ fetchPolicy: "network-only"
71
+ });
72
+ if (!result.data) throw new Error("Workflow not found");
73
+ return toResourceSharing(result.data.workflowSharing);
74
+ },
75
+ listPrincipals,
76
+ async share(input) {
77
+ const result = await client.mutate({
78
+ mutation: ShareWorkflowDocument,
79
+ variables: { input: {
80
+ workflowKey: input.resourceId,
81
+ visibility: VISIBILITY_OUT[input.visibility],
82
+ shares: input.shares.map((share) => ({
83
+ principalType: PRINCIPAL_OUT[share.principalType],
84
+ principalId: share.principalId,
85
+ role: ROLE_OUT[share.role]
86
+ })),
87
+ ...input.ownerId ? { ownerId: input.ownerId } : {}
88
+ } }
89
+ });
90
+ if (!result.data) throw new Error("Workflow not found");
91
+ return toResourceSharing(result.data.shareWorkflow);
92
+ }
93
+ };
94
+ }
95
+
96
+ // src/components/WorkflowShareDialog.tsx
97
+ /**
98
+ * WorkflowShareDialog — who can reach a workflow (BOFF-9726).
99
+ *
100
+ * The workflow version of fe-libs' ResourceShareDialog: a workflow is shared
101
+ * with people, groups or workspace projects as "Can view" or "Can edit", or
102
+ * left to the whole workspace. The workflow service decides who may change
103
+ * the sharing (its owner, an editor it is shared with, or a workspace admin)
104
+ * and answers NOT_FOUND to anyone else; this dialog only shows what the
105
+ * caller may do and sends the complete new sharing back.
106
+ */
107
+ var WorkflowShareDialog_exports = /* @__PURE__ */ __exportAll({ WorkflowShareDialog: () => WorkflowShareDialog });
108
+ function workflowShareCopy(tr, name) {
109
+ return {
110
+ title: (loaded) => tr("fluidgrids.sharing.title", "Share {{name}}", { name: name || loaded }),
111
+ titleLoading: tr("fluidgrids.sharing.titleLoading", "Share workflow"),
112
+ loading: tr("fluidgrids.sharing.loading", "Loading sharing…"),
113
+ readOnly: tr("fluidgrids.sharing.readOnly", "Only the owner, editors and workspace admins can change who can open this workflow."),
114
+ save: tr("fluidgrids.sharing.save", "Save"),
115
+ cancel: tr("fluidgrids.sharing.cancel", "Cancel"),
116
+ close: tr("fluidgrids.sharing.close", "Close"),
117
+ owner: tr("fluidgrids.sharing.owner", "Owner"),
118
+ add: tr("fluidgrids.sharing.add", "Share with people, groups or projects"),
119
+ addPlaceholder: tr("fluidgrids.sharing.addPlaceholder", "Search by name or email"),
120
+ sharedWith: tr("fluidgrids.sharing.sharedWith", "Shared with"),
121
+ onlyOwner: tr("fluidgrids.sharing.onlyOwner", "No one else yet. Only the owner and workspace admins can open it."),
122
+ none: tr("fluidgrids.sharing.none", "No one in particular."),
123
+ principalType: {
124
+ USER: tr("fluidgrids.sharing.type.user", "Person"),
125
+ GROUP: tr("fluidgrids.sharing.type.group", "Group"),
126
+ PROJECT: tr("fluidgrids.sharing.type.project", "Project")
127
+ },
128
+ roleFor: (grantee) => tr("fluidgrids.sharing.roleFor", "Access for {{name}}", { name: grantee }),
129
+ remove: (grantee) => tr("fluidgrids.sharing.remove", "Stop sharing with {{name}}", { name: grantee }),
130
+ visibility: {
131
+ legend: tr("fluidgrids.sharing.visibility.label", "Who can open this workflow"),
132
+ restricted: tr("fluidgrids.sharing.visibility.restricted", "Only people I choose"),
133
+ restrictedHint: tr("fluidgrids.sharing.visibility.restrictedHint", "You, the people, groups or projects you share it with, and workspace admins. Everyone else cannot find it."),
134
+ workspace: tr("fluidgrids.sharing.visibility.workspace", "Everyone in this workspace"),
135
+ workspaceHint: tr("fluidgrids.sharing.visibility.workspaceHint", "Anyone in the workspace can open it, as their workspace role allows.")
136
+ }
137
+ };
138
+ }
139
+ function WorkflowShareDialog({ open, onClose, workflowKey, workflowName, workspaceId, client, onShared, api: apiOverride }) {
140
+ const tr = useTr();
141
+ const api = useMemo(() => {
142
+ if (apiOverride) return apiOverride;
143
+ const apollo = client ?? getFluidGridsClient();
144
+ return workflowSharingApi(apollo, createSharePrincipalLister(apollo));
145
+ }, [apiOverride, client]);
146
+ const copy = useMemo(() => workflowShareCopy(tr, workflowName), [tr, workflowName]);
147
+ const roles = useMemo(() => [{
148
+ value: "VIEWER",
149
+ label: tr("fluidgrids.sharing.role.viewer", "Can view")
150
+ }, {
151
+ value: "EDITOR",
152
+ label: tr("fluidgrids.sharing.role.editor", "Can edit")
153
+ }], [tr]);
154
+ return /* @__PURE__ */ jsx(ResourceShareDialog, {
155
+ open,
156
+ onClose,
157
+ api,
158
+ resourceId: workflowKey,
159
+ workspaceId,
160
+ roles,
161
+ copy,
162
+ testIdPrefix: "share-workflow",
163
+ translate: tr,
164
+ onShared: onShared ? () => onShared() : void 0
165
+ });
166
+ }
167
+
168
+ export { canShareWorkflow as i, WorkflowShareDialog_exports as n, canEditWorkflow as r, WorkflowShareDialog as t };
@@ -2,13 +2,14 @@
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
3
  import { c as useImportWorkflow, h as useWorkflowDefinitions, l as useLazyWorkflowDefinition, n as useCreateWorkflow, o as useDeleteWorkflow, s as useDuplicateWorkflow, u as usePreviewN8nImport } from "./useWorkflows-BlVVu8NM.js";
4
4
  import { Dt as createThenOpen, t as FluidGridsContext } from "./FluidGridsProvider-dvZSNtSW.js";
5
- import { C as readProjectArchive, D as workflowNameFromFile, E as typeScriptModuleFile, O as CreateWorkflowRefusalDialog, S as primaryImportModule, T as typeScriptImportSource, _ as importFileKind, a as downloadWorkflowExport, c as canShareWorkflow, d as MAX_IMPORT_ARCHIVE_BYTES, f as MAX_IMPORT_SOURCE_BYTES, g as importAcceptAttribute, l as CODE_EXPORT_FORMATS, m as checkTypeScriptImport, o as parseWorkflowImport, p as assertImportByteSize, r as useExportWorkflowAs, s as canEditWorkflow, t as WorkflowShareDialog, u as ImportSourceError } from "./WorkflowShareDialog-B_gn968c.js";
5
+ import { C as typeScriptModuleFile, S as typeScriptImportSource, T as CreateWorkflowRefusalDialog, a as parseWorkflowImport, b as readProjectArchive, c as MAX_IMPORT_ARCHIVE_BYTES, d as checkTypeScriptImport, i as downloadWorkflowExport, l as MAX_IMPORT_SOURCE_BYTES, m as importFileKind, n as useExportWorkflowAs, o as CODE_EXPORT_FORMATS, p as importAcceptAttribute, s as ImportSourceError, u as assertImportByteSize, w as workflowNameFromFile, y as primaryImportModule } from "./useWorkflowCodeExport-Bu2QYJzA.js";
6
6
  import { f as BILLING_PLANS_PATH, y as isQuotaExhaustedFailure } from "./fluidgridsEvents-CNdcrUNN.js";
7
7
  import { H as useFlowStore } from "./node-operation-selection--oy-yEq_.js";
8
8
  import { a as SdkPermissionGate, i as SdkPermissionButton, s as useFluidGridsPermission, t as FLUIDGRIDS_PERMISSIONS } from "./constants-B2lhWNoN.js";
9
9
  import { t as QuotaExhaustedDialog } from "./QuotaExhaustedDialog-7IFSaLyi.js";
10
10
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
11
11
  import { CF as WorkflowExportFormat, gF as WorkflowAccessFilter } from "./graphql-types-CDKVWH2R.js";
12
+ import { i as canShareWorkflow, r as canEditWorkflow, t as WorkflowShareDialog } from "./WorkflowShareDialog-mbIwRQUY.js";
12
13
  import { l as useFluidGridsFlag, t as FLUIDGRIDS_FLAGS } from "./feature-flags-DM5myWaD.js";
13
14
  import { _ as throwIfResultError, m as describeOperationError, n as getFluidGridsClient } from "./client-C9XSzSXr.js";
14
15
  import { $t as RenameWorkflowProjectDocument, G as DeleteWorkflowFolderDocument, Hr as WorkflowProjectsDocument, K as DeleteWorkflowProjectDocument, M as CreateWorkflowProjectDocument, Nr as WorkflowFoldersDocument, Qt as RenameWorkflowFolderDocument, Tt as MoveWorkflowFolderDocument, g as BulkMoveWorkflowsToFolderDocument, h as BulkDeleteWorkflowsDocument, j as CreateWorkflowFolderDocument, m as BulkAssignWorkflowsToProjectDocument } from "./graphql-operation-types-kX-MMjgW.js";
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { At as useSaveStatusStore, J as paramsFromOperation, K as nodeTitle, L as createConfiguredNode, W as fixedParameterState, Y as selectedOperationOf, _ as registryNodeData, f as isNodeConfigsInitialized, o as NODE_CONFIGS, pt as autosaveAllowedFor, xt as isFlowComplete } from "./FluidGridsProvider-dvZSNtSW.js";
3
3
  import { E as resolveOutputHandles, H as useFlowStore, T as resolveInputHandles } from "./node-operation-selection--oy-yEq_.js";
4
- import { j as isNodeTypeHiddenFromPalette } from "./TemplateCard-DsC0KNH1.js";
5
- import { y as computeAttachPosition } from "./flow-builder-y2Y1iG4Y.js";
4
+ import { j as isNodeTypeHiddenFromPalette } from "./TemplateCard-BdB087SP.js";
5
+ import { b as computeAttachPosition } from "./flow-builder-CV5Zu_Hq.js";
6
6
  import { AgentToolError, defineAgentTool } from "@burdenoff/fe-libs/shared/agent-tools";
7
7
  import { z } from "zod";
8
8
 
@@ -2,7 +2,7 @@
2
2
  import { n as useAgentMarksStore, r as toPlainText } from "./marksStore-DGCXjTmZ.js";
3
3
  import { J as paramsFromOperation, K as nodeTitle, Y as selectedOperationOf } from "./FluidGridsProvider-dvZSNtSW.js";
4
4
  import { H as useFlowStore } from "./node-operation-selection--oy-yEq_.js";
5
- import { g as notesKeyOf } from "./flow-builder-y2Y1iG4Y.js";
5
+ import { _ as notesKeyOf } from "./flow-builder-CV5Zu_Hq.js";
6
6
  import { AgentToolError, defineAgentTool } from "@burdenoff/fe-libs/shared/agent-tools";
7
7
  import { z } from "zod";
8
8
 
@@ -0,0 +1,111 @@
1
+ "use client";
2
+ import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
+ import { g as toCodeMirrorDiagnostics } from "./node-operation-selection--oy-yEq_.js";
4
+ import { Fn as useBuilderReadOnly } from "./FluidGridsProvider-dvZSNtSW.js";
5
+ import { useEffect, useMemo, useRef, useState } from "react";
6
+ import { cn } from "@burdenoff/fe-libs/utils";
7
+ import { jsx } from "react/jsx-runtime";
8
+ import CodeMirror, { EditorView } from "@uiw/react-codemirror";
9
+ import { lintGutter, setDiagnostics } from "@codemirror/lint";
10
+
11
+ // src/builder/components/ndv/code-editor-field.tsx
12
+ var code_editor_field_exports = /* @__PURE__ */ __exportAll({ CodeEditorField: () => CodeEditorField });
13
+ /**
14
+ * Loads the grammar for the selected language on demand (BOFF-6695). The
15
+ * lezer parsers are heavy and only one language is ever shown per editor, so
16
+ * each language pack is a separate chunk that loads when an editor for it
17
+ * mounts. `null` until the pack resolves: the editor renders as plain text
18
+ * for that first moment and reconfigures once highlighting arrives.
19
+ */
20
+ function useLanguageExtension(language) {
21
+ const [extension, setExtension] = useState(null);
22
+ useEffect(() => {
23
+ let cancelled = false;
24
+ (language === "python" ? import("@codemirror/lang-python").then((m) => m.python()) : import("@codemirror/lang-javascript").then((m) => m.javascript({ typescript: language === "typescript" }))).then((ext) => {
25
+ if (!cancelled) setExtension(ext);
26
+ });
27
+ return () => {
28
+ cancelled = true;
29
+ };
30
+ }, [language]);
31
+ return extension;
32
+ }
33
+ function normalizeCodeLanguage(value) {
34
+ return typeof value === "string" && value.toLowerCase().startsWith("py") ? "python" : "javascript";
35
+ }
36
+ /**
37
+ * Follows the app shell's `.dark` class on <html> so the editor theme matches
38
+ * the surrounding UI (fe-libs toggles that class; there is no context to read).
39
+ */
40
+ function useIsDarkTheme() {
41
+ const [dark, setDark] = useState(() => typeof document !== "undefined" && document.documentElement.classList.contains("dark"));
42
+ useEffect(() => {
43
+ const root = document.documentElement;
44
+ const observer = new MutationObserver(() => setDark(root.classList.contains("dark")));
45
+ observer.observe(root, {
46
+ attributes: true,
47
+ attributeFilter: ["class"]
48
+ });
49
+ return () => observer.disconnect();
50
+ }, []);
51
+ return dark;
52
+ }
53
+ /**
54
+ * CodeMirror-backed editor for `code`-typed NDV parameters (n8n-style Code
55
+ * node). Purely presentational — the stored value stays a plain string in
56
+ * `args.state`, exactly like the single-line input it replaces.
57
+ */
58
+ function CodeEditorField({ value, language, placeholder, label, onChange, readOnly: readOnlyProp = false, diagnostics, minHeight = "180px", maxHeight = "420px", className, "data-testid": testId }) {
59
+ const dark = useIsDarkTheme();
60
+ const withLint = diagnostics !== void 0;
61
+ const builderReadOnly = useBuilderReadOnly();
62
+ const readOnly = readOnlyProp || builderReadOnly;
63
+ const languageExtension = useLanguageExtension(language);
64
+ const extensions = useMemo(() => [
65
+ ...languageExtension ? [languageExtension] : [],
66
+ ...label ? [EditorView.contentAttributes.of({ "aria-label": label })] : [],
67
+ ...withLint ? [lintGutter()] : []
68
+ ], [
69
+ languageExtension,
70
+ label,
71
+ withLint
72
+ ]);
73
+ const viewRef = useRef(null);
74
+ useEffect(() => {
75
+ const view = viewRef.current;
76
+ if (!view || diagnostics === void 0) return;
77
+ view.dispatch(setDiagnostics(view.state, toCodeMirrorDiagnostics(view.state.doc.toString(), diagnostics)));
78
+ }, [diagnostics]);
79
+ return /* @__PURE__ */ jsx("div", {
80
+ "data-testid": testId,
81
+ className: cn("overflow-hidden rounded-md border border-input text-sm transition-colors focus-within:border-primary", className),
82
+ onKeyDown: (e) => e.stopPropagation(),
83
+ onMouseDown: (e) => e.stopPropagation(),
84
+ children: /* @__PURE__ */ jsx(CodeMirror, {
85
+ value,
86
+ onChange: readOnly ? void 0 : onChange,
87
+ extensions,
88
+ theme: dark ? "dark" : "light",
89
+ placeholder,
90
+ minHeight,
91
+ maxHeight,
92
+ editable: !readOnly,
93
+ readOnly,
94
+ onCreateEditor: (view) => {
95
+ viewRef.current = view;
96
+ if (diagnostics?.length) view.dispatch(setDiagnostics(view.state, toCodeMirrorDiagnostics(view.state.doc.toString(), diagnostics)));
97
+ },
98
+ basicSetup: {
99
+ lineNumbers: true,
100
+ foldGutter: true,
101
+ highlightActiveLine: !readOnly,
102
+ autocompletion: !readOnly,
103
+ bracketMatching: true,
104
+ closeBrackets: !readOnly,
105
+ indentOnInput: !readOnly
106
+ }
107
+ })
108
+ });
109
+ }
110
+
111
+ export { code_editor_field_exports as n, normalizeCodeLanguage as r, CodeEditorField as t };