import {
  ActionNode,
  ConditionNode,
  DEFAULT_NODE_HEIGHT,
  DEFAULT_NODE_KINDS,
  DEFAULT_NODE_WIDTH,
  ErrorNode,
  FlowCanvas,
  FlowEdge,
  FlowNode,
  ForEachNode,
  GroupNode,
  Handle,
  HttpRequestNode,
  OutputNode,
  ParallelNode,
  StickyNode,
  TriggerNode,
  WaitNode,
  WebhookNode,
  applyEdgeChanges,
  applyNodeChanges,
  bezierPath,
  bezierPath2,
  buildEdgePath,
  buildNodeKindRegistry,
  change,
  clampToParentExtent,
  descendantsOf,
  findAncestor,
  findContainingGroup,
  flowToScreen,
  handleCentre,
  screenToFlow,
  smoothStepPath,
  stepPath,
  straightPath,
  useFlow,
  useFlowNodeContext,
  useFlowStore
} from "./chunk-2RD5OERK.js";
import {
  cn
} from "./chunk-ZAUUGK2Y.js";

// src/workflow/hooks/useAutoLayout.ts
import { useCallback, useEffect, useRef, useState } from "react";
function signature(nodes, edges) {
  const nodeKeys = nodes.map((n) => `${n.id}:${n.parentId ?? ""}`).sort().join("|");
  const edgeKeys = edges.map((e) => `${e.id}:${e.source}>${e.target}`).sort().join("|");
  return `${nodeKeys}::${edgeKeys}`;
}
function useAutoLayout({
  engine,
  nodes,
  edges,
  onNodesChange,
  options,
  auto = false,
  debounceMs = 250,
  onLayout,
  onError
}) {
  const [isLaying, setIsLaying] = useState(false);
  const runningRef = useRef(false);
  const optionsRef = useRef(options);
  const onLayoutRef = useRef(onLayout);
  const onErrorRef = useRef(onError);
  const onNodesChangeRef = useRef(onNodesChange);
  useEffect(() => {
    optionsRef.current = options;
  }, [options]);
  useEffect(() => {
    onLayoutRef.current = onLayout;
  }, [onLayout]);
  useEffect(() => {
    onErrorRef.current = onError;
  }, [onError]);
  useEffect(() => {
    onNodesChangeRef.current = onNodesChange;
  }, [onNodesChange]);
  const nodesRef = useRef(nodes);
  const edgesRef = useRef(edges);
  useEffect(() => {
    nodesRef.current = nodes;
  }, [nodes]);
  useEffect(() => {
    edgesRef.current = edges;
  }, [edges]);
  const runLayout = useCallback(async () => {
    if (!engine) return;
    if (runningRef.current) return;
    runningRef.current = true;
    setIsLaying(true);
    try {
      const positions = await engine.layout(nodesRef.current, edgesRef.current, optionsRef.current);
      const positionedIds = new Set(positions.map((p) => p.id));
      const deltas = /* @__PURE__ */ new Map();
      const cs = [];
      for (const p of positions) {
        const cur = nodesRef.current.find((n) => n.id === p.id);
        if (!cur) continue;
        if (cur.position.x === p.position.x && cur.position.y === p.position.y) continue;
        cs.push(change.node.position(p.id, p.position, false));
        deltas.set(p.id, {
          dx: p.position.x - cur.position.x,
          dy: p.position.y - cur.position.y
        });
      }
      const findParent = (id) => nodesRef.current.find((x) => x.id === id);
      for (const n of nodesRef.current) {
        if (positionedIds.has(n.id)) continue;
        if (!n.parentId) continue;
        let cursor = n.parentId;
        const seen = /* @__PURE__ */ new Set();
        let delta;
        while (cursor) {
          if (seen.has(cursor)) break;
          seen.add(cursor);
          const found = deltas.get(cursor);
          if (found) {
            delta = found;
            break;
          }
          cursor = findParent(cursor)?.parentId;
        }
        if (!delta) continue;
        cs.push(
          change.node.position(
            n.id,
            { x: n.position.x + delta.dx, y: n.position.y + delta.dy },
            false
          )
        );
      }
      if (cs.length) onNodesChangeRef.current?.(cs);
      onLayoutRef.current?.(positions);
    } catch (err) {
      onErrorRef.current?.(err);
    } finally {
      runningRef.current = false;
      setIsLaying(false);
    }
  }, [engine]);
  const sig = signature(nodes, edges);
  const sigRef = useRef(sig);
  useEffect(() => {
    if (!auto) return;
    if (sigRef.current === sig) return;
    sigRef.current = sig;
    const timer = setTimeout(() => void runLayout(), debounceMs);
    return () => clearTimeout(timer);
  }, [sig, auto, debounceMs, runLayout]);
  return { runLayout, isLaying };
}

