var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __esm = (fn, res) => function __init() {
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);

// src/commands/registry.ts
function registerCommand(command) {
  commandRegistry.register(command);
}
var CommandRegistry, commandRegistry;
var init_registry = __esm({
  "src/commands/registry.ts"() {
    "use strict";
    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
          }))
        }));
      }
    };
    commandRegistry = new CommandRegistry();
  }
});

// src/core/graph-store.ts
var graph_store_exports = {};
__export(graph_store_exports, {
  currentGraphIdAtom: () => currentGraphIdAtom,
  draggingNodeIdAtom: () => draggingNodeIdAtom,
  edgeCreationAtom: () => edgeCreationAtom,
  graphAtom: () => graphAtom,
  graphOptions: () => graphOptions,
  graphUpdateVersionAtom: () => graphUpdateVersionAtom,
  preDragNodeAttributesAtom: () => preDragNodeAttributesAtom
});
import { atom as atom3 } from "jotai";
import Graph from "graphology";
var graphOptions, currentGraphIdAtom, graphAtom, graphUpdateVersionAtom, edgeCreationAtom, draggingNodeIdAtom, preDragNodeAttributesAtom;
var init_graph_store = __esm({
  "src/core/graph-store.ts"() {
    "use strict";
    graphOptions = {
      type: "directed",
      multi: true,
      allowSelfLoops: true
    };
    currentGraphIdAtom = atom3(null);
    graphAtom = atom3(new Graph(graphOptions));
    graphUpdateVersionAtom = atom3(0);
    edgeCreationAtom = atom3({
      isCreating: false,
      sourceNodeId: null,
      sourceNodePosition: null,
      targetPosition: null,
      hoveredTargetNodeId: null,
      sourceHandle: null,
      targetHandle: null,
      sourcePort: null,
      targetPort: null,
      snappedTargetPosition: null
    });
    draggingNodeIdAtom = atom3(null);
    preDragNodeAttributesAtom = atom3(null);
  }
});

