@oomol-lab/open-flow 0.1.0-beta.62 → 0.1.0-beta.64

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 (55) hide show
  1. package/dist/browser/{createResourceDialog-DZgJsr3e.js → createResourceDialog-BAJXbzNH.js} +20 -7
  2. package/dist/browser/flow-authoring.js +8 -7
  3. package/dist/browser/flow-change-schema.d.ts +1 -0
  4. package/dist/browser/flow-change.d.ts +1 -0
  5. package/dist/browser/flow-change.js +8 -7
  6. package/dist/browser/flow-notifications.d.ts +6 -0
  7. package/dist/browser/{flowChanges-CzYIVevI.js → flowChanges-BAMkSaKF.js} +1771 -1331
  8. package/dist/browser/{flowRunInputEditorStore-Dsxyfwae.js → flowRunInputEditorStore-B2Rw4VsT.js} +1 -1
  9. package/dist/browser/{flowWorkspace-BgZaFKZm.js → flowWorkspace-DxLc1Ybv.js} +429 -205
  10. package/dist/browser/{icons-CajfDpy_.js → icons-Db2Jb2iX.js} +30 -10
  11. package/dist/browser/preview.css +40 -52
  12. package/dist/browser/preview.js +7752 -7452
  13. package/dist/browser/theme.css +4 -0
  14. package/dist/browser/{typeScriptSession-fXPMFGjW.js → typeScriptSession-D0bjYDeg.js} +1 -1
  15. package/dist/browser/ui.css +39 -51
  16. package/dist/browser/ui.js +5 -5
  17. package/dist/browser/{value-DL_poNiC.js → value-BADHeZ9I.js} +12 -1
  18. package/dist/browser/workbench-contract.d.ts +10 -0
  19. package/dist/browser/workbench.css +43 -59
  20. package/dist/browser/workbench.d.ts +5 -3
  21. package/dist/browser/workbench.js +255 -216
  22. package/dist/browser/{workbenchSelect-H9wzEFkH.js → workbenchSelect-C-jf6NIM.js} +1 -1
  23. package/dist/command/index.js +49 -48
  24. package/dist/common/authoringSchema.d.ts +1 -0
  25. package/dist/common/canvas-commands.d.ts +19 -0
  26. package/dist/common/connector-action.d.ts +1 -0
  27. package/dist/common/connector-action.js +15 -12
  28. package/dist/common/control-api-conformance.js +127 -8
  29. package/dist/common/control-api.d.ts +16 -0
  30. package/dist/common/control-api.js +67 -8
  31. package/dist/common/control-requests.d.ts +23 -0
  32. package/dist/common/control-requests.js +64 -19
  33. package/dist/common/decision.js +8 -7
  34. package/dist/common/flow-connectionUsage.d.ts +1 -0
  35. package/dist/common/flow-encoding.d.ts +1 -0
  36. package/dist/common/flow-encoding.js +202 -10
  37. package/dist/common/flow-notifications.d.ts +6 -0
  38. package/dist/common/flow-semantics.js +64 -46
  39. package/dist/common/flow-workflowFile.d.ts +23 -0
  40. package/dist/common/integration-trigger.js +8 -7
  41. package/dist/common/localization.d.ts +1 -0
  42. package/dist/common/localization.js +12 -1
  43. package/dist/common/mcp.d.ts +103 -0
  44. package/dist/common/mcp.js +135 -11
  45. package/dist/common/openapi.js +8 -7
  46. package/dist/common/poll-trigger.js +8 -7
  47. package/dist/common/runtime-contract.js +1 -1
  48. package/dist/common/scheduler.d.ts +22 -2
  49. package/dist/common/scheduler.js +354 -83
  50. package/dist/common/trigger-config.d.ts +2 -0
  51. package/dist/common/webhook-trigger.js +8 -7
  52. package/dist/index.d.ts +2 -2
  53. package/package.json +1 -1
  54. package/skills/open-flow/SKILL.md +1 -1
  55. package/skills/open-flow/references/flow-authoring.md +49 -0
@@ -1,11 +1,11 @@
1
1
  import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } from "./rolldown-runtime-CEgmsyvS.js";
2
- import { $t as DropdownMenuContent, A as datePickerValue, An as zh_TW_default$1, At as followWorkbenchLink, Bn as errorNotice, Bt as VirtualScrollArea, C as valueForEditor, Cn as Badge, D as calendarChange, Dn as createI18n$1, E as typeOfSchema, En as SourceSelect, F as feishuResourceKind, Fn as fr_default$1, Ft as PopoverPanelContent, G as semanticNodeIcon, Gt as Empty, H as resourceData, Ht as InputGroup, I as supportsFeishuChatFilter, In as en_default$1, It as PopoverTitle, J as providerIcon, Jt as EmptyHeader, K as triggerNodeIcon, Kn as resolutionOutputPorts, Kt as EmptyContent, Ln as I18n, Lt as PopoverTrigger, Mn as ru_default$1, Mt as PopoverContent, Nn as ko_default$1, Nt as PopoverDescription, O as calendarDate, On as uiLocales, P as indexAddNodeOptions, Pn as ja_default$1, Pt as PopoverHeader, Q as useIconifyData, Qn as triggerConfigValues, Qt as DropdownMenuCheckboxItem, R as mapSource, Rn as FeishuEventPicker, Rt as NativeScrollArea, S as schemaForEditor, Sn as useMenuRootContext, T as isUnconstrainedSchema, Tn as SourceForm, U as resourceValue, Ut as InputGroupAddon, V as observeResource, Vt as Z$2, W as designerGraph, Wn as decodeResultList, Wt as InputGroupInput, X as IconifyProvider, Xn as isJsonObject, Xt as EmptyTitle, Y as ContentIcon, Yn as triggerOutputDefinitions, Yt as EmptyMedia, Z as useIconifyCollectionLoader, Zn as triggerConfigValue, Zt as DropdownMenu, _n as MenuItem, _r as Checkbox, an as DropdownMenuSeparator, ar as openApiTask, b as editorComponent, bn as useContextMenuRootContext, cn as DropdownMenuSubTrigger, cr as serverAddress, ct as diagnosticNodeId, dn as findRootOwnerId, dt as connectionCatalog, en as DropdownMenuGroup, et as canCancelRun, fn as useCompositeItem, gn as MenuPopup, gr as controlErrorCode, hn as MenuPortal, in as DropdownMenuRadioItem, ir as openApiFieldIssues, j as isDateFormat, jn as zh_CN_default$1, jt as Popover, k as datePickerChange, kn as waitBranchDescription, kt as IdTooltip, ln as DropdownMenuTrigger, lr as servers, lt as schemaMismatchMessage, mn as MenuPositioner, n as agentTool, nn as DropdownMenuLabel, nr as listOperations, nt as revisionView, on as DropdownMenuSub, or as securityOptions, pn as MenuRoot, q as actionSummary, qn as sourceOutputLabel, qt as EmptyDescription, rn as DropdownMenuRadioGroup, rr as object, sn as DropdownMenuSubContent, sr as selectOperation, st as diagnosticMessage, tn as DropdownMenuItem, tt as hasRunFilter, un as isElementDisabled, ur as inputValue, vn as MenuGroup, vr as useAriaLabelledBy, w as getDefaultValue, x as editorGroups, xn as MenuRootContext, y as defaultValueEditorSchema, yn as ContextMenuRootContext, yr as IconThemeContext, zn as EventSourceSetup, zt as ScrollArea } from "./flowChanges-CzYIVevI.js";
2
+ import { $ as useIconifyData, $n as isJsonObject, $t as EmptyTitle, A as datePickerValue, An as createI18n$1, Bn as I18n, Bt as PopoverTrigger, C as valueForEditor, Cn as useContextMenuRootContext, Cr as controlErrorCode, D as calendarChange, E as typeOfSchema, En as Badge, Er as IconThemeContext, F as feishuResourceKind, Fn as ru_default$1, Ft as PopoverContent, G as designerGraph, Gt as InputGroup, H as resourceData, Hn as EventSourceSetup, Ht as ScrollArea, I as supportsFeishuChatFilter, In as ko_default$1, It as PopoverDescription, J as actionSummary, Jt as Empty, K as semanticNodeIcon, Kt as InputGroupAddon, Ln as ja_default$1, Lt as PopoverHeader, Mn as waitBranchDescription, Mt as IdTooltip, Nn as zh_TW_default$1, Nt as followWorkbenchLink, O as calendarDate, On as SourceForm, P as indexAddNodeOptions, Pn as zh_CN_default$1, Pt as Popover, Q as useIconifyCollectionLoader, Qn as triggerOutputDefinitions, Qt as EmptyMedia, R as mapSource, Rn as fr_default$1, Rt as PopoverPanelContent, S as schemaForEditor, Sn as ContextMenuRootContext, T as isUnconstrainedSchema, Tn as useMenuRootContext, Tr as useAriaLabelledBy, U as resourceValue, Un as errorNotice, Ut as VirtualScrollArea, V as observeResource, Vn as FeishuEventPicker, Vt as NativeScrollArea, Wt as Z$2, X as ContentIcon, Xn as sourceOutputLabel, Xt as EmptyDescription, Y as providerIcon, Yn as resolutionOutputPorts, Yt as EmptyContent, Z as IconifyProvider, Zt as EmptyHeader, _n as MenuPositioner, _r as inputValue, an as DropdownMenuLabel, b as editorComponent, bn as MenuItem, cn as DropdownMenuSeparator, dn as DropdownMenuSubTrigger, dr as openApiFieldIssues, dt as schemaMismatchMessage, en as DropdownMenu, et as wrapLayout, fn as DropdownMenuTrigger, fr as openApiTask, gn as MenuRoot, gr as servers, hn as useCompositeItem, hr as serverAddress, in as DropdownMenuItem, it as revisionView, j as isDateFormat, jn as uiLocales, jt as WorkflowExportMenuItem, k as datePickerChange, kn as SourceSelect, ln as DropdownMenuSub, lr as listOperations, lt as diagnosticMessage, mn as findRootOwnerId, mr as selectOperation, n as agentTool, nn as DropdownMenuContent, nt as canCancelRun, on as DropdownMenuRadioGroup, or as triggerConfigValue, pn as isElementDisabled, pr as securityOptions, pt as connectionCatalog, q as triggerNodeIcon, qn as decodeResultList, qt as InputGroupInput, rn as DropdownMenuGroup, rt as hasRunFilter, sn as DropdownMenuRadioItem, sr as triggerConfigValues, tn as DropdownMenuCheckboxItem, un as DropdownMenuSubContent, ur as object, ut as diagnosticNodeId, vn as MenuPortal, w as getDefaultValue, wn as MenuRootContext, wr as Checkbox, x as editorGroups, xn as MenuGroup, y as defaultValueEditorSchema, yn as MenuPopup, zn as en_default$1, zt as PopoverTitle } from "./flowChanges-BAMkSaKF.js";
3
3
  import { a as derive, c as strictEqual, i as compute, l as val, n as attachSetter, r as combine, s as readonlyVal } from "./dist-bO8nvHFH.js";