// src/workflow/hooks/useFlowState.ts
import { useCallback as useCallback2, useMemo, useRef as useRef2, useState as useState2 } from "react";
function useFlowState(options = {}) {
  const [nodes, setNodes] = useState2(options.initialNodes ?? []);
  const [edges, setEdges] = useState2(options.initialEdges ?? []);
  const nodesRef = useRef2(nodes);
  nodesRef.current = nodes;
  const onNodesChange = useCallback2((changes) => {
    setNodes((prev) => applyNodeChanges(prev, changes));
  }, []);
  const onEdgesChange = useCallback2((changes) => {
    setEdges((prev) => applyEdgeChanges(prev, changes));
  }, []);
  const updateNodeData = useCallback2((id, patch) => {
    setNodes((prev) => {
      let touched = false;
      const next = prev.map((n) => {
        if (n.id !== id) return n;
        const nextData = typeof patch === "function" ? patch(n.data) : { ...n.data ?? {}, ...patch };
        if (Object.is(nextData, n.data)) return n;
        touched = true;
        return { ...n, data: nextData };
      });
      return touched ? next : prev;
    });
  }, []);
  const getNode = useCallback2((id) => nodesRef.current.find((n) => n.id === id), []);
  return useMemo(
    () => ({
      nodes,
      edges,
      onNodesChange,
      onEdgesChange,
      setNodes,
      setEdges,
      updateNodeData,
      getNode
    }),
    [nodes, edges, onNodesChange, onEdgesChange, updateNodeData, getNode]
  );
}

// src/workflow/layout/dagre.ts
function createDagreEngine(dagre, defaults = {}) {
  const lib = resolveDagre(dagre);
  const defaultDir = defaults.direction ?? "TB";
  const defaultNodeSpacing = defaults.nodeSpacing ?? 60;
  const defaultRankSpacing = defaults.rankSpacing ?? 80;
  return {
    name: "dagre",
    async layout(nodes, edges, options) {
      const direction = options?.direction ?? defaultDir;
      const nodesep = options?.nodeSpacing ?? defaultNodeSpacing;
      const ranksep = options?.rankSpacing ?? defaultRankSpacing;
      const g = new lib.graphlib.Graph({ multigraph: true, compound: true });
      g.setGraph({
        rankdir: direction,
        nodesep,
        ranksep,
        ...options?.engine ?? {}
      });
      g.setDefaultEdgeLabel(() => ({}));
      for (const n of nodes) {
        if (n.hidden) continue;
        g.setNode(n.id, {
          width: n.width ?? DEFAULT_NODE_WIDTH,
          height: n.height ?? DEFAULT_NODE_HEIGHT,
          ...n.parentId ? { parent: n.parentId } : {}
        });
        if (n.parentId) {
          g.setParent(
            n.id,
            n.parentId
          );
        }
      }
      for (const e of edges) {
        if (e.hidden) continue;
        g.setEdge(e.source, e.target, { id: e.id });
      }
      lib.layout(g);
      const result = [];
      for (const id of g.nodes()) {
        const out = g.node(id);
        if (!out) continue;
        result.push({
          id,
          position: { x: out.x - out.width / 2, y: out.y - out.height / 2 }
        });
      }
      return result;
    }
  };
}
function resolveDagre(input) {
  const asAny = input;
  if (asAny && typeof asAny.layout === "function" && asAny.graphlib?.Graph) {
    return asAny;
  }
  if (asAny?.default && typeof asAny.default.layout === "function") {
    return asAny.default;
  }
  throw new Error(
    "[@octaviaflow/core/workflow] createDagreEngine: input does not look like the dagre module. Pass `dagre` (the default export) or `import * as dagre from 'dagre'`."
  );
}