// src/utils/debug.ts
import debugFactory from "debug";
function createDebug(module) {
  const base = debugFactory(`${NAMESPACE}:${module}`);
  const warn = debugFactory(`${NAMESPACE}:${module}:warn`);
  const error = debugFactory(`${NAMESPACE}:${module}: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 NAMESPACE, debug;
var init_debug = __esm({
  "src/utils/debug.ts"() {
    "use strict";
    NAMESPACE = "canvas";
    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 selection_store_exports = {};
__export(selection_store_exports, {
  addNodesToSelectionAtom: () => addNodesToSelectionAtom,
  clearEdgeSelectionAtom: () => clearEdgeSelectionAtom,
  clearSelectionAtom: () => clearSelectionAtom,
  focusedNodeIdAtom: () => focusedNodeIdAtom,
  handleNodePointerDownSelectionAtom: () => handleNodePointerDownSelectionAtom,
  hasFocusedNodeAtom: () => hasFocusedNodeAtom,
  hasSelectionAtom: () => hasSelectionAtom,
  removeNodesFromSelectionAtom: () => removeNodesFromSelectionAtom,
  selectEdgeAtom: () => selectEdgeAtom,
  selectSingleNodeAtom: () => selectSingleNodeAtom,
  selectedEdgeIdAtom: () => selectedEdgeIdAtom,
  selectedNodeIdsAtom: () => selectedNodeIdsAtom,
  selectedNodesCountAtom: () => selectedNodesCountAtom,
  setFocusedNodeAtom: () => setFocusedNodeAtom,
  toggleNodeInSelectionAtom: () => toggleNodeInSelectionAtom
});
import { atom as atom4 } from "jotai";
var debug2, selectedNodeIdsAtom, selectedEdgeIdAtom, handleNodePointerDownSelectionAtom, selectSingleNodeAtom, toggleNodeInSelectionAtom, clearSelectionAtom, addNodesToSelectionAtom, removeNodesFromSelectionAtom, selectEdgeAtom, clearEdgeSelectionAtom, focusedNodeIdAtom, setFocusedNodeAtom, hasFocusedNodeAtom, selectedNodesCountAtom, hasSelectionAtom;
var init_selection_store = __esm({
  "src/core/selection-store.ts"() {
    "use strict";
    init_debug();
    debug2 = createDebug("selection");
    selectedNodeIdsAtom = atom4(/* @__PURE__ */ new Set());
    selectedEdgeIdAtom = atom4(null);
    handleNodePointerDownSelectionAtom = atom4(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");
        }
      }
    });
    selectSingleNodeAtom = atom4(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]));
      }
    });
    toggleNodeInSelectionAtom = atom4(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);
    });
    clearSelectionAtom = atom4(null, (_get, set) => {
      debug2("clearSelection");
      set(selectedNodeIdsAtom, /* @__PURE__ */ new Set());
    });
    addNodesToSelectionAtom = atom4(null, (get, set, nodeIds) => {
      const currentSelection = get(selectedNodeIdsAtom);
      const newSelection = new Set(currentSelection);
      for (const nodeId of nodeIds) {
        newSelection.add(nodeId);
      }
      set(selectedNodeIdsAtom, newSelection);
    });
    removeNodesFromSelectionAtom = atom4(null, (get, set, nodeIds) => {
      const currentSelection = get(selectedNodeIdsAtom);
      const newSelection = new Set(currentSelection);
      for (const nodeId of nodeIds) {
        newSelection.delete(nodeId);
      }
      set(selectedNodeIdsAtom, newSelection);
    });
    selectEdgeAtom = atom4(null, (get, set, edgeId) => {
      set(selectedEdgeIdAtom, edgeId);
      if (edgeId !== null) {
        set(selectedNodeIdsAtom, /* @__PURE__ */ new Set());
      }
    });
    clearEdgeSelectionAtom = atom4(null, (_get, set) => {
      set(selectedEdgeIdAtom, null);
    });
    focusedNodeIdAtom = atom4(null);
    setFocusedNodeAtom = atom4(null, (_get, set, nodeId) => {
      set(focusedNodeIdAtom, nodeId);
    });
    hasFocusedNodeAtom = atom4((get) => get(focusedNodeIdAtom) !== null);
    selectedNodesCountAtom = atom4((get) => get(selectedNodeIdsAtom).size);
    hasSelectionAtom = atom4((get) => get(selectedNodeIdsAtom).size > 0);
  }
});

// src/utils/mutation-queue.ts
function clearAllPendingMutations() {
  pendingNodeMutations.clear();
}
var pendingNodeMutations;
var init_mutation_queue = __esm({
  "src/utils/mutation-queue.ts"() {
    "use strict";
    pendingNodeMutations = /* @__PURE__ */ new Map();
  }
});

// src/core/perf.ts
import { atom as atom5 } from "jotai";
function setPerfEnabled(enabled) {
  _enabled = enabled;
}
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() {
}
function canvasWrap(name, fn) {
  const end = canvasMark(name);
  try {
    return fn();
  } finally {
    end();
  }
}
var perfEnabledAtom, _enabled;
var init_perf = __esm({
  "src/core/perf.ts"() {
    "use strict";
    perfEnabledAtom = atom5(false);
    _enabled = false;
    if (typeof window !== "undefined") {
      window.__canvasPerf = setPerfEnabled;
    }
  }
});

// src/core/graph-position.ts
import { atom as atom6 } from "jotai";
import { atomFamily } from "jotai-family";
import Graph2 from "graphology";
function getPositionCache(graph) {
  let cache = _positionCacheByGraph.get(graph);
  if (!cache) {
    cache = /* @__PURE__ */ new Map();
    _positionCacheByGraph.set(graph, cache);
  }
  return cache;
}
var debug3, _positionCacheByGraph, nodePositionUpdateCounterAtom, nodePositionAtomFamily, updateNodePositionAtom, cleanupNodePositionAtom, cleanupAllNodePositionsAtom, clearGraphOnSwitchAtom;
var init_graph_position = __esm({
  "src/core/graph-position.ts"() {
    "use strict";
    init_graph_store();
    init_debug();
    init_mutation_queue();
    init_perf();
    debug3 = createDebug("graph:position");
    _positionCacheByGraph = /* @__PURE__ */ new WeakMap();
    nodePositionUpdateCounterAtom = atom6(0);
    nodePositionAtomFamily = atomFamily((nodeId) => atom6((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;
    }));
    updateNodePositionAtom = atom6(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();
    });
    cleanupNodePositionAtom = atom6(null, (get, _set, nodeId) => {
      nodePositionAtomFamily.remove(nodeId);
      const graph = get(graphAtom);
      getPositionCache(graph).delete(nodeId);
      debug3("Removed position atom for node: %s", nodeId);
    });
    cleanupAllNodePositionsAtom = atom6(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);
    });
    clearGraphOnSwitchAtom = atom6(null, (get, set) => {
      debug3("Clearing graph for switch");
      set(cleanupAllNodePositionsAtom);
      clearAllPendingMutations();
      const emptyGraph = new Graph2(graphOptions);
      set(graphAtom, emptyGraph);
      set(graphUpdateVersionAtom, (v) => v + 1);
    });
  }
});

// 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
  };
}
var init_history_actions = __esm({
  "src/core/history-actions.ts"() {
    "use strict";
  }
});

// src/core/history-store.ts
var history_store_exports = {};
__export(history_store_exports, {
  applyDelta: () => applyDelta,
  canRedoAtom: () => canRedoAtom,
  canUndoAtom: () => canUndoAtom,
  clearHistoryAtom: () => clearHistoryAtom,
  createSnapshot: () => createSnapshot,
  historyLabelsAtom: () => historyLabelsAtom,
  historyStateAtom: () => historyStateAtom,
  invertDelta: () => invertDelta,
  pushDeltaAtom: () => pushDeltaAtom,
  pushHistoryAtom: () => pushHistoryAtom,
  redoAtom: () => redoAtom,
  redoCountAtom: () => redoCountAtom,
  undoAtom: () => undoAtom,
  undoCountAtom: () => undoCountAtom
});
import { atom as atom7 } from "jotai";
var debug4, MAX_HISTORY_SIZE, historyStateAtom, canUndoAtom, canRedoAtom, undoCountAtom, redoCountAtom, pushDeltaAtom, pushHistoryAtom, undoAtom, redoAtom, clearHistoryAtom, historyLabelsAtom;
var init_history_store = __esm({
  "src/core/history-store.ts"() {
    "use strict";
    init_graph_store();
    init_graph_position();
    init_debug();
    init_history_actions();
    init_history_actions();
    debug4 = createDebug("history");
    MAX_HISTORY_SIZE = 50;
    historyStateAtom = atom7({
      past: [],
      future: [],
      isApplying: false
    });
    canUndoAtom = atom7((get) => {
      const history = get(historyStateAtom);
      return history.past.length > 0 && !history.isApplying;
    });
    canRedoAtom = atom7((get) => {
      const history = get(historyStateAtom);
      return history.future.length > 0 && !history.isApplying;
    });
    undoCountAtom = atom7((get) => get(historyStateAtom).past.length);
    redoCountAtom = atom7((get) => get(historyStateAtom).future.length);
    pushDeltaAtom = atom7(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);
    });
    pushHistoryAtom = atom7(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);
    });
    undoAtom = atom7(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;
      }
    });
    redoAtom = atom7(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;
      }
    });
    clearHistoryAtom = atom7(null, (_get, set) => {
      set(historyStateAtom, {
        past: [],
        future: [],
        isApplying: false
      });
      debug4("History cleared");
    });
    historyLabelsAtom = atom7((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 group_store_exports = {};
__export(group_store_exports, {
  autoResizeGroupAtom: () => autoResizeGroupAtom,
  collapseGroupAtom: () => collapseGroupAtom,
  collapsedEdgeRemapAtom: () => collapsedEdgeRemapAtom,
  collapsedGroupsAtom: () => collapsedGroupsAtom,
  expandGroupAtom: () => expandGroupAtom,
  getNodeDescendants: () => getNodeDescendants,
  groupChildCountAtom: () => groupChildCountAtom,
  groupSelectedNodesAtom: () => groupSelectedNodesAtom,
  isGroupNodeAtom: () => isGroupNodeAtom,
  isNodeCollapsed: () => isNodeCollapsed,
  moveNodesToGroupAtom: () => moveNodesToGroupAtom,
  nestNodesOnDropAtom: () => nestNodesOnDropAtom,
  nodeChildrenAtom: () => nodeChildrenAtom,
  nodeParentAtom: () => nodeParentAtom,
  removeFromGroupAtom: () => removeFromGroupAtom,
  setNodeParentAtom: () => setNodeParentAtom,
  toggleGroupCollapseAtom: () => toggleGroupCollapseAtom,
  ungroupNodesAtom: () => ungroupNodesAtom
});
import { atom as atom8 } from "jotai";
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;
}
function isNodeCollapsed(nodeId, getParentId, collapsed) {
  let current = nodeId;
  while (true) {
    const parentId = getParentId(current);
    if (!parentId) return false;
    if (collapsed.has(parentId)) return true;
    current = parentId;
  }
}
var collapsedGroupsAtom, toggleGroupCollapseAtom, collapseGroupAtom, expandGroupAtom, nodeChildrenAtom, nodeParentAtom, isGroupNodeAtom, groupChildCountAtom, setNodeParentAtom, moveNodesToGroupAtom, removeFromGroupAtom, groupSelectedNodesAtom, ungroupNodesAtom, nestNodesOnDropAtom, collapsedEdgeRemapAtom, autoResizeGroupAtom;
var init_group_store = __esm({
  "src/core/group-store.ts"() {
    "use strict";
    init_graph_store();
    init_graph_position();
    init_history_store();
    collapsedGroupsAtom = atom8(/* @__PURE__ */ new Set());
    toggleGroupCollapseAtom = atom8(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);
    });
    collapseGroupAtom = atom8(null, (get, set, groupId) => {
      const current = get(collapsedGroupsAtom);
      if (!current.has(groupId)) {
        const next = new Set(current);
        next.add(groupId);
        set(collapsedGroupsAtom, next);
      }
    });
    expandGroupAtom = atom8(null, (get, set, groupId) => {
      const current = get(collapsedGroupsAtom);
      if (current.has(groupId)) {
        const next = new Set(current);
        next.delete(groupId);
        set(collapsedGroupsAtom, next);
      }
    });
    nodeChildrenAtom = atom8((get) => {
      get(graphUpdateVersionAtom);
      const graph = get(graphAtom);
      return (parentId) => {
        const children = [];
        graph.forEachNode((nodeId, attrs) => {
          if (attrs.parentId === parentId) {
            children.push(nodeId);
          }
        });
        return children;
      };
    });
    nodeParentAtom = atom8((get) => {
      get(graphUpdateVersionAtom);
      const graph = get(graphAtom);
      return (nodeId) => {
        if (!graph.hasNode(nodeId)) return void 0;
        return graph.getNodeAttribute(nodeId, "parentId");
      };
    });
    isGroupNodeAtom = atom8((get) => {
      const getChildren = get(nodeChildrenAtom);
      return (nodeId) => getChildren(nodeId).length > 0;
    });
    groupChildCountAtom = atom8((get) => {
      const getChildren = get(nodeChildrenAtom);
      return (groupId) => getChildren(groupId).length;
    });
    setNodeParentAtom = atom8(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);
    });
    moveNodesToGroupAtom = atom8(null, (get, set, {
      nodeIds,
      groupId
    }) => {
      for (const nodeId of nodeIds) {
        set(setNodeParentAtom, {
          nodeId,
          parentId: groupId
        });
      }
    });
    removeFromGroupAtom = atom8(null, (get, set, nodeId) => {
      set(setNodeParentAtom, {
        nodeId,
        parentId: void 0
      });
    });
    groupSelectedNodesAtom = atom8(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);
    });
    ungroupNodesAtom = atom8(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);
    });
    nestNodesOnDropAtom = atom8(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);
    });
    collapsedEdgeRemapAtom = atom8((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;
    });
    autoResizeGroupAtom = atom8(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
import { atom as atom9 } from "jotai";
import { atomFamily as atomFamily2 } from "jotai-family";
function getEdgeCache(graph) {
  let cache = _edgeCacheByGraph.get(graph);
  if (!cache) {
    cache = /* @__PURE__ */ new Map();
    _edgeCacheByGraph.set(graph, cache);
  }
  return cache;
}
var highestZIndexAtom, _prevUiNodesByGraph, uiNodesAtom, nodeKeysAtom, nodeFamilyAtom, edgeKeysAtom, edgeKeysWithTempEdgeAtom, _edgeCacheByGraph, edgeFamilyAtom;
var init_graph_derived = __esm({
  "src/core/graph-derived.ts"() {
    "use strict";
    init_graph_store();
    init_graph_position();
    init_viewport_store();
    init_group_store();
    highestZIndexAtom = atom9((get) => {
      get(graphUpdateVersionAtom);
      const graph = get(graphAtom);
      let maxZ = 0;
      graph.forEachNode((_node, attributes) => {
        if (attributes.zIndex > maxZ) {
          maxZ = attributes.zIndex;
        }
      });
      return maxZ;
    });
    _prevUiNodesByGraph = /* @__PURE__ */ new WeakMap();
    uiNodesAtom = atom9((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;
    });
    nodeKeysAtom = atom9((get) => {
      get(graphUpdateVersionAtom);
      const graph = get(graphAtom);
      return graph.nodes();
    });
    nodeFamilyAtom = atomFamily2((nodeId) => atom9((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);
    edgeKeysAtom = atom9((get) => {
      get(graphUpdateVersionAtom);
      const graph = get(graphAtom);
      return graph.edges();
    });
    edgeKeysWithTempEdgeAtom = atom9((get) => {
      const keys = get(edgeKeysAtom);
      const edgeCreation = get(edgeCreationAtom);
      if (edgeCreation.isCreating) {
        return [...keys, "temp-creating-edge"];
      }
      return keys;
    });
    _edgeCacheByGraph = /* @__PURE__ */ new WeakMap();
    edgeFamilyAtom = atomFamily2((key) => atom9((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/utils/layout.ts
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;
}
var FitToBoundsMode, calculateBounds;
var init_layout = __esm({
  "src/utils/layout.ts"() {
    "use strict";
    FitToBoundsMode = /* @__PURE__ */ (function(FitToBoundsMode2) {
      FitToBoundsMode2["Graph"] = "graph";
      FitToBoundsMode2["Selection"] = "selection";
      return FitToBoundsMode2;
    })({});
    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
      };
    };
  }
});

// src/core/viewport-store.ts
var viewport_store_exports = {};
__export(viewport_store_exports, {
  ZOOM_EXIT_THRESHOLD: () => ZOOM_EXIT_THRESHOLD,
  ZOOM_TRANSITION_THRESHOLD: () => ZOOM_TRANSITION_THRESHOLD,
  animateFitToBoundsAtom: () => animateFitToBoundsAtom,
  animateZoomToNodeAtom: () => animateZoomToNodeAtom,
  centerOnNodeAtom: () => centerOnNodeAtom,
  fitToBoundsAtom: () => fitToBoundsAtom,
  isZoomTransitioningAtom: () => isZoomTransitioningAtom,
  panAtom: () => panAtom,
  resetViewportAtom: () => resetViewportAtom,
  screenToWorldAtom: () => screenToWorldAtom,
  setZoomAtom: () => setZoomAtom,
  viewportRectAtom: () => viewportRectAtom,
  worldToScreenAtom: () => worldToScreenAtom,
  zoomAnimationTargetAtom: () => zoomAnimationTargetAtom,
  zoomAtom: () => zoomAtom,
  zoomFocusNodeIdAtom: () => zoomFocusNodeIdAtom,
  zoomTransitionProgressAtom: () => zoomTransitionProgressAtom
});
import { atom as atom10 } from "jotai";
var zoomAtom, panAtom, viewportRectAtom, screenToWorldAtom, worldToScreenAtom, setZoomAtom, resetViewportAtom, fitToBoundsAtom, centerOnNodeAtom, ZOOM_TRANSITION_THRESHOLD, ZOOM_EXIT_THRESHOLD, zoomFocusNodeIdAtom, zoomTransitionProgressAtom, isZoomTransitioningAtom, zoomAnimationTargetAtom, animateZoomToNodeAtom, animateFitToBoundsAtom;
var init_viewport_store = __esm({
  "src/core/viewport-store.ts"() {
    "use strict";
    init_graph_store();
    init_graph_position();
    init_graph_derived();
    init_selection_store();
    init_layout();
    zoomAtom = atom10(1);
    panAtom = atom10({
      x: 0,
      y: 0
    });
    viewportRectAtom = atom10(null);
    screenToWorldAtom = atom10((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
        };
      };
    });
    worldToScreenAtom = atom10((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
        };
      };
    });
    setZoomAtom = atom10(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);
    });
    resetViewportAtom = atom10(null, (_get, set) => {
      set(zoomAtom, 1);
      set(panAtom, {
        x: 0,
        y: 0
      });
    });
    fitToBoundsAtom = atom10(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
      });
    });
    centerOnNodeAtom = atom10(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
      });
    });
    ZOOM_TRANSITION_THRESHOLD = 3.5;
    ZOOM_EXIT_THRESHOLD = 2;
    zoomFocusNodeIdAtom = atom10(null);
    zoomTransitionProgressAtom = atom10(0);
    isZoomTransitioningAtom = atom10((get) => {
      const progress = get(zoomTransitionProgressAtom);
      return progress > 0 && progress < 1;
    });
    zoomAnimationTargetAtom = atom10(null);
    animateZoomToNodeAtom = atom10(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()
      });
    });
    animateFitToBoundsAtom = atom10(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/reduced-motion-store.ts
import { atom as atom11 } from "jotai";
var prefersReducedMotionAtom, watchReducedMotionAtom;
var init_reduced_motion_store = __esm({
  "src/core/reduced-motion-store.ts"() {
    "use strict";
    prefersReducedMotionAtom = atom11(typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)").matches : false);
    watchReducedMotionAtom = atom11(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/types.ts
var init_types = __esm({
  "src/core/types.ts"() {
    "use strict";
  }
});

// src/core/graph-mutations-edges.ts
import { atom as atom12 } from "jotai";
var debug7, addEdgeToLocalGraphAtom, removeEdgeFromLocalGraphAtom, swapEdgeAtomicAtom, departingEdgesAtom, EDGE_ANIMATION_DURATION, removeEdgeWithAnimationAtom, editingEdgeLabelAtom, updateEdgeLabelAtom;
var init_graph_mutations_edges = __esm({
  "src/core/graph-mutations-edges.ts"() {
    "use strict";
    init_graph_store();
    init_graph_position();
    init_graph_derived();
    init_debug();
    init_reduced_motion_store();
    debug7 = createDebug("graph:mutations:edges");
    addEdgeToLocalGraphAtom = atom12(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 {
            debug7("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) {
            debug7("Failed to add edge %s: %o", newEdge.id, e);
          }
        }
      }
    });
    removeEdgeFromLocalGraphAtom = atom12(null, (get, set, edgeId) => {
      const graph = get(graphAtom);
      if (graph.hasEdge(edgeId)) {
        graph.dropEdge(edgeId);
        set(graphAtom, graph.copy());
        set(graphUpdateVersionAtom, (v) => v + 1);
      }
    });
    swapEdgeAtomicAtom = atom12(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 {
            debug7("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) {
            debug7("Failed to add edge %s: %o", newEdge.id, e);
          }
        }
      }
      set(graphAtom, graph.copy());
      set(graphUpdateVersionAtom, (v) => v + 1);
    });
    departingEdgesAtom = atom12(/* @__PURE__ */ new Map());
    EDGE_ANIMATION_DURATION = 300;
    removeEdgeWithAnimationAtom = atom12(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);
      }
    });
    editingEdgeLabelAtom = atom12(null);
    updateEdgeLabelAtom = atom12(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
import { atom as atom13 } from "jotai";
var debug8, dropTargetNodeIdAtom, splitNodeAtom, mergeNodesAtom;
var init_graph_mutations_advanced = __esm({
  "src/core/graph-mutations-advanced.ts"() {
    "use strict";
    init_graph_store();
    init_graph_position();
    init_history_store();
    init_debug();
    init_graph_mutations_edges();
    init_graph_mutations();
    debug8 = createDebug("graph:mutations:advanced");
    dropTargetNodeIdAtom = atom13(null);
    splitNodeAtom = atom13(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);
      debug8("Split node %s \u2192 clone %s", nodeId, cloneId);
    });
    mergeNodesAtom = atom13(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);
      debug8("Merged nodes %o \u2192 survivor %s", nodeIds, survivorId);
    });
  }
});

// src/core/graph-mutations.ts
var graph_mutations_exports = {};
__export(graph_mutations_exports, {
  EDGE_ANIMATION_DURATION: () => EDGE_ANIMATION_DURATION,
  addNodeToLocalGraphAtom: () => addNodeToLocalGraphAtom,
  departingEdgesAtom: () => departingEdgesAtom,
  dropTargetNodeIdAtom: () => dropTargetNodeIdAtom,
  editingEdgeLabelAtom: () => editingEdgeLabelAtom,
  endNodeDragAtom: () => endNodeDragAtom,
  loadGraphFromDbAtom: () => loadGraphFromDbAtom,
  mergeNodesAtom: () => mergeNodesAtom,
  optimisticDeleteEdgeAtom: () => optimisticDeleteEdgeAtom,
  optimisticDeleteNodeAtom: () => optimisticDeleteNodeAtom,
  removeEdgeWithAnimationAtom: () => removeEdgeWithAnimationAtom,
  splitNodeAtom: () => splitNodeAtom,
  startNodeDragAtom: () => startNodeDragAtom,
  swapEdgeAtomicAtom: () => swapEdgeAtomicAtom,
  updateEdgeLabelAtom: () => updateEdgeLabelAtom
});
import { atom as atom14 } from "jotai";
import Graph3 from "graphology";
var debug9, startNodeDragAtom, endNodeDragAtom, optimisticDeleteNodeAtom, optimisticDeleteEdgeAtom, addNodeToLocalGraphAtom, loadGraphFromDbAtom;
var init_graph_mutations = __esm({
  "src/core/graph-mutations.ts"() {
    "use strict";
    init_graph_store();
    init_graph_position();
    init_graph_derived();
    init_group_store();
    init_debug();
    init_graph_mutations_edges();
    init_graph_mutations_advanced();
    debug9 = createDebug("graph:mutations");
    startNodeDragAtom = atom14(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);
    });
    endNodeDragAtom = atom14(null, (get, set, _payload) => {
      const currentDraggingId = get(draggingNodeIdAtom);
      if (currentDraggingId) {
        debug9("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);
    });
    optimisticDeleteNodeAtom = atom14(null, (get, set, {
      nodeId
    }) => {
      const graph = get(graphAtom);
      if (graph.hasNode(nodeId)) {
        graph.dropNode(nodeId);
        set(cleanupNodePositionAtom, nodeId);
        set(graphAtom, graph.copy());
        debug9("Optimistically deleted node %s", nodeId);
      }
    });
    optimisticDeleteEdgeAtom = atom14(null, (get, set, {
      edgeKey
    }) => {
      const graph = get(graphAtom);
      if (graph.hasEdge(edgeKey)) {
        graph.dropEdge(edgeKey);
        set(graphAtom, graph.copy());
        debug9("Optimistically deleted edge %s", edgeKey);
      }
    });
    addNodeToLocalGraphAtom = atom14(null, (get, set, newNode) => {
      const graph = get(graphAtom);
      if (graph.hasNode(newNode.id)) {
        debug9("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
      };
      debug9("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);
    });
    loadGraphFromDbAtom = atom14(null, (get, set, fetchedNodes, fetchedEdges) => {
      debug9("========== START SYNC ==========");
      debug9("Fetched nodes: %d, edges: %d", fetchedNodes.length, fetchedEdges.length);
      const currentGraphId = get(currentGraphIdAtom);
      if (fetchedNodes.length > 0 && fetchedNodes[0].graph_id !== currentGraphId) {
        debug9("Skipping sync - data belongs to different graph");
        return;
      }
      const existingGraph = get(graphAtom);
      const isDragging = get(draggingNodeIdAtom) !== null;
      if (isDragging) {
        debug9("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) {
        debug9("Merging DB data into existing graph");
        graph = existingGraph.copy();
      } else {
        debug9("Creating fresh graph (graph switch detected)");
        graph = new Graph3(graphOptions);
      }
      const fetchedEdgeIds = new Set(fetchedEdges.map((e) => e.id));
      if (hasAnyCommonNodes && existingNodeIds.size > 0) {
        graph.forEachNode((nodeId) => {
          if (!fetchedNodeIds.has(nodeId)) {
            debug9("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)) {
          debug9("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) {
              debug9("Failed to add edge %s: %o", edge.id, e);
            }
          }
        }
      });
      set(graphAtom, graph);
      set(graphUpdateVersionAtom, (v) => v + 1);
      debug9("========== SYNC COMPLETE ==========");
      debug9("Final graph: %d nodes, %d edges", graph.order, graph.size);
    });
  }
});

// src/core/sync-store.ts
import { atom as atom15 } from "jotai";
var debug10, syncStatusAtom, pendingMutationsCountAtom, isOnlineAtom, lastSyncErrorAtom, lastSyncTimeAtom, mutationQueueAtom, syncStateAtom, startMutationAtom, completeMutationAtom, trackMutationErrorAtom, setOnlineStatusAtom, queueMutationAtom, dequeueMutationAtom, incrementRetryCountAtom, getNextQueuedMutationAtom, clearMutationQueueAtom;
var init_sync_store = __esm({
  "src/core/sync-store.ts"() {
    "use strict";
    init_debug();
    debug10 = createDebug("sync");
    syncStatusAtom = atom15("synced");
    pendingMutationsCountAtom = atom15(0);
    isOnlineAtom = atom15(typeof navigator !== "undefined" ? navigator.onLine : true);
    lastSyncErrorAtom = atom15(null);
    lastSyncTimeAtom = atom15(Date.now());
    mutationQueueAtom = atom15([]);
    syncStateAtom = atom15((get) => ({
      status: get(syncStatusAtom),
      pendingMutations: get(pendingMutationsCountAtom),
      lastError: get(lastSyncErrorAtom),
      lastSyncTime: get(lastSyncTimeAtom),
      isOnline: get(isOnlineAtom),
      queuedMutations: get(mutationQueueAtom).length
    }));
    startMutationAtom = atom15(null, (get, set) => {
      const currentCount = get(pendingMutationsCountAtom);
      const newCount = currentCount + 1;
      set(pendingMutationsCountAtom, newCount);
      debug10("Mutation started. Pending count: %d -> %d", currentCount, newCount);
      if (newCount > 0 && get(syncStatusAtom) !== "syncing") {
        set(syncStatusAtom, "syncing");
        debug10("Status -> syncing");
      }
    });
    completeMutationAtom = atom15(null, (get, set, success = true) => {
      const currentCount = get(pendingMutationsCountAtom);
      const newCount = Math.max(0, currentCount - 1);
      set(pendingMutationsCountAtom, newCount);
      debug10("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");
          debug10("Status -> error");
        } else if (!isOnline) {
          set(syncStatusAtom, "offline");
          debug10("Status -> offline");
        } else {
          set(syncStatusAtom, "synced");
          debug10("Status -> synced");
        }
      }
    });
    trackMutationErrorAtom = atom15(null, (_get, set, error) => {
      set(lastSyncErrorAtom, error);
      debug10("Mutation failed: %s", error);
    });
    setOnlineStatusAtom = atom15(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");
        }
      }
    });
    queueMutationAtom = atom15(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);
      debug10("Queued mutation: %s. Queue size: %d", mutation.type, newQueue.length);
      if (get(pendingMutationsCountAtom) === 0) {
        set(syncStatusAtom, "error");
      }
      return newMutation.id;
    });
    dequeueMutationAtom = atom15(null, (get, set, mutationId) => {
      const queue = get(mutationQueueAtom);
      const newQueue = queue.filter((m) => m.id !== mutationId);
      set(mutationQueueAtom, newQueue);
      debug10("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");
      }
    });
    incrementRetryCountAtom = atom15(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);
    });
    getNextQueuedMutationAtom = atom15((get) => {
      const queue = get(mutationQueueAtom);
      return queue.find((m) => m.retryCount < m.maxRetries) ?? null;
    });
    clearMutationQueueAtom = atom15(null, (get, set) => {
      set(mutationQueueAtom, []);
      debug10("Cleared mutation queue");
      if (get(pendingMutationsCountAtom) === 0 && get(lastSyncErrorAtom) === null) {
        set(syncStatusAtom, get(isOnlineAtom) ? "synced" : "offline");
      }
    });
  }
});

// src/core/interaction-store.ts
import { atom as atom16 } from "jotai";
var inputModeAtom2, keyboardInteractionModeAtom, interactionFeedbackAtom, pendingInputResolverAtom2, resetInputModeAtom, resetKeyboardInteractionModeAtom, setKeyboardInteractionModeAtom, startPickNodeAtom, startPickNodesAtom, startPickPointAtom, provideInputAtom2, updateInteractionFeedbackAtom, isPickingModeAtom, isPickNodeModeAtom;
var init_interaction_store = __esm({
  "src/core/interaction-store.ts"() {
    "use strict";
    inputModeAtom2 = atom16({
      type: "normal"
    });
    keyboardInteractionModeAtom = atom16("navigate");
    interactionFeedbackAtom = atom16(null);
    pendingInputResolverAtom2 = atom16(null);
    resetInputModeAtom = atom16(null, (_get, set) => {
      set(inputModeAtom2, {
        type: "normal"
      });
      set(interactionFeedbackAtom, null);
      set(pendingInputResolverAtom2, null);
    });
    resetKeyboardInteractionModeAtom = atom16(null, (_get, set) => {
      set(keyboardInteractionModeAtom, "navigate");
    });
    setKeyboardInteractionModeAtom = atom16(null, (_get, set, mode) => {
      set(keyboardInteractionModeAtom, mode);
    });
    startPickNodeAtom = atom16(null, (_get, set, options) => {
      set(inputModeAtom2, {
        type: "pickNode",
        ...options
      });
    });
    startPickNodesAtom = atom16(null, (_get, set, options) => {
      set(inputModeAtom2, {
        type: "pickNodes",
        ...options
      });
    });
    startPickPointAtom = atom16(null, (_get, set, options) => {
      set(inputModeAtom2, {
        type: "pickPoint",
        ...options
      });
    });
    provideInputAtom2 = atom16(null, (get, set, value) => {
      set(pendingInputResolverAtom2, value);
    });
    updateInteractionFeedbackAtom = atom16(null, (get, set, feedback) => {
      const current = get(interactionFeedbackAtom);
      set(interactionFeedbackAtom, {
        ...current,
        ...feedback
      });
    });
    isPickingModeAtom = atom16((get) => {
      const mode = get(inputModeAtom2);
      return mode.type !== "normal";
    });
    isPickNodeModeAtom = atom16((get) => {
      const mode = get(inputModeAtom2);
      return mode.type === "pickNode" || mode.type === "pickNodes";
    });
  }
});

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

// src/core/node-type-registry.tsx
import { c as _c3 } from "react/compiler-runtime";
import React2 from "react";
import { jsxs as _jsxs, jsx as _jsx2 } from "react/jsx-runtime";
function registerNodeType(nodeType, component) {
  nodeTypeRegistry.set(nodeType, component);
}
function registerNodeTypes(types) {
  for (const [nodeType, component] of Object.entries(types)) {
    nodeTypeRegistry.set(nodeType, component);
  }
}
function unregisterNodeType(nodeType) {
  return nodeTypeRegistry.delete(nodeType);
}
function getNodeTypeComponent(nodeType) {
  if (!nodeType) return void 0;
  return nodeTypeRegistry.get(nodeType);
}
function hasNodeTypeComponent(nodeType) {
  if (!nodeType) return false;
  return nodeTypeRegistry.has(nodeType);
}
function getRegisteredNodeTypes() {
  return Array.from(nodeTypeRegistry.keys());
}
function clearNodeTypeRegistry() {
  nodeTypeRegistry.clear();
}
var nodeTypeRegistry, FallbackNodeTypeComponent;
var init_node_type_registry = __esm({
  "src/core/node-type-registry.tsx"() {
    "use strict";
    nodeTypeRegistry = /* @__PURE__ */ new Map();
    FallbackNodeTypeComponent = (t0) => {
      const $ = _c3(11);
      const {
        nodeData
      } = t0;
      let t1;
      if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
        t1 = {
          padding: "12px",
          display: "flex",
          flexDirection: "column",
          alignItems: "center",
          justifyContent: "center",
          height: "100%",
          color: "#666",
          fontSize: "12px"
        };
        $[0] = t1;
      } else {
        t1 = $[0];
      }
      const t2 = nodeData.dbData.node_type || "none";
      let t3;
      if ($[1] !== t2) {
        t3 = /* @__PURE__ */ _jsxs("div", {
          children: ["Unknown type: ", t2]
        });
        $[1] = t2;
        $[2] = t3;
      } else {
        t3 = $[2];
      }
      let t4;
      if ($[3] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
        t4 = {
          marginTop: "4px",
          opacity: 0.7
        };
        $[3] = t4;
      } else {
        t4 = $[3];
      }
      let t5;
      if ($[4] !== nodeData.id) {
        t5 = nodeData.id.substring(0, 8);
        $[4] = nodeData.id;
        $[5] = t5;
      } else {
        t5 = $[5];
      }
      let t6;
      if ($[6] !== t5) {
        t6 = /* @__PURE__ */ _jsx2("div", {
          style: t4,
          children: t5
        });
        $[6] = t5;
        $[7] = t6;
      } else {
        t6 = $[7];
      }
      let t7;
      if ($[8] !== t3 || $[9] !== t6) {
        t7 = /* @__PURE__ */ _jsxs("div", {
          style: t1,
          children: [t3, t6]
        });
        $[8] = t3;
        $[9] = t6;
        $[10] = t7;
      } else {
        t7 = $[10];
      }
      return t7;
    };
  }
});

// src/core/toast-store.ts
var toast_store_exports = {};
__export(toast_store_exports, {
  canvasToastAtom: () => canvasToastAtom,
  showToastAtom: () => showToastAtom
});
import { atom as atom18 } from "jotai";
var canvasToastAtom, showToastAtom;
var init_toast_store = __esm({
  "src/core/toast-store.ts"() {
    "use strict";
    canvasToastAtom = atom18(null);
    showToastAtom = atom18(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/core/snap-store.ts
import { atom as atom19 } from "jotai";
function snapToGrid(pos, gridSize) {
  return {
    x: Math.round(pos.x / gridSize) * gridSize,
    y: Math.round(pos.y / gridSize) * gridSize
  };
}
function conditionalSnap(pos, gridSize, isActive) {
  return isActive ? snapToGrid(pos, gridSize) : pos;
}
function getSnapGuides(pos, gridSize, tolerance = 5) {
  const snappedX = Math.round(pos.x / gridSize) * gridSize;
  const snappedY = Math.round(pos.y / gridSize) * gridSize;
  return {
    x: Math.abs(pos.x - snappedX) < tolerance ? snappedX : null,
    y: Math.abs(pos.y - snappedY) < tolerance ? snappedY : null
  };
}
function findAlignmentGuides(dragged, others, tolerance = 5) {
  const verticals = /* @__PURE__ */ new Set();
  const horizontals = /* @__PURE__ */ new Set();
  const dragCX = dragged.x + dragged.width / 2;
  const dragCY = dragged.y + dragged.height / 2;
  const dragRight = dragged.x + dragged.width;
  const dragBottom = dragged.y + dragged.height;
  for (const other of others) {
    const otherCX = other.x + other.width / 2;
    const otherCY = other.y + other.height / 2;
    const otherRight = other.x + other.width;
    const otherBottom = other.y + other.height;
    if (Math.abs(dragCX - otherCX) < tolerance) verticals.add(otherCX);
    if (Math.abs(dragged.x - other.x) < tolerance) verticals.add(other.x);
    if (Math.abs(dragRight - otherRight) < tolerance) verticals.add(otherRight);
    if (Math.abs(dragged.x - otherRight) < tolerance) verticals.add(otherRight);
    if (Math.abs(dragRight - other.x) < tolerance) verticals.add(other.x);
    if (Math.abs(dragCX - other.x) < tolerance) verticals.add(other.x);
    if (Math.abs(dragCX - otherRight) < tolerance) verticals.add(otherRight);
    if (Math.abs(dragCY - otherCY) < tolerance) horizontals.add(otherCY);
    if (Math.abs(dragged.y - other.y) < tolerance) horizontals.add(other.y);
    if (Math.abs(dragBottom - otherBottom) < tolerance) horizontals.add(otherBottom);
    if (Math.abs(dragged.y - otherBottom) < tolerance) horizontals.add(otherBottom);
    if (Math.abs(dragBottom - other.y) < tolerance) horizontals.add(other.y);
    if (Math.abs(dragCY - other.y) < tolerance) horizontals.add(other.y);
    if (Math.abs(dragCY - otherBottom) < tolerance) horizontals.add(otherBottom);
  }
  return {
    verticalGuides: Array.from(verticals),
    horizontalGuides: Array.from(horizontals)
  };
}
var snapEnabledAtom, snapGridSizeAtom, snapTemporaryDisableAtom, isSnappingActiveAtom, toggleSnapAtom, setGridSizeAtom, snapAlignmentEnabledAtom, toggleAlignmentGuidesAtom, alignmentGuidesAtom, clearAlignmentGuidesAtom;
var init_snap_store = __esm({
  "src/core/snap-store.ts"() {
    "use strict";
    snapEnabledAtom = atom19(false);
    snapGridSizeAtom = atom19(20);
    snapTemporaryDisableAtom = atom19(false);
    isSnappingActiveAtom = atom19((get) => {
      return get(snapEnabledAtom) && !get(snapTemporaryDisableAtom);
    });
    toggleSnapAtom = atom19(null, (get, set) => {
      set(snapEnabledAtom, !get(snapEnabledAtom));
    });
    setGridSizeAtom = atom19(null, (_get, set, size) => {
      set(snapGridSizeAtom, Math.max(5, Math.min(200, size)));
    });
    snapAlignmentEnabledAtom = atom19(true);
    toggleAlignmentGuidesAtom = atom19(null, (get, set) => {
      set(snapAlignmentEnabledAtom, !get(snapAlignmentEnabledAtom));
    });
    alignmentGuidesAtom = atom19({
      verticalGuides: [],
      horizontalGuides: []
    });
    clearAlignmentGuidesAtom = atom19(null, (_get, set) => {
      set(alignmentGuidesAtom, {
        verticalGuides: [],
        horizontalGuides: []
      });
    });
  }
});

// src/core/event-types.ts
var CanvasEventType, EVENT_TYPE_INFO;
var init_event_types = __esm({
  "src/core/event-types.ts"() {
    "use strict";
    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;
    })({});
    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, BuiltInActionId;
var init_action_types = __esm({
  "src/core/action-types.ts"() {
    "use strict";
    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;
    })({});
    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;
var init_settings_state_types = __esm({
  "src/core/settings-state-types.ts"() {
    "use strict";
    init_event_types();
    init_action_types();
    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-types.ts
var init_settings_types = __esm({
  "src/core/settings-types.ts"() {
    "use strict";
    init_event_types();
    init_action_types();
    init_settings_state_types();
  }
});

// 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());
      }
    }
  });
}
var init_actions_node = __esm({
  "src/core/actions-node.ts"() {
    "use strict";
    init_settings_types();
    init_action_registry();
  }
});

// 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();
    }
  });
}
var init_actions_viewport = __esm({
  "src/core/actions-viewport.ts"() {
    "use strict";
    init_settings_types();
    init_action_registry();
  }
});

// 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();
}
var init_built_in_actions = __esm({
  "src/core/built-in-actions.ts"() {
    "use strict";
    init_settings_types();
    init_action_registry();
    init_actions_node();
    init_actions_viewport();
  }
});

// src/core/action-registry.ts
function registerAction(action) {
  actionRegistry.set(action.id, action);
}
function getAction(id) {
  return actionRegistry.get(id);
}
function hasAction(id) {
  return actionRegistry.has(id);
}
function getAllActions() {
  return Array.from(actionRegistry.values());
}
function getActionsByCategory(category) {
  return getAllActions().filter((action) => action.category === category);
}
function unregisterAction(id) {
  return actionRegistry.delete(id);
}
function clearActions() {
  actionRegistry.clear();
}
function getActionsByCategories() {
  const categoryLabels = {
    [ActionCategory.None]: "None",
    [ActionCategory.Selection]: "Selection",
    [ActionCategory.Viewport]: "Viewport",
    [ActionCategory.Node]: "Node",
    [ActionCategory.Layout]: "Layout",
    [ActionCategory.History]: "History",
    [ActionCategory.Custom]: "Custom"
  };
  const categoryOrder = [ActionCategory.None, ActionCategory.Selection, ActionCategory.Viewport, ActionCategory.Node, ActionCategory.Layout, ActionCategory.History, ActionCategory.Custom];
  return categoryOrder.map((category) => ({
    category,
    label: categoryLabels[category],
    actions: getActionsByCategory(category)
  })).filter((group) => group.actions.length > 0);
}
var actionRegistry;
var init_action_registry = __esm({
  "src/core/action-registry.ts"() {
    "use strict";
    init_settings_types();
    init_built_in_actions();
    actionRegistry = /* @__PURE__ */ new Map();
    registerBuiltInActions();
  }
});

// src/core/action-executor.ts
async function executeAction(actionId, context, helpers) {
  if (actionId === BuiltInActionId.None) {
    return {
      success: true,
      actionId
    };
  }
  const action = getAction(actionId);
  if (!action) {
    debug11.warn("Action not found: %s", actionId);
    return {
      success: false,
      actionId,
      error: new Error(`Action not found: ${actionId}`)
    };
  }
  if (action.requiresNode && !context.nodeId) {
    debug11.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) {
    debug11.error("Error executing action %s: %O", actionId, error);
    return {
      success: false,
      actionId,
      error: error instanceof Error ? error : new Error(String(error))
    };
  }
}
function createActionContext(eventType, screenEvent, worldPosition, options) {
  return {
    eventType,
    nodeId: options?.nodeId,
    nodeData: options?.nodeData,
    edgeId: options?.edgeId,
    edgeData: options?.edgeData,
    worldPosition,
    screenPosition: {
      x: screenEvent.clientX,
      y: screenEvent.clientY
    },
    modifiers: {
      shift: false,
      ctrl: false,
      alt: false,
      meta: false
    }
  };
}
function createActionContextFromReactEvent(eventType, event, worldPosition, options) {
  return {
    eventType,
    nodeId: options?.nodeId,
    nodeData: options?.nodeData,
    edgeId: options?.edgeId,
    edgeData: options?.edgeData,
    worldPosition,
    screenPosition: {
      x: event.clientX,
      y: event.clientY
    },
    modifiers: {
      shift: event.shiftKey,
      ctrl: event.ctrlKey,
      alt: event.altKey,
      meta: event.metaKey
    }
  };
}
function createActionContextFromTouchEvent(eventType, touch, worldPosition, options) {
  return {
    eventType,
    nodeId: options?.nodeId,
    nodeData: options?.nodeData,
    edgeId: options?.edgeId,
    edgeData: options?.edgeData,
    worldPosition,
    screenPosition: {
      x: touch.clientX,
      y: touch.clientY
    },
    modifiers: {
      shift: false,
      ctrl: false,
      alt: false,
      meta: false
    }
  };
}
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 {
        debug11.warn("deleteNode called but onDeleteNode callback not provided");
      }
    },
    isNodeLocked: (nodeId) => store.get(lockedNodeIdAtom) === nodeId,
    applyForceLayout: async () => {
      if (options.onApplyForceLayout) {
        await options.onApplyForceLayout();
      } else {
        debug11.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
  };
}
var debug11;
var init_action_executor = __esm({
  "src/core/action-executor.ts"() {
    "use strict";
    init_action_registry();
    init_settings_types();
    init_selection_store();
    init_viewport_store();
    init_locked_node_store();
    init_history_store();
    init_layout();
    init_debug();
    debug11 = createDebug("actions");
  }
});

// src/core/settings-presets.ts
function getActionForEvent(mappings, event) {
  return mappings[event] || BuiltInActionId.None;
}
var BUILT_IN_PRESETS;
var init_settings_presets = __esm({
  "src/core/settings-presets.ts"() {
    "use strict";
    init_settings_types();
    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
      }
    }];
  }
});

// src/core/settings-store.ts
import { atom as atom20 } from "jotai";
import { atomWithStorage as atomWithStorage2 } from "jotai/utils";
var debug12, DEFAULT_STATE, canvasSettingsAtom, eventMappingsAtom, activePresetIdAtom, allPresetsAtom, activePresetAtom, isPanelOpenAtom, virtualizationEnabledAtom, hasUnsavedChangesAtom, setEventMappingAtom, applyPresetAtom, saveAsPresetAtom, updatePresetAtom, deletePresetAtom, resetSettingsAtom, togglePanelAtom, setPanelOpenAtom, setVirtualizationEnabledAtom, toggleVirtualizationAtom;
var init_settings_store = __esm({
  "src/core/settings-store.ts"() {
    "use strict";
    init_settings_types();
    init_debug();
    init_settings_presets();
    init_settings_presets();
    debug12 = createDebug("settings");
    DEFAULT_STATE = {
      mappings: DEFAULT_MAPPINGS,
      activePresetId: "default",
      customPresets: [],
      isPanelOpen: false,
      virtualizationEnabled: true
    };
    canvasSettingsAtom = atomWithStorage2("@blinksgg/canvas/settings", DEFAULT_STATE);
    eventMappingsAtom = atom20((get) => get(canvasSettingsAtom).mappings);
    activePresetIdAtom = atom20((get) => get(canvasSettingsAtom).activePresetId);
    allPresetsAtom = atom20((get) => {
      const state = get(canvasSettingsAtom);
      return [...BUILT_IN_PRESETS, ...state.customPresets];
    });
    activePresetAtom = atom20((get) => {
      const presetId = get(activePresetIdAtom);
      if (!presetId) return null;
      const allPresets = get(allPresetsAtom);
      return allPresets.find((p) => p.id === presetId) || null;
    });
    isPanelOpenAtom = atom20((get) => get(canvasSettingsAtom).isPanelOpen);
    virtualizationEnabledAtom = atom20((get) => get(canvasSettingsAtom).virtualizationEnabled ?? true);
    hasUnsavedChangesAtom = atom20((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]);
    });
    setEventMappingAtom = atom20(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
      });
    });
    applyPresetAtom = atom20(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
      });
    });
    saveAsPresetAtom = atom20(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;
    });
    updatePresetAtom = atom20(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
      });
    });
    deletePresetAtom = atom20(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
      });
    });
    resetSettingsAtom = atom20(null, (get, set) => {
      const current = get(canvasSettingsAtom);
      set(canvasSettingsAtom, {
        ...current,
        mappings: DEFAULT_MAPPINGS,
        activePresetId: "default"
      });
    });
    togglePanelAtom = atom20(null, (get, set) => {
      const current = get(canvasSettingsAtom);
      set(canvasSettingsAtom, {
        ...current,
        isPanelOpen: !current.isPanelOpen
      });
    });
    setPanelOpenAtom = atom20(null, (get, set, isOpen) => {
      const current = get(canvasSettingsAtom);
      set(canvasSettingsAtom, {
        ...current,
        isPanelOpen: isOpen
      });
    });
    setVirtualizationEnabledAtom = atom20(null, (get, set, enabled) => {
      const current = get(canvasSettingsAtom);
      set(canvasSettingsAtom, {
        ...current,
        virtualizationEnabled: enabled
      });
    });
    toggleVirtualizationAtom = atom20(null, (get, set) => {
      const current = get(canvasSettingsAtom);
      set(canvasSettingsAtom, {
        ...current,
        virtualizationEnabled: !(current.virtualizationEnabled ?? true)
      });
    });
  }
});

// src/core/canvas-serializer.ts
var canvas_serializer_exports = {};
__export(canvas_serializer_exports, {
  SNAPSHOT_VERSION: () => SNAPSHOT_VERSION,
  exportGraph: () => exportGraph,
  importGraph: () => importGraph,
  validateSnapshot: () => validateSnapshot
});
import Graph4 from "graphology";
function exportGraph(store, metadata) {
  const graph = store.get(graphAtom);
  const zoom = store.get(zoomAtom);
  const pan = store.get(panAtom);
  const collapsed = store.get(collapsedGroupsAtom);
  const nodes = [];
  const groups = [];
  const seenGroupParents = /* @__PURE__ */ new Set();
  graph.forEachNode((nodeId, attrs) => {
    const a = attrs;
    nodes.push({
      id: nodeId,
      position: {
        x: a.x,
        y: a.y
      },
      dimensions: {
        width: a.width,
        height: a.height
      },
      size: a.size,
      color: a.color,
      zIndex: a.zIndex,
      label: a.label,
      parentId: a.parentId,
      dbData: a.dbData
    });
    if (a.parentId) {
      const key = `${nodeId}:${a.parentId}`;
      if (!seenGroupParents.has(key)) {
        seenGroupParents.add(key);
        groups.push({
          nodeId,
          parentId: a.parentId,
          isCollapsed: collapsed.has(a.parentId)
        });
      }
    }
  });
  const edges = [];
  graph.forEachEdge((key, attrs, source, target) => {
    const a = attrs;
    edges.push({
      key,
      sourceId: source,
      targetId: target,
      attributes: {
        weight: a.weight,
        type: a.type,
        color: a.color,
        label: a.label
      },
      dbData: a.dbData
    });
  });
  return {
    version: SNAPSHOT_VERSION,
    exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
    nodes,
    edges,
    groups,
    viewport: {
      zoom,
      pan: {
        ...pan
      }
    },
    metadata
  };
}
function importGraph(store, snapshot, options = {}) {
  const {
    clearExisting = true,
    offsetPosition,
    remapIds = false
  } = options;
  const idMap = /* @__PURE__ */ new Map();
  if (remapIds) {
    for (const node of snapshot.nodes) {
      idMap.set(node.id, crypto.randomUUID());
    }
    for (const edge of snapshot.edges) {
      idMap.set(edge.key, crypto.randomUUID());
    }
  }
  const remap = (id) => idMap.get(id) ?? id;
  let graph;
  if (clearExisting) {
    graph = new Graph4(graphOptions);
  } else {
    graph = store.get(graphAtom);
  }
  const ox = offsetPosition?.x ?? 0;
  const oy = offsetPosition?.y ?? 0;
  for (const node of snapshot.nodes) {
    const nodeId = remap(node.id);
    const parentId = node.parentId ? remap(node.parentId) : void 0;
    const dbData = remapIds ? {
      ...node.dbData,
      id: nodeId
    } : node.dbData;
    const attrs = {
      x: node.position.x + ox,
      y: node.position.y + oy,
      width: node.dimensions.width,
      height: node.dimensions.height,
      size: node.size,
      color: node.color,
      zIndex: node.zIndex,
      label: node.label,
      parentId,
      dbData
    };
    graph.addNode(nodeId, attrs);
  }
  for (const edge of snapshot.edges) {
    const edgeKey = remap(edge.key);
    const sourceId = remap(edge.sourceId);
    const targetId = remap(edge.targetId);
    if (!graph.hasNode(sourceId) || !graph.hasNode(targetId)) continue;
    const dbData = remapIds ? {
      ...edge.dbData,
      id: edgeKey,
      source_node_id: sourceId,
      target_node_id: targetId
    } : edge.dbData;
    const attrs = {
      weight: edge.attributes.weight,
      type: edge.attributes.type,
      color: edge.attributes.color,
      label: edge.attributes.label,
      dbData
    };
    graph.addEdgeWithKey(edgeKey, sourceId, targetId, attrs);
  }
  store.set(graphAtom, graph);
  store.set(graphUpdateVersionAtom, (v) => v + 1);
  store.set(nodePositionUpdateCounterAtom, (c) => c + 1);
  const collapsedSet = /* @__PURE__ */ new Set();
  for (const group of snapshot.groups) {
    if (group.isCollapsed) {
      collapsedSet.add(remap(group.parentId));
    }
  }
  store.set(collapsedGroupsAtom, collapsedSet);
  store.set(zoomAtom, snapshot.viewport.zoom);
  store.set(panAtom, {
    ...snapshot.viewport.pan
  });
}
function validateSnapshot(data) {
  const errors = [];
  if (!data || typeof data !== "object") {
    return {
      valid: false,
      errors: ["Snapshot must be a non-null object"]
    };
  }
  const obj = data;
  if (obj.version !== SNAPSHOT_VERSION) {
    errors.push(`Expected version ${SNAPSHOT_VERSION}, got ${String(obj.version)}`);
  }
  if (typeof obj.exportedAt !== "string") {
    errors.push('Missing or invalid "exportedAt" (expected ISO string)');
  }
  if (!Array.isArray(obj.nodes)) {
    errors.push('Missing or invalid "nodes" (expected array)');
  } else {
    for (let i = 0; i < obj.nodes.length; i++) {
      const node = obj.nodes[i];
      if (!node || typeof node !== "object") {
        errors.push(`nodes[${i}]: expected object`);
        continue;
      }
      if (typeof node.id !== "string") errors.push(`nodes[${i}]: missing "id"`);
      if (!node.position || typeof node.position !== "object") errors.push(`nodes[${i}]: missing "position"`);
      if (!node.dimensions || typeof node.dimensions !== "object") errors.push(`nodes[${i}]: missing "dimensions"`);
      if (!node.dbData || typeof node.dbData !== "object") errors.push(`nodes[${i}]: missing "dbData"`);
    }
  }
  if (!Array.isArray(obj.edges)) {
    errors.push('Missing or invalid "edges" (expected array)');
  } else {
    for (let i = 0; i < obj.edges.length; i++) {
      const edge = obj.edges[i];
      if (!edge || typeof edge !== "object") {
        errors.push(`edges[${i}]: expected object`);
        continue;
      }
      if (typeof edge.key !== "string") errors.push(`edges[${i}]: missing "key"`);
      if (typeof edge.sourceId !== "string") errors.push(`edges[${i}]: missing "sourceId"`);
      if (typeof edge.targetId !== "string") errors.push(`edges[${i}]: missing "targetId"`);
      if (!edge.dbData || typeof edge.dbData !== "object") errors.push(`edges[${i}]: missing "dbData"`);
    }
  }
  if (!Array.isArray(obj.groups)) {
    errors.push('Missing or invalid "groups" (expected array)');
  }
  if (!obj.viewport || typeof obj.viewport !== "object") {
    errors.push('Missing or invalid "viewport" (expected object)');
  } else {
    const vp = obj.viewport;
    if (typeof vp.zoom !== "number") errors.push('viewport: missing "zoom"');
    if (!vp.pan || typeof vp.pan !== "object") errors.push('viewport: missing "pan"');
  }
  return {
    valid: errors.length === 0,
    errors
  };
}
var SNAPSHOT_VERSION;
var init_canvas_serializer = __esm({
  "src/core/canvas-serializer.ts"() {
    "use strict";
    init_graph_store();
    init_graph_position();
    init_viewport_store();
    init_group_store();
    SNAPSHOT_VERSION = 1;
  }
});

// src/core/clipboard-store.ts
var clipboard_store_exports = {};
__export(clipboard_store_exports, {
  PASTE_OFFSET: () => PASTE_OFFSET,
  clearClipboardAtom: () => clearClipboardAtom,
  clipboardAtom: () => clipboardAtom,
  clipboardNodeCountAtom: () => clipboardNodeCountAtom,
  copyToClipboardAtom: () => copyToClipboardAtom,
  cutToClipboardAtom: () => cutToClipboardAtom,
  duplicateSelectionAtom: () => duplicateSelectionAtom,
  hasClipboardContentAtom: () => hasClipboardContentAtom,
  pasteFromClipboardAtom: () => pasteFromClipboardAtom
});
import { atom as atom21 } from "jotai";
function calculateBounds2(nodes) {
  if (nodes.length === 0) {
    return {
      minX: 0,
      minY: 0,
      maxX: 0,
      maxY: 0
    };
  }
  let minX = Infinity;
  let minY = Infinity;
  let maxX = -Infinity;
  let maxY = -Infinity;
  for (const node of nodes) {
    minX = Math.min(minX, node.attrs.x);
    minY = Math.min(minY, node.attrs.y);
    maxX = Math.max(maxX, node.attrs.x + node.attrs.width);
    maxY = Math.max(maxY, node.attrs.y + node.attrs.height);
  }
  return {
    minX,
    minY,
    maxX,
    maxY
  };
}
function generatePasteId(index) {
  return `paste-${Date.now()}-${index}-${Math.random().toString(36).slice(2, 8)}`;
}
var debug13, PASTE_OFFSET, clipboardAtom, hasClipboardContentAtom, clipboardNodeCountAtom, copyToClipboardAtom, cutToClipboardAtom, pasteFromClipboardAtom, duplicateSelectionAtom, clearClipboardAtom;
var init_clipboard_store = __esm({
  "src/core/clipboard-store.ts"() {
    "use strict";
    init_graph_store();
    init_graph_mutations();
    init_graph_mutations_edges();
    init_selection_store();
    init_history_store();
    init_debug();
    debug13 = createDebug("clipboard");
    PASTE_OFFSET = {
      x: 50,
      y: 50
    };
    clipboardAtom = atom21(null);
    hasClipboardContentAtom = atom21((get) => get(clipboardAtom) !== null);
    clipboardNodeCountAtom = atom21((get) => {
      const clipboard = get(clipboardAtom);
      return clipboard?.nodes.length ?? 0;
    });
    copyToClipboardAtom = atom21(null, (get, set, nodeIds) => {
      const selectedIds = nodeIds ?? Array.from(get(selectedNodeIdsAtom));
      if (selectedIds.length === 0) {
        debug13("Nothing to copy - no nodes selected");
        return;
      }
      const graph = get(graphAtom);
      const selectedSet = new Set(selectedIds);
      const nodes = [];
      const edges = [];
      for (const nodeId of selectedIds) {
        if (!graph.hasNode(nodeId)) {
          debug13("Node %s not found in graph, skipping", nodeId);
          continue;
        }
        const attrs = graph.getNodeAttributes(nodeId);
        nodes.push({
          attrs: {
            ...attrs
          },
          dbData: {
            ...attrs.dbData
          }
        });
      }
      graph.forEachEdge((edgeKey, attrs, source, target) => {
        if (selectedSet.has(source) && selectedSet.has(target)) {
          edges.push({
            source,
            target,
            attrs: {
              ...attrs
            },
            dbData: {
              ...attrs.dbData
            }
          });
        }
      });
      const bounds = calculateBounds2(nodes);
      const clipboardData = {
        nodes,
        edges,
        bounds,
        timestamp: Date.now()
      };
      set(clipboardAtom, clipboardData);
      debug13("Copied %d nodes and %d edges to clipboard", nodes.length, edges.length);
    });
    cutToClipboardAtom = atom21(null, (get, set, nodeIds) => {
      const selectedIds = nodeIds ?? Array.from(get(selectedNodeIdsAtom));
      if (selectedIds.length === 0) return;
      set(copyToClipboardAtom, selectedIds);
      set(pushHistoryAtom, "Cut nodes");
      for (const nodeId of selectedIds) {
        set(optimisticDeleteNodeAtom, {
          nodeId
        });
      }
      set(clearSelectionAtom);
      debug13("Cut %d nodes \u2014 copied to clipboard and deleted from graph", selectedIds.length);
    });
    pasteFromClipboardAtom = atom21(null, (get, set, offset) => {
      const clipboard = get(clipboardAtom);
      if (!clipboard || clipboard.nodes.length === 0) {
        debug13("Nothing to paste - clipboard empty");
        return [];
      }
      const pasteOffset = offset ?? PASTE_OFFSET;
      const graph = get(graphAtom);
      set(pushHistoryAtom, "Paste nodes");
      const idMap = /* @__PURE__ */ new Map();
      const newNodeIds = [];
      for (let i = 0; i < clipboard.nodes.length; i++) {
        const nodeData = clipboard.nodes[i];
        const newId = generatePasteId(i);
        idMap.set(nodeData.dbData.id, newId);
        newNodeIds.push(newId);
        const newDbNode = {
          ...nodeData.dbData,
          id: newId,
          created_at: (/* @__PURE__ */ new Date()).toISOString(),
          updated_at: (/* @__PURE__ */ new Date()).toISOString(),
          ui_properties: {
            ...nodeData.dbData.ui_properties || {},
            x: nodeData.attrs.x + pasteOffset.x,
            y: nodeData.attrs.y + pasteOffset.y
          }
        };
        debug13("Pasting node %s -> %s at (%d, %d)", nodeData.dbData.id, newId, nodeData.attrs.x + pasteOffset.x, nodeData.attrs.y + pasteOffset.y);
        set(addNodeToLocalGraphAtom, newDbNode);
      }
      for (const edgeData of clipboard.edges) {
        const newSourceId = idMap.get(edgeData.source);
        const newTargetId = idMap.get(edgeData.target);
        if (!newSourceId || !newTargetId) {
          debug13("Edge %s: source or target not found in id map, skipping", edgeData.dbData.id);
          continue;
        }
        const newEdgeId = generatePasteId(clipboard.edges.indexOf(edgeData) + clipboard.nodes.length);
        const newDbEdge = {
          ...edgeData.dbData,
          id: newEdgeId,
          source_node_id: newSourceId,
          target_node_id: newTargetId,
          created_at: (/* @__PURE__ */ new Date()).toISOString(),
          updated_at: (/* @__PURE__ */ new Date()).toISOString()
        };
        debug13("Pasting edge %s -> %s (from %s to %s)", edgeData.dbData.id, newEdgeId, newSourceId, newTargetId);
        set(addEdgeToLocalGraphAtom, newDbEdge);
      }
      set(clearSelectionAtom);
      set(addNodesToSelectionAtom, newNodeIds);
      debug13("Pasted %d nodes and %d edges", newNodeIds.length, clipboard.edges.length);
      return newNodeIds;
    });
    duplicateSelectionAtom = atom21(null, (get, set) => {
      set(copyToClipboardAtom);
      return set(pasteFromClipboardAtom);
    });
    clearClipboardAtom = atom21(null, (_get, set) => {
      set(clipboardAtom, null);
      debug13("Clipboard cleared");
    });
  }
});

// src/core/spatial-index.ts
var SpatialGrid;
var init_spatial_index = __esm({
  "src/core/spatial-index.ts"() {
    "use strict";
    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
import { atom as atom22 } from "jotai";
var VIRTUALIZATION_BUFFER, spatialIndexAtom, visibleBoundsAtom, visibleNodeKeysAtom, visibleEdgeKeysAtom, virtualizationMetricsAtom;
var init_virtualization_store = __esm({
  "src/core/virtualization-store.ts"() {
    "use strict";
    init_graph_store();
    init_graph_position();
    init_graph_derived();
    init_viewport_store();
    init_settings_store();
    init_group_store();
    init_spatial_index();
    init_perf();
    init_settings_store();
    VIRTUALIZATION_BUFFER = 200;
    spatialIndexAtom = atom22((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;
    });
    visibleBoundsAtom = atom22((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
      };
    });
    visibleNodeKeysAtom = atom22((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;
    });
    visibleEdgeKeysAtom = atom22((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;
    });
    virtualizationMetricsAtom = atom22((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/core/canvas-api.ts
function createCanvasAPI(store, options = {}) {
  const helpers = buildActionHelpers(store, options);
  const api = {
    // Selection
    selectNode: (id) => store.set(selectSingleNodeAtom, id),
    addToSelection: (ids) => store.set(addNodesToSelectionAtom, ids),
    clearSelection: () => store.set(clearSelectionAtom),
    getSelectedNodeIds: () => Array.from(store.get(selectedNodeIdsAtom)),
    selectEdge: (edgeId) => store.set(selectEdgeAtom, edgeId),
    clearEdgeSelection: () => store.set(clearEdgeSelectionAtom),
    getSelectedEdgeId: () => store.get(selectedEdgeIdAtom),
    // Viewport
    getZoom: () => store.get(zoomAtom),
    setZoom: (zoom) => store.set(zoomAtom, zoom),
    getPan: () => store.get(panAtom),
    setPan: (pan) => store.set(panAtom, pan),
    resetViewport: () => store.set(resetViewportAtom),
    fitToBounds: (mode, padding) => {
      const fitMode = mode === "graph" ? FitToBoundsMode.Graph : FitToBoundsMode.Selection;
      store.set(fitToBoundsAtom, {
        mode: fitMode,
        padding
      });
    },
    centerOnNode: (nodeId) => store.set(centerOnNodeAtom, nodeId),
    // Graph
    addNode: (node) => store.set(addNodeToLocalGraphAtom, node),
    removeNode: (nodeId) => store.set(optimisticDeleteNodeAtom, {
      nodeId
    }),
    addEdge: (edge) => store.set(addEdgeToLocalGraphAtom, edge),
    removeEdge: (edgeKey) => store.set(optimisticDeleteEdgeAtom, {
      edgeKey
    }),
    getNodeKeys: () => store.get(nodeKeysAtom),
    getEdgeKeys: () => store.get(edgeKeysAtom),
    getNodeAttributes: (id) => {
      const graph = store.get(graphAtom);
      return graph.hasNode(id) ? graph.getNodeAttributes(id) : void 0;
    },
    // History
    undo: () => store.set(undoAtom),
    redo: () => store.set(redoAtom),
    canUndo: () => store.get(canUndoAtom),
    canRedo: () => store.get(canRedoAtom),
    recordSnapshot: (label) => store.set(pushHistoryAtom, label),
    clearHistory: () => store.set(clearHistoryAtom),
    // Clipboard
    copy: () => store.set(copyToClipboardAtom),
    cut: () => store.set(cutToClipboardAtom),
    paste: () => store.set(pasteFromClipboardAtom),
    duplicate: () => store.set(duplicateSelectionAtom),
    hasClipboardContent: () => store.get(clipboardAtom) !== null,
    // Snap
    isSnapEnabled: () => store.get(snapEnabledAtom),
    toggleSnap: () => store.set(toggleSnapAtom),
    getSnapGridSize: () => store.get(snapGridSizeAtom),
    // Virtualization
    isVirtualizationEnabled: () => store.get(virtualizationEnabledAtom),
    getVisibleNodeKeys: () => store.get(visibleNodeKeysAtom),
    getVisibleEdgeKeys: () => store.get(visibleEdgeKeysAtom),
    // Actions
    executeAction: (actionId, context) => executeAction(actionId, context, helpers),
    executeEventAction: (event, context) => {
      const mappings = store.get(eventMappingsAtom);
      const actionId = getActionForEvent(mappings, event);
      return executeAction(actionId, context, helpers);
    },
    // Serialization
    exportSnapshot: (metadata) => exportGraph(store, metadata),
    importSnapshot: (snapshot, options2) => importGraph(store, snapshot, options2),
    validateSnapshot: (data) => validateSnapshot(data)
  };
  return api;
}
var init_canvas_api = __esm({
  "src/core/canvas-api.ts"() {
    "use strict";
    init_action_executor();
    init_canvas_serializer();
    init_settings_store();
    init_selection_store();
    init_viewport_store();
    init_graph_store();
    init_graph_derived();
    init_graph_mutations();
    init_graph_mutations_edges();
    init_history_store();
    init_clipboard_store();
    init_snap_store();
    init_virtualization_store();
    init_layout();
  }
});

// src/core/port-types.ts
function calculatePortPosition(nodeX, nodeY, nodeWidth, nodeHeight, port) {
  switch (port.side) {
    case "left":
      return {
        x: nodeX,
        y: nodeY + nodeHeight * port.position
      };
    case "right":
      return {
        x: nodeX + nodeWidth,
        y: nodeY + nodeHeight * port.position
      };
    case "top":
      return {
        x: nodeX + nodeWidth * port.position,
        y: nodeY
      };
    case "bottom":
      return {
        x: nodeX + nodeWidth * port.position,
        y: nodeY + nodeHeight
      };
  }
}
function getNodePorts(ports) {
  if (ports && ports.length > 0) {
    return ports;
  }
  return [DEFAULT_PORT];
}
function canPortAcceptConnection(port, currentConnections, isSource) {
  if (isSource && port.type === "input") {
    return false;
  }
  if (!isSource && port.type === "output") {
    return false;
  }
  if (port.maxConnections !== void 0 && currentConnections >= port.maxConnections) {
    return false;
  }
  return true;
}
function arePortsCompatible(sourcePort, targetPort) {
  if (sourcePort.type === "input") {
    return false;
  }
  if (targetPort.type === "output") {
    return false;
  }
  return true;
}
var DEFAULT_PORT;
var init_port_types = __esm({
  "src/core/port-types.ts"() {
    "use strict";
    DEFAULT_PORT = {
      id: "default",
      type: "bidirectional",
      side: "right",
      position: 0.5
    };
  }
});

// src/core/input-classifier.ts
function classifyPointer(e) {
  const source = pointerTypeToSource(e.pointerType);
  return {
    source,
    pointerId: e.pointerId,
    pressure: e.pressure,
    tiltX: e.tiltX,
    tiltY: e.tiltY,
    isPrimary: e.isPrimary,
    rawPointerType: e.pointerType
  };
}
function pointerTypeToSource(pointerType) {
  switch (pointerType) {
    case "pen":
      return "pencil";
    case "touch":
      return "finger";
    case "mouse":
      return "mouse";
    default:
      return "mouse";
  }
}
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
  };
}
function getGestureThresholds(source) {
  switch (source) {
    case "finger":
      return {
        dragThreshold: 10,
        tapThreshold: 10,
        longPressDuration: 600,
        longPressMoveLimit: 10
      };
    case "pencil":
      return {
        dragThreshold: 2,
        tapThreshold: 3,
        longPressDuration: 500,
        longPressMoveLimit: 5
      };
    case "mouse":
      return {
        dragThreshold: 3,
        tapThreshold: 5,
        longPressDuration: 0,
        // Mouse uses right-click instead
        longPressMoveLimit: 0
      };
  }
}
function getHitTargetSize(source) {
  return HIT_TARGET_SIZES[source];
}
var HIT_TARGET_SIZES;
var init_input_classifier = __esm({
  "src/core/input-classifier.ts"() {
    "use strict";
    HIT_TARGET_SIZES = {
      /** Minimum touch target (Apple HIG: 44pt) */
      finger: 44,
      /** Stylus target (precise, can use smaller targets) */
      pencil: 24,
      /** Mouse target (hover-discoverable, smallest) */
      mouse: 16
    };
  }
});

// src/core/input-store.ts
import { atom as atom23 } from "jotai";
var activePointersAtom, primaryInputSourceAtom, inputCapabilitiesAtom, isStylusActiveAtom, isMultiTouchAtom, fingerCountAtom, isTouchDeviceAtom, pointerDownAtom, pointerUpAtom, clearPointersAtom;
var init_input_store = __esm({
  "src/core/input-store.ts"() {
    "use strict";
    init_input_classifier();
    activePointersAtom = atom23(/* @__PURE__ */ new Map());
    primaryInputSourceAtom = atom23("mouse");
    inputCapabilitiesAtom = atom23(detectInputCapabilities());
    isStylusActiveAtom = atom23((get) => {
      const pointers = get(activePointersAtom);
      for (const [, pointer] of pointers) {
        if (pointer.source === "pencil") return true;
      }
      return false;
    });
    isMultiTouchAtom = atom23((get) => {
      const pointers = get(activePointersAtom);
      let fingerCount = 0;
      for (const [, pointer] of pointers) {
        if (pointer.source === "finger") fingerCount++;
      }
      return fingerCount > 1;
    });
    fingerCountAtom = atom23((get) => {
      const pointers = get(activePointersAtom);
      let count = 0;
      for (const [, pointer] of pointers) {
        if (pointer.source === "finger") count++;
      }
      return count;
    });
    isTouchDeviceAtom = atom23((get) => {
      const caps = get(inputCapabilitiesAtom);
      return caps.hasTouch;
    });
    pointerDownAtom = atom23(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
          });
        }
      }
    });
    pointerUpAtom = atom23(null, (get, set, pointerId) => {
      const pointers = new Map(get(activePointersAtom));
      pointers.delete(pointerId);
      set(activePointersAtom, pointers);
    });
    clearPointersAtom = atom23(null, (_get, set) => {
      set(activePointersAtom, /* @__PURE__ */ new Map());
    });
  }
});

// src/core/selection-path-store.ts
import { atom as atom24 } from "jotai";
function pointInPolygon(px, py, polygon) {
  let inside = false;
  const n = polygon.length;
  for (let i = 0, j = n - 1; i < n; j = i++) {
    const xi = polygon[i].x;
    const yi = polygon[i].y;
    const xj = polygon[j].x;
    const yj = polygon[j].y;
    if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi) {
      inside = !inside;
    }
  }
  return inside;
}
var selectionPathAtom, isSelectingAtom, startSelectionAtom, updateSelectionAtom, cancelSelectionAtom, endSelectionAtom, selectionRectAtom;
var init_selection_path_store = __esm({
  "src/core/selection-path-store.ts"() {
    "use strict";
    init_graph_derived();
    init_selection_store();
    selectionPathAtom = atom24(null);
    isSelectingAtom = atom24((get) => get(selectionPathAtom) !== null);
    startSelectionAtom = atom24(null, (_get, set, {
      type,
      point
    }) => {
      set(selectionPathAtom, {
        type,
        points: [point]
      });
    });
    updateSelectionAtom = atom24(null, (get, set, point) => {
      const current = get(selectionPathAtom);
      if (!current) return;
      if (current.type === "rect") {
        set(selectionPathAtom, {
          ...current,
          points: [current.points[0], point]
        });
      } else {
        set(selectionPathAtom, {
          ...current,
          points: [...current.points, point]
        });
      }
    });
    cancelSelectionAtom = atom24(null, (_get, set) => {
      set(selectionPathAtom, null);
    });
    endSelectionAtom = atom24(null, (get, set) => {
      const path = get(selectionPathAtom);
      if (!path || path.points.length < 2) {
        set(selectionPathAtom, null);
        return;
      }
      const nodes = get(uiNodesAtom);
      const selectedIds = [];
      if (path.type === "rect") {
        const [p1, p2] = [path.points[0], path.points[path.points.length - 1]];
        const minX = Math.min(p1.x, p2.x);
        const maxX = Math.max(p1.x, p2.x);
        const minY = Math.min(p1.y, p2.y);
        const maxY = Math.max(p1.y, p2.y);
        for (const node of nodes) {
          const nodeRight = node.position.x + (node.width ?? 200);
          const nodeBottom = node.position.y + (node.height ?? 100);
          if (node.position.x < maxX && nodeRight > minX && node.position.y < maxY && nodeBottom > minY) {
            selectedIds.push(node.id);
          }
        }
      } else {
        const polygon = path.points;
        for (const node of nodes) {
          const cx = node.position.x + (node.width ?? 200) / 2;
          const cy = node.position.y + (node.height ?? 100) / 2;
          if (pointInPolygon(cx, cy, polygon)) {
            selectedIds.push(node.id);
          }
        }
      }
      set(selectedNodeIdsAtom, new Set(selectedIds));
      set(selectionPathAtom, null);
    });
    selectionRectAtom = atom24((get) => {
      const path = get(selectionPathAtom);
      if (!path || path.type !== "rect" || path.points.length < 2) return null;
      const [p1, p2] = [path.points[0], path.points[path.points.length - 1]];
      return {
        x: Math.min(p1.x, p2.x),
        y: Math.min(p1.y, p2.y),
        width: Math.abs(p2.x - p1.x),
        height: Math.abs(p2.y - p1.y)
      };
    });
  }
});

// src/core/search-store.ts
var search_store_exports = {};
__export(search_store_exports, {
  clearSearchAtom: () => clearSearchAtom,
  fuzzyMatch: () => fuzzyMatch,
  highlightedSearchIndexAtom: () => highlightedSearchIndexAtom,
  highlightedSearchNodeIdAtom: () => highlightedSearchNodeIdAtom,
  isFilterActiveAtom: () => isFilterActiveAtom,
  nextSearchResultAtom: () => nextSearchResultAtom,
  prevSearchResultAtom: () => prevSearchResultAtom,
  searchEdgeResultCountAtom: () => searchEdgeResultCountAtom,
  searchEdgeResultsAtom: () => searchEdgeResultsAtom,
  searchQueryAtom: () => searchQueryAtom,
  searchResultCountAtom: () => searchResultCountAtom,
  searchResultsArrayAtom: () => searchResultsArrayAtom,
  searchResultsAtom: () => searchResultsAtom,
  searchTotalResultCountAtom: () => searchTotalResultCountAtom,
  setSearchQueryAtom: () => setSearchQueryAtom
});
import { atom as atom25 } from "jotai";
function fuzzyMatch(query, ...haystacks) {
  const tokens = query.toLowerCase().split(/\s+/).filter(Boolean);
  if (tokens.length === 0) return false;
  const combined = haystacks.join(" ").toLowerCase();
  return tokens.every((token) => combined.includes(token));
}
var searchQueryAtom, setSearchQueryAtom, clearSearchAtom, searchResultsAtom, searchResultsArrayAtom, searchResultCountAtom, searchEdgeResultsAtom, searchEdgeResultCountAtom, isFilterActiveAtom, searchTotalResultCountAtom, highlightedSearchIndexAtom, nextSearchResultAtom, prevSearchResultAtom, highlightedSearchNodeIdAtom;
var init_search_store = __esm({
  "src/core/search-store.ts"() {
    "use strict";
    init_graph_derived();
    init_graph_store();
    init_viewport_store();
    init_selection_store();
    searchQueryAtom = atom25("");
    setSearchQueryAtom = atom25(null, (_get, set, query) => {
      set(searchQueryAtom, query);
      set(highlightedSearchIndexAtom, 0);
    });
    clearSearchAtom = atom25(null, (_get, set) => {
      set(searchQueryAtom, "");
      set(highlightedSearchIndexAtom, 0);
    });
    searchResultsAtom = atom25((get) => {
      const query = get(searchQueryAtom).trim();
      if (!query) return /* @__PURE__ */ new Set();
      const nodes = get(uiNodesAtom);
      const matches = /* @__PURE__ */ new Set();
      for (const node of nodes) {
        if (fuzzyMatch(query, node.label || "", node.dbData.node_type || "", node.id)) {
          matches.add(node.id);
        }
      }
      return matches;
    });
    searchResultsArrayAtom = atom25((get) => {
      return Array.from(get(searchResultsAtom));
    });
    searchResultCountAtom = atom25((get) => {
      return get(searchResultsAtom).size;
    });
    searchEdgeResultsAtom = atom25((get) => {
      const query = get(searchQueryAtom).trim();
      if (!query) return /* @__PURE__ */ new Set();
      get(graphUpdateVersionAtom);
      const graph = get(graphAtom);
      const matches = /* @__PURE__ */ new Set();
      graph.forEachEdge((edgeKey, attrs) => {
        const label = attrs.label || "";
        const edgeType = attrs.dbData?.edge_type || "";
        if (fuzzyMatch(query, label, edgeType, edgeKey)) {
          matches.add(edgeKey);
        }
      });
      return matches;
    });
    searchEdgeResultCountAtom = atom25((get) => {
      return get(searchEdgeResultsAtom).size;
    });
    isFilterActiveAtom = atom25((get) => {
      return get(searchQueryAtom).trim().length > 0;
    });
    searchTotalResultCountAtom = atom25((get) => {
      return get(searchResultCountAtom) + get(searchEdgeResultCountAtom);
    });
    highlightedSearchIndexAtom = atom25(0);
    nextSearchResultAtom = atom25(null, (get, set) => {
      const results = get(searchResultsArrayAtom);
      if (results.length === 0) return;
      const currentIndex = get(highlightedSearchIndexAtom);
      const nextIndex = (currentIndex + 1) % results.length;
      set(highlightedSearchIndexAtom, nextIndex);
      const nodeId = results[nextIndex];
      set(centerOnNodeAtom, nodeId);
      set(selectSingleNodeAtom, nodeId);
    });
    prevSearchResultAtom = atom25(null, (get, set) => {
      const results = get(searchResultsArrayAtom);
      if (results.length === 0) return;
      const currentIndex = get(highlightedSearchIndexAtom);
      const prevIndex = (currentIndex - 1 + results.length) % results.length;
      set(highlightedSearchIndexAtom, prevIndex);
      const nodeId = results[prevIndex];
      set(centerOnNodeAtom, nodeId);
      set(selectSingleNodeAtom, nodeId);
    });
    highlightedSearchNodeIdAtom = atom25((get) => {
      const results = get(searchResultsArrayAtom);
      if (results.length === 0) return null;
      const index = get(highlightedSearchIndexAtom);
      return results[index] ?? null;
    });
  }
});

// src/core/gesture-resolver.ts
var init_gesture_resolver = __esm({
  "src/core/gesture-resolver.ts"() {
    "use strict";
  }
});

// src/core/gesture-rules-defaults.ts
function formatRuleLabel(pattern) {
  const parts = [];
  if (pattern.modifiers) {
    const mods = MODIFIER_KEYS.filter((k) => pattern.modifiers[k]).map((k) => k.charAt(0).toUpperCase() + k.slice(1));
    if (mods.length) parts.push(mods.join("+"));
  }
  if (pattern.button !== void 0 && pattern.button !== 0) {
    parts.push(BUTTON_LABELS[pattern.button]);
  }
  if (pattern.source) {
    parts.push(SOURCE_LABELS[pattern.source]);
  }
  if (pattern.gesture) {
    parts.push(GESTURE_LABELS[pattern.gesture] ?? pattern.gesture);
  }
  if (pattern.target) {
    parts.push("on " + (TARGET_LABELS[pattern.target] ?? pattern.target));
  }
  if (parts.length === 0) return "Any gesture";
  if (pattern.modifiers) {
    const modCount = MODIFIER_KEYS.filter((k) => pattern.modifiers[k]).length;
    if (modCount > 0 && parts.length > modCount) {
      const modPart = parts.slice(0, 1).join("");
      const rest = parts.slice(1).join(" ").toLowerCase();
      return `${modPart} + ${rest}`;
    }
  }
  return parts.join(" ");
}
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 MODIFIER_KEYS, SOURCE_LABELS, GESTURE_LABELS, TARGET_LABELS, BUTTON_LABELS, DEFAULT_GESTURE_RULES;
var init_gesture_rules_defaults = __esm({
  "src/core/gesture-rules-defaults.ts"() {
    "use strict";
    MODIFIER_KEYS = ["shift", "ctrl", "alt", "meta"];
    SOURCE_LABELS = {
      mouse: "Mouse",
      pencil: "Pencil",
      finger: "Touch"
    };
    GESTURE_LABELS = {
      tap: "Tap",
      "double-tap": "Double-tap",
      "triple-tap": "Triple-tap",
      drag: "Drag",
      "long-press": "Long-press",
      "right-click": "Right-click",
      pinch: "Pinch",
      scroll: "Scroll"
    };
    TARGET_LABELS = {
      node: "node",
      edge: "edge",
      port: "port",
      "resize-handle": "resize handle",
      background: "background"
    };
    BUTTON_LABELS = {
      0: "Left",
      1: "Middle",
      2: "Right"
    };
    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
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_KEYS2) {
      const required = pattern.modifiers[key];
      if (required === void 0) continue;
      const actual = dm[key] ?? false;
      if (required !== actual) return -1;
      score += 8;
    }
  }
  return score;
}
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);
}
var MODIFIER_KEYS2, PALM_REJECTION_RULE;
var init_gesture_rules = __esm({
  "src/core/gesture-rules.ts"() {
    "use strict";
    init_gesture_rules_defaults();
    MODIFIER_KEYS2 = ["shift", "ctrl", "alt", "meta"];
    PALM_REJECTION_RULE = {
      id: "__palm-rejection__",
      pattern: {},
      actionId: "none",
      label: "Palm rejection"
    };
  }
});

// src/core/gesture-rule-store.ts
import { atom as atom26 } from "jotai";
import { atomWithStorage as atomWithStorage3 } from "jotai/utils";
var DEFAULT_RULE_STATE, gestureRuleSettingsAtom, consumerGestureRulesAtom, gestureRulesAtom, gestureRuleIndexAtom, palmRejectionEnabledAtom, addGestureRuleAtom, removeGestureRuleAtom, updateGestureRuleAtom, resetGestureRulesAtom;
var init_gesture_rule_store = __esm({
  "src/core/gesture-rule-store.ts"() {
    "use strict";
    init_gesture_rules();
    DEFAULT_RULE_STATE = {
      customRules: [],
      palmRejection: true
    };
    gestureRuleSettingsAtom = atomWithStorage3("canvas-gesture-rules", DEFAULT_RULE_STATE);
    consumerGestureRulesAtom = atom26([]);
    gestureRulesAtom = atom26((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;
    });
    gestureRuleIndexAtom = atom26((get) => {
      return buildRuleIndex(get(gestureRulesAtom));
    });
    palmRejectionEnabledAtom = atom26((get) => get(gestureRuleSettingsAtom).palmRejection, (get, set, enabled) => {
      const current = get(gestureRuleSettingsAtom);
      set(gestureRuleSettingsAtom, {
        ...current,
        palmRejection: enabled
      });
    });
    addGestureRuleAtom = atom26(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
      });
    });
    removeGestureRuleAtom = atom26(null, (get, set, ruleId) => {
      const current = get(gestureRuleSettingsAtom);
      set(gestureRuleSettingsAtom, {
        ...current,
        customRules: current.customRules.filter((r) => r.id !== ruleId)
      });
    });
    updateGestureRuleAtom = atom26(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
      });
    });
    resetGestureRulesAtom = atom26(null, (get, set) => {
      const current = get(gestureRuleSettingsAtom);
      set(gestureRuleSettingsAtom, {
        ...current,
        customRules: []
      });
    });
  }
});

// src/core/external-keyboard-store.ts
import { atom as atom27 } from "jotai";
var hasExternalKeyboardAtom, watchExternalKeyboardAtom;
var init_external_keyboard_store = __esm({
  "src/core/external-keyboard-store.ts"() {
    "use strict";
    hasExternalKeyboardAtom = atom27(false);
    watchExternalKeyboardAtom = atom27(null, (get, set) => {
      if (typeof window === "undefined") return;
      const handler = (e) => {
        if (e.key && e.key.length === 1 || ["Tab", "Escape", "Enter", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
          set(hasExternalKeyboardAtom, true);
          window.removeEventListener("keydown", handler);
        }
      };
      window.addEventListener("keydown", handler);
      return () => window.removeEventListener("keydown", handler);
    });
  }
});

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

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

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

// src/utils/edge-path-calculators.ts
var init_edge_path_calculators = __esm({
  "src/utils/edge-path-calculators.ts"() {
    "use strict";
  }
});

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

// src/core/plugin-registry.ts
function registerPlugin(plugin) {
  debug14("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()
    });
    debug14("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);
  debug14("Plugin unregistered: %s", pluginId);
}
function getPlugin(id) {
  return plugins.get(id)?.plugin;
}
function hasPlugin(id) {
  return plugins.has(id);
}
function getAllPlugins() {
  return Array.from(plugins.values()).map((r) => r.plugin);
}
function getPluginIds() {
  return Array.from(plugins.keys());
}
function getPluginGestureContexts() {
  const contexts = [];
  for (const registration of plugins.values()) {
    if (registration.plugin.gestureContexts) {
      contexts.push(...registration.plugin.gestureContexts);
    }
  }
  return contexts;
}
function clearPlugins() {
  const ids = Array.from(plugins.keys()).reverse();
  for (const id of ids) {
    const reg = plugins.get(id);
    if (reg?.cleanup) {
      try {
        reg.cleanup();
      } catch {
      }
    }
    plugins.delete(id);
  }
  debug14("All plugins cleared");
}
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
  };
}
var debug14, plugins;
var init_plugin_registry = __esm({
  "src/core/plugin-registry.ts"() {
    "use strict";
    init_plugin_types();
    init_node_type_registry();
    init_action_registry();
    init_dispatcher();
    init_registry();
    init_edge_path_registry();
    init_debug();
    debug14 = createDebug("plugins");
    plugins = /* @__PURE__ */ new Map();
  }
});

// src/core/index.ts
var core_exports = {};
__export(core_exports, {
  ActionCategory: () => ActionCategory,
  BUILT_IN_PRESETS: () => BUILT_IN_PRESETS,
  BuiltInActionId: () => BuiltInActionId,
  CanvasEventType: () => CanvasEventType,
  DEFAULT_GESTURE_RULES: () => DEFAULT_GESTURE_RULES,
  DEFAULT_MAPPINGS: () => DEFAULT_MAPPINGS,
  DEFAULT_PORT: () => DEFAULT_PORT,
  EDGE_ANIMATION_DURATION: () => EDGE_ANIMATION_DURATION,
  EVENT_TYPE_INFO: () => EVENT_TYPE_INFO,
  FallbackNodeTypeComponent: () => FallbackNodeTypeComponent,
  HIT_TARGET_SIZES: () => HIT_TARGET_SIZES,
  PASTE_OFFSET: () => PASTE_OFFSET,
  PluginError: () => PluginError,
  SNAPSHOT_VERSION: () => SNAPSHOT_VERSION,
  SpatialGrid: () => SpatialGrid,
  VIRTUALIZATION_BUFFER: () => VIRTUALIZATION_BUFFER,
  ZOOM_EXIT_THRESHOLD: () => ZOOM_EXIT_THRESHOLD,
  ZOOM_TRANSITION_THRESHOLD: () => ZOOM_TRANSITION_THRESHOLD,
  activePointersAtom: () => activePointersAtom,
  activePresetAtom: () => activePresetAtom,
  activePresetIdAtom: () => activePresetIdAtom,
  addGestureRuleAtom: () => addGestureRuleAtom,
  addNodeToLocalGraphAtom: () => addNodeToLocalGraphAtom,
  addNodesToSelectionAtom: () => addNodesToSelectionAtom,
  alignmentGuidesAtom: () => alignmentGuidesAtom,
  allPresetsAtom: () => allPresetsAtom,
  animateFitToBoundsAtom: () => animateFitToBoundsAtom,
  animateZoomToNodeAtom: () => animateZoomToNodeAtom,
  applyDelta: () => applyDelta,
  applyPresetAtom: () => applyPresetAtom,
  arePortsCompatible: () => arePortsCompatible,
  autoResizeGroupAtom: () => autoResizeGroupAtom,
  buildActionHelpers: () => buildActionHelpers,
  buildRuleIndex: () => buildRuleIndex,
  calculatePortPosition: () => calculatePortPosition,
  canPortAcceptConnection: () => canPortAcceptConnection,
  canRedoAtom: () => canRedoAtom,
  canUndoAtom: () => canUndoAtom,
  cancelSelectionAtom: () => cancelSelectionAtom,
  canvasMark: () => canvasMark,
  canvasSettingsAtom: () => canvasSettingsAtom,
  canvasToastAtom: () => canvasToastAtom,
  canvasWrap: () => canvasWrap,
  centerOnNodeAtom: () => centerOnNodeAtom,
  classifyPointer: () => classifyPointer,
  cleanupAllNodePositionsAtom: () => cleanupAllNodePositionsAtom,
  cleanupNodePositionAtom: () => cleanupNodePositionAtom,
  clearActions: () => clearActions,
  clearAlignmentGuidesAtom: () => clearAlignmentGuidesAtom,
  clearClipboardAtom: () => clearClipboardAtom,
  clearEdgeSelectionAtom: () => clearEdgeSelectionAtom,
  clearGraphOnSwitchAtom: () => clearGraphOnSwitchAtom,
  clearHistoryAtom: () => clearHistoryAtom,
  clearMutationQueueAtom: () => clearMutationQueueAtom,
  clearNodeTypeRegistry: () => clearNodeTypeRegistry,
  clearPlugins: () => clearPlugins,
  clearPointersAtom: () => clearPointersAtom,
  clearSearchAtom: () => clearSearchAtom,
  clearSelectionAtom: () => clearSelectionAtom,
  clipboardAtom: () => clipboardAtom,
  clipboardNodeCountAtom: () => clipboardNodeCountAtom,
  collapseGroupAtom: () => collapseGroupAtom,
  collapsedEdgeRemapAtom: () => collapsedEdgeRemapAtom,
  collapsedGroupsAtom: () => collapsedGroupsAtom,
  completeMutationAtom: () => completeMutationAtom,
  conditionalSnap: () => conditionalSnap,
  consumerGestureRulesAtom: () => consumerGestureRulesAtom,
  copyToClipboardAtom: () => copyToClipboardAtom,
  createActionContext: () => createActionContext,
  createActionContextFromReactEvent: () => createActionContextFromReactEvent,
  createActionContextFromTouchEvent: () => createActionContextFromTouchEvent,
  createCanvasAPI: () => createCanvasAPI,
  createSnapshot: () => createSnapshot,
  currentGraphIdAtom: () => currentGraphIdAtom,
  cutToClipboardAtom: () => cutToClipboardAtom,
  deletePresetAtom: () => deletePresetAtom,
  departingEdgesAtom: () => departingEdgesAtom,
  dequeueMutationAtom: () => dequeueMutationAtom,
  detectInputCapabilities: () => detectInputCapabilities,
  draggingNodeIdAtom: () => draggingNodeIdAtom,
  dropTargetNodeIdAtom: () => dropTargetNodeIdAtom,
  duplicateSelectionAtom: () => duplicateSelectionAtom,
  edgeCreationAtom: () => edgeCreationAtom,
  edgeFamilyAtom: () => edgeFamilyAtom,
  edgeKeysAtom: () => edgeKeysAtom,
  edgeKeysWithTempEdgeAtom: () => edgeKeysWithTempEdgeAtom,
  editingEdgeLabelAtom: () => editingEdgeLabelAtom,
  endNodeDragAtom: () => endNodeDragAtom,
  endSelectionAtom: () => endSelectionAtom,
  eventMappingsAtom: () => eventMappingsAtom,
  executeAction: () => executeAction,
  expandGroupAtom: () => expandGroupAtom,
  exportGraph: () => exportGraph,
  findAlignmentGuides: () => findAlignmentGuides,
  fingerCountAtom: () => fingerCountAtom,
  fitToBoundsAtom: () => fitToBoundsAtom,
  focusedNodeIdAtom: () => focusedNodeIdAtom,
  formatRuleLabel: () => formatRuleLabel,
  fuzzyMatch: () => fuzzyMatch,
  gestureRuleIndexAtom: () => gestureRuleIndexAtom,
  gestureRuleSettingsAtom: () => gestureRuleSettingsAtom,
  gestureRulesAtom: () => gestureRulesAtom,
  getAction: () => getAction,
  getActionForEvent: () => getActionForEvent,
  getActionsByCategories: () => getActionsByCategories,
  getActionsByCategory: () => getActionsByCategory,
  getAllActions: () => getAllActions,
  getAllPlugins: () => getAllPlugins,
  getGestureThresholds: () => getGestureThresholds,
  getHitTargetSize: () => getHitTargetSize,
  getNextQueuedMutationAtom: () => getNextQueuedMutationAtom,
  getNodeDescendants: () => getNodeDescendants,
  getNodePorts: () => getNodePorts,
  getNodeTypeComponent: () => getNodeTypeComponent,
  getPlugin: () => getPlugin,
  getPluginGestureContexts: () => getPluginGestureContexts,
  getPluginIds: () => getPluginIds,
  getRegisteredNodeTypes: () => getRegisteredNodeTypes,
  getSnapGuides: () => getSnapGuides,
  goToLockedPageAtom: () => goToLockedPageAtom,
  graphAtom: () => graphAtom,
  graphOptions: () => graphOptions,
  graphUpdateVersionAtom: () => graphUpdateVersionAtom,
  groupChildCountAtom: () => groupChildCountAtom,
  groupSelectedNodesAtom: () => groupSelectedNodesAtom,
  handleNodePointerDownSelectionAtom: () => handleNodePointerDownSelectionAtom,
  hasAction: () => hasAction,
  hasClipboardContentAtom: () => hasClipboardContentAtom,
  hasExternalKeyboardAtom: () => hasExternalKeyboardAtom,
  hasFocusedNodeAtom: () => hasFocusedNodeAtom,
  hasLockedNodeAtom: () => hasLockedNodeAtom,
  hasNodeTypeComponent: () => hasNodeTypeComponent,
  hasPlugin: () => hasPlugin,
  hasSelectionAtom: () => hasSelectionAtom,
  hasUnsavedChangesAtom: () => hasUnsavedChangesAtom,
  highestZIndexAtom: () => highestZIndexAtom,
  highlightedSearchIndexAtom: () => highlightedSearchIndexAtom,
  highlightedSearchNodeIdAtom: () => highlightedSearchNodeIdAtom,
  historyLabelsAtom: () => historyLabelsAtom,
  historyStateAtom: () => historyStateAtom,
  importGraph: () => importGraph,
  incrementRetryCountAtom: () => incrementRetryCountAtom,
  inputCapabilitiesAtom: () => inputCapabilitiesAtom,
  inputModeAtom: () => inputModeAtom2,
  interactionFeedbackAtom: () => interactionFeedbackAtom,
  invertDelta: () => invertDelta,
  isFilterActiveAtom: () => isFilterActiveAtom,
  isGroupNodeAtom: () => isGroupNodeAtom,
  isMultiTouchAtom: () => isMultiTouchAtom,
  isNodeCollapsed: () => isNodeCollapsed,
  isOnlineAtom: () => isOnlineAtom,
  isPanelOpenAtom: () => isPanelOpenAtom,
  isPickNodeModeAtom: () => isPickNodeModeAtom,
  isPickingModeAtom: () => isPickingModeAtom,
  isSelectingAtom: () => isSelectingAtom,
  isSnappingActiveAtom: () => isSnappingActiveAtom,
  isStylusActiveAtom: () => isStylusActiveAtom,
  isTouchDeviceAtom: () => isTouchDeviceAtom,
  isZoomTransitioningAtom: () => isZoomTransitioningAtom,
  keyboardInteractionModeAtom: () => keyboardInteractionModeAtom,
  lastSyncErrorAtom: () => lastSyncErrorAtom,
  lastSyncTimeAtom: () => lastSyncTimeAtom,
  loadGraphFromDbAtom: () => loadGraphFromDbAtom,
  lockNodeAtom: () => lockNodeAtom,
  lockedNodeDataAtom: () => lockedNodeDataAtom,
  lockedNodeIdAtom: () => lockedNodeIdAtom,
  lockedNodePageCountAtom: () => lockedNodePageCountAtom,
  lockedNodePageIndexAtom: () => lockedNodePageIndexAtom,
  matchSpecificity: () => matchSpecificity,
  mergeNodesAtom: () => mergeNodesAtom,
  mergeRules: () => mergeRules,
  moveNodesToGroupAtom: () => moveNodesToGroupAtom,
  mutationQueueAtom: () => mutationQueueAtom,
  nestNodesOnDropAtom: () => nestNodesOnDropAtom,
  nextLockedPageAtom: () => nextLockedPageAtom,
  nextSearchResultAtom: () => nextSearchResultAtom,
  nodeChildrenAtom: () => nodeChildrenAtom,
  nodeFamilyAtom: () => nodeFamilyAtom,
  nodeKeysAtom: () => nodeKeysAtom,
  nodeParentAtom: () => nodeParentAtom,
  nodePositionAtomFamily: () => nodePositionAtomFamily,
  nodePositionUpdateCounterAtom: () => nodePositionUpdateCounterAtom,
  optimisticDeleteEdgeAtom: () => optimisticDeleteEdgeAtom,
  optimisticDeleteNodeAtom: () => optimisticDeleteNodeAtom,
  palmRejectionEnabledAtom: () => palmRejectionEnabledAtom,
  panAtom: () => panAtom,
  pasteFromClipboardAtom: () => pasteFromClipboardAtom,
  pendingInputResolverAtom: () => pendingInputResolverAtom2,
  pendingMutationsCountAtom: () => pendingMutationsCountAtom,
  perfEnabledAtom: () => perfEnabledAtom,
  pointInPolygon: () => pointInPolygon,
  pointerDownAtom: () => pointerDownAtom,
  pointerUpAtom: () => pointerUpAtom,
  preDragNodeAttributesAtom: () => preDragNodeAttributesAtom,
  prefersReducedMotionAtom: () => prefersReducedMotionAtom,
  prevLockedPageAtom: () => prevLockedPageAtom,
  prevSearchResultAtom: () => prevSearchResultAtom,
  primaryInputSourceAtom: () => primaryInputSourceAtom,
  provideInputAtom: () => provideInputAtom2,
  pushDeltaAtom: () => pushDeltaAtom,
  pushHistoryAtom: () => pushHistoryAtom,
  queueMutationAtom: () => queueMutationAtom,
  redoAtom: () => redoAtom,
  redoCountAtom: () => redoCountAtom,
  registerAction: () => registerAction,
  registerNodeType: () => registerNodeType,
  registerNodeTypes: () => registerNodeTypes,
  registerPlugin: () => registerPlugin,
  removeEdgeWithAnimationAtom: () => removeEdgeWithAnimationAtom,
  removeFromGroupAtom: () => removeFromGroupAtom,
  removeGestureRuleAtom: () => removeGestureRuleAtom,
  removeNodesFromSelectionAtom: () => removeNodesFromSelectionAtom,
  resetGestureRulesAtom: () => resetGestureRulesAtom,
  resetInputModeAtom: () => resetInputModeAtom,
  resetKeyboardInteractionModeAtom: () => resetKeyboardInteractionModeAtom,
  resetSettingsAtom: () => resetSettingsAtom,
  resetViewportAtom: () => resetViewportAtom,
  resolveGesture: () => resolveGesture,
  resolveGestureIndexed: () => resolveGestureIndexed,
  saveAsPresetAtom: () => saveAsPresetAtom,
  screenToWorldAtom: () => screenToWorldAtom,
  searchEdgeResultCountAtom: () => searchEdgeResultCountAtom,
  searchEdgeResultsAtom: () => searchEdgeResultsAtom,
  searchQueryAtom: () => searchQueryAtom,
  searchResultCountAtom: () => searchResultCountAtom,
  searchResultsArrayAtom: () => searchResultsArrayAtom,
  searchResultsAtom: () => searchResultsAtom,
  searchTotalResultCountAtom: () => searchTotalResultCountAtom,
  selectEdgeAtom: () => selectEdgeAtom,
  selectSingleNodeAtom: () => selectSingleNodeAtom,
  selectedEdgeIdAtom: () => selectedEdgeIdAtom,
  selectedNodeIdsAtom: () => selectedNodeIdsAtom,
  selectedNodesCountAtom: () => selectedNodesCountAtom,
  selectionPathAtom: () => selectionPathAtom,
  selectionRectAtom: () => selectionRectAtom,
  setEventMappingAtom: () => setEventMappingAtom,
  setFocusedNodeAtom: () => setFocusedNodeAtom,
  setGridSizeAtom: () => setGridSizeAtom,
  setKeyboardInteractionModeAtom: () => setKeyboardInteractionModeAtom,
  setNodeParentAtom: () => setNodeParentAtom,
  setOnlineStatusAtom: () => setOnlineStatusAtom,
  setPanelOpenAtom: () => setPanelOpenAtom,
  setPerfEnabled: () => setPerfEnabled,
  setSearchQueryAtom: () => setSearchQueryAtom,
  setVirtualizationEnabledAtom: () => setVirtualizationEnabledAtom,
  setZoomAtom: () => setZoomAtom,
  showToastAtom: () => showToastAtom,
  snapAlignmentEnabledAtom: () => snapAlignmentEnabledAtom,
  snapEnabledAtom: () => snapEnabledAtom,
  snapGridSizeAtom: () => snapGridSizeAtom,
  snapTemporaryDisableAtom: () => snapTemporaryDisableAtom,
  snapToGrid: () => snapToGrid,
  spatialIndexAtom: () => spatialIndexAtom,
  splitNodeAtom: () => splitNodeAtom,
  startMutationAtom: () => startMutationAtom,
  startNodeDragAtom: () => startNodeDragAtom,
  startPickNodeAtom: () => startPickNodeAtom,
  startPickNodesAtom: () => startPickNodesAtom,
  startPickPointAtom: () => startPickPointAtom,
  startSelectionAtom: () => startSelectionAtom,
  swapEdgeAtomicAtom: () => swapEdgeAtomicAtom,
  syncStateAtom: () => syncStateAtom,
  syncStatusAtom: () => syncStatusAtom,
  toggleAlignmentGuidesAtom: () => toggleAlignmentGuidesAtom,
  toggleGroupCollapseAtom: () => toggleGroupCollapseAtom,
  toggleNodeInSelectionAtom: () => toggleNodeInSelectionAtom,
  togglePanelAtom: () => togglePanelAtom,
  toggleSnapAtom: () => toggleSnapAtom,
  toggleVirtualizationAtom: () => toggleVirtualizationAtom,
  trackMutationErrorAtom: () => trackMutationErrorAtom,
  uiNodesAtom: () => uiNodesAtom,
  undoAtom: () => undoAtom,
  undoCountAtom: () => undoCountAtom,
  ungroupNodesAtom: () => ungroupNodesAtom,
  unlockNodeAtom: () => unlockNodeAtom,
  unregisterAction: () => unregisterAction,
  unregisterNodeType: () => unregisterNodeType,
  unregisterPlugin: () => unregisterPlugin,
  updateEdgeLabelAtom: () => updateEdgeLabelAtom,
  updateGestureRuleAtom: () => updateGestureRuleAtom,
  updateInteractionFeedbackAtom: () => updateInteractionFeedbackAtom,
  updateNodePositionAtom: () => updateNodePositionAtom,
  updatePresetAtom: () => updatePresetAtom,
  updateSelectionAtom: () => updateSelectionAtom,
  validateSnapshot: () => validateSnapshot,
  viewportRectAtom: () => viewportRectAtom,
  virtualizationEnabledAtom: () => virtualizationEnabledAtom,
  virtualizationMetricsAtom: () => virtualizationMetricsAtom,
  visibleBoundsAtom: () => visibleBoundsAtom,
  visibleEdgeKeysAtom: () => visibleEdgeKeysAtom,
  visibleNodeKeysAtom: () => visibleNodeKeysAtom,
  watchExternalKeyboardAtom: () => watchExternalKeyboardAtom,
  watchReducedMotionAtom: () => watchReducedMotionAtom,
  worldToScreenAtom: () => worldToScreenAtom,
  zoomAnimationTargetAtom: () => zoomAnimationTargetAtom,
  zoomAtom: () => zoomAtom,
  zoomFocusNodeIdAtom: () => zoomFocusNodeIdAtom,
  zoomTransitionProgressAtom: () => zoomTransitionProgressAtom
});
var init_core = __esm({
  "src/core/index.ts"() {
    "use strict";
    init_types();
    init_graph_store();
    init_graph_position();
    init_graph_derived();
    init_graph_mutations();
    init_viewport_store();
    init_selection_store();
    init_sync_store();
    init_interaction_store();
    init_locked_node_store();
    init_node_type_registry();
    init_history_store();
    init_toast_store();
    init_snap_store();
    init_settings_types();
    init_action_registry();
    init_action_executor();
    init_settings_store();
    init_canvas_api();
    init_virtualization_store();
    init_port_types();
    init_clipboard_store();
    init_input_classifier();
    init_input_store();
    init_selection_path_store();
    init_group_store();
    init_search_store();
    init_gesture_resolver();
    init_gesture_rules();
    init_gesture_rule_store();
    init_reduced_motion_store();
    init_external_keyboard_store();
    init_perf();
    init_spatial_index();
    init_plugin_types();
    init_plugin_registry();
    init_canvas_serializer();
  }
});

// src/commands/index.ts
init_registry();

// src/commands/store.ts
init_registry();
import { atom as atom2 } from "jotai";

// src/commands/store-atoms.ts
import { atom } from "jotai";
import { atomWithStorage } from "jotai/utils";
var inputModeAtom = atom({
  type: "normal"
});
var commandLineVisibleAtom = atom(false);
var commandLineStateAtom = atom({
  phase: "idle"
});
var commandFeedbackAtom = atom(null);
var commandHistoryAtom = atomWithStorage("canvas-command-history", []);
var selectedSuggestionIndexAtom = atom(0);
var pendingInputResolverAtom = atom(null);
var isCommandActiveAtom = atom((get) => {
  const state = get(commandLineStateAtom);
  return state.phase === "collecting" || state.phase === "executing";
});
var currentInputAtom = atom((get) => {
  const state = get(commandLineStateAtom);
  if (state.phase !== "collecting") return null;
  return state.command.inputs[state.inputIndex];
});
var commandProgressAtom = 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 = atom2(null, (get, set) => {
  set(commandLineVisibleAtom, true);
  set(commandLineStateAtom, {
    phase: "searching",
    query: "",
    suggestions: commandRegistry.all()
  });
  set(selectedSuggestionIndexAtom, 0);
});
var closeCommandLineAtom = atom2(null, (get, set) => {
  set(commandLineVisibleAtom, false);
  set(commandLineStateAtom, {
    phase: "idle"
  });
  set(inputModeAtom, {
    type: "normal"
  });
  set(commandFeedbackAtom, null);
  set(pendingInputResolverAtom, null);
});
var updateSearchQueryAtom = atom2(null, (get, set, query) => {
  const suggestions = commandRegistry.search(query);
  set(commandLineStateAtom, {
    phase: "searching",
    query,
    suggestions
  });
  set(selectedSuggestionIndexAtom, 0);
});
var selectCommandAtom = atom2(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 = atom2(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 = atom2(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 = atom2(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 = atom2(null, (get, set, message) => {
  set(commandLineStateAtom, {
    phase: "error",
    message
  });
  set(inputModeAtom, {
    type: "normal"
  });
});
var clearCommandErrorAtom = atom2(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/commands/keyboard.ts
var DEFAULT_SHORTCUTS = {
  openCommandLine: "/",
  closeCommandLine: "Escape",
  clearSelection: "Escape",
  copy: "ctrl+c",
  cut: "ctrl+x",
  paste: "ctrl+v",
  duplicate: "ctrl+d",
  selectAll: "ctrl+a",
  delete: "Delete",
  search: "ctrl+f",
  nextSearchResult: "Enter",
  prevSearchResult: "shift+Enter",
  nextSearchResultAlt: "ctrl+g",
  prevSearchResultAlt: "ctrl+shift+g",
  mergeNodes: "ctrl+m"
};
function useGlobalKeyboard(_options) {
}
function useKeyState(_key) {
  return false;
}

// src/commands/executor.ts
function collectInput(_get, _set, _inputDef, _collected) {
  return Promise.reject(new Error("Interactive input collection is not yet implemented. Pre-fill all inputs via initialInputs."));
}
async function executeCommandInteractive(get, set, command, initialInputs) {
  const collected = {
    ...initialInputs
  };
  for (let i = 0; i < command.inputs.length; i++) {
    const inputDef = command.inputs[i];
    if (collected[inputDef.name] !== void 0) {
      continue;
    }
    if (inputDef.required === false && inputDef.default !== void 0) {
      collected[inputDef.name] = inputDef.default;
      continue;
    }
    set(commandLineStateAtom, {
      phase: "collecting",
      command,
      inputIndex: i,
      collected
    });
    const value = await collectInput(get, set, inputDef, collected);
    collected[inputDef.name] = value;
  }
  set(commandLineStateAtom, {
    phase: "executing",
    command
  });
}
function handlePickedPoint(set, point) {
  set(provideInputAtom, point);
}
function handlePickedNode(set, node) {
  set(provideInputAtom, node);
}
function cancelCommand(set) {
  set(closeCommandLineAtom);
}

// src/commands/CommandProvider.tsx
init_graph_store();
init_selection_store();
init_viewport_store();
init_history_store();
import { c as _c2 } from "react/compiler-runtime";
import React, { createContext, useContext, useEffect, useRef as useRef5 } from "react";
import { useAtomValue as useAtomValue6, useSetAtom as useSetAtom4, useAtom } from "jotai";
import { useStore } from "jotai";
init_registry();

// src/hooks/useLayout.ts
init_graph_store();
init_graph_position();
init_graph_derived();
init_viewport_store();
init_selection_store();
init_layout();
init_layout();
import { c as _c } from "react/compiler-runtime";
import { useAtomValue, useSetAtom } from "jotai";
var useFitToBounds = () => {
  const $ = _c(2);
  const setFitToBounds = 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;
};

// src/hooks/useForceLayout.ts
init_graph_store();
init_graph_position();
init_graph_derived();
init_layout();
init_debug();
import * as d3 from "d3-force";
import { useAtomValue as useAtomValue2, useSetAtom as useSetAtom2 } from "jotai";
import { useRef } from "react";
var debug5 = createDebug("force-layout");
var useForceLayout = (options = {}) => {
  const {
    onPositionsChanged,
    maxIterations = 1e3,
    chargeStrength = -6e3,
    linkStrength = 0.03
  } = options;
  const nodes = useAtomValue2(uiNodesAtom);
  const graph = useAtomValue2(graphAtom);
  const updateNodePosition = useSetAtom2(updateNodePositionAtom);
  const isRunningRef = 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) {
      debug5.warn("Layout already running, ignoring request.");
      return;
    }
    if (nodes.length === 0) {
      debug5.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();
    debug5("Starting simulation...");
    return new Promise((resolve) => {
      let iterations = 0;
      function runSimulationStep() {
        if (iterations >= maxIterations) {
          debug5("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) {
          debug5("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) {
          debug5("Saving %d positions via callback...", positionUpdates.length);
          Promise.resolve(onPositionsChanged(positionUpdates)).then(() => debug5("Positions saved successfully.")).catch((err) => debug5.error("Error saving positions: %O", err));
        }
        debug5("Layout complete.");
        isRunningRef.current = false;
        resolve();
      }
      requestAnimationFrame(runSimulationStep);
    });
  };
  return {
    applyForceLayout,
    isRunning: isRunningRef.current
  };
};

// src/hooks/useTreeLayout.ts
init_graph_store();
init_graph_derived();
import { useAtomValue as useAtomValue4 } from "jotai";
import { useRef as useRef3 } from "react";

// src/hooks/useAnimatedLayout.ts
init_graph_store();
init_graph_position();
init_history_store();
init_debug();
init_reduced_motion_store();
import { useAtomValue as useAtomValue3, useSetAtom as useSetAtom3 } from "jotai";
import { useRef as useRef2 } from "react";
var debug6 = createDebug("animated-layout");
function easeInOutCubic(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 = useAtomValue3(graphAtom);
  const updateNodePosition = useSetAtom3(updateNodePositionAtom);
  const pushHistory = useSetAtom3(pushHistoryAtom);
  const setPositionCounter = useSetAtom3(nodePositionUpdateCounterAtom);
  const reducedMotion = useAtomValue3(prefersReducedMotionAtom);
  const isAnimatingRef = useRef2(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) => debug6.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 = easeInOutCubic(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) => debug6.error("Position change callback failed: %O", err_0));
          }
          resolve();
        }
      }
      requestAnimationFrame(tick);
    });
  };
  return {
    animate,
    isAnimating: isAnimatingRef.current
  };
}

// src/hooks/useTreeLayout.ts
function useTreeLayout(options = {}) {
  const {
    direction = "top-down",
    levelGap = 200,
    nodeGap = 100,
    ...animateOptions
  } = options;
  const graph = useAtomValue4(graphAtom);
  const nodes = useAtomValue4(uiNodesAtom);
  const {
    animate,
    isAnimating
  } = useAnimatedLayout(animateOptions);
  const isRunningRef = useRef3(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
init_graph_store();
init_graph_derived();
import { useAtomValue as useAtomValue5 } from "jotai";
import { useRef as useRef4 } from "react";
function useGridLayout(options = {}) {
  const {
    columns,
    gap = 80,
    ...animateOptions
  } = options;
  const graph = useAtomValue5(graphAtom);
  const nodes = useAtomValue5(uiNodesAtom);
  const {
    animate,
    isAnimating
  } = useAnimatedLayout(animateOptions);
  const isRunningRef = useRef4(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/commands/CommandProvider.tsx
import { jsx as _jsx } from "react/jsx-runtime";
var CommandContextContext = /* @__PURE__ */ createContext(null);
function CommandProvider(t0) {
  const $ = _c2(52);
  const {
    children,
    onCreateNode,
    onUpdateNode,
    onDeleteNode,
    onCreateEdge,
    onDeleteEdge,
    onForceLayoutPersist
  } = t0;
  const store = useStore();
  const currentGraphId = useAtomValue6(currentGraphIdAtom);
  const selectedNodeIds = useAtomValue6(selectedNodeIdsAtom);
  const zoom = useAtomValue6(zoomAtom);
  const pan = useAtomValue6(panAtom);
  const undo = useSetAtom4(undoAtom);
  const redo = useSetAtom4(redoAtom);
  const {
    fitToBounds
  } = useFitToBounds();
  let t1;
  if ($[0] !== onForceLayoutPersist) {
    t1 = onForceLayoutPersist ? async (updates) => {
      await onForceLayoutPersist(updates.map(_temp));
    } : void 0;
    $[0] = onForceLayoutPersist;
    $[1] = t1;
  } else {
    t1 = $[1];
  }
  const persistCallback = t1;
  let t2;
  if ($[2] !== persistCallback) {
    t2 = {
      onPositionsChanged: persistCallback
    };
    $[2] = persistCallback;
    $[3] = t2;
  } else {
    t2 = $[3];
  }
  const {
    applyForceLayout
  } = useForceLayout(t2);
  let t3;
  if ($[4] !== persistCallback) {
    t3 = {
      onPositionsChanged: persistCallback
    };
    $[4] = persistCallback;
    $[5] = t3;
  } else {
    t3 = $[5];
  }
  const {
    applyLayout: applyTreeLayoutTopDown
  } = useTreeLayout(t3);
  let t4;
  if ($[6] !== persistCallback) {
    t4 = {
      direction: "left-right",
      onPositionsChanged: persistCallback
    };
    $[6] = persistCallback;
    $[7] = t4;
  } else {
    t4 = $[7];
  }
  const {
    applyLayout: applyTreeLayoutLeftRight
  } = useTreeLayout(t4);
  let t5;
  if ($[8] !== persistCallback) {
    t5 = {
      onPositionsChanged: persistCallback
    };
    $[8] = persistCallback;
    $[9] = t5;
  } else {
    t5 = $[9];
  }
  const {
    applyLayout: applyGridLayoutDefault
  } = useGridLayout(t5);
  const closeCommandLine = useSetAtom4(closeCommandLineAtom);
  const setCommandError = useSetAtom4(setCommandErrorAtom);
  let t6;
  if ($[10] !== applyForceLayout || $[11] !== applyGridLayoutDefault || $[12] !== applyTreeLayoutLeftRight || $[13] !== applyTreeLayoutTopDown || $[14] !== currentGraphId || $[15] !== fitToBounds || $[16] !== onCreateEdge || $[17] !== onCreateNode || $[18] !== onDeleteEdge || $[19] !== onDeleteNode || $[20] !== onUpdateNode || $[21] !== pan || $[22] !== redo || $[23] !== selectedNodeIds || $[24] !== store.get || $[25] !== store.set || $[26] !== undo || $[27] !== zoom) {
    t6 = () => ({
      get: store.get,
      set: store.set,
      currentGraphId,
      selectedNodeIds,
      viewport: {
        zoom,
        pan
      },
      mutations: {
        createNode: async (payload) => {
          if (!onCreateNode) {
            throw new Error("onCreateNode callback not provided to CommandProvider");
          }
          return onCreateNode(payload);
        },
        updateNode: async (nodeId, updates_0) => {
          if (!onUpdateNode) {
            throw new Error("onUpdateNode callback not provided to CommandProvider");
          }
          return onUpdateNode(nodeId, updates_0);
        },
        deleteNode: async (nodeId_0) => {
          if (!onDeleteNode) {
            throw new Error("onDeleteNode callback not provided to CommandProvider");
          }
          return onDeleteNode(nodeId_0);
        },
        createEdge: async (payload_0) => {
          if (!onCreateEdge) {
            throw new Error("onCreateEdge callback not provided to CommandProvider");
          }
          return onCreateEdge(payload_0);
        },
        deleteEdge: async (edgeId) => {
          if (!onDeleteEdge) {
            throw new Error("onDeleteEdge callback not provided to CommandProvider");
          }
          return onDeleteEdge(edgeId);
        }
      },
      layout: {
        fitToBounds: (mode, padding) => {
          const fitMode = mode === "graph" ? FitToBoundsMode.Graph : FitToBoundsMode.Selection;
          fitToBounds(fitMode, padding);
        },
        applyForceLayout,
        applyTreeLayout: async (opts) => {
          if (opts?.direction === "left-right") {
            await applyTreeLayoutLeftRight();
          } else {
            await applyTreeLayoutTopDown();
          }
        },
        applyGridLayout: async () => {
          await applyGridLayoutDefault();
        }
      },
      history: {
        undo,
        redo
      }
    });
    $[10] = applyForceLayout;
    $[11] = applyGridLayoutDefault;
    $[12] = applyTreeLayoutLeftRight;
    $[13] = applyTreeLayoutTopDown;
    $[14] = currentGraphId;
    $[15] = fitToBounds;
    $[16] = onCreateEdge;
    $[17] = onCreateNode;
    $[18] = onDeleteEdge;
    $[19] = onDeleteNode;
    $[20] = onUpdateNode;
    $[21] = pan;
    $[22] = redo;
    $[23] = selectedNodeIds;
    $[24] = store.get;
    $[25] = store.set;
    $[26] = undo;
    $[27] = zoom;
    $[28] = t6;
  } else {
    t6 = $[28];
  }
  const getContext = t6;
  let t7;
  if ($[29] !== closeCommandLine || $[30] !== getContext || $[31] !== setCommandError) {
    t7 = async (commandName, inputs) => {
      const command = commandRegistry.get(commandName);
      if (!command) {
        throw new Error(`Unknown command: ${commandName}`);
      }
      for (const inputDef of command.inputs) {
        if (inputDef.required !== false && inputs[inputDef.name] === void 0) {
          throw new Error(`Missing required input: ${inputDef.name}`);
        }
      }
      const ctx = getContext();
      ;
      try {
        await command.execute(inputs, ctx);
        closeCommandLine();
      } catch (t82) {
        const error = t82;
        const message = error instanceof Error ? error.message : "Command execution failed";
        setCommandError(message);
        throw error;
      }
    };
    $[29] = closeCommandLine;
    $[30] = getContext;
    $[31] = setCommandError;
    $[32] = t7;
  } else {
    t7 = $[32];
  }
  const executeCommand = t7;
  const hasCommand = _temp2;
  const [commandState, setCommandState] = useAtom(commandLineStateAtom);
  const isExecutingRef = useRef5(false);
  let t8;
  if ($[33] !== commandState || $[34] !== getContext || $[35] !== setCommandError || $[36] !== setCommandState || $[37] !== store) {
    t8 = () => {
      if (commandState.phase === "executing" && !isExecutingRef.current) {
        const {
          command: command_0
        } = commandState;
        if (command_0.inputs.length === 0) {
          isExecutingRef.current = true;
          const ctx_0 = getContext();
          command_0.execute({}, ctx_0).then(() => {
            setCommandState({
              phase: "searching",
              query: "",
              suggestions: commandRegistry.all()
            });
          }).catch((error_0) => {
            const message_0 = error_0 instanceof Error ? error_0.message : "Command execution failed";
            setCommandError(message_0);
          }).finally(() => {
            isExecutingRef.current = false;
          });
        }
        return;
      }
      if (commandState.phase !== "collecting") {
        isExecutingRef.current = false;
        return;
      }
      const {
        command: command_1,
        inputIndex,
        collected
      } = commandState;
      const lastInputIndex = command_1.inputs.length - 1;
      const lastInput = command_1.inputs[lastInputIndex];
      if (inputIndex === lastInputIndex && collected[lastInput.name] !== void 0 && !isExecutingRef.current) {
        isExecutingRef.current = true;
        setCommandState({
          phase: "executing",
          command: command_1
        });
        const ctx_1 = getContext();
        command_1.execute(collected, ctx_1).then(() => {
          setCommandState({
            phase: "searching",
            query: "",
            suggestions: commandRegistry.all()
          });
          store.set(inputModeAtom, {
            type: "normal"
          });
          store.set(commandFeedbackAtom, null);
        }).catch((error_1) => {
          const message_1 = error_1 instanceof Error ? error_1.message : "Command execution failed";
          setCommandError(message_1);
        }).finally(() => {
          isExecutingRef.current = false;
        });
      }
    };
    $[33] = commandState;
    $[34] = getContext;
    $[35] = setCommandError;
    $[36] = setCommandState;
    $[37] = store;
    $[38] = t8;
  } else {
    t8 = $[38];
  }
  let t9;
  if ($[39] !== closeCommandLine || $[40] !== commandState || $[41] !== getContext || $[42] !== setCommandError || $[43] !== setCommandState || $[44] !== store) {
    t9 = [commandState, getContext, closeCommandLine, setCommandError, setCommandState, store];
    $[39] = closeCommandLine;
    $[40] = commandState;
    $[41] = getContext;
    $[42] = setCommandError;
    $[43] = setCommandState;
    $[44] = store;
    $[45] = t9;
  } else {
    t9 = $[45];
  }
  useEffect(t8, t9);
  let t10;
  if ($[46] !== executeCommand || $[47] !== getContext) {
    t10 = {
      executeCommand,
      getContext,
      hasCommand
    };
    $[46] = executeCommand;
    $[47] = getContext;
    $[48] = t10;
  } else {
    t10 = $[48];
  }
  const value = t10;
  let t11;
  if ($[49] !== children || $[50] !== value) {
    t11 = /* @__PURE__ */ _jsx(CommandContextContext, {
      value,
      children
    });
    $[49] = children;
    $[50] = value;
    $[51] = t11;
  } else {
    t11 = $[51];
  }
  return t11;
}
function _temp2(name) {
  return commandRegistry.has(name);
}
function _temp(u) {
  return {
    nodeId: u.nodeId,
    x: u.position.x,
    y: u.position.y
  };
}
function useCommandContext() {
  const context = useContext(CommandContextContext);
  if (!context) {
    throw new Error("useCommandContext must be used within a CommandProvider");
  }
  return context;
}
function useExecuteCommand() {
  const {
    executeCommand
  } = useCommandContext();
  return executeCommand;
}

// src/commands/builtins/viewport-commands.ts
init_registry();
var fitToViewCommand = {
  name: "fitToView",
  description: "Fit all nodes in the viewport",
  aliases: ["fit", "fitAll"],
  category: "viewport",
  inputs: [],
  execute: async (_inputs, ctx) => {
    ctx.layout.fitToBounds("graph");
  }
};
var fitSelectionCommand = {
  name: "fitSelection",
  description: "Fit selected nodes in the viewport",
  aliases: ["fitSel"],
  category: "viewport",
  inputs: [],
  execute: async (_inputs, ctx) => {
    if (ctx.selectedNodeIds.size === 0) {
      throw new Error("No nodes selected");
    }
    ctx.layout.fitToBounds("selection");
  }
};
var resetViewportCommand = {
  name: "resetViewport",
  description: "Reset zoom and pan to default",
  aliases: ["reset", "home"],
  category: "viewport",
  inputs: [],
  execute: async (_inputs, ctx) => {
    const {
      resetViewportAtom: resetViewportAtom2
    } = await Promise.resolve().then(() => (init_viewport_store(), viewport_store_exports));
    ctx.set(resetViewportAtom2);
  }
};
var zoomInCommand = {
  name: "zoomIn",
  description: "Zoom in on the canvas",
  aliases: ["+", "in"],
  category: "viewport",
  inputs: [],
  execute: async (_inputs, ctx) => {
    const {
      zoomAtom: zoomAtom2,
      setZoomAtom: setZoomAtom2
    } = await Promise.resolve().then(() => (init_viewport_store(), viewport_store_exports));
    const currentZoom = ctx.get(zoomAtom2);
    ctx.set(setZoomAtom2, {
      zoom: Math.min(5, currentZoom * 1.25)
    });
  }
};
var zoomOutCommand = {
  name: "zoomOut",
  description: "Zoom out on the canvas",
  aliases: ["-", "out"],
  category: "viewport",
  inputs: [],
  execute: async (_inputs, ctx) => {
    const {
      zoomAtom: zoomAtom2,
      setZoomAtom: setZoomAtom2
    } = await Promise.resolve().then(() => (init_viewport_store(), viewport_store_exports));
    const currentZoom = ctx.get(zoomAtom2);
    ctx.set(setZoomAtom2, {
      zoom: Math.max(0.1, currentZoom / 1.25)
    });
  }
};
function registerViewportCommands() {
  registerCommand(fitToViewCommand);
  registerCommand(fitSelectionCommand);
  registerCommand(resetViewportCommand);
  registerCommand(zoomInCommand);
  registerCommand(zoomOutCommand);
}

// src/commands/builtins/selection-commands.ts
init_registry();
var selectAllCommand = {
  name: "selectAll",
  description: "Select all nodes in the graph",
  aliases: ["all"],
  category: "selection",
  inputs: [],
  execute: async (_inputs, ctx) => {
    const {
      nodeKeysAtom: nodeKeysAtom2,
      selectedNodeIdsAtom: selectedNodeIdsAtom2
    } = await Promise.resolve().then(() => (init_core(), core_exports));
    const nodeKeys = ctx.get(nodeKeysAtom2);
    ctx.set(selectedNodeIdsAtom2, new Set(nodeKeys));
  }
};
var clearSelectionCommand = {
  name: "clearSelection",
  description: "Clear all selection",
  aliases: ["deselect", "clear"],
  category: "selection",
  inputs: [],
  execute: async (_inputs, ctx) => {
    const {
      clearSelectionAtom: clearSelectionAtom2
    } = await Promise.resolve().then(() => (init_core(), core_exports));
    ctx.set(clearSelectionAtom2);
  }
};
var invertSelectionCommand = {
  name: "invertSelection",
  description: "Invert the current selection",
  aliases: ["invert"],
  category: "selection",
  inputs: [],
  execute: async (_inputs, ctx) => {
    const {
      nodeKeysAtom: nodeKeysAtom2,
      selectedNodeIdsAtom: selectedNodeIdsAtom2
    } = await Promise.resolve().then(() => (init_core(), core_exports));
    const allNodeKeys = ctx.get(nodeKeysAtom2);
    const currentSelection = ctx.selectedNodeIds;
    const invertedSelection = allNodeKeys.filter((id) => !currentSelection.has(id));
    ctx.set(selectedNodeIdsAtom2, new Set(invertedSelection));
  }
};
function registerSelectionCommands() {
  registerCommand(selectAllCommand);
  registerCommand(clearSelectionCommand);
  registerCommand(invertSelectionCommand);
}

// src/commands/builtins/history-commands.ts
init_registry();
var undoCommand = {
  name: "undo",
  description: "Undo the last action",
  aliases: ["z"],
  category: "history",
  inputs: [],
  execute: async (_inputs, ctx) => {
    ctx.history.undo();
  }
};
var redoCommand = {
  name: "redo",
  description: "Redo the last undone action",
  aliases: ["y"],
  category: "history",
  inputs: [],
  execute: async (_inputs, ctx) => {
    ctx.history.redo();
  }
};
function registerHistoryCommands() {
  registerCommand(undoCommand);
  registerCommand(redoCommand);
}

// src/commands/builtins/layout-commands.ts
init_registry();
var forceLayoutCommand = {
  name: "forceLayout",
  description: "Apply force-directed layout to all nodes",
  aliases: ["force", "autoLayout"],
  category: "layout",
  inputs: [],
  execute: async (_inputs, ctx) => {
    await ctx.layout.applyForceLayout();
  }
};
var treeLayoutCommand = {
  name: "treeLayout",
  description: "Arrange nodes in a hierarchical tree (top-down)",
  aliases: ["tree"],
  category: "layout",
  inputs: [],
  execute: async (_inputs, ctx) => {
    await ctx.layout.applyTreeLayout();
  }
};
var gridLayoutCommand = {
  name: "gridLayout",
  description: "Arrange nodes in a uniform grid",
  aliases: ["grid"],
  category: "layout",
  inputs: [],
  execute: async (_inputs, ctx) => {
    await ctx.layout.applyGridLayout();
  }
};
var horizontalLayoutCommand = {
  name: "horizontalLayout",
  description: "Arrange nodes in a horizontal tree (left-to-right)",
  aliases: ["horizontal", "hLayout"],
  category: "layout",
  inputs: [],
  execute: async (_inputs, ctx) => {
    await ctx.layout.applyTreeLayout({
      direction: "left-right"
    });
  }
};
function registerLayoutCommands() {
  registerCommand(forceLayoutCommand);
  registerCommand(treeLayoutCommand);
  registerCommand(gridLayoutCommand);
  registerCommand(horizontalLayoutCommand);
}

// src/commands/builtins/clipboard-commands.ts
init_registry();
var copyCommand = {
  name: "copy",
  description: "Copy selected nodes to clipboard",
  aliases: ["cp"],
  category: "selection",
  inputs: [],
  execute: async (_inputs, ctx) => {
    const {
      copyToClipboardAtom: copyToClipboardAtom2,
      hasClipboardContentAtom: hasClipboardContentAtom2
    } = await Promise.resolve().then(() => (init_clipboard_store(), clipboard_store_exports));
    if (ctx.selectedNodeIds.size === 0) {
      throw new Error("No nodes selected to copy");
    }
    ctx.set(copyToClipboardAtom2, Array.from(ctx.selectedNodeIds));
    const hasContent = ctx.get(hasClipboardContentAtom2);
    if (!hasContent) {
      throw new Error("Failed to copy nodes");
    }
  }
};
var cutCommand = {
  name: "cut",
  description: "Cut selected nodes (copy to clipboard)",
  aliases: ["x"],
  category: "selection",
  inputs: [],
  execute: async (_inputs, ctx) => {
    const {
      copyToClipboardAtom: copyToClipboardAtom2,
      hasClipboardContentAtom: hasClipboardContentAtom2
    } = await Promise.resolve().then(() => (init_clipboard_store(), clipboard_store_exports));
    if (ctx.selectedNodeIds.size === 0) {
      throw new Error("No nodes selected to cut");
    }
    ctx.set(copyToClipboardAtom2, Array.from(ctx.selectedNodeIds));
    const hasContent = ctx.get(hasClipboardContentAtom2);
    if (!hasContent) {
      throw new Error("Failed to cut nodes");
    }
    for (const nodeId of ctx.selectedNodeIds) {
      await ctx.mutations.deleteNode(nodeId);
    }
  }
};
var pasteCommand = {
  name: "paste",
  description: "Paste nodes from clipboard",
  aliases: ["v"],
  category: "selection",
  inputs: [{
    name: "position",
    type: "point",
    prompt: "Click to paste at position (or Enter for default offset)",
    required: false
  }],
  execute: async (inputs, ctx) => {
    const {
      pasteFromClipboardAtom: pasteFromClipboardAtom2,
      clipboardAtom: clipboardAtom2,
      PASTE_OFFSET: PASTE_OFFSET2
    } = await Promise.resolve().then(() => (init_clipboard_store(), clipboard_store_exports));
    const clipboard = ctx.get(clipboardAtom2);
    if (!clipboard || clipboard.nodes.length === 0) {
      throw new Error("Clipboard is empty");
    }
    let offset = PASTE_OFFSET2;
    if (inputs.position) {
      const pos = inputs.position;
      offset = {
        x: pos.x - clipboard.bounds.minX,
        y: pos.y - clipboard.bounds.minY
      };
    }
    const newNodeIds = ctx.set(pasteFromClipboardAtom2, offset);
    if (!newNodeIds || newNodeIds.length === 0) {
      throw new Error("Failed to paste nodes");
    }
  },
  feedback: (collected, currentInput) => {
    if (currentInput.name === "position") {
      return {
        crosshair: true
      };
    }
    return null;
  }
};
var duplicateCommand = {
  name: "duplicate",
  description: "Duplicate selected nodes",
  aliases: ["d", "dup"],
  category: "selection",
  inputs: [],
  execute: async (_inputs, ctx) => {
    const {
      duplicateSelectionAtom: duplicateSelectionAtom2
    } = await Promise.resolve().then(() => (init_clipboard_store(), clipboard_store_exports));
    if (ctx.selectedNodeIds.size === 0) {
      throw new Error("No nodes selected to duplicate");
    }
    const newNodeIds = ctx.set(duplicateSelectionAtom2);
    if (!newNodeIds || newNodeIds.length === 0) {
      throw new Error("Failed to duplicate nodes");
    }
  }
};
var deleteSelectedCommand = {
  name: "deleteSelected",
  description: "Delete selected nodes",
  aliases: ["del", "delete", "remove"],
  category: "selection",
  inputs: [],
  execute: async (_inputs, ctx) => {
    if (ctx.selectedNodeIds.size === 0) {
      throw new Error("No nodes selected to delete");
    }
    const {
      pushHistoryAtom: pushHistoryAtom2
    } = await Promise.resolve().then(() => (init_core(), core_exports));
    ctx.set(pushHistoryAtom2, "Delete nodes");
    for (const nodeId of ctx.selectedNodeIds) {
      await ctx.mutations.deleteNode(nodeId);
    }
  }
};
function registerClipboardCommands() {
  registerCommand(copyCommand);
  registerCommand(cutCommand);
  registerCommand(pasteCommand);
  registerCommand(duplicateCommand);
  registerCommand(deleteSelectedCommand);
}

// src/commands/builtins/group-commands.ts
init_registry();
var groupNodesCommand = {
  name: "groupNodes",
  description: "Group selected nodes into a container",
  aliases: ["group"],
  category: "nodes",
  inputs: [{
    name: "label",
    type: "text",
    prompt: "Group label:",
    required: false,
    default: "Group"
  }],
  execute: async (inputs, ctx) => {
    if (ctx.selectedNodeIds.size < 2) return;
    const {
      addNodeToLocalGraphAtom: addNodeToLocalGraphAtom2
    } = await Promise.resolve().then(() => (init_graph_mutations(), graph_mutations_exports));
    const {
      groupSelectedNodesAtom: groupSelectedNodesAtom2
    } = await Promise.resolve().then(() => (init_group_store(), group_store_exports));
    const label = inputs.label || "Group";
    const groupNodeId = crypto.randomUUID();
    ctx.set(addNodeToLocalGraphAtom2, {
      id: groupNodeId,
      graph_id: ctx.currentGraphId || "",
      label,
      node_type: "group",
      configuration: null,
      ui_properties: {
        x: 0,
        y: 0,
        width: 500,
        height: 500,
        size: 15,
        zIndex: 0
      },
      data: null,
      created_at: (/* @__PURE__ */ new Date()).toISOString(),
      updated_at: (/* @__PURE__ */ new Date()).toISOString()
    });
    ctx.set(groupSelectedNodesAtom2, {
      nodeIds: Array.from(ctx.selectedNodeIds),
      groupNodeId
    });
  }
};
var ungroupNodesCommand = {
  name: "ungroupNodes",
  description: "Remove grouping from a group node",
  aliases: ["ungroup"],
  category: "nodes",
  inputs: [{
    name: "groupNode",
    type: "node",
    prompt: "Pick a group node to ungroup:"
  }],
  execute: async (inputs, ctx) => {
    const groupId = inputs.groupNode;
    if (!groupId) return;
    const {
      ungroupNodesAtom: ungroupNodesAtom2
    } = await Promise.resolve().then(() => (init_group_store(), group_store_exports));
    ctx.set(ungroupNodesAtom2, groupId);
  }
};
var collapseGroupCommand = {
  name: "collapseGroup",
  description: "Collapse a group node to hide its children",
  aliases: ["collapse"],
  category: "nodes",
  inputs: [{
    name: "groupNode",
    type: "node",
    prompt: "Pick a group node to collapse:"
  }],
  execute: async (inputs, ctx) => {
    const groupId = inputs.groupNode;
    if (!groupId) return;
    const {
      collapseGroupAtom: collapseGroupAtom2
    } = await Promise.resolve().then(() => (init_group_store(), group_store_exports));
    ctx.set(collapseGroupAtom2, groupId);
  }
};
var expandGroupCommand = {
  name: "expandGroup",
  description: "Expand a collapsed group node",
  aliases: ["expand"],
  category: "nodes",
  inputs: [{
    name: "groupNode",
    type: "node",
    prompt: "Pick a group node to expand:"
  }],
  execute: async (inputs, ctx) => {
    const groupId = inputs.groupNode;
    if (!groupId) return;
    const {
      expandGroupAtom: expandGroupAtom2
    } = await Promise.resolve().then(() => (init_group_store(), group_store_exports));
    ctx.set(expandGroupAtom2, groupId);
  }
};
function registerGroupCommands() {
  registerCommand(groupNodesCommand);
  registerCommand(ungroupNodesCommand);
  registerCommand(collapseGroupCommand);
  registerCommand(expandGroupCommand);
}

// src/commands/builtins/search-commands.ts
init_registry();
var searchNodesCommand = {
  name: "searchNodes",
  description: "Search nodes by label, type, or ID",
  aliases: ["find", "search"],
  category: "selection",
  inputs: [{
    name: "query",
    type: "text",
    prompt: "Search:",
    required: true
  }],
  execute: async (inputs, ctx) => {
    const query = inputs.query;
    if (!query) return;
    const {
      setSearchQueryAtom: setSearchQueryAtom2
    } = await Promise.resolve().then(() => (init_search_store(), search_store_exports));
    ctx.set(setSearchQueryAtom2, query);
  }
};
var clearSearchCommand = {
  name: "clearSearch",
  description: "Clear the active search filter",
  aliases: ["clearsearch"],
  category: "selection",
  inputs: [],
  execute: async (_inputs, ctx) => {
    const {
      clearSearchAtom: clearSearchAtom2
    } = await Promise.resolve().then(() => (init_search_store(), search_store_exports));
    ctx.set(clearSearchAtom2);
  }
};
function registerSearchCommands() {
  registerCommand(searchNodesCommand);
  registerCommand(clearSearchCommand);
}

// src/commands/builtins/merge-commands.ts
init_registry();
var mergeNodesCommand = {
  name: "mergeNodes",
  description: "Merge selected nodes into one (first selected survives)",
  aliases: ["merge"],
  category: "nodes",
  inputs: [],
  execute: async (_inputs, ctx) => {
    if (ctx.selectedNodeIds.size < 2) return;
    const {
      mergeNodesAtom: mergeNodesAtom2
    } = await Promise.resolve().then(() => (init_graph_mutations(), graph_mutations_exports));
    const {
      clearSelectionAtom: clearSelectionAtom2,
      addNodesToSelectionAtom: addNodesToSelectionAtom2
    } = await Promise.resolve().then(() => (init_selection_store(), selection_store_exports));
    const nodeIds = Array.from(ctx.selectedNodeIds);
    ctx.set(mergeNodesAtom2, {
      nodeIds
    });
    ctx.set(clearSelectionAtom2);
    ctx.set(addNodesToSelectionAtom2, [nodeIds[0]]);
  }
};
function registerMergeCommands() {
  registerCommand(mergeNodesCommand);
}

// src/commands/builtins/serialization-commands.ts
init_registry();
var exportCanvasCommand = {
  name: "exportCanvas",
  description: "Export the current canvas to a JSON snapshot (copies to clipboard)",
  aliases: ["export"],
  category: "custom",
  inputs: [],
  execute: async (_inputs, ctx) => {
    const {
      exportGraph: exportGraph2
    } = await Promise.resolve().then(() => (init_canvas_serializer(), canvas_serializer_exports));
    const {
      showToastAtom: showToastAtom2
    } = await Promise.resolve().then(() => (init_toast_store(), toast_store_exports));
    const {
      graphAtom: graphAtom2
    } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
    const graph = ctx.get(graphAtom2);
    const nodeCount = graph.order;
    const edgeCount = graph.size;
    if (nodeCount === 0) {
      throw new Error("Canvas is empty \u2014 nothing to export");
    }
    const store = {
      get: ctx.get,
      set: ctx.set
    };
    const snapshot = exportGraph2(store);
    const json = JSON.stringify(snapshot, null, 2);
    await navigator.clipboard.writeText(json);
    ctx.set(showToastAtom2, `Exported ${nodeCount} nodes, ${edgeCount} edges to clipboard`);
  }
};
var importCanvasCommand = {
  name: "importCanvas",
  description: "Import a canvas from a JSON snapshot (reads from clipboard)",
  aliases: ["import"],
  category: "custom",
  inputs: [],
  execute: async (_inputs, ctx) => {
    const {
      importGraph: importGraph2,
      validateSnapshot: validateSnapshot2
    } = await Promise.resolve().then(() => (init_canvas_serializer(), canvas_serializer_exports));
    const {
      showToastAtom: showToastAtom2
    } = await Promise.resolve().then(() => (init_toast_store(), toast_store_exports));
    const {
      pushHistoryAtom: pushHistoryAtom2
    } = await Promise.resolve().then(() => (init_history_store(), history_store_exports));
    let json;
    try {
      json = await navigator.clipboard.readText();
    } catch {
      throw new Error("Could not read clipboard \u2014 paste the snapshot JSON manually");
    }
    let data;
    try {
      data = JSON.parse(json);
    } catch {
      throw new Error("Clipboard does not contain valid JSON");
    }
    const result = validateSnapshot2(data);
    if (!result.valid) {
      throw new Error(`Invalid snapshot: ${result.errors[0]}`);
    }
    ctx.set(pushHistoryAtom2, "Import canvas");
    const store = {
      get: ctx.get,
      set: ctx.set
    };
    const snapshot = data;
    importGraph2(store, snapshot);
    ctx.set(showToastAtom2, `Imported ${snapshot.nodes.length} nodes, ${snapshot.edges.length} edges`);
  }
};
function registerSerializationCommands() {
  registerCommand(exportCanvasCommand);
  registerCommand(importCanvasCommand);
}

// src/commands/builtins/index.ts
function registerBuiltinCommands() {
  registerViewportCommands();
  registerSelectionCommands();
  registerHistoryCommands();
  registerLayoutCommands();
  registerClipboardCommands();
  registerGroupCommands();
  registerSearchCommands();
  registerMergeCommands();
  registerSerializationCommands();
}
export {
  CommandProvider,
  DEFAULT_SHORTCUTS,
  cancelCommand,
  clearCommandErrorAtom,
  clearSelectionCommand,
  closeCommandLineAtom,
  collectInput,
  commandFeedbackAtom,
  commandHistoryAtom,
  commandLineStateAtom,
  commandLineVisibleAtom,
  commandProgressAtom,
  commandRegistry,
  copyCommand,
  currentInputAtom,
  cutCommand,
  deleteSelectedCommand,
  duplicateCommand,
  executeCommandInteractive,
  fitSelectionCommand,
  fitToViewCommand,
  forceLayoutCommand,
  goBackInputAtom,
  handlePickedNode,
  handlePickedPoint,
  invertSelectionCommand,
  isCommandActiveAtom,
  openCommandLineAtom,
  pasteCommand,
  redoCommand,
  registerBuiltinCommands,
  registerClipboardCommands,
  registerCommand,
  registerHistoryCommands,
  registerLayoutCommands,
  registerSelectionCommands,
  registerViewportCommands,
  resetViewportCommand,
  selectAllCommand,
  selectCommandAtom,
  selectedSuggestionIndexAtom,
  setCommandErrorAtom,
  skipInputAtom,
  undoCommand,
  updateSearchQueryAtom,
  useCommandContext,
  useExecuteCommand,
  useGlobalKeyboard,
  useKeyState,
  zoomInCommand,
  zoomOutCommand
};
//# sourceMappingURL=index.mjs.map