@oomol-lab/open-flow 0.1.0-beta.63 → 0.1.0-beta.65

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.
@@ -1,8 +1,8 @@
1
1
  import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } from "./rolldown-runtime-CEgmsyvS.js";
2
- import { $ as useIconifyData, $t as DropdownMenu, A as datePickerValue, An as uiLocales, At as WorkflowExportMenuItem, Bn as FeishuEventPicker, Bt as NativeScrollArea, C as valueForEditor, Cn as MenuRootContext, Cr as Checkbox, D as calendarChange, Dn as SourceForm, E as typeOfSchema, F as feishuResourceKind, Fn as ko_default$1, Ft as PopoverDescription, G as designerGraph, Gt as InputGroupAddon, H as resourceData, Hn as errorNotice, Ht as VirtualScrollArea, I as supportsFeishuChatFilter, In as ja_default$1, It as PopoverHeader, J as actionSummary, Jn as resolutionOutputPorts, Jt as EmptyContent, K as semanticNodeIcon, Kn as decodeResultList, Kt as InputGroupInput, Ln as fr_default$1, Lt as PopoverPanelContent, Mn as zh_TW_default$1, Mt as followWorkbenchLink, Nn as zh_CN_default$1, Nt as Popover, O as calendarDate, On as SourceSelect, P as indexAddNodeOptions, Pn as ru_default$1, Pt as PopoverContent, Q as useIconifyCollectionLoader, Qn as isJsonObject, Qt as EmptyTitle, R as mapSource, Rn as en_default$1, Rt as PopoverTitle, S as schemaForEditor, Sn as useContextMenuRootContext, Sr as controlErrorCode, T as isUnconstrainedSchema, Tn as Badge, Tr as IconThemeContext, U as resourceValue, Ut as Z$2, V as observeResource, Vn as EventSourceSetup, Vt as ScrollArea, Wt as InputGroup, X as ContentIcon, Xt as EmptyHeader, Y as providerIcon, Yn as sourceOutputLabel, Yt as EmptyDescription, Z as IconifyProvider, Zn as triggerOutputDefinitions, Zt as EmptyMedia, _n as MenuPortal, an as DropdownMenuRadioGroup, ar as triggerConfigValue, b as editorComponent, bn as MenuGroup, cn as DropdownMenuSub, cr as listOperations, ct as diagnosticMessage, dn as DropdownMenuTrigger, dr as openApiTask, en as DropdownMenuCheckboxItem, fn as isElementDisabled, fr as securityOptions, ft as connectionCatalog, gn as MenuPositioner, gr as inputValue, hn as MenuRoot, hr as servers, in as DropdownMenuLabel, j as isDateFormat, jn as waitBranchDescription, jt as IdTooltip, k as datePickerChange, kn as createI18n$1, ln as DropdownMenuSubContent, lr as object, lt as diagnosticNodeId, mn as useCompositeItem, mr as serverAddress, n as agentTool, nn as DropdownMenuGroup, nt as hasRunFilter, on as DropdownMenuRadioItem, or as triggerConfigValues, pn as findRootOwnerId, pr as selectOperation, q as triggerNodeIcon, qt as Empty, rn as DropdownMenuItem, rt as revisionView, sn as DropdownMenuSeparator, tn as DropdownMenuContent, tt as canCancelRun, un as DropdownMenuSubTrigger, ur as openApiFieldIssues, ut as schemaMismatchMessage, vn as MenuPopup, w as getDefaultValue, wn as useMenuRootContext, wr as useAriaLabelledBy, x as editorGroups, xn as ContextMenuRootContext, y as defaultValueEditorSchema, yn as MenuItem, zn as I18n, zt as PopoverTrigger } from "./flowChanges-DpZOQuJE.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-DkvG5uDq.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 { $ 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, 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, h as TooltipArrow, hi as useId$1, hn as compareItemEquality, 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, z as variableInputCompatible, zi as useStableCallback, zn as FloatingFocusManager, zr as useOpenChangeComplete, zt as ARROW_UP } from "./icons-Db2Jb2iX.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
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
7
  import { n as compile, r as validate } from "./flowRunInputEditorStore-B2Rw4VsT.js";
8
8
  import { t as WorkbenchSelect } from "./workbenchSelect-C-jf6NIM.js";
@@ -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
  })
@@ -28954,6 +28973,108 @@ function GeneralSettings({ title, children, disabled = false, readOnly = false,
28954
28973
  }, nodeId);
28955
28974
  }
28956
28975
  //#endregion