// src/workflow/layout/elk.ts
var ELK_DIRECTION = {
  TB: "DOWN",
  BT: "UP",
  LR: "RIGHT",
  RL: "LEFT"
};
function createElkEngine(elk, defaults = {}) {
  const algorithm = defaults.algorithm ?? "layered";
  const defaultDir = defaults.direction ?? "TB";
  const defaultNodeSpacing = defaults.nodeSpacing ?? 60;
  const defaultRankSpacing = defaults.rankSpacing ?? 80;
  return {
    name: "elk",
    async layout(nodes, edges, options) {
      const direction = options?.direction ?? defaultDir;
      const nodeSpacing = options?.nodeSpacing ?? defaultNodeSpacing;
      const rankSpacing = options?.rankSpacing ?? defaultRankSpacing;
      const childrenByParent = /* @__PURE__ */ new Map();
      for (const n of nodes) {
        if (n.hidden) continue;
        const key = n.parentId ?? void 0;
        const list = childrenByParent.get(key);
        if (list) list.push(n);
        else childrenByParent.set(key, [n]);
      }
      const buildChildren = (parentId) => {
        const list = childrenByParent.get(parentId) ?? [];
        return list.map((n) => ({
          id: n.id,
          width: n.width ?? DEFAULT_NODE_WIDTH,
          height: n.height ?? DEFAULT_NODE_HEIGHT,
          children: childrenByParent.has(n.id) ? buildChildren(n.id) : void 0,
          layoutOptions: childrenByParent.has(n.id) ? {
            "elk.algorithm": algorithm,
            "elk.direction": ELK_DIRECTION[direction],
            "elk.spacing.nodeNode": String(nodeSpacing),
            "elk.layered.spacing.nodeNodeBetweenLayers": String(rankSpacing)
          } : void 0
        }));
      };
      const elkEdges = edges.filter((e) => !e.hidden).map((e) => ({
        id: e.id,
        sources: [e.source],
        targets: [e.target]
      }));
      const graph = {
        id: "root",
        layoutOptions: {
          "elk.algorithm": algorithm,
          "elk.direction": ELK_DIRECTION[direction],
          "elk.spacing.nodeNode": String(nodeSpacing),
          "elk.layered.spacing.nodeNodeBetweenLayers": String(rankSpacing),
          ...Object.fromEntries(
            Object.entries(options?.engine ?? {}).map(([k, v]) => [k, String(v)])
          )
        },
        children: buildChildren(void 0),
        edges: elkEdges
      };
      const laidOut = await elk.layout(graph);
      const result = [];
      const walk = (g, ox, oy) => {
        const ax = ox + (g.x ?? 0);
        const ay = oy + (g.y ?? 0);
        if (g.id !== "root") {
          result.push({ id: g.id, position: { x: ax, y: ay } });
        }
        if (g.children) {
          for (const c of g.children) walk(c, ax, ay);
        }
      };
      walk(laidOut, 0, 0);
      return result;
    }
  };
}

// src/workflow/store/selectors.ts
import { useMemo as useMemo2, useSyncExternalStore } from "react";
function useFlowSelector(selector, isEqual = Object.is) {
  void isEqual;
  const store = useFlowStore();
  return useSyncExternalStore(
    store.subscribe,
    () => selector(store.getSnapshot()),
    () => selector(store.getSnapshot())
  );
}
function useNodes() {
  return useFlowSelector((s) => s.nodes);
}
function useEdges() {
  return useFlowSelector((s) => s.edges);
}
function useViewport() {
  return useFlowSelector((s) => s.viewport);
}
function useNodeById(id) {
  return useFlowSelector((s) => s.nodes.find((n) => n.id === id));
}
function useNodeData(id) {
  return useFlowSelector(
    (s) => s.nodes.find((n) => n.id === id)?.data ?? void 0
  );
}
function useEdgeById(id) {
  return useFlowSelector((s) => s.edges.find((e) => e.id === id));
}
function useIsNodeSelected(id) {
  return useFlowSelector((s) => s.selectedNodeIds.has(id));
}
function useIsEdgeSelected(id) {
  return useFlowSelector((s) => s.selectedEdgeIds.has(id));
}
function useConnection() {
  return useFlowSelector((s) => s.connection);
}
function useSelection() {
  const nodes = useNodes();
  const edges = useEdges();
  const selectedNodeIds = useFlowSelector((s) => s.selectedNodeIds);
  const selectedEdgeIds = useFlowSelector((s) => s.selectedEdgeIds);
  return useMemo2(
    () => ({
      nodes: nodes.filter((n) => selectedNodeIds.has(n.id)),
      edges: edges.filter((e) => selectedEdgeIds.has(e.id))
    }),
    [nodes, edges, selectedNodeIds, selectedEdgeIds]
  );
}

