// src/utils/debug.ts
import debugFactory from "debug";
var NAMESPACE = "canvas";
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 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/utils/gesture-configs.ts
var fingerGestureConfig = {
  eventOptions: {
    passive: false,
    capture: false
  },
  drag: {
    pointer: {
      touch: true,
      keys: false,
      capture: false,
      buttons: -1
    },
    filterTaps: true,
    tapsThreshold: 10,
    // Was 3 — too strict for fingers
    threshold: 10
    // Was 3 — needs larger dead zone
  }
};
var pencilGestureConfig = {
  eventOptions: {
    passive: false,
    capture: false
  },
  drag: {
    pointer: {
      touch: true,
      keys: false,
      capture: false,
      buttons: -1
    },
    filterTaps: true,
    tapsThreshold: 3,
    threshold: 2
    // Very precise — small dead zone
  }
};
var mouseGestureConfig = {
  eventOptions: {
    passive: false,
    capture: false
  },
  drag: {
    pointer: {
      touch: true,
      keys: false,
      capture: false,
      buttons: -1
    },
    filterTaps: true,
    tapsThreshold: 5,
    // Was 3
    threshold: 3
  }
};
function getNodeGestureConfig(source) {
  switch (source) {
    case "finger":
      return fingerGestureConfig;
    case "pencil":
      return pencilGestureConfig;
    case "mouse":
      return mouseGestureConfig;
  }
}
function getViewportGestureConfig(source) {
  const base = getNodeGestureConfig(source);
  return {
    ...base,
    eventOptions: {
      passive: false
    },
    pinch: {
      pointer: {
        touch: true
      }
    },
    wheel: {
      eventOptions: {
        passive: false
      }
    }
  };
}

// src/utils/mutation-queue.ts
var pendingNodeMutations = /* @__PURE__ */ new Map();
function getPendingState(nodeId) {
  let state = pendingNodeMutations.get(nodeId);
  if (!state) {
    state = {
      inFlight: false,
      queuedPosition: null,
      queuedUiProperties: null,
      graphId: null
    };
    pendingNodeMutations.set(nodeId, state);
  }
  return state;
}
function clearPendingState(nodeId) {
  pendingNodeMutations.delete(nodeId);
}
function hasPendingMutations() {
  for (const state of pendingNodeMutations.values()) {
    if (state.inFlight || state.queuedPosition !== null || state.queuedUiProperties !== null) {
      return true;
    }
  }
  return false;
}

// src/utils/component-registry.tsx
import { c as _c } from "react/compiler-runtime";
import React from "react";
import { jsx as _jsx } from "react/jsx-runtime";
function createComponentRegistry({
  registry,
  fallback,
  getDisplayName = (type) => `Memoized${type}Component`
}) {
  const memoizedCache = /* @__PURE__ */ new Map();
  const getComponent = (type) => {
    if (memoizedCache.has(type)) {
      return memoizedCache.get(type);
    }
    const LazyComponent = registry[type];
    if (!LazyComponent) {
      return fallback || null;
    }
    function RegistryComponent(props) {
      const $ = _c(2);
      let t0;
      if ($[0] !== props) {
        t0 = /* @__PURE__ */ _jsx(LazyComponent, {
          ...props
        });
        $[0] = props;
        $[1] = t0;
      } else {
        t0 = $[1];
      }
      return t0;
    }
    RegistryComponent.displayName = getDisplayName(type);
    memoizedCache.set(type, RegistryComponent);
    return RegistryComponent;
  };
  const getTypes = () => Object.keys(registry);
  const hasType = (type) => type in registry;
  return {
    getComponent,
    getTypes,
    hasType,
    Fallback: fallback || null
  };
}
function createFallbackComponent(getMessage) {
  return function FallbackComponent(props) {
    const message = getMessage ? getMessage(props) : `Unsupported type: "${props.nodeData?.dbData?.node_type || "Unknown"}"`;
    return /* @__PURE__ */ _jsx("div", {
      style: {
        padding: "10px",
        color: "orange"
      },
      children: /* @__PURE__ */ _jsx("p", {
        children: message
      })
    });
  };
}

