@burdenoff/fluidgrids-fe-sdk 2026.1112.1 → 2026.1112.3

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 (59) hide show
  1. package/dist/{AiStudioPage-EfvgZS7d.js → AiStudioPage-C-jio5Bz.js} +4 -4
  2. package/dist/{DataSinkDashboardPage-BfdKumMH.js → DataSinkDashboardPage-CRBsNjoS.js} +1 -1
  3. package/dist/{ExecutionDetailPage-Dtpn7Xnl.js → ExecutionDetailPage-Cw4zyaIG.js} +5 -5
  4. package/dist/{ExecutionsListPage-DeqJnEO7.js → ExecutionsListPage-BgeqwVr2.js} +4 -4
  5. package/dist/{FluidGridsProvider-BuZERrTA.js → FluidGridsProvider-BFfTxl9m.js} +2 -6
  6. package/dist/{HistoryPage-BC7ld-oQ.js → HistoryPage-BdijEOHp.js} +2 -2
  7. package/dist/{InsightsPage-C8X1g_Ya.js → InsightsPage-2jnYlozA.js} +1 -1
  8. package/dist/{MissingNodesInstallModal-DUmwDs4e.js → MissingNodesInstallModal-C0wPwR0Q.js} +1 -1
  9. package/dist/NavigationV2Provider-D4kj0Gjt.js +42 -0
  10. package/dist/{QuotaExhaustedDialog-RctNPfz_.js → QuotaExhaustedDialog-BKZ9moP8.js} +1 -1
  11. package/dist/{SettingsPage-BbE4S63W.js → SettingsPage-BaM98QU_.js} +3 -3
  12. package/dist/{TemplateCard-D800haiE.js → TemplateCard-DYDVNYo6.js} +2 -2
  13. package/dist/{TemplateConfigurationModal-DYs1YxYy.js → TemplateConfigurationModal-BzdkGWim.js} +1 -1
  14. package/dist/{TemplateDetailPanel-CKhCQlyo.js → TemplateDetailPanel-s4EqQhra.js} +1 -1
  15. package/dist/{TemplatePreview-C1eRwO8z.js → TemplatePreview-B_-zS_Mm.js} +3 -3
  16. package/dist/{TemplatesPage-BR6iOnC0.js → TemplatesPage-OIMd6cIl.js} +4 -4
  17. package/dist/{VersionDiffDialog-B00qeGiE.js → VersionDiffDialog-Cp2doV3f.js} +1 -1
  18. package/dist/{WorkflowBuilder-rAHCVI8z.js → WorkflowBuilder-B2tIOPi0.js} +4 -4
  19. package/dist/{WorkflowBuilder-DRZ0xJIP.js → WorkflowBuilder-DD4UIAjC.js} +10 -9
  20. package/dist/{WorkflowSearchRedirect-7Kc7NniX.js → WorkflowSearchRedirect-BhXzWDj7.js} +1 -1
  21. package/dist/{WorkflowsList-Bnov6LCT.js → WorkflowsList-BV-2J_gG.js} +138 -10
  22. package/dist/{agentTools-CvPvDlPV.js → agentTools-EVdEThES.js} +4 -4
  23. package/dist/{canvasNoteTools-OaF81_4T.js → canvasNoteTools-C5Vf0pxu.js} +3 -3
  24. package/dist/{code-editor-field-Dg2U8mk1.js → code-editor-field-o7qp9eTq.js} +2 -2
  25. package/dist/{definition-canvas--3MPHoP-.js → definition-canvas-F8DGty5m.js} +1 -1
  26. package/dist/executions.js +2 -2
  27. package/dist/{flow-builder-CDIUMBSO.js → flow-builder-Dn4PCWuf.js} +193 -18
  28. package/dist/history.js +1 -1
  29. package/dist/{index-T8fJI6Ys.d.ts → index-DbTgk7dK.d.ts} +1 -1
  30. package/dist/{index-DjiFgEQl.d.ts → index-f_Nl7Ons.d.ts} +1 -0
  31. package/dist/index.d.ts +4 -3
  32. package/dist/index.js +27 -26
  33. package/dist/navigation-v2.d.ts +3 -0
  34. package/dist/navigation-v2.js +4 -0
  35. package/dist/{node-detail-view-nirlvqEK.js → node-detail-view-kWiwJEKr.js} +6 -6
  36. package/dist/{node-editor-tab-body-Ch0JBF2j.js → node-editor-tab-body-C71Y0fRJ.js} +3 -3
  37. package/dist/{node-operation-selection--oy-yEq_.js → node-operation-selection-icAhaNza.js} +1 -0
  38. package/dist/observability.d.ts +1 -1
  39. package/dist/observability.js +2 -2
  40. package/dist/{popover-B6Rjrhar.js → popover-v7g8TWmm.js} +1 -1
  41. package/dist/{quick-config-panel-B-zC__4A.js → quick-config-panel-C84vsDYD.js} +4 -4
  42. package/dist/{schedule-panel-Bpi6Ev3g.js → schedule-panel-BrNKy9DY.js} +5 -5
  43. package/dist/settings.js +1 -1
  44. package/dist/{sidebar-node-editor-C9drL1rk.js → sidebar-node-editor-D-2lPHwq.js} +4 -4
  45. package/dist/templates.js +2 -2
  46. package/dist/{useDurableParity-DEceXnEa.js → useDurableParity-fFmfa97r.js} +1 -1
  47. package/dist/{useExecutions-D3S4aqIQ.js → useExecutions-DNGW4yxG.js} +1 -1
  48. package/dist/useNavigationV2-DANUmg12.d.ts +9 -0
  49. package/dist/{useObservability-B9cOl5mw.js → useObservability-u-MZCWUV.js} +1 -1
  50. package/dist/{useRunHistory-uTJepemz.js → useRunHistory-x9bVUB9T.js} +1 -1
  51. package/dist/{useWorkflowCodeExport-L-5NIRiH.js → useWorkflowCodeExport-CV3SZeKm.js} +82 -3
  52. package/dist/{useWorkflows-D9BlhpWH.js → useWorkflows-CVb5zXKt.js} +1 -1
  53. package/dist/workflow-builder.d.ts +1 -1
  54. package/dist/workflow-builder.js +5 -5
  55. package/dist/workflow-pages.d.ts +2 -2
  56. package/dist/workflow-pages.js +13 -13
  57. package/dist/workflow-preview.js +2 -2
  58. package/dist/workflows-list.js +1 -1
  59. package/package.json +5 -1
