"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);

// src/hooks/index.ts
var index_exports = {};
__export(index_exports, {
  FitToBoundsMode: () => FitToBoundsMode,
  useActionExecutor: () => useActionExecutor,
  useAnimatedLayout: () => useAnimatedLayout,
  useArrowKeyNavigation: () => useArrowKeyNavigation,
  useCanvasDrag: () => useCanvasDrag,
  useCanvasGraph: () => useCanvasGraph,
  useCanvasHistory: () => useCanvasHistory,
  useCanvasSelection: () => useCanvasSelection,
  useCanvasSettings: () => useCanvasSettings,
  useCanvasViewport: () => useCanvasViewport,
  useCommandLine: () => useCommandLine,
  useFitToBounds: () => useFitToBounds,
  useForceLayout: () => useForceLayout,
  useGestureResolver: () => useGestureResolver,
  useGetGraphBounds: () => useGetGraphBounds,
  useGridLayout: () => useGridLayout,
  useLayout: () => useLayout,
  useNodeDrag: () => useNodeDrag,
  useNodeResize: () => useNodeResize,
  useNodeSelection: () => useNodeSelection,
  usePlugin: () => usePlugin,
  usePlugins: () => usePlugins,
  useSelectionBounds: () => useSelectionBounds,
  useSplitGesture: () => useSplitGesture,
  useTapGesture: () => useTapGesture,
  useTreeLayout: () => useTreeLayout,
  useVirtualization: () => useVirtualization,
  useZoomTransition: () => useZoomTransition
});
module.exports = __toCommonJS(index_exports);

// src/hooks/useNodeSelection.ts
var import_compiler_runtime = require("react/compiler-runtime");
var import_jotai2 = require("jotai");

// src/core/selection-store.ts
var import_jotai = require("jotai");

// src/utils/debug.ts
var import_debug = __toESM(require("debug"));
var NAMESPACE = "canvas";
function createDebug(module2) {
  const base = (0, import_debug.default)(`${NAMESPACE}:${module2}`);
  const warn = (0, import_debug.default)(`${NAMESPACE}:${module2}:warn`);
  const error = (0, import_debug.default)(`${NAMESPACE}:${module2}:error`);
  warn.enabled = true;
  error.enabled = true;
  warn.log = console.warn.bind(console);
  error.log = console.error.bind(console);
  const debugFn = Object.assign(base, {
    warn,
    error
  });
  return debugFn;
}
var debug = {
  graph: {
    node: createDebug("graph:node"),
    edge: createDebug("graph:edge"),
    sync: createDebug("graph:sync")
  },
  ui: {
    selection: createDebug("ui:selection"),
    drag: createDebug("ui:drag"),
    resize: createDebug("ui:resize")
  },
  sync: {
    status: createDebug("sync:status"),
    mutations: createDebug("sync:mutations"),
    queue: createDebug("sync:queue")
  },
  viewport: createDebug("viewport")
};

// src/core/selection-store.ts
var debug2 = createDebug("selection");
var selectedNodeIdsAtom = (0, import_jotai.atom)(/* @__PURE__ */ new Set());
var selectedEdgeIdAtom = (0, import_jotai.atom)(null);
var handleNodePointerDownSelectionAtom = (0, import_jotai.atom)(null, (get, set, {
  nodeId,
  isShiftPressed
}) => {
  const currentSelection = get(selectedNodeIdsAtom);
  debug2("handleNodePointerDownSelection: nodeId=%s, shift=%s, current=%o", nodeId, isShiftPressed, Array.from(currentSelection));
  set(selectedEdgeIdAtom, null);
  if (isShiftPressed) {
    const newSelection = new Set(currentSelection);
    if (newSelection.has(nodeId)) {
      newSelection.delete(nodeId);
    } else {
      newSelection.add(nodeId);
    }
    debug2("Shift-click, setting selection to: %o", Array.from(newSelection));
    set(selectedNodeIdsAtom, newSelection);
  } else {
    if (!currentSelection.has(nodeId)) {
      debug2("Node not in selection, selecting: %s", nodeId);
      set(selectedNodeIdsAtom, /* @__PURE__ */ new Set([nodeId]));
    } else {
      debug2("Node already selected, preserving multi-select");
    }
  }
});
var selectSingleNodeAtom = (0, import_jotai.atom)(null, (get, set, nodeId) => {
  debug2("selectSingleNode: %s", nodeId);
  set(selectedEdgeIdAtom, null);
  if (nodeId === null || nodeId === void 0) {
    debug2("Clearing selection");
    set(selectedNodeIdsAtom, /* @__PURE__ */ new Set());
  } else {
    const currentSelection = get(selectedNodeIdsAtom);
    if (currentSelection.has(nodeId) && currentSelection.size === 1) {
      return;
    }
    set(selectedNodeIdsAtom, /* @__PURE__ */ new Set([nodeId]));
  }
});
var toggleNodeInSelectionAtom = (0, import_jotai.atom)(null, (get, set, nodeId) => {
  const currentSelection = get(selectedNodeIdsAtom);
  const newSelection = new Set(currentSelection);
  if (newSelection.has(nodeId)) {
    newSelection.delete(nodeId);
  } else {
    newSelection.add(nodeId);
  }
  set(selectedNodeIdsAtom, newSelection);
});
var clearSelectionAtom = (0, import_jotai.atom)(null, (_get, set) => {
  debug2("clearSelection");
  set(selectedNodeIdsAtom, /* @__PURE__ */ new Set());
});
var addNodesToSelectionAtom = (0, import_jotai.atom)(null, (get, set, nodeIds) => {
  const currentSelection = get(selectedNodeIdsAtom);
  const newSelection = new Set(currentSelection);
  for (const nodeId of nodeIds) {
    newSelection.add(nodeId);
  }
  set(selectedNodeIdsAtom, newSelection);
});
var removeNodesFromSelectionAtom = (0, import_jotai.atom)(null, (get, set, nodeIds) => {
  const currentSelection = get(selectedNodeIdsAtom);
  const newSelection = new Set(currentSelection);
  for (const nodeId of nodeIds) {
    newSelection.delete(nodeId);
  }
  set(selectedNodeIdsAtom, newSelection);
});
var selectEdgeAtom = (0, import_jotai.atom)(null, (get, set, edgeId) => {
  set(selectedEdgeIdAtom, edgeId);
  if (edgeId !== null) {
    set(selectedNodeIdsAtom, /* @__PURE__ */ new Set());
  }
});
var clearEdgeSelectionAtom = (0, import_jotai.atom)(null, (_get, set) => {
  set(selectedEdgeIdAtom, null);
});
var focusedNodeIdAtom = (0, import_jotai.atom)(null);
var setFocusedNodeAtom = (0, import_jotai.atom)(null, (_get, set, nodeId) => {
  set(focusedNodeIdAtom, nodeId);
});
var hasFocusedNodeAtom = (0, import_jotai.atom)((get) => get(focusedNodeIdAtom) !== null);
var selectedNodesCountAtom = (0, import_jotai.atom)((get) => get(selectedNodeIdsAtom).size);
var hasSelectionAtom = (0, import_jotai.atom)((get) => get(selectedNodeIdsAtom).size > 0);

// src/hooks/useNodeSelection.ts
function useNodeSelection(nodeId) {
  const $ = (0, import_compiler_runtime.c)(13);
  const [selectedIds] = (0, import_jotai2.useAtom)(selectedNodeIdsAtom);
  const selectSingle = (0, import_jotai2.useSetAtom)(selectSingleNodeAtom);
  const toggleNode = (0, import_jotai2.useSetAtom)(toggleNodeInSelectionAtom);
  let t0;
  if ($[0] !== nodeId || $[1] !== selectedIds) {
    t0 = selectedIds.has(nodeId);
    $[0] = nodeId;
    $[1] = selectedIds;
    $[2] = t0;
  } else {
    t0 = $[2];
  }
  let t1;
  if ($[3] !== nodeId || $[4] !== selectSingle) {
    t1 = () => selectSingle(nodeId);
    $[3] = nodeId;
    $[4] = selectSingle;
    $[5] = t1;
  } else {
    t1 = $[5];
  }
  let t2;
  if ($[6] !== nodeId || $[7] !== toggleNode) {
    t2 = () => toggleNode(nodeId);
    $[6] = nodeId;
    $[7] = toggleNode;
    $[8] = t2;
  } else {
    t2 = $[8];
  }
  let t3;
  if ($[9] !== t0 || $[10] !== t1 || $[11] !== t2) {
    t3 = {
      isSelected: t0,
      selectNode: t1,
      toggleNode: t2
    };
    $[9] = t0;
    $[10] = t1;
    $[11] = t2;
    $[12] = t3;
  } else {
    t3 = $[12];
  }
  return t3;
}

// src/hooks/useNodeDrag.ts
var import_compiler_runtime2 = require("react/compiler-runtime");
var import_jotai16 = require("jotai");
var import_react = require("@use-gesture/react");
var import_react2 = require("react");

// src/core/graph-store.ts
var import_jotai3 = require("jotai");
var import_graphology = __toESM(require("graphology"));
var graphOptions = {
  type: "directed",
  multi: true,
  allowSelfLoops: true
};
var currentGraphIdAtom = (0, import_jotai3.atom)(null);
var graphAtom = (0, import_jotai3.atom)(new import_graphology.default(graphOptions));
var graphUpdateVersionAtom = (0, import_jotai3.atom)(0);
var edgeCreationAtom = (0, import_jotai3.atom)({
  isCreating: false,
  sourceNodeId: null,
  sourceNodePosition: null,
  targetPosition: null,
  hoveredTargetNodeId: null,
  sourceHandle: null,
  targetHandle: null,
  sourcePort: null,
  targetPort: null,
  snappedTargetPosition: null
});
var draggingNodeIdAtom = (0, import_jotai3.atom)(null);
var preDragNodeAttributesAtom = (0, import_jotai3.atom)(null);

// src/core/graph-position.ts
var import_jotai5 = require("jotai");
var import_jotai_family = require("jotai-family");
var import_graphology2 = __toESM(require("graphology"));

// src/utils/mutation-queue.ts
var pendingNodeMutations = /* @__PURE__ */ new Map();
function getPendingState(nodeId) {
  let state = pendingNodeMutations.get(nodeId);
  if (!state) {
    state = {
      inFlight: false,
      queuedPosition: null,
      queuedUiProperties: null,
      graphId: null
    };
    pendingNodeMutations.set(nodeId, state);
  }
  return state;
}
function clearAllPendingMutations() {
  pendingNodeMutations.clear();
}

// src/core/perf.ts
var import_jotai4 = require("jotai");
var perfEnabledAtom = (0, import_jotai4.atom)(false);
var _enabled = false;
function setPerfEnabled(enabled) {
  _enabled = enabled;
}
if (typeof window !== "undefined") {
  window.__canvasPerf = setPerfEnabled;
}
function canvasMark(name) {
  if (!_enabled) return _noop;
  const markName = `canvas:${name}`;
  try {
    performance.mark(markName);
  } catch {
    return _noop;
  }
  return () => {
    try {
      performance.measure(`canvas:${name}`, markName);
    } catch {
    }
  };
}
function _noop() {
}

// src/core/graph-position.ts
var debug3 = createDebug("graph:position");
var _positionCacheByGraph = /* @__PURE__ */ new WeakMap();
function getPositionCache(graph) {
  let cache = _positionCacheByGraph.get(graph);
  if (!cache) {
    cache = /* @__PURE__ */ new Map();
    _positionCacheByGraph.set(graph, cache);
  }
  return cache;
}
var nodePositionUpdateCounterAtom = (0, import_jotai5.atom)(0);
var nodePositionAtomFamily = (0, import_jotai_family.atomFamily)((nodeId) => (0, import_jotai5.atom)((get) => {
  get(nodePositionUpdateCounterAtom);
  const graph = get(graphAtom);
  if (!graph.hasNode(nodeId)) {
    return {
      x: 0,
      y: 0
    };
  }
  const x = graph.getNodeAttribute(nodeId, "x");
  const y = graph.getNodeAttribute(nodeId, "y");
  const cache = getPositionCache(graph);
  const prev = cache.get(nodeId);
  if (prev && prev.x === x && prev.y === y) {
    return prev;
  }
  const pos = {
    x,
    y
  };
  cache.set(nodeId, pos);
  return pos;
}));
var updateNodePositionAtom = (0, import_jotai5.atom)(null, (get, set, {
  nodeId,
  position
}) => {
  const end = canvasMark("drag-frame");
  const graph = get(graphAtom);
  if (graph.hasNode(nodeId)) {
    debug3("Updating node %s position to %o", nodeId, position);
    graph.setNodeAttribute(nodeId, "x", position.x);
    graph.setNodeAttribute(nodeId, "y", position.y);
    set(nodePositionUpdateCounterAtom, (c) => c + 1);
  }
  end();
});
var cleanupNodePositionAtom = (0, import_jotai5.atom)(null, (get, _set, nodeId) => {
  nodePositionAtomFamily.remove(nodeId);
  const graph = get(graphAtom);
  getPositionCache(graph).delete(nodeId);
  debug3("Removed position atom for node: %s", nodeId);
});
var cleanupAllNodePositionsAtom = (0, import_jotai5.atom)(null, (get, _set) => {
  const graph = get(graphAtom);
  const nodeIds = graph.nodes();
  nodeIds.forEach((nodeId) => {
    nodePositionAtomFamily.remove(nodeId);
  });
  _positionCacheByGraph.delete(graph);
  debug3("Removed %d position atoms", nodeIds.length);
});
var clearGraphOnSwitchAtom = (0, import_jotai5.atom)(null, (get, set) => {
  debug3("Clearing graph for switch");
  set(cleanupAllNodePositionsAtom);
  clearAllPendingMutations();
  const emptyGraph = new import_graphology2.default(graphOptions);
  set(graphAtom, emptyGraph);
  set(graphUpdateVersionAtom, (v) => v + 1);
});

// src/core/graph-mutations.ts
var import_jotai13 = require("jotai");
var import_graphology3 = __toESM(require("graphology"));

// src/core/graph-derived.ts
var import_jotai9 = require("jotai");
var import_jotai_family2 = require("jotai-family");

// src/core/viewport-store.ts
var import_jotai6 = require("jotai");

// src/utils/layout.ts
var FitToBoundsMode = /* @__PURE__ */ (function(FitToBoundsMode2) {
  FitToBoundsMode2["Graph"] = "graph";
  FitToBoundsMode2["Selection"] = "selection";
  return FitToBoundsMode2;
})({});
var calculateBounds = (nodes) => {
  if (nodes.length === 0) {
    return {
      x: 0,
      y: 0,
      width: 0,
      height: 0
    };
  }
  const minX = Math.min(...nodes.map((node) => node.x));
  const minY = Math.min(...nodes.map((node) => node.y));
  const maxX = Math.max(...nodes.map((node) => node.x + node.width));
  const maxY = Math.max(...nodes.map((node) => node.y + node.height));
  return {
    x: minX,
    y: minY,
    width: maxX - minX,
    height: maxY - minY
  };
};
function getNodeCenter(node) {
  return {
    x: node.x + node.width / 2,
    y: node.y + node.height / 2
  };
}
function checkNodesOverlap(node1, node2) {
  const center1 = getNodeCenter(node1);
  const center2 = getNodeCenter(node2);
  const dx = Math.abs(center1.x - center2.x);
  const dy = Math.abs(center1.y - center2.y);
  const minDistanceX = (node1.width + node2.width) / 2;
  const minDistanceY = (node1.height + node2.height) / 2;
  return dx < minDistanceX && dy < minDistanceY;
}

// src/core/viewport-store.ts
var zoomAtom = (0, import_jotai6.atom)(1);
var panAtom = (0, import_jotai6.atom)({
  x: 0,
  y: 0
});
var viewportRectAtom = (0, import_jotai6.atom)(null);
var screenToWorldAtom = (0, import_jotai6.atom)((get) => {
  return (screenX, screenY) => {
    const pan = get(panAtom);
    const zoom = get(zoomAtom);
    const rect = get(viewportRectAtom);
    if (!rect) {
      return {
        x: screenX,
        y: screenY
      };
    }
    const relativeX = screenX - rect.left;
    const relativeY = screenY - rect.top;
    return {
      x: (relativeX - pan.x) / zoom,
      y: (relativeY - pan.y) / zoom
    };
  };
});
var worldToScreenAtom = (0, import_jotai6.atom)((get) => {
  return (worldX, worldY) => {
    const pan = get(panAtom);
    const zoom = get(zoomAtom);
    const rect = get(viewportRectAtom);
    if (!rect) {
      return {
        x: worldX,
        y: worldY
      };
    }
    return {
      x: worldX * zoom + pan.x + rect.left,
      y: worldY * zoom + pan.y + rect.top
    };
  };
});
var setZoomAtom = (0, import_jotai6.atom)(null, (get, set, {
  zoom,
  centerX,
  centerY
}) => {
  const currentZoom = get(zoomAtom);
  const pan = get(panAtom);
  const rect = get(viewportRectAtom);
  const newZoom = Math.max(0.1, Math.min(5, zoom));
  if (centerX !== void 0 && centerY !== void 0 && rect) {
    const relativeX = centerX - rect.left;
    const relativeY = centerY - rect.top;
    const worldX = (relativeX - pan.x) / currentZoom;
    const worldY = (relativeY - pan.y) / currentZoom;
    const newPanX = relativeX - worldX * newZoom;
    const newPanY = relativeY - worldY * newZoom;
    set(panAtom, {
      x: newPanX,
      y: newPanY
    });
  }
  set(zoomAtom, newZoom);
});
var resetViewportAtom = (0, import_jotai6.atom)(null, (_get, set) => {
  set(zoomAtom, 1);
  set(panAtom, {
    x: 0,
    y: 0
  });
});
var fitToBoundsAtom = (0, import_jotai6.atom)(null, (get, set, {
  mode,
  padding = 20
}) => {
  const normalizedMode = typeof mode === "string" ? mode === "graph" ? FitToBoundsMode.Graph : FitToBoundsMode.Selection : mode;
  const viewportSize = get(viewportRectAtom);
  if (!viewportSize || viewportSize.width <= 0 || viewportSize.height <= 0) return;
  get(nodePositionUpdateCounterAtom);
  let bounds;
  if (normalizedMode === FitToBoundsMode.Graph) {
    const graph = get(graphAtom);
    const nodes = graph.nodes().map((node) => {
      const attrs = graph.getNodeAttributes(node);
      return {
        x: attrs.x,
        y: attrs.y,
        width: attrs.width || 500,
        height: attrs.height || 500
      };
    });
    bounds = calculateBounds(nodes);
  } else {
    const selectedIds = get(selectedNodeIdsAtom);
    const allNodes = get(uiNodesAtom);
    const selectedNodes = allNodes.filter((n) => selectedIds.has(n.id)).map((n) => ({
      x: n.position.x,
      y: n.position.y,
      width: n.width ?? 500,
      height: n.height ?? 500
    }));
    bounds = calculateBounds(selectedNodes);
  }
  if (bounds.width <= 0 || bounds.height <= 0) return;
  const maxHPad = Math.max(0, viewportSize.width / 2 - 1);
  const maxVPad = Math.max(0, viewportSize.height / 2 - 1);
  const safePadding = Math.max(0, Math.min(padding, maxHPad, maxVPad));
  const effW = Math.max(1, viewportSize.width - 2 * safePadding);
  const effH = Math.max(1, viewportSize.height - 2 * safePadding);
  const scale = Math.min(effW / bounds.width, effH / bounds.height);
  if (scale <= 0 || !isFinite(scale)) return;
  set(zoomAtom, scale);
  const scaledW = bounds.width * scale;
  const scaledH = bounds.height * scale;
  const startX = safePadding + (effW - scaledW) / 2;
  const startY = safePadding + (effH - scaledH) / 2;
  set(panAtom, {
    x: startX - bounds.x * scale,
    y: startY - bounds.y * scale
  });
});
var centerOnNodeAtom = (0, import_jotai6.atom)(null, (get, set, nodeId) => {
  const nodes = get(uiNodesAtom);
  const node = nodes.find((n) => n.id === nodeId);
  if (!node) return;
  const {
    x,
    y,
    width = 200,
    height = 100
  } = node;
  const zoom = get(zoomAtom);
  const centerX = x + width / 2;
  const centerY = y + height / 2;
  const rect = get(viewportRectAtom);
  const halfWidth = rect ? rect.width / 2 : 400;
  const halfHeight = rect ? rect.height / 2 : 300;
  set(panAtom, {
    x: halfWidth - centerX * zoom,
    y: halfHeight - centerY * zoom
  });
});
var ZOOM_TRANSITION_THRESHOLD = 3.5;
var ZOOM_EXIT_THRESHOLD = 2;
var zoomFocusNodeIdAtom = (0, import_jotai6.atom)(null);
var zoomTransitionProgressAtom = (0, import_jotai6.atom)(0);
var isZoomTransitioningAtom = (0, import_jotai6.atom)((get) => {
  const progress = get(zoomTransitionProgressAtom);
  return progress > 0 && progress < 1;
});
var zoomAnimationTargetAtom = (0, import_jotai6.atom)(null);
var animateZoomToNodeAtom = (0, import_jotai6.atom)(null, (get, set, {
  nodeId,
  targetZoom,
  duration = 300
}) => {
  const nodes = get(uiNodesAtom);
  const node = nodes.find((n) => n.id === nodeId);
  if (!node) return;
  const {
    x,
    y,
    width = 200,
    height = 100
  } = node;
  const centerX = x + width / 2;
  const centerY = y + height / 2;
  const rect = get(viewportRectAtom);
  const halfWidth = rect ? rect.width / 2 : 400;
  const halfHeight = rect ? rect.height / 2 : 300;
  const finalZoom = targetZoom ?? get(zoomAtom);
  const targetPan = {
    x: halfWidth - centerX * finalZoom,
    y: halfHeight - centerY * finalZoom
  };
  set(zoomFocusNodeIdAtom, nodeId);
  set(zoomAnimationTargetAtom, {
    targetZoom: finalZoom,
    targetPan,
    startZoom: get(zoomAtom),
    startPan: {
      ...get(panAtom)
    },
    duration,
    startTime: performance.now()
  });
});
var animateFitToBoundsAtom = (0, import_jotai6.atom)(null, (get, set, {
  mode,
  padding = 20,
  duration = 300
}) => {
  const viewportSize = get(viewportRectAtom);
  if (!viewportSize || viewportSize.width <= 0 || viewportSize.height <= 0) return;
  get(nodePositionUpdateCounterAtom);
  let bounds;
  if (mode === "graph") {
    const graph = get(graphAtom);
    const nodes = graph.nodes().map((node) => {
      const attrs = graph.getNodeAttributes(node);
      return {
        x: attrs.x,
        y: attrs.y,
        width: attrs.width || 500,
        height: attrs.height || 500
      };
    });
    bounds = calculateBounds(nodes);
  } else {
    const selectedIds = get(selectedNodeIdsAtom);
    const allNodes = get(uiNodesAtom);
    const selectedNodes = allNodes.filter((n) => selectedIds.has(n.id)).map((n) => ({
      x: n.position.x,
      y: n.position.y,
      width: n.width ?? 500,
      height: n.height ?? 500
    }));
    bounds = calculateBounds(selectedNodes);
  }
  if (bounds.width <= 0 || bounds.height <= 0) return;
  const safePadding = Math.max(0, Math.min(padding, viewportSize.width / 2 - 1, viewportSize.height / 2 - 1));
  const effW = Math.max(1, viewportSize.width - 2 * safePadding);
  const effH = Math.max(1, viewportSize.height - 2 * safePadding);
  const scale = Math.min(effW / bounds.width, effH / bounds.height);
  if (scale <= 0 || !isFinite(scale)) return;
  const scaledW = bounds.width * scale;
  const scaledH = bounds.height * scale;
  const startX = safePadding + (effW - scaledW) / 2;
  const startY = safePadding + (effH - scaledH) / 2;
  const targetPan = {
    x: startX - bounds.x * scale,
    y: startY - bounds.y * scale
  };
  set(zoomAnimationTargetAtom, {
    targetZoom: scale,
    targetPan,
    startZoom: get(zoomAtom),
    startPan: {
      ...get(panAtom)
    },
    duration,
    startTime: performance.now()
  });
});

// src/core/group-store.ts
var import_jotai8 = require("jotai");

// src/core/history-store.ts
var import_jotai7 = require("jotai");

// src/core/history-actions.ts
function applyDelta(graph, delta) {
  switch (delta.type) {
    case "move-node": {
      if (!graph.hasNode(delta.nodeId)) return false;
      graph.setNodeAttribute(delta.nodeId, "x", delta.to.x);
      graph.setNodeAttribute(delta.nodeId, "y", delta.to.y);
      return false;
    }
    case "resize-node": {
      if (!graph.hasNode(delta.nodeId)) return false;
      graph.setNodeAttribute(delta.nodeId, "width", delta.to.width);
      graph.setNodeAttribute(delta.nodeId, "height", delta.to.height);
      return false;
    }
    case "add-node": {
      if (graph.hasNode(delta.nodeId)) return false;
      graph.addNode(delta.nodeId, delta.attributes);
      return true;
    }
    case "remove-node": {
      if (!graph.hasNode(delta.nodeId)) return false;
      graph.dropNode(delta.nodeId);
      return true;
    }
    case "add-edge": {
      if (graph.hasEdge(delta.edgeId)) return false;
      if (!graph.hasNode(delta.source) || !graph.hasNode(delta.target)) return false;
      graph.addEdgeWithKey(delta.edgeId, delta.source, delta.target, delta.attributes);
      return true;
    }
    case "remove-edge": {
      if (!graph.hasEdge(delta.edgeId)) return false;
      graph.dropEdge(delta.edgeId);
      return true;
    }
    case "update-node-attr": {
      if (!graph.hasNode(delta.nodeId)) return false;
      graph.setNodeAttribute(delta.nodeId, delta.key, delta.to);
      return false;
    }
    case "batch": {
      let structuralChange = false;
      for (const d of delta.deltas) {
        if (applyDelta(graph, d)) structuralChange = true;
      }
      return structuralChange;
    }
    case "full-snapshot": {
      graph.clear();
      for (const node of delta.nodes) {
        graph.addNode(node.id, node.attributes);
      }
      for (const edge of delta.edges) {
        if (graph.hasNode(edge.source) && graph.hasNode(edge.target)) {
          graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge.attributes);
        }
      }
      return true;
    }
  }
}
function invertDelta(delta) {
  switch (delta.type) {
    case "move-node":
      return {
        ...delta,
        from: delta.to,
        to: delta.from
      };
    case "resize-node":
      return {
        ...delta,
        from: delta.to,
        to: delta.from
      };
    case "add-node":
      return {
        type: "remove-node",
        nodeId: delta.nodeId,
        attributes: delta.attributes,
        connectedEdges: []
      };
    case "remove-node": {
      const batch = [{
        type: "add-node",
        nodeId: delta.nodeId,
        attributes: delta.attributes
      }, ...delta.connectedEdges.map((e) => ({
        type: "add-edge",
        edgeId: e.id,
        source: e.source,
        target: e.target,
        attributes: e.attributes
      }))];
      return batch.length === 1 ? batch[0] : {
        type: "batch",
        deltas: batch
      };
    }
    case "add-edge":
      return {
        type: "remove-edge",
        edgeId: delta.edgeId,
        source: delta.source,
        target: delta.target,
        attributes: delta.attributes
      };
    case "remove-edge":
      return {
        type: "add-edge",
        edgeId: delta.edgeId,
        source: delta.source,
        target: delta.target,
        attributes: delta.attributes
      };
    case "update-node-attr":
      return {
        ...delta,
        from: delta.to,
        to: delta.from
      };
    case "batch":
      return {
        type: "batch",
        deltas: delta.deltas.map(invertDelta).reverse()
      };
    case "full-snapshot":
      return delta;
  }
}
function createSnapshot(graph, label) {
  const nodes = [];
  const edges = [];
  graph.forEachNode((nodeId, attributes) => {
    nodes.push({
      id: nodeId,
      attributes: {
        ...attributes
      }
    });
  });
  graph.forEachEdge((edgeId, attributes, source, target) => {
    edges.push({
      id: edgeId,
      source,
      target,
      attributes: {
        ...attributes
      }
    });
  });
  return {
    timestamp: Date.now(),
    label,
    nodes,
    edges
  };
}