// src/workflow/components/ConfigPanel/ConfigPanel.tsx
import {
  useCallback as useCallback3,
  useEffect as useEffect2,
  useRef as useRef3,
  useState as useState3
} from "react";
import { jsx, jsxs } from "react/jsx-runtime";
var SAVE_STATE_LABEL = {
  clean: "Saved",
  dirty: "Unsaved changes",
  saving: "Saving\u2026",
  saved: "Saved",
  error: "Save failed"
};
var SAVE_STATE_DOT = {
  clean: "var(--ods-text-tertiary)",
  dirty: "var(--ods-status-warning, #d97706)",
  saving: "var(--ods-status-running, #2563eb)",
  saved: "var(--ods-status-success, #16a34a)",
  error: "var(--ods-status-failed, #dc2626)"
};
function ConfigPanel({
  open = true,
  title,
  kindLabel,
  icon,
  banner,
  tabs,
  activeTab: controlledActive,
  defaultActiveTab,
  onTabChange,
  saveState = "clean",
  onTitleChange,
  onClose,
  onSave,
  onCancel,
  resizable = true,
  defaultWidth = 360,
  minWidth = 280,
  maxWidth = 720,
  maxWidthContainerInset = 80,
  variant = "pinned",
  children,
  footer,
  className,
  style
}) {
  const [width, setWidth] = useState3(defaultWidth);
  const [effectiveMax, setEffectiveMax] = useState3(maxWidth);
  const panelRef = useRef3(null);
  useEffect2(() => {
    const el = panelRef.current;
    if (!el) return;
    const parent = el.parentElement;
    if (!parent || typeof ResizeObserver === "undefined") return;
    const update = () => {
      const room = parent.clientWidth - maxWidthContainerInset;
      const next = Math.max(minWidth, Math.min(maxWidth, room));
      setEffectiveMax(next);
      setWidth((w) => w > next ? next : w);
    };
    update();
    const ro = new ResizeObserver(update);
    ro.observe(parent);
    return () => ro.disconnect();
  }, [maxWidth, minWidth, maxWidthContainerInset]);
  const [internalActive, setInternalActive] = useState3(
    defaultActiveTab ?? tabs?.[0]?.id
  );
  const activeTabId = controlledActive ?? internalActive;
  const selectTab = (id) => {
    if (controlledActive === void 0) setInternalActive(id);
    onTabChange?.(id);
  };
  const [editingTitle, setEditingTitle] = useState3(false);
  const [draftTitle, setDraftTitle] = useState3(title ?? "");
  useEffect2(() => {
    if (!editingTitle) setDraftTitle(title ?? "");
  }, [title, editingTitle]);
  const commitTitle = (next) => {
    setEditingTitle(false);
    if (next !== title) onTitleChange?.(next);
  };
  const titleKeyDown = (e) => {
    if (e.key === "Enter") {
      e.preventDefault();
      commitTitle(draftTitle);
    } else if (e.key === "Escape") {
      e.preventDefault();
      setEditingTitle(false);
      setDraftTitle(title ?? "");
    }
  };
  const resizeRef = useRef3(null);
  const onResizeDown = useCallback3(
    (e) => {
      if (!resizable) return;
      e.preventDefault();
      e.stopPropagation();
      resizeRef.current = { pointerId: e.pointerId, startX: e.clientX, startWidth: width };
      e.target.setPointerCapture(e.pointerId);
    },
    [resizable, width]
  );
  const onResizeMove = (e) => {
    const r = resizeRef.current;
    if (!r || r.pointerId !== e.pointerId) return;
    const delta = r.startX - e.clientX;
    const next = Math.max(minWidth, Math.min(effectiveMax, r.startWidth + delta));
    setWidth(next);
  };
  const onResizeUp = (e) => {
    if (resizeRef.current?.pointerId === e.pointerId) resizeRef.current = null;
  };
  if (!open) return null;
  const activeTab = tabs?.find((t) => t.id === activeTabId);
  return /* @__PURE__ */ jsxs(
    "aside",
    {
      ref: panelRef,
      className: cn(
        "ods-flow-config-panel",
        `ods-flow-config-panel--${variant}`,
        editingTitle && "ods-flow-config-panel--editing-title",
        className
      ),
      style: { width, ...style },
      "aria-label": title ?? "Configuration",
      children: [
        resizable && /* @__PURE__ */ jsx(
          "div",
          {
            className: "ods-flow-config-panel__resize",
            onPointerDown: onResizeDown,
            onPointerMove: onResizeMove,
            onPointerUp: onResizeUp,
            onPointerCancel: onResizeUp,
            "aria-hidden": "true"
          }
        ),
        /* @__PURE__ */ jsxs("header", { className: "ods-flow-config-panel__header", children: [
          kindLabel && /* @__PURE__ */ jsx("span", { className: "ods-flow-config-panel__breadcrumb", children: kindLabel }),
          /* @__PURE__ */ jsxs("div", { className: "ods-flow-config-panel__title-row", children: [
            icon && /* @__PURE__ */ jsx("span", { className: "ods-flow-config-panel__icon", children: icon }),
            editingTitle ? /* @__PURE__ */ jsx(
              "input",
              {
                className: "ods-flow-config-panel__title-input",
                value: draftTitle,
                autoFocus: true,
                onChange: (e) => setDraftTitle(e.target.value),
                onBlur: () => commitTitle(draftTitle),
                onKeyDown: titleKeyDown,
                "aria-label": "Edit title"
              }
            ) : /* @__PURE__ */ jsx(
              "button",
              {
                type: "button",
                className: "ods-flow-config-panel__title",
                onDoubleClick: () => {
                  if (!onTitleChange) return;
                  setDraftTitle(title ?? "");
                  setEditingTitle(true);
                },
                title: onTitleChange ? "Double-click to rename" : void 0,
                children: title ?? "Untitled"
              }
            ),
            onClose && /* @__PURE__ */ jsx(
              "button",
              {
                type: "button",
                className: "ods-flow-config-panel__close",
                onClick: onClose,
                "aria-label": "Close panel",
                children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
                  "path",
                  {
                    d: "M3 3l8 8M11 3l-8 8",
                    stroke: "currentColor",
                    strokeWidth: "1.5",
                    strokeLinecap: "round"
                  }
                ) })
              }
            )
          ] })
        ] }),
        tabs && tabs.length > 1 && /* @__PURE__ */ jsx("nav", { className: "ods-flow-config-panel__tabs", role: "tablist", children: tabs.map((t) => /* @__PURE__ */ jsxs(
          "button",
          {
            type: "button",
            role: "tab",
            "aria-selected": activeTabId === t.id,
            disabled: t.disabled,
            className: cn(
              "ods-flow-config-panel__tab",
              activeTabId === t.id && "ods-flow-config-panel__tab--active"
            ),
            onClick: () => selectTab(t.id),
            children: [
              /* @__PURE__ */ jsx("span", { children: t.label }),
              t.badge !== void 0 && t.badge !== null && /* @__PURE__ */ jsx("span", { className: "ods-flow-config-panel__tab-badge", children: t.badge })
            ]
          },
          t.id
        )) }),
        banner && /* @__PURE__ */ jsx("div", { className: "ods-flow-config-panel__banner", children: banner }),
        /* @__PURE__ */ jsx("div", { className: "ods-flow-config-panel__body", role: "tabpanel", children: tabs ? activeTab?.content : children }),
        /* @__PURE__ */ jsxs("footer", { className: "ods-flow-config-panel__footer", children: [
          /* @__PURE__ */ jsxs(
            "span",
            {
              className: cn(
                "ods-flow-config-panel__save-state",
                `ods-flow-config-panel__save-state--${saveState}`
              ),
              children: [
                /* @__PURE__ */ jsx(
                  "span",
                  {
                    className: "ods-flow-config-panel__save-dot",
                    style: { background: SAVE_STATE_DOT[saveState] },
                    "aria-hidden": "true"
                  }
                ),
                SAVE_STATE_LABEL[saveState]
              ]
            }
          ),
          footer ?? /* @__PURE__ */ jsxs("div", { className: "ods-flow-config-panel__footer-actions", children: [
            onCancel && /* @__PURE__ */ jsx(
              "button",
              {
                type: "button",
                className: "ods-flow-config-panel__btn ods-flow-config-panel__btn--ghost",
                onClick: onCancel,
                children: "Cancel"
              }
            ),
            onSave && /* @__PURE__ */ jsx(
              "button",
              {
                type: "button",
                className: "ods-flow-config-panel__btn ods-flow-config-panel__btn--primary",
                onClick: onSave,
                disabled: saveState === "saving" || saveState === "clean",
                children: saveState === "saving" ? "Saving\u2026" : "Save"
              }
            )
          ] })
        ] })
      ]
    }
  );
}