@@ -1,12 +1,12 @@
1
1
  "use client";
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
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-D9BlhpWH.js";
4
- import { At as createThenOpen, t as FluidGridsContext } from "./FluidGridsProvider-BuZERrTA.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-L-5NIRiH.js";
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-CVb5zXKt.js";
4
+ import { At as createThenOpen, t as FluidGridsContext } from "./FluidGridsProvider-BFfTxl9m.js";
5
+ import { A as CreateWorkflowRefusalDialog, D as typeScriptImportSource, O as typeScriptModuleFile, T as readProjectArchive, a as parseWorkflowImport, c as saveWorkflowDescription, d as CODE_EXPORT_FORMATS, f as ImportSourceError, g as checkTypeScriptImport, h as assertImportByteSize, i as downloadWorkflowExport, k as workflowNameFromFile, l as visibleWorkflowDescription, m as MAX_IMPORT_SOURCE_BYTES, n as useExportWorkflowAs, o as commitWorkflowDescription, p as MAX_IMPORT_ARCHIVE_BYTES, u as workflowDescriptionCharactersLeft, v as importAcceptAttribute, w as primaryImportModule, y as importFileKind } from "./useWorkflowCodeExport-CV3SZeKm.js";
6
6
  import { c as isQuotaExhaustedFailure, t as BILLING_PLANS_PATH } from "./quota-Cdt8yz7N.js";
7
- import { H as useFlowStore } from "./node-operation-selection--oy-yEq_.js";
7
+ import { H as useFlowStore } from "./node-operation-selection-icAhaNza.js";
8
8
  import { a as SdkPermissionGate, i as SdkPermissionButton, s as useFluidGridsPermission, t as FLUIDGRIDS_PERMISSIONS } from "./constants-B2lhWNoN.js";
9
- import { t as QuotaExhaustedDialog } from "./QuotaExhaustedDialog-RctNPfz_.js";
9
+ import { t as QuotaExhaustedDialog } from "./QuotaExhaustedDialog-BKZ9moP8.js";
10
10
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
11
11
  import { YF as WorkflowAccessFilter, rI as WorkflowExportFormat } from "./graphql-types-v6CTGwA0.js";
12
12
  import { i as canShareWorkflow, r as canEditWorkflow, t as WorkflowShareDialog } from "./WorkflowShareDialog-Dv46nAyh.js";
@@ -14,7 +14,7 @@ import { i as useOpenRequest, n as ACTION_PARAM_VALUES, r as useActionParam } fr
14
14
  import { l as useFluidGridsFlag, t as FLUIDGRIDS_FLAGS } from "./feature-flags-DM5myWaD.js";
15
15
  import { _ as throwIfResultError, m as describeOperationError, n as getFluidGridsClient } from "./client-C9XSzSXr.js";
16
16
  import { Dt as MoveWorkflowFolderDocument, Fr as WorkflowFoldersDocument, J as DeleteWorkflowProjectDocument, N as CreateWorkflowFolderDocument, P as CreateWorkflowProjectDocument, Wr as WorkflowProjectsDocument, _ as BulkDeleteWorkflowsDocument, en as RenameWorkflowFolderDocument, g as BulkAssignWorkflowsToProjectDocument, q as DeleteWorkflowFolderDocument, tn as RenameWorkflowProjectDocument, v as BulkMoveWorkflowsToFolderDocument } from "./graphql-operation-types-BdU65FNm.js";
17
- import { a as useExecutions } from "./useExecutions-D3S4aqIQ.js";
17
+ import { a as useExecutions } from "./useExecutions-DNGW4yxG.js";
18
18
  import { a as firstWorkflowProgress, i as builderPathFor, r as GUIDED_TOUR_STATE } from "./firstWorkflow-C_h0KMB2.js";
19
19
  import { n as useFluidGridsPageControls } from "./pageControlsBridge-0pX9s-3k.js";
20
20
  import { i as workflowsListControls } from "./pageControlSpecs-DZJ9K37m.js";
@@ -25,7 +25,7 @@ import { Icon } from "@iconify/react";
25
25
  import { cn } from "@burdenoff/fe-libs/utils";
26
26
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
27
27
  import { toast } from "sonner";
28
- import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Badge, Button, Card, Checkbox, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, IllustratedEmptyState, Input, Label, NextSteps, Select, SelectContent, SelectItem, SelectSeparator, SelectTrigger, SelectValue, Skeleton, Textarea, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@burdenoff/fe-libs/ui";
28
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Badge, Button, Card, Checkbox, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, IllustratedEmptyState, Input, Label, NextSteps, ResponsiveDialog, ResponsiveDialogContent, ResponsiveDialogDescription, ResponsiveDialogFooter, ResponsiveDialogHeader, ResponsiveDialogTitle, Select, SelectContent, SelectItem, SelectSeparator, SelectTrigger, SelectValue, Skeleton, Textarea, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@burdenoff/fe-libs/ui";
29
29
  import { v4 } from "uuid";
30
30
  import { TagBadge } from "@burdenoff/fe-libs/tag";
31
31
 
@@ -182,7 +182,7 @@ function relativeTime(iso) {
182
182
  year: "numeric"
183
183
  }).format(new Date(then));
184
184
  }
185
- const CardWorkflow = ({ workflow, onUpdateSuccess, onExport, onExportAs, exportingFormat = null, onShare, onMoveToProject }) => {
185
+ const CardWorkflow = ({ workflow, onUpdateSuccess, onExport, onExportAs, exportingFormat = null, onShare, onMoveToProject, onEditDescription }) => {
186
186
  const tr = useTr();
187
187
  const navigate = useNavigate();
188
188
  const resetWorkflow = useFlowStore((s) => s.actions.resetWorkflow);
@@ -242,6 +242,7 @@ const CardWorkflow = ({ workflow, onUpdateSuccess, onExport, onExportAs, exporti
242
242
  const restricted = workflow.visibility === "RESTRICTED";
243
243
  const editable = canEditWorkflow(workflow.my_access);
244
244
  const shareable = canShareWorkflow(workflow.my_access);
245
+ const description = visibleWorkflowDescription(workflow.description, workflow.name);
245
246
  const updatedExact = workflow.updated_at ? new Date(workflow.updated_at).toLocaleString() : void 0;
246
247
  return /* @__PURE__ */ jsxs(TooltipProvider, { children: [
247
248
  /* @__PURE__ */ jsxs(Card, {
@@ -292,8 +293,10 @@ const CardWorkflow = ({ workflow, onUpdateSuccess, onExport, onExportAs, exporti
292
293
  }), /* @__PURE__ */ jsx(WorkflowStatusPill, { status: workflow.status })]
293
294
  }),
294
295
  /* @__PURE__ */ jsx("p", {
295
- className: "mt-1 line-clamp-2 text-xs leading-snug text-muted-foreground",
296
- children: workflow.description || tr("fluidgrids.builder.cardWorkflow.noDescription", "No description")
296
+ className: "mt-1 truncate text-xs leading-snug text-muted-foreground",
297
+ "data-testid": "workflow-card-description",
298
+ title: description || void 0,
299
+ children: description || tr("fluidgrids.builder.cardWorkflow.noDescription", "No description")
297
300
  }),
298
301
  workflow.tags && workflow.tags.length > 0 && /* @__PURE__ */ jsxs("div", {
299
302
  className: "mt-2 flex flex-wrap items-center gap-1",
@@ -403,6 +406,27 @@ const CardWorkflow = ({ workflow, onUpdateSuccess, onExport, onExportAs, exporti
403
406
  children: /* @__PURE__ */ jsx("p", { children: tr("fluidgrids.builder.cardWorkflow.shareWorkflow", "Share workflow") })
404
407
  })] })
405
408
  }),
409
+ onEditDescription && editable && /* @__PURE__ */ jsx(SdkPermissionGate, {
410
+ permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_UPDATE,
411
+ children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
412
+ asChild: true,
413
+ children: /* @__PURE__ */ jsx(Button, {
414
+ variant: "ghost",
415
+ size: "icon",
416
+ className: "size-8",
417
+ onClick: onEditDescription,
418
+ "data-testid": "workflow-card-edit-description",
419
+ "aria-label": tr("fluidgrids.builder.cardWorkflow.editDescription", "Edit description"),
420
+ children: /* @__PURE__ */ jsx(Icon, {
421
+ icon: "mdi:text-box-edit-outline",
422
+ className: "size-4"
423
+ })
424
+ })
425
+ }), /* @__PURE__ */ jsx(TooltipContent, {
426
+ side: "top",
427
+ children: /* @__PURE__ */ jsx("p", { children: tr("fluidgrids.builder.cardWorkflow.editDescription", "Edit description") })
428
+ })] })
429
+ }),
406
430
  onMoveToProject && editable && /* @__PURE__ */ jsx(SdkPermissionGate, {
407
431
  permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_UPDATE,
408
432
  children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
@@ -1980,6 +2004,99 @@ function WorkflowMoveToProjectDialog({ workflow, onClose, onMoved }) {
1980
2004
  });
1981
2005
  }