// src/core/history-store.ts
var debug4 = createDebug("history");
var MAX_HISTORY_SIZE = 50;
var historyStateAtom = (0, import_jotai7.atom)({
  past: [],
  future: [],
  isApplying: false
});
var canUndoAtom = (0, import_jotai7.atom)((get) => {
  const history = get(historyStateAtom);
  return history.past.length > 0 && !history.isApplying;
});
var canRedoAtom = (0, import_jotai7.atom)((get) => {
  const history = get(historyStateAtom);
  return history.future.length > 0 && !history.isApplying;
});
var undoCountAtom = (0, import_jotai7.atom)((get) => get(historyStateAtom).past.length);
var redoCountAtom = (0, import_jotai7.atom)((get) => get(historyStateAtom).future.length);
var pushDeltaAtom = (0, import_jotai7.atom)(null, (get, set, delta) => {
  const history = get(historyStateAtom);
  if (history.isApplying) return;
  const {
    label,
    ...cleanDelta
  } = delta;
  const entry = {
    forward: cleanDelta,
    reverse: invertDelta(cleanDelta),
    timestamp: Date.now(),
    label
  };
  const newPast = [...history.past, entry];
  if (newPast.length > MAX_HISTORY_SIZE) newPast.shift();
  set(historyStateAtom, {
    past: newPast,
    future: [],
    // Clear redo stack
    isApplying: false
  });
  debug4("Pushed delta: %s (past: %d)", label || delta.type, newPast.length);
});
var pushHistoryAtom = (0, import_jotai7.atom)(null, (get, set, label) => {
  const history = get(historyStateAtom);
  if (history.isApplying) return;
  const graph = get(graphAtom);
  const snapshot = createSnapshot(graph, label);
  const forward = {
    type: "full-snapshot",
    nodes: snapshot.nodes,
    edges: snapshot.edges
  };
  const entry = {
    forward,
    reverse: forward,
    // For full snapshots, reverse IS the current state
    timestamp: Date.now(),
    label
  };
  const newPast = [...history.past, entry];
  if (newPast.length > MAX_HISTORY_SIZE) newPast.shift();
  set(historyStateAtom, {
    past: newPast,
    future: [],
    isApplying: false
  });
  debug4("Pushed snapshot: %s (past: %d)", label || "unnamed", newPast.length);
});
var undoAtom = (0, import_jotai7.atom)(null, (get, set) => {
  const history = get(historyStateAtom);
  if (history.past.length === 0 || history.isApplying) return false;
  set(historyStateAtom, {
    ...history,
    isApplying: true
  });
  try {
    const graph = get(graphAtom);
    const newPast = [...history.past];
    const entry = newPast.pop();
    let forwardForRedo = entry.forward;
    if (entry.reverse.type === "full-snapshot") {
      const currentSnapshot = createSnapshot(graph, "current");
      forwardForRedo = {
        type: "full-snapshot",
        nodes: currentSnapshot.nodes,
        edges: currentSnapshot.edges
      };
    }
    const structuralChange = applyDelta(graph, entry.reverse);
    if (structuralChange) {
      set(graphAtom, graph);
      set(graphUpdateVersionAtom, (v) => v + 1);
    }
    set(nodePositionUpdateCounterAtom, (c) => c + 1);
    const redoEntry = {
      forward: forwardForRedo,
      reverse: entry.reverse,
      timestamp: entry.timestamp,
      label: entry.label
    };
    set(historyStateAtom, {
      past: newPast,
      future: [redoEntry, ...history.future],
      isApplying: false
    });
    debug4("Undo: %s (past: %d, future: %d)", entry.label, newPast.length, history.future.length + 1);
    return true;
  } catch (error) {
    debug4.error("Undo failed: %O", error);
    set(historyStateAtom, {
      ...history,
      isApplying: false
    });
    return false;
  }
});
var redoAtom = (0, import_jotai7.atom)(null, (get, set) => {
  const history = get(historyStateAtom);
  if (history.future.length === 0 || history.isApplying) return false;
  set(historyStateAtom, {
    ...history,
    isApplying: true
  });
  try {
    const graph = get(graphAtom);
    const newFuture = [...history.future];
    const entry = newFuture.shift();
    let reverseForUndo = entry.reverse;
    if (entry.forward.type === "full-snapshot") {
      const currentSnapshot = createSnapshot(graph, "current");
      reverseForUndo = {
        type: "full-snapshot",
        nodes: currentSnapshot.nodes,
        edges: currentSnapshot.edges
      };
    }
    const structuralChange = applyDelta(graph, entry.forward);
    if (structuralChange) {
      set(graphAtom, graph);
      set(graphUpdateVersionAtom, (v) => v + 1);
    }
    set(nodePositionUpdateCounterAtom, (c) => c + 1);
    const undoEntry = {
      forward: entry.forward,
      reverse: reverseForUndo,
      timestamp: entry.timestamp,
      label: entry.label
    };
    set(historyStateAtom, {
      past: [...history.past, undoEntry],
      future: newFuture,
      isApplying: false
    });
    debug4("Redo: %s (past: %d, future: %d)", entry.label, history.past.length + 1, newFuture.length);
    return true;
  } catch (error) {
    debug4.error("Redo failed: %O", error);
    set(historyStateAtom, {
      ...history,
      isApplying: false
    });
    return false;
  }
});
var clearHistoryAtom = (0, import_jotai7.atom)(null, (_get, set) => {
  set(historyStateAtom, {
    past: [],
    future: [],
    isApplying: false
  });
  debug4("History cleared");
});
var historyLabelsAtom = (0, import_jotai7.atom)((get) => {
  const history = get(historyStateAtom);
  return {
    past: history.past.map((e) => e.label || "Unnamed"),
    future: history.future.map((e) => e.label || "Unnamed")
  };
});

// src/core/group-store.ts
var collapsedGroupsAtom = (0, import_jotai8.atom)(/* @__PURE__ */ new Set());
var toggleGroupCollapseAtom = (0, import_jotai8.atom)(null, (get, set, groupId) => {
  const current = get(collapsedGroupsAtom);
  const next = new Set(current);
  if (next.has(groupId)) {
    next.delete(groupId);
  } else {
    next.add(groupId);
  }
  set(collapsedGroupsAtom, next);
});
var collapseGroupAtom = (0, import_jotai8.atom)(null, (get, set, groupId) => {
  const current = get(collapsedGroupsAtom);
  if (!current.has(groupId)) {
    const next = new Set(current);
    next.add(groupId);
    set(collapsedGroupsAtom, next);
  }
});
var expandGroupAtom = (0, import_jotai8.atom)(null, (get, set, groupId) => {
  const current = get(collapsedGroupsAtom);
  if (current.has(groupId)) {
    const next = new Set(current);
    next.delete(groupId);
    set(collapsedGroupsAtom, next);
  }
});
var nodeChildrenAtom = (0, import_jotai8.atom)((get) => {
  get(graphUpdateVersionAtom);
  const graph = get(graphAtom);
  return (parentId) => {
    const children = [];
    graph.forEachNode((nodeId, attrs) => {
      if (attrs.parentId === parentId) {
        children.push(nodeId);
      }
    });
    return children;
  };
});
var nodeParentAtom = (0, import_jotai8.atom)((get) => {
  get(graphUpdateVersionAtom);
  const graph = get(graphAtom);
  return (nodeId) => {
    if (!graph.hasNode(nodeId)) return void 0;
    return graph.getNodeAttribute(nodeId, "parentId");
  };
});
var isGroupNodeAtom = (0, import_jotai8.atom)((get) => {
  const getChildren = get(nodeChildrenAtom);
  return (nodeId) => getChildren(nodeId).length > 0;
});
var groupChildCountAtom = (0, import_jotai8.atom)((get) => {
  const getChildren = get(nodeChildrenAtom);
  return (groupId) => getChildren(groupId).length;
});
var setNodeParentAtom = (0, import_jotai8.atom)(null, (get, set, {
  nodeId,
  parentId
}) => {
  const graph = get(graphAtom);
  if (!graph.hasNode(nodeId)) return;
  if (parentId) {
    if (parentId === nodeId) return;
    let current = parentId;
    while (current) {
      if (current === nodeId) return;
      if (!graph.hasNode(current)) break;
      current = graph.getNodeAttribute(current, "parentId");
    }
  }
  graph.setNodeAttribute(nodeId, "parentId", parentId);
  set(graphUpdateVersionAtom, (v) => v + 1);
});
var moveNodesToGroupAtom = (0, import_jotai8.atom)(null, (get, set, {
  nodeIds,
  groupId
}) => {
  for (const nodeId of nodeIds) {
    set(setNodeParentAtom, {
      nodeId,
      parentId: groupId
    });
  }
});
var removeFromGroupAtom = (0, import_jotai8.atom)(null, (get, set, nodeId) => {
  set(setNodeParentAtom, {
    nodeId,
    parentId: void 0
  });
});
var groupSelectedNodesAtom = (0, import_jotai8.atom)(null, (get, set, {
  nodeIds,
  groupNodeId
}) => {
  set(pushHistoryAtom, `Group ${nodeIds.length} nodes`);
  const graph = get(graphAtom);
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
  for (const nodeId of nodeIds) {
    if (!graph.hasNode(nodeId)) continue;
    const attrs = graph.getNodeAttributes(nodeId);
    minX = Math.min(minX, attrs.x);
    minY = Math.min(minY, attrs.y);
    maxX = Math.max(maxX, attrs.x + (attrs.width || 200));
    maxY = Math.max(maxY, attrs.y + (attrs.height || 100));
  }
  const padding = 20;
  if (graph.hasNode(groupNodeId)) {
    graph.setNodeAttribute(groupNodeId, "x", minX - padding);
    graph.setNodeAttribute(groupNodeId, "y", minY - padding - 30);
    graph.setNodeAttribute(groupNodeId, "width", maxX - minX + 2 * padding);
    graph.setNodeAttribute(groupNodeId, "height", maxY - minY + 2 * padding + 30);
  }
  for (const nodeId of nodeIds) {
    if (nodeId !== groupNodeId && graph.hasNode(nodeId)) {
      graph.setNodeAttribute(nodeId, "parentId", groupNodeId);
    }
  }
  set(graphUpdateVersionAtom, (v) => v + 1);
  set(nodePositionUpdateCounterAtom, (c) => c + 1);
});
var ungroupNodesAtom = (0, import_jotai8.atom)(null, (get, set, groupId) => {
  set(pushHistoryAtom, "Ungroup nodes");
  const graph = get(graphAtom);
  graph.forEachNode((nodeId, attrs) => {
    if (attrs.parentId === groupId) {
      graph.setNodeAttribute(nodeId, "parentId", void 0);
    }
  });
  set(graphUpdateVersionAtom, (v) => v + 1);
});
var nestNodesOnDropAtom = (0, import_jotai8.atom)(null, (get, set, {
  nodeIds,
  targetId
}) => {
  set(pushHistoryAtom, "Nest nodes");
  for (const nodeId of nodeIds) {
    if (nodeId === targetId) continue;
    set(setNodeParentAtom, {
      nodeId,
      parentId: targetId
    });
  }
  set(autoResizeGroupAtom, targetId);
});
function getNodeDescendants(graph, groupId) {
  const descendants = [];
  const stack = [groupId];
  while (stack.length > 0) {
    const current = stack.pop();
    graph.forEachNode((nodeId, attrs) => {
      if (attrs.parentId === current) {
        descendants.push(nodeId);
        stack.push(nodeId);
      }
    });
  }
  return descendants;
}
var collapsedEdgeRemapAtom = (0, import_jotai8.atom)((get) => {
  const collapsed = get(collapsedGroupsAtom);
  if (collapsed.size === 0) return /* @__PURE__ */ new Map();
  get(graphUpdateVersionAtom);
  const graph = get(graphAtom);
  const remap = /* @__PURE__ */ new Map();
  for (const nodeId of graph.nodes()) {
    let current = nodeId;
    let outermost = null;
    while (true) {
      if (!graph.hasNode(current)) break;
      const parent = graph.getNodeAttribute(current, "parentId");
      if (!parent) break;
      if (collapsed.has(parent)) outermost = parent;
      current = parent;
    }
    if (outermost) remap.set(nodeId, outermost);
  }
  return remap;
});
var autoResizeGroupAtom = (0, import_jotai8.atom)(null, (get, set, groupId) => {
  const graph = get(graphAtom);
  if (!graph.hasNode(groupId)) return;
  const children = [];
  graph.forEachNode((nodeId, attrs) => {
    if (attrs.parentId === groupId) {
      children.push(nodeId);
    }
  });
  if (children.length === 0) return;
  const padding = 20;
  const headerHeight = 30;
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
  for (const childId of children) {
    const attrs = graph.getNodeAttributes(childId);
    minX = Math.min(minX, attrs.x);
    minY = Math.min(minY, attrs.y);
    maxX = Math.max(maxX, attrs.x + (attrs.width || 200));
    maxY = Math.max(maxY, attrs.y + (attrs.height || 100));
  }
  graph.setNodeAttribute(groupId, "x", minX - padding);
  graph.setNodeAttribute(groupId, "y", minY - padding - headerHeight);
  graph.setNodeAttribute(groupId, "width", maxX - minX + 2 * padding);
  graph.setNodeAttribute(groupId, "height", maxY - minY + 2 * padding + headerHeight);
  set(nodePositionUpdateCounterAtom, (c) => c + 1);
});

// src/core/graph-derived.ts
var highestZIndexAtom = (0, import_jotai9.atom)((get) => {
  get(graphUpdateVersionAtom);
  const graph = get(graphAtom);
  let maxZ = 0;
  graph.forEachNode((_node, attributes) => {
    if (attributes.zIndex > maxZ) {
      maxZ = attributes.zIndex;
    }
  });
  return maxZ;
});
var _prevUiNodesByGraph = /* @__PURE__ */ new WeakMap();
var uiNodesAtom = (0, import_jotai9.atom)((get) => {
  get(graphUpdateVersionAtom);
  const graph = get(graphAtom);
  const currentDraggingId = get(draggingNodeIdAtom);
  const collapsed = get(collapsedGroupsAtom);
  const nodes = [];
  graph.forEachNode((nodeId, attributes) => {
    if (collapsed.size > 0) {
      let current = nodeId;
      let hidden = false;
      while (true) {
        if (!graph.hasNode(current)) break;
        const pid = graph.getNodeAttributes(current).parentId;
        if (!pid) break;
        if (collapsed.has(pid)) {
          hidden = true;
          break;
        }
        current = pid;
      }
      if (hidden) return;
    }
    const position = get(nodePositionAtomFamily(nodeId));
    nodes.push({
      ...attributes,
      id: nodeId,
      position,
      isDragging: nodeId === currentDraggingId
    });
  });
  const prev = _prevUiNodesByGraph.get(graph) ?? [];
  if (nodes.length === prev.length && nodes.every((n, i) => n.id === prev[i].id && n.position === prev[i].position && n.isDragging === prev[i].isDragging)) {
    return prev;
  }
  _prevUiNodesByGraph.set(graph, nodes);
  return nodes;
});
var nodeKeysAtom = (0, import_jotai9.atom)((get) => {
  get(graphUpdateVersionAtom);
  const graph = get(graphAtom);
  return graph.nodes();
});
var nodeFamilyAtom = (0, import_jotai_family2.atomFamily)((nodeId) => (0, import_jotai9.atom)((get) => {
  get(graphUpdateVersionAtom);
  const graph = get(graphAtom);
  if (!graph.hasNode(nodeId)) {
    return null;
  }
  const attributes = graph.getNodeAttributes(nodeId);
  const position = get(nodePositionAtomFamily(nodeId));
  const currentDraggingId = get(draggingNodeIdAtom);
  return {
    ...attributes,
    id: nodeId,
    position,
    isDragging: nodeId === currentDraggingId
  };
}), (a, b) => a === b);
var edgeKeysAtom = (0, import_jotai9.atom)((get) => {
  get(graphUpdateVersionAtom);
  const graph = get(graphAtom);
  return graph.edges();
});
var edgeKeysWithTempEdgeAtom = (0, import_jotai9.atom)((get) => {
  const keys = get(edgeKeysAtom);
  const edgeCreation = get(edgeCreationAtom);
  if (edgeCreation.isCreating) {
    return [...keys, "temp-creating-edge"];
  }
  return keys;
});
var _edgeCacheByGraph = /* @__PURE__ */ new WeakMap();
function getEdgeCache(graph) {
  let cache = _edgeCacheByGraph.get(graph);
  if (!cache) {
    cache = /* @__PURE__ */ new Map();
    _edgeCacheByGraph.set(graph, cache);
  }
  return cache;
}
var edgeFamilyAtom = (0, import_jotai_family2.atomFamily)((key) => (0, import_jotai9.atom)((get) => {
  get(graphUpdateVersionAtom);
  if (key === "temp-creating-edge") {
    const edgeCreationState = get(edgeCreationAtom);
    const graph2 = get(graphAtom);
    if (edgeCreationState.isCreating && edgeCreationState.sourceNodeId && edgeCreationState.targetPosition) {
      const sourceNodeAttrs = graph2.getNodeAttributes(edgeCreationState.sourceNodeId);
      const sourceNodePosition = get(nodePositionAtomFamily(edgeCreationState.sourceNodeId));
      const pan = get(panAtom);
      const zoom = get(zoomAtom);
      const viewportRect = get(viewportRectAtom);
      if (sourceNodeAttrs && viewportRect) {
        const mouseX = edgeCreationState.targetPosition.x - viewportRect.left;
        const mouseY = edgeCreationState.targetPosition.y - viewportRect.top;
        const worldTargetX = (mouseX - pan.x) / zoom;
        const worldTargetY = (mouseY - pan.y) / zoom;
        const tempEdge = {
          key: "temp-creating-edge",
          sourceId: edgeCreationState.sourceNodeId,
          targetId: "temp-cursor",
          sourcePosition: sourceNodePosition,
          targetPosition: {
            x: worldTargetX,
            y: worldTargetY
          },
          sourceNodeSize: sourceNodeAttrs.size,
          sourceNodeWidth: sourceNodeAttrs.width,
          sourceNodeHeight: sourceNodeAttrs.height,
          targetNodeSize: 0,
          targetNodeWidth: 0,
          targetNodeHeight: 0,
          type: "dashed",
          color: "#FF9800",
          weight: 2,
          label: void 0,
          dbData: {
            id: "temp-creating-edge",
            graph_id: get(currentGraphIdAtom) || "",
            source_node_id: edgeCreationState.sourceNodeId,
            target_node_id: "temp-cursor",
            edge_type: "temp",
            filter_condition: null,
            ui_properties: null,
            data: null,
            created_at: (/* @__PURE__ */ new Date()).toISOString(),
            updated_at: (/* @__PURE__ */ new Date()).toISOString()
          }
        };
        return tempEdge;
      }
    }
    return null;
  }
  const graph = get(graphAtom);
  if (!graph.hasEdge(key)) {
    getEdgeCache(graph).delete(key);
    return null;
  }
  const sourceId = graph.source(key);
  const targetId = graph.target(key);
  const attributes = graph.getEdgeAttributes(key);
  const remap = get(collapsedEdgeRemapAtom);
  const effectiveSourceId = remap.get(sourceId) ?? sourceId;
  const effectiveTargetId = remap.get(targetId) ?? targetId;
  if (!graph.hasNode(effectiveSourceId) || !graph.hasNode(effectiveTargetId)) {
    getEdgeCache(graph).delete(key);
    return null;
  }
  const sourceAttributes = graph.getNodeAttributes(effectiveSourceId);
  const targetAttributes = graph.getNodeAttributes(effectiveTargetId);
  const sourcePosition = get(nodePositionAtomFamily(effectiveSourceId));
  const targetPosition = get(nodePositionAtomFamily(effectiveTargetId));
  if (sourceAttributes && targetAttributes) {
    const next = {
      ...attributes,
      key,
      sourceId: effectiveSourceId,
      targetId: effectiveTargetId,
      sourcePosition,
      targetPosition,
      sourceNodeSize: sourceAttributes.size,
      targetNodeSize: targetAttributes.size,
      sourceNodeWidth: sourceAttributes.width ?? sourceAttributes.size,
      sourceNodeHeight: sourceAttributes.height ?? sourceAttributes.size,
      targetNodeWidth: targetAttributes.width ?? targetAttributes.size,
      targetNodeHeight: targetAttributes.height ?? targetAttributes.size
    };
    const edgeCache = getEdgeCache(graph);
    const prev = edgeCache.get(key);
    if (prev && prev.sourcePosition === next.sourcePosition && prev.targetPosition === next.targetPosition && prev.sourceId === next.sourceId && prev.targetId === next.targetId && prev.type === next.type && prev.color === next.color && prev.weight === next.weight && prev.label === next.label && prev.sourceNodeSize === next.sourceNodeSize && prev.targetNodeSize === next.targetNodeSize && prev.sourceNodeWidth === next.sourceNodeWidth && prev.sourceNodeHeight === next.sourceNodeHeight && prev.targetNodeWidth === next.targetNodeWidth && prev.targetNodeHeight === next.targetNodeHeight) {
      return prev;
    }
    edgeCache.set(key, next);
    return next;
  }
  getEdgeCache(graph).delete(key);
  return null;
}), (a, b) => a === b);

// src/core/graph-mutations-edges.ts
var import_jotai11 = require("jotai");

// src/core/reduced-motion-store.ts
var import_jotai10 = require("jotai");
var prefersReducedMotionAtom = (0, import_jotai10.atom)(typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)").matches : false);
var watchReducedMotionAtom = (0, import_jotai10.atom)(null, (_get, set) => {
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
  const mql = window.matchMedia("(prefers-reduced-motion: reduce)");
  const handler = (e) => {
    set(prefersReducedMotionAtom, e.matches);
  };
  set(prefersReducedMotionAtom, mql.matches);
  mql.addEventListener("change", handler);
  return () => mql.removeEventListener("change", handler);
});

// src/core/graph-mutations-edges.ts
var debug5 = createDebug("graph:mutations:edges");
var addEdgeToLocalGraphAtom = (0, import_jotai11.atom)(null, (get, set, newEdge) => {
  const graph = get(graphAtom);
  if (graph.hasNode(newEdge.source_node_id) && graph.hasNode(newEdge.target_node_id)) {
    const uiProps = newEdge.ui_properties || {};
    const attributes = {
      type: typeof uiProps.style === "string" ? uiProps.style : "solid",
      color: typeof uiProps.color === "string" ? uiProps.color : "#999",
      label: newEdge.edge_type ?? void 0,
      weight: typeof uiProps.weight === "number" ? uiProps.weight : 1,
      dbData: newEdge
    };
    if (!graph.hasEdge(newEdge.id)) {
      try {
        debug5("Adding edge %s to local graph", newEdge.id);
        graph.addEdgeWithKey(newEdge.id, newEdge.source_node_id, newEdge.target_node_id, attributes);
        set(graphAtom, graph.copy());
        set(graphUpdateVersionAtom, (v) => v + 1);
      } catch (e) {
        debug5("Failed to add edge %s: %o", newEdge.id, e);
      }
    }
  }
});
var removeEdgeFromLocalGraphAtom = (0, import_jotai11.atom)(null, (get, set, edgeId) => {
  const graph = get(graphAtom);
  if (graph.hasEdge(edgeId)) {
    graph.dropEdge(edgeId);
    set(graphAtom, graph.copy());
    set(graphUpdateVersionAtom, (v) => v + 1);
  }
});
var swapEdgeAtomicAtom = (0, import_jotai11.atom)(null, (get, set, {
  tempEdgeId,
  newEdge
}) => {
  const graph = get(graphAtom);
  if (graph.hasEdge(tempEdgeId)) {
    graph.dropEdge(tempEdgeId);
  }
  if (graph.hasNode(newEdge.source_node_id) && graph.hasNode(newEdge.target_node_id)) {
    const uiProps = newEdge.ui_properties || {};
    const attributes = {
      type: typeof uiProps.style === "string" ? uiProps.style : "solid",
      color: typeof uiProps.color === "string" ? uiProps.color : "#999",
      label: newEdge.edge_type ?? void 0,
      weight: typeof uiProps.weight === "number" ? uiProps.weight : 1,
      dbData: newEdge
    };
    if (!graph.hasEdge(newEdge.id)) {
      try {
        debug5("Atomically swapping temp edge %s with real edge %s", tempEdgeId, newEdge.id);
        graph.addEdgeWithKey(newEdge.id, newEdge.source_node_id, newEdge.target_node_id, attributes);
      } catch (e) {
        debug5("Failed to add edge %s: %o", newEdge.id, e);
      }
    }
  }
  set(graphAtom, graph.copy());
  set(graphUpdateVersionAtom, (v) => v + 1);
});
var departingEdgesAtom = (0, import_jotai11.atom)(/* @__PURE__ */ new Map());
var EDGE_ANIMATION_DURATION = 300;
var removeEdgeWithAnimationAtom = (0, import_jotai11.atom)(null, (get, set, edgeKey) => {
  const edgeState = get(edgeFamilyAtom(edgeKey));
  if (edgeState) {
    const departing = new Map(get(departingEdgesAtom));
    departing.set(edgeKey, edgeState);
    set(departingEdgesAtom, departing);
    set(removeEdgeFromLocalGraphAtom, edgeKey);
    const duration = get(prefersReducedMotionAtom) ? 0 : EDGE_ANIMATION_DURATION;
    setTimeout(() => {
      const current = new Map(get(departingEdgesAtom));
      current.delete(edgeKey);
      set(departingEdgesAtom, current);
    }, duration);
  }
});
var editingEdgeLabelAtom = (0, import_jotai11.atom)(null);
var updateEdgeLabelAtom = (0, import_jotai11.atom)(null, (get, set, {
  edgeKey,
  label
}) => {
  const graph = get(graphAtom);
  if (graph.hasEdge(edgeKey)) {
    graph.setEdgeAttribute(edgeKey, "label", label || void 0);
    set(graphUpdateVersionAtom, (v) => v + 1);
    set(nodePositionUpdateCounterAtom, (c) => c + 1);
  }
});