// src/workflow/components/NodeResizer/NodeResizer.tsx
import { useRef as useRef4 } from "react";
import { jsx as jsx2 } from "react/jsx-runtime";
function NodeResizer({
  isVisible,
  minWidth = 80,
  minHeight = 60,
  maxWidth,
  maxHeight,
  keepAspectRatio = false,
  onResize,
  onResizeEnd,
  color
}) {
  const { node, selected } = useFlowNodeContext();
  const viewport = useViewport();
  const flow = useFlow();
  const dragRef = useRef4(null);
  const show = isVisible ?? selected;
  if (!show) return null;
  const beginResize = (e, corner) => {
    e.preventDefault();
    e.stopPropagation();
    e.target.setPointerCapture(e.pointerId);
    const w = node.width ?? DEFAULT_NODE_WIDTH;
    const h = node.height ?? DEFAULT_NODE_HEIGHT;
    dragRef.current = {
      pointerId: e.pointerId,
      corner,
      startClientX: e.clientX,
      startClientY: e.clientY,
      startWidth: w,
      startHeight: h,
      startX: node.position.x,
      startY: node.position.y,
      aspect: w / Math.max(1, h)
    };
  };
  const onMove = (e) => {
    const drag = dragRef.current;
    if (!drag || drag.pointerId !== e.pointerId) return;
    const dx = (e.clientX - drag.startClientX) / viewport.zoom;
    const dy = (e.clientY - drag.startClientY) / viewport.zoom;
    let nextW = drag.startWidth;
    let nextH = drag.startHeight;
    let nextX = drag.startX;
    let nextY = drag.startY;
    switch (drag.corner) {
      case "se":
        nextW = drag.startWidth + dx;
        nextH = drag.startHeight + dy;
        break;
      case "sw":
        nextW = drag.startWidth - dx;
        nextH = drag.startHeight + dy;
        nextX = drag.startX + dx;
        break;
      case "ne":
        nextW = drag.startWidth + dx;
        nextH = drag.startHeight - dy;
        nextY = drag.startY + dy;
        break;
      case "nw":
        nextW = drag.startWidth - dx;
        nextH = drag.startHeight - dy;
        nextX = drag.startX + dx;
        nextY = drag.startY + dy;
        break;
    }
    if (keepAspectRatio) {
      nextH = nextW / drag.aspect;
      if (drag.corner === "nw" || drag.corner === "ne") {
        nextY = drag.startY + (drag.startHeight - nextH);
      }
    }
    nextW = Math.max(minWidth, maxWidth ? Math.min(maxWidth, nextW) : nextW);
    nextH = Math.max(minHeight, maxHeight ? Math.min(maxHeight, nextH) : nextH);
    flow.updateNode(node.id, {
      width: nextW,
      height: nextH,
      position: { x: nextX, y: nextY }
    });
    onResize?.({ width: nextW, height: nextH });
  };
  const onUp = (e) => {
    if (dragRef.current?.pointerId === e.pointerId) {
      const cur = flow.getNode(node.id);
      if (cur) {
        onResizeEnd?.({
          width: cur.width ?? DEFAULT_NODE_WIDTH,
          height: cur.height ?? DEFAULT_NODE_HEIGHT
        });
      }
      dragRef.current = null;
    }
  };
  const handleColor = color ?? "var(--ods-accent)";
  const handleStyle = (corner) => {
    const base = {
      position: "absolute",
      width: 12,
      height: 12,
      background: "var(--ods-surface-canvas)",
      border: `2px solid ${handleColor}`,
      borderRadius: 2,
      cursor: cursorFor(corner),
      touchAction: "none",
      // Place each handle so its CENTRE sits on the corresponding corner.
      transform: "translate(-50%, -50%)"
    };
    switch (corner) {
      case "nw":
        return { ...base, top: 0, left: 0 };
      case "ne":
        return { ...base, top: 0, left: "100%" };
      case "sw":
        return { ...base, top: "100%", left: 0 };
      case "se":
        return { ...base, top: "100%", left: "100%" };
    }
  };
  return /* @__PURE__ */ jsx2("div", { className: cn("ods-node-resizer"), "data-flow-no-drag": "true", children: ["nw", "ne", "sw", "se"].map((corner) => /* @__PURE__ */ jsx2(
    "div",
    {
      style: handleStyle(corner),
      onPointerDown: (e) => beginResize(e, corner),
      onPointerMove: onMove,
      onPointerUp: onUp,
      onPointerCancel: onUp,
      "aria-label": `Resize ${corner}`
    },
    corner
  )) });
}
function cursorFor(corner) {
  switch (corner) {
    case "nw":
    case "se":
      return "nwse-resize";
    case "ne":
    case "sw":
      return "nesw-resize";
  }
}