1982
2006
 
2007
+ // src/components/WorkflowDescriptionDialog.tsx
2008
+ /**
2009
+ * Edit a workflow's description from its card in the workflow list
2010
+ * (BOFF-9743). The description of the listed version changes in place: no new
2011
+ * version is created. An empty description clears it.
2012
+ */
2013
+ function WorkflowDescriptionDialog({ workflow, onClose, onSaved }) {
2014
+ return /* @__PURE__ */ jsx(ResponsiveDialog, {
2015
+ open: workflow !== null,
2016
+ onOpenChange: (open) => !open && onClose(),
2017
+ children: workflow && /* @__PURE__ */ jsx(DescriptionForm, {
2018
+ workflow,
2019
+ onClose,
2020
+ onSaved
2021
+ }, workflow.id)
2022
+ });
2023
+ }
2024
+ function DescriptionForm({ workflow, onClose, onSaved }) {
2025
+ const tr = useTr();
2026
+ const shown = visibleWorkflowDescription(workflow.description, workflow.name);
2027
+ const [draft, setDraft] = useState(shown);
2028
+ const [saving, setSaving] = useState(false);
2029
+ const decision = commitWorkflowDescription(shown, draft);
2030
+ const left = workflowDescriptionCharactersLeft(draft);
2031
+ const save = async () => {
2032
+ if (decision.kind !== "save") return;
2033
+ setSaving(true);
2034
+ try {
2035
+ const saved = await saveWorkflowDescription(getFluidGridsClient(), workflow.id, decision.description);
2036
+ toast.success(tr("fluidgrids.workflowsList.editDescription.saved", "Description saved"));
2037
+ onSaved?.(saved.description);
2038
+ onClose();
2039
+ } catch (error) {
2040
+ toast.error(tr("fluidgrids.builder.workflowDescription.saveFailed", "The description couldn't be saved"), { description: describeOperationError(error) });
2041
+ } finally {
2042
+ setSaving(false);
2043
+ }
2044
+ };
2045
+ return /* @__PURE__ */ jsxs(ResponsiveDialogContent, {
2046
+ "data-testid": "workflow-description-dialog",
2047
+ children: [
2048
+ /* @__PURE__ */ jsxs(ResponsiveDialogHeader, { children: [/* @__PURE__ */ jsx(ResponsiveDialogTitle, { children: tr("fluidgrids.workflowsList.editDescription.title", "Describe {{name}}", { name: workflow.name }) }), /* @__PURE__ */ jsx(ResponsiveDialogDescription, { children: tr("fluidgrids.workflowsList.editDescription.help", "Shown under the workflow's name. Saving changes this version in place.") })] }),
2049
+ /* @__PURE__ */ jsxs("div", {
2050
+ className: "flex flex-col gap-2",
2051
+ children: [
2052
+ /* @__PURE__ */ jsx(Label, {
2053
+ htmlFor: "workflow-description-input",
2054
+ children: tr("fluidgrids.builder.workflowDescription.label", "Workflow description")
2055
+ }),
2056
+ /* @__PURE__ */ jsx(Textarea, {
2057
+ id: "workflow-description-input",
2058
+ "data-testid": "workflow-description-input",
2059
+ autoFocus: true,
2060
+ rows: 4,
2061
+ maxLength: 500,
2062
+ placeholder: tr("fluidgrids.builder.workflowDescription.placeholder", "What does this workflow do?"),
2063
+ value: draft,
2064
+ onChange: (event) => setDraft(event.target.value),
2065
+ onKeyDown: (event) => {
2066
+ if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
2067
+ event.preventDefault();
2068
+ save();
2069
+ }
2070
+ },
2071
+ className: "resize-none"
2072
+ }),
2073
+ /* @__PURE__ */ jsxs("span", {
2074
+ "data-testid": "workflow-description-counter",
2075
+ className: cn("self-end text-xs tabular-nums text-muted-foreground", left < 0 && "text-destructive"),
2076
+ children: [
2077
+ 500 - left,
2078
+ "/",
2079
+ 500
2080
+ ]
2081
+ })
2082
+ ]
2083
+ }),
2084
+ /* @__PURE__ */ jsxs(ResponsiveDialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
2085
+ variant: "outline",
2086
+ onClick: onClose,
2087
+ disabled: saving,
2088
+ children: tr("fluidgrids.workflowsList.moveToProject.cancel", "Cancel")
2089
+ }), /* @__PURE__ */ jsx(Button, {
2090
+ onClick: () => void save(),
2091
+ disabled: saving || decision.kind !== "save",
2092
+ "aria-busy": saving,
2093
+ "data-testid": "workflow-description-save",
2094
+ children: tr("fluidgrids.workflowsList.editDescription.save", "Save")
2095
+ })] })
2096
+ ]
2097
+ });
2098
+ }
2099
+
1983
2100
  // src/pages/workflowsListUrlState.ts