4
4
  import { c as useLang, i as isString, l as useTranslate, n as isBoolean, o as I18nProvider, r as isPlainObject, s as useI18n, t as isArray$1, u as useVal } from "./dist-BFmpkX4P.js";
5
- import { $r as inputPress, Ai as useButton, At as IndexGuessBehavior, Bi as isElement, Br as transitionStatusMapping, Bt as HORIZONTAL_KEYS, C as FieldDescription, Ci as clsx, Cr as stopEvent$1, Ct as SelectContent, Di as NOOP, Dn as useFloatingRootContext, Dr as getTarget, Dt as SelectTrigger, E as FieldLabel, Ei as EMPTY_OBJECT, Er as contains, Et as SelectSeparator, Fi as useIsoLayoutEffect, Fn as useDismiss, Fr as createLucideIcon, Ft as ARROW_LEFT, Gr as cancelOpen, Gt as isNativeInput, Hr as useValueChanged, Ht as MODIFIER_KEYS, I as matchesSchema, In as useClick, Ir as useTransitionStatus, It as ARROW_RIGHT, Jr as escapeKey, Jt as useAnchoredPopupScrollLock, Kn as DROPDOWN_COLLISION_AVOIDANCE, Kr as clearPress, Kt as scrollIntoViewIfNeeded, L as schemaObject, Li as useStableCallback, Ln as FloatingFocusManager, Lr as useOpenChangeComplete, Lt as ARROW_UP, M as decisionTask, Mr as useLabelableId, Mt as ALL_KEYS, N as defaultDecisionQuestion, Ni as formatErrorMessage, Nr as ChevronDown, Nt as ARROW_DOWN, O as decodeRevisionContent, Or as isAndroid, Ot as SelectValue, P as limitDecisionQuestions, Pi as mergeProps, Pt as ARROW_KEYS, Q as operatorsForType, Qn as useStore$1, Qr as inputClear, Qt as useAnchorPositioning, R as variableInputCompatible, Ri as useRefWithInit, S as Field, Si as cva, Sn as useInteractions, Sr as isListIndexDisabled, St as SelectChevron, T as FieldGroup, Ti as EMPTY_ARRAY$1, Tr as activeElement, Tt as SelectItem, U as nodeNameIssue, Ur as createChangeEventDetails, Ut as VERTICAL_KEYS, Vt as HORIZONTAL_KEYS_WITH_EXTRA_KEYS, Wn as FloatingPortal, Wr as createGenericEventDetails, Wt as VERTICAL_KEYS_WITH_EXTRA_KEYS, X as nodeInputMappings, Xt as getDisabledMountTransitionStyles, Yn as addEventListener, Yt as usePositioner, Z as operandHandle, Zn as Store, Zr as inputChange, Zt as InternalBackdrop, _ as TooltipPositioner, _i as visuallyHiddenInput, _n as selectedValueIncludes, _r as getGridCellIndices, a as DialogDescription, an as pressableTriggerOpenStateMapping, ar as useOnFirstRender, at as webhookSupportsBody, b as TooltipRoot, bi as buttonVariants, br as getMinListIndex, c as DialogTitle, ci as useLabelableContext, cn as useOpenInteractionType, d as TooltipContent, di as useFieldRootContext, dn as stringifyAsLabel, ei as itemPress, en as useDirection, et as unaryOperator, f as TooltipProvider, fi as useBaseUiId, fn as stringifyAsValue, g as TooltipPopup, gi as visuallyHidden, gn as removeItem, gr as getGridCellIndexOfCorner, h as TooltipArrow, hi as fieldValidityMapping, hn as findItemIndex, hr as findNonDisabledListIndex, i as DialogContent, in as popupStateMapping, ir as useValueAsRef, it as webhookMethods, j as decisionQuestionIssues, ji as CompositeRootContext, jr as ownerDocument, jt as useCompositeListItem, k as decisionLimits, ki as useMergedRefs, kr as isFirefox, l as DialogTrigger, li as useFormContext, ln as hasNullItemLabel, lr as useTimeout, m as createTooltipHandle, mi as DEFAULT_FIELD_STATE_ATTRIBUTES, mn as defaultItemEquality, mr as createGridCellMap, n as Dialog, ni as none$1, nn as inertValue, nr as require_builtin_esm_external_require_react, on as triggerOpenStateMapping, or as Label, p as TooltipTrigger, pi as useId$1, pn as compareItemEquality, q as comparisonIssue, qr as closePress, rn as getPseudoElementBounds, rr as createSelector, rt as dequal, si as triggerPress, sn as resolveAriaLabelledBy, sr as Input$1, t as Icon, ti as listNavigation, tn as CompositeList, tr as require_with_selector, tt as valueType, u as Tooltip, ui as useRegisterFieldControl, un as isGroupedItems, v as TooltipPortal, vi as useControlled, vr as getGridNavigatedIndex, vt as string, w as FieldError, wi as useRenderElement, wr as isInteractiveElement, wt as SelectGroup, x as Spinner, xi as cn$1, xn as useListNavigation, xr as isIndexOutOfListBounds, xt as Select$1, y as TooltipTrigger$1, yi as Button$1, yn as useTypeahead, yr as getMaxListIndex, zr as TransitionStatusDataAttributes } from "./icons-CajfDpy_.js";
6
- import { a as renameObjectField, i as objectValue, l as resolveUiLanguage, n as isJsonValue, o as setObjectField, r as jsonDataTypes, s as valueType$1, t as initialValue } from "./value-DL_poNiC.js";
7
- import { n as compile, r as validate } from "./flowRunInputEditorStore-Dsxyfwae.js";
8
- import { t as WorkbenchSelect } from "./workbenchSelect-H9wzEFkH.js";
5
+ import { $ as operandHandle, $n as Store, $r as inputChange, $t as InternalBackdrop, A as decisionLimits, Ar as isAndroid, At as SelectValue, Bi as useRefWithInit, C as FieldDescription, Ci as cn$1, Cn as useListNavigation, Cr as isIndexOutOfListBounds, Ct as Select$1, Di as EMPTY_ARRAY$1, Dr as activeElement, Dt as SelectItem, E as FieldLabel, Ei as useRenderElement, Er as isInteractiveElement, Et as SelectGroup, F as limitDecisionQuestions, Fi as formatErrorMessage, Fr as ChevronDown, Ft as ARROW_DOWN, Gr as createChangeEventDetails, Gt as VERTICAL_KEYS, Hi as isElement, Hr as transitionStatusMapping, Ht as HORIZONTAL_KEYS, Ii as mergeProps, It as ARROW_KEYS, Jn as DROPDOWN_COLLISION_AVOIDANCE, Jr as clearPress, Jt as scrollIntoViewIfNeeded, Kn as FloatingPortal, Kr as createGenericEventDetails, Kt as VERTICAL_KEYS_WITH_EXTRA_KEYS, L as matchesSchema, Li as useIsoLayoutEffect, Ln as useDismiss, Lr as createLucideIcon, Lt as ARROW_LEFT, M as decisionQuestionIssues, Mi as useButton, Mt as IndexGuessBehavior, N as decisionTask, Ni as CompositeRootContext, Nr as ownerDocument, Nt as useCompositeListItem, O as decodeRevisionContent, Oi as EMPTY_OBJECT, Or as contains, Ot as SelectSeparator, P as defaultDecisionQuestion, Pr as useLabelableId, Pt as ALL_KEYS, Q as nodeInputMappings, Qt as getDisabledMountTransitionStyles, R as schemaObject, Rn as useClick, Rr as useTransitionStatus, Rt as ARROW_RIGHT, S as Field, Si as buttonVariants, Sr as getMinListIndex, T as FieldGroup, Ti as clsx, Tr as stopEvent$1, Tt as SelectContent, Ut as HORIZONTAL_KEYS_WITH_EXTRA_KEYS, Vr as TransitionStatusDataAttributes, W as nodeNameIssue, Wr as useValueChanged, Wt as MODIFIER_KEYS, Xr as escapeKey, Xt as useAnchoredPopupScrollLock, Y as comparisonIssue, Yr as closePress, Zn as addEventListener, Zt as usePositioner, _ as TooltipPositioner, _i as fieldValidityMapping, _n as findItemIndex, _r as findNonDisabledListIndex, a as DialogDescription, an as getPseudoElementBounds, ar as createSelector, at as dequal, b as TooltipRoot, bi as useControlled, br as getGridNavigatedIndex, bt as string, c as DialogTitle, cn as triggerOpenStateMapping, cr as Label, d as TooltipContent, di as useFormContext, dn as hasNullItemLabel, dr as useTimeout, dt as discriminatedUnion, ei as inputClear, en as useAnchorPositioning, er as useStore$1, et as operatorsForType, f as TooltipProvider, fi as useRegisterFieldControl, fn as isGroupedItems, g as TooltipPopup, gi as DEFAULT_FIELD_STATE_ATTRIBUTES, gn as defaultItemEquality, gr as createGridCellMap, gt as number, h as TooltipArrow, hi as useId$1, hn as compareItemEquality, ht as literal, i as DialogContent, ii as none$1, in as inertValue, ir as require_builtin_esm_external_require_react, ji as useMergedRefs, jr as isFirefox, ki as NOOP, kn as useFloatingRootContext, kr as getTarget, kt as SelectTrigger, l as DialogTrigger, li as triggerPress, ln as resolveAriaLabelledBy, lr as Input$1, m as createTooltipHandle, mi as useBaseUiId, mn as stringifyAsValue, n as Dialog, ni as itemPress, nn as useDirection, nt as unaryOperator, on as popupStateMapping, or as useValueAsRef, ot as webhookMethods, p as TooltipTrigger, pi as useFieldRootContext, pn as stringifyAsLabel, qr as cancelOpen, qt as isNativeInput, ri as listNavigation, rn as CompositeList, rr as require_with_selector, rt as valueType, sn as pressableTriggerOpenStateMapping, sr as useOnFirstRender, st as webhookSupportsBody, t as Icon, ti as inputPress, u as Tooltip, ui as useLabelableContext, un as useOpenInteractionType, v as TooltipPortal, vi as visuallyHidden, vn as removeItem, vr as getGridCellIndexOfCorner, w as FieldError, wi as cva, wn as useInteractions, wr as isListIndexDisabled, wt as SelectChevron, x as Spinner, xi as Button$1, xn as useTypeahead, xr as getMaxListIndex, y as TooltipTrigger$1, yi as visuallyHiddenInput, yn as selectedValueIncludes, yr as getGridCellIndices, yt as strictObject, z as variableInputCompatible, zi as useStableCallback, zn as FloatingFocusManager, zr as useOpenChangeComplete, zt as ARROW_UP } from "./icons-Db2Jb2iX.js";
6
+ import { a as renameObjectField, i as objectValue, n as isJsonValue, o as setObjectField, r as jsonDataTypes, s as valueType$1, t as initialValue, u as resolveUiLanguage } from "./value-BADHeZ9I.js";
7
+ import { n as compile, r as validate } from "./flowRunInputEditorStore-B2Rw4VsT.js";
8
+ import { t as WorkbenchSelect } from "./workbenchSelect-C-jf6NIM.js";
9
9
  import * as React from "react";