// src/utils/edge-path-calculators.ts
var bezierHorizontal = ({
  x1,
  y1,
  x2,
  y2
}) => {
  const dist = Math.abs(x2 - x1);
  const offset = Math.max(dist * 0.5, 50);
  const cp1x = x1 + offset;
  const cp1y = y1;
  const cp2x = x2 - offset;
  const cp2y = y2;
  const path = `M ${x1} ${y1} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${x2} ${y2}`;
  const labelX = (x1 + x2) / 2;
  const labelY = (y1 + y2) / 2;
  return {
    path,
    labelX,
    labelY
  };
};
var bezierVertical = ({
  x1,
  y1,
  x2,
  y2
}) => {
  const dist = Math.abs(y2 - y1);
  const offset = Math.max(dist * 0.5, 50);
  const cp1x = x1;
  const cp1y = y1 + offset;
  const cp2x = x2;
  const cp2y = y2 - offset;
  const path = `M ${x1} ${y1} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${x2} ${y2}`;
  const labelX = (x1 + x2) / 2;
  const labelY = (y1 + y2) / 2;
  return {
    path,
    labelX,
    labelY
  };
};
var bezierSmart = (input) => {
  const {
    x1,
    y1,
    x2,
    y2
  } = input;
  const dx = Math.abs(x2 - x1);
  const dy = Math.abs(y2 - y1);
  return dx > dy ? bezierHorizontal(input) : bezierVertical(input);
};
var straight = ({
  x1,
  y1,
  x2,
  y2
}) => {
  const path = `M ${x1} ${y1} L ${x2} ${y2}`;
  const labelX = (x1 + x2) / 2;
  const labelY = (y1 + y2) / 2;
  return {
    path,
    labelX,
    labelY
  };
};
var stepHorizontal = ({
  x1,
  y1,
  x2,
  y2
}) => {
  const midX = (x1 + x2) / 2;
  const path = `M ${x1} ${y1} L ${midX} ${y1} L ${midX} ${y2} L ${x2} ${y2}`;
  const labelX = midX;
  const labelY = (y1 + y2) / 2;
  return {
    path,
    labelX,
    labelY
  };
};
var stepVertical = ({
  x1,
  y1,
  x2,
  y2
}) => {
  const midY = (y1 + y2) / 2;
  const path = `M ${x1} ${y1} L ${x1} ${midY} L ${x2} ${midY} L ${x2} ${y2}`;
  const labelX = (x1 + x2) / 2;
  const labelY = midY;
  return {
    path,
    labelX,
    labelY
  };
};
var stepSmart = (input) => {
  const {
    x1,
    y1,
    x2,
    y2
  } = input;
  const dx = Math.abs(x2 - x1);
  const dy = Math.abs(y2 - y1);
  return dx > dy ? stepHorizontal(input) : stepVertical(input);
};
var smoothStep = ({
  x1,
  y1,
  x2,
  y2
}) => {
  const midX = (x1 + x2) / 2;
  const radius = Math.min(20, Math.abs(x2 - x1) / 4, Math.abs(y2 - y1) / 2);
  if (radius < 5 || Math.abs(y2 - y1) < radius * 2) {
    return stepHorizontal({
      x1,
      y1,
      x2,
      y2,
      sourceWidth: 0,
      sourceHeight: 0,
      targetWidth: 0,
      targetHeight: 0
    });
  }
  const yDir = y2 > y1 ? 1 : -1;
  const path = `
    M ${x1} ${y1}
    L ${midX - radius} ${y1}
    Q ${midX} ${y1}, ${midX} ${y1 + radius * yDir}
    L ${midX} ${y2 - radius * yDir}
    Q ${midX} ${y2}, ${midX + radius} ${y2}
    L ${x2} ${y2}
  `.replace(/\s+/g, " ").trim();
  const labelX = midX;
  const labelY = (y1 + y2) / 2;
  return {
    path,
    labelX,
    labelY
  };
};
function getEdgePathCalculator(type) {
  switch (type) {
    case "bezier":
      return bezierHorizontal;
    case "bezier-vertical":
      return bezierVertical;
    case "bezier-smart":
      return bezierSmart;
    case "straight":
      return straight;
    case "step":
      return stepHorizontal;
    case "step-vertical":
      return stepVertical;
    case "step-smart":
      return stepSmart;
    case "smooth-step":
      return smoothStep;
    default:
      return bezierHorizontal;
  }
}
var defaultEdgePathCalculator = bezierHorizontal;