1984
2101
  /**
1985
2102
  * URL-driven filter state for the workflows list (BOFF-10854).
@@ -2155,6 +2272,7 @@ function WorkflowsList() {
2155
2272
  });
2156
2273
  const [sharing, setSharing] = useState(null);
2157
2274
  const [moving, setMoving] = useState(null);
2275
+ const [describing, setDescribing] = useState(null);
2158
2276
  const filteredByAccess = accessFilter !== "ALL";
2159
2277
  const { getWorkflow } = useLazyWorkflowDefinition();
2160
2278
  const createPermission = useFluidGridsPermission(FLUIDGRIDS_PERMISSIONS.WORKFLOW_CREATE);
@@ -2537,6 +2655,11 @@ function WorkflowsList() {
2537
2655
  name: workflow.name,
2538
2656
  projectId: workflow.projectId ?? null
2539
2657
  }),
2658
+ onEditDescription: () => setDescribing({
2659
+ id: workflow.id,
2660
+ name: workflow.name,
2661
+ description: workflow.description ?? null
2662
+ }),
2540
2663
  onExport: () => void handleExport(workflow.id),
2541
2664
  onExportAs: codeFirst ? (format) => void handleExportAs(workflow.workflowKey, workflow.version, format) : void 0,
2542
2665
  exportingFormat: exportingKey === workflow.workflowKey ? exporting : null
@@ -2558,6 +2681,11 @@ function WorkflowsList() {
2558
2681
  onClose: () => setMoving(null),
2559
2682
  onMoved: () => void refetch()
2560
2683
  }),
2684
+ /* @__PURE__ */ jsx(WorkflowDescriptionDialog, {
2685
+ workflow: describing,
2686
+ onClose: () => setDescribing(null),
2687
+ onSaved: () => void refetch()
2688
+ }),
2561
2689
  /* @__PURE__ */ jsx(CreateWorkflowRefusalDialog, {
2562
2690
  surface: "builder-create",
2563
2691
  refusal: createFirst.refusal,
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { B as createConfiguredNode, Nt as useSaveStatusStore, Q as selectedOperationOf, R as SENSITIVE_PARAM, Y as nodeTitle, Z as paramsFromOperation, _ as registryNodeData, f as isNodeConfigsInitialized, gt as autosaveAllowedFor, o as NODE_CONFIGS, q as fixedParameterState, wt as isFlowComplete } from "./FluidGridsProvider-BuZERrTA.js";
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-D800haiE.js";
5
- import { g as computeAttachPosition } from "./flow-builder-CDIUMBSO.js";
2
+ import { B as createConfiguredNode, Nt as useSaveStatusStore, Q as selectedOperationOf, R as SENSITIVE_PARAM, Y as nodeTitle, Z as paramsFromOperation, _ as registryNodeData, f as isNodeConfigsInitialized, gt as autosaveAllowedFor, o as NODE_CONFIGS, q as fixedParameterState, wt as isFlowComplete } from "./FluidGridsProvider-BFfTxl9m.js";
3
+ import { E as resolveOutputHandles, H as useFlowStore, T as resolveInputHandles } from "./node-operation-selection-icAhaNza.js";
4
+ import { j as isNodeTypeHiddenFromPalette } from "./TemplateCard-DYDVNYo6.js";
5
+ import { g as computeAttachPosition } from "./flow-builder-Dn4PCWuf.js";
6
6
  import { AgentToolError, defineAgentTool } from "@burdenoff/fe-libs/shared/agent-tools";
7
7
  import { z } from "zod";
8
8
 
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { n as useAgentMarksStore, r as toPlainText } from "./marksStore-DGCXjTmZ.js";
3
- import { Q as selectedOperationOf, Y as nodeTitle, Z as paramsFromOperation } from "./FluidGridsProvider-BuZERrTA.js";
4
- import { H as useFlowStore } from "./node-operation-selection--oy-yEq_.js";
5
- import { p as notesKeyOf } from "./flow-builder-CDIUMBSO.js";
3
+ import { Q as selectedOperationOf, Y as nodeTitle, Z as paramsFromOperation } from "./FluidGridsProvider-BFfTxl9m.js";
4
+ import { H as useFlowStore } from "./node-operation-selection-icAhaNza.js";
5
+ import { p as notesKeyOf } from "./flow-builder-Dn4PCWuf.js";
6
6
  import { AgentToolError, defineAgentTool } from "@burdenoff/fe-libs/shared/agent-tools";
7
7
  import { z } from "zod";
8
8
 
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
- import { g as toCodeMirrorDiagnostics } from "./node-operation-selection--oy-yEq_.js";
4
- import { Bn as useBuilderReadOnly } from "./FluidGridsProvider-BuZERrTA.js";
3
+ import { g as toCodeMirrorDiagnostics } from "./node-operation-selection-icAhaNza.js";
4
+ import { Hn as useBuilderReadOnly } from "./FluidGridsProvider-BFfTxl9m.js";
5
5
  import { useEffect, useMemo, useRef, useState } from "react";
6
6
  import { cn } from "@burdenoff/fe-libs/utils";
7
7
  import { jsx } from "react/jsx-runtime";
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { F as mergeStickyNotes } from "./node-operation-selection--oy-yEq_.js";
2
+ import { F as mergeStickyNotes } from "./node-operation-selection-icAhaNza.js";
3
3
 
4
4
  // src/builder/live/definition-canvas.ts
5
5
  function isRecord(value) {
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { t as ExecutionsListPage } from "./ExecutionsListPage-DeqJnEO7.js";
3
- import { t as ExecutionDetailPage } from "./ExecutionDetailPage-Dtpn7Xnl.js";
2
+ import { t as ExecutionsListPage } from "./ExecutionsListPage-BgeqwVr2.js";
3
+ import { t as ExecutionDetailPage } from "./ExecutionDetailPage-Cw4zyaIG.js";
4
4
 
5
5
  export { ExecutionDetailPage, ExecutionsListPage };
@@ -1,10 +1,10 @@
1
1
  "use client";
2
- import { 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, 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 { $t as canAutosave, Bn as useBuilderReadOnly, Bt as phaseAfterAcquire, Ct as watchLeavingLosesChanges, D as InstallWorkflowNodeDocument, E as InstallPurchasedNodeDocument, Et as useSaveWorkflow, F as getNodeMetadata, Fn as getNodeDetail, Ft as useEditLeaseStore, Gt as EDIT_SESSION_ID, H as MissingParametersDialog, Ht as releasesOnClose, In as buildStickyNode, It as bannerOf, Jt as sessionBeforeReload, Kt as forgetSessionForReload, Ln as loadMarkdown, Lt as isLocked, M as UninstallOrganizationPrivateNodeDocument, Mt as saveIndicatorOf, N as UninstallWorkflowNodeDocument, Nt as useSaveStatusStore, Pt as useEditLeaseLocked, Qt as AUTOSAVE_RETRY_DELAYS_MS, Rn as useMarkdownRenderer, Rt as leaseCallQueue, S as CreateStoreOrderDocument, St as touchesSavedFields, T as InstallOrganizationPrivateNodeDocument, Tt as useFlowValidator, U as useRunGuard, Ut as shouldReleaseForIdle, V as useInsertNode, Vt as phaseAfterTakeOver, W as workflowParameterIssues, Wt as tickAction, Xt as rememberRevision, Yt as rememberOwnStatusChange, Zt as AUTOSAVE_DEBOUNCE_MS, _t as departingSave, a as NODES_METADATA, an as workflowFingerprint, b as BrowseWorkflowNodesDocument, bt as leavingLosesChanges, f as isNodeConfigsInitialized, g as registerWorkflowNodeTypes, gt as autosaveAllowedFor, ht as useManualSave, i as AVAILABLE_NODES, in as saveFailureActionOf, jt as refusalOfQuota, k as OrganizationPrivateNodesDocument, l as ensureNodeConfigsLoaded, m as refetchStoreNodes, mt as useGuardedNavigate, nn as inAppLeaveTarget, o as NODE_CONFIGS, on as useNodeExecution, p as onNodeTypesUpdate, qt as rememberSessionForReload, rn as resettledBaseline, s as NODE_TYPES, t as FluidGridsContext, v as scheduleNodePaletteLoad, vn as useCanvasEdges, vt as editStateFor, w as GetNodeInstallableVersionsDocument, wt as isFlowComplete, xn as loadDefinitionJson, xt as savedEverythingBeforeLeaving, yn as useNodeRegistryRevision, yt as editingLeaseLocked, z as buildOperationSeed, zn as BuilderReadOnlyProvider, zt as leaseCallSequence } from "./FluidGridsProvider-BuZERrTA.js";
2
+ import { 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, j as NodeTypeIcon, r as CODE_WORKFLOW_NODE_TYPE, z as NODE_OPERATION_DRAG_DATA_FORMAT } from "./node-operation-selection-icAhaNza.js";
3
+ import { $t as AUTOSAVE_DEBOUNCE_MS, Bn as useMarkdownRenderer, Bt as leaseCallSequence, Cn as loadDefinitionJson, Ct as watchLeavingLosesChanges, D as InstallWorkflowNodeDocument, E as InstallPurchasedNodeDocument, Et as useSaveWorkflow, F as getNodeMetadata, Ft as useEditLeaseLocked, Gt as tickAction, H as MissingParametersDialog, Hn as useBuilderReadOnly, Ht as phaseAfterTakeOver, It as useEditLeaseStore, Jt as rememberSessionForReload, Kt as EDIT_SESSION_ID, Ln as getNodeDetail, Lt as bannerOf, M as UninstallOrganizationPrivateNodeDocument, Mt as saveIndicatorOf, N as UninstallWorkflowNodeDocument, Nt as useSaveStatusStore, Pt as workflowSaveQueue, Qt as revisionOf, Rn as buildStickyNode, Rt as isLocked, S as CreateStoreOrderDocument, St as touchesSavedFields, T as InstallOrganizationPrivateNodeDocument, Tt as useFlowValidator, U as useRunGuard, Ut as releasesOnClose, V as useInsertNode, Vn as BuilderReadOnlyProvider, Vt as phaseAfterAcquire, W as workflowParameterIssues, Wt as shouldReleaseForIdle, Xt as rememberOwnStatusChange, Yt as sessionBeforeReload, Zt as rememberRevision, _t as departingSave, a as NODES_METADATA, an as resettledBaseline, b as BrowseWorkflowNodesDocument, bn as useCanvasEdges, bt as leavingLosesChanges, cn as useNodeExecution, en as AUTOSAVE_RETRY_DELAYS_MS, f as isNodeConfigsInitialized, g as registerWorkflowNodeTypes, gt as autosaveAllowedFor, ht as useManualSave, i as AVAILABLE_NODES, in as inAppLeaveTarget, jt as refusalOfQuota, k as OrganizationPrivateNodesDocument, l as ensureNodeConfigsLoaded, m as refetchStoreNodes, mt as useGuardedNavigate, o as NODE_CONFIGS, on as saveFailureActionOf, p as onNodeTypesUpdate, qt as forgetSessionForReload, s as NODE_TYPES, sn as workflowFingerprint, t as FluidGridsContext, tn as canAutosave, v as scheduleNodePaletteLoad, vt as editStateFor, w as GetNodeInstallableVersionsDocument, wt as isFlowComplete, xn as useNodeRegistryRevision, xt as savedEverythingBeforeLeaving, yt as editingLeaseLocked, z as buildOperationSeed, zn as loadMarkdown, zt as leaseCallQueue } from "./FluidGridsProvider-BFfTxl9m.js";
4
4
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
5
5
  import { n as preloadableLazy } from "./preloadable-lazy-Pp47mqQ9.js";
6
6
  import { a as DialogFooter$1, d as Input$1, i as DialogDescription$1, l as DialogTitle$1, o as DialogHeader$1, r as DialogContent$1, t as Dialog$1 } from "./dialog-Cjb4lB4D.js";
7
- import { _ as storeNodeInstallVersion, a as useTemplateImportFlow, d as filterStoreNodesForPalette, f as getStoreNodeAvailability, g as storeNodeDisplayVersion, h as isStoreNodeUpdateAvailable, k as filterPaletteNodes, m as getStoreNodeType, p as getStoreNodeIcon, t as TemplateCard, u as useWorkflowTemplates, v as useInstalledNodes, y as useGlobalStoreClient } from "./TemplateCard-D800haiE.js";
7
+ import { _ as storeNodeInstallVersion, a as useTemplateImportFlow, d as filterStoreNodesForPalette, f as getStoreNodeAvailability, g as storeNodeDisplayVersion, h as isStoreNodeUpdateAvailable, k as filterPaletteNodes, m as getStoreNodeType, p as getStoreNodeIcon, t as TemplateCard, u as useWorkflowTemplates, v as useInstalledNodes, y as useGlobalStoreClient } from "./TemplateCard-DYDVNYo6.js";
8
8
  import { _ as throwIfResultError, d as emitFluidGridsEvent, m as describeOperationError, n as getFluidGridsClient, s as waitForFluidGridsClient, x as getFluidGridsConfig } from "./client-C9XSzSXr.js";
9
9
  import { c as useDurableParityEnabled, d as usePaletteGates, l as useFluidGridsFlag, m as useWorkflowTestsEnabled, s as useDurableFunctionNodesEnabled, t as FLUIDGRIDS_FLAGS } from "./feature-flags-DM5myWaD.js";
10
10
  import { t as Button$2 } from "./button-DG7y_FVJ.js";
@@ -12,14 +12,14 @@ import { t as PublishCustomNodeAction } from "./PublishCustomNodeAction-5XXnSA3H
12
12
  import { o as emitUpgradeClicked, t as emitNodeInstalled } from "./fluidgridsEvents-Dm5CaHtr.js";
13
13
  import { $t as RemoveNodeFromWorkflowsDocument, Br as WorkflowGitPushesDocument, Ci as WorkflowVersioningDocument, Ft as NodeUsageStatusDocument, Gr as WorkflowPromotionRequestsDocument, Hn as TakeOverWorkflowEditLeaseDocument, Lr as WorkflowGenerationTemplatesDocument, Mn as SetWorkflowRolloutDocument, Mr as WorkflowEditLeaseDocument, Nt as NodeSchemaIntrospectionDocument, Or as WorkflowDefinitionDocument, Pr as WorkflowEnvironmentsDocument, Q as DeployWorkflowVersionToDevDocument, Qt as ReleaseWorkflowEditLeaseDocument, Rr as WorkflowGitCommitsDocument, S as CancelWorkflowPromotionDocument, Si as WorkflowVersionDrainageDocument, Vr as WorkflowGitRepositoryDocument, Xt as RejectWorkflowPromotionDocument, Y as DeleteWorkflowRolloutDocument, Zn as UnlinkWorkflowGitRepositoryDocument, br as WorkflowAuthoringDocument, l as ApproveWorkflowPromotionDocument, lr as UpdateWorkflowEnvironmentPolicyDocument, qt as PushWorkflowToGitDocument, r as ActivateWorkflowVersionCheckedDocument, rn as RequestWorkflowPromotionDocument, sn as RestoreWorkflowFromGitDocument, t as AcquireWorkflowEditLeaseDocument, wt as LinkWorkflowGitRepositoryDocument, zr as WorkflowGitDiffDocument } from "./graphql-operation-types-BdU65FNm.js";
14
14
  import { n as useAgentMarksStore } from "./marksStore-DGCXjTmZ.js";
15
- import { T as CreateWorkflowRefusalDialog, _ as lineOfNode, f as fileBaseName, g as isLossyFidelityItem, h as isLosslessReport, r as useWorkflowCodeExport, t as downloadWorkflowCodeExport, v as orderedExportFiles, x as sortFidelityItems } from "./useWorkflowCodeExport-L-5NIRiH.js";
15
+ import { A as CreateWorkflowRefusalDialog, C as orderedExportFiles, E as sortFidelityItems, S as lineOfNode, _ as fileBaseName, b as isLosslessReport, c as saveWorkflowDescription, l as visibleWorkflowDescription, o as commitWorkflowDescription, r as useWorkflowCodeExport, s as normalizeWorkflowDescription, t as downloadWorkflowCodeExport, u as workflowDescriptionCharactersLeft, x as isLossyFidelityItem } from "./useWorkflowCodeExport-CV3SZeKm.js";
16
16
  import { r as RUNS_QUOTA_NAME, t as BILLING_PLANS_PATH } from "./quota-Cdt8yz7N.js";
17
17
  import { t as EntityTagPicker } from "./EntityTagPicker-Dk3CufFt.js";
18
18
  import { t as FluidGridsDocsLink } from "./FluidGridsDocsLink-JxkBpOeb.js";
19
19
  import { n as runsQuotaLevel, t as readRunsQuota } from "./runsQuota-DvXR6mRN.js";
20
20
  import { a as SdkPermissionGate, i as SdkPermissionButton, s as useFluidGridsPermission, t as FLUIDGRIDS_PERMISSIONS } from "./constants-B2lhWNoN.js";
21
- import { a as useDeactivateWorkflow, d as useRestoreWorkflowVersion, o as useDeleteWorkflow, t as useActivateWorkflow, v as useWorkflowVersions } from "./useWorkflows-D9BlhpWH.js";
22
- import { n as VersionLifecycleBar, r as countLifecycle, t as VersionDiffDialog } from "./VersionDiffDialog-B00qeGiE.js";
21
+ import { a as useDeactivateWorkflow, d as useRestoreWorkflowVersion, o as useDeleteWorkflow, t as useActivateWorkflow, v as useWorkflowVersions } from "./useWorkflows-CVb5zXKt.js";
22
+ import { n as VersionLifecycleBar, r as countLifecycle, t as VersionDiffDialog } from "./VersionDiffDialog-Cp2doV3f.js";
23
23
  import { $P as VersioningBehavior, Eu as DrainageStatus, MI as WorkflowTestExpectedStatus, jo as CodeReplaceStatus, nI as WorkflowEnvironment, rI as WorkflowExportFormat } from "./graphql-types-v6CTGwA0.js";
24
24
  import { n as useConnections } from "./useConnections-Cs7LCWEM.js";
25
25
  import { t as buildNodeDisplayMap } from "./run-display-BycKYSU0.js";
@@ -40,7 +40,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
40
40
  import { toast } from "sonner";
41
41
  import { edgeColor, nodeCategoryColor } from "@burdenoff/fe-libs/shared/utils";
42
42
  import { shallow, useShallow } from "zustand/shallow";
43
- import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Input, Label, Popover, PopoverContent, PopoverTrigger, ScrollArea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster as Toaster$1, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@burdenoff/fe-libs/ui";
43
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Input, Label, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, ScrollArea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster as Toaster$1, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@burdenoff/fe-libs/ui";
44
44
  import { useEventBus } from "@burdenoff/fe-libs/shared/events";
45
45
  import { Button as Button$1 } from "@burdenoff/fe-libs/button";
46
46
  import { ThemeProvider } from "@burdenoff/fe-libs/shared/providers/shell";
@@ -260,9 +260,9 @@ function IntroductionPropertyPanel() {
260
260
  * waiting for its chunk and React's Suspense throttle (about 0.5 s on first
261
261
  * open even with the chunk cached).
262
262
  */
263
- const NodeDetailViewOverlay = preloadableLazy(() => import("./node-detail-view-nirlvqEK.js").then((m) => m.NodeDetailView), "NodeDetailView");
264
- const QuickConfigPanelOverlay = preloadableLazy(() => import("./quick-config-panel-B-zC__4A.js").then((m) => m.QuickConfigPanel), "QuickConfigPanel");
265
- const SidebarNodeEditorOverlay = preloadableLazy(() => import("./sidebar-node-editor-C9drL1rk.js").then((m) => m.SidebarNodeEditor), "SidebarNodeEditor");
263
+ const NodeDetailViewOverlay = preloadableLazy(() => import("./node-detail-view-kWiwJEKr.js").then((m) => m.NodeDetailView), "NodeDetailView");
264
+ const QuickConfigPanelOverlay = preloadableLazy(() => import("./quick-config-panel-C84vsDYD.js").then((m) => m.QuickConfigPanel), "QuickConfigPanel");
265
+ const SidebarNodeEditorOverlay = preloadableLazy(() => import("./sidebar-node-editor-D-2lPHwq.js").then((m) => m.SidebarNodeEditor), "SidebarNodeEditor");
266
266
  const WorkflowShareDialogOverlay = preloadableLazy(() => import("./WorkflowShareDialog-Dv46nAyh.js").then((n) => n.n).then((m) => m.WorkflowShareDialog), "WorkflowShareDialog");
267
267
  const ConnectionFormDialogOverlay = preloadableLazy(() => import("./ConnectionFormDialog-Bg9_OdHX.js").then((n) => n.n).then((m) => m.ConnectionFormDialog), "ConnectionFormDialog");
268
268
  /**
@@ -2624,7 +2624,7 @@ function TemplatesGrid({ templates, loading, hasMore, onLoadMore, onTemplateClic
2624
2624
  }
2625
2625
 
2626
2626
  // src/builder/components/flow-builder/components/templates/TemplatesModal.tsx
2627
- const LazyTemplateDetailPanel = lazy(() => import("./TemplateDetailPanel-CKhCQlyo.js").then((m) => ({ default: m.TemplateDetailPanel })));
2627
+ const LazyTemplateDetailPanel = lazy(() => import("./TemplateDetailPanel-s4EqQhra.js").then((m) => ({ default: m.TemplateDetailPanel })));
2628
2628
  /**
2629
2629
  * Modal for browsing and selecting workflow templates
2630
2630
  * Features:
@@ -16495,7 +16495,7 @@ function FidelityReportList({ fidelity, source, hasNode, onShowNode }) {
16495
16495
  * Behind the `fluidgrids.code-first` flag (default off); with the flag off
16496
16496
  * the panel renders nothing and the header is exactly as before.
16497
16497
  */
16498
- const LazyCodeEditorField = lazy(() => import("./code-editor-field-Dg2U8mk1.js").then((n) => n.n).then((m) => ({ default: m.CodeEditorField })));
16498
+ const LazyCodeEditorField = lazy(() => import("./code-editor-field-o7qp9eTq.js").then((n) => n.n).then((m) => ({ default: m.CodeEditorField })));
16499
16499
  /** Read-only lifted-code editor with a sized placeholder while its chunk loads. */
16500
16500
  function CodeViewEditor({ content, path, label }) {
16501
16501
  return /* @__PURE__ */ jsx(Suspense, {
@@ -16798,11 +16798,185 @@ function ShareWorkflowButton({ compact = false, className }) {
16798
16798
  });
16799
16799
  }
16800
16800
 
16801
+ // src/builder/lib/workflow-description-save.ts
16802
+ /**
16803
+ * Saving the open workflow's description from the builder header (BOFF-9743).
16804
+ *
16805
+ * The description is written on its own, in place, through
16806
+ * `updateWorkflowDefinition`; the canvas save never writes it. The store shows
16807
+ * the new text at once and gets the old one back when the service refuses.
16808
+ *
16809
+ * A description change raises the version's revision (BOFF-9725), so the save
16810
+ * goes through the editor's save queue, never alongside a canvas save that
16811
+ * carries the same revision, and sends that revision itself. Its answer is the
16812
+ * page's new revision: without it the person's next canvas save would be
16813
+ * refused as stale over their own edit.
16814
+ */
16815
+ /** Numbers each save, so the queue never folds one description save into another. */
16816
+ let saveSequence = 0;
16817
+ /**
16818
+ * Save `description` on the open version. Resolves (never rejects) with what
16819
+ * happened; a refusal carries the service's message for the caller's toast.
16820
+ */
16821
+ async function saveOpenWorkflowDescription(description) {
16822
+ const flow = useFlowStore.getState();
16823
+ const dbId = flow.workflow.dbId;
16824
+ if (!dbId) return {
16825
+ saved: false,
16826
+ message: "Save the workflow first"
16827
+ };
16828
+ const generation = flow.loadGeneration;
16829
+ const previous = flow.workflow.description ?? "";
16830
+ const next = normalizeWorkflowDescription(description);
16831
+ const { setWorkflow } = flow.actions;
16832
+ const showsThisSave = () => {
16833
+ const state = useFlowStore.getState();
16834
+ return state.loadGeneration === generation && state.workflow.dbId === dbId && (state.workflow.description ?? "") === next;
16835
+ };
16836
+ setWorkflow({ description: next });
16837
+ let result = {
16838
+ saved: false,
16839
+ message: "The description was not saved"
16840
+ };
16841
+ await workflowSaveQueue.run(async () => {
16842
+ const expectedRevision = revisionOf(dbId);
16843
+ try {
16844
+ const saved = await saveWorkflowDescription(getFluidGridsClient(), dbId, next, {
16845
+ expectedRevision,
16846
+ editSessionId: EDIT_SESSION_ID
16847
+ });
16848
+ if (expectedRevision !== void 0) rememberRevision(saved.id, saved.revision);
16849
+ else rememberOwnStatusChange(saved.id, saved.revision);
16850
+ if (showsThisSave() && saved.description !== next) setWorkflow({ description: saved.description });
16851
+ result = {
16852
+ saved: true,
16853
+ description: saved.description
16854
+ };
16855
+ return true;
16856
+ } catch (error) {
16857
+ if (showsThisSave()) setWorkflow({ description: previous });
16858
+ result = {
16859
+ saved: false,
16860
+ message: describeOperationError(error)
16861
+ };
16862
+ return false;
16863
+ }
16864
+ }, `description:${++saveSequence}`);
16865
+ return result;
16866
+ }
16867
+
16868
+ // src/builder/components/flow-builder/components/ui/workflow-description-field.tsx
16869
+ /**
16870
+ * The workflow's description in the builder header (BOFF-9743), n8n-style:
16871
+ * the text itself is the edit affordance. Clicking it opens a small editor;
16872
+ * Ctrl/Cmd+Enter or leaving the editor saves, Escape cancels.
16873
+ *
16874
+ * The header only renders while this page may change the workflow (not a
16875
+ * read-only view, and not while someone else holds the edit lease, BOFF-9725);
16876
+ * without the update permission the description is shown, not offered for
16877
+ * editing. The service still refuses a save it does not allow (a viewer of a
16878
+ * shared workflow, BOFF-9726), and the old text comes back with a toast.
16879
+ */
16880
+ function WorkflowDescriptionField({ className }) {
16881
+ const tr = useTr();
16882
+ const [description, name, dbId] = useFlowStore(useShallow((s) => [
16883
+ s.workflow.description,
16884
+ s.workflow.name,
16885
+ s.workflow.dbId
16886
+ ]));
16887
+ const { allowed: canEdit } = useFluidGridsPermission(FLUIDGRIDS_PERMISSIONS.WORKFLOW_UPDATE);
16888
+ const [draft, setDraftState] = useState(null);
16889
+ const draftRef = useRef(null);
16890
+ const setDraft = (next) => {
16891
+ draftRef.current = next;
16892
+ setDraftState(next);
16893
+ };
16894
+ const shown = visibleWorkflowDescription(description, name);
16895
+ if (!dbId) return null;
16896
+ if (!canEdit) {
16897
+ if (!shown) return null;
16898
+ return /* @__PURE__ */ jsx("span", {
16899
+ "data-testid": "fluidgrids-workflow-description",
16900
+ title: shown,
16901
+ className: cn("min-w-0 shrink-[6] truncate text-xs text-muted-foreground", className),
16902
+ children: shown
16903
+ });
16904
+ }
16905
+ const editing = draft !== null;
16906
+ const commit = () => {
16907
+ const current = draftRef.current;
16908
+ if (current === null) return;
16909
+ const decision = commitWorkflowDescription(shown, current);
16910
+ setDraft(null);
16911
+ if (decision.kind !== "save") return;
16912
+ saveOpenWorkflowDescription(decision.description).then((result) => {
16913
+ if (result.saved) return;
16914
+ toast.error(tr("fluidgrids.builder.workflowDescription.saveFailed", "The description couldn't be saved"), { description: result.message });
16915
+ });
16916
+ };
16917
+ const placeholder = tr("fluidgrids.builder.workflowDescription.add", "Add description");
16918
+ const left = editing ? workflowDescriptionCharactersLeft(draft) : 0;
16919
+ return /* @__PURE__ */ jsxs(Popover, {
16920
+ open: editing,
16921
+ onOpenChange: (open) => {
16922
+ if (!open) commit();
16923
+ },
16924
+ children: [/* @__PURE__ */ jsx(PopoverAnchor, {
16925
+ asChild: true,
16926
+ children: /* @__PURE__ */ jsx("button", {
16927
+ type: "button",
16928
+ "data-testid": "fluidgrids-workflow-description",
16929
+ title: shown || placeholder,
16930
+ "aria-label": tr("fluidgrids.builder.workflowDescription.label", "Workflow description"),
16931
+ className: cn("-mx-1 min-w-0 shrink-[6] cursor-text truncate rounded px-1 text-left text-xs transition-colors hover:bg-card-foreground/10", shown ? "text-card-foreground/80" : "italic text-muted-foreground", className),
16932
+ onClick: () => setDraft(shown),
16933
+ children: shown || placeholder
16934
+ })
16935
+ }), /* @__PURE__ */ jsxs(PopoverContent, {
16936
+ align: "start",
16937
+ className: "flex w-80 flex-col gap-1.5 p-2",
16938
+ onEscapeKeyDown: (event) => {
16939
+ event.preventDefault();
16940
+ setDraft(null);
16941
+ },
16942
+ children: [/* @__PURE__ */ jsx(Textarea, {
16943
+ autoFocus: true,
16944
+ "data-testid": "fluidgrids-workflow-description-input",
16945
+ "aria-label": tr("fluidgrids.builder.workflowDescription.label", "Workflow description"),
16946
+ placeholder: tr("fluidgrids.builder.workflowDescription.placeholder", "What does this workflow do?"),
16947
+ rows: 3,
16948
+ maxLength: 500,
16949
+ value: draft ?? "",
16950
+ onChange: (event) => setDraft(event.target.value),
16951
+ onKeyDown: (event) => {
16952
+ if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
16953
+ event.preventDefault();
16954
+ commit();
16955
+ }
16956
+ },
16957
+ className: "min-h-20 resize-none text-sm"
16958
+ }), /* @__PURE__ */ jsxs("div", {
16959
+ className: "flex items-center justify-between gap-2 text-[11px] text-muted-foreground",
16960
+ children: [/* @__PURE__ */ jsx("span", { children: tr("fluidgrids.builder.workflowDescription.hint", "Ctrl+Enter to save, Esc to cancel") }), /* @__PURE__ */ jsxs("span", {
16961
+ "data-testid": "fluidgrids-workflow-description-counter",
16962
+ className: cn("tabular-nums", left < 0 && "text-destructive"),
16963
+ children: [
16964
+ 500 - left,
16965
+ "/",
16966
+ 500
16967
+ ]
16968
+ })]
16969
+ })]
16970
+ })]
16971
+ });
16972
+ }
16973
+
16801
16974
  // src/builder/components/flow-builder/components/ui/builder-header.tsx
16802
16975
  /**
16803
- * The workflow editor's header (BOFF-9773): back, the workflow's name and
16804
- * version alias, tags and save state on the left; the actions on the right,
16805
- * laid out by `BuilderHeaderLayout` for the header's own width.
16976
+ * The workflow editor's header (BOFF-9773): back, the workflow's name,
16977
+ * version alias, description (BOFF-9743), tags and save state on the left;
16978
+ * the actions on the right, laid out by `BuilderHeaderLayout` for the
16979
+ * header's own width.
16806
16980
  */
16807
16981
  /** Run button with an attached chevron opening a small menu (currently just "View Runs"). */
16808
16982
  function RunWithViewRuns({ onViewRuns, canViewRuns, className, runButtonClassName }) {
@@ -16906,7 +17080,7 @@ function VersionAliasChip({ value, onChange, iconOnly }) {
16906
17080
  children: label
16907
17081
  });
16908
17082
  }
16909
- /** Back, name, alias, tags and save state. */
17083
+ /** Back, name, description, alias, tags and save state. */
16910
17084
  function HeaderIdentity({ tier }) {
16911
17085
  const tr = useTr();
16912
17086
  const { id } = useParams();
@@ -16955,6 +17129,7 @@ function HeaderIdentity({ tier }) {
16955
17129
  });
16956
17130
  }
16957
17131
  }),