28976
+ //#region src/workbench/browser/runtime/editor/leinaBotSelect.tsx
28977
+ function LeinaBotSelect({ source, flowId, value, disabled, onChange }) {
28978
+ const t = useTranslate();
28979
+ const [attempt, setAttempt] = useState(0);
28980
+ const [state, setState] = useState();
28981
+ useEffect(() => {
28982
+ const controller = new AbortController();
28983
+ setState((previous) => ({
28984
+ flowId,
28985
+ loading: true,
28986
+ bots: previous?.flowId == flowId ? previous.bots : void 0
28987
+ }));
28988
+ source.listLeinaBots(flowId, controller.signal).then((bots) => {
28989
+ if (!controller.signal.aborted) setState({
28990
+ flowId,
28991
+ bots,
28992
+ loading: false
28993
+ });
28994
+ }, (error) => {
28995
+ if (!controller.signal.aborted) setState({
28996
+ flowId,
28997
+ loading: false,
28998
+ error: error instanceof Error ? error.message : t("contextPanel.loadFailed")
28999
+ });
29000
+ });
29001
+ return () => controller.abort();
29002
+ }, [
29003
+ source,
29004
+ flowId,
29005
+ attempt,
29006
+ t
29007
+ ]);
29008
+ const current = state?.flowId == flowId ? state : void 0;
29009
+ const bots = current?.bots;
29010
+ const selected = typeof value == "string" ? value : value === void 0 ? void 0 : JSON.stringify(value);
29011
+ const missing = selected != null && bots != null && !bots.some((bot) => bot.leinaId == selected);
29012
+ const items = [...(bots ?? []).map((bot) => ({
29013
+ value: bot.leinaId,
29014
+ label: bot.name
29015
+ })), ...selected != null && !bots?.some((bot) => bot.leinaId == selected) ? [{
29016
+ value: selected,
29017
+ label: missing ? t("leina.unavailable", { id: selected }) : selected
29018
+ }] : []];
29019
+ return /* @__PURE__ */ jsxs(Select$1, {
29020
+ items,
29021
+ value: selected ?? null,
29022
+ disabled,
29023
+ onOpenChange: (open) => {
29024
+ if (open) setAttempt((previous) => previous + 1);
29025
+ },
29026
+ onValueChange: (next) => {
29027
+ if (typeof next == "string" && bots?.some((bot) => bot.leinaId == next)) onChange(next);
29028
+ },
29029
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
29030
+ size: "field",
29031
+ className: "w-full",
29032
+ "data-field-control": true,
29033
+ "aria-label": t("leina.robot"),
29034
+ "aria-invalid": missing || void 0,
29035
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder: t("leina.select") })
29036
+ }), /* @__PURE__ */ jsxs(SelectContent, {
29037
+ align: "start",
29038
+ alignItemWithTrigger: false,
29039
+ className: selectionMenuContentClass,
29040
+ children: [
29041
+ (current?.loading ?? true) && /* @__PURE__ */ jsx("span", {
29042
+ role: "status",
29043
+ className: "block px-1.5 py-1 text-xs leading-5 text-muted-foreground",
29044
+ children: t("leina.loading")
29045
+ }),
29046
+ bots?.map((bot) => /* @__PURE__ */ jsx(SelectItem, {
29047
+ value: bot.leinaId,
29048
+ className: selectionMenuItemClass,
29049
+ children: /* @__PURE__ */ jsx("span", {
29050
+ className: "truncate",
29051
+ children: bot.name
29052
+ })
29053
+ }, bot.leinaId)),
29054
+ missing && selected != null && /* @__PURE__ */ jsx(SelectItem, {
29055
+ value: selected,
29056
+ disabled: true,
29057
+ className: `mt-1 first-of-type:mt-0 min-h-7 rounded-[var(--ui-control-radius,var(--ui-radius))] py-1 text-xs leading-5 font-normal text-destructive`,
29058
+ children: t("leina.unavailable", { id: selected })
29059
+ }),
29060
+ current?.error != null && /* @__PURE__ */ jsxs("span", {
29061
+ role: "alert",
29062
+ className: "block px-1.5 py-1 text-xs leading-5 whitespace-normal break-words text-destructive",
29063
+ children: [
29064
+ t("leina.loadFailed"),
29065
+ " ",
29066
+ current.error
29067
+ ]
29068
+ }),
29069
+ bots?.length == 0 && !current?.loading && /* @__PURE__ */ jsx("span", {
29070
+ className: "block px-1.5 py-1 text-xs leading-5 whitespace-normal text-muted-foreground",
29071
+ children: t("leina.empty")
29072
+ })
29073
+ ]
29074
+ })]
29075
+ });
29076
+ }
29077
+ //#endregion
28957
29078
  //#region src/workbench/browser/runtime/editor/linearTriggerConfig.tsx