// src/utils/layout.ts
var FitToBoundsMode = /* @__PURE__ */ (function(FitToBoundsMode2) {
  FitToBoundsMode2["Graph"] = "graph";
  FitToBoundsMode2["Selection"] = "selection";
  return FitToBoundsMode2;
})({});
var calculateBounds = (nodes) => {
  if (nodes.length === 0) {
    return {
      x: 0,
      y: 0,
      width: 0,
      height: 0
    };
  }
  const minX = Math.min(...nodes.map((node) => node.x));
  const minY = Math.min(...nodes.map((node) => node.y));
  const maxX = Math.max(...nodes.map((node) => node.x + node.width));
  const maxY = Math.max(...nodes.map((node) => node.y + node.height));
  return {
    x: minX,
    y: minY,
    width: maxX - minX,
    height: maxY - minY
  };
};
function getNodeCenter(node) {
  return {
    x: node.x + node.width / 2,
    y: node.y + node.height / 2
  };
}
function setNodeCenter(node, centerX, centerY) {
  return {
    ...node,
    x: centerX - node.width / 2,
    y: centerY - 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;
}
function getNodeBounds(node) {
  return {
    x: node.x,
    y: node.y,
    width: node.width,
    height: node.height,
    left: node.x,
    right: node.x + node.width,
    top: node.y,
    bottom: node.y + node.height
  };
}
function areNodesClose(node1, node2, threshold = 200) {
  const center1 = getNodeCenter(node1);
  const center2 = getNodeCenter(node2);
  const distance = Math.sqrt(Math.pow(center1.x - center2.x, 2) + Math.pow(center1.y - center2.y, 2));
  return distance <= threshold;
}

// src/utils/hit-test.ts
var defaultProvider = {
  elementFromPoint: (x, y) => document.elementFromPoint(x, y),
  elementsFromPoint: (x, y) => document.elementsFromPoint(x, y)
};
var _provider = defaultProvider;
function setHitTestProvider(provider) {
  _provider = provider ?? defaultProvider;
}
function hitTestNode(screenX, screenY) {
  const element = _provider.elementFromPoint(screenX, screenY);
  const nodeElement = element?.closest("[data-node-id]") ?? null;
  const nodeId = nodeElement?.getAttribute("data-node-id") ?? null;
  return {
    nodeId,
    element: nodeElement
  };
}
function hitTestPort(screenX, screenY) {
  const elements = _provider.elementsFromPoint(screenX, screenY);
  for (const el of elements) {
    const portElement = el.closest("[data-drag-port-id]");
    if (portElement) {
      const portId = portElement.dataset.dragPortId;
      const portBar = portElement.closest("[data-port-bar]");
      const nodeId = portBar?.dataset.nodeId;
      if (portId && nodeId) {
        return {
          nodeId,
          portId
        };
      }
    }
  }
  return null;
}

// src/utils/edge-path-registry.ts
var customCalculators = /* @__PURE__ */ new Map();
function registerEdgePathCalculator(name, calculator) {
  customCalculators.set(name, calculator);
}
function unregisterEdgePathCalculator(name) {
  return customCalculators.delete(name);
}
function resolveEdgePathCalculator(name) {
  return customCalculators.get(name) ?? getEdgePathCalculator(name);
}
function hasCustomEdgePathCalculator(name) {
  return customCalculators.has(name);
}
function getCustomEdgePathCalculatorNames() {
  return Array.from(customCalculators.keys());
}
function clearCustomEdgePathCalculators() {
  customCalculators.clear();
}
export {
  FitToBoundsMode,
  areNodesClose,
  bezierHorizontal,
  bezierSmart,
  bezierVertical,
  calculateBounds,
  checkNodesOverlap,
  clearCustomEdgePathCalculators,
  clearPendingState,
  createComponentRegistry,
  createDebug,
  createFallbackComponent,
  debug,
  defaultEdgePathCalculator,
  getCustomEdgePathCalculatorNames,
  getEdgePathCalculator,
  getNodeBounds,
  getNodeCenter,
  getNodeGestureConfig,
  getPendingState,
  getViewportGestureConfig,
  hasCustomEdgePathCalculator,
  hasPendingMutations,
  hitTestNode,
  hitTestPort,
  pendingNodeMutations,
  registerEdgePathCalculator,
  resolveEdgePathCalculator,
  setHitTestProvider,
  setNodeCenter,
  smoothStep,
  stepHorizontal,
  stepSmart,
  stepVertical,
  straight,
  unregisterEdgePathCalculator
};
//# sourceMappingURL=index.mjs.map