17132
+ /* @__PURE__ */ jsx(WorkflowDescriptionField, { className: tier === "full" ? "max-w-[20rem]" : "max-w-[12rem]" }),
16958
17133
  /* @__PURE__ */ jsx(CurrentVersionAiBadge, { iconOnly: tier === "mid" }),
16959
17134
  id && /* @__PURE__ */ jsx(EntityTagPicker, {
16960
17135
  entityType: "FLUIDGRIDS_WORKFLOW",
@@ -17443,7 +17618,7 @@ const FlowBuilder = ({ readOnly = false }) => {
17443
17618
  paletteGatesRef.current = paletteGates;
17444
17619
  }, [paletteGates]);
17445
17620
  useWarmBuilderOverlays();
17446
- useLazyAgentTools(() => import("./agentTools-CvPvDlPV.js").then(({ createBuilderAgentTools }) => createBuilderAgentTools({ durableFunctionsEnabled: () => paletteGatesRef.current })), !editingBlocked, "Agent tools");
17621
+ useLazyAgentTools(() => import("./agentTools-EVdEThES.js").then(({ createBuilderAgentTools }) => createBuilderAgentTools({ durableFunctionsEnabled: () => paletteGatesRef.current })), !editingBlocked, "Agent tools");
17447
17622
  const [nodes, edges, onNodesChange, onEdgesChange, onConnect] = useFlowStore(useShallow((s) => {
17448
17623
  return [
17449
17624
  s.workflow.nodes,
@@ -17485,7 +17660,7 @@ const FlowBuilder = ({ readOnly = false }) => {
17485
17660
  useEffect(() => {
17486
17661
  fitViewRef.current = fitView;
17487
17662
  }, [fitView]);
17488
- useLazyAgentTools(() => import("./canvasNoteTools-OaF81_4T.js").then(({ createCanvasNoteTools }) => createCanvasNoteTools({ focusNode: (nodeId) => fitViewRef.current({
17663
+ useLazyAgentTools(() => import("./canvasNoteTools-C5Vf0pxu.js").then(({ createCanvasNoteTools }) => createCanvasNoteTools({ focusNode: (nodeId) => fitViewRef.current({
17489
17664
  nodes: [{ id: nodeId }],
17490
17665
  duration: 400,
17491
17666
  padding: .6,
package/dist/history.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use client";
2
- import { t as HistoryPage } from "./HistoryPage-BC7ld-oQ.js";
2
+ import { t as HistoryPage } from "./HistoryPage-BdijEOHp.js";
3
3
 
4
4
  export { HistoryPage };
@@ -1,6 +1,6 @@
1
1
 
2
2
  import { f as FluidGridsTheme, r as FluidGridsRefreshedTokens } from "./index-72XFgvbn.js";
3
- import { R as FluidGridsProviderProps, w as WorkflowData } from "./index-DjiFgEQl.js";
3
+ import { R as FluidGridsProviderProps, w as WorkflowData } from "./index-f_Nl7Ons.js";
4
4
  import { j as Translator } from "./constants-XdvUxeWK.js";
5
5
  import { t as RunsQuota } from "./runsQuota-DsniKiSg.js";
6
6
  import * as React$2 from "react";
@@ -528,6 +528,7 @@ interface IFlowState {
528
528
  name: string;
529
529
  version: string;
530
530
  alias?: string;
531
+ description?: string;
531
532
  dbId?: string;
532
533
  status?: string;
533
534
  codeBuildId?: string;