// src/workflow/components/NodeToolbar/NodeToolbar.tsx
import { jsx as jsx3 } from "react/jsx-runtime";
function NodeToolbar({
  isVisible,
  position = "top",
  offset = 10,
  align = "center",
  children,
  className,
  style
}) {
  const node = useFlowNodeContext();
  const viewport = useViewport();
  const show = isVisible ?? node.selected;
  if (!show) return null;
  const inverseScale = 1 / viewport.zoom;
  return /* @__PURE__ */ jsx3(
    "div",
    {
      className: cn("ods-node-toolbar", `ods-node-toolbar--${position}`, className),
      "data-flow-no-drag": "true",
      style: {
        ...positionStyle(position, offset, align),
        ...style
      },
      onPointerDown: (e) => e.stopPropagation(),
      onMouseDown: (e) => e.stopPropagation(),
      onClick: (e) => e.stopPropagation(),
      children: /* @__PURE__ */ jsx3(
        "div",
        {
          className: "ods-node-toolbar__inner",
          style: {
            transform: `scale(${inverseScale})`,
            transformOrigin: transformOriginFor(position, align)
          },
          children
        }
      )
    }
  );
}
function positionStyle(side, offset, align) {
  const center = align === "center";
  const start = align === "start";
  switch (side) {
    case "top":
      return {
        position: "absolute",
        bottom: "100%",
        marginBottom: offset,
        left: start ? 0 : center ? "50%" : void 0,
        right: align === "end" ? 0 : void 0,
        transform: center ? "translateX(-50%)" : void 0
      };
    case "bottom":
      return {
        position: "absolute",
        top: "100%",
        marginTop: offset,
        left: start ? 0 : center ? "50%" : void 0,
        right: align === "end" ? 0 : void 0,
        transform: center ? "translateX(-50%)" : void 0
      };
    case "left":
      return {
        position: "absolute",
        right: "100%",
        marginRight: offset,
        top: start ? 0 : center ? "50%" : void 0,
        bottom: align === "end" ? 0 : void 0,
        transform: center ? "translateY(-50%)" : void 0
      };
    case "right":
      return {
        position: "absolute",
        left: "100%",
        marginLeft: offset,
        top: start ? 0 : center ? "50%" : void 0,
        bottom: align === "end" ? 0 : void 0,
        transform: center ? "translateY(-50%)" : void 0
      };
  }
}
function transformOriginFor(side, align) {
  if (side === "top")
    return align === "start" ? "left bottom" : align === "end" ? "right bottom" : "center bottom";
  if (side === "bottom")
    return align === "start" ? "left top" : align === "end" ? "right top" : "center top";
  if (side === "left")
    return align === "start" ? "right top" : align === "end" ? "right bottom" : "right center";
  return align === "start" ? "left top" : align === "end" ? "left bottom" : "left center";
}