// src/core/graph-mutations-advanced.ts
var import_jotai12 = require("jotai");
var debug6 = createDebug("graph:mutations:advanced");
var dropTargetNodeIdAtom = (0, import_jotai12.atom)(null);
var splitNodeAtom = (0, import_jotai12.atom)(null, (get, set, {
  nodeId,
  position1,
  position2
}) => {
  const graph = get(graphAtom);
  if (!graph.hasNode(nodeId)) return;
  const attrs = graph.getNodeAttributes(nodeId);
  const graphId = get(currentGraphIdAtom) || attrs.dbData.graph_id;
  set(pushHistoryAtom, "Split node");
  graph.setNodeAttribute(nodeId, "x", position1.x);
  graph.setNodeAttribute(nodeId, "y", position1.y);
  const edges = [];
  graph.forEachEdge(nodeId, (_key, eAttrs, source, target) => {
    edges.push({
      source,
      target,
      attrs: eAttrs
    });
  });
  const cloneId = `split-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
  const cloneDbNode = {
    ...attrs.dbData,
    id: cloneId,
    graph_id: graphId,
    ui_properties: {
      ...attrs.dbData.ui_properties || {},
      x: position2.x,
      y: position2.y
    },
    created_at: (/* @__PURE__ */ new Date()).toISOString(),
    updated_at: (/* @__PURE__ */ new Date()).toISOString()
  };
  set(addNodeToLocalGraphAtom, cloneDbNode);
  for (const edge of edges) {
    const newSource = edge.source === nodeId ? cloneId : edge.source;
    const newTarget = edge.target === nodeId ? cloneId : edge.target;
    set(addEdgeToLocalGraphAtom, {
      ...edge.attrs.dbData,
      id: `split-e-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
      source_node_id: newSource,
      target_node_id: newTarget
    });
  }
  set(graphUpdateVersionAtom, (v) => v + 1);
  set(nodePositionUpdateCounterAtom, (c) => c + 1);
  debug6("Split node %s \u2192 clone %s", nodeId, cloneId);
});
var mergeNodesAtom = (0, import_jotai12.atom)(null, (get, set, {
  nodeIds
}) => {
  if (nodeIds.length < 2) return;
  const graph = get(graphAtom);
  const [survivorId, ...doomed] = nodeIds;
  if (!graph.hasNode(survivorId)) return;
  set(pushHistoryAtom, `Merge ${nodeIds.length} nodes`);
  const doomedSet = new Set(doomed);
  for (const doomedId of doomed) {
    if (!graph.hasNode(doomedId)) continue;
    const edges = [];
    graph.forEachEdge(doomedId, (_key, eAttrs, source, target) => {
      edges.push({
        source,
        target,
        attrs: eAttrs
      });
    });
    for (const edge of edges) {
      const newSource = doomedSet.has(edge.source) ? survivorId : edge.source;
      const newTarget = doomedSet.has(edge.target) ? survivorId : edge.target;
      if (newSource === newTarget) continue;
      set(addEdgeToLocalGraphAtom, {
        ...edge.attrs.dbData,
        id: `merge-e-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
        source_node_id: newSource,
        target_node_id: newTarget
      });
    }
    set(optimisticDeleteNodeAtom, {
      nodeId: doomedId
    });
  }
  set(graphUpdateVersionAtom, (v) => v + 1);
  debug6("Merged nodes %o \u2192 survivor %s", nodeIds, survivorId);
});

// src/core/graph-mutations.ts
var debug7 = createDebug("graph:mutations");
var startNodeDragAtom = (0, import_jotai13.atom)(null, (get, set, {
  nodeId
}) => {
  const graph = get(graphAtom);
  if (!graph.hasNode(nodeId)) return;
  const currentAttributes = graph.getNodeAttributes(nodeId);
  set(preDragNodeAttributesAtom, JSON.parse(JSON.stringify(currentAttributes)));
  const currentHighestZIndex = get(highestZIndexAtom);
  const newZIndex = currentHighestZIndex + 1;
  graph.setNodeAttribute(nodeId, "zIndex", newZIndex);
  set(draggingNodeIdAtom, nodeId);
});
var endNodeDragAtom = (0, import_jotai13.atom)(null, (get, set, _payload) => {
  const currentDraggingId = get(draggingNodeIdAtom);
  if (currentDraggingId) {
    debug7("Node %s drag ended", currentDraggingId);
    const graph = get(graphAtom);
    if (graph.hasNode(currentDraggingId)) {
      const parentId = graph.getNodeAttribute(currentDraggingId, "parentId");
      if (parentId) {
        set(autoResizeGroupAtom, parentId);
      }
    }
  }
  set(draggingNodeIdAtom, null);
  set(preDragNodeAttributesAtom, null);
});
var optimisticDeleteNodeAtom = (0, import_jotai13.atom)(null, (get, set, {
  nodeId
}) => {
  const graph = get(graphAtom);
  if (graph.hasNode(nodeId)) {
    graph.dropNode(nodeId);
    set(cleanupNodePositionAtom, nodeId);
    set(graphAtom, graph.copy());
    debug7("Optimistically deleted node %s", nodeId);
  }
});
var optimisticDeleteEdgeAtom = (0, import_jotai13.atom)(null, (get, set, {
  edgeKey
}) => {
  const graph = get(graphAtom);
  if (graph.hasEdge(edgeKey)) {
    graph.dropEdge(edgeKey);
    set(graphAtom, graph.copy());
    debug7("Optimistically deleted edge %s", edgeKey);
  }
});
var addNodeToLocalGraphAtom = (0, import_jotai13.atom)(null, (get, set, newNode) => {
  const graph = get(graphAtom);
  if (graph.hasNode(newNode.id)) {
    debug7("Node %s already exists, skipping", newNode.id);
    return;
  }
  const uiProps = newNode.ui_properties || {};
  const attributes = {
    x: typeof uiProps.x === "number" ? uiProps.x : Math.random() * 800,
    y: typeof uiProps.y === "number" ? uiProps.y : Math.random() * 600,
    size: typeof uiProps.size === "number" ? uiProps.size : 15,
    width: typeof uiProps.width === "number" ? uiProps.width : 500,
    height: typeof uiProps.height === "number" ? uiProps.height : 500,
    color: typeof uiProps.color === "string" ? uiProps.color : "#ccc",
    label: newNode.label || newNode.node_type || newNode.id,
    zIndex: typeof uiProps.zIndex === "number" ? uiProps.zIndex : 0,
    dbData: newNode
  };
  debug7("Adding node %s to local graph at (%d, %d)", newNode.id, attributes.x, attributes.y);
  graph.addNode(newNode.id, attributes);
  set(graphAtom, graph.copy());
  set(graphUpdateVersionAtom, (v) => v + 1);
  set(nodePositionUpdateCounterAtom, (c) => c + 1);
});
var loadGraphFromDbAtom = (0, import_jotai13.atom)(null, (get, set, fetchedNodes, fetchedEdges) => {
  debug7("========== START SYNC ==========");
  debug7("Fetched nodes: %d, edges: %d", fetchedNodes.length, fetchedEdges.length);
  const currentGraphId = get(currentGraphIdAtom);
  if (fetchedNodes.length > 0 && fetchedNodes[0].graph_id !== currentGraphId) {
    debug7("Skipping sync - data belongs to different graph");
    return;
  }
  const existingGraph = get(graphAtom);
  const isDragging = get(draggingNodeIdAtom) !== null;
  if (isDragging) {
    debug7("Skipping sync - drag in progress");
    return;
  }
  const existingNodeIds = new Set(existingGraph.nodes());
  const fetchedNodeIds = new Set(fetchedNodes.map((n) => n.id));
  const hasAnyCommonNodes = Array.from(existingNodeIds).some((id) => fetchedNodeIds.has(id));
  let graph;
  if (hasAnyCommonNodes && existingNodeIds.size > 0) {
    debug7("Merging DB data into existing graph");
    graph = existingGraph.copy();
  } else {
    debug7("Creating fresh graph (graph switch detected)");
    graph = new import_graphology3.default(graphOptions);
  }
  const fetchedEdgeIds = new Set(fetchedEdges.map((e) => e.id));
  if (hasAnyCommonNodes && existingNodeIds.size > 0) {
    graph.forEachNode((nodeId) => {
      if (!fetchedNodeIds.has(nodeId)) {
        debug7("Removing deleted node: %s", nodeId);
        graph.dropNode(nodeId);
        nodePositionAtomFamily.remove(nodeId);
      }
    });
  }
  fetchedNodes.forEach((node) => {
    const uiProps = node.ui_properties || {};
    const newX = typeof uiProps.x === "number" ? uiProps.x : Math.random() * 800;
    const newY = typeof uiProps.y === "number" ? uiProps.y : Math.random() * 600;
    if (graph.hasNode(node.id)) {
      const currentAttrs = graph.getNodeAttributes(node.id);
      const attributes = {
        x: newX,
        y: newY,
        size: typeof uiProps.size === "number" ? uiProps.size : currentAttrs.size,
        width: typeof uiProps.width === "number" ? uiProps.width : currentAttrs.width ?? 500,
        height: typeof uiProps.height === "number" ? uiProps.height : currentAttrs.height ?? 500,
        color: typeof uiProps.color === "string" ? uiProps.color : currentAttrs.color,
        label: node.label || node.node_type || node.id,
        zIndex: typeof uiProps.zIndex === "number" ? uiProps.zIndex : currentAttrs.zIndex,
        dbData: node
      };
      graph.replaceNodeAttributes(node.id, attributes);
    } else {
      const attributes = {
        x: newX,
        y: newY,
        size: typeof uiProps.size === "number" ? uiProps.size : 15,
        width: typeof uiProps.width === "number" ? uiProps.width : 500,
        height: typeof uiProps.height === "number" ? uiProps.height : 500,
        color: typeof uiProps.color === "string" ? uiProps.color : "#ccc",
        label: node.label || node.node_type || node.id,
        zIndex: typeof uiProps.zIndex === "number" ? uiProps.zIndex : 0,
        dbData: node
      };
      graph.addNode(node.id, attributes);
    }
  });
  graph.forEachEdge((edgeId) => {
    if (!fetchedEdgeIds.has(edgeId)) {
      debug7("Removing deleted edge: %s", edgeId);
      graph.dropEdge(edgeId);
    }
  });
  fetchedEdges.forEach((edge) => {
    if (graph.hasNode(edge.source_node_id) && graph.hasNode(edge.target_node_id)) {
      const uiProps = edge.ui_properties || {};
      const attributes = {
        type: typeof uiProps.style === "string" ? uiProps.style : "solid",
        color: typeof uiProps.color === "string" ? uiProps.color : "#999",
        label: edge.edge_type ?? void 0,
        weight: typeof uiProps.weight === "number" ? uiProps.weight : 1,
        dbData: edge
      };
      if (graph.hasEdge(edge.id)) {
        graph.replaceEdgeAttributes(edge.id, attributes);
      } else {
        try {
          graph.addEdgeWithKey(edge.id, edge.source_node_id, edge.target_node_id, attributes);
        } catch (e) {
          debug7("Failed to add edge %s: %o", edge.id, e);
        }
      }
    }
  });
  set(graphAtom, graph);
  set(graphUpdateVersionAtom, (v) => v + 1);
  debug7("========== SYNC COMPLETE ==========");
  debug7("Final graph: %d nodes, %d edges", graph.order, graph.size);
});

// src/core/sync-store.ts
var import_jotai14 = require("jotai");
var debug8 = createDebug("sync");
var syncStatusAtom = (0, import_jotai14.atom)("synced");
var pendingMutationsCountAtom = (0, import_jotai14.atom)(0);
var isOnlineAtom = (0, import_jotai14.atom)(typeof navigator !== "undefined" ? navigator.onLine : true);
var lastSyncErrorAtom = (0, import_jotai14.atom)(null);
var lastSyncTimeAtom = (0, import_jotai14.atom)(Date.now());
var mutationQueueAtom = (0, import_jotai14.atom)([]);
var syncStateAtom = (0, import_jotai14.atom)((get) => ({
  status: get(syncStatusAtom),
  pendingMutations: get(pendingMutationsCountAtom),
  lastError: get(lastSyncErrorAtom),
  lastSyncTime: get(lastSyncTimeAtom),
  isOnline: get(isOnlineAtom),
  queuedMutations: get(mutationQueueAtom).length
}));
var startMutationAtom = (0, import_jotai14.atom)(null, (get, set) => {
  const currentCount = get(pendingMutationsCountAtom);
  const newCount = currentCount + 1;
  set(pendingMutationsCountAtom, newCount);
  debug8("Mutation started. Pending count: %d -> %d", currentCount, newCount);
  if (newCount > 0 && get(syncStatusAtom) !== "syncing") {
    set(syncStatusAtom, "syncing");
    debug8("Status -> syncing");
  }
});
var completeMutationAtom = (0, import_jotai14.atom)(null, (get, set, success = true) => {
  const currentCount = get(pendingMutationsCountAtom);
  const newCount = Math.max(0, currentCount - 1);
  set(pendingMutationsCountAtom, newCount);
  debug8("Mutation completed (success: %s). Pending count: %d -> %d", success, currentCount, newCount);
  if (success) {
    set(lastSyncTimeAtom, Date.now());
    if (newCount === 0) {
      set(lastSyncErrorAtom, null);
    }
  }
  if (newCount === 0) {
    const isOnline = get(isOnlineAtom);
    const hasError = get(lastSyncErrorAtom) !== null;
    if (hasError) {
      set(syncStatusAtom, "error");
      debug8("Status -> error");
    } else if (!isOnline) {
      set(syncStatusAtom, "offline");
      debug8("Status -> offline");
    } else {
      set(syncStatusAtom, "synced");
      debug8("Status -> synced");
    }
  }
});
var trackMutationErrorAtom = (0, import_jotai14.atom)(null, (_get, set, error) => {
  set(lastSyncErrorAtom, error);
  debug8("Mutation failed: %s", error);
});
var setOnlineStatusAtom = (0, import_jotai14.atom)(null, (get, set, isOnline) => {
  set(isOnlineAtom, isOnline);
  const pendingCount = get(pendingMutationsCountAtom);
  const hasError = get(lastSyncErrorAtom) !== null;
  const queueLength = get(mutationQueueAtom).length;
  if (pendingCount === 0) {
    if (hasError || queueLength > 0) {
      set(syncStatusAtom, "error");
    } else {
      set(syncStatusAtom, isOnline ? "synced" : "offline");
    }
  }
});
var queueMutationAtom = (0, import_jotai14.atom)(null, (get, set, mutation) => {
  const queue = get(mutationQueueAtom);
  const newMutation = {
    ...mutation,
    id: crypto.randomUUID(),
    timestamp: Date.now(),
    retryCount: 0,
    maxRetries: mutation.maxRetries ?? 3
  };
  const newQueue = [...queue, newMutation];
  set(mutationQueueAtom, newQueue);
  debug8("Queued mutation: %s. Queue size: %d", mutation.type, newQueue.length);
  if (get(pendingMutationsCountAtom) === 0) {
    set(syncStatusAtom, "error");
  }
  return newMutation.id;
});
var dequeueMutationAtom = (0, import_jotai14.atom)(null, (get, set, mutationId) => {
  const queue = get(mutationQueueAtom);
  const newQueue = queue.filter((m) => m.id !== mutationId);
  set(mutationQueueAtom, newQueue);
  debug8("Dequeued mutation: %s. Queue size: %d", mutationId, newQueue.length);
  if (newQueue.length === 0 && get(pendingMutationsCountAtom) === 0 && get(lastSyncErrorAtom) === null) {
    set(syncStatusAtom, get(isOnlineAtom) ? "synced" : "offline");
  }
});
var incrementRetryCountAtom = (0, import_jotai14.atom)(null, (get, set, mutationId) => {
  const queue = get(mutationQueueAtom);
  const newQueue = queue.map((m) => m.id === mutationId ? {
    ...m,
    retryCount: m.retryCount + 1
  } : m);
  set(mutationQueueAtom, newQueue);
});
var getNextQueuedMutationAtom = (0, import_jotai14.atom)((get) => {
  const queue = get(mutationQueueAtom);
  return queue.find((m) => m.retryCount < m.maxRetries) ?? null;
});
var clearMutationQueueAtom = (0, import_jotai14.atom)(null, (get, set) => {
  set(mutationQueueAtom, []);
  debug8("Cleared mutation queue");
  if (get(pendingMutationsCountAtom) === 0 && get(lastSyncErrorAtom) === null) {
    set(syncStatusAtom, get(isOnlineAtom) ? "synced" : "offline");
  }
});

// src/utils/gesture-configs.ts
var fingerGestureConfig = {
  eventOptions: {
    passive: false,
    capture: false
  },
  drag: {
    pointer: {
      touch: true,
      keys: false,
      capture: false,
      buttons: -1
    },
    filterTaps: true,
    tapsThreshold: 10,
    // Was 3 — too strict for fingers
    threshold: 10
    // Was 3 — needs larger dead zone
  }
};
var pencilGestureConfig = {
  eventOptions: {
    passive: false,
    capture: false
  },
  drag: {
    pointer: {
      touch: true,
      keys: false,
      capture: false,
      buttons: -1
    },
    filterTaps: true,
    tapsThreshold: 3,
    threshold: 2
    // Very precise — small dead zone
  }
};
var mouseGestureConfig = {
  eventOptions: {
    passive: false,
    capture: false
  },
  drag: {
    pointer: {
      touch: true,
      keys: false,
      capture: false,
      buttons: -1
    },
    filterTaps: true,
    tapsThreshold: 5,
    // Was 3
    threshold: 3
  }
};
function getNodeGestureConfig(source) {
  switch (source) {
    case "finger":
      return fingerGestureConfig;
    case "pencil":
      return pencilGestureConfig;
    case "mouse":
      return mouseGestureConfig;
  }
}

// src/core/input-store.ts
var import_jotai15 = require("jotai");

// src/core/input-classifier.ts
function detectInputCapabilities() {
  if (typeof window === "undefined") {
    return {
      hasTouch: false,
      hasStylus: false,
      hasMouse: true,
      hasCoarsePointer: false
    };
  }
  const hasTouch = "ontouchstart" in window || navigator.maxTouchPoints > 0;
  const supportsMatchMedia = typeof window.matchMedia === "function";
  const hasCoarsePointer = supportsMatchMedia ? window.matchMedia("(pointer: coarse)").matches : false;
  const hasFinePointer = supportsMatchMedia ? window.matchMedia("(pointer: fine)").matches : true;
  const hasMouse = hasFinePointer || !hasTouch;
  return {
    hasTouch,
    hasStylus: false,
    // Set to true on first pen event
    hasMouse,
    hasCoarsePointer
  };
}

// src/core/input-store.ts
var activePointersAtom = (0, import_jotai15.atom)(/* @__PURE__ */ new Map());
var primaryInputSourceAtom = (0, import_jotai15.atom)("mouse");
var inputCapabilitiesAtom = (0, import_jotai15.atom)(detectInputCapabilities());
var isStylusActiveAtom = (0, import_jotai15.atom)((get) => {
  const pointers = get(activePointersAtom);
  for (const [, pointer] of pointers) {
    if (pointer.source === "pencil") return true;
  }
  return false;
});
var isMultiTouchAtom = (0, import_jotai15.atom)((get) => {
  const pointers = get(activePointersAtom);
  let fingerCount = 0;
  for (const [, pointer] of pointers) {
    if (pointer.source === "finger") fingerCount++;
  }
  return fingerCount > 1;
});
var fingerCountAtom = (0, import_jotai15.atom)((get) => {
  const pointers = get(activePointersAtom);
  let count = 0;
  for (const [, pointer] of pointers) {
    if (pointer.source === "finger") count++;
  }
  return count;
});
var isTouchDeviceAtom = (0, import_jotai15.atom)((get) => {
  const caps = get(inputCapabilitiesAtom);
  return caps.hasTouch;
});
var pointerDownAtom = (0, import_jotai15.atom)(null, (get, set, pointer) => {
  const pointers = new Map(get(activePointersAtom));
  pointers.set(pointer.pointerId, pointer);
  set(activePointersAtom, pointers);
  set(primaryInputSourceAtom, pointer.source);
  if (pointer.source === "pencil") {
    const caps = get(inputCapabilitiesAtom);
    if (!caps.hasStylus) {
      set(inputCapabilitiesAtom, {
        ...caps,
        hasStylus: true
      });
    }
  }
});
var pointerUpAtom = (0, import_jotai15.atom)(null, (get, set, pointerId) => {
  const pointers = new Map(get(activePointersAtom));
  pointers.delete(pointerId);
  set(activePointersAtom, pointers);
});
var clearPointersAtom = (0, import_jotai15.atom)(null, (_get, set) => {
  set(activePointersAtom, /* @__PURE__ */ new Map());
});

// src/utils/hit-test.ts
var defaultProvider = {
  elementFromPoint: (x, y) => document.elementFromPoint(x, y),
  elementsFromPoint: (x, y) => document.elementsFromPoint(x, y)
};
var _provider = defaultProvider;
function hitTestNode(screenX, screenY) {
  const element = _provider.elementFromPoint(screenX, screenY);
  const nodeElement = element?.closest("[data-node-id]") ?? null;
  const nodeId = nodeElement?.getAttribute("data-node-id") ?? null;
  return {
    nodeId,
    element: nodeElement
  };
}

// src/hooks/useDragStateMachine.ts
function buildDragPositions(graph, selectedNodeIds) {
  const positions = /* @__PURE__ */ new Map();
  for (const nodeId of selectedNodeIds) {
    if (graph.hasNode(nodeId)) {
      const attrs = graph.getNodeAttributes(nodeId);
      positions.set(nodeId, {
        x: attrs.x,
        y: attrs.y
      });
    }
  }
  const currentKeys = Array.from(positions.keys());
  for (const nodeId of currentKeys) {
    const descendants = getNodeDescendants(graph, nodeId);
    for (const descId of descendants) {
      if (!positions.has(descId) && graph.hasNode(descId)) {
        const attrs = graph.getNodeAttributes(descId);
        positions.set(descId, {
          x: attrs.x,
          y: attrs.y
        });
      }
    }
  }
  return positions;
}
function computeDragUpdates(initialPositions, movementX, movementY, zoom, graph) {
  const deltaX = movementX / zoom;
  const deltaY = movementY / zoom;
  const updates = [];
  initialPositions.forEach((initialPos, nodeId) => {
    if (graph.hasNode(nodeId)) {
      updates.push({
        id: nodeId,
        pos: {
          x: initialPos.x + deltaX,
          y: initialPos.y + deltaY
        }
      });
    }
  });
  return updates;
}
function isDragPrevented(target) {
  return !!target.closest('[data-no-drag="true"]') || target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT";
}

// src/hooks/useNodeDrag.ts
var debug9 = createDebug("drag");
function useNodeDrag(id, t0) {
  const $ = (0, import_compiler_runtime2.c)(49);
  let t1;
  if ($[0] !== t0) {
    t1 = t0 === void 0 ? {} : t0;
    $[0] = t0;
    $[1] = t1;
  } else {
    t1 = $[1];
  }
  const options = t1;
  const {
    onPersist,
    onPersistError,
    heldKeys
  } = options;
  const graph = (0, import_jotai16.useAtomValue)(graphAtom);
  const [pan, setPan] = (0, import_jotai16.useAtom)(panAtom);
  const startMutation = (0, import_jotai16.useSetAtom)(startMutationAtom);
  const completeMutation = (0, import_jotai16.useSetAtom)(completeMutationAtom);
  const setStartDrag = (0, import_jotai16.useSetAtom)(startNodeDragAtom);
  const setEndDrag = (0, import_jotai16.useSetAtom)(endNodeDragAtom);
  const getPreDragAttributes = (0, import_jotai16.useAtomValue)(preDragNodeAttributesAtom);
  const currentZoom = (0, import_jotai16.useAtomValue)(zoomAtom);
  const getSelectedNodeIds = (0, import_jotai16.useAtomValue)(selectedNodeIdsAtom);
  const currentGraphId = (0, import_jotai16.useAtomValue)(currentGraphIdAtom);
  const edgeCreation = (0, import_jotai16.useAtomValue)(edgeCreationAtom);
  const setGraph = (0, import_jotai16.useSetAtom)(graphAtom);
  (0, import_jotai16.useSetAtom)(nodePositionUpdateCounterAtom);
  const pushHistory = (0, import_jotai16.useSetAtom)(pushHistoryAtom);
  const setDropTarget = (0, import_jotai16.useSetAtom)(dropTargetNodeIdAtom);
  const nestNodesOnDrop = (0, import_jotai16.useSetAtom)(nestNodesOnDropAtom);
  const inputSource = (0, import_jotai16.useAtomValue)(primaryInputSourceAtom);
  let t2;
  if ($[2] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
    t2 = (0, import_jotai16.atom)(null, _temp2);
    $[2] = t2;
  } else {
    t2 = $[2];
  }
  const batchUpdatePositions = (0, import_jotai16.useSetAtom)(t2);
  let t3;
  if ($[3] !== batchUpdatePositions) {
    t3 = (updates_0) => {
      batchUpdatePositions(updates_0);
    };
    $[3] = batchUpdatePositions;
    $[4] = t3;
  } else {
    t3 = $[4];
  }
  const updateNodePositions = t3;
  const gestureInstanceRef = (0, import_react2.useRef)(0);
  let t4;
  if ($[5] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
    t4 = {
      x: 0,
      y: 0
    };
    $[5] = t4;
  } else {
    t4 = $[5];
  }
  const panStartRef = (0, import_react2.useRef)(t4);
  const isSpaceHeld = Boolean(heldKeys?.byCode.Space || heldKeys?.byKey[" "] || heldKeys?.byKey.Spacebar);
  let t5;
  if ($[6] !== isSpaceHeld || $[7] !== pan) {
    t5 = (state) => {
      if (isDragPrevented(state.event.target)) {
        return;
      }
      gestureInstanceRef.current = gestureInstanceRef.current + 1;
      if (isSpaceHeld) {
        panStartRef.current = pan;
      }
    };
    $[6] = isSpaceHeld;
    $[7] = pan;
    $[8] = t5;
  } else {
    t5 = $[8];
  }
  let t6;
  if ($[9] !== currentZoom || $[10] !== edgeCreation || $[11] !== getSelectedNodeIds || $[12] !== graph || $[13] !== id || $[14] !== isSpaceHeld || $[15] !== pushHistory || $[16] !== setDropTarget || $[17] !== setPan || $[18] !== setStartDrag || $[19] !== startMutation || $[20] !== updateNodePositions) {
    t6 = (state_0) => {
      if (isDragPrevented(state_0.event.target)) {
        return;
      }
      if (edgeCreation.isCreating) {
        return;
      }
      state_0.event.stopPropagation();
      if (isSpaceHeld) {
        if (!state_0.tap && state_0.active) {
          const [mx, my] = state_0.movement;
          setPan({
            x: panStartRef.current.x + mx,
            y: panStartRef.current.y + my
          });
        }
        return state_0.memo;
      }
      const {
        movement: t72,
        first,
        active,
        down,
        pinching,
        cancel,
        tap
      } = state_0;
      const [mx_0, my_0] = t72;
      let currentMemo = state_0.memo;
      if (tap || !active) {
        return currentMemo;
      }
      const currentGestureInstance = gestureInstanceRef.current;
      if (first) {
        const selectionSize = getSelectedNodeIds.size;
        const label = selectionSize > 1 ? `Move ${selectionSize} nodes` : "Move node";
        pushHistory(label);
        setStartDrag({
          nodeId: id
        });
        const initialPositions = buildDragPositions(graph, getSelectedNodeIds);
        initialPositions.forEach(() => startMutation());
        currentMemo = {
          initialPositions,
          gestureInstance: currentGestureInstance
        };
      }
      if (!currentMemo || currentMemo.gestureInstance !== currentGestureInstance || !currentMemo.initialPositions) {
        if (cancel && !pinching && !down && !tap && !active) {
          cancel();
        }
        return currentMemo;
      }
      const updates_1 = computeDragUpdates(currentMemo.initialPositions, mx_0, my_0, currentZoom, graph);
      if (updates_1.length > 0) {
        updateNodePositions(updates_1);
      }
      if (state_0.event && "clientX" in state_0.event) {
        const {
          nodeId: hoveredId
        } = hitTestNode(state_0.event.clientX, state_0.event.clientY);
        const validTarget = hoveredId && !currentMemo.initialPositions.has(hoveredId) ? hoveredId : null;
        setDropTarget(validTarget);
      }
      return currentMemo;
    };
    $[9] = currentZoom;
    $[10] = edgeCreation;
    $[11] = getSelectedNodeIds;
    $[12] = graph;
    $[13] = id;
    $[14] = isSpaceHeld;
    $[15] = pushHistory;
    $[16] = setDropTarget;
    $[17] = setPan;
    $[18] = setStartDrag;
    $[19] = startMutation;
    $[20] = updateNodePositions;
    $[21] = t6;
  } else {
    t6 = $[21];
  }
  let t7;
  if ($[22] !== completeMutation || $[23] !== currentGraphId || $[24] !== currentZoom || $[25] !== edgeCreation || $[26] !== getPreDragAttributes || $[27] !== getSelectedNodeIds || $[28] !== graph || $[29] !== id || $[30] !== isSpaceHeld || $[31] !== nestNodesOnDrop || $[32] !== onPersist || $[33] !== onPersistError || $[34] !== setDropTarget || $[35] !== setEndDrag || $[36] !== setGraph || $[37] !== startMutation || $[38] !== updateNodePositions) {
    t7 = (state_1) => {
      if (isDragPrevented(state_1.event.target)) {
        return;
      }
      if (edgeCreation.isCreating) {
        setEndDrag({
          nodeId: id
        });
        return;
      }
      if (isSpaceHeld) {
        return;
      }
      state_1.event.stopPropagation();
      const memo = state_1.memo;
      setDropTarget(null);
      if (state_1.event && "clientX" in state_1.event && memo?.initialPositions) {
        const {
          nodeId: hoveredId_0
        } = hitTestNode(state_1.event.clientX, state_1.event.clientY);
        if (hoveredId_0 && !memo.initialPositions.has(hoveredId_0)) {
          const draggedNodeIds = Array.from(memo.initialPositions.keys()).filter((nid) => getSelectedNodeIds.has(nid));
          if (draggedNodeIds.length > 0) {
            nestNodesOnDrop({
              nodeIds: draggedNodeIds,
              targetId: hoveredId_0
            });
          }
        }
      }
      if (!currentGraphId) {
        debug9("Cannot update node position: currentGraphId is not set");
        setEndDrag({
          nodeId: id
        });
        return;
      }
      const nodesToUpdate = memo?.initialPositions ? Array.from(memo.initialPositions.keys()) : [id];
      nodesToUpdate.forEach((draggedNodeId) => {
        if (!graph.hasNode(draggedNodeId)) {
          completeMutation(false);
          return;
        }
        const finalAttrs = graph.getNodeAttributes(draggedNodeId);
        const initialPos = memo?.initialPositions.get(draggedNodeId);
        if (!initialPos) {
          completeMutation(false);
          return;
        }
        const [mx_1, my_1] = state_1.movement;
        const deltaX = mx_1 / currentZoom;
        const deltaY = my_1 / currentZoom;
        const finalPosition = {
          x: initialPos.x + deltaX,
          y: initialPos.y + deltaY
        };
        updateNodePositions([{
          id: draggedNodeId,
          pos: finalPosition
        }]);
        if (!onPersist) {
          completeMutation(true);
          setEndDrag({
            nodeId: draggedNodeId
          });
          return;
        }
        const existingDbUiProps = typeof finalAttrs.dbData.ui_properties === "object" && finalAttrs.dbData.ui_properties !== null && !Array.isArray(finalAttrs.dbData.ui_properties) ? finalAttrs.dbData.ui_properties : {};
        const newUiProperties = {
          ...existingDbUiProps,
          x: finalPosition.x,
          y: finalPosition.y,
          zIndex: finalAttrs.zIndex
        };
        const pendingState = getPendingState(draggedNodeId);
        if (pendingState.inFlight) {
          pendingState.queuedPosition = finalPosition;
          pendingState.queuedUiProperties = newUiProperties;
          pendingState.graphId = currentGraphId;
          return;
        }
        pendingState.inFlight = true;
        pendingState.graphId = currentGraphId;
        const processQueuedUpdate = async (nodeId) => {
          const state_2 = getPendingState(nodeId);
          if (state_2 && state_2.queuedPosition && state_2.queuedUiProperties && state_2.graphId) {
            const queuedProps = state_2.queuedUiProperties;
            const queuedGraphId = state_2.graphId;
            state_2.queuedPosition = null;
            state_2.queuedUiProperties = null;
            startMutation();
            ;
            try {
              await onPersist(nodeId, queuedGraphId, queuedProps);
              completeMutation(true);
            } catch (t82) {
              const error = t82;
              completeMutation(false);
              onPersistError?.(nodeId, error);
            }
            state_2.inFlight = false;
            processQueuedUpdate(nodeId);
          } else {
            if (state_2) {
              state_2.inFlight = false;
            }
          }
        };
        onPersist(draggedNodeId, currentGraphId, newUiProperties).then(() => {
          completeMutation(true);
          processQueuedUpdate(draggedNodeId);
        }).catch((error_0) => {
          completeMutation(false);
          const state_3 = getPendingState(draggedNodeId);
          if (state_3) {
            state_3.inFlight = false;
          }
          const preDragAttrsForNode = getPreDragAttributes;
          if (preDragAttrsForNode && preDragAttrsForNode.dbData.id === draggedNodeId && graph.hasNode(draggedNodeId)) {
            graph.replaceNodeAttributes(draggedNodeId, preDragAttrsForNode);
            setGraph(graph);
          }
          onPersistError?.(draggedNodeId, error_0);
          processQueuedUpdate(draggedNodeId);
        }).finally(() => {
          setEndDrag({
            nodeId: draggedNodeId
          });
        });
      });
    };
    $[22] = completeMutation;
    $[23] = currentGraphId;
    $[24] = currentZoom;
    $[25] = edgeCreation;
    $[26] = getPreDragAttributes;
    $[27] = getSelectedNodeIds;
    $[28] = graph;
    $[29] = id;
    $[30] = isSpaceHeld;
    $[31] = nestNodesOnDrop;
    $[32] = onPersist;
    $[33] = onPersistError;
    $[34] = setDropTarget;
    $[35] = setEndDrag;
    $[36] = setGraph;
    $[37] = startMutation;
    $[38] = updateNodePositions;
    $[39] = t7;
  } else {
    t7 = $[39];
  }
  let t8;
  if ($[40] !== t5 || $[41] !== t6 || $[42] !== t7) {
    t8 = {
      onPointerDown: t5,
      onDrag: t6,
      onDragEnd: t7
    };
    $[40] = t5;
    $[41] = t6;
    $[42] = t7;
    $[43] = t8;
  } else {
    t8 = $[43];
  }
  let t9;
  if ($[44] !== inputSource) {
    t9 = getNodeGestureConfig(inputSource);
    $[44] = inputSource;
    $[45] = t9;
  } else {
    t9 = $[45];
  }
  const bind = (0, import_react.useGesture)(t8, t9);
  let t10;
  if ($[46] !== bind || $[47] !== updateNodePositions) {
    t10 = {
      bind,
      updateNodePositions
    };
    $[46] = bind;
    $[47] = updateNodePositions;
    $[48] = t10;
  } else {
    t10 = $[48];
  }
  return t10;
}
function _temp2(get, set, updates) {
  const graph_0 = get(graphAtom);
  updates.forEach((u) => {
    if (graph_0.hasNode(u.id)) {
      graph_0.setNodeAttribute(u.id, "x", u.pos.x);
      graph_0.setNodeAttribute(u.id, "y", u.pos.y);
    }
  });
  set(nodePositionUpdateCounterAtom, _temp);
}
function _temp(c) {
  return c + 1;
}

// src/hooks/useNodeResize.ts
var import_compiler_runtime3 = require("react/compiler-runtime");
var import_jotai17 = require("jotai");
var import_react3 = require("react");
var import_react_dom = require("react-dom");
var debug10 = createDebug("resize");
function useNodeResize(t0) {
  const $ = (0, import_compiler_runtime3.c)(38);
  const {
    id,
    nodeData,
    updateNodePositions,
    options: t1
  } = t0;
  let t2;
  if ($[0] !== t1) {
    t2 = t1 === void 0 ? {} : t1;
    $[0] = t1;
    $[1] = t2;
  } else {
    t2 = $[1];
  }
  const options = t2;
  const {
    onPersist,
    onPersistError,
    minWidth: t3,
    minHeight: t4
  } = options;
  const minWidth = t3 === void 0 ? 200 : t3;
  const minHeight = t4 === void 0 ? 150 : t4;
  const [localWidth, setLocalWidth] = (0, import_react3.useState)(nodeData.width || 500);
  const [localHeight, setLocalHeight] = (0, import_react3.useState)(nodeData.height || 500);
  const [isResizing, setIsResizing] = (0, import_react3.useState)(false);
  const resizeStartRef = (0, import_react3.useRef)(null);
  const graph = (0, import_jotai17.useAtomValue)(graphAtom);
  const currentZoom = (0, import_jotai17.useAtomValue)(zoomAtom);
  const currentGraphId = (0, import_jotai17.useAtomValue)(currentGraphIdAtom);
  const startMutation = (0, import_jotai17.useSetAtom)(startMutationAtom);
  const completeMutation = (0, import_jotai17.useSetAtom)(completeMutationAtom);
  const setGraphUpdateVersion = (0, import_jotai17.useSetAtom)(graphUpdateVersionAtom);
  const setNodePositionUpdateCounter = (0, import_jotai17.useSetAtom)(nodePositionUpdateCounterAtom);
  let t5;
  let t6;
  if ($[2] !== isResizing || $[3] !== nodeData.height || $[4] !== nodeData.width) {
    t5 = () => {
      if (!isResizing) {
        setLocalWidth(nodeData.width || 500);
        setLocalHeight(nodeData.height || 500);
      }
    };
    t6 = [nodeData.width, nodeData.height, isResizing];
    $[2] = isResizing;
    $[3] = nodeData.height;
    $[4] = nodeData.width;
    $[5] = t5;
    $[6] = t6;
  } else {
    t5 = $[5];
    t6 = $[6];
  }
  (0, import_react3.useEffect)(t5, t6);
  let t7;
  if ($[7] !== graph || $[8] !== id || $[9] !== localHeight || $[10] !== localWidth) {
    t7 = (direction) => (e) => {
      e.stopPropagation();
      e.preventDefault();
      setIsResizing(true);
      const nodeAttrs = graph.hasNode(id) ? graph.getNodeAttributes(id) : {
        x: 0,
        y: 0
      };
      resizeStartRef.current = {
        width: localWidth,
        height: localHeight,
        startX: e.clientX,
        startY: e.clientY,
        startNodeX: nodeAttrs.x,
        startNodeY: nodeAttrs.y,
        direction
      };
      e.target.setPointerCapture(e.pointerId);
    };
    $[7] = graph;
    $[8] = id;
    $[9] = localHeight;
    $[10] = localWidth;
    $[11] = t7;
  } else {
    t7 = $[11];
  }
  const createResizeStart = t7;
  let t8;
  if ($[12] !== currentZoom || $[13] !== graph || $[14] !== id || $[15] !== minHeight || $[16] !== minWidth || $[17] !== setGraphUpdateVersion || $[18] !== setNodePositionUpdateCounter || $[19] !== updateNodePositions) {
    t8 = (e_0) => {
      if (!resizeStartRef.current) {
        return;
      }
      e_0.stopPropagation();
      e_0.preventDefault();
      const deltaX = (e_0.clientX - resizeStartRef.current.startX) / currentZoom;
      const deltaY = (e_0.clientY - resizeStartRef.current.startY) / currentZoom;
      const {
        direction: direction_0,
        width: startWidth,
        height: startHeight,
        startNodeX,
        startNodeY
      } = resizeStartRef.current;
      let newWidth = startWidth;
      let newHeight = startHeight;
      let newX = startNodeX;
      let newY = startNodeY;
      if (direction_0.includes("e")) {
        newWidth = Math.max(minWidth, startWidth + deltaX);
      }
      if (direction_0.includes("w")) {
        newWidth = Math.max(minWidth, startWidth - deltaX);
        newX = startNodeX + (startWidth - newWidth);
      }
      if (direction_0.includes("s")) {
        newHeight = Math.max(minHeight, startHeight + deltaY);
      }
      if (direction_0.includes("n")) {
        newHeight = Math.max(minHeight, startHeight - deltaY);
        newY = startNodeY + (startHeight - newHeight);
      }
      if (graph.hasNode(id)) {
        graph.setNodeAttribute(id, "width", newWidth);
        graph.setNodeAttribute(id, "height", newHeight);
        graph.setNodeAttribute(id, "x", newX);
        graph.setNodeAttribute(id, "y", newY);
      }
      (0, import_react_dom.flushSync)(() => {
        setLocalWidth(newWidth);
        setLocalHeight(newHeight);
        setGraphUpdateVersion(_temp3);
      });
      if (direction_0.includes("n") || direction_0.includes("w")) {
        updateNodePositions([{
          id,
          pos: {
            x: newX,
            y: newY
          }
        }]);
      } else {
        setNodePositionUpdateCounter(_temp22);
      }
    };
    $[12] = currentZoom;
    $[13] = graph;
    $[14] = id;
    $[15] = minHeight;
    $[16] = minWidth;
    $[17] = setGraphUpdateVersion;
    $[18] = setNodePositionUpdateCounter;
    $[19] = updateNodePositions;
    $[20] = t8;
  } else {
    t8 = $[20];
  }
  const handleResizeMove = t8;
  let t9;
  if ($[21] !== completeMutation || $[22] !== currentGraphId || $[23] !== graph || $[24] !== id || $[25] !== localHeight || $[26] !== localWidth || $[27] !== onPersist || $[28] !== onPersistError || $[29] !== startMutation) {
    t9 = (e_1) => {
      if (!resizeStartRef.current) {
        return;
      }
      e_1.stopPropagation();
      e_1.target.releasePointerCapture(e_1.pointerId);
      setIsResizing(false);
      if (!currentGraphId || !resizeStartRef.current) {
        resizeStartRef.current = null;
        return;
      }
      const finalAttrs = graph.hasNode(id) ? graph.getNodeAttributes(id) : null;
      if (!finalAttrs) {
        resizeStartRef.current = null;
        return;
      }
      const finalWidth = finalAttrs.width || localWidth;
      const finalHeight = finalAttrs.height || localHeight;
      const finalX = finalAttrs.x;
      const finalY = finalAttrs.y;
      setLocalWidth(finalWidth);
      setLocalHeight(finalHeight);
      if (!onPersist) {
        resizeStartRef.current = null;
        return;
      }
      const existingDbUiProps = typeof finalAttrs.dbData.ui_properties === "object" && finalAttrs.dbData.ui_properties !== null && !Array.isArray(finalAttrs.dbData.ui_properties) ? finalAttrs.dbData.ui_properties : {};
      const newUiProperties = {
        ...existingDbUiProps,
        width: finalWidth,
        height: finalHeight,
        x: finalX,
        y: finalY
      };
      startMutation();
      onPersist(id, currentGraphId, newUiProperties).then(() => {
        completeMutation(true);
      }).catch((error) => {
        completeMutation(false);
        if (resizeStartRef.current) {
          setLocalWidth(resizeStartRef.current.width);
          setLocalHeight(resizeStartRef.current.height);
        }
        onPersistError?.(id, error);
      }).finally(() => {
        resizeStartRef.current = null;
      });
    };
    $[21] = completeMutation;
    $[22] = currentGraphId;
    $[23] = graph;
    $[24] = id;
    $[25] = localHeight;
    $[26] = localWidth;
    $[27] = onPersist;
    $[28] = onPersistError;
    $[29] = startMutation;
    $[30] = t9;
  } else {
    t9 = $[30];
  }
  const handleResizeEnd = t9;
  let t10;
  if ($[31] !== createResizeStart || $[32] !== handleResizeEnd || $[33] !== handleResizeMove || $[34] !== isResizing || $[35] !== localHeight || $[36] !== localWidth) {
    t10 = {
      localWidth,
      localHeight,
      isResizing,
      createResizeStart,
      handleResizeMove,
      handleResizeEnd
    };
    $[31] = createResizeStart;
    $[32] = handleResizeEnd;
    $[33] = handleResizeMove;
    $[34] = isResizing;
    $[35] = localHeight;
    $[36] = localWidth;
    $[37] = t10;
  } else {
    t10 = $[37];
  }
  return t10;
}
function _temp22(c) {
  return c + 1;
}
function _temp3(v) {
  return v + 1;
}

// src/hooks/useCanvasHistory.ts
var import_compiler_runtime4 = require("react/compiler-runtime");
var import_jotai19 = require("jotai");

// src/core/toast-store.ts
var import_jotai18 = require("jotai");
var canvasToastAtom = (0, import_jotai18.atom)(null);
var showToastAtom = (0, import_jotai18.atom)(null, (_get, set, message) => {
  const id = `toast-${Date.now()}`;
  set(canvasToastAtom, {
    id,
    message,
    timestamp: Date.now()
  });
  setTimeout(() => {
    set(canvasToastAtom, (current) => current?.id === id ? null : current);
  }, 2e3);
});

// src/hooks/useCanvasHistory.ts
function useCanvasHistory(t0) {
  const $ = (0, import_compiler_runtime4.c)(22);
  const options = t0 === void 0 ? {} : t0;
  const {
    enableKeyboardShortcuts: t1
  } = options;
  t1 === void 0 ? false : t1;
  const canUndo = (0, import_jotai19.useAtomValue)(canUndoAtom);
  const canRedo = (0, import_jotai19.useAtomValue)(canRedoAtom);
  const undoCount = (0, import_jotai19.useAtomValue)(undoCountAtom);
  const redoCount = (0, import_jotai19.useAtomValue)(redoCountAtom);
  const historyLabels = (0, import_jotai19.useAtomValue)(historyLabelsAtom);
  const undoAction = (0, import_jotai19.useSetAtom)(undoAtom);
  const redoAction = (0, import_jotai19.useSetAtom)(redoAtom);
  const pushHistory = (0, import_jotai19.useSetAtom)(pushHistoryAtom);
  const clearHistory = (0, import_jotai19.useSetAtom)(clearHistoryAtom);
  const showToast = (0, import_jotai19.useSetAtom)(showToastAtom);
  const store = (0, import_jotai19.useStore)();
  let t2;
  if ($[0] !== showToast || $[1] !== store || $[2] !== undoAction) {
    t2 = () => {
      const state = store.get(historyStateAtom);
      const label = state.past[state.past.length - 1]?.label;
      const result = undoAction();
      if (result && label) {
        showToast(`Undo: ${label}`);
      }
      return result;
    };
    $[0] = showToast;
    $[1] = store;
    $[2] = undoAction;
    $[3] = t2;
  } else {
    t2 = $[3];
  }
  const undo = t2;
  let t3;
  if ($[4] !== redoAction || $[5] !== showToast || $[6] !== store) {
    t3 = () => {
      const state_0 = store.get(historyStateAtom);
      const label_0 = state_0.future[0]?.label;
      const result_0 = redoAction();
      if (result_0 && label_0) {
        showToast(`Redo: ${label_0}`);
      }
      return result_0;
    };
    $[4] = redoAction;
    $[5] = showToast;
    $[6] = store;
    $[7] = t3;
  } else {
    t3 = $[7];
  }
  const redo = t3;
  let t4;
  if ($[8] !== pushHistory) {
    t4 = (label_1) => {
      pushHistory(label_1);
    };
    $[8] = pushHistory;
    $[9] = t4;
  } else {
    t4 = $[9];
  }
  const recordSnapshot = t4;
  let t5;
  if ($[10] !== clearHistory) {
    t5 = () => {
      clearHistory();
    };
    $[10] = clearHistory;
    $[11] = t5;
  } else {
    t5 = $[11];
  }
  const clear = t5;
  let t6;
  if ($[12] !== canRedo || $[13] !== canUndo || $[14] !== clear || $[15] !== historyLabels || $[16] !== recordSnapshot || $[17] !== redo || $[18] !== redoCount || $[19] !== undo || $[20] !== undoCount) {
    t6 = {
      undo,
      redo,
      canUndo,
      canRedo,
      undoCount,
      redoCount,
      historyLabels,
      recordSnapshot,
      clear
    };
    $[12] = canRedo;
    $[13] = canUndo;
    $[14] = clear;
    $[15] = historyLabels;
    $[16] = recordSnapshot;
    $[17] = redo;
    $[18] = redoCount;
    $[19] = undo;
    $[20] = undoCount;
    $[21] = t6;
  } else {
    t6 = $[21];
  }
  return t6;
}

// src/hooks/useCanvasSelection.ts
var import_compiler_runtime5 = require("react/compiler-runtime");
var import_jotai20 = require("jotai");
function useCanvasSelection() {
  const $ = (0, import_compiler_runtime5.c)(6);
  const selectedNodeIds = (0, import_jotai20.useAtomValue)(selectedNodeIdsAtom);
  const selectedEdgeId = (0, import_jotai20.useAtomValue)(selectedEdgeIdAtom);
  const count = (0, import_jotai20.useAtomValue)(selectedNodesCountAtom);
  const hasSelection = (0, import_jotai20.useAtomValue)(hasSelectionAtom);
  const t0 = selectedEdgeId !== null;
  let t1;
  if ($[0] !== count || $[1] !== hasSelection || $[2] !== selectedEdgeId || $[3] !== selectedNodeIds || $[4] !== t0) {
    t1 = {
      selectedNodeIds,
      selectedEdgeId,
      count,
      hasSelection,
      hasEdgeSelection: t0
    };
    $[0] = count;
    $[1] = hasSelection;
    $[2] = selectedEdgeId;
    $[3] = selectedNodeIds;
    $[4] = t0;
    $[5] = t1;
  } else {
    t1 = $[5];
  }
  return t1;
}

// src/hooks/useCanvasViewport.ts
var import_compiler_runtime6 = require("react/compiler-runtime");
var import_jotai21 = require("jotai");
function useCanvasViewport() {
  const $ = (0, import_compiler_runtime6.c)(9);
  const zoom = (0, import_jotai21.useAtomValue)(zoomAtom);
  const pan = (0, import_jotai21.useAtomValue)(panAtom);
  const viewportRect = (0, import_jotai21.useAtomValue)(viewportRectAtom);
  const screenToWorld = (0, import_jotai21.useAtomValue)(screenToWorldAtom);
  const worldToScreen = (0, import_jotai21.useAtomValue)(worldToScreenAtom);
  const zoomFocusNodeId = (0, import_jotai21.useAtomValue)(zoomFocusNodeIdAtom);
  const zoomTransitionProgress = (0, import_jotai21.useAtomValue)(zoomTransitionProgressAtom);
  const isZoomTransitioning = (0, import_jotai21.useAtomValue)(isZoomTransitioningAtom);
  let t0;
  if ($[0] !== isZoomTransitioning || $[1] !== pan || $[2] !== screenToWorld || $[3] !== viewportRect || $[4] !== worldToScreen || $[5] !== zoom || $[6] !== zoomFocusNodeId || $[7] !== zoomTransitionProgress) {
    t0 = {
      zoom,
      pan,
      viewportRect,
      screenToWorld,
      worldToScreen,
      zoomFocusNodeId,
      zoomTransitionProgress,
      isZoomTransitioning,
      zoomTransitionThreshold: ZOOM_TRANSITION_THRESHOLD,
      zoomExitThreshold: ZOOM_EXIT_THRESHOLD
    };
    $[0] = isZoomTransitioning;
    $[1] = pan;
    $[2] = screenToWorld;
    $[3] = viewportRect;
    $[4] = worldToScreen;
    $[5] = zoom;
    $[6] = zoomFocusNodeId;
    $[7] = zoomTransitionProgress;
    $[8] = t0;
  } else {
    t0 = $[8];
  }
  return t0;
}

// src/hooks/useCanvasDrag.ts
var import_compiler_runtime7 = require("react/compiler-runtime");
var import_jotai22 = require("jotai");
function useCanvasDrag() {
  const $ = (0, import_compiler_runtime7.c)(3);
  const draggingNodeId = (0, import_jotai22.useAtomValue)(draggingNodeIdAtom);
  const t0 = draggingNodeId !== null;
  let t1;
  if ($[0] !== draggingNodeId || $[1] !== t0) {
    t1 = {
      draggingNodeId,
      isDragging: t0
    };
    $[0] = draggingNodeId;
    $[1] = t0;
    $[2] = t1;
  } else {
    t1 = $[2];
  }
  return t1;
}

// src/hooks/useLayout.ts
var import_compiler_runtime8 = require("react/compiler-runtime");
var import_jotai23 = require("jotai");
var useGetGraphBounds = () => {
  const $ = (0, import_compiler_runtime8.c)(6);
  const graph = (0, import_jotai23.useAtomValue)(graphAtom);
  (0, import_jotai23.useAtomValue)(nodePositionUpdateCounterAtom);
  let nodes;
  let t0;
  if ($[0] !== graph) {
    nodes = graph.nodes().map((node) => {
      const nodeAttributes = graph.getNodeAttributes(node);
      return {
        x: nodeAttributes.x,
        y: nodeAttributes.y,
        width: nodeAttributes.width || 500,
        height: nodeAttributes.height || 500
      };
    });
    t0 = calculateBounds(nodes);
    $[0] = graph;
    $[1] = nodes;
    $[2] = t0;
  } else {
    nodes = $[1];
    t0 = $[2];
  }
  const bounds = t0;
  let t1;
  if ($[3] !== bounds || $[4] !== nodes) {
    t1 = {
      bounds,
      nodes
    };
    $[3] = bounds;
    $[4] = nodes;
    $[5] = t1;
  } else {
    t1 = $[5];
  }
  return t1;
};
var useSelectionBounds = () => {
  const $ = (0, import_compiler_runtime8.c)(5);
  const selectedNodeIds = (0, import_jotai23.useAtomValue)(selectedNodeIdsAtom);
  const nodes = (0, import_jotai23.useAtomValue)(uiNodesAtom);
  let t0;
  if ($[0] !== nodes || $[1] !== selectedNodeIds) {
    let t1;
    if ($[3] !== selectedNodeIds) {
      t1 = (node) => selectedNodeIds.has(node.id);
      $[3] = selectedNodeIds;
      $[4] = t1;
    } else {
      t1 = $[4];
    }
    const selectedNodes = nodes.filter(t1).map(_temp4);
    t0 = calculateBounds(selectedNodes);
    $[0] = nodes;
    $[1] = selectedNodeIds;
    $[2] = t0;
  } else {
    t0 = $[2];
  }
  return t0;
};
var useFitToBounds = () => {
  const $ = (0, import_compiler_runtime8.c)(2);
  const setFitToBounds = (0, import_jotai23.useSetAtom)(fitToBoundsAtom);
  let t0;
  if ($[0] !== setFitToBounds) {
    const fitToBounds = (mode, t1) => {
      const padding = t1 === void 0 ? 20 : t1;
      setFitToBounds({
        mode,
        padding
      });
    };
    t0 = {
      fitToBounds
    };
    $[0] = setFitToBounds;
    $[1] = t0;
  } else {
    t0 = $[1];
  }
  return t0;
};
var useLayout = () => {
  const $ = (0, import_compiler_runtime8.c)(5);
  const {
    fitToBounds
  } = useFitToBounds();
  const {
    bounds: graphBounds,
    nodes: graphNodes
  } = useGetGraphBounds();
  const selectionBounds = useSelectionBounds();
  let t0;
  if ($[0] !== fitToBounds || $[1] !== graphBounds || $[2] !== graphNodes || $[3] !== selectionBounds) {
    t0 = {
      fitToBounds,
      graphBounds,
      graphNodes,
      selectionBounds
    };
    $[0] = fitToBounds;
    $[1] = graphBounds;
    $[2] = graphNodes;
    $[3] = selectionBounds;
    $[4] = t0;
  } else {
    t0 = $[4];
  }
  return t0;
};
function _temp4(node_0) {
  return {
    x: node_0.position.x,
    y: node_0.position.y,
    width: node_0.width ?? 500,
    height: node_0.height ?? 500
  };
}

// src/hooks/useForceLayout.ts
var d3 = __toESM(require("d3-force"));
var import_jotai24 = require("jotai");
var import_react4 = require("react");
var debug11 = createDebug("force-layout");
var useForceLayout = (options = {}) => {
  const {
    onPositionsChanged,
    maxIterations = 1e3,
    chargeStrength = -6e3,
    linkStrength = 0.03
  } = options;
  const nodes = (0, import_jotai24.useAtomValue)(uiNodesAtom);
  const graph = (0, import_jotai24.useAtomValue)(graphAtom);
  const updateNodePosition = (0, import_jotai24.useSetAtom)(updateNodePositionAtom);
  const isRunningRef = (0, import_react4.useRef)(false);
  const createVirtualLinks = () => {
    const links = [];
    for (let i = 0; i < nodes.length; i++) {
      for (let j = 1; j <= 3; j++) {
        const targetIndex = (i + j) % nodes.length;
        links.push({
          source: nodes[i].id,
          target: nodes[targetIndex].id,
          strength: 0.05
          // Very weak connection
        });
      }
    }
    return links;
  };
  const applyForceLayout = async () => {
    if (isRunningRef.current) {
      debug11.warn("Layout already running, ignoring request.");
      return;
    }
    if (nodes.length === 0) {
      debug11.warn("No nodes to layout.");
      return;
    }
    isRunningRef.current = true;
    const simulationNodes = nodes.map((node) => {
      const width = node.width || 80;
      const height = node.height || 80;
      return {
        id: node.id,
        x: node.position?.x || 0,
        y: node.position?.y || 0,
        width,
        height,
        radius: Math.max(width, height) + 80
      };
    });
    const simulation = d3.forceSimulation(simulationNodes).force("charge", d3.forceManyBody().strength(chargeStrength).distanceMax(900)).force("collide", d3.forceCollide().radius((d) => d.radius).strength(2).iterations(8)).force("link", d3.forceLink(createVirtualLinks()).id((d_0) => d_0.id).strength(linkStrength)).force("center", d3.forceCenter(0, 0)).stop();
    debug11("Starting simulation...");
    return new Promise((resolve) => {
      let iterations = 0;
      function runSimulationStep() {
        if (iterations >= maxIterations) {
          debug11("Reached max iterations (%d), finalizing.", maxIterations);
          finalizeLayout();
          return;
        }
        simulation.tick();
        iterations++;
        let hasOverlaps = false;
        for (let i_0 = 0; i_0 < simulationNodes.length; i_0++) {
          for (let j_0 = i_0 + 1; j_0 < simulationNodes.length; j_0++) {
            if (checkNodesOverlap(simulationNodes[i_0], simulationNodes[j_0])) {
              hasOverlaps = true;
              break;
            }
          }
          if (hasOverlaps) break;
        }
        if (!hasOverlaps) {
          debug11("No overlaps after %d iterations.", iterations);
          finalizeLayout();
          return;
        }
        requestAnimationFrame(runSimulationStep);
      }
      function finalizeLayout() {
        const positionUpdates = [];
        simulationNodes.forEach((simNode) => {
          if (graph.hasNode(simNode.id)) {
            const newPosition = {
              x: Math.round(simNode.x),
              y: Math.round(simNode.y)
            };
            updateNodePosition({
              nodeId: simNode.id,
              position: newPosition
            });
            positionUpdates.push({
              nodeId: simNode.id,
              position: newPosition
            });
          }
        });
        if (onPositionsChanged && positionUpdates.length > 0) {
          debug11("Saving %d positions via callback...", positionUpdates.length);
          Promise.resolve(onPositionsChanged(positionUpdates)).then(() => debug11("Positions saved successfully.")).catch((err) => debug11.error("Error saving positions: %O", err));
        }
        debug11("Layout complete.");
        isRunningRef.current = false;
        resolve();
      }
      requestAnimationFrame(runSimulationStep);
    });
  };
  return {
    applyForceLayout,
    isRunning: isRunningRef.current
  };
};

// src/hooks/useCanvasSettings.ts
var import_compiler_runtime9 = require("react/compiler-runtime");
var import_jotai26 = require("jotai");

// src/core/settings-store.ts
var import_jotai25 = require("jotai");
var import_utils = require("jotai/utils");

// src/core/event-types.ts
var CanvasEventType = /* @__PURE__ */ (function(CanvasEventType2) {
  CanvasEventType2["NodeClick"] = "node:click";
  CanvasEventType2["NodeDoubleClick"] = "node:double-click";
  CanvasEventType2["NodeTripleClick"] = "node:triple-click";
  CanvasEventType2["NodeRightClick"] = "node:right-click";
  CanvasEventType2["NodeLongPress"] = "node:long-press";
  CanvasEventType2["EdgeClick"] = "edge:click";
  CanvasEventType2["EdgeDoubleClick"] = "edge:double-click";
  CanvasEventType2["EdgeRightClick"] = "edge:right-click";
  CanvasEventType2["BackgroundClick"] = "background:click";
  CanvasEventType2["BackgroundDoubleClick"] = "background:double-click";
  CanvasEventType2["BackgroundRightClick"] = "background:right-click";
  CanvasEventType2["BackgroundLongPress"] = "background:long-press";
  return CanvasEventType2;
})({});
var EVENT_TYPE_INFO = {
  [CanvasEventType.NodeClick]: {
    type: CanvasEventType.NodeClick,
    label: "Click Node",
    description: "Triggered when clicking on a node",
    category: "node"
  },
  [CanvasEventType.NodeDoubleClick]: {
    type: CanvasEventType.NodeDoubleClick,
    label: "Double-click Node",
    description: "Triggered when double-clicking on a node",
    category: "node"
  },
  [CanvasEventType.NodeTripleClick]: {
    type: CanvasEventType.NodeTripleClick,
    label: "Triple-click Node",
    description: "Triggered when triple-clicking on a node",
    category: "node"
  },
  [CanvasEventType.NodeRightClick]: {
    type: CanvasEventType.NodeRightClick,
    label: "Right-click Node",
    description: "Triggered when right-clicking on a node",
    category: "node"
  },
  [CanvasEventType.NodeLongPress]: {
    type: CanvasEventType.NodeLongPress,
    label: "Long-press Node",
    description: "Triggered when long-pressing on a node (mobile/touch)",
    category: "node"
  },
  [CanvasEventType.EdgeClick]: {
    type: CanvasEventType.EdgeClick,
    label: "Click Edge",
    description: "Triggered when clicking on an edge",
    category: "edge"
  },
  [CanvasEventType.EdgeDoubleClick]: {
    type: CanvasEventType.EdgeDoubleClick,
    label: "Double-click Edge",
    description: "Triggered when double-clicking on an edge",
    category: "edge"
  },
  [CanvasEventType.EdgeRightClick]: {
    type: CanvasEventType.EdgeRightClick,
    label: "Right-click Edge",
    description: "Triggered when right-clicking on an edge",
    category: "edge"
  },
  [CanvasEventType.BackgroundClick]: {
    type: CanvasEventType.BackgroundClick,
    label: "Click Background",
    description: "Triggered when clicking on the canvas background",
    category: "background"
  },
  [CanvasEventType.BackgroundDoubleClick]: {
    type: CanvasEventType.BackgroundDoubleClick,
    label: "Double-click Background",
    description: "Triggered when double-clicking on the canvas background",
    category: "background"
  },
  [CanvasEventType.BackgroundRightClick]: {
    type: CanvasEventType.BackgroundRightClick,
    label: "Right-click Background",
    description: "Triggered when right-clicking on the canvas background",
    category: "background"
  },
  [CanvasEventType.BackgroundLongPress]: {
    type: CanvasEventType.BackgroundLongPress,
    label: "Long-press Background",
    description: "Triggered when long-pressing on the canvas background (mobile/touch)",
    category: "background"
  }
};

// src/core/action-types.ts
var ActionCategory = /* @__PURE__ */ (function(ActionCategory2) {
  ActionCategory2["None"] = "none";
  ActionCategory2["Selection"] = "selection";
  ActionCategory2["Viewport"] = "viewport";
  ActionCategory2["Node"] = "node";
  ActionCategory2["Layout"] = "layout";
  ActionCategory2["History"] = "history";
  ActionCategory2["Custom"] = "custom";
  return ActionCategory2;
})({});
var BuiltInActionId = {
  // None
  None: "none",
  // Selection
  SelectNode: "select-node",
  SelectEdge: "select-edge",
  AddToSelection: "add-to-selection",
  ClearSelection: "clear-selection",
  DeleteSelected: "delete-selected",
  // Viewport
  FitToView: "fit-to-view",
  FitAllToView: "fit-all-to-view",
  CenterOnNode: "center-on-node",
  ResetViewport: "reset-viewport",
  // Node
  LockNode: "lock-node",
  UnlockNode: "unlock-node",
  ToggleLock: "toggle-lock",
  OpenContextMenu: "open-context-menu",
  SplitNode: "split-node",
  GroupNodes: "group-nodes",
  MergeNodes: "merge-nodes",
  // Layout
  ApplyForceLayout: "apply-force-layout",
  // History
  Undo: "undo",
  Redo: "redo",
  // Creation
  CreateNode: "create-node"
};

// src/core/settings-state-types.ts
var DEFAULT_MAPPINGS = {
  [CanvasEventType.NodeClick]: BuiltInActionId.None,
  [CanvasEventType.NodeDoubleClick]: BuiltInActionId.FitToView,
  [CanvasEventType.NodeTripleClick]: BuiltInActionId.ToggleLock,
  [CanvasEventType.NodeRightClick]: BuiltInActionId.OpenContextMenu,
  [CanvasEventType.NodeLongPress]: BuiltInActionId.OpenContextMenu,
  [CanvasEventType.EdgeClick]: BuiltInActionId.SelectEdge,
  [CanvasEventType.EdgeDoubleClick]: BuiltInActionId.None,
  [CanvasEventType.EdgeRightClick]: BuiltInActionId.OpenContextMenu,
  [CanvasEventType.BackgroundClick]: BuiltInActionId.ClearSelection,
  [CanvasEventType.BackgroundDoubleClick]: BuiltInActionId.FitAllToView,
  [CanvasEventType.BackgroundRightClick]: BuiltInActionId.None,
  [CanvasEventType.BackgroundLongPress]: BuiltInActionId.CreateNode
};

// src/core/settings-presets.ts
var BUILT_IN_PRESETS = [{
  id: "default",
  name: "Default",
  description: "Standard canvas interactions",
  isBuiltIn: true,
  mappings: DEFAULT_MAPPINGS
}, {
  id: "minimal",
  name: "Minimal",
  description: "Only essential selection and context menu actions",
  isBuiltIn: true,
  mappings: {
    [CanvasEventType.NodeClick]: BuiltInActionId.None,
    [CanvasEventType.NodeDoubleClick]: BuiltInActionId.None,
    [CanvasEventType.NodeTripleClick]: BuiltInActionId.None,
    [CanvasEventType.NodeRightClick]: BuiltInActionId.OpenContextMenu,
    [CanvasEventType.NodeLongPress]: BuiltInActionId.OpenContextMenu,
    [CanvasEventType.EdgeClick]: BuiltInActionId.SelectEdge,
    [CanvasEventType.EdgeDoubleClick]: BuiltInActionId.None,
    [CanvasEventType.EdgeRightClick]: BuiltInActionId.None,
    [CanvasEventType.BackgroundClick]: BuiltInActionId.ClearSelection,
    [CanvasEventType.BackgroundDoubleClick]: BuiltInActionId.None,
    [CanvasEventType.BackgroundRightClick]: BuiltInActionId.None,
    [CanvasEventType.BackgroundLongPress]: BuiltInActionId.None
  }
}, {
  id: "power-user",
  name: "Power User",
  description: "Quick actions for experienced users",
  isBuiltIn: true,
  mappings: {
    [CanvasEventType.NodeClick]: BuiltInActionId.None,
    [CanvasEventType.NodeDoubleClick]: BuiltInActionId.ToggleLock,
    [CanvasEventType.NodeTripleClick]: BuiltInActionId.DeleteSelected,
    [CanvasEventType.NodeRightClick]: BuiltInActionId.OpenContextMenu,
    [CanvasEventType.NodeLongPress]: BuiltInActionId.AddToSelection,
    [CanvasEventType.EdgeClick]: BuiltInActionId.SelectEdge,
    [CanvasEventType.EdgeDoubleClick]: BuiltInActionId.None,
    [CanvasEventType.EdgeRightClick]: BuiltInActionId.OpenContextMenu,
    [CanvasEventType.BackgroundClick]: BuiltInActionId.ClearSelection,
    [CanvasEventType.BackgroundDoubleClick]: BuiltInActionId.CreateNode,
    [CanvasEventType.BackgroundRightClick]: BuiltInActionId.OpenContextMenu,
    [CanvasEventType.BackgroundLongPress]: BuiltInActionId.ApplyForceLayout
  }
}];
function getActionForEvent(mappings, event) {
  return mappings[event] || BuiltInActionId.None;
}

// src/core/settings-store.ts
var debug12 = createDebug("settings");
var DEFAULT_STATE = {
  mappings: DEFAULT_MAPPINGS,
  activePresetId: "default",
  customPresets: [],
  isPanelOpen: false,
  virtualizationEnabled: true
};
var canvasSettingsAtom = (0, import_utils.atomWithStorage)("@blinksgg/canvas/settings", DEFAULT_STATE);
var eventMappingsAtom = (0, import_jotai25.atom)((get) => get(canvasSettingsAtom).mappings);
var activePresetIdAtom = (0, import_jotai25.atom)((get) => get(canvasSettingsAtom).activePresetId);
var allPresetsAtom = (0, import_jotai25.atom)((get) => {
  const state = get(canvasSettingsAtom);
  return [...BUILT_IN_PRESETS, ...state.customPresets];
});
var activePresetAtom = (0, import_jotai25.atom)((get) => {
  const presetId = get(activePresetIdAtom);
  if (!presetId) return null;
  const allPresets = get(allPresetsAtom);
  return allPresets.find((p) => p.id === presetId) || null;
});
var isPanelOpenAtom = (0, import_jotai25.atom)((get) => get(canvasSettingsAtom).isPanelOpen);
var virtualizationEnabledAtom = (0, import_jotai25.atom)((get) => get(canvasSettingsAtom).virtualizationEnabled ?? true);
var hasUnsavedChangesAtom = (0, import_jotai25.atom)((get) => {
  const state = get(canvasSettingsAtom);
  const activePreset = get(activePresetAtom);
  if (!activePreset) return true;
  const events = Object.values(CanvasEventType);
  return events.some((event) => state.mappings[event] !== activePreset.mappings[event]);
});
var setEventMappingAtom = (0, import_jotai25.atom)(null, (get, set, {
  event,
  actionId
}) => {
  const current = get(canvasSettingsAtom);
  set(canvasSettingsAtom, {
    ...current,
    mappings: {
      ...current.mappings,
      [event]: actionId
    },
    // Clear active preset since mappings have changed
    activePresetId: null
  });
});
var applyPresetAtom = (0, import_jotai25.atom)(null, (get, set, presetId) => {
  const allPresets = get(allPresetsAtom);
  const preset = allPresets.find((p) => p.id === presetId);
  if (!preset) {
    debug12.warn("Preset not found: %s", presetId);
    return;
  }
  const current = get(canvasSettingsAtom);
  set(canvasSettingsAtom, {
    ...current,
    mappings: {
      ...preset.mappings
    },
    activePresetId: presetId
  });
});
var saveAsPresetAtom = (0, import_jotai25.atom)(null, (get, set, {
  name,
  description
}) => {
  const current = get(canvasSettingsAtom);
  const id = `custom-${Date.now()}`;
  const newPreset = {
    id,
    name,
    description,
    mappings: {
      ...current.mappings
    },
    isBuiltIn: false
  };
  set(canvasSettingsAtom, {
    ...current,
    customPresets: [...current.customPresets, newPreset],
    activePresetId: id
  });
  return id;
});
var updatePresetAtom = (0, import_jotai25.atom)(null, (get, set, presetId) => {
  const current = get(canvasSettingsAtom);
  const presetIndex = current.customPresets.findIndex((p) => p.id === presetId);
  if (presetIndex === -1) {
    debug12.warn("Cannot update preset: %s (not found or built-in)", presetId);
    return;
  }
  const updatedPresets = [...current.customPresets];
  updatedPresets[presetIndex] = {
    ...updatedPresets[presetIndex],
    mappings: {
      ...current.mappings
    }
  };
  set(canvasSettingsAtom, {
    ...current,
    customPresets: updatedPresets,
    activePresetId: presetId
  });
});
var deletePresetAtom = (0, import_jotai25.atom)(null, (get, set, presetId) => {
  const current = get(canvasSettingsAtom);
  const newCustomPresets = current.customPresets.filter((p) => p.id !== presetId);
  if (newCustomPresets.length === current.customPresets.length) {
    debug12.warn("Cannot delete preset: %s (not found or built-in)", presetId);
    return;
  }
  const newActiveId = current.activePresetId === presetId ? "default" : current.activePresetId;
  const newMappings = newActiveId === "default" ? DEFAULT_MAPPINGS : current.mappings;
  set(canvasSettingsAtom, {
    ...current,
    customPresets: newCustomPresets,
    activePresetId: newActiveId,
    mappings: newMappings
  });
});
var resetSettingsAtom = (0, import_jotai25.atom)(null, (get, set) => {
  const current = get(canvasSettingsAtom);
  set(canvasSettingsAtom, {
    ...current,
    mappings: DEFAULT_MAPPINGS,
    activePresetId: "default"
  });
});
var togglePanelAtom = (0, import_jotai25.atom)(null, (get, set) => {
  const current = get(canvasSettingsAtom);
  set(canvasSettingsAtom, {
    ...current,
    isPanelOpen: !current.isPanelOpen
  });
});
var setPanelOpenAtom = (0, import_jotai25.atom)(null, (get, set, isOpen) => {
  const current = get(canvasSettingsAtom);
  set(canvasSettingsAtom, {
    ...current,
    isPanelOpen: isOpen
  });
});
var setVirtualizationEnabledAtom = (0, import_jotai25.atom)(null, (get, set, enabled) => {
  const current = get(canvasSettingsAtom);
  set(canvasSettingsAtom, {
    ...current,
    virtualizationEnabled: enabled
  });
});
var toggleVirtualizationAtom = (0, import_jotai25.atom)(null, (get, set) => {
  const current = get(canvasSettingsAtom);
  set(canvasSettingsAtom, {
    ...current,
    virtualizationEnabled: !(current.virtualizationEnabled ?? true)
  });
});

// src/hooks/useCanvasSettings.ts
function useCanvasSettings() {
  const $ = (0, import_compiler_runtime9.c)(34);
  const mappings = (0, import_jotai26.useAtomValue)(eventMappingsAtom);
  const activePresetId = (0, import_jotai26.useAtomValue)(activePresetIdAtom);
  const activePreset = (0, import_jotai26.useAtomValue)(activePresetAtom);
  const allPresets = (0, import_jotai26.useAtomValue)(allPresetsAtom);
  const hasUnsavedChanges = (0, import_jotai26.useAtomValue)(hasUnsavedChangesAtom);
  const isPanelOpen = (0, import_jotai26.useAtomValue)(isPanelOpenAtom);
  const setEventMappingAction = (0, import_jotai26.useSetAtom)(setEventMappingAtom);
  const applyPresetAction = (0, import_jotai26.useSetAtom)(applyPresetAtom);
  const saveAsPresetAction = (0, import_jotai26.useSetAtom)(saveAsPresetAtom);
  const updatePresetAction = (0, import_jotai26.useSetAtom)(updatePresetAtom);
  const deletePresetAction = (0, import_jotai26.useSetAtom)(deletePresetAtom);
  const resetSettingsAction = (0, import_jotai26.useSetAtom)(resetSettingsAtom);
  const togglePanelAction = (0, import_jotai26.useSetAtom)(togglePanelAtom);
  const setPanelOpenAction = (0, import_jotai26.useSetAtom)(setPanelOpenAtom);
  let t0;
  if ($[0] !== setEventMappingAction) {
    t0 = (event, actionId) => {
      setEventMappingAction({
        event,
        actionId
      });
    };
    $[0] = setEventMappingAction;
    $[1] = t0;
  } else {
    t0 = $[1];
  }
  const setEventMapping = t0;
  let t1;
  if ($[2] !== applyPresetAction) {
    t1 = (presetId) => {
      applyPresetAction(presetId);
    };
    $[2] = applyPresetAction;
    $[3] = t1;
  } else {
    t1 = $[3];
  }
  const applyPreset = t1;
  let t2;
  if ($[4] !== saveAsPresetAction) {
    t2 = (name, description) => saveAsPresetAction({
      name,
      description
    });
    $[4] = saveAsPresetAction;
    $[5] = t2;
  } else {
    t2 = $[5];
  }
  const saveAsPreset = t2;
  let t3;
  if ($[6] !== updatePresetAction) {
    t3 = (presetId_0) => {
      updatePresetAction(presetId_0);
    };
    $[6] = updatePresetAction;
    $[7] = t3;
  } else {
    t3 = $[7];
  }
  const updatePreset = t3;
  let t4;
  if ($[8] !== deletePresetAction) {
    t4 = (presetId_1) => {
      deletePresetAction(presetId_1);
    };
    $[8] = deletePresetAction;
    $[9] = t4;
  } else {
    t4 = $[9];
  }
  const deletePreset = t4;
  let t5;
  if ($[10] !== resetSettingsAction) {
    t5 = () => {
      resetSettingsAction();
    };
    $[10] = resetSettingsAction;
    $[11] = t5;
  } else {
    t5 = $[11];
  }
  const resetSettings = t5;
  let t6;
  if ($[12] !== togglePanelAction) {
    t6 = () => {
      togglePanelAction();
    };
    $[12] = togglePanelAction;
    $[13] = t6;
  } else {
    t6 = $[13];
  }
  const togglePanel = t6;
  let t7;
  if ($[14] !== setPanelOpenAction) {
    t7 = (isOpen) => {
      setPanelOpenAction(isOpen);
    };
    $[14] = setPanelOpenAction;
    $[15] = t7;
  } else {
    t7 = $[15];
  }
  const setPanelOpen = t7;
  let t8;
  if ($[16] !== mappings) {
    t8 = (event_0) => getActionForEvent(mappings, event_0);
    $[16] = mappings;
    $[17] = t8;
  } else {
    t8 = $[17];
  }
  const getActionForEventFn = t8;
  let t9;
  if ($[18] !== activePreset || $[19] !== activePresetId || $[20] !== allPresets || $[21] !== applyPreset || $[22] !== deletePreset || $[23] !== getActionForEventFn || $[24] !== hasUnsavedChanges || $[25] !== isPanelOpen || $[26] !== mappings || $[27] !== resetSettings || $[28] !== saveAsPreset || $[29] !== setEventMapping || $[30] !== setPanelOpen || $[31] !== togglePanel || $[32] !== updatePreset) {
    t9 = {
      mappings,
      activePresetId,
      activePreset,
      allPresets,
      hasUnsavedChanges,
      isPanelOpen,
      setEventMapping,
      applyPreset,
      saveAsPreset,
      updatePreset,
      deletePreset,
      resetSettings,
      togglePanel,
      setPanelOpen,
      getActionForEvent: getActionForEventFn
    };
    $[18] = activePreset;
    $[19] = activePresetId;
    $[20] = allPresets;
    $[21] = applyPreset;
    $[22] = deletePreset;
    $[23] = getActionForEventFn;
    $[24] = hasUnsavedChanges;
    $[25] = isPanelOpen;
    $[26] = mappings;
    $[27] = resetSettings;
    $[28] = saveAsPreset;
    $[29] = setEventMapping;
    $[30] = setPanelOpen;
    $[31] = togglePanel;
    $[32] = updatePreset;
    $[33] = t9;
  } else {
    t9 = $[33];
  }
  return t9;
}

// src/hooks/useActionExecutor.ts
var import_compiler_runtime10 = require("react/compiler-runtime");
var import_jotai28 = require("jotai");

// src/core/actions-node.ts
function registerSelectionActions() {
  registerAction({
    id: BuiltInActionId.SelectNode,
    label: "Select Node",
    description: "Select this node (replacing current selection)",
    category: ActionCategory.Selection,
    icon: "pointer",
    requiresNode: true,
    isBuiltIn: true,
    handler: (context, helpers) => {
      if (context.nodeId) {
        helpers.selectNode(context.nodeId);
      }
    }
  });
  registerAction({
    id: BuiltInActionId.SelectEdge,
    label: "Select Edge",
    description: "Select this edge",
    category: ActionCategory.Selection,
    icon: "git-commit",
    isBuiltIn: true,
    handler: (context, helpers) => {
      if (context.edgeId) {
        helpers.selectEdge(context.edgeId);
      }
    }
  });
  registerAction({
    id: BuiltInActionId.AddToSelection,
    label: "Add to Selection",
    description: "Add this node to the current selection",
    category: ActionCategory.Selection,
    icon: "plus-square",
    requiresNode: true,
    isBuiltIn: true,
    handler: (context, helpers) => {
      if (context.nodeId) {
        helpers.addToSelection(context.nodeId);
      }
    }
  });
  registerAction({
    id: BuiltInActionId.ClearSelection,
    label: "Clear Selection",
    description: "Deselect all nodes",
    category: ActionCategory.Selection,
    icon: "x-square",
    isBuiltIn: true,
    handler: (_context, helpers) => {
      helpers.clearSelection();
    }
  });
  registerAction({
    id: BuiltInActionId.DeleteSelected,
    label: "Delete Selected",
    description: "Delete all selected nodes",
    category: ActionCategory.Selection,
    icon: "trash-2",
    isBuiltIn: true,
    handler: async (_context, helpers) => {
      const selectedIds = helpers.getSelectedNodeIds();
      for (const nodeId of selectedIds) {
        await helpers.deleteNode(nodeId);
      }
    }
  });
}
function registerNodeActions() {
  registerAction({
    id: BuiltInActionId.LockNode,
    label: "Lock Node",
    description: "Prevent this node from being moved",
    category: ActionCategory.Node,
    icon: "lock",
    requiresNode: true,
    isBuiltIn: true,
    handler: (context, helpers) => {
      if (context.nodeId) {
        helpers.lockNode(context.nodeId);
      }
    }
  });
  registerAction({
    id: BuiltInActionId.UnlockNode,
    label: "Unlock Node",
    description: "Allow this node to be moved",
    category: ActionCategory.Node,
    icon: "unlock",
    requiresNode: true,
    isBuiltIn: true,
    handler: (context, helpers) => {
      if (context.nodeId) {
        helpers.unlockNode(context.nodeId);
      }
    }
  });
  registerAction({
    id: BuiltInActionId.ToggleLock,
    label: "Toggle Lock",
    description: "Toggle whether this node can be moved",
    category: ActionCategory.Node,
    icon: "lock",
    requiresNode: true,
    isBuiltIn: true,
    handler: (context, helpers) => {
      if (context.nodeId) {
        helpers.toggleLock(context.nodeId);
      }
    }
  });
  registerAction({
    id: BuiltInActionId.OpenContextMenu,
    label: "Open Context Menu",
    description: "Show the context menu for this node",
    category: ActionCategory.Node,
    icon: "more-vertical",
    isBuiltIn: true,
    handler: (context, helpers) => {
      if (helpers.openContextMenu) {
        helpers.openContextMenu(context.screenPosition, context.nodeId);
      }
    }
  });
  registerAction({
    id: BuiltInActionId.CreateNode,
    label: "Create Node",
    description: "Create a new node at this position",
    category: ActionCategory.Node,
    icon: "plus",
    isBuiltIn: true,
    handler: async (context, helpers) => {
      if (helpers.createNode) {
        await helpers.createNode(context.worldPosition);
      }
    }
  });
  registerAction({
    id: BuiltInActionId.SplitNode,
    label: "Split Node",
    description: "Split a node into two separate nodes",
    category: ActionCategory.Node,
    icon: "split",
    isBuiltIn: true,
    handler: async (context, helpers) => {
      if (helpers.splitNode && context.nodeId) {
        await helpers.splitNode(context.nodeId);
      }
    }
  });
  registerAction({
    id: BuiltInActionId.GroupNodes,
    label: "Group Nodes",
    description: "Group selected nodes into a parent container",
    category: ActionCategory.Node,
    icon: "group",
    isBuiltIn: true,
    handler: async (context, helpers) => {
      if (helpers.groupNodes) {
        await helpers.groupNodes(context.selectedNodeIds ?? helpers.getSelectedNodeIds());
      }
    }
  });
  registerAction({
    id: BuiltInActionId.MergeNodes,
    label: "Merge Nodes",
    description: "Merge selected nodes into one",
    category: ActionCategory.Node,
    icon: "merge",
    isBuiltIn: true,
    handler: async (context, helpers) => {
      if (helpers.mergeNodes) {
        await helpers.mergeNodes(context.selectedNodeIds ?? helpers.getSelectedNodeIds());
      }
    }
  });
}

// src/core/actions-viewport.ts
function registerViewportActions() {
  registerAction({
    id: BuiltInActionId.FitToView,
    label: "Fit to View",
    description: "Zoom and pan to fit this node in view",
    category: ActionCategory.Viewport,
    icon: "maximize-2",
    requiresNode: true,
    isBuiltIn: true,
    handler: (context, helpers) => {
      if (context.nodeId) {
        helpers.centerOnNode(context.nodeId);
      }
    }
  });
  registerAction({
    id: BuiltInActionId.FitAllToView,
    label: "Fit All to View",
    description: "Zoom and pan to fit all nodes in view",
    category: ActionCategory.Viewport,
    icon: "maximize",
    isBuiltIn: true,
    handler: (_context, helpers) => {
      helpers.fitToBounds("graph");
    }
  });
  registerAction({
    id: BuiltInActionId.CenterOnNode,
    label: "Center on Node",
    description: "Center the viewport on this node",
    category: ActionCategory.Viewport,
    icon: "crosshair",
    requiresNode: true,
    isBuiltIn: true,
    handler: (context, helpers) => {
      if (context.nodeId) {
        helpers.centerOnNode(context.nodeId);
      }
    }
  });
  registerAction({
    id: BuiltInActionId.ResetViewport,
    label: "Reset Viewport",
    description: "Reset zoom to 100% and center on origin",
    category: ActionCategory.Viewport,
    icon: "home",
    isBuiltIn: true,
    handler: (_context, helpers) => {
      helpers.resetViewport();
    }
  });
}
function registerHistoryActions() {
  registerAction({
    id: BuiltInActionId.Undo,
    label: "Undo",
    description: "Undo the last action",
    category: ActionCategory.History,
    icon: "undo-2",
    isBuiltIn: true,
    handler: (_context, helpers) => {
      if (helpers.canUndo()) {
        helpers.undo();
      }
    }
  });
  registerAction({
    id: BuiltInActionId.Redo,
    label: "Redo",
    description: "Redo the last undone action",
    category: ActionCategory.History,
    icon: "redo-2",
    isBuiltIn: true,
    handler: (_context, helpers) => {
      if (helpers.canRedo()) {
        helpers.redo();
      }
    }
  });
  registerAction({
    id: BuiltInActionId.ApplyForceLayout,
    label: "Apply Force Layout",
    description: "Automatically arrange nodes using force-directed layout",
    category: ActionCategory.Layout,
    icon: "layout-grid",
    isBuiltIn: true,
    handler: async (_context, helpers) => {
      await helpers.applyForceLayout();
    }
  });
}

// src/core/built-in-actions.ts
function registerBuiltInActions() {
  registerAction({
    id: BuiltInActionId.None,
    label: "None",
    description: "Do nothing",
    category: ActionCategory.None,
    icon: "ban",
    isBuiltIn: true,
    handler: () => {
    }
  });
  registerSelectionActions();
  registerNodeActions();
  registerViewportActions();
  registerHistoryActions();
}

// src/core/action-registry.ts
var actionRegistry = /* @__PURE__ */ new Map();
function registerAction(action) {
  actionRegistry.set(action.id, action);
}
function getAction(id) {
  return actionRegistry.get(id);
}
function unregisterAction(id) {
  return actionRegistry.delete(id);
}
registerBuiltInActions();

// src/core/locked-node-store.ts
var import_jotai27 = require("jotai");
var lockedNodeIdAtom = (0, import_jotai27.atom)(null);
var lockedNodeDataAtom = (0, import_jotai27.atom)((get) => {
  const id = get(lockedNodeIdAtom);
  if (!id) return null;
  const nodes = get(uiNodesAtom);
  return nodes.find((n) => n.id === id) || null;
});
var lockedNodePageIndexAtom = (0, import_jotai27.atom)(0);
var lockedNodePageCountAtom = (0, import_jotai27.atom)(1);
var lockNodeAtom = (0, import_jotai27.atom)(null, (_get, set, payload) => {
  set(lockedNodeIdAtom, payload.nodeId);
  set(lockedNodePageIndexAtom, 0);
});
var unlockNodeAtom = (0, import_jotai27.atom)(null, (_get, set) => {
  set(lockedNodeIdAtom, null);
});
var nextLockedPageAtom = (0, import_jotai27.atom)(null, (get, set) => {
  const current = get(lockedNodePageIndexAtom);
  const pageCount = get(lockedNodePageCountAtom);
  set(lockedNodePageIndexAtom, (current + 1) % pageCount);
});
var prevLockedPageAtom = (0, import_jotai27.atom)(null, (get, set) => {
  const current = get(lockedNodePageIndexAtom);
  const pageCount = get(lockedNodePageCountAtom);
  set(lockedNodePageIndexAtom, (current - 1 + pageCount) % pageCount);
});
var goToLockedPageAtom = (0, import_jotai27.atom)(null, (get, set, index) => {
  const pageCount = get(lockedNodePageCountAtom);
  if (index >= 0 && index < pageCount) {
    set(lockedNodePageIndexAtom, index);
  }
});
var hasLockedNodeAtom = (0, import_jotai27.atom)((get) => get(lockedNodeIdAtom) !== null);

// src/core/action-executor.ts
var debug13 = createDebug("actions");
async function executeAction(actionId, context, helpers) {
  if (actionId === BuiltInActionId.None) {
    return {
      success: true,
      actionId
    };
  }
  const action = getAction(actionId);
  if (!action) {
    debug13.warn("Action not found: %s", actionId);
    return {
      success: false,
      actionId,
      error: new Error(`Action not found: ${actionId}`)
    };
  }
  if (action.requiresNode && !context.nodeId) {
    debug13.warn("Action %s requires a node context", actionId);
    return {
      success: false,
      actionId,
      error: new Error(`Action ${actionId} requires a node context`)
    };
  }
  try {
    const result = action.handler(context, helpers);
    if (result instanceof Promise) {
      await result;
    }
    return {
      success: true,
      actionId
    };
  } catch (error) {
    debug13.error("Error executing action %s: %O", actionId, error);
    return {
      success: false,
      actionId,
      error: error instanceof Error ? error : new Error(String(error))
    };
  }
}
function buildActionHelpers(store, options = {}) {
  return {
    selectNode: (nodeId) => store.set(selectSingleNodeAtom, nodeId),
    addToSelection: (nodeId) => store.set(addNodesToSelectionAtom, [nodeId]),
    clearSelection: () => store.set(clearSelectionAtom),
    getSelectedNodeIds: () => Array.from(store.get(selectedNodeIdsAtom)),
    fitToBounds: (mode, padding) => {
      const fitMode = mode === "graph" ? FitToBoundsMode.Graph : FitToBoundsMode.Selection;
      store.set(fitToBoundsAtom, {
        mode: fitMode,
        padding
      });
    },
    centerOnNode: (nodeId) => store.set(centerOnNodeAtom, nodeId),
    resetViewport: () => store.set(resetViewportAtom),
    lockNode: (nodeId) => store.set(lockNodeAtom, {
      nodeId
    }),
    unlockNode: (_nodeId) => store.set(unlockNodeAtom),
    toggleLock: (nodeId) => {
      const currentLockedId = store.get(lockedNodeIdAtom);
      if (currentLockedId === nodeId) {
        store.set(unlockNodeAtom);
      } else {
        store.set(lockNodeAtom, {
          nodeId
        });
      }
    },
    deleteNode: async (nodeId) => {
      if (options.onDeleteNode) {
        await options.onDeleteNode(nodeId);
      } else {
        debug13.warn("deleteNode called but onDeleteNode callback not provided");
      }
    },
    isNodeLocked: (nodeId) => store.get(lockedNodeIdAtom) === nodeId,
    applyForceLayout: async () => {
      if (options.onApplyForceLayout) {
        await options.onApplyForceLayout();
      } else {
        debug13.warn("applyForceLayout called but onApplyForceLayout callback not provided");
      }
    },
    undo: () => store.set(undoAtom),
    redo: () => store.set(redoAtom),
    canUndo: () => store.get(canUndoAtom),
    canRedo: () => store.get(canRedoAtom),
    selectEdge: (edgeId) => store.set(selectEdgeAtom, edgeId),
    clearEdgeSelection: () => store.set(clearEdgeSelectionAtom),
    openContextMenu: options.onOpenContextMenu,
    createNode: options.onCreateNode
  };
}

// src/hooks/useActionExecutor.ts
function useActionExecutor(t0) {
  const $ = (0, import_compiler_runtime10.c)(13);
  const options = t0 === void 0 ? {} : t0;
  const store = (0, import_jotai28.useStore)();
  const mappings = (0, import_jotai28.useAtomValue)(eventMappingsAtom);
  const helpers = buildActionHelpers(store, {
    onDeleteNode: options.onDeleteNode,
    onOpenContextMenu: options.onOpenContextMenu,
    onCreateNode: options.onCreateNode,
    onApplyForceLayout: options.onApplyForceLayout
  });
  let t1;
  if ($[0] !== helpers) {
    t1 = async (actionId, context) => executeAction(actionId, context, helpers);
    $[0] = helpers;
    $[1] = t1;
  } else {
    t1 = $[1];
  }
  const executeActionById = t1;
  let t2;
  if ($[2] !== helpers || $[3] !== mappings) {
    t2 = async (event, context_0) => {
      const actionId_0 = getActionForEvent(mappings, event);
      return executeAction(actionId_0, context_0, helpers);
    };
    $[2] = helpers;
    $[3] = mappings;
    $[4] = t2;
  } else {
    t2 = $[4];
  }
  const executeEventAction = t2;
  let t3;
  if ($[5] !== mappings) {
    t3 = (event_0) => getActionForEvent(mappings, event_0);
    $[5] = mappings;
    $[6] = t3;
  } else {
    t3 = $[6];
  }
  const getActionForEventFn = t3;
  let t4;
  if ($[7] !== executeActionById || $[8] !== executeEventAction || $[9] !== getActionForEventFn || $[10] !== helpers || $[11] !== mappings) {
    t4 = {
      executeActionById,
      executeEventAction,
      getActionForEvent: getActionForEventFn,
      mappings,
      helpers
    };
    $[7] = executeActionById;
    $[8] = executeEventAction;
    $[9] = getActionForEventFn;
    $[10] = helpers;
    $[11] = mappings;
    $[12] = t4;
  } else {
    t4 = $[12];
  }
  return t4;
}

// src/hooks/useGestureResolver.ts
var import_compiler_runtime11 = require("react/compiler-runtime");
var import_jotai30 = require("jotai");

// src/core/gesture-rules-defaults.ts
function mergeRules(defaults, overrides) {
  const overrideMap = new Map(overrides.map((r) => [r.id, r]));
  const result = [];
  for (const rule of defaults) {
    const override = overrideMap.get(rule.id);
    if (override) {
      result.push(override);
      overrideMap.delete(rule.id);
    } else {
      result.push(rule);
    }
  }
  for (const rule of overrideMap.values()) {
    result.push(rule);
  }
  return result;
}
var DEFAULT_GESTURE_RULES = [
  // ── Tap gestures ──────────────────────────────────────────────
  {
    id: "tap-node",
    pattern: {
      gesture: "tap",
      target: "node"
    },
    actionId: "select-node"
  },
  {
    id: "tap-edge",
    pattern: {
      gesture: "tap",
      target: "edge"
    },
    actionId: "select-edge"
  },
  {
    id: "tap-port",
    pattern: {
      gesture: "tap",
      target: "port"
    },
    actionId: "select-node"
  },
  {
    id: "tap-bg",
    pattern: {
      gesture: "tap",
      target: "background"
    },
    actionId: "clear-selection"
  },
  // ── Double-tap ────────────────────────────────────────────────
  {
    id: "dtap-node",
    pattern: {
      gesture: "double-tap",
      target: "node"
    },
    actionId: "fit-to-view"
  },
  {
    id: "dtap-bg",
    pattern: {
      gesture: "double-tap",
      target: "background"
    },
    actionId: "fit-all-to-view"
  },
  // ── Triple-tap ────────────────────────────────────────────────
  {
    id: "ttap-node",
    pattern: {
      gesture: "triple-tap",
      target: "node"
    },
    actionId: "toggle-lock"
  },
  // ── Left-button drag ──────────────────────────────────────────
  {
    id: "drag-node",
    pattern: {
      gesture: "drag",
      target: "node"
    },
    actionId: "move-node"
  },
  {
    id: "drag-port",
    pattern: {
      gesture: "drag",
      target: "port"
    },
    actionId: "create-edge"
  },
  {
    id: "drag-bg-finger",
    pattern: {
      gesture: "drag",
      target: "background",
      source: "finger"
    },
    actionId: "pan"
  },
  {
    id: "drag-bg-mouse",
    pattern: {
      gesture: "drag",
      target: "background",
      source: "mouse"
    },
    actionId: "pan"
  },
  {
    id: "drag-bg-pencil",
    pattern: {
      gesture: "drag",
      target: "background",
      source: "pencil"
    },
    actionId: "lasso-select"
  },
  // ── Shift+drag overrides ──────────────────────────────────────
  {
    id: "shift-drag-bg",
    pattern: {
      gesture: "drag",
      target: "background",
      modifiers: {
        shift: true
      }
    },
    actionId: "rect-select"
  },
  // ── Right-click tap (context menu) ────────────────────────────
  {
    id: "rc-node",
    pattern: {
      gesture: "tap",
      target: "node",
      button: 2
    },
    actionId: "open-context-menu"
  },
  {
    id: "rc-edge",
    pattern: {
      gesture: "tap",
      target: "edge",
      button: 2
    },
    actionId: "open-context-menu"
  },
  {
    id: "rc-bg",
    pattern: {
      gesture: "tap",
      target: "background",
      button: 2
    },
    actionId: "open-context-menu"
  },
  // ── Long-press ────────────────────────────────────────────────
  {
    id: "lp-node",
    pattern: {
      gesture: "long-press",
      target: "node"
    },
    actionId: "open-context-menu"
  },
  {
    id: "lp-bg-finger",
    pattern: {
      gesture: "long-press",
      target: "background",
      source: "finger"
    },
    actionId: "create-node"
  },
  // ── Right-button drag (defaults to none — consumers override) ─
  {
    id: "rdrag-node",
    pattern: {
      gesture: "drag",
      target: "node",
      button: 2
    },
    actionId: "none"
  },
  {
    id: "rdrag-bg",
    pattern: {
      gesture: "drag",
      target: "background",
      button: 2
    },
    actionId: "none"
  },
  // ── Middle-button drag (defaults to none) ─────────────────────
  {
    id: "mdrag-node",
    pattern: {
      gesture: "drag",
      target: "node",
      button: 1
    },
    actionId: "none"
  },
  {
    id: "mdrag-bg",
    pattern: {
      gesture: "drag",
      target: "background",
      button: 1
    },
    actionId: "none"
  },
  // ── Zoom ──────────────────────────────────────────────────────
  {
    id: "pinch-bg",
    pattern: {
      gesture: "pinch",
      target: "background"
    },
    actionId: "zoom"
  },
  {
    id: "scroll-any",
    pattern: {
      gesture: "scroll"
    },
    actionId: "zoom"
  },
  // ── Split ─────────────────────────────────────────────────────
  {
    id: "pinch-node",
    pattern: {
      gesture: "pinch",
      target: "node"
    },
    actionId: "split-node"
  }
];

// src/core/gesture-rules.ts
var MODIFIER_KEYS = ["shift", "ctrl", "alt", "meta"];
function matchSpecificity(pattern, desc) {
  let score = 0;
  if (pattern.gesture !== void 0) {
    if (pattern.gesture !== desc.gesture) return -1;
    score += 32;
  }
  if (pattern.target !== void 0) {
    if (pattern.target !== desc.target) return -1;
    score += 16;
  }
  if (pattern.source !== void 0) {
    if (pattern.source !== desc.source) return -1;
    score += 4;
  }
  if (pattern.button !== void 0) {
    if (pattern.button !== (desc.button ?? 0)) return -1;
    score += 2;
  }
  if (pattern.modifiers !== void 0) {
    const dm = desc.modifiers ?? {};
    for (const key of MODIFIER_KEYS) {
      const required = pattern.modifiers[key];
      if (required === void 0) continue;
      const actual = dm[key] ?? false;
      if (required !== actual) return -1;
      score += 8;
    }
  }
  return score;
}
var PALM_REJECTION_RULE = {
  id: "__palm-rejection__",
  pattern: {},
  actionId: "none",
  label: "Palm rejection"
};
function resolveGesture(desc, rules, options) {
  const palmRejection = options?.palmRejection !== false;
  if (palmRejection && desc.isStylusActive && desc.source === "finger") {
    if (desc.gesture === "tap" || desc.gesture === "long-press" || desc.gesture === "double-tap" || desc.gesture === "triple-tap") {
      return {
        actionId: "none",
        rule: PALM_REJECTION_RULE,
        score: Infinity
      };
    }
    if (desc.gesture === "drag" && desc.target !== "background") {
      return resolveGesture({
        ...desc,
        target: "background",
        isStylusActive: false
      }, rules, {
        palmRejection: false
      });
    }
  }
  let best = null;
  for (const rule of rules) {
    const specificity = matchSpecificity(rule.pattern, desc);
    if (specificity < 0) continue;
    const effectiveScore = specificity * 1e3 + (rule.priority ?? 0);
    if (!best || effectiveScore > best.score) {
      best = {
        actionId: rule.actionId,
        rule,
        score: effectiveScore
      };
    }
  }
  return best;
}
function buildRuleIndex(rules) {
  const buckets = /* @__PURE__ */ new Map();
  const wildcardRules = [];
  for (const rule of rules) {
    const key = rule.pattern.gesture;
    if (key === void 0) {
      wildcardRules.push(rule);
    } else {
      const bucket = buckets.get(key);
      if (bucket) {
        bucket.push(rule);
      } else {
        buckets.set(key, [rule]);
      }
    }
  }
  const index = /* @__PURE__ */ new Map();
  if (wildcardRules.length > 0) {
    for (const [key, bucket] of buckets) {
      index.set(key, bucket.concat(wildcardRules));
    }
    index.set("__wildcard__", wildcardRules);
  } else {
    for (const [key, bucket] of buckets) {
      index.set(key, bucket);
    }
  }
  return index;
}
function resolveGestureIndexed(desc, index, options) {
  const rules = index.get(desc.gesture) ?? index.get("__wildcard__") ?? [];
  return resolveGesture(desc, rules, options);
}

// src/core/gesture-rule-store.ts
var import_jotai29 = require("jotai");
var import_utils2 = require("jotai/utils");
var DEFAULT_RULE_STATE = {
  customRules: [],
  palmRejection: true
};
var gestureRuleSettingsAtom = (0, import_utils2.atomWithStorage)("canvas-gesture-rules", DEFAULT_RULE_STATE);
var consumerGestureRulesAtom = (0, import_jotai29.atom)([]);
var gestureRulesAtom = (0, import_jotai29.atom)((get) => {
  const settings = get(gestureRuleSettingsAtom);
  const consumerRules = get(consumerGestureRulesAtom);
  let rules = mergeRules(DEFAULT_GESTURE_RULES, settings.customRules);
  if (consumerRules.length > 0) {
    rules = mergeRules(rules, consumerRules);
  }
  return rules;
});
var gestureRuleIndexAtom = (0, import_jotai29.atom)((get) => {
  return buildRuleIndex(get(gestureRulesAtom));
});
var palmRejectionEnabledAtom = (0, import_jotai29.atom)((get) => get(gestureRuleSettingsAtom).palmRejection, (get, set, enabled) => {
  const current = get(gestureRuleSettingsAtom);
  set(gestureRuleSettingsAtom, {
    ...current,
    palmRejection: enabled
  });
});
var addGestureRuleAtom = (0, import_jotai29.atom)(null, (get, set, rule) => {
  const current = get(gestureRuleSettingsAtom);
  const existing = current.customRules.findIndex((r) => r.id === rule.id);
  const newRules = [...current.customRules];
  if (existing >= 0) {
    newRules[existing] = rule;
  } else {
    newRules.push(rule);
  }
  set(gestureRuleSettingsAtom, {
    ...current,
    customRules: newRules
  });
});
var removeGestureRuleAtom = (0, import_jotai29.atom)(null, (get, set, ruleId) => {
  const current = get(gestureRuleSettingsAtom);
  set(gestureRuleSettingsAtom, {
    ...current,
    customRules: current.customRules.filter((r) => r.id !== ruleId)
  });
});
var updateGestureRuleAtom = (0, import_jotai29.atom)(null, (get, set, {
  id,
  updates
}) => {
  const current = get(gestureRuleSettingsAtom);
  const index = current.customRules.findIndex((r) => r.id === id);
  if (index < 0) return;
  const newRules = [...current.customRules];
  newRules[index] = {
    ...newRules[index],
    ...updates
  };
  set(gestureRuleSettingsAtom, {
    ...current,
    customRules: newRules
  });
});
var resetGestureRulesAtom = (0, import_jotai29.atom)(null, (get, set) => {
  const current = get(gestureRuleSettingsAtom);
  set(gestureRuleSettingsAtom, {
    ...current,
    customRules: []
  });
});

// src/hooks/useGestureResolver.ts
function useGestureResolver() {
  const $ = (0, import_compiler_runtime11.c)(4);
  const index = (0, import_jotai30.useAtomValue)(gestureRuleIndexAtom);
  const palmRejection = (0, import_jotai30.useAtomValue)(palmRejectionEnabledAtom);
  const isStylusActive = (0, import_jotai30.useAtomValue)(isStylusActiveAtom);
  let t0;
  if ($[0] !== index || $[1] !== isStylusActive || $[2] !== palmRejection) {
    t0 = (desc) => resolveGestureIndexed({
      ...desc,
      isStylusActive: desc.isStylusActive ?? isStylusActive
    }, index, {
      palmRejection
    });
    $[0] = index;
    $[1] = isStylusActive;
    $[2] = palmRejection;
    $[3] = t0;
  } else {
    t0 = $[3];
  }
  return t0;
}

// src/hooks/useCommandLine.ts
var import_compiler_runtime12 = require("react/compiler-runtime");
var import_jotai33 = require("jotai");

// src/commands/store.ts
var import_jotai32 = require("jotai");

// src/commands/registry.ts
var CommandRegistry = class {
  constructor() {
    __publicField(this, "commands", /* @__PURE__ */ new Map());
    __publicField(this, "aliases", /* @__PURE__ */ new Map());
  }
  // alias -> command name
  /**
   * Register a command with the registry.
   * @param command The command definition to register
   * @throws Error if command name or alias already exists
   */
  register(command) {
    if (this.commands.has(command.name)) {
      throw new Error(`Command "${command.name}" is already registered`);
    }
    this.commands.set(command.name, command);
    if (command.aliases) {
      for (const alias of command.aliases) {
        if (this.aliases.has(alias)) {
          throw new Error(`Alias "${alias}" is already registered for command "${this.aliases.get(alias)}"`);
        }
        if (this.commands.has(alias)) {
          throw new Error(`Alias "${alias}" conflicts with existing command name`);
        }
        this.aliases.set(alias, command.name);
      }
    }
  }
  /**
   * Unregister a command by name.
   * @param name The command name to remove
   */
  unregister(name) {
    const command = this.commands.get(name);
    if (command) {
      if (command.aliases) {
        for (const alias of command.aliases) {
          this.aliases.delete(alias);
        }
      }
      this.commands.delete(name);
    }
  }
  /**
   * Get a command by name or alias.
   * @param nameOrAlias Command name or alias
   * @returns The command definition or undefined if not found
   */
  get(nameOrAlias) {
    const direct = this.commands.get(nameOrAlias);
    if (direct) return direct;
    const commandName = this.aliases.get(nameOrAlias);
    if (commandName) {
      return this.commands.get(commandName);
    }
    return void 0;
  }
  /**
   * Check if a command exists by name or alias.
   * @param nameOrAlias Command name or alias
   */
  has(nameOrAlias) {
    return this.commands.has(nameOrAlias) || this.aliases.has(nameOrAlias);
  }
  /**
   * Search for commands matching a query.
   * Searches command names, aliases, and descriptions.
   * @param query Search query (case-insensitive)
   * @returns Array of matching commands, sorted by relevance
   */
  search(query) {
    if (!query.trim()) {
      return this.all();
    }
    const lowerQuery = query.toLowerCase().trim();
    const results = [];
    const commands = Array.from(this.commands.values());
    for (const command of commands) {
      let score = 0;
      if (command.name.toLowerCase() === lowerQuery) {
        score = 100;
      } else if (command.name.toLowerCase().startsWith(lowerQuery)) {
        score = 80;
      } else if (command.name.toLowerCase().includes(lowerQuery)) {
        score = 60;
      } else if (command.aliases?.some((a) => a.toLowerCase() === lowerQuery)) {
        score = 90;
      } else if (command.aliases?.some((a) => a.toLowerCase().startsWith(lowerQuery))) {
        score = 70;
      } else if (command.aliases?.some((a) => a.toLowerCase().includes(lowerQuery))) {
        score = 50;
      } else if (command.description.toLowerCase().includes(lowerQuery)) {
        score = 30;
      }
      if (score > 0) {
        results.push({
          command,
          score
        });
      }
    }
    return results.sort((a, b) => b.score - a.score || a.command.name.localeCompare(b.command.name)).map((r) => r.command);
  }
  /**
   * Get all registered commands.
   * @returns Array of all commands, sorted alphabetically by name
   */
  all() {
    return Array.from(this.commands.values()).sort((a, b) => a.name.localeCompare(b.name));
  }
  /**
   * Get commands by category.
   * @param category The category to filter by
   * @returns Array of commands in the category
   */
  byCategory(category) {
    return this.all().filter((cmd) => cmd.category === category);
  }
  /**
   * Get all available categories.
   * @returns Array of unique categories
   */
  categories() {
    const categories = /* @__PURE__ */ new Set();
    const commands = Array.from(this.commands.values());
    for (const command of commands) {
      categories.add(command.category);
    }
    return Array.from(categories).sort();
  }
  /**
   * Get the count of registered commands.
   */
  get size() {
    return this.commands.size;
  }
  /**
   * Clear all registered commands.
   * Useful for testing.
   */
  clear() {
    this.commands.clear();
    this.aliases.clear();
  }
  /**
   * Get a serializable list of commands for API responses.
   */
  toJSON() {
    return this.all().map((cmd) => ({
      name: cmd.name,
      aliases: cmd.aliases || [],
      description: cmd.description,
      category: cmd.category,
      inputs: cmd.inputs.map((input) => ({
        name: input.name,
        type: input.type,
        prompt: input.prompt,
        required: input.required !== false
      }))
    }));
  }
};
var commandRegistry = new CommandRegistry();

// src/commands/store-atoms.ts
var import_jotai31 = require("jotai");
var import_utils3 = require("jotai/utils");
var inputModeAtom = (0, import_jotai31.atom)({
  type: "normal"
});
var commandLineVisibleAtom = (0, import_jotai31.atom)(false);
var commandLineStateAtom = (0, import_jotai31.atom)({
  phase: "idle"
});
var commandFeedbackAtom = (0, import_jotai31.atom)(null);
var commandHistoryAtom = (0, import_utils3.atomWithStorage)("canvas-command-history", []);
var selectedSuggestionIndexAtom = (0, import_jotai31.atom)(0);
var pendingInputResolverAtom = (0, import_jotai31.atom)(null);
var isCommandActiveAtom = (0, import_jotai31.atom)((get) => {
  const state = get(commandLineStateAtom);
  return state.phase === "collecting" || state.phase === "executing";
});
var currentInputAtom = (0, import_jotai31.atom)((get) => {
  const state = get(commandLineStateAtom);
  if (state.phase !== "collecting") return null;
  return state.command.inputs[state.inputIndex];
});
var commandProgressAtom = (0, import_jotai31.atom)((get) => {
  const state = get(commandLineStateAtom);
  if (state.phase !== "collecting") return null;
  return {
    current: state.inputIndex + 1,
    total: state.command.inputs.length
  };
});

// src/commands/store.ts
var openCommandLineAtom = (0, import_jotai32.atom)(null, (get, set) => {
  set(commandLineVisibleAtom, true);
  set(commandLineStateAtom, {
    phase: "searching",
    query: "",
    suggestions: commandRegistry.all()
  });
  set(selectedSuggestionIndexAtom, 0);
});
var closeCommandLineAtom = (0, import_jotai32.atom)(null, (get, set) => {
  set(commandLineVisibleAtom, false);
  set(commandLineStateAtom, {
    phase: "idle"
  });
  set(inputModeAtom, {
    type: "normal"
  });
  set(commandFeedbackAtom, null);
  set(pendingInputResolverAtom, null);
});
var updateSearchQueryAtom = (0, import_jotai32.atom)(null, (get, set, query) => {
  const suggestions = commandRegistry.search(query);
  set(commandLineStateAtom, {
    phase: "searching",
    query,
    suggestions
  });
  set(selectedSuggestionIndexAtom, 0);
});
var selectCommandAtom = (0, import_jotai32.atom)(null, (get, set, command) => {
  const history = get(commandHistoryAtom);
  const newHistory = [command.name, ...history.filter((h) => h !== command.name)].slice(0, 50);
  set(commandHistoryAtom, newHistory);
  if (command.inputs.length === 0) {
    set(commandLineStateAtom, {
      phase: "executing",
      command
    });
    return;
  }
  set(commandLineStateAtom, {
    phase: "collecting",
    command,
    inputIndex: 0,
    collected: {}
  });
  const firstInput = command.inputs[0];
  set(inputModeAtom, inputDefToMode(firstInput));
});
var provideInputAtom = (0, import_jotai32.atom)(null, (get, set, value) => {
  const state = get(commandLineStateAtom);
  if (state.phase !== "collecting") return;
  const {
    command,
    inputIndex,
    collected
  } = state;
  const currentInput = command.inputs[inputIndex];
  if (currentInput.validate) {
    const result = currentInput.validate(value, collected);
    if (result !== true) {
      set(commandLineStateAtom, {
        phase: "error",
        message: typeof result === "string" ? result : `Invalid value for ${currentInput.name}`
      });
      return;
    }
  }
  const newCollected = {
    ...collected,
    [currentInput.name]: value
  };
  if (inputIndex < command.inputs.length - 1) {
    const nextInputIndex = inputIndex + 1;
    const nextInput = command.inputs[nextInputIndex];
    set(commandLineStateAtom, {
      phase: "collecting",
      command,
      inputIndex: nextInputIndex,
      collected: newCollected
    });
    set(inputModeAtom, inputDefToMode(nextInput, newCollected));
    if (command.feedback) {
      const feedback = command.feedback(newCollected, nextInput);
      if (feedback) {
        const feedbackState = {
          hoveredNodeId: feedback.highlightNodeId,
          ghostNode: feedback.ghostNode,
          crosshair: feedback.crosshair,
          // Handle previewEdge conversion - toCursor variant needs cursorWorldPos
          previewEdge: feedback.previewEdge && "to" in feedback.previewEdge ? {
            from: feedback.previewEdge.from,
            to: feedback.previewEdge.to
          } : void 0
        };
        set(commandFeedbackAtom, feedbackState);
      } else {
        set(commandFeedbackAtom, null);
      }
    }
  } else {
    set(commandLineStateAtom, {
      phase: "collecting",
      command,
      inputIndex,
      collected: newCollected
    });
    set(inputModeAtom, {
      type: "normal"
    });
  }
});
var skipInputAtom = (0, import_jotai32.atom)(null, (get, set) => {
  const state = get(commandLineStateAtom);
  if (state.phase !== "collecting") return;
  const {
    command,
    inputIndex
  } = state;
  const currentInput = command.inputs[inputIndex];
  if (currentInput.required !== false) {
    return;
  }
  const value = currentInput.default;
  set(provideInputAtom, value);
});
var goBackInputAtom = (0, import_jotai32.atom)(null, (get, set) => {
  const state = get(commandLineStateAtom);
  if (state.phase !== "collecting") return;
  const {
    command,
    inputIndex,
    collected
  } = state;
  if (inputIndex === 0) {
    set(commandLineStateAtom, {
      phase: "searching",
      query: command.name,
      suggestions: [command]
    });
    set(inputModeAtom, {
      type: "normal"
    });
    return;
  }
  const prevInputIndex = inputIndex - 1;
  const prevInput = command.inputs[prevInputIndex];
  const newCollected = {
    ...collected
  };
  delete newCollected[prevInput.name];
  set(commandLineStateAtom, {
    phase: "collecting",
    command,
    inputIndex: prevInputIndex,
    collected: newCollected
  });
  set(inputModeAtom, inputDefToMode(prevInput, newCollected));
});
var setCommandErrorAtom = (0, import_jotai32.atom)(null, (get, set, message) => {
  set(commandLineStateAtom, {
    phase: "error",
    message
  });
  set(inputModeAtom, {
    type: "normal"
  });
});
var clearCommandErrorAtom = (0, import_jotai32.atom)(null, (get, set) => {
  set(commandLineStateAtom, {
    phase: "idle"
  });
});
function inputDefToMode(input, collected) {
  switch (input.type) {
    case "point":
      return {
        type: "pickPoint",
        prompt: input.prompt,
        snapToGrid: input.snapToGrid
      };
    case "node":
      return {
        type: "pickNode",
        prompt: input.prompt,
        filter: input.filter ? (node) => input.filter(node, collected || {}) : void 0
      };
    case "nodes":
      return {
        type: "pickNodes",
        prompt: input.prompt,
        filter: input.filter ? (node) => input.filter(node, collected || {}) : void 0
      };
    case "select":
      return {
        type: "select",
        prompt: input.prompt,
        options: input.options || []
      };
    case "text":
    case "number":
    case "color":
    case "boolean":
    default:
      return {
        type: "text",
        prompt: input.prompt
      };
  }
}

// src/hooks/useCommandLine.ts
function useCommandLine() {
  const $ = (0, import_compiler_runtime12.c)(16);
  const visible = (0, import_jotai33.useAtomValue)(commandLineVisibleAtom);
  const state = (0, import_jotai33.useAtomValue)(commandLineStateAtom);
  const history = (0, import_jotai33.useAtomValue)(commandHistoryAtom);
  const currentInput = (0, import_jotai33.useAtomValue)(currentInputAtom);
  const progress = (0, import_jotai33.useAtomValue)(commandProgressAtom);
  const open = (0, import_jotai33.useSetAtom)(openCommandLineAtom);
  const close = (0, import_jotai33.useSetAtom)(closeCommandLineAtom);
  const updateQuery = (0, import_jotai33.useSetAtom)(updateSearchQueryAtom);
  const selectCommand = (0, import_jotai33.useSetAtom)(selectCommandAtom);
  const t0 = state.phase === "searching";
  const t1 = state.phase === "collecting";
  const t2 = state.phase === "executing";
  const t3 = state.phase === "error";
  const t4 = state.phase === "error" ? state.message : null;
  let t5;
  if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
    t5 = commandRegistry.all();
    $[0] = t5;
  } else {
    t5 = $[0];
  }
  let t6;
  if ($[1] !== close || $[2] !== currentInput || $[3] !== history || $[4] !== open || $[5] !== progress || $[6] !== selectCommand || $[7] !== state || $[8] !== t0 || $[9] !== t1 || $[10] !== t2 || $[11] !== t3 || $[12] !== t4 || $[13] !== updateQuery || $[14] !== visible) {
    t6 = {
      visible,
      state,
      history,
      currentInput,
      progress,
      open,
      close,
      updateQuery,
      selectCommand,
      isSearching: t0,
      isCollecting: t1,
      isExecuting: t2,
      hasError: t3,
      errorMessage: t4,
      allCommands: t5,
      searchCommands: _temp5
    };
    $[1] = close;
    $[2] = currentInput;
    $[3] = history;
    $[4] = open;
    $[5] = progress;
    $[6] = selectCommand;
    $[7] = state;
    $[8] = t0;
    $[9] = t1;
    $[10] = t2;
    $[11] = t3;
    $[12] = t4;
    $[13] = updateQuery;
    $[14] = visible;
    $[15] = t6;
  } else {
    t6 = $[15];
  }
  return t6;
}
function _temp5(query) {
  return commandRegistry.search(query);
}

// src/hooks/useVirtualization.ts
var import_compiler_runtime13 = require("react/compiler-runtime");
var import_jotai35 = require("jotai");

// src/core/virtualization-store.ts
var import_jotai34 = require("jotai");

// src/core/spatial-index.ts
var SpatialGrid = class {
  constructor(cellSize = 500) {
    /** cell key → set of node IDs in that cell */
    __publicField(this, "cells", /* @__PURE__ */ new Map());
    /** node ID → entry data (for update/remove) */
    __publicField(this, "entries", /* @__PURE__ */ new Map());
    this.cellSize = cellSize;
  }
  /** Number of tracked entries */
  get size() {
    return this.entries.size;
  }
  cellKey(cx, cy) {
    return `${cx},${cy}`;
  }
  getCellRange(x, y, w, h) {
    const cs = this.cellSize;
    return {
      minCX: Math.floor(x / cs),
      minCY: Math.floor(y / cs),
      maxCX: Math.floor((x + w) / cs),
      maxCY: Math.floor((y + h) / cs)
    };
  }
  /**
   * Insert a node into the index.
   * If the node already exists, it is updated.
   */
  insert(id, x, y, width, height) {
    if (this.entries.has(id)) {
      this.update(id, x, y, width, height);
      return;
    }
    const entry = {
      id,
      x,
      y,
      width,
      height
    };
    this.entries.set(id, entry);
    const {
      minCX,
      minCY,
      maxCX,
      maxCY
    } = this.getCellRange(x, y, width, height);
    for (let cx = minCX; cx <= maxCX; cx++) {
      for (let cy = minCY; cy <= maxCY; cy++) {
        const key = this.cellKey(cx, cy);
        let cell = this.cells.get(key);
        if (!cell) {
          cell = /* @__PURE__ */ new Set();
          this.cells.set(key, cell);
        }
        cell.add(id);
      }
    }
  }
  /**
   * Update a node's position/dimensions.
   */
  update(id, x, y, width, height) {
    const prev = this.entries.get(id);
    if (!prev) {
      this.insert(id, x, y, width, height);
      return;
    }
    const prevRange = this.getCellRange(prev.x, prev.y, prev.width, prev.height);
    const newRange = this.getCellRange(x, y, width, height);
    prev.x = x;
    prev.y = y;
    prev.width = width;
    prev.height = height;
    if (prevRange.minCX === newRange.minCX && prevRange.minCY === newRange.minCY && prevRange.maxCX === newRange.maxCX && prevRange.maxCY === newRange.maxCY) {
      return;
    }
    for (let cx = prevRange.minCX; cx <= prevRange.maxCX; cx++) {
      for (let cy = prevRange.minCY; cy <= prevRange.maxCY; cy++) {
        const key = this.cellKey(cx, cy);
        const cell = this.cells.get(key);
        if (cell) {
          cell.delete(id);
          if (cell.size === 0) this.cells.delete(key);
        }
      }
    }
    for (let cx = newRange.minCX; cx <= newRange.maxCX; cx++) {
      for (let cy = newRange.minCY; cy <= newRange.maxCY; cy++) {
        const key = this.cellKey(cx, cy);
        let cell = this.cells.get(key);
        if (!cell) {
          cell = /* @__PURE__ */ new Set();
          this.cells.set(key, cell);
        }
        cell.add(id);
      }
    }
  }
  /**
   * Remove a node from the index.
   */
  remove(id) {
    const entry = this.entries.get(id);
    if (!entry) return;
    const {
      minCX,
      minCY,
      maxCX,
      maxCY
    } = this.getCellRange(entry.x, entry.y, entry.width, entry.height);
    for (let cx = minCX; cx <= maxCX; cx++) {
      for (let cy = minCY; cy <= maxCY; cy++) {
        const key = this.cellKey(cx, cy);
        const cell = this.cells.get(key);
        if (cell) {
          cell.delete(id);
          if (cell.size === 0) this.cells.delete(key);
        }
      }
    }
    this.entries.delete(id);
  }
  /**
   * Query all node IDs whose bounding box overlaps the given bounds.
   * Returns a Set for O(1) membership checks.
   */
  query(bounds) {
    const result = /* @__PURE__ */ new Set();
    const {
      minCX,
      minCY,
      maxCX,
      maxCY
    } = this.getCellRange(bounds.minX, bounds.minY, bounds.maxX - bounds.minX, bounds.maxY - bounds.minY);
    for (let cx = minCX; cx <= maxCX; cx++) {
      for (let cy = minCY; cy <= maxCY; cy++) {
        const cell = this.cells.get(this.cellKey(cx, cy));
        if (!cell) continue;
        for (const id of cell) {
          if (result.has(id)) continue;
          const entry = this.entries.get(id);
          const entryRight = entry.x + entry.width;
          const entryBottom = entry.y + entry.height;
          if (entry.x <= bounds.maxX && entryRight >= bounds.minX && entry.y <= bounds.maxY && entryBottom >= bounds.minY) {
            result.add(id);
          }
        }
      }
    }
    return result;
  }
  /**
   * Clear all entries.
   */
  clear() {
    this.cells.clear();
    this.entries.clear();
  }
  /**
   * Check if a node is tracked.
   */
  has(id) {
    return this.entries.has(id);
  }
};

// src/core/virtualization-store.ts
var VIRTUALIZATION_BUFFER = 200;
var spatialIndexAtom = (0, import_jotai34.atom)((get) => {
  get(graphUpdateVersionAtom);
  get(nodePositionUpdateCounterAtom);
  const graph = get(graphAtom);
  const grid = new SpatialGrid(500);
  graph.forEachNode((nodeId, attrs) => {
    const a = attrs;
    grid.insert(nodeId, a.x, a.y, a.width || 200, a.height || 100);
  });
  return grid;
});
var visibleBoundsAtom = (0, import_jotai34.atom)((get) => {
  const viewport = get(viewportRectAtom);
  const pan = get(panAtom);
  const zoom = get(zoomAtom);
  if (!viewport || zoom === 0) {
    return null;
  }
  const buffer = VIRTUALIZATION_BUFFER;
  return {
    minX: (-buffer - pan.x) / zoom,
    minY: (-buffer - pan.y) / zoom,
    maxX: (viewport.width + buffer - pan.x) / zoom,
    maxY: (viewport.height + buffer - pan.y) / zoom
  };
});
var visibleNodeKeysAtom = (0, import_jotai34.atom)((get) => {
  const end = canvasMark("virtualization-cull");
  const enabled = get(virtualizationEnabledAtom);
  const allKeys = get(nodeKeysAtom);
  if (!enabled) {
    end();
    return allKeys;
  }
  const bounds = get(visibleBoundsAtom);
  if (!bounds) {
    end();
    return allKeys;
  }
  const grid = get(spatialIndexAtom);
  const visibleSet = grid.query(bounds);
  const result = allKeys.filter((k) => visibleSet.has(k));
  end();
  return result;
});
var visibleEdgeKeysAtom = (0, import_jotai34.atom)((get) => {
  const enabled = get(virtualizationEnabledAtom);
  const allEdgeKeys = get(edgeKeysAtom);
  const edgeCreation = get(edgeCreationAtom);
  const remap = get(collapsedEdgeRemapAtom);
  const tempEdgeKey = edgeCreation.isCreating ? "temp-creating-edge" : null;
  get(graphUpdateVersionAtom);
  const graph = get(graphAtom);
  const filteredEdges = allEdgeKeys.filter((edgeKey) => {
    const source = graph.source(edgeKey);
    const target = graph.target(edgeKey);
    const effectiveSource = remap.get(source) ?? source;
    const effectiveTarget = remap.get(target) ?? target;
    if (effectiveSource === effectiveTarget) return false;
    return true;
  });
  if (!enabled) {
    return tempEdgeKey ? [...filteredEdges, tempEdgeKey] : filteredEdges;
  }
  const visibleNodeKeys = get(visibleNodeKeysAtom);
  const visibleNodeSet = new Set(visibleNodeKeys);
  const visibleEdges = filteredEdges.filter((edgeKey) => {
    const source = graph.source(edgeKey);
    const target = graph.target(edgeKey);
    const effectiveSource = remap.get(source) ?? source;
    const effectiveTarget = remap.get(target) ?? target;
    return visibleNodeSet.has(effectiveSource) && visibleNodeSet.has(effectiveTarget);
  });
  return tempEdgeKey ? [...visibleEdges, tempEdgeKey] : visibleEdges;
});
var virtualizationMetricsAtom = (0, import_jotai34.atom)((get) => {
  const enabled = get(virtualizationEnabledAtom);
  const totalNodes = get(nodeKeysAtom).length;
  const totalEdges = get(edgeKeysAtom).length;
  const visibleNodes = get(visibleNodeKeysAtom).length;
  const visibleEdges = get(visibleEdgeKeysAtom).length;
  const bounds = get(visibleBoundsAtom);
  return {
    enabled,
    totalNodes,
    totalEdges,
    visibleNodes,
    visibleEdges,
    culledNodes: totalNodes - visibleNodes,
    culledEdges: totalEdges - visibleEdges,
    bounds
  };
});

// src/hooks/useVirtualization.ts
function useVirtualization() {
  const $ = (0, import_compiler_runtime13.c)(8);
  const metrics = (0, import_jotai35.useAtomValue)(virtualizationMetricsAtom);
  const setEnabled = (0, import_jotai35.useSetAtom)(setVirtualizationEnabledAtom);
  const toggle = (0, import_jotai35.useSetAtom)(toggleVirtualizationAtom);
  let t0;
  let t1;
  if ($[0] !== setEnabled) {
    t0 = () => setEnabled(true);
    t1 = () => setEnabled(false);
    $[0] = setEnabled;
    $[1] = t0;
    $[2] = t1;
  } else {
    t0 = $[1];
    t1 = $[2];
  }
  let t2;
  if ($[3] !== metrics || $[4] !== t0 || $[5] !== t1 || $[6] !== toggle) {
    t2 = {
      ...metrics,
      enable: t0,
      disable: t1,
      toggle
    };
    $[3] = metrics;
    $[4] = t0;
    $[5] = t1;
    $[6] = toggle;
    $[7] = t2;
  } else {
    t2 = $[7];
  }
  return t2;
}

// src/hooks/useTapGesture.ts
var import_compiler_runtime14 = require("react/compiler-runtime");
var import_react5 = require("react");
function useTapGesture(t0) {
  const $ = (0, import_compiler_runtime14.c)(12);
  let t1;
  if ($[0] !== t0) {
    t1 = t0 === void 0 ? {} : t0;
    $[0] = t0;
    $[1] = t1;
  } else {
    t1 = $[1];
  }
  const options = t1;
  const {
    onSingleTap,
    onDoubleTap,
    onTripleTap,
    tapDelay: t2,
    tapDistance: t3
  } = options;
  const tapDelay = t2 === void 0 ? 300 : t2;
  const tapDistance = t3 === void 0 ? 25 : t3;
  let t4;
  if ($[2] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
    t4 = {
      count: 0,
      timer: null,
      lastX: 0,
      lastY: 0,
      lastEvent: null
    };
    $[2] = t4;
  } else {
    t4 = $[2];
  }
  const stateRef = (0, import_react5.useRef)(t4);
  let t5;
  if ($[3] !== onDoubleTap || $[4] !== onSingleTap || $[5] !== onTripleTap || $[6] !== tapDelay || $[7] !== tapDistance) {
    t5 = (event) => {
      const state = stateRef.current;
      const dx = Math.abs(event.clientX - state.lastX);
      const dy = Math.abs(event.clientY - state.lastY);
      const isSameSpot = state.count === 0 || dx < tapDistance && dy < tapDistance;
      if (!isSameSpot) {
        if (state.timer) {
          clearTimeout(state.timer);
        }
        state.count = 0;
      }
      state.count = state.count + 1;
      state.lastX = event.clientX;
      state.lastY = event.clientY;
      state.lastEvent = event;
      if (state.timer) {
        clearTimeout(state.timer);
      }
      if (state.count >= 3) {
        onTripleTap?.(event);
        state.count = 0;
        state.timer = null;
        return;
      }
      if (state.count === 2) {
        onDoubleTap?.(event);
        state.timer = setTimeout(() => {
          state.count = 0;
          state.timer = null;
        }, tapDelay);
        return;
      }
      state.timer = setTimeout(() => {
        if (state.count === 1 && state.lastEvent) {
          onSingleTap?.(state.lastEvent);
        }
        state.count = 0;
        state.timer = null;
      }, tapDelay);
    };
    $[3] = onDoubleTap;
    $[4] = onSingleTap;
    $[5] = onTripleTap;
    $[6] = tapDelay;
    $[7] = tapDistance;
    $[8] = t5;
  } else {
    t5 = $[8];
  }
  const handleTap = t5;
  let t6;
  if ($[9] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
    t6 = () => {
      const state_0 = stateRef.current;
      if (state_0.timer) {
        clearTimeout(state_0.timer);
        state_0.timer = null;
      }
      state_0.count = 0;
    };
    $[9] = t6;
  } else {
    t6 = $[9];
  }
  const cleanup = t6;
  let t7;
  if ($[10] !== handleTap) {
    t7 = {
      handleTap,
      cleanup
    };
    $[10] = handleTap;
    $[11] = t7;
  } else {
    t7 = $[11];
  }
  return t7;
}

// src/hooks/useArrowKeyNavigation.ts
var import_compiler_runtime15 = require("react/compiler-runtime");
var import_jotai36 = require("jotai");
function useArrowKeyNavigation() {
  const $ = (0, import_compiler_runtime15.c)(2);
  const focusedNodeId = (0, import_jotai36.useAtomValue)(focusedNodeIdAtom);
  let t0;
  if ($[0] !== focusedNodeId) {
    t0 = {
      focusedNodeId
    };
    $[0] = focusedNodeId;
    $[1] = t0;
  } else {
    t0 = $[1];
  }
  return t0;
}

// src/hooks/useCanvasGraph.ts
var import_compiler_runtime16 = require("react/compiler-runtime");
var import_jotai37 = require("jotai");
function useCanvasGraph() {
  const $ = (0, import_compiler_runtime16.c)(9);
  const graph = (0, import_jotai37.useAtomValue)(graphAtom);
  const nodeKeys = (0, import_jotai37.useAtomValue)(nodeKeysAtom);
  const edgeKeys = (0, import_jotai37.useAtomValue)(edgeKeysAtom);
  let t0;
  if ($[0] !== graph) {
    t0 = (id) => graph.hasNode(id) ? graph.getNodeAttributes(id) : void 0;
    $[0] = graph;
    $[1] = t0;
  } else {
    t0 = $[1];
  }
  const getNode = t0;
  let t1;
  if ($[2] !== graph) {
    t1 = (id_0) => graph.hasEdge(id_0) ? graph.getEdgeAttributes(id_0) : void 0;
    $[2] = graph;
    $[3] = t1;
  } else {
    t1 = $[3];
  }
  const getEdge = t1;
  let t2;
  if ($[4] !== edgeKeys || $[5] !== getEdge || $[6] !== getNode || $[7] !== nodeKeys) {
    t2 = {
      nodeCount: nodeKeys.length,
      edgeCount: edgeKeys.length,
      nodeKeys,
      edgeKeys,
      getNode,
      getEdge
    };
    $[4] = edgeKeys;
    $[5] = getEdge;
    $[6] = getNode;
    $[7] = nodeKeys;
    $[8] = t2;
  } else {
    t2 = $[8];
  }
  return t2;
}

// src/hooks/useZoomTransition.ts
var import_compiler_runtime17 = require("react/compiler-runtime");
var import_react6 = require("react");
var import_jotai38 = require("jotai");
function easeInOutCubic(t) {
  return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
}
function useZoomTransition() {
  const $ = (0, import_compiler_runtime17.c)(15);
  const target = (0, import_jotai38.useAtomValue)(zoomAnimationTargetAtom);
  const setZoom = (0, import_jotai38.useSetAtom)(zoomAtom);
  const setPan = (0, import_jotai38.useSetAtom)(panAtom);
  const setTarget = (0, import_jotai38.useSetAtom)(zoomAnimationTargetAtom);
  const setProgress = (0, import_jotai38.useSetAtom)(zoomTransitionProgressAtom);
  const setFocusNode = (0, import_jotai38.useSetAtom)(zoomFocusNodeIdAtom);
  const progress = (0, import_jotai38.useAtomValue)(zoomTransitionProgressAtom);
  const rafRef = (0, import_react6.useRef)(null);
  let t0;
  if ($[0] !== setFocusNode || $[1] !== setProgress || $[2] !== setTarget) {
    t0 = () => {
      if (rafRef.current !== null) {
        cancelAnimationFrame(rafRef.current);
        rafRef.current = null;
      }
      setTarget(null);
      setProgress(0);
      setFocusNode(null);
    };
    $[0] = setFocusNode;
    $[1] = setProgress;
    $[2] = setTarget;
    $[3] = t0;
  } else {
    t0 = $[3];
  }
  const cancel = t0;
  let t1;
  let t2;
  if ($[4] !== setPan || $[5] !== setProgress || $[6] !== setTarget || $[7] !== setZoom || $[8] !== target) {
    t1 = () => {
      if (!target) {
        return;
      }
      const animate = () => {
        const elapsed = performance.now() - target.startTime;
        const rawT = Math.min(1, elapsed / target.duration);
        const t = easeInOutCubic(rawT);
        const currentZoom = target.startZoom + (target.targetZoom - target.startZoom) * t;
        const currentPanX = target.startPan.x + (target.targetPan.x - target.startPan.x) * t;
        const currentPanY = target.startPan.y + (target.targetPan.y - target.startPan.y) * t;
        setZoom(currentZoom);
        setPan({
          x: currentPanX,
          y: currentPanY
        });
        setProgress(t);
        if (rawT < 1) {
          rafRef.current = requestAnimationFrame(animate);
        } else {
          rafRef.current = null;
          setTarget(null);
        }
      };
      rafRef.current = requestAnimationFrame(animate);
      return () => {
        if (rafRef.current !== null) {
          cancelAnimationFrame(rafRef.current);
          rafRef.current = null;
        }
      };
    };
    t2 = [target, setZoom, setPan, setTarget, setProgress];
    $[4] = setPan;
    $[5] = setProgress;
    $[6] = setTarget;
    $[7] = setZoom;
    $[8] = target;
    $[9] = t1;
    $[10] = t2;
  } else {
    t1 = $[9];
    t2 = $[10];
  }
  (0, import_react6.useEffect)(t1, t2);
  const t3 = target !== null;
  let t4;
  if ($[11] !== cancel || $[12] !== progress || $[13] !== t3) {
    t4 = {
      isAnimating: t3,
      progress,
      cancel
    };
    $[11] = cancel;
    $[12] = progress;
    $[13] = t3;
    $[14] = t4;
  } else {
    t4 = $[14];
  }
  return t4;
}

// src/hooks/useSplitGesture.ts
var import_compiler_runtime18 = require("react/compiler-runtime");
var import_react7 = require("react");
var import_jotai39 = require("jotai");
var SPLIT_THRESHOLD = 80;
function useSplitGesture(nodeId) {
  const $ = (0, import_compiler_runtime18.c)(9);
  const splitNode = (0, import_jotai39.useSetAtom)(splitNodeAtom);
  const screenToWorld = (0, import_jotai39.useAtomValue)(screenToWorldAtom);
  let t0;
  if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
    t0 = /* @__PURE__ */ new Map();
    $[0] = t0;
  } else {
    t0 = $[0];
  }
  const pointersRef = (0, import_react7.useRef)(t0);
  const initialDistanceRef = (0, import_react7.useRef)(null);
  const splitFiredRef = (0, import_react7.useRef)(false);
  const getDistance = _temp6;
  let t1;
  if ($[1] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
    t1 = (e) => {
      if (e.pointerType !== "touch") {
        return;
      }
      pointersRef.current.set(e.pointerId, {
        pointerId: e.pointerId,
        x: e.clientX,
        y: e.clientY
      });
      if (pointersRef.current.size === 2) {
        const [p1, p2] = Array.from(pointersRef.current.values());
        initialDistanceRef.current = getDistance(p1, p2);
        splitFiredRef.current = false;
      }
    };
    $[1] = t1;
  } else {
    t1 = $[1];
  }
  const onPointerDown = t1;
  let t2;
  if ($[2] !== nodeId || $[3] !== screenToWorld || $[4] !== splitNode) {
    t2 = (e_0) => {
      if (e_0.pointerType !== "touch") {
        return;
      }
      if (!pointersRef.current.has(e_0.pointerId)) {
        return;
      }
      pointersRef.current.set(e_0.pointerId, {
        pointerId: e_0.pointerId,
        x: e_0.clientX,
        y: e_0.clientY
      });
      if (pointersRef.current.size === 2 && initialDistanceRef.current !== null && !splitFiredRef.current) {
        const [p1_0, p2_0] = Array.from(pointersRef.current.values());
        const currentDistance = getDistance(p1_0, p2_0);
        const delta = currentDistance - initialDistanceRef.current;
        if (delta > SPLIT_THRESHOLD) {
          splitFiredRef.current = true;
          e_0.stopPropagation();
          const world1 = screenToWorld(p1_0.x, p1_0.y);
          const world2 = screenToWorld(p2_0.x, p2_0.y);
          splitNode({
            nodeId,
            position1: world1,
            position2: world2
          });
        }
      }
    };
    $[2] = nodeId;
    $[3] = screenToWorld;
    $[4] = splitNode;
    $[5] = t2;
  } else {
    t2 = $[5];
  }
  const onPointerMove = t2;
  let t3;
  if ($[6] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
    t3 = (e_1) => {
      pointersRef.current.delete(e_1.pointerId);
      if (pointersRef.current.size < 2) {
        initialDistanceRef.current = null;
        splitFiredRef.current = false;
      }
    };
    $[6] = t3;
  } else {
    t3 = $[6];
  }
  const onPointerUp = t3;
  let t4;
  if ($[7] !== onPointerMove) {
    t4 = {
      onPointerDown,
      onPointerMove,
      onPointerUp
    };
    $[7] = onPointerMove;
    $[8] = t4;
  } else {
    t4 = $[8];
  }
  return t4;
}
function _temp6(a, b) {
  return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2);
}

// src/hooks/useAnimatedLayout.ts
var import_jotai40 = require("jotai");
var import_react8 = require("react");
var debug14 = createDebug("animated-layout");
function easeInOutCubic2(t) {
  return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
}
function useAnimatedLayout(options = {}) {
  const {
    onPositionsChanged,
    duration = 400
  } = options;
  const graph = (0, import_jotai40.useAtomValue)(graphAtom);
  const updateNodePosition = (0, import_jotai40.useSetAtom)(updateNodePositionAtom);
  const pushHistory = (0, import_jotai40.useSetAtom)(pushHistoryAtom);
  const setPositionCounter = (0, import_jotai40.useSetAtom)(nodePositionUpdateCounterAtom);
  const reducedMotion = (0, import_jotai40.useAtomValue)(prefersReducedMotionAtom);
  const isAnimatingRef = (0, import_react8.useRef)(false);
  const animate = async (targets, label) => {
    if (isAnimatingRef.current) return;
    if (targets.size === 0) return;
    if (label) pushHistory(label);
    isAnimatingRef.current = true;
    if (reducedMotion) {
      for (const [nodeId, target] of targets) {
        updateNodePosition({
          nodeId,
          position: target
        });
      }
      isAnimatingRef.current = false;
      setPositionCounter((c) => c + 1);
      if (onPositionsChanged) {
        const updates = [];
        for (const [nodeId_0, target_0] of targets) {
          updates.push({
            nodeId: nodeId_0,
            position: target_0
          });
        }
        Promise.resolve(onPositionsChanged(updates)).catch((err) => debug14.error("Position change callback failed: %O", err));
      }
      return;
    }
    const starts = /* @__PURE__ */ new Map();
    for (const [nodeId_1] of targets) {
      if (graph.hasNode(nodeId_1)) {
        const attrs = graph.getNodeAttributes(nodeId_1);
        starts.set(nodeId_1, {
          x: attrs.x,
          y: attrs.y
        });
      }
    }
    return new Promise((resolve) => {
      const startTime = performance.now();
      function tick() {
        const elapsed = performance.now() - startTime;
        const t = Math.min(elapsed / duration, 1);
        const eased = easeInOutCubic2(t);
        for (const [nodeId_2, target_1] of targets) {
          const start = starts.get(nodeId_2);
          if (!start) continue;
          const x = Math.round(start.x + (target_1.x - start.x) * eased);
          const y = Math.round(start.y + (target_1.y - start.y) * eased);
          updateNodePosition({
            nodeId: nodeId_2,
            position: {
              x,
              y
            }
          });
        }
        if (t < 1) {
          requestAnimationFrame(tick);
        } else {
          isAnimatingRef.current = false;
          setPositionCounter((c_0) => c_0 + 1);
          if (onPositionsChanged) {
            const updates_0 = [];
            for (const [nodeId_3, target_2] of targets) {
              updates_0.push({
                nodeId: nodeId_3,
                position: target_2
              });
            }
            Promise.resolve(onPositionsChanged(updates_0)).catch((err_0) => debug14.error("Position change callback failed: %O", err_0));
          }
          resolve();
        }
      }
      requestAnimationFrame(tick);
    });
  };
  return {
    animate,
    isAnimating: isAnimatingRef.current
  };
}

// src/hooks/useTreeLayout.ts
var import_jotai41 = require("jotai");
var import_react9 = require("react");
function useTreeLayout(options = {}) {
  const {
    direction = "top-down",
    levelGap = 200,
    nodeGap = 100,
    ...animateOptions
  } = options;
  const graph = (0, import_jotai41.useAtomValue)(graphAtom);
  const nodes = (0, import_jotai41.useAtomValue)(uiNodesAtom);
  const {
    animate,
    isAnimating
  } = useAnimatedLayout(animateOptions);
  const isRunningRef = (0, import_react9.useRef)(false);
  const applyLayout = async () => {
    if (isRunningRef.current || isAnimating) return;
    if (nodes.length === 0) return;
    isRunningRef.current = true;
    const nodeIds = new Set(nodes.map((n) => n.id));
    const children = /* @__PURE__ */ new Map();
    const hasIncoming = /* @__PURE__ */ new Set();
    for (const nodeId of nodeIds) {
      children.set(nodeId, []);
    }
    graph.forEachEdge((_key, _attrs, source, target) => {
      if (nodeIds.has(source) && nodeIds.has(target) && source !== target) {
        children.get(source)?.push(target);
        hasIncoming.add(target);
      }
    });
    const roots = [...nodeIds].filter((id) => !hasIncoming.has(id));
    if (roots.length === 0) {
      roots.push(nodes[0].id);
    }
    const levels = /* @__PURE__ */ new Map();
    const queue = [...roots];
    for (const r of roots) levels.set(r, 0);
    while (queue.length > 0) {
      const current = queue.shift();
      const level = levels.get(current);
      for (const child of children.get(current) || []) {
        if (!levels.has(child)) {
          levels.set(child, level + 1);
          queue.push(child);
        }
      }
    }
    for (const nodeId_0 of nodeIds) {
      if (!levels.has(nodeId_0)) levels.set(nodeId_0, 0);
    }
    const byLevel = /* @__PURE__ */ new Map();
    for (const [nodeId_1, level_0] of levels) {
      if (!byLevel.has(level_0)) byLevel.set(level_0, []);
      byLevel.get(level_0).push(nodeId_1);
    }
    const targets = /* @__PURE__ */ new Map();
    const maxLevel = Math.max(...byLevel.keys());
    for (const [level_1, nodeIdsAtLevel] of byLevel) {
      const count = nodeIdsAtLevel.length;
      let maxNodeSize = 200;
      for (const nid of nodeIdsAtLevel) {
        if (graph.hasNode(nid)) {
          const attrs = graph.getNodeAttributes(nid);
          maxNodeSize = Math.max(maxNodeSize, attrs.width || 200);
        }
      }
      const totalWidth = (count - 1) * (maxNodeSize + nodeGap);
      const startX = -totalWidth / 2;
      for (let i = 0; i < count; i++) {
        const primary = level_1 * levelGap;
        const secondary = startX + i * (maxNodeSize + nodeGap);
        if (direction === "top-down") {
          targets.set(nodeIdsAtLevel[i], {
            x: secondary,
            y: primary
          });
        } else {
          targets.set(nodeIdsAtLevel[i], {
            x: primary,
            y: secondary
          });
        }
      }
    }
    await animate(targets, direction === "top-down" ? "Tree layout" : "Horizontal layout");
    isRunningRef.current = false;
  };
  return {
    applyLayout,
    isRunning: isRunningRef.current || isAnimating
  };
}

// src/hooks/useGridLayout.ts
var import_jotai42 = require("jotai");
var import_react10 = require("react");
function useGridLayout(options = {}) {
  const {
    columns,
    gap = 80,
    ...animateOptions
  } = options;
  const graph = (0, import_jotai42.useAtomValue)(graphAtom);
  const nodes = (0, import_jotai42.useAtomValue)(uiNodesAtom);
  const {
    animate,
    isAnimating
  } = useAnimatedLayout(animateOptions);
  const isRunningRef = (0, import_react10.useRef)(false);
  const applyLayout = async () => {
    if (isRunningRef.current || isAnimating) return;
    if (nodes.length === 0) return;
    isRunningRef.current = true;
    const sorted = [...nodes].sort((a, b) => {
      const ay = a.position?.y ?? 0;
      const by = b.position?.y ?? 0;
      if (Math.abs(ay - by) > 50) return ay - by;
      return (a.position?.x ?? 0) - (b.position?.x ?? 0);
    });
    const cols = columns ?? Math.ceil(Math.sqrt(sorted.length));
    let maxW = 200;
    let maxH = 100;
    for (const node of sorted) {
      if (graph.hasNode(node.id)) {
        const attrs = graph.getNodeAttributes(node.id);
        maxW = Math.max(maxW, attrs.width || 200);
        maxH = Math.max(maxH, attrs.height || 100);
      }
    }
    const cellW = maxW + gap;
    const cellH = maxH + gap;
    const rows = Math.ceil(sorted.length / cols);
    const totalW = (cols - 1) * cellW;
    const totalH = (rows - 1) * cellH;
    const offsetX = -totalW / 2;
    const offsetY = -totalH / 2;
    const targets = /* @__PURE__ */ new Map();
    for (let i = 0; i < sorted.length; i++) {
      const col = i % cols;
      const row = Math.floor(i / cols);
      targets.set(sorted[i].id, {
        x: Math.round(offsetX + col * cellW),
        y: Math.round(offsetY + row * cellH)
      });
    }
    await animate(targets, "Grid layout");
    isRunningRef.current = false;
  };
  return {
    applyLayout,
    isRunning: isRunningRef.current || isAnimating
  };
}

// src/hooks/usePlugin.ts
var import_compiler_runtime20 = require("react/compiler-runtime");
var import_react12 = require("react");

// src/core/plugin-types.ts
var PluginError = class extends Error {
  constructor(message, pluginId, code) {
    super(`[Plugin "${pluginId}"] ${message}`);
    this.pluginId = pluginId;
    this.code = code;
    this.name = "PluginError";
  }
};

// src/core/node-type-registry.tsx
var import_compiler_runtime19 = require("react/compiler-runtime");
var import_react11 = __toESM(require("react"));
var import_jsx_runtime = require("react/jsx-runtime");
var nodeTypeRegistry = /* @__PURE__ */ new Map();
function registerNodeTypes(types) {
  for (const [nodeType, component] of Object.entries(types)) {
    nodeTypeRegistry.set(nodeType, component);
  }
}
function unregisterNodeType(nodeType) {
  return nodeTypeRegistry.delete(nodeType);
}

// src/gestures/types.ts
var NO_MODIFIERS = Object.freeze({
  shift: false,
  ctrl: false,
  alt: false,
  meta: false
});
var NO_HELD_KEYS = Object.freeze({
  byKey: Object.freeze({}),
  byCode: Object.freeze({})
});

// src/gestures/dispatcher.ts
var handlers = /* @__PURE__ */ new Map();
function registerAction2(actionId, handler) {
  handlers.set(actionId, handler);
}
function unregisterAction2(actionId) {
  handlers.delete(actionId);
}

// src/utils/edge-path-registry.ts
var customCalculators = /* @__PURE__ */ new Map();
function registerEdgePathCalculator(name, calculator) {
  customCalculators.set(name, calculator);
}
function unregisterEdgePathCalculator(name) {
  return customCalculators.delete(name);
}

// src/core/plugin-registry.ts
var debug15 = createDebug("plugins");
var plugins = /* @__PURE__ */ new Map();
function registerPlugin(plugin) {
  debug15("Registering plugin: %s", plugin.id);
  if (plugins.has(plugin.id)) {
    throw new PluginError("Plugin is already registered", plugin.id, "ALREADY_REGISTERED");
  }
  if (plugin.dependencies) {
    for (const depId of plugin.dependencies) {
      if (!plugins.has(depId)) {
        throw new PluginError(`Missing dependency: "${depId}"`, plugin.id, "MISSING_DEPENDENCY");
      }
    }
  }
  detectConflicts(plugin);
  const cleanups = [];
  try {
    if (plugin.nodeTypes) {
      const nodeTypeNames = Object.keys(plugin.nodeTypes);
      registerNodeTypes(plugin.nodeTypes);
      cleanups.push(() => {
        for (const name of nodeTypeNames) {
          unregisterNodeType(name);
        }
      });
    }
    if (plugin.edgePathCalculators) {
      for (const [name, calc] of Object.entries(plugin.edgePathCalculators)) {
        registerEdgePathCalculator(name, calc);
        cleanups.push(() => unregisterEdgePathCalculator(name));
      }
    }
    if (plugin.actionHandlers) {
      for (const [actionId, handler] of Object.entries(plugin.actionHandlers)) {
        registerAction2(actionId, handler);
        cleanups.push(() => unregisterAction2(actionId));
      }
    }
    if (plugin.commands) {
      for (const cmd of plugin.commands) {
        commandRegistry.register(cmd);
        cleanups.push(() => commandRegistry.unregister(cmd.name));
      }
    }
    if (plugin.actions) {
      for (const action of plugin.actions) {
        registerAction(action);
        cleanups.push(() => unregisterAction(action.id));
      }
    }
    let lifecycleCleanup = null;
    if (plugin.onRegister) {
      const ctx = makePluginContext(plugin.id);
      try {
        const result = plugin.onRegister(ctx);
        if (typeof result === "function") {
          lifecycleCleanup = result;
        }
      } catch (err) {
        for (const cleanup of cleanups.reverse()) {
          try {
            cleanup();
          } catch {
          }
        }
        throw new PluginError(`onRegister failed: ${err instanceof Error ? err.message : String(err)}`, plugin.id, "LIFECYCLE_ERROR");
      }
    }
    plugins.set(plugin.id, {
      plugin,
      cleanup: () => {
        for (const cleanup of cleanups.reverse()) {
          try {
            cleanup();
          } catch {
          }
        }
        if (lifecycleCleanup) {
          try {
            lifecycleCleanup();
          } catch {
          }
        }
      },
      registeredAt: Date.now()
    });
    debug15("Plugin registered: %s (%d node types, %d commands, %d actions)", plugin.id, Object.keys(plugin.nodeTypes ?? {}).length, plugin.commands?.length ?? 0, plugin.actions?.length ?? 0);
  } catch (err) {
    if (err instanceof PluginError) throw err;
    for (const cleanup of cleanups.reverse()) {
      try {
        cleanup();
      } catch {
      }
    }
    throw err;
  }
}
function unregisterPlugin(pluginId) {
  const registration = plugins.get(pluginId);
  if (!registration) {
    throw new PluginError("Plugin is not registered", pluginId, "NOT_FOUND");
  }
  for (const [otherId, other] of plugins) {
    if (other.plugin.dependencies?.includes(pluginId)) {
      throw new PluginError(`Cannot unregister: plugin "${otherId}" depends on it`, pluginId, "CONFLICT");
    }
  }
  if (registration.cleanup) {
    registration.cleanup();
  }
  plugins.delete(pluginId);
  debug15("Plugin unregistered: %s", pluginId);
}
function getPlugin(id) {
  return plugins.get(id)?.plugin;
}
function hasPlugin(id) {
  return plugins.has(id);
}
function detectConflicts(plugin) {
  if (plugin.commands) {
    for (const cmd of plugin.commands) {
      if (commandRegistry.has(cmd.name)) {
        throw new PluginError(`Command "${cmd.name}" is already registered`, plugin.id, "CONFLICT");
      }
    }
  }
  if (plugin.edgePathCalculators) {
    for (const name of Object.keys(plugin.edgePathCalculators)) {
      for (const [otherId, other] of plugins) {
        if (other.plugin.edgePathCalculators?.[name]) {
          throw new PluginError(`Edge path calculator "${name}" already registered by plugin "${otherId}"`, plugin.id, "CONFLICT");
        }
      }
    }
  }
  if (plugin.nodeTypes) {
    for (const nodeType of Object.keys(plugin.nodeTypes)) {
      for (const [otherId, other] of plugins) {
        if (other.plugin.nodeTypes?.[nodeType]) {
          throw new PluginError(`Node type "${nodeType}" already registered by plugin "${otherId}"`, plugin.id, "CONFLICT");
        }
      }
    }
  }
  if (plugin.actionHandlers) {
    for (const actionId of Object.keys(plugin.actionHandlers)) {
      for (const [otherId, other] of plugins) {
        if (other.plugin.actionHandlers?.[actionId]) {
          throw new PluginError(`Action handler "${actionId}" already registered by plugin "${otherId}"`, plugin.id, "CONFLICT");
        }
      }
    }
  }
}
function makePluginContext(pluginId) {
  return {
    pluginId,
    getPlugin,
    hasPlugin
  };
}

// src/hooks/usePlugin.ts
function usePlugin(plugin) {
  const $ = (0, import_compiler_runtime20.c)(4);
  const registeredRef = (0, import_react12.useRef)(false);
  let t0;
  if ($[0] !== plugin) {
    t0 = () => {
      if (!hasPlugin(plugin.id)) {
        registerPlugin(plugin);
        registeredRef.current = true;
      }
      return () => {
        if (registeredRef.current && hasPlugin(plugin.id)) {
          try {
            unregisterPlugin(plugin.id);
          } catch {
          }
          registeredRef.current = false;
        }
      };
    };
    $[0] = plugin;
    $[1] = t0;
  } else {
    t0 = $[1];
  }
  let t1;
  if ($[2] !== plugin.id) {
    t1 = [plugin.id];
    $[2] = plugin.id;
    $[3] = t1;
  } else {
    t1 = $[3];
  }
  (0, import_react12.useEffect)(t0, t1);
}
function usePlugins(plugins2) {
  const $ = (0, import_compiler_runtime20.c)(6);
  let t0;
  if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
    t0 = [];
    $[0] = t0;
  } else {
    t0 = $[0];
  }
  const registeredRef = (0, import_react12.useRef)(t0);
  let t1;
  let t2;
  if ($[1] !== plugins2) {
    t1 = () => {
      const registered = [];
      for (const plugin of plugins2) {
        if (!hasPlugin(plugin.id)) {
          registerPlugin(plugin);
          registered.push(plugin.id);
        }
      }
      registeredRef.current = registered;
      return () => {
        for (const id of registeredRef.current.reverse()) {
          if (hasPlugin(id)) {
            try {
              unregisterPlugin(id);
            } catch {
            }
          }
        }
        registeredRef.current = [];
      };
    };
    t2 = plugins2.map(_temp7).join(",");
    $[1] = plugins2;
    $[2] = t1;
    $[3] = t2;
  } else {
    t1 = $[2];
    t2 = $[3];
  }
  let t3;
  if ($[4] !== t2) {
    t3 = [t2];
    $[4] = t2;
    $[5] = t3;
  } else {
    t3 = $[5];
  }
  (0, import_react12.useEffect)(t1, t3);
}
function _temp7(p) {
  return p.id;
}
//# sourceMappingURL=index.js.map