10
10
  import reactExports, { Children, Fragment, Suspense, createContext, forwardRef, isValidElement, lazy, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
11
11
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -360,6 +360,16 @@ function useIgnoredNodes(identity) {
360
360
  }, [])
361
361
  };
362
362
  }
363
+ //#endregion
364
+ //#region src/workbench/browser/runtime/editor/canvasAnnotations.ts
365
+ function canvasAnnotations(flowId, draft, status, moduleStatus, saveFailed) {
366
+ if (flowId == null || draft == null || draft.flowId != flowId || status == "loading" || status == "noDraft") return;
367
+ return {
368
+ flowId,
369
+ revisionId: draft.revisionId,
370
+ syncState: saveFailed || status == "failed" || moduleStatus == "failed" ? "failed" : status == "saving" || moduleStatus == "saving" ? "saving" : moduleStatus == "dirty" ? "dirty" : "saved"
371
+ };
372
+ }
363
373
  Array.isArray;
364
374
  var isNumber = (value) => {
365
375
  try {
@@ -1716,7 +1726,7 @@ function NodePickerContent({ options, connections, loadConnections, browseOption
1716
1726
  const [appId, setAppId] = useState();
1717
1727
  const [navigation, setNavigation] = useState();
1718
1728
  const navigateApp = (id) => {
1719
- if (list.current) scrollPositions.current.set(pickerViewKey(appId, term, page), list.current.scrollTop);
1729
+ if (viewport) scrollPositions.current.set(pickerViewKey(appId, term, page), viewport.scrollTop);
1720
1730
  setNavigation(id ? "forward" : "back");
1721
1731
  setAppId(id);
1722
1732
  setAppQuery("");
@@ -1729,7 +1739,7 @@ function NodePickerContent({ options, connections, loadConnections, browseOption
1729
1739
  const [failed, setFailed] = useState(false);
1730
1740
  const [adding, setAdding] = useState(false);
1731
1741
  const busy = useRef(false);
1732
- const list = useRef(null);
1742
+ const [viewport, setViewport] = useState(null);
1733
1743
  const [root, setRoot] = useState(null);
1734
1744
  const mount = useCallback((element) => {
1735
1745
  setRoot(element?.closest(".open-flow-workbench") ?? element?.closest(".open-flow-theme") ?? null);
@@ -1823,8 +1833,9 @@ function NodePickerContent({ options, connections, loadConnections, browseOption
1823
1833
  t
1824
1834
  ]);
1825
1835
  useLayoutEffect(() => {
1826
- list.current?.scrollTo(0, scrollPositions.current.get(pickerViewKey(appId, term, page)) ?? 0);
1836
+ viewport?.scrollTo(0, scrollPositions.current.get(pickerViewKey(appId, term, page)) ?? 0);
1827
1837
  }, [
1838
+ viewport,
1828
1839
  appId,
1829
1840
  term,
1830
1841
  page,
@@ -1857,9 +1868,9 @@ function NodePickerContent({ options, connections, loadConnections, browseOption
1857
1868
  onClick: () => void add(item),
1858
1869
  onDragStart: (event) => drag(event, item),
1859
1870
  onDragEnd,
1860
- className: `group/app h-auto justify-start whitespace-normal font-normal flex min-w-0 items-start gap-2.5 rounded-lg px-2.5 py-1.5 text-left outline-none hover:bg-accent focus-visible:bg-accent focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 ${compact ? "" : "[&>span:first-child]:translate-y-[2px]"}`,
1871
+ className: `group/app h-auto justify-start whitespace-normal font-normal flex min-w-0 items-start gap-2.5 rounded-[calc(var(--ui-control-radius)+2px)] px-2.5 py-1.5 text-left outline-none hover:bg-accent focus-visible:bg-accent focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 ${compact ? "" : "[&>span:first-child]:translate-y-[2px]"}`,
1861
1872
  children: [item.kind == "connector" || item.kind == "trigger" && "trigger" in item && (item.trigger.kind == "catalog" || item.trigger.kind == "connect") ? /* @__PURE__ */ jsx(ProviderAppIcon, { src: item.icon }) : /* @__PURE__ */ jsx("span", {
1862
- className: "flex size-7 shrink-0 items-center justify-center rounded-md bg-muted",
1873
+ className: "flex size-7 shrink-0 items-center justify-center rounded-[var(--ui-control-radius)] bg-muted",
1863
1874
  children: /* @__PURE__ */ jsx(ContentIcon, {
1864
1875
  src: item.icon,
1865
1876
  className: "size-[18px] data-[icon-kind=initials]:text-[20px]"
@@ -1897,7 +1908,6 @@ function NodePickerContent({ options, connections, loadConnections, browseOption
1897
1908
  };
1898
1909
  const scrollToGroup = (event) => {
1899
1910
  const section = event.currentTarget.closest("section");
1900
- const viewport = list.current;
1901
1911
  if (section && viewport) viewport.scrollTo({
1902
1912
  top: viewport.scrollTop + section.getBoundingClientRect().top - viewport.getBoundingClientRect().top,
1903
1913
  behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "instant" : "smooth"
@@ -1933,7 +1943,7 @@ function NodePickerContent({ options, connections, loadConnections, browseOption
1933
1943
  type: "button",
1934
1944
  disabled: disabled || adding,
1935
1945
  onClick: () => navigateApp(item.id),
1936
- className: "group/app h-auto justify-start whitespace-normal font-normal flex min-w-0 items-center gap-2.5 rounded-lg px-2.5 py-1.5 text-left text-[13px] outline-none hover:bg-accent focus-visible:bg-accent focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50",
1946
+ className: "group/app h-auto justify-start whitespace-normal font-normal flex min-w-0 items-center gap-2.5 rounded-[calc(var(--ui-control-radius)+2px)] px-2.5 py-1.5 text-left text-[13px] outline-none hover:bg-accent focus-visible:bg-accent focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50",
1937
1947
  children: [
1938
1948
  /* @__PURE__ */ jsx(ProviderAppIcon, { src: item.icon }),
1939
1949
  /* @__PURE__ */ jsxs("span", {
@@ -1957,7 +1967,7 @@ function NodePickerContent({ options, connections, loadConnections, browseOption
1957
1967
  return /* @__PURE__ */ jsxs(Tabs, {
1958
1968
  value: page,
1959
1969
  onValueChange: (value) => setPage(value === "triggers" ? "triggers" : "nodes"),
1960
- className: "h-full min-h-0 gap-0",
1970
+ className: "open-flow-node-picker h-full min-h-0 gap-0",
1961
1971
  "aria-busy": adding,
1962
1972
  children: [
1963
1973
  /* @__PURE__ */ jsx("div", {
@@ -1978,7 +1988,7 @@ function NodePickerContent({ options, connections, loadConnections, browseOption
1978
1988
  onKeyDown: (event) => {
1979
1989
  if (event.key == "ArrowDown") {
1980
1990
  event.preventDefault();
1981
- list.current?.querySelector("button:not(:disabled)")?.focus();
1991
+ viewport?.querySelector("button:not(:disabled)")?.focus();
1982
1992
  }
1983
1993
  }
1984
1994
  }),
@@ -2002,14 +2012,14 @@ function NodePickerContent({ options, connections, loadConnections, browseOption
2002
2012
  children: /* @__PURE__ */ jsxs(TabsList, {
2003
2013
  "aria-label": t("designer.addNode"),
2004
2014
  variant: "flat",
2005
- className: "w-full",
2015
+ className: "w-full rounded-[var(--ui-control-radius)]",
2006
2016
  children: [/* @__PURE__ */ jsx(TabsTrigger, {
2007
2017
  value: "nodes",
2008
- className: "px-3",
2018
+ className: "rounded-[calc(var(--ui-control-radius)-3px)] px-3",
2009
2019
  children: t("addNode.blocks")
2010
2020
  }), /* @__PURE__ */ jsx(TabsTrigger, {
2011
2021
  value: "triggers",
2012
- className: "px-3",
2022
+ className: "rounded-[calc(var(--ui-control-radius)-3px)] px-3",
2013
2023
  children: t("addNode.triggers")
2014
2024
  })]
2015
2025
  })
@@ -2021,79 +2031,88 @@ function NodePickerContent({ options, connections, loadConnections, browseOption
2021
2031
  className: navigation_page_module_default.page,
2022
2032
  "data-navigation": navigation,
2023
2033
  children: [app != null && /* @__PURE__ */ jsxs("div", {
2024
- className: "mx-3 mb-2 grid h-8 shrink-0 grid-cols-[28px_minmax(0,1fr)_28px] items-center gap-2 rounded-lg bg-[color-mix(in_srgb,var(--ui-foreground)_4%,var(--ui-popover))] px-1.5",
2025
- children: [/* @__PURE__ */ jsx(Button$1, {
2026
- size: "icon-sm",
2027
- variant: "ghost",
2028
- className: "text-muted-foreground hover:bg-popover hover:text-foreground hover:shadow-sm focus-visible:bg-popover focus-visible:text-foreground dark:hover:bg-[color-mix(in_srgb,var(--ui-foreground)_12%,var(--ui-popover))] dark:focus-visible:bg-[color-mix(in_srgb,var(--ui-foreground)_12%,var(--ui-popover))]",
2029
- "aria-label": t(query.trim() ? "nodePicker.backToResults" : "nodePicker.back"),
2030
- onClick: () => navigateApp(),
2034
+ className: "mx-3 mb-2 grid h-8 shrink-0 grid-cols-[28px_minmax(0,1fr)_28px] items-center gap-2 rounded-[var(--ui-control-radius)] bg-[color-mix(in_srgb,var(--ui-foreground)_4%,var(--ui-popover))] px-1.5",
2035
+ children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
2036
+ render: /* @__PURE__ */ jsx(Button$1, {
2037
+ size: "icon-sm",
2038
+ variant: "ghost",
2039
+ className: "text-muted-foreground [--ui-control-hover-background:color-mix(in_srgb,var(--ui-foreground)_8%,var(--ui-popover))]",
2040
+ "aria-label": t(query.trim() ? "nodePicker.backToResults" : "nodePicker.back"),
2041
+ onClick: () => navigateApp()
2042
+ }),
2031
2043
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-left" })
2032
- }), /* @__PURE__ */ jsx("span", {
2044
+ }), /* @__PURE__ */ jsx(TooltipContent, {
2045
+ container: root,
2046
+ children: t(query.trim() ? "nodePicker.backToResults" : "nodePicker.back")
2047
+ })] }), /* @__PURE__ */ jsx("span", {
2033
2048
  className: "min-w-0 truncate text-center text-sm font-semibold",
2034
2049
  title: app.label,
2035
2050
  children: app.label
2036
2051
  })]
2037
- }), /* @__PURE__ */ jsxs("div", {
2038
- ref: list,
2039
- className: "min-h-0 flex-1 overflow-y-scroll overscroll-contain px-2 pb-2",
2040
- onKeyDown: (event) => {
2041
- if (event.key != "ArrowDown" && event.key != "ArrowUp") return;
2042
- const buttons = [...event.currentTarget.querySelectorAll("button:not(:disabled)")];
2043
- const index = buttons.indexOf(event.target);
2044
- if (index < 0) return;
2045
- event.preventDefault();
2046
- buttons[(index + (event.key == "ArrowDown" ? 1 : -1) + buttons.length) % buttons.length]?.focus();
2047
- },
2048
- children: [app != null ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
2049
- section(t("addNode.triggers"), visibleTriggers),
2050
- actionSections(visibleActions),
2051
- !choicesLoading && !choicesFailed && !catalogFailed && visibleActions.length == 0 && visibleTriggers.length == 0 && /* @__PURE__ */ jsx(PickerStatus, {})
2052
- ] }) : searching ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
2053
- matchedApps.length > 0 && /* @__PURE__ */ jsxs("section", {
2054
- className: "mb-3",
2055
- children: [/* @__PURE__ */ jsx("h3", {
2056
- style: { margin: 0 },
2057
- className: "sticky top-0 z-10 bg-popover px-2.5 pb-1 pt-2 text-xs font-medium text-muted-foreground",
2058
- children: /* @__PURE__ */ jsx(PickerGroupButton, {
2059
- onClick: scrollToGroup,
2060
- children: t("nodePicker.apps")
2061
- })
2062
- }), matchedApps.length == 1 ? /* @__PURE__ */ jsx("div", {
2063
- className: "grid",
2064
- children: matchedApps.map((item) => appRow(item, true))
2065
- }) : /* @__PURE__ */ jsx(AppDirectory, {
2066
- viewport: list.current,
2067
- children: matchedApps.map((item) => appRow(item))
2068
- })]
2069
- }),
2070
- section(t("addNode.triggers"), matches.filter((item) => item.kind == "trigger")),
2071
- section(t("addNode.blocks"), builtInNodeItems(matches)),
2072
- section(humanInTheLoop, humanInTheLoopItems(matches)),
2073
- actionSections(matches),
2074
- !loading && !catalogLoading && !failed && !catalogError && !catalogFailed && matchedApps.length == 0 && matches.length == 0 && /* @__PURE__ */ jsx(PickerStatus, {})
2075
- ] }) : page == "triggers" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [section(t("nodePicker.builtIn"), local.filter((item) => item.kind == "trigger")), apps.filter((item) => item.triggers.length > 0).map((item) => /* @__PURE__ */ jsx("div", { children: section(item.label, item.triggers, false, item.label) }, item.id))] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
2076
- section(t("nodePicker.builtInNodes"), builtInNodeItems(local), true),
2077
- section(humanInTheLoop, humanInTheLoopItems(local), true),
2078
- pickerAppGroups.map((group, priority) => {
2079
- const items = apps.filter((item) => item.directory != null && (item.priority ?? 3) == priority);
2080
- if (items.length == 0) return null;
2081
- return /* @__PURE__ */ jsxs("section", {
2052
+ }), /* @__PURE__ */ jsx(ScrollArea, {
2053
+ className: "min-h-0 flex-1 [&_[data-overlayscrollbars-viewport]]:overscroll-contain",
2054
+ defer: false,
2055
+ events: { initialized: (instance) => setViewport(instance.elements().viewport) },
2056
+ children: /* @__PURE__ */ jsxs("div", {
2057
+ className: "px-3 pb-2",
2058
+ onKeyDown: (event) => {
2059
+ if (event.key != "ArrowDown" && event.key != "ArrowUp") return;
2060
+ const buttons = [...event.currentTarget.querySelectorAll("button:not(:disabled)")];
2061
+ const index = buttons.indexOf(event.target);
2062
+ if (index < 0) return;
2063
+ event.preventDefault();
2064
+ buttons[(index + (event.key == "ArrowDown" ? 1 : -1) + buttons.length) % buttons.length]?.focus();
2065
+ },
2066
+ children: [app != null ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
2067
+ section(t("addNode.triggers"), visibleTriggers),
2068
+ actionSections(visibleActions),
2069
+ !choicesLoading && !choicesFailed && !catalogFailed && visibleActions.length == 0 && visibleTriggers.length == 0 && /* @__PURE__ */ jsx(PickerStatus, {})
2070
+ ] }) : searching ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
2071
+ matchedApps.length > 0 && /* @__PURE__ */ jsxs("section", {
2082
2072
  className: "mb-3",
2083
- children: [/* @__PURE__ */ jsx(ProviderGroupHeading, {
2084
- group,
2085
- container: root,
2086
- onClick: scrollToGroup
2087
- }), /* @__PURE__ */ jsx(AppDirectory, {
2088
- viewport: list.current,
2089
- children: items.map((item) => appRow(item))
2073
+ children: [/* @__PURE__ */ jsx("h3", {
2074
+ style: { margin: 0 },
2075
+ className: "sticky top-0 z-10 bg-popover px-2.5 pb-1 pt-2 text-xs font-medium text-muted-foreground",
2076
+ children: /* @__PURE__ */ jsx(PickerGroupButton, {
2077
+ onClick: scrollToGroup,
2078
+ children: t("nodePicker.apps")
2079
+ })
2080
+ }), matchedApps.length == 1 ? /* @__PURE__ */ jsx("div", {
2081
+ className: "grid",
2082
+ children: matchedApps.map((item) => appRow(item, true))
2083
+ }) : /* @__PURE__ */ jsx(AppDirectory, {
2084
+ viewport,
2085
+ children: matchedApps.map((item) => appRow(item))
2090
2086
  })]
2091
- }, group);
2092
- })
2093
- ] }), (app != null ? choicesLoading : catalogLoading || loading) && /* @__PURE__ */ jsx(PickerStatus, {
2094
- loading: true,
2095
- searching
2096
- })]
2087
+ }),
2088
+ section(t("addNode.triggers"), matches.filter((item) => item.kind == "trigger")),
2089
+ section(t("addNode.blocks"), builtInNodeItems(matches)),
2090
+ section(humanInTheLoop, humanInTheLoopItems(matches)),
2091
+ actionSections(matches),
2092
+ !loading && !catalogLoading && !failed && !catalogError && !catalogFailed && matchedApps.length == 0 && matches.length == 0 && /* @__PURE__ */ jsx(PickerStatus, {})
2093
+ ] }) : page == "triggers" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [section(t("nodePicker.builtIn"), local.filter((item) => item.kind == "trigger")), apps.filter((item) => item.triggers.length > 0).map((item) => /* @__PURE__ */ jsx("div", { children: section(item.label, item.triggers, false, item.label) }, item.id))] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
2094
+ section(t("nodePicker.builtInNodes"), builtInNodeItems(local), true),
2095
+ section(humanInTheLoop, humanInTheLoopItems(local), true),
2096
+ pickerAppGroups.map((group, priority) => {
2097
+ const items = apps.filter((item) => item.directory != null && (item.priority ?? 3) == priority);
2098
+ if (items.length == 0) return null;
2099
+ return /* @__PURE__ */ jsxs("section", {
2100
+ className: "mb-3",
2101
+ children: [/* @__PURE__ */ jsx(ProviderGroupHeading, {
2102
+ group,
2103
+ container: root,
2104
+ onClick: scrollToGroup
2105
+ }), /* @__PURE__ */ jsx(AppDirectory, {
2106
+ viewport,
2107
+ children: items.map((item) => appRow(item))
2108
+ })]
2109
+ }, group);
2110
+ })
2111
+ ] }), (app != null ? choicesLoading : catalogLoading || loading) && /* @__PURE__ */ jsx(PickerStatus, {
2112
+ loading: true,
2113
+ searching
2114
+ })]
2115
+ })
2097
2116
  })]
2098
2117
  }, appId ?? "catalog")
2099
2118
  })
@@ -22060,7 +22079,7 @@ function CodeEditor({ ariaDescribedBy, ariaLabel, diagnostics, disabled, errorLa
22060
22079
  darkMode.current = darkMode$;
22061
22080
  setFailed(false);
22062
22081
  setLoading(true);
22063
- const extension = import("./typeScriptSession-fXPMFGjW.js").then(({ loadTypeScriptExtension }) => loadTypeScriptExtension(uri, typingRef.current, () => prepareCompletionRef.current?.())).catch(() => void 0);
22082
+ const extension = import("./typeScriptSession-D0bjYDeg.js").then(({ loadTypeScriptExtension }) => loadTypeScriptExtension(uri, typingRef.current, () => prepareCompletionRef.current?.())).catch(() => void 0);
22064
22083
  claimEditor(uri).then((nextRelease) => {
22065
22084
  release = nextRelease;
22066
22085
  if (disposed) {
@@ -22147,7 +22166,7 @@ function CodeEditor({ ariaDescribedBy, ariaLabel, diagnostics, disabled, errorLa
22147
22166
  editor.current?.updateOptions({ ariaDescribedBy });
22148
22167
  }, [ariaDescribedBy]);
22149
22168
  useEffect(() => {
22150
- import("./typeScriptSession-fXPMFGjW.js").then(({ updateTypeScriptTyping }) => updateTypeScriptTyping(uri, typing)).catch(noop$1);
22169
+ import("./typeScriptSession-D0bjYDeg.js").then(({ updateTypeScriptTyping }) => updateTypeScriptTyping(uri, typing)).catch(noop$1);
22151
22170
  }, [typing, uri]);
22152
22171
  useEffect(() => {
22153
22172
  if (location != null) editor.current?.revealPosition?.(location.line, location.column);
@@ -35094,6 +35113,12 @@ function useInspectorPanel({ identity, preferences, selectedNodeIds, onSelectNod
35094
35113
  };
35095
35114
  }
35096
35115
  //#endregion
35116
+ //#region src/canvas/browser/base/compare.ts
35117
+ var isSameXYPosition = (p1, p2) => !p1 || !p2 ? p1 === p2 : p1.x === p2.x && p1.y === p2.y;
35118
+ var isSize = (s) => isPlainObject(s) && isNumber(s.width) && isNumber(s.height);
35119
+ var isSameSize = (s1, s2) => !s1 || !s2 ? s1 === s2 : s1.width === s2.width && s1.height === s2.height;
35120
+ var isSameViewport = (v1, v2) => !v1 || !v2 ? v1 === v2 : v1.x === v2.x && v1.y === v2.y && v1.zoom === v2.zoom;
35121
+ //#endregion
35097
35122
  //#region src/canvas/browser/stores/node/constants.ts
35098
35123
  var NODE_TYPE = /* @__PURE__ */ function(NODE_TYPE) {
35099
35124
  NODE_TYPE["TaskNode"] = "task_node";
@@ -35113,23 +35138,11 @@ var DEFAULT_POSITION = /* @__PURE__ */ Object.freeze({
35113
35138
  y: 0
35114
35139
  });
35115
35140
  //#endregion
35116
- //#region src/canvas/browser/base/compare.ts
35117
- var isSameXYPosition = (p1, p2) => !p1 || !p2 ? p1 === p2 : p1.x === p2.x && p1.y === p2.y;
35118
- var isSize = (s) => isPlainObject(s) && isNumber(s.width) && isNumber(s.height);
35119
- var isSameSize = (s1, s2) => !s1 || !s2 ? s1 === s2 : s1.width === s2.width && s1.height === s2.height;
35120
- var isSameViewport = (v1, v2) => !v1 || !v2 ? v1 === v2 : v1.x === v2.x && v1.y === v2.y && v1.zoom === v2.zoom;
35121
- //#endregion
35122
35141
  //#region src/canvas/browser/base/rfHelpers.ts
35123
35142
  /** Converts a manifest node identifier to a React Flow identifier. */
35124
35143
  function toRFNodeId(nodeId, nodeType) {
35125
35144
  return (nodeType === NODE_TYPE.CommentNode ? "c:" : "m:") + nodeId;
35126
35145
  }
35127
- var RF_NODE_TYPE = /* @__PURE__ */ function(RF_NODE_TYPE) {
35128
- RF_NODE_TYPE["CommentNode"] = "comment_node";
35129
- /** Node generated from manifest */
35130
- RF_NODE_TYPE["ManifestNode"] = "manifest_node";
35131
- return RF_NODE_TYPE;
35132
- }({});
35133
35146
  function getRFNodeType(rfNodeId) {
35134
35147
  if (rfNodeId[1] === ":") switch (rfNodeId[0]) {
35135
35148
  case "m": return "manifest_node";
@@ -38051,17 +38064,64 @@ var createRFCommand = (nodes) => new RFCommandImpl(nodes);
38051
38064
  * registered instance.
38052
38065
  */
38053
38066
  var RFCommandImpl = class {
38067
+ get remoteViewportChange() {
38068
+ return this.remoteMoves > 0;
38069
+ }
38070
+ async executeRemote(command) {
38071
+ const executions = [];
38072
+ for (const [rf, registration] of this.rfs) {
38073
+ if (!rf.viewportInitialized) continue;
38074
+ const run = async () => {
38075
+ if (this.rfs.get(rf) !== registration) return;
38076
+ this.remoteMoves++;
38077
+ try {
38078
+ switch (command.type) {
38079
+ case "focus": {
38080
+ const node = this.nodes.get(command.nodeId)?.$.rfNode.value;
38081
+ if (node != null) await focusNodes(this.nodes.size, rf, [node], { duration: 0 });
38082
+ break;
38083
+ }
38084
+ case "zoom":
38085
+ await rf.zoomTo(command.zoom, { duration: 0 });
38086
+ break;
38087
+ case "pan": {
38088
+ const viewport = rf.getViewport();
38089
+ await rf.setViewport({
38090
+ ...viewport,
38091
+ x: viewport.x + command.dx,
38092
+ y: viewport.y + command.dy
38093
+ }, { duration: 0 });
38094
+ break;
38095
+ }
38096
+ case "fit": if (rf.getNodes().length > 0) await rf.fitView({
38097
+ padding: .15,
38098
+ maxZoom: 1,
38099
+ duration: 0
38100
+ });
38101
+ }
38102
+ } finally {
38103
+ this.remoteMoves--;
38104
+ }
38105
+ };
38106
+ const next = (registration.pending ?? Promise.resolve()).then(run, run);
38107
+ registration.pending = next;
38108
+ executions.push(next);
38109
+ }
38110
+ await Promise.all(executions);
38111
+ }
38054
38112
  send(method, ...args) {
38055
- for (const rf of this.rfs) this[method](rf, ...args);
38113
+ for (const rf of this.rfs.keys()) this[method](rf, ...args);
38056
38114
  }
38057
38115
  constructor(nodes) {
38058
38116
  this.nodes = nodes;
38059
38117
  this.dispose = disposableStore();
38060
- this.rfs = /* @__PURE__ */ new Set();
38118
+ this.rfs = /* @__PURE__ */ new Map();
38119
+ this.remoteMoves = 0;
38061
38120
  this.onRFInstance = (rf) => {
38062
- this.rfs.add(rf);
38121
+ const registration = {};
38122
+ this.rfs.set(rf, registration);
38063
38123
  return () => {
38064
- this.rfs.delete(rf);
38124
+ if (this.rfs.get(rf) === registration) this.rfs.delete(rf);
38065
38125
  };
38066
38126
  };
38067
38127
  this.dispose.add(() => {
@@ -38085,7 +38145,7 @@ function getCenter(rfNode) {
38085
38145
  }
38086
38146
  function focusNodes(nodesCount, rf, rfNodes, options) {
38087
38147
  if (!rfNodes.length) return;
38088
- if (rfNodes.length > 1) rf.fitBounds(rf.getNodesBounds(rfNodes), {
38148
+ if (rfNodes.length > 1) return rf.fitBounds(rf.getNodesBounds(rfNodes), {
38089
38149
  duration: 150,
38090
38150
  padding: .15,
38091
38151
  ...options
@@ -38094,7 +38154,7 @@ function focusNodes(nodesCount, rf, rfNodes, options) {
38094
38154
  const rfNode = rfNodes[0];
38095
38155
  const center = getCenter(rfNode);
38096
38156
  const zoom = getNodeMinimap(nodesCount, rf.getZoom()) === NodeMiniMapPhase.None ? rf.getZoom() : 1;
38097
- rf.setCenter(center.x, center.y, {
38157
+ return rf.setCenter(center.x, center.y, {
38098
38158
  zoom,
38099
38159
  ...options
38100
38160
  });
@@ -38346,19 +38406,19 @@ var CanvasStore = class {
38346
38406
  };
38347
38407
  }
38348
38408
  doRelayout() {
38409
+ const nodes = this.allLayoutNodes();
38349
38410
  const g = new ie.Graph().setDefaultEdgeLabel(() => ({}));
38350
38411
  g.setGraph({
38351
38412
  rankdir: "LR",
38352
38413
  ranksep: 80
38353
38414
  });
38354
38415
  const singles = /* @__PURE__ */ new Set();
38355
- for (const rfNode of this.$.rfNodes.value) {
38356
- if (getRFNodeType(rfNode.id) === RF_NODE_TYPE.CommentNode) continue;
38357
- singles.add(rfNode.id);
38358
- g.setNode(rfNode.id, {
38359
- ...rfNode,
38360
- width: rfNode.measured?.width ?? 0,
38361
- height: rfNode.measured?.height ?? 0
38416
+ for (const node of nodes) {
38417
+ const { id, measured } = node.$.rfNode.value;
38418
+ singles.add(id);
38419
+ g.setNode(id, {
38420
+ width: measured?.width ?? 0,
38421
+ height: measured?.height ?? 0
38362
38422
  });
38363
38423
  }
38364
38424
  for (const rfEdge of this.$.rfEdges.value) {
@@ -38368,16 +38428,18 @@ var CanvasStore = class {
38368
38428
  }
38369
38429
  if (singles.size > 0) for (const list of cluster([...singles], 10)) for (let i = 0; i < list.length - 1; i++) g.setEdge(list[i], list[i + 1]);
38370
38430
  Oe(g);
38371
- const updateNodePosition = (node) => {
38372
- const { id, measured } = node.$.rfNode.value;
38373
- const { x, y } = g.node(id);
38374
- node.$$.position.set({
38375
- x: x - (measured?.width ?? 0) / 2,
38376
- y: y - (measured?.height ?? 0) / 2
38377
- });
38378
- };
38379
- this.$.nodes.forEach(updateNodePosition);
38380
- this.#callbacks.onMoveNodes(Object.fromEntries(this.allLayoutNodes().map((node) => [node.nodeId, node.$.position.value])));
38431
+ const positions = wrapLayout(nodes.map((node) => {
38432
+ const { x, y, width, height, rank } = g.node(node.rfNodeId);
38433
+ return {
38434
+ id: node.nodeId,
38435
+ column: rank,
38436
+ x: x - width / 2,
38437
+ y: y - height / 2,
38438
+ height
38439
+ };
38440
+ }));
38441
+ for (const node of nodes) node.$$.position.set(positions[node.nodeId]);
38442
+ this.#callbacks.onMoveNodes(positions);
38381
38443
  }
38382
38444
  doRemoveNodes(nodes) {
38383
38445
  if (this.disposed || !nodes?.size) return;
@@ -38479,6 +38541,75 @@ function useNodeMiniMapPhase() {
38479
38541
  return useContext(NodeMiniMapContext);
38480
38542
  }
38481
38543
  //#endregion
38544
+ //#region src/canvas/browser/graph/annotations.tsx
38545
+ var CanvasAnnotationContext = createContext(void 0);
38546
+ var metadataStringLimit = 256;
38547
+ var metadataByteLimit = 2048;
38548
+ var encoder = new TextEncoder();
38549
+ function validIdentity(value) {
38550
+ return value.length > 0 && value.length <= metadataStringLimit;
38551
+ }
38552
+ function serializeMetadata(metadata) {
38553
+ const text = JSON.stringify(metadata);
38554
+ return encoder.encode(text).length <= metadataByteLimit ? text : void 0;
38555
+ }
38556
+ function excerpt(value, limit) {
38557
+ if (value.length <= limit) return value;
38558
+ if (limit <= 1) return "";
38559
+ let end = limit - 1;
38560
+ const last = value.charCodeAt(end - 1);
38561
+ if (last >= 55296 && last <= 56319) end--;
38562
+ return `${value.slice(0, end)}…`;
38563
+ }
38564
+ /** Keep identities intact; shorten display text to the annotation protocol's limits. */
38565
+ function nodeAnnotationMetadata(context, node) {
38566
+ if (![
38567
+ context.flowId,
38568
+ context.revisionId,
38569
+ node.id
38570
+ ].every(validIdentity)) return;
38571
+ const metadata = {
38572
+ description: node.description?.trim() ? excerpt(node.description, metadataStringLimit) : void 0,
38573
+ flowId: context.flowId,
38574
+ revisionId: context.revisionId,
38575
+ nodeId: node.id,
38576
+ syncState: context.syncState
38577
+ };
38578
+ let text = serializeMetadata(metadata);
38579
+ while (text == null) {
38580
+ if (!metadata.description) return;
38581
+ metadata.description = excerpt(metadata.description, metadata.description.length - 1) || void 0;
38582
+ text = serializeMetadata(metadata);
38583
+ }
38584
+ return text;
38585
+ }
38586
+ /** Browser annotation fields are scalar values, so the complete ID list is encoded as a JSON string. */
38587
+ function selectionAnnotationMetadata(context, nodeIds, tooLarge) {
38588
+ if (nodeIds.length < 2 || ![context.flowId, context.revisionId].every(validIdentity) || nodeIds.some((id) => !id)) return;
38589
+ const identities = {
38590
+ flowId: context.flowId,
38591
+ revisionId: context.revisionId,
38592
+ syncState: context.syncState
38593
+ };
38594
+ const ids = JSON.stringify(nodeIds);
38595
+ return (ids.length <= metadataStringLimit ? serializeMetadata({
38596
+ ...identities,
38597
+ nodeIds: ids
38598
+ }) : void 0) ?? serializeMetadata({
38599
+ ...identities,
38600
+ nodeCount: nodeIds.length,
38601
+ error: excerpt(tooLarge, metadataStringLimit)
38602
+ });
38603
+ }
38604
+ function useNodeAnnotation(node) {
38605
+ const context = useContext(CanvasAnnotationContext);
38606
+ if (context == null || node == null || node.kind == "comment") return {};
38607
+ return {
38608
+ "oai-annotatable": node.title,
38609
+ "oai-annotation-metadata": nodeAnnotationMetadata(context, node)
38610
+ };
38611
+ }
38612
+ //#endregion
38482
38613
  //#region src/canvas/browser/graph/CanvasStoreContext.tsx
38483
38614
  var CanvasDarkContext = createContext(false);
38484
38615
  var CanvasStoreContext = createContext(null);
@@ -49434,7 +49565,7 @@ function connectionPortIndex(node, handleId) {
49434
49565
  if (!handle) return 0;
49435
49566
  return handles.filter((item) => item.position === handle.position && item.y < handle.y).length;
49436
49567
  }
49437
- /** A return loop belongs to its output: fixed side offsets and a lane below that card. */
49568
+ /** Return connections use fixed side offsets and a lane below the output or above a lower row. */
49438
49569
  function getConnectionPath({ sourceBounds, targetBounds, sourcePortIndex = 0, targetPortIndex = 0, ...params }) {
49439
49570
  const { sourceX, sourceY, targetX, targetY, sourcePosition = Position.Bottom, targetPosition = Position.Top } = params;
49440
49571
  const reverse = sourcePosition === Position.Left && targetPosition === Position.Right;
@@ -49452,11 +49583,13 @@ function getConnectionPath({ sourceBounds, targetBounds, sourcePortIndex = 0, ta
49452
49583
  const clearance = 40 + Math.max(sourcePortIndex, targetPortIndex) * 12;
49453
49584
  const radius = 16;
49454
49585
  const outputBounds = reverse ? targetBounds : sourceBounds;
49586
+ const inputBounds = reverse ? sourceBounds : targetBounds;
49455
49587
  const rightY = reverse ? targetY : sourceY;
49456
49588
  const leftY = reverse ? sourceY : targetY;
49457
49589
  const right = rightX + clearance;
49458
49590
  const left = leftX - clearance;
49459
- const laneY = Math.max(rightY, outputBounds ? outputBounds.y + outputBounds.height : rightY) + clearance;
49591
+ const outputLane = Math.max(rightY, outputBounds ? outputBounds.y + outputBounds.height : rightY) + clearance;
49592
+ const laneY = Math.max(outputLane, inputBounds ? inputBounds.y - clearance : outputLane);
49460
49593
  const points = [
49461
49594
  {
49462
49595
  x: rightX,
@@ -51459,6 +51592,7 @@ function ConditionRule({ item, t }) {
51459
51592
  var NodeLayout = /* @__PURE__ */ memo(({ canvasStore, nodeStore, visible }) => {
51460
51593
  const t = useTranslate();
51461
51594
  const modelNode = useVal(NodeStore.to(nodeStore)?.content$);
51595
+ const annotation = useNodeAnnotation(modelNode);
51462
51596
  const branches = modelNode?.kind == "condition" || modelNode?.kind == "approval" || modelNode?.kind == "wait" ? modelNode.outputs.flatMap((port) => "handle" in port ? [port.handle] : []) : void 0;
51463
51597
  const executionInput = modelNode != null && modelNode.kind != "trigger" && modelNode.kind != "value";
51464
51598
  const editable = useVal(canvasStore.$.editable);
@@ -51550,6 +51684,7 @@ var NodeLayout = /* @__PURE__ */ memo(({ canvasStore, nodeStore, visible }) => {
51550
51684
  children: /* @__PURE__ */ jsx(NodeStoreContext.Provider, {
51551
51685
  value: nodeStore,
51552
51686
  children: /* @__PURE__ */ jsx("div", {
51687
+ ...annotation,
51553
51688
  className: clsx(NodeLayout_module_default.outerContainer, animateEntry && NodeLayout_module_default.enter, !initialized && "open-flow-canvas-fitting-view", nodeMiniMapPhase >= NodeMiniMapPhase.Phase1 && "open-flow-canvas-node-minimap1", nodeMiniMapPhase >= NodeMiniMapPhase.Phase2 && "open-flow-canvas-node-minimap2"),
51554
51689
  children: /* @__PURE__ */ jsxs("div", {
51555
51690
  className: clsx(NodeLayout_module_default.offsetContainer, skip && NodeLayout_module_default.skipOuter),
@@ -51799,6 +51934,25 @@ var ReactFlowContainer_module_default = {
51799
51934
  viewSeparator: "ReactFlowContainer_viewSeparator_ebe22"
51800
51935
  };
51801
51936
  //#endregion
51937
+ //#region src/canvas/common/commands.ts
51938
+ var canvasMinZoom = .1;
51939
+ discriminatedUnion("type", [
51940
+ strictObject({
51941
+ type: literal("focus"),
51942
+ nodeId: string().min(1)
51943
+ }),
51944
+ strictObject({
51945
+ type: literal("zoom"),
51946
+ zoom: number().min(canvasMinZoom).max(3)
51947
+ }),
51948
+ strictObject({
51949
+ type: literal("pan"),
51950
+ dx: number(),
51951
+ dy: number()
51952
+ }),
51953
+ strictObject({ type: literal("fit") })
51954
+ ]);
51955
+ //#endregion
51802
51956
  //#region src/canvas/browser/base/dragNDrop.ts
51803
51957
  var TRANSFER_SHARED_BLOCK_KEY = "application/oomol-designer/sharedBlock";
51804
51958
  var TRANSFER_SCRIPTLET_KEY = "application/oomol-designer/scriptlet";
@@ -52397,6 +52551,34 @@ function HelperLines({ horizontal, vertical, strokeStyle = "#b7654d" }) {
52397
52551
  });
52398
52552
  }
52399
52553
  //#endregion
52554
+ //#region src/canvas/browser/graph/ReactFlowContainer/selectionAnnotation.tsx
52555
+ /** React Flow owns the selection rectangle and exposes no prop for its custom attributes. */
52556
+ function SelectionAnnotation({ nodes }) {
52557
+ const context = useContext(CanvasAnnotationContext);
52558
+ const root = useStore((state) => state.domNode);
52559
+ const active = useStore((state) => state.nodesSelectionActive && !state.userSelectionActive);
52560
+ const t = useTranslate();
52561
+ const label = t("annotation.selectedNodes", { count: nodes.length });
52562
+ const metadata = context != null && nodes.every((node) => NodeStore.is(node.data.store)) ? selectionAnnotationMetadata(context, nodes.map((node) => node.data.store.nodeId), t("annotation.selectionTooLarge")) : void 0;
52563
+ useLayoutEffect(() => {
52564
+ if (!active || metadata == null) return;
52565
+ const target = root?.querySelector(".react-flow__nodesselection-rect");
52566
+ if (target == null) return;
52567
+ target.setAttribute("oai-annotatable", label);
52568
+ target.setAttribute("oai-annotation-metadata", metadata);
52569
+ return () => {
52570
+ target.removeAttribute("oai-annotatable");
52571
+ target.removeAttribute("oai-annotation-metadata");
52572
+ };
52573
+ }, [
52574
+ root,
52575
+ active,
52576
+ label,
52577
+ metadata
52578
+ ]);
52579
+ return null;
52580
+ }
52581
+ //#endregion
52400
52582
  //#region src/canvas/browser/graph/ReactFlowContainer/ReactFlowContainer.tsx
52401
52583
  var PAN_ON_DRAG_MOUSE = [0];
52402
52584
  var PAN_ON_DRAG_TOUCHPAD = [1];
@@ -52509,6 +52691,7 @@ var FlowControls = /*#__PURE__*/ memo((props) => {
52509
52691
  ] });
52510
52692
  });
52511
52693
  var ReactFlowContainerInner = (props) => {
52694
+ const annotationContext = useContext(CanvasAnnotationContext);
52512
52695
  const flowState = useStoreApi();
52513
52696
  const selecting = useRef(false);
52514
52697
  const selectionEnd = useRef(props.onSelectionEnd);
@@ -52794,6 +52977,7 @@ var ReactFlowContainerInner = (props) => {
52794
52977
  children: /* @__PURE__ */ jsx(PaneRectContext.Provider, {
52795
52978
  value: paneRect$,
52796
52979
  children: /* @__PURE__ */ jsxs(index, {
52980
+ "oai-annotation-container": annotationContext == null ? void 0 : "",
52797
52981
  onKeyDown: (event) => {
52798
52982
  if ((event.key === "Enter" || event.key === " ") && event.target instanceof Element && event.target.matches(".react-flow__node")) props.onActivateSelection?.();
52799
52983
  if (!editable || event.defaultPrevented || event.nativeEvent.isComposing) return;
@@ -52837,7 +53021,7 @@ var ReactFlowContainerInner = (props) => {
52837
53021
  viewport,
52838
53022
  onViewportChange,
52839
53023
  maxZoom: 3,
52840
- minZoom: .1,
53024
+ minZoom: canvasMinZoom,
52841
53025
  nodesDraggable: props.nodesDraggable ?? props.editable,
52842
53026
  nodesConnectable: editable && props.onConnect != null,
52843
53027
  onEdgeContextMenu: (event, edge) => (event.preventDefault(), setEdgeContextMenu({
@@ -52864,6 +53048,7 @@ var ReactFlowContainerInner = (props) => {
52864
53048
  onConnect: editable ? props.onConnect : void 0,
52865
53049
  onDragOver: editable && (props.onAddNode != null || props.onDropAddItem != null) ? handleDragOver : void 0,
52866
53050
  onDrop: editable && (props.onAddNode != null || props.onDropAddItem != null) ? onDrop : void 0,
53051
+ onMove: props.onMove,
52867
53052
  onMoveEnd: props.onMoveEnd,
52868
53053
  onNodeDragStop: props.onNodeDragStop,
52869
53054
  onSelectionChange: props.onSelectionChange,
@@ -52896,6 +53081,7 @@ var ReactFlowContainerInner = (props) => {
52896
53081
  connectionLineComponent: ConnectionLine,
52897
53082
  "aria-readonly": !editable,
52898
53083
  children: [
53084
+ /* @__PURE__ */ jsx(SelectionAnnotation, { nodes: selectedNodes }),
52899
53085
  (props.canDeleteNodes ?? true) && !selectionInProgress && /* @__PURE__ */ jsx(SelectionFloatBar, {
52900
53086
  editable: !!editable,
52901
53087
  nodes: selectedNodes,
@@ -53117,67 +53303,71 @@ function tooShort(from, to) {
53117
53303
  }
53118
53304
  //#endregion
53119
53305
  //#region src/canvas/browser/graph/FlowCanvas/FlowCanvas.tsx
53120
- var FlowCanvas = ({ flowCanvasStore, onActivateSelection, onSelectionStart, onSelectionEnd, onInspectSelection, inspectorOpen = false, onRequestAddNode, cornerTools, cornerLeading, topLeftTools, bottomRightTools, toolbar, dark, dottedBackground, fitView, nodesDraggable, layoutMotion, className, addNodeRequest, addItemRequest, onMoveEnd, onNodeDragStop, onSelectionChange, isValidConnection, onDropAddItem }) => {
53306
+ var FlowCanvas = ({ flowCanvasStore, annotationContext, onActivateSelection, onSelectionStart, onSelectionEnd, onInspectSelection, inspectorOpen = false, onRequestAddNode, cornerTools, cornerLeading, topLeftTools, bottomRightTools, toolbar, dark, dottedBackground, fitView, nodesDraggable, layoutMotion, className, addNodeRequest, addItemRequest, onMove, onMoveEnd, onNodeDragStop, onSelectionChange, isValidConnection, onDropAddItem }) => {
53121
53307
  const editable = useVal(flowCanvasStore.$.editable);
53122
53308
  const nodeMiniMapPhase = useVal(flowCanvasStore.$.nodeMiniMapPhase);
53123
53309
  useEffect(flowCanvasStore.setupForceDelete, []);
53124
- return /* @__PURE__ */ jsx(InspectSelectionContext.Provider, {
53125
- value: {
53126
- onInspect: onInspectSelection,
53127
- expanded: inspectorOpen
53128
- },
53129
- children: /* @__PURE__ */ jsx(CanvasStoreProvider, {
53130
- value: flowCanvasStore,
53131
- dark,
53132
- children: /* @__PURE__ */ jsx(NodeMiniMapProvider, {
53133
- value: nodeMiniMapPhase,
53134
- children: /* @__PURE__ */ jsx(ReactFlowContainer, {
53135
- cornerTools,
53136
- cornerLeading,
53137
- topLeftTools,
53138
- bottomRightTools,
53139
- toolbar,
53140
- editable,
53141
- nodesDraggable: nodesDraggable ?? editable,
53142
- className,
53143
- i18n: flowCanvasStore.i18n,
53144
- dark,
53145
- dottedBackground: dottedBackground ?? editable,
53146
- fitView: fitView ?? !editable,
53147
- fitViewOptions,
53148
- layoutMotion,
53149
- nodeTypes: NODE_TYPES,
53150
- edgeTypes: EDGE_TYPES,
53151
- miniMapExpanded$: flowCanvasStore.$$.miniMapExpanded,
53152
- interactiveMode$: flowCanvasStore.$$.interactiveMode,
53153
- nodes$: flowCanvasStore.$.rfNodes,
53154
- edges$: flowCanvasStore.$.rfEdges,
53155
- viewport$: flowCanvasStore.$$.viewport,
53156
- onRequestAddNode,
53157
- addNodeRequest,
53158
- addItemRequest,
53159
- onAddNode: flowCanvasStore.onAddNode,
53160
- onBeforeDelete: flowCanvasStore.onBeforeDelete,
53161
- onNodesChange: flowCanvasStore.handleNodesChange,
53162
- onEdgesChange: flowCanvasStore.handleEdgesChange,
53163
- onConnect: flowCanvasStore.onRFConnect,
53164
- onMoveEnd,
53165
- onNodeDragStop,
53166
- onSelectionChange,
53167
- onActivateSelection,
53168
- onInspectSelection,
53169
- onSelectionStart,
53170
- onSelectionEnd,
53171
- isValidConnection,
53172
- onDropAddItem,
53173
- onRelayout: editable ? flowCanvasStore.onRelayout : void 0,
53174
- onLayoutMeasured: flowCanvasStore.completeLayout,
53175
- onInstance: flowCanvasStore.rfCommand.onRFInstance,
53176
- onInit: flowCanvasStore.onInit,
53177
- onCopy: flowCanvasStore.onCopy,
53178
- onPaste: flowCanvasStore.onPaste,
53179
- waitNode: flowCanvasStore.waitNode,
53180
- duplicateNodes: flowCanvasStore.duplicateNodes
53310
+ return /* @__PURE__ */ jsx(CanvasAnnotationContext.Provider, {
53311
+ value: annotationContext,
53312
+ children: /* @__PURE__ */ jsx(InspectSelectionContext.Provider, {
53313
+ value: {
53314
+ onInspect: onInspectSelection,
53315
+ expanded: inspectorOpen
53316
+ },
53317
+ children: /* @__PURE__ */ jsx(CanvasStoreProvider, {
53318
+ value: flowCanvasStore,
53319
+ dark,
53320
+ children: /* @__PURE__ */ jsx(NodeMiniMapProvider, {
53321
+ value: nodeMiniMapPhase,
53322
+ children: /* @__PURE__ */ jsx(ReactFlowContainer, {
53323
+ cornerTools,
53324
+ cornerLeading,
53325
+ topLeftTools,
53326
+ bottomRightTools,
53327
+ toolbar,
53328
+ editable,
53329
+ nodesDraggable: nodesDraggable ?? editable,
53330
+ className,
53331
+ i18n: flowCanvasStore.i18n,
53332
+ dark,
53333
+ dottedBackground: dottedBackground ?? editable,
53334
+ fitView: fitView ?? !editable,
53335
+ fitViewOptions,
53336
+ layoutMotion,
53337
+ nodeTypes: NODE_TYPES,
53338
+ edgeTypes: EDGE_TYPES,
53339
+ miniMapExpanded$: flowCanvasStore.$$.miniMapExpanded,
53340
+ interactiveMode$: flowCanvasStore.$$.interactiveMode,
53341
+ nodes$: flowCanvasStore.$.rfNodes,
53342
+ edges$: flowCanvasStore.$.rfEdges,
53343
+ viewport$: flowCanvasStore.$$.viewport,
53344
+ onRequestAddNode,
53345
+ addNodeRequest,
53346
+ addItemRequest,
53347
+ onAddNode: flowCanvasStore.onAddNode,
53348
+ onBeforeDelete: flowCanvasStore.onBeforeDelete,
53349
+ onNodesChange: flowCanvasStore.handleNodesChange,
53350
+ onEdgesChange: flowCanvasStore.handleEdgesChange,
53351
+ onConnect: flowCanvasStore.onRFConnect,
53352
+ onMove,
53353
+ onMoveEnd,
53354
+ onNodeDragStop,
53355
+ onSelectionChange,
53356
+ onActivateSelection,
53357
+ onInspectSelection,
53358
+ onSelectionStart,
53359
+ onSelectionEnd,
53360
+ isValidConnection,
53361
+ onDropAddItem,
53362
+ onRelayout: editable ? flowCanvasStore.onRelayout : void 0,
53363
+ onLayoutMeasured: flowCanvasStore.completeLayout,
53364
+ onInstance: flowCanvasStore.rfCommand.onRFInstance,
53365
+ onInit: flowCanvasStore.onInit,
53366
+ onCopy: flowCanvasStore.onCopy,
53367
+ onPaste: flowCanvasStore.onPaste,
53368
+ waitNode: flowCanvasStore.waitNode,
53369
+ duplicateNodes: flowCanvasStore.duplicateNodes
53370
+ })
53181
53371
  })
53182
53372
  })
53183
53373
  })
@@ -53206,7 +53396,21 @@ function FlowCanvasView(props) {
53206
53396
  const propsRef = useRef(props);
53207
53397
  const selectedEdge = useRef();
53208
53398
  propsRef.current = props;
53209
- const onMoveEnd = useCallback((_, viewport) => propsRef.current.onMoveViewport(viewport), [store]);
53399
+ const lastMove = useRef();
53400
+ const onMove = useCallback((_, viewport) => {
53401
+ lastMove.current = {
53402
+ viewport,
53403
+ remote: store.rfCommand.remoteViewportChange
53404
+ };
53405
+ }, [store]);
53406
+ const onMoveEnd = useCallback((_, viewport) => {
53407
+ const move = lastMove.current;
53408
+ if (move != null && (move.remote || !isSameViewport(move.viewport, viewport))) return;
53409
+ propsRef.current.onMoveViewport(viewport);
53410
+ }, [store]);
53411
+ useEffect(() => props.subscribeCanvasCommands?.((command) => {
53412
+ store.rfCommand.executeRemote(command).catch((error) => console.error("Failed to execute canvas command.", error));
53413
+ }), [store, props.subscribeCanvasCommands]);
53210
53414
  const onNodeDragStop = useCallback((_, node, nodes) => {
53211
53415
  const moved = nodes.length > 0 ? nodes : [node];
53212
53416
  propsRef.current.onMoveNodes(Object.fromEntries(moved.flatMap((item) => {
@@ -53264,6 +53468,7 @@ function FlowCanvasView(props) {
53264
53468
  }, [store, props.focusNodeRequest]);
53265
53469
  const selectionIds = () => store.$.rfNodes.value.flatMap((node) => node.selected && node.data?.store ? [node.data.store.nodeId] : []);
53266
53470
  return /* @__PURE__ */ jsx(FlowCanvas, {
53471
+ annotationContext: props.annotationContext,
53267
53472
  cornerTools: props.cornerTools,
53268
53473
  cornerLeading: props.cornerLeading,
53269
53474
  topLeftTools: props.topLeftTools,
@@ -53280,6 +53485,7 @@ function FlowCanvasView(props) {
53280
53485
  flowCanvasStore: store,
53281
53486
  isValidConnection,
53282
53487
  layoutMotion: props.layoutMotion,
53488
+ onMove,
53283
53489
  onMoveEnd,
53284
53490
  onNodeDragStop,
53285
53491
  onDropAddItem,
@@ -53391,7 +53597,7 @@ function focusPanel(event) {
53391
53597
  if (element instanceof Element && element.closest("a, button, input, label, select, textarea, [contenteditable=\"true\"]")) return;
53392
53598
  event.currentTarget.focus({ preventScroll: true });
53393
53599
  }
53394
- var WorkbenchCanvas = forwardRef(function WorkbenchCanvas({ nodePicker, addNodeControl, addNodeOptions, history, disabled, interactiveMode$, focusNodeRequest, inspectorOpen, model, onAddNode, onConnect, onChangeNodeContentHidden, onChangeComment, onCopy, onDeleteEdge, onDeleteNodes, onDuplicate, onMoveNodes, onMoveViewport, onPaste, provideAddNodeOptions, onSelectNodes, onActivateSelection, onSelectionStart, onSelectionEnd, onToggleInspector, ignoredNodeIds, onIgnoreNodes, runControl, cornerLeading, connectionControl, topLeftTools, bottomRightTools, selectedNodeIds, theme }, ref) {
53600
+ var WorkbenchCanvas = forwardRef(function WorkbenchCanvas({ nodePicker, annotationContext, addNodeControl, addNodeOptions, history, disabled, interactiveMode$, focusNodeRequest, inspectorOpen, model, onAddNode, onConnect, onChangeNodeContentHidden, onChangeComment, onCopy, onDeleteEdge, onDeleteNodes, onDuplicate, onMoveNodes, onMoveViewport, subscribeCanvasCommands, onPaste, provideAddNodeOptions, onSelectNodes, onActivateSelection, onSelectionStart, onSelectionEnd, onToggleInspector, ignoredNodeIds, onIgnoreNodes, runControl, cornerLeading, connectionControl, topLeftTools, bottomRightTools, selectedNodeIds, theme }, ref) {
53395
53601
  const [pickerRequest, setPickerRequest] = useState();
53396
53602
  const [pickerCentered, setPickerCentered] = useState(false);
53397
53603
  useEffect(() => {
@@ -53532,6 +53738,7 @@ var WorkbenchCanvas = forwardRef(function WorkbenchCanvas({ nodePicker, addNodeC
53532
53738
  tabIndex: 0,
53533
53739
  children: [
53534
53740
  /* @__PURE__ */ jsx(FlowCanvasView, {
53741
+ annotationContext,
53535
53742
  onRequestAddNode: (request) => {
53536
53743
  if (addNodeRequest == null) setPickerCentered(false);
53537
53744
  setPickerRequest(request);
@@ -53599,6 +53806,7 @@ var WorkbenchCanvas = forwardRef(function WorkbenchCanvas({ nodePicker, addNodeC
53599
53806
  },
53600
53807
  onMoveNodes,
53601
53808
  onMoveViewport,
53809
+ subscribeCanvasCommands,
53602
53810
  onCopy: (nodeIds) => {
53603
53811
  onSelectNodes(nodeIds);
53604
53812
  onCopy();
@@ -57397,7 +57605,7 @@ var blockedReasonKey = {
57397
57605
  busy: "workspace.publishActionInProgress",
57398
57606
  publishing: "workspace.publishInProgress"
57399
57607
  };
57400
- function WorkspacePublishIsland({ hasPublications, onOpenPublications, onOpenRuns, onPublish, state }) {
57608
+ function WorkspacePublishIsland({ hasPublications, onOpenPublications, onExport, onOpenRuns, onPublish, state }) {
57401
57609
  const t = useTranslate();
57402
57610
  const [popupContainer, setPopupContainer] = useState(null);
57403
57611
  const [confirming, setConfirming] = useState(false);
@@ -57497,28 +57705,36 @@ function WorkspacePublishIsland({ hasPublications, onOpenPublications, onOpenRun
57497
57705
  container: popupContainer,
57498
57706
  side: "bottom",
57499
57707
  sideOffset: 8,
57500
- children: [/* @__PURE__ */ jsxs(DropdownMenuItem, {
57501
- className: "gap-2 px-2 py-[5px]",
57502
- disabled: hasPublications !== true,
57503
- onClick: onOpenPublications,
57504
- children: [/* @__PURE__ */ jsx("i", {
57505
- "aria-hidden": "true",
57506
- className: "i-lucide-light:cloud-upload size-4 shrink-0 text-foreground/70"
57507
- }), /* @__PURE__ */ jsxs("span", {
57508
- className: "grid gap-0.5",
57509
- children: [/* @__PURE__ */ jsx("span", { children: historyLabel }), historyHint != null && /* @__PURE__ */ jsx("span", {
57510
- className: "text-[11px] text-muted-foreground",
57511
- children: historyHint
57708
+ children: [
57709
+ onExport != null && /* @__PURE__ */ jsx(WorkflowExportMenuItem, {
57710
+ className: "gap-2 px-2 py-[5px]",
57711
+ disabled: state == "busy" || state == "publishing",
57712
+ onClick: onExport
57713
+ }),
57714
+ /* @__PURE__ */ jsxs(DropdownMenuItem, {
57715
+ className: "gap-2 px-2 py-[5px]",
57716
+ disabled: hasPublications !== true,
57717
+ onClick: onOpenPublications,
57718
+ children: [/* @__PURE__ */ jsx("i", {
57719
+ "aria-hidden": "true",
57720
+ className: "i-lucide-light:cloud-upload size-4 shrink-0 text-foreground/70"
57721
+ }), /* @__PURE__ */ jsxs("span", {
57722
+ className: "grid gap-0.5",
57723
+ children: [/* @__PURE__ */ jsx("span", { children: historyLabel }), historyHint != null && /* @__PURE__ */ jsx("span", {
57724
+ className: "text-[11px] text-muted-foreground",
57725
+ children: historyHint
57726
+ })]
57512
57727
  })]
57513
- })]
57514
- }), /* @__PURE__ */ jsxs(DropdownMenuItem, {
57515
- className: "gap-2 px-2 py-[5px]",
57516
- onClick: onOpenRuns,
57517
- children: [/* @__PURE__ */ jsx("i", {
57518
- "aria-hidden": "true",
57519
- className: "i-lucide-light:history size-4 shrink-0 text-foreground/70"
57520
- }), runsLabel]
57521
- })]
57728
+ }),
57729
+ /* @__PURE__ */ jsxs(DropdownMenuItem, {
57730
+ className: "gap-2 px-2 py-[5px]",
57731
+ onClick: onOpenRuns,
57732
+ children: [/* @__PURE__ */ jsx("i", {
57733
+ "aria-hidden": "true",
57734
+ className: "i-lucide-light:history size-4 shrink-0 text-foreground/70"
57735
+ }), runsLabel]
57736
+ })
57737
+ ]
57522
57738
  })] })]
57523
57739
  });
57524
57740
  }
@@ -57745,6 +57961,8 @@ function FlowEditor({ navigationIsland, onRun, onRunStarted, onCloseRuns, onConf
57745
57961
  const selectedDesignerNode = useVal(store.$.selectedDesignerNode);
57746
57962
  const selection = useVal(store.workspace.$.selection);
57747
57963
  const selectedNodeIds = useVal(store.workspace.$.selectedNodeIds);
57964
+ const workspaceStatus = useVal(store.workspace.$.status);
57965
+ const moduleEditor = useVal(store.workspace.$.moduleEditor);
57748
57966
  const initialAddNodeTab = triggers.length == 0 ? "triggers" : "nodes";
57749
57967
  const { ignoredNodeIds, onIgnoreNodes } = useIgnoredNodes(flowId ?? "");
57750
57968
  const panel = useInspectorPanel({
@@ -57889,6 +58107,10 @@ function FlowEditor({ navigationIsland, onRun, onRunStarted, onCloseRuns, onConf
57889
58107
  },
57890
58108
  open: diagnosticsOpen
57891
58109
  }), /* @__PURE__ */ jsx(WorkspacePublishIsland, {
58110
+ onExport: () => {
58111
+ const id = store.workspace.$.flowId.value;
58112
+ if (id != null) store.workspace.exportFlow(id);
58113
+ },
57892
58114
  hasPublications: live == null ? void 0 : live.publication != null,
57893
58115
  onOpenPublications,
57894
58116
  onOpenRuns,
@@ -57948,9 +58170,11 @@ function FlowEditor({ navigationIsland, onRun, onRunStarted, onCloseRuns, onConf
57948
58170
  },
57949
58171
  addNodeOptions,
57950
58172
  disabled: authoringDisabled,
58173
+ subscribeCanvasCommands: store.workspace.subscribeCanvasCommands,
57951
58174
  focusNodeRequest: diagnosticFocus ?? nodeFocus,
57952
58175
  inspectorOpen: contextPanelVisible,
57953
58176
  model: designer,
58177
+ annotationContext: canvasAnnotations(flowId, draft, workspaceStatus, moduleEditor?.status, history.failed),
57954
58178
  onAddNode: async (option, position, connection) => {
57955
58179
  const nodeId = await store.addNode(option, position, connection);
57956
58180
  if (nodeId != null && panel.open) panel.activate([nodeId]);