// src/workflow/utils/collapse.ts
function collapseFor(nodes, edges) {
  const collapsedAncestorOf = /* @__PURE__ */ new Map();
  const parents = /* @__PURE__ */ new Map();
  for (const n of nodes) if (n.parentId) parents.set(n.id, n.parentId);
  const isCollapsed = (node) => {
    const d = node?.data;
    return !!d?.collapsed;
  };
  for (const n of nodes) {
    let cursor = n.id;
    let outermostCollapsed;
    const seen = /* @__PURE__ */ new Set([cursor]);
    while (cursor) {
      const p = parents.get(cursor);
      if (!p || seen.has(p)) break;
      seen.add(p);
      const parentNode = nodes.find((x) => x.id === p);
      if (isCollapsed(parentNode)) outermostCollapsed = p;
      cursor = p;
    }
    if (outermostCollapsed && outermostCollapsed !== n.id) {
      collapsedAncestorOf.set(n.id, outermostCollapsed);
    }
  }
  const outNodes = nodes.map((n) => {
    const anc = collapsedAncestorOf.get(n.id);
    if (anc) return { ...n, hidden: true };
    return n;
  });
  const BOUNDARY_DASH = "2 4";
  const outEdges = [];
  const hiddenCount = /* @__PURE__ */ new Map();
  for (const e of edges) {
    const srcAnc = collapsedAncestorOf.get(e.source);
    const tgtAnc = collapsedAncestorOf.get(e.target);
    if (srcAnc && tgtAnc && srcAnc === tgtAnc) continue;
    let src = e.source;
    let srcHandle = e.sourceHandle;
    let tgt = e.target;
    let tgtHandle = e.targetHandle;
    const rewritten = !!(srcAnc || tgtAnc);
    if (srcAnc) {
      src = srcAnc;
      srcHandle = "__group_out";
    }
    if (tgtAnc) {
      tgt = tgtAnc;
      tgtHandle = "__group_in";
    }
    if (src === tgt) continue;
    const nextEdge = {
      ...e,
      source: src,
      sourceHandle: srcHandle,
      target: tgt,
      targetHandle: tgtHandle
    };
    if (rewritten) {
      nextEdge.style = { ...e.style ?? {}, strokeDasharray: BOUNDARY_DASH };
    }
    outEdges.push(nextEdge);
  }
  for (const [_id, anc] of collapsedAncestorOf) {
    hiddenCount.set(anc, (hiddenCount.get(anc) ?? 0) + 1);
  }
  return { nodes: outNodes, edges: outEdges, hiddenCountByGroupId: hiddenCount };
}
function toggleGroupCollapse(groupId, nodes) {
  return nodes.map((n) => {
    if (n.id !== groupId) return n;
    const data = n.data ?? {};
    return { ...n, data: { ...data, collapsed: !data.collapsed } };
  });
}
export {
  ActionNode,
  ConditionNode,
  ConfigPanel,
  DEFAULT_NODE_HEIGHT,
  DEFAULT_NODE_KINDS,
  DEFAULT_NODE_WIDTH,
  ErrorNode,
  FlowCanvas,
  FlowEdge,
  FlowNode,
  ForEachNode,
  GroupNode,
  Handle,
  HttpRequestNode,
  NodeResizer,
  NodeToolbar,
  OutputNode,
  ParallelNode,
  StickyNode,
  TriggerNode,
  WaitNode,
  WebhookNode,
  applyEdgeChanges,
  applyNodeChanges,
  bezierPath2 as bezierPath,
  bezierPath as bezierPathFn,
  buildEdgePath,
  buildNodeKindRegistry,
  change,
  clampToParentExtent,
  collapseFor,
  createDagreEngine,
  createElkEngine,
  descendantsOf,
  findAncestor,
  findContainingGroup,
  flowToScreen,
  handleCentre,
  screenToFlow,
  smoothStepPath,
  stepPath,
  straightPath,
  toggleGroupCollapse,
  useAutoLayout,
  useConnection,
  useEdgeById,
  useEdges,
  useFlow,
  useFlowNodeContext,
  useFlowSelector,
  useFlowState,
  useIsEdgeSelected,
  useIsNodeSelected,
  useNodeById,
  useNodeData,
  useNodes,
  useSelection,
  useViewport
};
//# sourceMappingURL=workflow.js.map