28958
29079
  function useOptions(store, nodeId, field, scope, enabled) {
28959
29080
  const [attempt, setAttempt] = useState(0);
@@ -34700,6 +34821,15 @@ function NodeInspector({ webhookDeployment, variables = {
34700
34821
  const binding = source?.kind === "binding" ? revision.binding(source.bindingId) : void 0;
34701
34822
  return {
34702
34823
  definition,
34824
+ editor: !readOnly && connectors != null && connector?.action == "leina.send_notification" && definition.handle == "leinaId" && mapping?.kind != "sources" ? /* @__PURE__ */ jsx(LeinaBotSelect, {
34825
+ source: connectors,
34826
+ flowId: revision.revision.flowId,
34827
+ value: inputValue(mapping, definition.value),
34828
+ disabled,
34829
+ onChange: (value) => {
34830
+ store?.setInputValue(selection.id, definition.handle, value);
34831
+ }
34832
+ }, revision.revision.flowId) : void 0,
34703
34833
  value: inputValue(mapping, definition.value),
34704
34834
  onReset: definition.value !== void 0 && mapping != null ? () => void store?.resetInputs(selection.id, [definition.handle]) : void 0,
34705
34835
  connected: mapping?.kind === "sources" && binding?.kind !== "variable",
@@ -34711,6 +34841,10 @@ function NodeInspector({ webhookDeployment, variables = {
34711
34841
  allowAddGroup: selection.kind !== "wait" && selection.kind !== "approval",
34712
34842
  title: t("inspector.ports.inputsTitle"),
34713
34843
  entries,
34844
+ fieldLabels: connector?.action == "leina.send_notification" ? {
34845
+ leinaId: t("leina.robot"),
34846
+ message: t("leina.message")
34847
+ } : void 0,
34714
34848
  onReset: () => {
34715
34849
  return store?.resetInputs(selection.id, entries.flatMap((entry) => "group" in entry ? [] : [entry.definition.handle]));
34716
34850
  },
@@ -35094,6 +35228,12 @@ function useInspectorPanel({ identity, preferences, selectedNodeIds, onSelectNod
35094
35228
  };
35095
35229
  }
35096
35230
  //#endregion
35231
+ //#region src/canvas/browser/base/compare.ts
35232
+ var isSameXYPosition = (p1, p2) => !p1 || !p2 ? p1 === p2 : p1.x === p2.x && p1.y === p2.y;
35233
+ var isSize = (s) => isPlainObject(s) && isNumber(s.width) && isNumber(s.height);
35234
+ var isSameSize = (s1, s2) => !s1 || !s2 ? s1 === s2 : s1.width === s2.width && s1.height === s2.height;
35235
+ var isSameViewport = (v1, v2) => !v1 || !v2 ? v1 === v2 : v1.x === v2.x && v1.y === v2.y && v1.zoom === v2.zoom;
35236
+ //#endregion
35097
35237
  //#region src/canvas/browser/stores/node/constants.ts
35098
35238
  var NODE_TYPE = /* @__PURE__ */ function(NODE_TYPE) {
35099
35239
  NODE_TYPE["TaskNode"] = "task_node";
@@ -35113,23 +35253,11 @@ var DEFAULT_POSITION = /* @__PURE__ */ Object.freeze({
35113
35253
  y: 0
35114
35254
  });
35115
35255
  //#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
35256
  //#region src/canvas/browser/base/rfHelpers.ts
35123
35257
  /** Converts a manifest node identifier to a React Flow identifier. */
35124
35258
  function toRFNodeId(nodeId, nodeType) {
35125
35259
  return (nodeType === NODE_TYPE.CommentNode ? "c:" : "m:") + nodeId;
35126
35260
  }
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
35261
  function getRFNodeType(rfNodeId) {
35134
35262
  if (rfNodeId[1] === ":") switch (rfNodeId[0]) {
35135
35263
  case "m": return "manifest_node";
@@ -38051,17 +38179,64 @@ var createRFCommand = (nodes) => new RFCommandImpl(nodes);
38051
38179
  * registered instance.
38052
38180
  */
38053
38181
  var RFCommandImpl = class {
38182
+ get remoteViewportChange() {
38183
+ return this.remoteMoves > 0;
38184
+ }
38185
+ async executeRemote(command) {
38186
+ const executions = [];
38187
+ for (const [rf, registration] of this.rfs) {
38188
+ if (!rf.viewportInitialized) continue;
38189
+ const run = async () => {
38190
+ if (this.rfs.get(rf) !== registration) return;
38191
+ this.remoteMoves++;
38192
+ try {
38193
+ switch (command.type) {
38194
+ case "focus": {
38195
+ const node = this.nodes.get(command.nodeId)?.$.rfNode.value;
38196
+ if (node != null) await focusNodes(this.nodes.size, rf, [node], { duration: 0 });
38197
+ break;
38198
+ }
38199
+ case "zoom":
38200
+ await rf.zoomTo(command.zoom, { duration: 0 });
38201
+ break;
38202
+ case "pan": {
38203
+ const viewport = rf.getViewport();
38204
+ await rf.setViewport({
38205
+ ...viewport,
38206
+ x: viewport.x + command.dx,
38207
+ y: viewport.y + command.dy
38208
+ }, { duration: 0 });
38209
+ break;
38210
+ }
38211
+ case "fit": if (rf.getNodes().length > 0) await rf.fitView({
38212
+ padding: .15,
38213
+ maxZoom: 1,
38214
+ duration: 0
38215
+ });
38216
+ }
38217
+ } finally {
38218
+ this.remoteMoves--;
38219
+ }
38220
+ };
38221
+ const next = (registration.pending ?? Promise.resolve()).then(run, run);
38222
+ registration.pending = next;
38223
+ executions.push(next);
38224
+ }
38225
+ await Promise.all(executions);
38226
+ }
38054
38227
  send(method, ...args) {
38055
- for (const rf of this.rfs) this[method](rf, ...args);
38228
+ for (const rf of this.rfs.keys()) this[method](rf, ...args);
38056
38229
  }
38057
38230
  constructor(nodes) {
38058
38231
  this.nodes = nodes;
38059
38232
  this.dispose = disposableStore();
38060
- this.rfs = /* @__PURE__ */ new Set();
38233
+ this.rfs = /* @__PURE__ */ new Map();
38234
+ this.remoteMoves = 0;
38061
38235
  this.onRFInstance = (rf) => {
38062
- this.rfs.add(rf);
38236
+ const registration = {};
38237
+ this.rfs.set(rf, registration);
38063
38238
  return () => {
38064
- this.rfs.delete(rf);
38239
+ if (this.rfs.get(rf) === registration) this.rfs.delete(rf);
38065
38240
  };
38066
38241
  };
38067
38242
  this.dispose.add(() => {
@@ -38085,7 +38260,7 @@ function getCenter(rfNode) {
38085
38260
  }
38086
38261
  function focusNodes(nodesCount, rf, rfNodes, options) {
38087
38262
  if (!rfNodes.length) return;
38088
- if (rfNodes.length > 1) rf.fitBounds(rf.getNodesBounds(rfNodes), {
38263
+ if (rfNodes.length > 1) return rf.fitBounds(rf.getNodesBounds(rfNodes), {
38089
38264
  duration: 150,
38090
38265
  padding: .15,
38091
38266
  ...options
@@ -38094,7 +38269,7 @@ function focusNodes(nodesCount, rf, rfNodes, options) {
38094
38269
  const rfNode = rfNodes[0];
38095
38270
  const center = getCenter(rfNode);
38096
38271
  const zoom = getNodeMinimap(nodesCount, rf.getZoom()) === NodeMiniMapPhase.None ? rf.getZoom() : 1;
38097
- rf.setCenter(center.x, center.y, {
38272
+ return rf.setCenter(center.x, center.y, {
38098
38273
  zoom,
38099
38274
  ...options
38100
38275
  });
@@ -38346,19 +38521,19 @@ var CanvasStore = class {
38346
38521
  };
38347
38522
  }
38348
38523
  doRelayout() {
38524
+ const nodes = this.allLayoutNodes();
38349
38525
  const g = new ie.Graph().setDefaultEdgeLabel(() => ({}));
38350
38526
  g.setGraph({
38351
38527
  rankdir: "LR",
38352
38528
  ranksep: 80
38353
38529
  });
38354
38530
  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
38531
+ for (const node of nodes) {
38532
+ const { id, measured } = node.$.rfNode.value;
38533
+ singles.add(id);
38534
+ g.setNode(id, {
38535
+ width: measured?.width ?? 0,
38536
+ height: measured?.height ?? 0
38362
38537
  });
38363
38538
  }
38364
38539
  for (const rfEdge of this.$.rfEdges.value) {
@@ -38368,16 +38543,18 @@ var CanvasStore = class {
38368
38543
  }
38369
38544
  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
38545
  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])));
38546
+ const positions = wrapLayout(nodes.map((node) => {
38547
+ const { x, y, width, height, rank } = g.node(node.rfNodeId);
38548
+ return {
38549
+ id: node.nodeId,
38550
+ column: rank,
38551
+ x: x - width / 2,
38552
+ y: y - height / 2,
38553
+ height
38554
+ };
38555
+ }));
38556
+ for (const node of nodes) node.$$.position.set(positions[node.nodeId]);
38557
+ this.#callbacks.onMoveNodes(positions);
38381
38558
  }
38382
38559
  doRemoveNodes(nodes) {
38383
38560
  if (this.disposed || !nodes?.size) return;
@@ -38479,6 +38656,75 @@ function useNodeMiniMapPhase() {
38479
38656
  return useContext(NodeMiniMapContext);
38480
38657
  }
38481
38658
  //#endregion
38659
+ //#region src/canvas/browser/graph/annotations.tsx
38660
+ var CanvasAnnotationContext = createContext(void 0);
38661
+ var metadataStringLimit = 256;
38662
+ var metadataByteLimit = 2048;
38663
+ var encoder = new TextEncoder();
38664
+ function validIdentity(value) {
38665
+ return value.length > 0 && value.length <= metadataStringLimit;
38666
+ }
38667
+ function serializeMetadata(metadata) {
38668
+ const text = JSON.stringify(metadata);
38669
+ return encoder.encode(text).length <= metadataByteLimit ? text : void 0;
38670
+ }
38671
+ function excerpt(value, limit) {
38672
+ if (value.length <= limit) return value;
38673
+ if (limit <= 1) return "";
38674
+ let end = limit - 1;
38675
+ const last = value.charCodeAt(end - 1);
38676
+ if (last >= 55296 && last <= 56319) end--;
38677
+ return `${value.slice(0, end)}…`;
38678
+ }
38679
+ /** Keep identities intact; shorten display text to the annotation protocol's limits. */
38680
+ function nodeAnnotationMetadata(context, node) {
38681
+ if (![
38682
+ context.flowId,
38683
+ context.revisionId,
38684
+ node.id
38685
+ ].every(validIdentity)) return;
38686
+ const metadata = {
38687
+ description: node.description?.trim() ? excerpt(node.description, metadataStringLimit) : void 0,
38688
+ flowId: context.flowId,
38689
+ revisionId: context.revisionId,
38690
+ nodeId: node.id,
38691
+ syncState: context.syncState
38692
+ };
38693
+ let text = serializeMetadata(metadata);
38694
+ while (text == null) {
38695
+ if (!metadata.description) return;
38696
+ metadata.description = excerpt(metadata.description, metadata.description.length - 1) || void 0;
38697
+ text = serializeMetadata(metadata);
38698
+ }
38699
+ return text;
38700
+ }
38701
+ /** Browser annotation fields are scalar values, so the complete ID list is encoded as a JSON string. */
38702
+ function selectionAnnotationMetadata(context, nodeIds, tooLarge) {
38703
+ if (nodeIds.length < 2 || ![context.flowId, context.revisionId].every(validIdentity) || nodeIds.some((id) => !id)) return;
38704
+ const identities = {
38705
+ flowId: context.flowId,
38706
+ revisionId: context.revisionId,
38707
+ syncState: context.syncState
38708
+ };
38709
+ const ids = JSON.stringify(nodeIds);
38710
+ return (ids.length <= metadataStringLimit ? serializeMetadata({
38711
+ ...identities,
38712
+ nodeIds: ids
38713
+ }) : void 0) ?? serializeMetadata({
38714
+ ...identities,
38715
+ nodeCount: nodeIds.length,
38716
+ error: excerpt(tooLarge, metadataStringLimit)
38717
+ });
38718
+ }
38719
+ function useNodeAnnotation(node) {
38720
+ const context = useContext(CanvasAnnotationContext);
38721
+ if (context == null || node == null || node.kind == "comment") return {};
38722
+ return {
38723
+ "oai-annotatable": node.title,
38724
+ "oai-annotation-metadata": nodeAnnotationMetadata(context, node)
38725
+ };
38726
+ }
38727
+ //#endregion
38482
38728
  //#region src/canvas/browser/graph/CanvasStoreContext.tsx
38483
38729
  var CanvasDarkContext = createContext(false);
38484
38730
  var CanvasStoreContext = createContext(null);
@@ -49434,7 +49680,7 @@ function connectionPortIndex(node, handleId) {
49434
49680
  if (!handle) return 0;
49435
49681
  return handles.filter((item) => item.position === handle.position && item.y < handle.y).length;
49436
49682
  }
49437
- /** A return loop belongs to its output: fixed side offsets and a lane below that card. */
49683
+ /** Return connections use fixed side offsets and a lane below the output or above a lower row. */
49438
49684
  function getConnectionPath({ sourceBounds, targetBounds, sourcePortIndex = 0, targetPortIndex = 0, ...params }) {
49439
49685
  const { sourceX, sourceY, targetX, targetY, sourcePosition = Position.Bottom, targetPosition = Position.Top } = params;
49440
49686
  const reverse = sourcePosition === Position.Left && targetPosition === Position.Right;
@@ -49452,11 +49698,13 @@ function getConnectionPath({ sourceBounds, targetBounds, sourcePortIndex = 0, ta
49452
49698
  const clearance = 40 + Math.max(sourcePortIndex, targetPortIndex) * 12;
49453
49699
  const radius = 16;
49454
49700
  const outputBounds = reverse ? targetBounds : sourceBounds;
49701
+ const inputBounds = reverse ? sourceBounds : targetBounds;
49455
49702
  const rightY = reverse ? targetY : sourceY;
49456
49703
  const leftY = reverse ? sourceY : targetY;
49457
49704
  const right = rightX + clearance;
49458
49705
  const left = leftX - clearance;
49459
- const laneY = Math.max(rightY, outputBounds ? outputBounds.y + outputBounds.height : rightY) + clearance;
49706
+ const outputLane = Math.max(rightY, outputBounds ? outputBounds.y + outputBounds.height : rightY) + clearance;
49707
+ const laneY = Math.max(outputLane, inputBounds ? inputBounds.y - clearance : outputLane);
49460
49708
  const points = [
49461
49709
  {
49462
49710
  x: rightX,
@@ -51459,6 +51707,7 @@ function ConditionRule({ item, t }) {
51459
51707
  var NodeLayout = /* @__PURE__ */ memo(({ canvasStore, nodeStore, visible }) => {
51460
51708
  const t = useTranslate();
51461
51709
  const modelNode = useVal(NodeStore.to(nodeStore)?.content$);
51710
+ const annotation = useNodeAnnotation(modelNode);
51462
51711
  const branches = modelNode?.kind == "condition" || modelNode?.kind == "approval" || modelNode?.kind == "wait" ? modelNode.outputs.flatMap((port) => "handle" in port ? [port.handle] : []) : void 0;
51463
51712
  const executionInput = modelNode != null && modelNode.kind != "trigger" && modelNode.kind != "value";
51464
51713
  const editable = useVal(canvasStore.$.editable);
@@ -51550,6 +51799,7 @@ var NodeLayout = /* @__PURE__ */ memo(({ canvasStore, nodeStore, visible }) => {
51550
51799
  children: /* @__PURE__ */ jsx(NodeStoreContext.Provider, {
51551
51800
  value: nodeStore,
51552
51801
  children: /* @__PURE__ */ jsx("div", {
51802
+ ...annotation,
51553
51803
  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
51804
  children: /* @__PURE__ */ jsxs("div", {
51555
51805
  className: clsx(NodeLayout_module_default.offsetContainer, skip && NodeLayout_module_default.skipOuter),
@@ -51799,6 +52049,25 @@ var ReactFlowContainer_module_default = {
51799
52049
  viewSeparator: "ReactFlowContainer_viewSeparator_ebe22"
51800
52050
  };
51801
52051
  //#endregion
52052
+ //#region src/canvas/common/commands.ts
52053
+ var canvasMinZoom = .1;
52054
+ discriminatedUnion("type", [
52055
+ strictObject({
52056
+ type: literal("focus"),
52057
+ nodeId: string().min(1)
52058
+ }),
52059
+ strictObject({
52060
+ type: literal("zoom"),
52061
+ zoom: number().min(canvasMinZoom).max(3)
52062
+ }),
52063
+ strictObject({
52064
+ type: literal("pan"),
52065
+ dx: number(),
52066
+ dy: number()
52067
+ }),
52068
+ strictObject({ type: literal("fit") })
52069
+ ]);
52070
+ //#endregion
51802
52071
  //#region src/canvas/browser/base/dragNDrop.ts
51803
52072
  var TRANSFER_SHARED_BLOCK_KEY = "application/oomol-designer/sharedBlock";
51804
52073
  var TRANSFER_SCRIPTLET_KEY = "application/oomol-designer/scriptlet";
@@ -52397,6 +52666,34 @@ function HelperLines({ horizontal, vertical, strokeStyle = "#b7654d" }) {
52397
52666
  });
52398
52667
  }
52399
52668
  //#endregion
52669
+ //#region src/canvas/browser/graph/ReactFlowContainer/selectionAnnotation.tsx
52670
+ /** React Flow owns the selection rectangle and exposes no prop for its custom attributes. */
52671
+ function SelectionAnnotation({ nodes }) {
52672
+ const context = useContext(CanvasAnnotationContext);
52673
+ const root = useStore((state) => state.domNode);
52674
+ const active = useStore((state) => state.nodesSelectionActive && !state.userSelectionActive);
52675
+ const t = useTranslate();
52676
+ const label = t("annotation.selectedNodes", { count: nodes.length });
52677
+ 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;
52678
+ useLayoutEffect(() => {
52679
+ if (!active || metadata == null) return;
52680
+ const target = root?.querySelector(".react-flow__nodesselection-rect");
52681
+ if (target == null) return;
52682
+ target.setAttribute("oai-annotatable", label);
52683
+ target.setAttribute("oai-annotation-metadata", metadata);
52684
+ return () => {
52685
+ target.removeAttribute("oai-annotatable");
52686
+ target.removeAttribute("oai-annotation-metadata");
52687
+ };
52688
+ }, [
52689
+ root,
52690
+ active,
52691
+ label,
52692
+ metadata
52693
+ ]);
52694
+ return null;
52695
+ }
52696
+ //#endregion
52400
52697
  //#region src/canvas/browser/graph/ReactFlowContainer/ReactFlowContainer.tsx
52401
52698
  var PAN_ON_DRAG_MOUSE = [0];
52402
52699
  var PAN_ON_DRAG_TOUCHPAD = [1];
@@ -52509,6 +52806,7 @@ var FlowControls = /*#__PURE__*/ memo((props) => {
52509
52806
  ] });
52510
52807
  });
52511
52808
  var ReactFlowContainerInner = (props) => {
52809
+ const annotationContext = useContext(CanvasAnnotationContext);
52512
52810
  const flowState = useStoreApi();
52513
52811
  const selecting = useRef(false);
52514
52812
  const selectionEnd = useRef(props.onSelectionEnd);
@@ -52794,6 +53092,7 @@ var ReactFlowContainerInner = (props) => {
52794
53092
  children: /* @__PURE__ */ jsx(PaneRectContext.Provider, {
52795
53093
  value: paneRect$,
52796
53094
  children: /* @__PURE__ */ jsxs(index, {
53095
+ "oai-annotation-container": annotationContext == null ? void 0 : "",
52797
53096
  onKeyDown: (event) => {
52798
53097
  if ((event.key === "Enter" || event.key === " ") && event.target instanceof Element && event.target.matches(".react-flow__node")) props.onActivateSelection?.();
52799
53098
  if (!editable || event.defaultPrevented || event.nativeEvent.isComposing) return;
@@ -52837,7 +53136,7 @@ var ReactFlowContainerInner = (props) => {
52837
53136
  viewport,
52838
53137
  onViewportChange,
52839
53138
  maxZoom: 3,
52840
- minZoom: .1,
53139
+ minZoom: canvasMinZoom,
52841
53140
  nodesDraggable: props.nodesDraggable ?? props.editable,
52842
53141
  nodesConnectable: editable && props.onConnect != null,
52843
53142
  onEdgeContextMenu: (event, edge) => (event.preventDefault(), setEdgeContextMenu({
@@ -52864,6 +53163,7 @@ var ReactFlowContainerInner = (props) => {
52864
53163
  onConnect: editable ? props.onConnect : void 0,
52865
53164
  onDragOver: editable && (props.onAddNode != null || props.onDropAddItem != null) ? handleDragOver : void 0,
52866
53165
  onDrop: editable && (props.onAddNode != null || props.onDropAddItem != null) ? onDrop : void 0,
53166
+ onMove: props.onMove,
52867
53167
  onMoveEnd: props.onMoveEnd,
52868
53168
  onNodeDragStop: props.onNodeDragStop,
52869
53169
  onSelectionChange: props.onSelectionChange,
@@ -52896,6 +53196,7 @@ var ReactFlowContainerInner = (props) => {
52896
53196
  connectionLineComponent: ConnectionLine,
52897
53197
  "aria-readonly": !editable,
52898
53198
  children: [
53199
+ /* @__PURE__ */ jsx(SelectionAnnotation, { nodes: selectedNodes }),
52899
53200
  (props.canDeleteNodes ?? true) && !selectionInProgress && /* @__PURE__ */ jsx(SelectionFloatBar, {
52900
53201
  editable: !!editable,
52901
53202
  nodes: selectedNodes,
@@ -53117,67 +53418,71 @@ function tooShort(from, to) {
53117
53418
  }
53118
53419
  //#endregion
53119
53420
  //#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 }) => {
53421
+ 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
53422
  const editable = useVal(flowCanvasStore.$.editable);
53122
53423
  const nodeMiniMapPhase = useVal(flowCanvasStore.$.nodeMiniMapPhase);
53123
53424
  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
53425
+ return /* @__PURE__ */ jsx(CanvasAnnotationContext.Provider, {
53426
+ value: annotationContext,
53427
+ children: /* @__PURE__ */ jsx(InspectSelectionContext.Provider, {
53428
+ value: {
53429
+ onInspect: onInspectSelection,
53430
+ expanded: inspectorOpen
53431
+ },
53432
+ children: /* @__PURE__ */ jsx(CanvasStoreProvider, {
53433
+ value: flowCanvasStore,
53434
+ dark,
53435
+ children: /* @__PURE__ */ jsx(NodeMiniMapProvider, {
53436
+ value: nodeMiniMapPhase,
53437
+ children: /* @__PURE__ */ jsx(ReactFlowContainer, {
53438
+ cornerTools,
53439
+ cornerLeading,
53440
+ topLeftTools,
53441
+ bottomRightTools,
53442
+ toolbar,
53443
+ editable,
53444
+ nodesDraggable: nodesDraggable ?? editable,
53445
+ className,
53446
+ i18n: flowCanvasStore.i18n,
53447
+ dark,
53448
+ dottedBackground: dottedBackground ?? editable,
53449
+ fitView: fitView ?? !editable,
53450
+ fitViewOptions,
53451
+ layoutMotion,
53452
+ nodeTypes: NODE_TYPES,
53453
+ edgeTypes: EDGE_TYPES,
53454
+ miniMapExpanded$: flowCanvasStore.$$.miniMapExpanded,
53455
+ interactiveMode$: flowCanvasStore.$$.interactiveMode,
53456
+ nodes$: flowCanvasStore.$.rfNodes,
53457
+ edges$: flowCanvasStore.$.rfEdges,
53458
+ viewport$: flowCanvasStore.$$.viewport,
53459
+ onRequestAddNode,
53460
+ addNodeRequest,
53461
+ addItemRequest,
53462
+ onAddNode: flowCanvasStore.onAddNode,
53463
+ onBeforeDelete: flowCanvasStore.onBeforeDelete,
53464
+ onNodesChange: flowCanvasStore.handleNodesChange,
53465
+ onEdgesChange: flowCanvasStore.handleEdgesChange,
53466
+ onConnect: flowCanvasStore.onRFConnect,
53467
+ onMove,
53468
+ onMoveEnd,
53469
+ onNodeDragStop,
53470
+ onSelectionChange,
53471
+ onActivateSelection,
53472
+ onInspectSelection,
53473
+ onSelectionStart,
53474
+ onSelectionEnd,
53475
+ isValidConnection,
53476
+ onDropAddItem,
53477
+ onRelayout: editable ? flowCanvasStore.onRelayout : void 0,
53478
+ onLayoutMeasured: flowCanvasStore.completeLayout,
53479
+ onInstance: flowCanvasStore.rfCommand.onRFInstance,
53480
+ onInit: flowCanvasStore.onInit,
53481
+ onCopy: flowCanvasStore.onCopy,
53482
+ onPaste: flowCanvasStore.onPaste,
53483
+ waitNode: flowCanvasStore.waitNode,
53484
+ duplicateNodes: flowCanvasStore.duplicateNodes
53485
+ })
53181
53486
  })
53182
53487
  })
53183
53488
  })
@@ -53206,7 +53511,21 @@ function FlowCanvasView(props) {
53206
53511
  const propsRef = useRef(props);
53207
53512
  const selectedEdge = useRef();
53208
53513
  propsRef.current = props;
53209
- const onMoveEnd = useCallback((_, viewport) => propsRef.current.onMoveViewport(viewport), [store]);
53514
+ const lastMove = useRef();
53515
+ const onMove = useCallback((_, viewport) => {
53516
+ lastMove.current = {
53517
+ viewport,
53518
+ remote: store.rfCommand.remoteViewportChange
53519
+ };
53520
+ }, [store]);
53521
+ const onMoveEnd = useCallback((_, viewport) => {
53522
+ const move = lastMove.current;
53523
+ if (move != null && (move.remote || !isSameViewport(move.viewport, viewport))) return;
53524
+ propsRef.current.onMoveViewport(viewport);
53525
+ }, [store]);
53526
+ useEffect(() => props.subscribeCanvasCommands?.((command) => {
53527
+ store.rfCommand.executeRemote(command).catch((error) => console.error("Failed to execute canvas command.", error));
53528
+ }), [store, props.subscribeCanvasCommands]);
53210
53529
  const onNodeDragStop = useCallback((_, node, nodes) => {
53211
53530
  const moved = nodes.length > 0 ? nodes : [node];
53212
53531
  propsRef.current.onMoveNodes(Object.fromEntries(moved.flatMap((item) => {
@@ -53264,6 +53583,7 @@ function FlowCanvasView(props) {
53264
53583
  }, [store, props.focusNodeRequest]);
53265
53584
  const selectionIds = () => store.$.rfNodes.value.flatMap((node) => node.selected && node.data?.store ? [node.data.store.nodeId] : []);
53266
53585
  return /* @__PURE__ */ jsx(FlowCanvas, {
53586
+ annotationContext: props.annotationContext,
53267
53587
  cornerTools: props.cornerTools,
53268
53588
  cornerLeading: props.cornerLeading,
53269
53589
  topLeftTools: props.topLeftTools,
@@ -53280,6 +53600,7 @@ function FlowCanvasView(props) {
53280
53600
  flowCanvasStore: store,
53281
53601
  isValidConnection,
53282
53602
  layoutMotion: props.layoutMotion,
53603
+ onMove,
53283
53604
  onMoveEnd,
53284
53605
  onNodeDragStop,
53285
53606
  onDropAddItem,
@@ -53391,7 +53712,7 @@ function focusPanel(event) {
53391
53712
  if (element instanceof Element && element.closest("a, button, input, label, select, textarea, [contenteditable=\"true\"]")) return;
53392
53713
  event.currentTarget.focus({ preventScroll: true });
53393
53714
  }
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) {
53715
+ 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
53716
  const [pickerRequest, setPickerRequest] = useState();
53396
53717
  const [pickerCentered, setPickerCentered] = useState(false);
53397
53718
  useEffect(() => {
@@ -53532,6 +53853,7 @@ var WorkbenchCanvas = forwardRef(function WorkbenchCanvas({ nodePicker, addNodeC
53532
53853
  tabIndex: 0,
53533
53854
  children: [
53534
53855
  /* @__PURE__ */ jsx(FlowCanvasView, {
53856
+ annotationContext,
53535
53857
  onRequestAddNode: (request) => {
53536
53858
  if (addNodeRequest == null) setPickerCentered(false);
53537
53859
  setPickerRequest(request);
@@ -53599,6 +53921,7 @@ var WorkbenchCanvas = forwardRef(function WorkbenchCanvas({ nodePicker, addNodeC
53599
53921
  },
53600
53922
  onMoveNodes,
53601
53923
  onMoveViewport,
53924
+ subscribeCanvasCommands,
53602
53925
  onCopy: (nodeIds) => {
53603
53926
  onSelectNodes(nodeIds);
53604
53927
  onCopy();
@@ -57753,6 +58076,8 @@ function FlowEditor({ navigationIsland, onRun, onRunStarted, onCloseRuns, onConf
57753
58076
  const selectedDesignerNode = useVal(store.$.selectedDesignerNode);
57754
58077
  const selection = useVal(store.workspace.$.selection);
57755
58078
  const selectedNodeIds = useVal(store.workspace.$.selectedNodeIds);
58079
+ const workspaceStatus = useVal(store.workspace.$.status);
58080
+ const moduleEditor = useVal(store.workspace.$.moduleEditor);
57756
58081
  const initialAddNodeTab = triggers.length == 0 ? "triggers" : "nodes";
57757
58082
  const { ignoredNodeIds, onIgnoreNodes } = useIgnoredNodes(flowId ?? "");
57758
58083
  const panel = useInspectorPanel({
@@ -57960,9 +58285,11 @@ function FlowEditor({ navigationIsland, onRun, onRunStarted, onCloseRuns, onConf
57960
58285
  },
57961
58286
  addNodeOptions,
57962
58287
  disabled: authoringDisabled,
58288
+ subscribeCanvasCommands: store.workspace.subscribeCanvasCommands,
57963
58289
  focusNodeRequest: diagnosticFocus ?? nodeFocus,
57964
58290
  inspectorOpen: contextPanelVisible,
57965
58291
  model: designer,
58292
+ annotationContext: canvasAnnotations(flowId, draft, workspaceStatus, moduleEditor?.status, history.failed),
57966
58293
  onAddNode: async (option, position, connection) => {
57967
58294
  const nodeId = await store.addNode(option, position, connection);
57968
58295
  if (nodeId != null && panel.open) panel.activate([nodeId]);