"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __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 __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); // src/core/types.ts var init_types = __esm({ "src/core/types.ts"() { "use strict"; } }); // 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 }); var import_jotai, import_graphology, graphOptions, currentGraphIdAtom, graphAtom, graphUpdateVersionAtom, edgeCreationAtom, draggingNodeIdAtom, preDragNodeAttributesAtom; var init_graph_store = __esm({ "src/core/graph-store.ts"() { "use strict"; import_jotai = require("jotai"); import_graphology = __toESM(require("graphology")); graphOptions = { type: "directed", multi: true, allowSelfLoops: true }; currentGraphIdAtom = (0, import_jotai.atom)(null); graphAtom = (0, import_jotai.atom)(new import_graphology.default(graphOptions)); graphUpdateVersionAtom = (0, import_jotai.atom)(0); edgeCreationAtom = (0, import_jotai.atom)({ isCreating: false, sourceNodeId: null, sourceNodePosition: null, targetPosition: null, hoveredTargetNodeId: null, sourceHandle: null, targetHandle: null, sourcePort: null, targetPort: null, snappedTargetPosition: null }); draggingNodeIdAtom = (0, import_jotai.atom)(null); preDragNodeAttributesAtom = (0, import_jotai.atom)(null); } }); // src/utils/debug.ts function createDebug(module2) { const base = (0, import_debug.default)(`${NAMESPACE}:${module2}`); const warn = (0, import_debug.default)(`${NAMESPACE}:${module2}:warn`); const error = (0, import_debug.default)(`${NAMESPACE}:${module2}:error`); warn.enabled = true; error.enabled = true; warn.log = console.warn.bind(console); error.log = console.error.bind(console); const debugFn = Object.assign(base, { warn, error }); return debugFn; } var import_debug, NAMESPACE, debug; var init_debug = __esm({ "src/utils/debug.ts"() { "use strict"; import_debug = __toESM(require("debug")); 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/utils/mutation-queue.ts 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 clearAllPendingMutations() { pendingNodeMutations.clear(); } function hasPendingMutations() { for (const state of pendingNodeMutations.values()) { if (state.inFlight || state.queuedPosition !== null || state.queuedUiProperties !== null) { return true; } } return false; } var pendingNodeMutations; var init_mutation_queue = __esm({ "src/utils/mutation-queue.ts"() { "use strict"; pendingNodeMutations = /* @__PURE__ */ new Map(); } }); // src/core/perf.ts 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 import_jotai2, perfEnabledAtom, _enabled; var init_perf = __esm({ "src/core/perf.ts"() { "use strict"; import_jotai2 = require("jotai"); perfEnabledAtom = (0, import_jotai2.atom)(false); _enabled = false; if (typeof window !== "undefined") { window.__canvasPerf = setPerfEnabled; } } }); // src/core/graph-position.ts function getPositionCache(graph) { let cache = _positionCacheByGraph.get(graph); if (!cache) { cache = /* @__PURE__ */ new Map(); _positionCacheByGraph.set(graph, cache); } return cache; } var import_jotai3, import_jotai_family, import_graphology2, debug2, _positionCacheByGraph, nodePositionUpdateCounterAtom, nodePositionAtomFamily, updateNodePositionAtom, cleanupNodePositionAtom, cleanupAllNodePositionsAtom, clearGraphOnSwitchAtom; var init_graph_position = __esm({ "src/core/graph-position.ts"() { "use strict"; import_jotai3 = require("jotai"); import_jotai_family = require("jotai-family"); import_graphology2 = __toESM(require("graphology")); init_graph_store(); init_debug(); init_mutation_queue(); init_perf(); debug2 = createDebug("graph:position"); _positionCacheByGraph = /* @__PURE__ */ new WeakMap(); nodePositionUpdateCounterAtom = (0, import_jotai3.atom)(0); nodePositionAtomFamily = (0, import_jotai_family.atomFamily)((nodeId) => (0, import_jotai3.atom)((get) => { get(nodePositionUpdateCounterAtom); const graph = get(graphAtom); if (!graph.hasNode(nodeId)) { return { x: 0, y: 0 }; } const x = graph.getNodeAttribute(nodeId, "x"); const y = graph.getNodeAttribute(nodeId, "y"); const cache = getPositionCache(graph); const prev = cache.get(nodeId); if (prev && prev.x === x && prev.y === y) { return prev; } const pos = { x, y }; cache.set(nodeId, pos); return pos; })); updateNodePositionAtom = (0, import_jotai3.atom)(null, (get, set, { nodeId, position }) => { const end = canvasMark("drag-frame"); const graph = get(graphAtom); if (graph.hasNode(nodeId)) { debug2("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 = (0, import_jotai3.atom)(null, (get, _set, nodeId) => { nodePositionAtomFamily.remove(nodeId); const graph = get(graphAtom); getPositionCache(graph).delete(nodeId); debug2("Removed position atom for node: %s", nodeId); }); cleanupAllNodePositionsAtom = (0, import_jotai3.atom)(null, (get, _set) => { const graph = get(graphAtom); const nodeIds = graph.nodes(); nodeIds.forEach((nodeId) => { nodePositionAtomFamily.remove(nodeId); }); _positionCacheByGraph.delete(graph); debug2("Removed %d position atoms", nodeIds.length); }); clearGraphOnSwitchAtom = (0, import_jotai3.atom)(null, (get, set) => { debug2("Clearing graph for switch"); set(cleanupAllNodePositionsAtom); clearAllPendingMutations(); const emptyGraph = new import_graphology2.default(graphOptions); set(graphAtom, emptyGraph); set(graphUpdateVersionAtom, (v) => v + 1); }); } }); // 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 }); var import_jotai4, debug3, 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"; import_jotai4 = require("jotai"); init_debug(); debug3 = createDebug("selection"); selectedNodeIdsAtom = (0, import_jotai4.atom)(/* @__PURE__ */ new Set()); selectedEdgeIdAtom = (0, import_jotai4.atom)(null); handleNodePointerDownSelectionAtom = (0, import_jotai4.atom)(null, (get, set, { nodeId, isShiftPressed }) => { const currentSelection = get(selectedNodeIdsAtom); debug3("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); } debug3("Shift-click, setting selection to: %o", Array.from(newSelection)); set(selectedNodeIdsAtom, newSelection); } else { if (!currentSelection.has(nodeId)) { debug3("Node not in selection, selecting: %s", nodeId); set(selectedNodeIdsAtom, /* @__PURE__ */ new Set([nodeId])); } else { debug3("Node already selected, preserving multi-select"); } } }); selectSingleNodeAtom = (0, import_jotai4.atom)(null, (get, set, nodeId) => { debug3("selectSingleNode: %s", nodeId); set(selectedEdgeIdAtom, null); if (nodeId === null || nodeId === void 0) { debug3("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 = (0, import_jotai4.atom)(null, (get, set, nodeId) => { const currentSelection = get(selectedNodeIdsAtom); const newSelection = new Set(currentSelection); if (newSelection.has(nodeId)) { newSelection.delete(nodeId); } else { newSelection.add(nodeId); } set(selectedNodeIdsAtom, newSelection); }); clearSelectionAtom = (0, import_jotai4.atom)(null, (_get, set) => { debug3("clearSelection"); set(selectedNodeIdsAtom, /* @__PURE__ */ new Set()); }); addNodesToSelectionAtom = (0, import_jotai4.atom)(null, (get, set, nodeIds) => { const currentSelection = get(selectedNodeIdsAtom); const newSelection = new Set(currentSelection); for (const nodeId of nodeIds) { newSelection.add(nodeId); } set(selectedNodeIdsAtom, newSelection); }); removeNodesFromSelectionAtom = (0, import_jotai4.atom)(null, (get, set, nodeIds) => { const currentSelection = get(selectedNodeIdsAtom); const newSelection = new Set(currentSelection); for (const nodeId of nodeIds) { newSelection.delete(nodeId); } set(selectedNodeIdsAtom, newSelection); }); selectEdgeAtom = (0, import_jotai4.atom)(null, (get, set, edgeId) => { set(selectedEdgeIdAtom, edgeId); if (edgeId !== null) { set(selectedNodeIdsAtom, /* @__PURE__ */ new Set()); } }); clearEdgeSelectionAtom = (0, import_jotai4.atom)(null, (_get, set) => { set(selectedEdgeIdAtom, null); }); focusedNodeIdAtom = (0, import_jotai4.atom)(null); setFocusedNodeAtom = (0, import_jotai4.atom)(null, (_get, set, nodeId) => { set(focusedNodeIdAtom, nodeId); }); hasFocusedNodeAtom = (0, import_jotai4.atom)((get) => get(focusedNodeIdAtom) !== null); selectedNodesCountAtom = (0, import_jotai4.atom)((get) => get(selectedNodeIdsAtom).size); hasSelectionAtom = (0, import_jotai4.atom)((get) => get(selectedNodeIdsAtom).size > 0); } }); // src/utils/layout.ts 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; } 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 }); var import_jotai5, 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"; import_jotai5 = require("jotai"); init_graph_store(); init_graph_position(); init_graph_derived(); init_selection_store(); init_layout(); zoomAtom = (0, import_jotai5.atom)(1); panAtom = (0, import_jotai5.atom)({ x: 0, y: 0 }); viewportRectAtom = (0, import_jotai5.atom)(null); screenToWorldAtom = (0, import_jotai5.atom)((get) => { return (screenX, screenY) => { const pan = get(panAtom); const zoom = get(zoomAtom); const rect = get(viewportRectAtom); if (!rect) { return { x: screenX, y: screenY }; } const relativeX = screenX - rect.left; const relativeY = screenY - rect.top; return { x: (relativeX - pan.x) / zoom, y: (relativeY - pan.y) / zoom }; }; }); worldToScreenAtom = (0, import_jotai5.atom)((get) => { return (worldX, worldY) => { const pan = get(panAtom); const zoom = get(zoomAtom); const rect = get(viewportRectAtom); if (!rect) { return { x: worldX, y: worldY }; } return { x: worldX * zoom + pan.x + rect.left, y: worldY * zoom + pan.y + rect.top }; }; }); setZoomAtom = (0, import_jotai5.atom)(null, (get, set, { zoom, centerX, centerY }) => { const currentZoom = get(zoomAtom); const pan = get(panAtom); const rect = get(viewportRectAtom); const newZoom = Math.max(0.1, Math.min(5, zoom)); if (centerX !== void 0 && centerY !== void 0 && rect) { const relativeX = centerX - rect.left; const relativeY = centerY - rect.top; const worldX = (relativeX - pan.x) / currentZoom; const worldY = (relativeY - pan.y) / currentZoom; const newPanX = relativeX - worldX * newZoom; const newPanY = relativeY - worldY * newZoom; set(panAtom, { x: newPanX, y: newPanY }); } set(zoomAtom, newZoom); }); resetViewportAtom = (0, import_jotai5.atom)(null, (_get, set) => { set(zoomAtom, 1); set(panAtom, { x: 0, y: 0 }); }); fitToBoundsAtom = (0, import_jotai5.atom)(null, (get, set, { mode, padding = 20 }) => { const normalizedMode = typeof mode === "string" ? mode === "graph" ? FitToBoundsMode.Graph : FitToBoundsMode.Selection : mode; const viewportSize = get(viewportRectAtom); if (!viewportSize || viewportSize.width <= 0 || viewportSize.height <= 0) return; get(nodePositionUpdateCounterAtom); let bounds; if (normalizedMode === FitToBoundsMode.Graph) { const graph = get(graphAtom); const nodes = graph.nodes().map((node) => { const attrs = graph.getNodeAttributes(node); return { x: attrs.x, y: attrs.y, width: attrs.width || 500, height: attrs.height || 500 }; }); bounds = calculateBounds(nodes); } else { const selectedIds = get(selectedNodeIdsAtom); const allNodes = get(uiNodesAtom); const selectedNodes = allNodes.filter((n) => selectedIds.has(n.id)).map((n) => ({ x: n.position.x, y: n.position.y, width: n.width ?? 500, height: n.height ?? 500 })); bounds = calculateBounds(selectedNodes); } if (bounds.width <= 0 || bounds.height <= 0) return; const maxHPad = Math.max(0, viewportSize.width / 2 - 1); const maxVPad = Math.max(0, viewportSize.height / 2 - 1); const safePadding = Math.max(0, Math.min(padding, maxHPad, maxVPad)); const effW = Math.max(1, viewportSize.width - 2 * safePadding); const effH = Math.max(1, viewportSize.height - 2 * safePadding); const scale = Math.min(effW / bounds.width, effH / bounds.height); if (scale <= 0 || !isFinite(scale)) return; set(zoomAtom, scale); const scaledW = bounds.width * scale; const scaledH = bounds.height * scale; const startX = safePadding + (effW - scaledW) / 2; const startY = safePadding + (effH - scaledH) / 2; set(panAtom, { x: startX - bounds.x * scale, y: startY - bounds.y * scale }); }); centerOnNodeAtom = (0, import_jotai5.atom)(null, (get, set, nodeId) => { const nodes = get(uiNodesAtom); const node = nodes.find((n) => n.id === nodeId); if (!node) return; const { x, y, width = 200, height = 100 } = node; const zoom = get(zoomAtom); const centerX = x + width / 2; const centerY = y + height / 2; const rect = get(viewportRectAtom); const halfWidth = rect ? rect.width / 2 : 400; const halfHeight = rect ? rect.height / 2 : 300; set(panAtom, { x: halfWidth - centerX * zoom, y: halfHeight - centerY * zoom }); }); ZOOM_TRANSITION_THRESHOLD = 3.5; ZOOM_EXIT_THRESHOLD = 2; zoomFocusNodeIdAtom = (0, import_jotai5.atom)(null); zoomTransitionProgressAtom = (0, import_jotai5.atom)(0); isZoomTransitioningAtom = (0, import_jotai5.atom)((get) => { const progress = get(zoomTransitionProgressAtom); return progress > 0 && progress < 1; }); zoomAnimationTargetAtom = (0, import_jotai5.atom)(null); animateZoomToNodeAtom = (0, import_jotai5.atom)(null, (get, set, { nodeId, targetZoom, duration = 300 }) => { const nodes = get(uiNodesAtom); const node = nodes.find((n) => n.id === nodeId); if (!node) return; const { x, y, width = 200, height = 100 } = node; const centerX = x + width / 2; const centerY = y + height / 2; const rect = get(viewportRectAtom); const halfWidth = rect ? rect.width / 2 : 400; const halfHeight = rect ? rect.height / 2 : 300; const finalZoom = targetZoom ?? get(zoomAtom); const targetPan = { x: halfWidth - centerX * finalZoom, y: halfHeight - centerY * finalZoom }; set(zoomFocusNodeIdAtom, nodeId); set(zoomAnimationTargetAtom, { targetZoom: finalZoom, targetPan, startZoom: get(zoomAtom), startPan: { ...get(panAtom) }, duration, startTime: performance.now() }); }); animateFitToBoundsAtom = (0, import_jotai5.atom)(null, (get, set, { mode, padding = 20, duration = 300 }) => { const viewportSize = get(viewportRectAtom); if (!viewportSize || viewportSize.width <= 0 || viewportSize.height <= 0) return; get(nodePositionUpdateCounterAtom); let bounds; if (mode === "graph") { const graph = get(graphAtom); const nodes = graph.nodes().map((node) => { const attrs = graph.getNodeAttributes(node); return { x: attrs.x, y: attrs.y, width: attrs.width || 500, height: attrs.height || 500 }; }); bounds = calculateBounds(nodes); } else { const selectedIds = get(selectedNodeIdsAtom); const allNodes = get(uiNodesAtom); const selectedNodes = allNodes.filter((n) => selectedIds.has(n.id)).map((n) => ({ x: n.position.x, y: n.position.y, width: n.width ?? 500, height: n.height ?? 500 })); bounds = calculateBounds(selectedNodes); } if (bounds.width <= 0 || bounds.height <= 0) return; const safePadding = Math.max(0, Math.min(padding, viewportSize.width / 2 - 1, viewportSize.height / 2 - 1)); const effW = Math.max(1, viewportSize.width - 2 * safePadding); const effH = Math.max(1, viewportSize.height - 2 * safePadding); const scale = Math.min(effW / bounds.width, effH / bounds.height); if (scale <= 0 || !isFinite(scale)) return; const scaledW = bounds.width * scale; const scaledH = bounds.height * scale; const startX = safePadding + (effW - scaledW) / 2; const startY = safePadding + (effH - scaledH) / 2; const targetPan = { x: startX - bounds.x * scale, y: startY - bounds.y * scale }; set(zoomAnimationTargetAtom, { targetZoom: scale, targetPan, startZoom: get(zoomAtom), startPan: { ...get(panAtom) }, duration, startTime: performance.now() }); }); } }); // src/core/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 }); var import_jotai6, 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"; import_jotai6 = require("jotai"); init_graph_store(); init_graph_position(); init_debug(); init_history_actions(); init_history_actions(); debug4 = createDebug("history"); MAX_HISTORY_SIZE = 50; historyStateAtom = (0, import_jotai6.atom)({ past: [], future: [], isApplying: false }); canUndoAtom = (0, import_jotai6.atom)((get) => { const history = get(historyStateAtom); return history.past.length > 0 && !history.isApplying; }); canRedoAtom = (0, import_jotai6.atom)((get) => { const history = get(historyStateAtom); return history.future.length > 0 && !history.isApplying; }); undoCountAtom = (0, import_jotai6.atom)((get) => get(historyStateAtom).past.length); redoCountAtom = (0, import_jotai6.atom)((get) => get(historyStateAtom).future.length); pushDeltaAtom = (0, import_jotai6.atom)(null, (get, set, delta) => { const history = get(historyStateAtom); if (history.isApplying) return; const { label, ...cleanDelta } = delta; const entry = { forward: cleanDelta, reverse: invertDelta(cleanDelta), timestamp: Date.now(), label }; const newPast = [...history.past, entry]; if (newPast.length > MAX_HISTORY_SIZE) newPast.shift(); set(historyStateAtom, { past: newPast, future: [], // Clear redo stack isApplying: false }); debug4("Pushed delta: %s (past: %d)", label || delta.type, newPast.length); }); pushHistoryAtom = (0, import_jotai6.atom)(null, (get, set, label) => { const history = get(historyStateAtom); if (history.isApplying) return; const graph = get(graphAtom); const snapshot = createSnapshot(graph, label); const forward = { type: "full-snapshot", nodes: snapshot.nodes, edges: snapshot.edges }; const entry = { forward, reverse: forward, // For full snapshots, reverse IS the current state timestamp: Date.now(), label }; const newPast = [...history.past, entry]; if (newPast.length > MAX_HISTORY_SIZE) newPast.shift(); set(historyStateAtom, { past: newPast, future: [], isApplying: false }); debug4("Pushed snapshot: %s (past: %d)", label || "unnamed", newPast.length); }); undoAtom = (0, import_jotai6.atom)(null, (get, set) => { const history = get(historyStateAtom); if (history.past.length === 0 || history.isApplying) return false; set(historyStateAtom, { ...history, isApplying: true }); try { const graph = get(graphAtom); const newPast = [...history.past]; const entry = newPast.pop(); let forwardForRedo = entry.forward; if (entry.reverse.type === "full-snapshot") { const currentSnapshot = createSnapshot(graph, "current"); forwardForRedo = { type: "full-snapshot", nodes: currentSnapshot.nodes, edges: currentSnapshot.edges }; } const structuralChange = applyDelta(graph, entry.reverse); if (structuralChange) { set(graphAtom, graph); set(graphUpdateVersionAtom, (v) => v + 1); } set(nodePositionUpdateCounterAtom, (c) => c + 1); const redoEntry = { forward: forwardForRedo, reverse: entry.reverse, timestamp: entry.timestamp, label: entry.label }; set(historyStateAtom, { past: newPast, future: [redoEntry, ...history.future], isApplying: false }); debug4("Undo: %s (past: %d, future: %d)", entry.label, newPast.length, history.future.length + 1); return true; } catch (error) { debug4.error("Undo failed: %O", error); set(historyStateAtom, { ...history, isApplying: false }); return false; } }); redoAtom = (0, import_jotai6.atom)(null, (get, set) => { const history = get(historyStateAtom); if (history.future.length === 0 || history.isApplying) return false; set(historyStateAtom, { ...history, isApplying: true }); try { const graph = get(graphAtom); const newFuture = [...history.future]; const entry = newFuture.shift(); let reverseForUndo = entry.reverse; if (entry.forward.type === "full-snapshot") { const currentSnapshot = createSnapshot(graph, "current"); reverseForUndo = { type: "full-snapshot", nodes: currentSnapshot.nodes, edges: currentSnapshot.edges }; } const structuralChange = applyDelta(graph, entry.forward); if (structuralChange) { set(graphAtom, graph); set(graphUpdateVersionAtom, (v) => v + 1); } set(nodePositionUpdateCounterAtom, (c) => c + 1); const undoEntry = { forward: entry.forward, reverse: reverseForUndo, timestamp: entry.timestamp, label: entry.label }; set(historyStateAtom, { past: [...history.past, undoEntry], future: newFuture, isApplying: false }); debug4("Redo: %s (past: %d, future: %d)", entry.label, history.past.length + 1, newFuture.length); return true; } catch (error) { debug4.error("Redo failed: %O", error); set(historyStateAtom, { ...history, isApplying: false }); return false; } }); clearHistoryAtom = (0, import_jotai6.atom)(null, (_get, set) => { set(historyStateAtom, { past: [], future: [], isApplying: false }); debug4("History cleared"); }); historyLabelsAtom = (0, import_jotai6.atom)((get) => { const history = get(historyStateAtom); return { past: history.past.map((e) => e.label || "Unnamed"), future: history.future.map((e) => e.label || "Unnamed") }; }); } }); // src/core/group-store.ts var 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 }); 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 import_jotai7, 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"; import_jotai7 = require("jotai"); init_graph_store(); init_graph_position(); init_history_store(); collapsedGroupsAtom = (0, import_jotai7.atom)(/* @__PURE__ */ new Set()); toggleGroupCollapseAtom = (0, import_jotai7.atom)(null, (get, set, groupId) => { const current = get(collapsedGroupsAtom); const next = new Set(current); if (next.has(groupId)) { next.delete(groupId); } else { next.add(groupId); } set(collapsedGroupsAtom, next); }); collapseGroupAtom = (0, import_jotai7.atom)(null, (get, set, groupId) => { const current = get(collapsedGroupsAtom); if (!current.has(groupId)) { const next = new Set(current); next.add(groupId); set(collapsedGroupsAtom, next); } }); expandGroupAtom = (0, import_jotai7.atom)(null, (get, set, groupId) => { const current = get(collapsedGroupsAtom); if (current.has(groupId)) { const next = new Set(current); next.delete(groupId); set(collapsedGroupsAtom, next); } }); nodeChildrenAtom = (0, import_jotai7.atom)((get) => { get(graphUpdateVersionAtom); const graph = get(graphAtom); return (parentId) => { const children = []; graph.forEachNode((nodeId, attrs) => { if (attrs.parentId === parentId) { children.push(nodeId); } }); return children; }; }); nodeParentAtom = (0, import_jotai7.atom)((get) => { get(graphUpdateVersionAtom); const graph = get(graphAtom); return (nodeId) => { if (!graph.hasNode(nodeId)) return void 0; return graph.getNodeAttribute(nodeId, "parentId"); }; }); isGroupNodeAtom = (0, import_jotai7.atom)((get) => { const getChildren = get(nodeChildrenAtom); return (nodeId) => getChildren(nodeId).length > 0; }); groupChildCountAtom = (0, import_jotai7.atom)((get) => { const getChildren = get(nodeChildrenAtom); return (groupId) => getChildren(groupId).length; }); setNodeParentAtom = (0, import_jotai7.atom)(null, (get, set, { nodeId, parentId }) => { const graph = get(graphAtom); if (!graph.hasNode(nodeId)) return; if (parentId) { if (parentId === nodeId) return; let current = parentId; while (current) { if (current === nodeId) return; if (!graph.hasNode(current)) break; current = graph.getNodeAttribute(current, "parentId"); } } graph.setNodeAttribute(nodeId, "parentId", parentId); set(graphUpdateVersionAtom, (v) => v + 1); }); moveNodesToGroupAtom = (0, import_jotai7.atom)(null, (get, set, { nodeIds, groupId }) => { for (const nodeId of nodeIds) { set(setNodeParentAtom, { nodeId, parentId: groupId }); } }); removeFromGroupAtom = (0, import_jotai7.atom)(null, (get, set, nodeId) => { set(setNodeParentAtom, { nodeId, parentId: void 0 }); }); groupSelectedNodesAtom = (0, import_jotai7.atom)(null, (get, set, { nodeIds, groupNodeId }) => { set(pushHistoryAtom, `Group ${nodeIds.length} nodes`); const graph = get(graphAtom); let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; for (const nodeId of nodeIds) { if (!graph.hasNode(nodeId)) continue; const attrs = graph.getNodeAttributes(nodeId); minX = Math.min(minX, attrs.x); minY = Math.min(minY, attrs.y); maxX = Math.max(maxX, attrs.x + (attrs.width || 200)); maxY = Math.max(maxY, attrs.y + (attrs.height || 100)); } const padding = 20; if (graph.hasNode(groupNodeId)) { graph.setNodeAttribute(groupNodeId, "x", minX - padding); graph.setNodeAttribute(groupNodeId, "y", minY - padding - 30); graph.setNodeAttribute(groupNodeId, "width", maxX - minX + 2 * padding); graph.setNodeAttribute(groupNodeId, "height", maxY - minY + 2 * padding + 30); } for (const nodeId of nodeIds) { if (nodeId !== groupNodeId && graph.hasNode(nodeId)) { graph.setNodeAttribute(nodeId, "parentId", groupNodeId); } } set(graphUpdateVersionAtom, (v) => v + 1); set(nodePositionUpdateCounterAtom, (c) => c + 1); }); ungroupNodesAtom = (0, import_jotai7.atom)(null, (get, set, groupId) => { set(pushHistoryAtom, "Ungroup nodes"); const graph = get(graphAtom); graph.forEachNode((nodeId, attrs) => { if (attrs.parentId === groupId) { graph.setNodeAttribute(nodeId, "parentId", void 0); } }); set(graphUpdateVersionAtom, (v) => v + 1); }); nestNodesOnDropAtom = (0, import_jotai7.atom)(null, (get, set, { nodeIds, targetId }) => { set(pushHistoryAtom, "Nest nodes"); for (const nodeId of nodeIds) { if (nodeId === targetId) continue; set(setNodeParentAtom, { nodeId, parentId: targetId }); } set(autoResizeGroupAtom, targetId); }); collapsedEdgeRemapAtom = (0, import_jotai7.atom)((get) => { const collapsed = get(collapsedGroupsAtom); if (collapsed.size === 0) return /* @__PURE__ */ new Map(); get(graphUpdateVersionAtom); const graph = get(graphAtom); const remap = /* @__PURE__ */ new Map(); for (const nodeId of graph.nodes()) { let current = nodeId; let outermost = null; while (true) { if (!graph.hasNode(current)) break; const parent = graph.getNodeAttribute(current, "parentId"); if (!parent) break; if (collapsed.has(parent)) outermost = parent; current = parent; } if (outermost) remap.set(nodeId, outermost); } return remap; }); autoResizeGroupAtom = (0, import_jotai7.atom)(null, (get, set, groupId) => { const graph = get(graphAtom); if (!graph.hasNode(groupId)) return; const children = []; graph.forEachNode((nodeId, attrs) => { if (attrs.parentId === groupId) { children.push(nodeId); } }); if (children.length === 0) return; const padding = 20; const headerHeight = 30; let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; for (const childId of children) { const attrs = graph.getNodeAttributes(childId); minX = Math.min(minX, attrs.x); minY = Math.min(minY, attrs.y); maxX = Math.max(maxX, attrs.x + (attrs.width || 200)); maxY = Math.max(maxY, attrs.y + (attrs.height || 100)); } graph.setNodeAttribute(groupId, "x", minX - padding); graph.setNodeAttribute(groupId, "y", minY - padding - headerHeight); graph.setNodeAttribute(groupId, "width", maxX - minX + 2 * padding); graph.setNodeAttribute(groupId, "height", maxY - minY + 2 * padding + headerHeight); set(nodePositionUpdateCounterAtom, (c) => c + 1); }); } }); // src/core/graph-derived.ts function getEdgeCache(graph) { let cache = _edgeCacheByGraph.get(graph); if (!cache) { cache = /* @__PURE__ */ new Map(); _edgeCacheByGraph.set(graph, cache); } return cache; } var import_jotai8, import_jotai_family2, highestZIndexAtom, _prevUiNodesByGraph, uiNodesAtom, nodeKeysAtom, nodeFamilyAtom, edgeKeysAtom, edgeKeysWithTempEdgeAtom, _edgeCacheByGraph, edgeFamilyAtom; var init_graph_derived = __esm({ "src/core/graph-derived.ts"() { "use strict"; import_jotai8 = require("jotai"); import_jotai_family2 = require("jotai-family"); init_graph_store(); init_graph_position(); init_viewport_store(); init_group_store(); highestZIndexAtom = (0, import_jotai8.atom)((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 = (0, import_jotai8.atom)((get) => { get(graphUpdateVersionAtom); const graph = get(graphAtom); const currentDraggingId = get(draggingNodeIdAtom); const collapsed = get(collapsedGroupsAtom); const nodes = []; graph.forEachNode((nodeId, attributes) => { if (collapsed.size > 0) { let current = nodeId; let hidden = false; while (true) { if (!graph.hasNode(current)) break; const pid = graph.getNodeAttributes(current).parentId; if (!pid) break; if (collapsed.has(pid)) { hidden = true; break; } current = pid; } if (hidden) return; } const position = get(nodePositionAtomFamily(nodeId)); nodes.push({ ...attributes, id: nodeId, position, isDragging: nodeId === currentDraggingId }); }); const prev = _prevUiNodesByGraph.get(graph) ?? []; if (nodes.length === prev.length && nodes.every((n, i) => n.id === prev[i].id && n.position === prev[i].position && n.isDragging === prev[i].isDragging)) { return prev; } _prevUiNodesByGraph.set(graph, nodes); return nodes; }); nodeKeysAtom = (0, import_jotai8.atom)((get) => { get(graphUpdateVersionAtom); const graph = get(graphAtom); return graph.nodes(); }); nodeFamilyAtom = (0, import_jotai_family2.atomFamily)((nodeId) => (0, import_jotai8.atom)((get) => { get(graphUpdateVersionAtom); const graph = get(graphAtom); if (!graph.hasNode(nodeId)) { return null; } const attributes = graph.getNodeAttributes(nodeId); const position = get(nodePositionAtomFamily(nodeId)); const currentDraggingId = get(draggingNodeIdAtom); return { ...attributes, id: nodeId, position, isDragging: nodeId === currentDraggingId }; }), (a, b) => a === b); edgeKeysAtom = (0, import_jotai8.atom)((get) => { get(graphUpdateVersionAtom); const graph = get(graphAtom); return graph.edges(); }); edgeKeysWithTempEdgeAtom = (0, import_jotai8.atom)((get) => { const keys = get(edgeKeysAtom); const edgeCreation = get(edgeCreationAtom); if (edgeCreation.isCreating) { return [...keys, "temp-creating-edge"]; } return keys; }); _edgeCacheByGraph = /* @__PURE__ */ new WeakMap(); edgeFamilyAtom = (0, import_jotai_family2.atomFamily)((key) => (0, import_jotai8.atom)((get) => { get(graphUpdateVersionAtom); if (key === "temp-creating-edge") { const edgeCreationState = get(edgeCreationAtom); const graph2 = get(graphAtom); if (edgeCreationState.isCreating && edgeCreationState.sourceNodeId && edgeCreationState.targetPosition) { const sourceNodeAttrs = graph2.getNodeAttributes(edgeCreationState.sourceNodeId); const sourceNodePosition = get(nodePositionAtomFamily(edgeCreationState.sourceNodeId)); const pan = get(panAtom); const zoom = get(zoomAtom); const viewportRect = get(viewportRectAtom); if (sourceNodeAttrs && viewportRect) { const mouseX = edgeCreationState.targetPosition.x - viewportRect.left; const mouseY = edgeCreationState.targetPosition.y - viewportRect.top; const worldTargetX = (mouseX - pan.x) / zoom; const worldTargetY = (mouseY - pan.y) / zoom; const tempEdge = { key: "temp-creating-edge", sourceId: edgeCreationState.sourceNodeId, targetId: "temp-cursor", sourcePosition: sourceNodePosition, targetPosition: { x: worldTargetX, y: worldTargetY }, sourceNodeSize: sourceNodeAttrs.size, sourceNodeWidth: sourceNodeAttrs.width, sourceNodeHeight: sourceNodeAttrs.height, targetNodeSize: 0, targetNodeWidth: 0, targetNodeHeight: 0, type: "dashed", color: "#FF9800", weight: 2, label: void 0, dbData: { id: "temp-creating-edge", graph_id: get(currentGraphIdAtom) || "", source_node_id: edgeCreationState.sourceNodeId, target_node_id: "temp-cursor", edge_type: "temp", filter_condition: null, ui_properties: null, data: null, created_at: (/* @__PURE__ */ new Date()).toISOString(), updated_at: (/* @__PURE__ */ new Date()).toISOString() } }; return tempEdge; } } return null; } const graph = get(graphAtom); if (!graph.hasEdge(key)) { getEdgeCache(graph).delete(key); return null; } const sourceId = graph.source(key); const targetId = graph.target(key); const attributes = graph.getEdgeAttributes(key); const remap = get(collapsedEdgeRemapAtom); const effectiveSourceId = remap.get(sourceId) ?? sourceId; const effectiveTargetId = remap.get(targetId) ?? targetId; if (!graph.hasNode(effectiveSourceId) || !graph.hasNode(effectiveTargetId)) { getEdgeCache(graph).delete(key); return null; } const sourceAttributes = graph.getNodeAttributes(effectiveSourceId); const targetAttributes = graph.getNodeAttributes(effectiveTargetId); const sourcePosition = get(nodePositionAtomFamily(effectiveSourceId)); const targetPosition = get(nodePositionAtomFamily(effectiveTargetId)); if (sourceAttributes && targetAttributes) { const next = { ...attributes, key, sourceId: effectiveSourceId, targetId: effectiveTargetId, sourcePosition, targetPosition, sourceNodeSize: sourceAttributes.size, targetNodeSize: targetAttributes.size, sourceNodeWidth: sourceAttributes.width ?? sourceAttributes.size, sourceNodeHeight: sourceAttributes.height ?? sourceAttributes.size, targetNodeWidth: targetAttributes.width ?? targetAttributes.size, targetNodeHeight: targetAttributes.height ?? targetAttributes.size }; const edgeCache = getEdgeCache(graph); const prev = edgeCache.get(key); if (prev && prev.sourcePosition === next.sourcePosition && prev.targetPosition === next.targetPosition && prev.sourceId === next.sourceId && prev.targetId === next.targetId && prev.type === next.type && prev.color === next.color && prev.weight === next.weight && prev.label === next.label && prev.sourceNodeSize === next.sourceNodeSize && prev.targetNodeSize === next.targetNodeSize && prev.sourceNodeWidth === next.sourceNodeWidth && prev.sourceNodeHeight === next.sourceNodeHeight && prev.targetNodeWidth === next.targetNodeWidth && prev.targetNodeHeight === next.targetNodeHeight) { return prev; } edgeCache.set(key, next); return next; } getEdgeCache(graph).delete(key); return null; }), (a, b) => a === b); } }); // src/core/reduced-motion-store.ts var import_jotai9, prefersReducedMotionAtom, watchReducedMotionAtom; var init_reduced_motion_store = __esm({ "src/core/reduced-motion-store.ts"() { "use strict"; import_jotai9 = require("jotai"); prefersReducedMotionAtom = (0, import_jotai9.atom)(typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)").matches : false); watchReducedMotionAtom = (0, import_jotai9.atom)(null, (_get, set) => { if (typeof window === "undefined" || typeof window.matchMedia !== "function") return; const mql = window.matchMedia("(prefers-reduced-motion: reduce)"); const handler = (e) => { set(prefersReducedMotionAtom, e.matches); }; set(prefersReducedMotionAtom, mql.matches); mql.addEventListener("change", handler); return () => mql.removeEventListener("change", handler); }); } }); // src/core/graph-mutations-edges.ts var import_jotai10, debug5, addEdgeToLocalGraphAtom, removeEdgeFromLocalGraphAtom, swapEdgeAtomicAtom, departingEdgesAtom, EDGE_ANIMATION_DURATION, removeEdgeWithAnimationAtom, editingEdgeLabelAtom, updateEdgeLabelAtom; var init_graph_mutations_edges = __esm({ "src/core/graph-mutations-edges.ts"() { "use strict"; import_jotai10 = require("jotai"); init_graph_store(); init_graph_position(); init_graph_derived(); init_debug(); init_reduced_motion_store(); debug5 = createDebug("graph:mutations:edges"); addEdgeToLocalGraphAtom = (0, import_jotai10.atom)(null, (get, set, newEdge) => { const graph = get(graphAtom); if (graph.hasNode(newEdge.source_node_id) && graph.hasNode(newEdge.target_node_id)) { const uiProps = newEdge.ui_properties || {}; const attributes = { type: typeof uiProps.style === "string" ? uiProps.style : "solid", color: typeof uiProps.color === "string" ? uiProps.color : "#999", label: newEdge.edge_type ?? void 0, weight: typeof uiProps.weight === "number" ? uiProps.weight : 1, dbData: newEdge }; if (!graph.hasEdge(newEdge.id)) { try { debug5("Adding edge %s to local graph", newEdge.id); graph.addEdgeWithKey(newEdge.id, newEdge.source_node_id, newEdge.target_node_id, attributes); set(graphAtom, graph.copy()); set(graphUpdateVersionAtom, (v) => v + 1); } catch (e) { debug5("Failed to add edge %s: %o", newEdge.id, e); } } } }); removeEdgeFromLocalGraphAtom = (0, import_jotai10.atom)(null, (get, set, edgeId) => { const graph = get(graphAtom); if (graph.hasEdge(edgeId)) { graph.dropEdge(edgeId); set(graphAtom, graph.copy()); set(graphUpdateVersionAtom, (v) => v + 1); } }); swapEdgeAtomicAtom = (0, import_jotai10.atom)(null, (get, set, { tempEdgeId, newEdge }) => { const graph = get(graphAtom); if (graph.hasEdge(tempEdgeId)) { graph.dropEdge(tempEdgeId); } if (graph.hasNode(newEdge.source_node_id) && graph.hasNode(newEdge.target_node_id)) { const uiProps = newEdge.ui_properties || {}; const attributes = { type: typeof uiProps.style === "string" ? uiProps.style : "solid", color: typeof uiProps.color === "string" ? uiProps.color : "#999", label: newEdge.edge_type ?? void 0, weight: typeof uiProps.weight === "number" ? uiProps.weight : 1, dbData: newEdge }; if (!graph.hasEdge(newEdge.id)) { try { debug5("Atomically swapping temp edge %s with real edge %s", tempEdgeId, newEdge.id); graph.addEdgeWithKey(newEdge.id, newEdge.source_node_id, newEdge.target_node_id, attributes); } catch (e) { debug5("Failed to add edge %s: %o", newEdge.id, e); } } } set(graphAtom, graph.copy()); set(graphUpdateVersionAtom, (v) => v + 1); }); departingEdgesAtom = (0, import_jotai10.atom)(/* @__PURE__ */ new Map()); EDGE_ANIMATION_DURATION = 300; removeEdgeWithAnimationAtom = (0, import_jotai10.atom)(null, (get, set, edgeKey) => { const edgeState = get(edgeFamilyAtom(edgeKey)); if (edgeState) { const departing = new Map(get(departingEdgesAtom)); departing.set(edgeKey, edgeState); set(departingEdgesAtom, departing); set(removeEdgeFromLocalGraphAtom, edgeKey); const duration = get(prefersReducedMotionAtom) ? 0 : EDGE_ANIMATION_DURATION; setTimeout(() => { const current = new Map(get(departingEdgesAtom)); current.delete(edgeKey); set(departingEdgesAtom, current); }, duration); } }); editingEdgeLabelAtom = (0, import_jotai10.atom)(null); updateEdgeLabelAtom = (0, import_jotai10.atom)(null, (get, set, { edgeKey, label }) => { const graph = get(graphAtom); if (graph.hasEdge(edgeKey)) { graph.setEdgeAttribute(edgeKey, "label", label || void 0); set(graphUpdateVersionAtom, (v) => v + 1); set(nodePositionUpdateCounterAtom, (c) => c + 1); } }); } }); // src/core/graph-mutations-advanced.ts var import_jotai11, debug6, dropTargetNodeIdAtom, splitNodeAtom, mergeNodesAtom; var init_graph_mutations_advanced = __esm({ "src/core/graph-mutations-advanced.ts"() { "use strict"; import_jotai11 = require("jotai"); init_graph_store(); init_graph_position(); init_history_store(); init_debug(); init_graph_mutations_edges(); init_graph_mutations(); debug6 = createDebug("graph:mutations:advanced"); dropTargetNodeIdAtom = (0, import_jotai11.atom)(null); splitNodeAtom = (0, import_jotai11.atom)(null, (get, set, { nodeId, position1, position2 }) => { const graph = get(graphAtom); if (!graph.hasNode(nodeId)) return; const attrs = graph.getNodeAttributes(nodeId); const graphId = get(currentGraphIdAtom) || attrs.dbData.graph_id; set(pushHistoryAtom, "Split node"); graph.setNodeAttribute(nodeId, "x", position1.x); graph.setNodeAttribute(nodeId, "y", position1.y); const edges = []; graph.forEachEdge(nodeId, (_key, eAttrs, source, target) => { edges.push({ source, target, attrs: eAttrs }); }); const cloneId = `split-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; const cloneDbNode = { ...attrs.dbData, id: cloneId, graph_id: graphId, ui_properties: { ...attrs.dbData.ui_properties || {}, x: position2.x, y: position2.y }, created_at: (/* @__PURE__ */ new Date()).toISOString(), updated_at: (/* @__PURE__ */ new Date()).toISOString() }; set(addNodeToLocalGraphAtom, cloneDbNode); for (const edge of edges) { const newSource = edge.source === nodeId ? cloneId : edge.source; const newTarget = edge.target === nodeId ? cloneId : edge.target; set(addEdgeToLocalGraphAtom, { ...edge.attrs.dbData, id: `split-e-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, source_node_id: newSource, target_node_id: newTarget }); } set(graphUpdateVersionAtom, (v) => v + 1); set(nodePositionUpdateCounterAtom, (c) => c + 1); debug6("Split node %s \u2192 clone %s", nodeId, cloneId); }); mergeNodesAtom = (0, import_jotai11.atom)(null, (get, set, { nodeIds }) => { if (nodeIds.length < 2) return; const graph = get(graphAtom); const [survivorId, ...doomed] = nodeIds; if (!graph.hasNode(survivorId)) return; set(pushHistoryAtom, `Merge ${nodeIds.length} nodes`); const doomedSet = new Set(doomed); for (const doomedId of doomed) { if (!graph.hasNode(doomedId)) continue; const edges = []; graph.forEachEdge(doomedId, (_key, eAttrs, source, target) => { edges.push({ source, target, attrs: eAttrs }); }); for (const edge of edges) { const newSource = doomedSet.has(edge.source) ? survivorId : edge.source; const newTarget = doomedSet.has(edge.target) ? survivorId : edge.target; if (newSource === newTarget) continue; set(addEdgeToLocalGraphAtom, { ...edge.attrs.dbData, id: `merge-e-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, source_node_id: newSource, target_node_id: newTarget }); } set(optimisticDeleteNodeAtom, { nodeId: doomedId }); } set(graphUpdateVersionAtom, (v) => v + 1); debug6("Merged nodes %o \u2192 survivor %s", nodeIds, survivorId); }); } }); // src/core/graph-mutations.ts var 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 }); var import_jotai12, import_graphology3, debug7, startNodeDragAtom, endNodeDragAtom, optimisticDeleteNodeAtom, optimisticDeleteEdgeAtom, addNodeToLocalGraphAtom, loadGraphFromDbAtom; var init_graph_mutations = __esm({ "src/core/graph-mutations.ts"() { "use strict"; import_jotai12 = require("jotai"); import_graphology3 = __toESM(require("graphology")); init_graph_store(); init_graph_position(); init_graph_derived(); init_group_store(); init_debug(); init_graph_mutations_edges(); init_graph_mutations_advanced(); debug7 = createDebug("graph:mutations"); startNodeDragAtom = (0, import_jotai12.atom)(null, (get, set, { nodeId }) => { const graph = get(graphAtom); if (!graph.hasNode(nodeId)) return; const currentAttributes = graph.getNodeAttributes(nodeId); set(preDragNodeAttributesAtom, JSON.parse(JSON.stringify(currentAttributes))); const currentHighestZIndex = get(highestZIndexAtom); const newZIndex = currentHighestZIndex + 1; graph.setNodeAttribute(nodeId, "zIndex", newZIndex); set(draggingNodeIdAtom, nodeId); }); endNodeDragAtom = (0, import_jotai12.atom)(null, (get, set, _payload) => { const currentDraggingId = get(draggingNodeIdAtom); if (currentDraggingId) { debug7("Node %s drag ended", currentDraggingId); const graph = get(graphAtom); if (graph.hasNode(currentDraggingId)) { const parentId = graph.getNodeAttribute(currentDraggingId, "parentId"); if (parentId) { set(autoResizeGroupAtom, parentId); } } } set(draggingNodeIdAtom, null); set(preDragNodeAttributesAtom, null); }); optimisticDeleteNodeAtom = (0, import_jotai12.atom)(null, (get, set, { nodeId }) => { const graph = get(graphAtom); if (graph.hasNode(nodeId)) { graph.dropNode(nodeId); set(cleanupNodePositionAtom, nodeId); set(graphAtom, graph.copy()); debug7("Optimistically deleted node %s", nodeId); } }); optimisticDeleteEdgeAtom = (0, import_jotai12.atom)(null, (get, set, { edgeKey }) => { const graph = get(graphAtom); if (graph.hasEdge(edgeKey)) { graph.dropEdge(edgeKey); set(graphAtom, graph.copy()); debug7("Optimistically deleted edge %s", edgeKey); } }); addNodeToLocalGraphAtom = (0, import_jotai12.atom)(null, (get, set, newNode) => { const graph = get(graphAtom); if (graph.hasNode(newNode.id)) { debug7("Node %s already exists, skipping", newNode.id); return; } const uiProps = newNode.ui_properties || {}; const attributes = { x: typeof uiProps.x === "number" ? uiProps.x : Math.random() * 800, y: typeof uiProps.y === "number" ? uiProps.y : Math.random() * 600, size: typeof uiProps.size === "number" ? uiProps.size : 15, width: typeof uiProps.width === "number" ? uiProps.width : 500, height: typeof uiProps.height === "number" ? uiProps.height : 500, color: typeof uiProps.color === "string" ? uiProps.color : "#ccc", label: newNode.label || newNode.node_type || newNode.id, zIndex: typeof uiProps.zIndex === "number" ? uiProps.zIndex : 0, dbData: newNode }; debug7("Adding node %s to local graph at (%d, %d)", newNode.id, attributes.x, attributes.y); graph.addNode(newNode.id, attributes); set(graphAtom, graph.copy()); set(graphUpdateVersionAtom, (v) => v + 1); set(nodePositionUpdateCounterAtom, (c) => c + 1); }); loadGraphFromDbAtom = (0, import_jotai12.atom)(null, (get, set, fetchedNodes, fetchedEdges) => { debug7("========== START SYNC =========="); debug7("Fetched nodes: %d, edges: %d", fetchedNodes.length, fetchedEdges.length); const currentGraphId = get(currentGraphIdAtom); if (fetchedNodes.length > 0 && fetchedNodes[0].graph_id !== currentGraphId) { debug7("Skipping sync - data belongs to different graph"); return; } const existingGraph = get(graphAtom); const isDragging = get(draggingNodeIdAtom) !== null; if (isDragging) { debug7("Skipping sync - drag in progress"); return; } const existingNodeIds = new Set(existingGraph.nodes()); const fetchedNodeIds = new Set(fetchedNodes.map((n) => n.id)); const hasAnyCommonNodes = Array.from(existingNodeIds).some((id) => fetchedNodeIds.has(id)); let graph; if (hasAnyCommonNodes && existingNodeIds.size > 0) { debug7("Merging DB data into existing graph"); graph = existingGraph.copy(); } else { debug7("Creating fresh graph (graph switch detected)"); graph = new import_graphology3.default(graphOptions); } const fetchedEdgeIds = new Set(fetchedEdges.map((e) => e.id)); if (hasAnyCommonNodes && existingNodeIds.size > 0) { graph.forEachNode((nodeId) => { if (!fetchedNodeIds.has(nodeId)) { debug7("Removing deleted node: %s", nodeId); graph.dropNode(nodeId); nodePositionAtomFamily.remove(nodeId); } }); } fetchedNodes.forEach((node) => { const uiProps = node.ui_properties || {}; const newX = typeof uiProps.x === "number" ? uiProps.x : Math.random() * 800; const newY = typeof uiProps.y === "number" ? uiProps.y : Math.random() * 600; if (graph.hasNode(node.id)) { const currentAttrs = graph.getNodeAttributes(node.id); const attributes = { x: newX, y: newY, size: typeof uiProps.size === "number" ? uiProps.size : currentAttrs.size, width: typeof uiProps.width === "number" ? uiProps.width : currentAttrs.width ?? 500, height: typeof uiProps.height === "number" ? uiProps.height : currentAttrs.height ?? 500, color: typeof uiProps.color === "string" ? uiProps.color : currentAttrs.color, label: node.label || node.node_type || node.id, zIndex: typeof uiProps.zIndex === "number" ? uiProps.zIndex : currentAttrs.zIndex, dbData: node }; graph.replaceNodeAttributes(node.id, attributes); } else { const attributes = { x: newX, y: newY, size: typeof uiProps.size === "number" ? uiProps.size : 15, width: typeof uiProps.width === "number" ? uiProps.width : 500, height: typeof uiProps.height === "number" ? uiProps.height : 500, color: typeof uiProps.color === "string" ? uiProps.color : "#ccc", label: node.label || node.node_type || node.id, zIndex: typeof uiProps.zIndex === "number" ? uiProps.zIndex : 0, dbData: node }; graph.addNode(node.id, attributes); } }); graph.forEachEdge((edgeId) => { if (!fetchedEdgeIds.has(edgeId)) { debug7("Removing deleted edge: %s", edgeId); graph.dropEdge(edgeId); } }); fetchedEdges.forEach((edge) => { if (graph.hasNode(edge.source_node_id) && graph.hasNode(edge.target_node_id)) { const uiProps = edge.ui_properties || {}; const attributes = { type: typeof uiProps.style === "string" ? uiProps.style : "solid", color: typeof uiProps.color === "string" ? uiProps.color : "#999", label: edge.edge_type ?? void 0, weight: typeof uiProps.weight === "number" ? uiProps.weight : 1, dbData: edge }; if (graph.hasEdge(edge.id)) { graph.replaceEdgeAttributes(edge.id, attributes); } else { try { graph.addEdgeWithKey(edge.id, edge.source_node_id, edge.target_node_id, attributes); } catch (e) { debug7("Failed to add edge %s: %o", edge.id, e); } } } }); set(graphAtom, graph); set(graphUpdateVersionAtom, (v) => v + 1); debug7("========== SYNC COMPLETE =========="); debug7("Final graph: %d nodes, %d edges", graph.order, graph.size); }); } }); // src/core/sync-store.ts var import_jotai13, debug8, 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"; import_jotai13 = require("jotai"); init_debug(); debug8 = createDebug("sync"); syncStatusAtom = (0, import_jotai13.atom)("synced"); pendingMutationsCountAtom = (0, import_jotai13.atom)(0); isOnlineAtom = (0, import_jotai13.atom)(typeof navigator !== "undefined" ? navigator.onLine : true); lastSyncErrorAtom = (0, import_jotai13.atom)(null); lastSyncTimeAtom = (0, import_jotai13.atom)(Date.now()); mutationQueueAtom = (0, import_jotai13.atom)([]); syncStateAtom = (0, import_jotai13.atom)((get) => ({ status: get(syncStatusAtom), pendingMutations: get(pendingMutationsCountAtom), lastError: get(lastSyncErrorAtom), lastSyncTime: get(lastSyncTimeAtom), isOnline: get(isOnlineAtom), queuedMutations: get(mutationQueueAtom).length })); startMutationAtom = (0, import_jotai13.atom)(null, (get, set) => { const currentCount = get(pendingMutationsCountAtom); const newCount = currentCount + 1; set(pendingMutationsCountAtom, newCount); debug8("Mutation started. Pending count: %d -> %d", currentCount, newCount); if (newCount > 0 && get(syncStatusAtom) !== "syncing") { set(syncStatusAtom, "syncing"); debug8("Status -> syncing"); } }); completeMutationAtom = (0, import_jotai13.atom)(null, (get, set, success = true) => { const currentCount = get(pendingMutationsCountAtom); const newCount = Math.max(0, currentCount - 1); set(pendingMutationsCountAtom, newCount); debug8("Mutation completed (success: %s). Pending count: %d -> %d", success, currentCount, newCount); if (success) { set(lastSyncTimeAtom, Date.now()); if (newCount === 0) { set(lastSyncErrorAtom, null); } } if (newCount === 0) { const isOnline = get(isOnlineAtom); const hasError = get(lastSyncErrorAtom) !== null; if (hasError) { set(syncStatusAtom, "error"); debug8("Status -> error"); } else if (!isOnline) { set(syncStatusAtom, "offline"); debug8("Status -> offline"); } else { set(syncStatusAtom, "synced"); debug8("Status -> synced"); } } }); trackMutationErrorAtom = (0, import_jotai13.atom)(null, (_get, set, error) => { set(lastSyncErrorAtom, error); debug8("Mutation failed: %s", error); }); setOnlineStatusAtom = (0, import_jotai13.atom)(null, (get, set, isOnline) => { set(isOnlineAtom, isOnline); const pendingCount = get(pendingMutationsCountAtom); const hasError = get(lastSyncErrorAtom) !== null; const queueLength = get(mutationQueueAtom).length; if (pendingCount === 0) { if (hasError || queueLength > 0) { set(syncStatusAtom, "error"); } else { set(syncStatusAtom, isOnline ? "synced" : "offline"); } } }); queueMutationAtom = (0, import_jotai13.atom)(null, (get, set, mutation) => { const queue = get(mutationQueueAtom); const newMutation = { ...mutation, id: crypto.randomUUID(), timestamp: Date.now(), retryCount: 0, maxRetries: mutation.maxRetries ?? 3 }; const newQueue = [...queue, newMutation]; set(mutationQueueAtom, newQueue); debug8("Queued mutation: %s. Queue size: %d", mutation.type, newQueue.length); if (get(pendingMutationsCountAtom) === 0) { set(syncStatusAtom, "error"); } return newMutation.id; }); dequeueMutationAtom = (0, import_jotai13.atom)(null, (get, set, mutationId) => { const queue = get(mutationQueueAtom); const newQueue = queue.filter((m) => m.id !== mutationId); set(mutationQueueAtom, newQueue); debug8("Dequeued mutation: %s. Queue size: %d", mutationId, newQueue.length); if (newQueue.length === 0 && get(pendingMutationsCountAtom) === 0 && get(lastSyncErrorAtom) === null) { set(syncStatusAtom, get(isOnlineAtom) ? "synced" : "offline"); } }); incrementRetryCountAtom = (0, import_jotai13.atom)(null, (get, set, mutationId) => { const queue = get(mutationQueueAtom); const newQueue = queue.map((m) => m.id === mutationId ? { ...m, retryCount: m.retryCount + 1 } : m); set(mutationQueueAtom, newQueue); }); getNextQueuedMutationAtom = (0, import_jotai13.atom)((get) => { const queue = get(mutationQueueAtom); return queue.find((m) => m.retryCount < m.maxRetries) ?? null; }); clearMutationQueueAtom = (0, import_jotai13.atom)(null, (get, set) => { set(mutationQueueAtom, []); debug8("Cleared mutation queue"); if (get(pendingMutationsCountAtom) === 0 && get(lastSyncErrorAtom) === null) { set(syncStatusAtom, get(isOnlineAtom) ? "synced" : "offline"); } }); } }); // src/core/interaction-store.ts var import_jotai14, inputModeAtom, keyboardInteractionModeAtom, interactionFeedbackAtom, pendingInputResolverAtom, resetInputModeAtom, resetKeyboardInteractionModeAtom, setKeyboardInteractionModeAtom, startPickNodeAtom, startPickNodesAtom, startPickPointAtom, provideInputAtom, updateInteractionFeedbackAtom, isPickingModeAtom, isPickNodeModeAtom; var init_interaction_store = __esm({ "src/core/interaction-store.ts"() { "use strict"; import_jotai14 = require("jotai"); inputModeAtom = (0, import_jotai14.atom)({ type: "normal" }); keyboardInteractionModeAtom = (0, import_jotai14.atom)("navigate"); interactionFeedbackAtom = (0, import_jotai14.atom)(null); pendingInputResolverAtom = (0, import_jotai14.atom)(null); resetInputModeAtom = (0, import_jotai14.atom)(null, (_get, set) => { set(inputModeAtom, { type: "normal" }); set(interactionFeedbackAtom, null); set(pendingInputResolverAtom, null); }); resetKeyboardInteractionModeAtom = (0, import_jotai14.atom)(null, (_get, set) => { set(keyboardInteractionModeAtom, "navigate"); }); setKeyboardInteractionModeAtom = (0, import_jotai14.atom)(null, (_get, set, mode) => { set(keyboardInteractionModeAtom, mode); }); startPickNodeAtom = (0, import_jotai14.atom)(null, (_get, set, options) => { set(inputModeAtom, { type: "pickNode", ...options }); }); startPickNodesAtom = (0, import_jotai14.atom)(null, (_get, set, options) => { set(inputModeAtom, { type: "pickNodes", ...options }); }); startPickPointAtom = (0, import_jotai14.atom)(null, (_get, set, options) => { set(inputModeAtom, { type: "pickPoint", ...options }); }); provideInputAtom = (0, import_jotai14.atom)(null, (get, set, value) => { set(pendingInputResolverAtom, value); }); updateInteractionFeedbackAtom = (0, import_jotai14.atom)(null, (get, set, feedback) => { const current = get(interactionFeedbackAtom); set(interactionFeedbackAtom, { ...current, ...feedback }); }); isPickingModeAtom = (0, import_jotai14.atom)((get) => { const mode = get(inputModeAtom); return mode.type !== "normal"; }); isPickNodeModeAtom = (0, import_jotai14.atom)((get) => { const mode = get(inputModeAtom); return mode.type === "pickNode" || mode.type === "pickNodes"; }); } }); // src/core/locked-node-store.ts var import_jotai15, lockedNodeIdAtom, lockedNodeDataAtom, lockedNodePageIndexAtom, lockedNodePageCountAtom, lockNodeAtom, unlockNodeAtom, nextLockedPageAtom, prevLockedPageAtom, goToLockedPageAtom, hasLockedNodeAtom; var init_locked_node_store = __esm({ "src/core/locked-node-store.ts"() { "use strict"; import_jotai15 = require("jotai"); init_graph_derived(); lockedNodeIdAtom = (0, import_jotai15.atom)(null); lockedNodeDataAtom = (0, import_jotai15.atom)((get) => { const id = get(lockedNodeIdAtom); if (!id) return null; const nodes = get(uiNodesAtom); return nodes.find((n) => n.id === id) || null; }); lockedNodePageIndexAtom = (0, import_jotai15.atom)(0); lockedNodePageCountAtom = (0, import_jotai15.atom)(1); lockNodeAtom = (0, import_jotai15.atom)(null, (_get, set, payload) => { set(lockedNodeIdAtom, payload.nodeId); set(lockedNodePageIndexAtom, 0); }); unlockNodeAtom = (0, import_jotai15.atom)(null, (_get, set) => { set(lockedNodeIdAtom, null); }); nextLockedPageAtom = (0, import_jotai15.atom)(null, (get, set) => { const current = get(lockedNodePageIndexAtom); const pageCount = get(lockedNodePageCountAtom); set(lockedNodePageIndexAtom, (current + 1) % pageCount); }); prevLockedPageAtom = (0, import_jotai15.atom)(null, (get, set) => { const current = get(lockedNodePageIndexAtom); const pageCount = get(lockedNodePageCountAtom); set(lockedNodePageIndexAtom, (current - 1 + pageCount) % pageCount); }); goToLockedPageAtom = (0, import_jotai15.atom)(null, (get, set, index) => { const pageCount = get(lockedNodePageCountAtom); if (index >= 0 && index < pageCount) { set(lockedNodePageIndexAtom, index); } }); hasLockedNodeAtom = (0, import_jotai15.atom)((get) => get(lockedNodeIdAtom) !== null); } }); // src/core/node-type-registry.tsx 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 import_compiler_runtime, import_react, import_jsx_runtime, nodeTypeRegistry, FallbackNodeTypeComponent; var init_node_type_registry = __esm({ "src/core/node-type-registry.tsx"() { "use strict"; import_compiler_runtime = require("react/compiler-runtime"); import_react = __toESM(require("react")); import_jsx_runtime = require("react/jsx-runtime"); nodeTypeRegistry = /* @__PURE__ */ new Map(); FallbackNodeTypeComponent = (t0) => { const $ = (0, import_compiler_runtime.c)(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__ */ (0, import_jsx_runtime.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__ */ (0, import_jsx_runtime.jsx)("div", { style: t4, children: t5 }); $[6] = t5; $[7] = t6; } else { t6 = $[7]; } let t7; if ($[8] !== t3 || $[9] !== t6) { t7 = /* @__PURE__ */ (0, import_jsx_runtime.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 }); var import_jotai16, canvasToastAtom, showToastAtom; var init_toast_store = __esm({ "src/core/toast-store.ts"() { "use strict"; import_jotai16 = require("jotai"); canvasToastAtom = (0, import_jotai16.atom)(null); showToastAtom = (0, import_jotai16.atom)(null, (_get, set, message) => { const id = `toast-${Date.now()}`; set(canvasToastAtom, { id, message, timestamp: Date.now() }); setTimeout(() => { set(canvasToastAtom, (current) => current?.id === id ? null : current); }, 2e3); }); } }); // src/core/snap-store.ts 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 import_jotai17, snapEnabledAtom, snapGridSizeAtom, snapTemporaryDisableAtom, isSnappingActiveAtom, toggleSnapAtom, setGridSizeAtom, snapAlignmentEnabledAtom, toggleAlignmentGuidesAtom, alignmentGuidesAtom, clearAlignmentGuidesAtom; var init_snap_store = __esm({ "src/core/snap-store.ts"() { "use strict"; import_jotai17 = require("jotai"); snapEnabledAtom = (0, import_jotai17.atom)(false); snapGridSizeAtom = (0, import_jotai17.atom)(20); snapTemporaryDisableAtom = (0, import_jotai17.atom)(false); isSnappingActiveAtom = (0, import_jotai17.atom)((get) => { return get(snapEnabledAtom) && !get(snapTemporaryDisableAtom); }); toggleSnapAtom = (0, import_jotai17.atom)(null, (get, set) => { set(snapEnabledAtom, !get(snapEnabledAtom)); }); setGridSizeAtom = (0, import_jotai17.atom)(null, (_get, set, size) => { set(snapGridSizeAtom, Math.max(5, Math.min(200, size))); }); snapAlignmentEnabledAtom = (0, import_jotai17.atom)(true); toggleAlignmentGuidesAtom = (0, import_jotai17.atom)(null, (get, set) => { set(snapAlignmentEnabledAtom, !get(snapAlignmentEnabledAtom)); }); alignmentGuidesAtom = (0, import_jotai17.atom)({ verticalGuides: [], horizontalGuides: [] }); clearAlignmentGuidesAtom = (0, import_jotai17.atom)(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) { debug9.warn("Action not found: %s", actionId); return { success: false, actionId, error: new Error(`Action not found: ${actionId}`) }; } if (action.requiresNode && !context.nodeId) { debug9.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) { debug9.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 { debug9.warn("deleteNode called but onDeleteNode callback not provided"); } }, isNodeLocked: (nodeId) => store.get(lockedNodeIdAtom) === nodeId, applyForceLayout: async () => { if (options.onApplyForceLayout) { await options.onApplyForceLayout(); } else { debug9.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 debug9; 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(); debug9 = 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 var import_jotai18, import_utils, debug10, 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"; import_jotai18 = require("jotai"); import_utils = require("jotai/utils"); init_settings_types(); init_debug(); init_settings_presets(); init_settings_presets(); debug10 = createDebug("settings"); DEFAULT_STATE = { mappings: DEFAULT_MAPPINGS, activePresetId: "default", customPresets: [], isPanelOpen: false, virtualizationEnabled: true }; canvasSettingsAtom = (0, import_utils.atomWithStorage)("@blinksgg/canvas/settings", DEFAULT_STATE); eventMappingsAtom = (0, import_jotai18.atom)((get) => get(canvasSettingsAtom).mappings); activePresetIdAtom = (0, import_jotai18.atom)((get) => get(canvasSettingsAtom).activePresetId); allPresetsAtom = (0, import_jotai18.atom)((get) => { const state = get(canvasSettingsAtom); return [...BUILT_IN_PRESETS, ...state.customPresets]; }); activePresetAtom = (0, import_jotai18.atom)((get) => { const presetId = get(activePresetIdAtom); if (!presetId) return null; const allPresets = get(allPresetsAtom); return allPresets.find((p) => p.id === presetId) || null; }); isPanelOpenAtom = (0, import_jotai18.atom)((get) => get(canvasSettingsAtom).isPanelOpen); virtualizationEnabledAtom = (0, import_jotai18.atom)((get) => get(canvasSettingsAtom).virtualizationEnabled ?? true); hasUnsavedChangesAtom = (0, import_jotai18.atom)((get) => { const state = get(canvasSettingsAtom); const activePreset = get(activePresetAtom); if (!activePreset) return true; const events = Object.values(CanvasEventType); return events.some((event) => state.mappings[event] !== activePreset.mappings[event]); }); setEventMappingAtom = (0, import_jotai18.atom)(null, (get, set, { event, actionId }) => { const current = get(canvasSettingsAtom); set(canvasSettingsAtom, { ...current, mappings: { ...current.mappings, [event]: actionId }, // Clear active preset since mappings have changed activePresetId: null }); }); applyPresetAtom = (0, import_jotai18.atom)(null, (get, set, presetId) => { const allPresets = get(allPresetsAtom); const preset = allPresets.find((p) => p.id === presetId); if (!preset) { debug10.warn("Preset not found: %s", presetId); return; } const current = get(canvasSettingsAtom); set(canvasSettingsAtom, { ...current, mappings: { ...preset.mappings }, activePresetId: presetId }); }); saveAsPresetAtom = (0, import_jotai18.atom)(null, (get, set, { name, description }) => { const current = get(canvasSettingsAtom); const id = `custom-${Date.now()}`; const newPreset = { id, name, description, mappings: { ...current.mappings }, isBuiltIn: false }; set(canvasSettingsAtom, { ...current, customPresets: [...current.customPresets, newPreset], activePresetId: id }); return id; }); updatePresetAtom = (0, import_jotai18.atom)(null, (get, set, presetId) => { const current = get(canvasSettingsAtom); const presetIndex = current.customPresets.findIndex((p) => p.id === presetId); if (presetIndex === -1) { debug10.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 = (0, import_jotai18.atom)(null, (get, set, presetId) => { const current = get(canvasSettingsAtom); const newCustomPresets = current.customPresets.filter((p) => p.id !== presetId); if (newCustomPresets.length === current.customPresets.length) { debug10.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 = (0, import_jotai18.atom)(null, (get, set) => { const current = get(canvasSettingsAtom); set(canvasSettingsAtom, { ...current, mappings: DEFAULT_MAPPINGS, activePresetId: "default" }); }); togglePanelAtom = (0, import_jotai18.atom)(null, (get, set) => { const current = get(canvasSettingsAtom); set(canvasSettingsAtom, { ...current, isPanelOpen: !current.isPanelOpen }); }); setPanelOpenAtom = (0, import_jotai18.atom)(null, (get, set, isOpen) => { const current = get(canvasSettingsAtom); set(canvasSettingsAtom, { ...current, isPanelOpen: isOpen }); }); setVirtualizationEnabledAtom = (0, import_jotai18.atom)(null, (get, set, enabled) => { const current = get(canvasSettingsAtom); set(canvasSettingsAtom, { ...current, virtualizationEnabled: enabled }); }); toggleVirtualizationAtom = (0, import_jotai18.atom)(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 }); 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 import_graphology4.default(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 import_graphology4, SNAPSHOT_VERSION; var init_canvas_serializer = __esm({ "src/core/canvas-serializer.ts"() { "use strict"; import_graphology4 = __toESM(require("graphology")); 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 }); 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 import_jotai19, debug11, PASTE_OFFSET, clipboardAtom, hasClipboardContentAtom, clipboardNodeCountAtom, copyToClipboardAtom, cutToClipboardAtom, pasteFromClipboardAtom, duplicateSelectionAtom, clearClipboardAtom; var init_clipboard_store = __esm({ "src/core/clipboard-store.ts"() { "use strict"; import_jotai19 = require("jotai"); init_graph_store(); init_graph_mutations(); init_graph_mutations_edges(); init_selection_store(); init_history_store(); init_debug(); debug11 = createDebug("clipboard"); PASTE_OFFSET = { x: 50, y: 50 }; clipboardAtom = (0, import_jotai19.atom)(null); hasClipboardContentAtom = (0, import_jotai19.atom)((get) => get(clipboardAtom) !== null); clipboardNodeCountAtom = (0, import_jotai19.atom)((get) => { const clipboard = get(clipboardAtom); return clipboard?.nodes.length ?? 0; }); copyToClipboardAtom = (0, import_jotai19.atom)(null, (get, set, nodeIds) => { const selectedIds = nodeIds ?? Array.from(get(selectedNodeIdsAtom)); if (selectedIds.length === 0) { debug11("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)) { debug11("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); debug11("Copied %d nodes and %d edges to clipboard", nodes.length, edges.length); }); cutToClipboardAtom = (0, import_jotai19.atom)(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); debug11("Cut %d nodes \u2014 copied to clipboard and deleted from graph", selectedIds.length); }); pasteFromClipboardAtom = (0, import_jotai19.atom)(null, (get, set, offset) => { const clipboard = get(clipboardAtom); if (!clipboard || clipboard.nodes.length === 0) { debug11("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 } }; debug11("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) { debug11("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() }; debug11("Pasting edge %s -> %s (from %s to %s)", edgeData.dbData.id, newEdgeId, newSourceId, newTargetId); set(addEdgeToLocalGraphAtom, newDbEdge); } set(clearSelectionAtom); set(addNodesToSelectionAtom, newNodeIds); debug11("Pasted %d nodes and %d edges", newNodeIds.length, clipboard.edges.length); return newNodeIds; }); duplicateSelectionAtom = (0, import_jotai19.atom)(null, (get, set) => { set(copyToClipboardAtom); return set(pasteFromClipboardAtom); }); clearClipboardAtom = (0, import_jotai19.atom)(null, (_get, set) => { set(clipboardAtom, null); debug11("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 var import_jotai20, VIRTUALIZATION_BUFFER, spatialIndexAtom, visibleBoundsAtom, visibleNodeKeysAtom, visibleEdgeKeysAtom, virtualizationMetricsAtom; var init_virtualization_store = __esm({ "src/core/virtualization-store.ts"() { "use strict"; import_jotai20 = require("jotai"); 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 = (0, import_jotai20.atom)((get) => { get(graphUpdateVersionAtom); get(nodePositionUpdateCounterAtom); const graph = get(graphAtom); const grid = new SpatialGrid(500); graph.forEachNode((nodeId, attrs) => { const a = attrs; grid.insert(nodeId, a.x, a.y, a.width || 200, a.height || 100); }); return grid; }); visibleBoundsAtom = (0, import_jotai20.atom)((get) => { const viewport = get(viewportRectAtom); const pan = get(panAtom); const zoom = get(zoomAtom); if (!viewport || zoom === 0) { return null; } const buffer = VIRTUALIZATION_BUFFER; return { minX: (-buffer - pan.x) / zoom, minY: (-buffer - pan.y) / zoom, maxX: (viewport.width + buffer - pan.x) / zoom, maxY: (viewport.height + buffer - pan.y) / zoom }; }); visibleNodeKeysAtom = (0, import_jotai20.atom)((get) => { const end = canvasMark("virtualization-cull"); const enabled = get(virtualizationEnabledAtom); const allKeys = get(nodeKeysAtom); if (!enabled) { end(); return allKeys; } const bounds = get(visibleBoundsAtom); if (!bounds) { end(); return allKeys; } const grid = get(spatialIndexAtom); const visibleSet = grid.query(bounds); const result = allKeys.filter((k) => visibleSet.has(k)); end(); return result; }); visibleEdgeKeysAtom = (0, import_jotai20.atom)((get) => { const enabled = get(virtualizationEnabledAtom); const allEdgeKeys = get(edgeKeysAtom); const edgeCreation = get(edgeCreationAtom); const remap = get(collapsedEdgeRemapAtom); const tempEdgeKey = edgeCreation.isCreating ? "temp-creating-edge" : null; get(graphUpdateVersionAtom); const graph = get(graphAtom); const filteredEdges = allEdgeKeys.filter((edgeKey) => { const source = graph.source(edgeKey); const target = graph.target(edgeKey); const effectiveSource = remap.get(source) ?? source; const effectiveTarget = remap.get(target) ?? target; if (effectiveSource === effectiveTarget) return false; return true; }); if (!enabled) { return tempEdgeKey ? [...filteredEdges, tempEdgeKey] : filteredEdges; } const visibleNodeKeys = get(visibleNodeKeysAtom); const visibleNodeSet = new Set(visibleNodeKeys); const visibleEdges = filteredEdges.filter((edgeKey) => { const source = graph.source(edgeKey); const target = graph.target(edgeKey); const effectiveSource = remap.get(source) ?? source; const effectiveTarget = remap.get(target) ?? target; return visibleNodeSet.has(effectiveSource) && visibleNodeSet.has(effectiveTarget); }); return tempEdgeKey ? [...visibleEdges, tempEdgeKey] : visibleEdges; }); virtualizationMetricsAtom = (0, import_jotai20.atom)((get) => { const enabled = get(virtualizationEnabledAtom); const totalNodes = get(nodeKeysAtom).length; const totalEdges = get(edgeKeysAtom).length; const visibleNodes = get(visibleNodeKeysAtom).length; const visibleEdges = get(visibleEdgeKeysAtom).length; const bounds = get(visibleBoundsAtom); return { enabled, totalNodes, totalEdges, visibleNodes, visibleEdges, culledNodes: totalNodes - visibleNodes, culledEdges: totalEdges - visibleEdges, bounds }; }); } }); // src/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 var import_jotai21, activePointersAtom, primaryInputSourceAtom, inputCapabilitiesAtom, isStylusActiveAtom, isMultiTouchAtom, fingerCountAtom, isTouchDeviceAtom, pointerDownAtom, pointerUpAtom, clearPointersAtom; var init_input_store = __esm({ "src/core/input-store.ts"() { "use strict"; import_jotai21 = require("jotai"); init_input_classifier(); activePointersAtom = (0, import_jotai21.atom)(/* @__PURE__ */ new Map()); primaryInputSourceAtom = (0, import_jotai21.atom)("mouse"); inputCapabilitiesAtom = (0, import_jotai21.atom)(detectInputCapabilities()); isStylusActiveAtom = (0, import_jotai21.atom)((get) => { const pointers = get(activePointersAtom); for (const [, pointer] of pointers) { if (pointer.source === "pencil") return true; } return false; }); isMultiTouchAtom = (0, import_jotai21.atom)((get) => { const pointers = get(activePointersAtom); let fingerCount = 0; for (const [, pointer] of pointers) { if (pointer.source === "finger") fingerCount++; } return fingerCount > 1; }); fingerCountAtom = (0, import_jotai21.atom)((get) => { const pointers = get(activePointersAtom); let count = 0; for (const [, pointer] of pointers) { if (pointer.source === "finger") count++; } return count; }); isTouchDeviceAtom = (0, import_jotai21.atom)((get) => { const caps = get(inputCapabilitiesAtom); return caps.hasTouch; }); pointerDownAtom = (0, import_jotai21.atom)(null, (get, set, pointer) => { const pointers = new Map(get(activePointersAtom)); pointers.set(pointer.pointerId, pointer); set(activePointersAtom, pointers); set(primaryInputSourceAtom, pointer.source); if (pointer.source === "pencil") { const caps = get(inputCapabilitiesAtom); if (!caps.hasStylus) { set(inputCapabilitiesAtom, { ...caps, hasStylus: true }); } } }); pointerUpAtom = (0, import_jotai21.atom)(null, (get, set, pointerId) => { const pointers = new Map(get(activePointersAtom)); pointers.delete(pointerId); set(activePointersAtom, pointers); }); clearPointersAtom = (0, import_jotai21.atom)(null, (_get, set) => { set(activePointersAtom, /* @__PURE__ */ new Map()); }); } }); // src/core/selection-path-store.ts 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 import_jotai22, selectionPathAtom, isSelectingAtom, startSelectionAtom, updateSelectionAtom, cancelSelectionAtom, endSelectionAtom, selectionRectAtom; var init_selection_path_store = __esm({ "src/core/selection-path-store.ts"() { "use strict"; import_jotai22 = require("jotai"); init_graph_derived(); init_selection_store(); selectionPathAtom = (0, import_jotai22.atom)(null); isSelectingAtom = (0, import_jotai22.atom)((get) => get(selectionPathAtom) !== null); startSelectionAtom = (0, import_jotai22.atom)(null, (_get, set, { type, point }) => { set(selectionPathAtom, { type, points: [point] }); }); updateSelectionAtom = (0, import_jotai22.atom)(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 = (0, import_jotai22.atom)(null, (_get, set) => { set(selectionPathAtom, null); }); endSelectionAtom = (0, import_jotai22.atom)(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 = (0, import_jotai22.atom)((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 }); 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 import_jotai23, 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"; import_jotai23 = require("jotai"); init_graph_derived(); init_graph_store(); init_viewport_store(); init_selection_store(); searchQueryAtom = (0, import_jotai23.atom)(""); setSearchQueryAtom = (0, import_jotai23.atom)(null, (_get, set, query) => { set(searchQueryAtom, query); set(highlightedSearchIndexAtom, 0); }); clearSearchAtom = (0, import_jotai23.atom)(null, (_get, set) => { set(searchQueryAtom, ""); set(highlightedSearchIndexAtom, 0); }); searchResultsAtom = (0, import_jotai23.atom)((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 = (0, import_jotai23.atom)((get) => { return Array.from(get(searchResultsAtom)); }); searchResultCountAtom = (0, import_jotai23.atom)((get) => { return get(searchResultsAtom).size; }); searchEdgeResultsAtom = (0, import_jotai23.atom)((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 = (0, import_jotai23.atom)((get) => { return get(searchEdgeResultsAtom).size; }); isFilterActiveAtom = (0, import_jotai23.atom)((get) => { return get(searchQueryAtom).trim().length > 0; }); searchTotalResultCountAtom = (0, import_jotai23.atom)((get) => { return get(searchResultCountAtom) + get(searchEdgeResultCountAtom); }); highlightedSearchIndexAtom = (0, import_jotai23.atom)(0); nextSearchResultAtom = (0, import_jotai23.atom)(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 = (0, import_jotai23.atom)(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 = (0, import_jotai23.atom)((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 specificity2 = matchSpecificity(rule.pattern, desc); if (specificity2 < 0) continue; const effectiveScore = specificity2 * 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 var import_jotai24, import_utils2, 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"; import_jotai24 = require("jotai"); import_utils2 = require("jotai/utils"); init_gesture_rules(); DEFAULT_RULE_STATE = { customRules: [], palmRejection: true }; gestureRuleSettingsAtom = (0, import_utils2.atomWithStorage)("canvas-gesture-rules", DEFAULT_RULE_STATE); consumerGestureRulesAtom = (0, import_jotai24.atom)([]); gestureRulesAtom = (0, import_jotai24.atom)((get) => { const settings = get(gestureRuleSettingsAtom); const consumerRules = get(consumerGestureRulesAtom); let rules = mergeRules(DEFAULT_GESTURE_RULES, settings.customRules); if (consumerRules.length > 0) { rules = mergeRules(rules, consumerRules); } return rules; }); gestureRuleIndexAtom = (0, import_jotai24.atom)((get) => { return buildRuleIndex(get(gestureRulesAtom)); }); palmRejectionEnabledAtom = (0, import_jotai24.atom)((get) => get(gestureRuleSettingsAtom).palmRejection, (get, set, enabled) => { const current = get(gestureRuleSettingsAtom); set(gestureRuleSettingsAtom, { ...current, palmRejection: enabled }); }); addGestureRuleAtom = (0, import_jotai24.atom)(null, (get, set, rule) => { const current = get(gestureRuleSettingsAtom); const existing = current.customRules.findIndex((r) => r.id === rule.id); const newRules = [...current.customRules]; if (existing >= 0) { newRules[existing] = rule; } else { newRules.push(rule); } set(gestureRuleSettingsAtom, { ...current, customRules: newRules }); }); removeGestureRuleAtom = (0, import_jotai24.atom)(null, (get, set, ruleId) => { const current = get(gestureRuleSettingsAtom); set(gestureRuleSettingsAtom, { ...current, customRules: current.customRules.filter((r) => r.id !== ruleId) }); }); updateGestureRuleAtom = (0, import_jotai24.atom)(null, (get, set, { id, updates }) => { const current = get(gestureRuleSettingsAtom); const index = current.customRules.findIndex((r) => r.id === id); if (index < 0) return; const newRules = [...current.customRules]; newRules[index] = { ...newRules[index], ...updates }; set(gestureRuleSettingsAtom, { ...current, customRules: newRules }); }); resetGestureRulesAtom = (0, import_jotai24.atom)(null, (get, set) => { const current = get(gestureRuleSettingsAtom); set(gestureRuleSettingsAtom, { ...current, customRules: [] }); }); } }); // src/core/external-keyboard-store.ts var import_jotai25, hasExternalKeyboardAtom, watchExternalKeyboardAtom; var init_external_keyboard_store = __esm({ "src/core/external-keyboard-store.ts"() { "use strict"; import_jotai25 = require("jotai"); hasExternalKeyboardAtom = (0, import_jotai25.atom)(false); watchExternalKeyboardAtom = (0, import_jotai25.atom)(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 function isKeyInputEvent(event) { return event.kind === "key"; } function isPointerGestureEvent(event) { return event.kind !== "key"; } 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); } function getHandler(actionId) { return handlers.get(actionId); } function clearHandlers() { handlers.clear(); } function dispatch(event, resolution) { if (resolution.actionId === "none") return true; const handler = handlers.get(resolution.actionId); if (!handler) return false; if (typeof handler === "function") { if (isKeyInputEvent(event) && event.phase === "down" || !isKeyInputEvent(event) && (event.phase === "start" || event.phase === "instant")) { handler(event); } return true; } routePhase(handler, event.phase, event); return true; } function routePhase(handler, phase, event) { if (isKeyInputEvent(event)) { routeKeyPhase(handler, phase, event); return; } switch (phase) { case "start": handler.onStart?.(event); break; case "move": handler.onMove?.(event); break; case "end": handler.onEnd?.(event); break; case "instant": handler.onInstant?.(event); break; case "cancel": handler.onCancel?.(event); break; } } function routeKeyPhase(handler, phase, event) { switch (phase) { case "down": handler.onDown?.(event); break; case "up": handler.onUp?.(event); break; } } var handlers; var init_dispatcher = __esm({ "src/gestures/dispatcher.ts"() { "use strict"; init_types2(); handlers = /* @__PURE__ */ new Map(); } }); // 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/utils/edge-path-calculators.ts 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 bezierHorizontal, bezierVertical, bezierSmart, straight, stepHorizontal, stepVertical, stepSmart, smoothStep, defaultEdgePathCalculator; var init_edge_path_calculators = __esm({ "src/utils/edge-path-calculators.ts"() { "use strict"; 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 }; }; 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 }; }; 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); }; 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 }; }; 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 }; }; 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 }; }; 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); }; 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 }; }; defaultEdgePathCalculator = bezierHorizontal; } }); // src/utils/edge-path-registry.ts 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(); } 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) { debug12("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() }); debug12("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); debug12("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); } debug12("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 debug12, 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(); debug12 = 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: () => inputModeAtom, 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: () => pendingInputResolverAtom, pendingMutationsCountAtom: () => pendingMutationsCountAtom, perfEnabledAtom: () => perfEnabledAtom, pointInPolygon: () => pointInPolygon, pointerDownAtom: () => pointerDownAtom, pointerUpAtom: () => pointerUpAtom, preDragNodeAttributesAtom: () => preDragNodeAttributesAtom, prefersReducedMotionAtom: () => prefersReducedMotionAtom, prevLockedPageAtom: () => prevLockedPageAtom, prevSearchResultAtom: () => prevSearchResultAtom, primaryInputSourceAtom: () => primaryInputSourceAtom, provideInputAtom: () => provideInputAtom, 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/index.ts var index_exports = {}; __export(index_exports, { ActionCategory: () => ActionCategory, AlignmentGuides: () => AlignmentGuides, BUILT_IN_PRESETS: () => BUILT_IN_PRESETS, BuiltInActionId: () => BuiltInActionId, CANVAS_VERSION: () => CANVAS_VERSION, Canvas: () => Canvas, CanvasAnimations: () => CanvasAnimations, CanvasDbProvider: () => CanvasDbProvider, CanvasEventType: () => CanvasEventType, CanvasProvider: () => CanvasProvider, CanvasStyleProvider: () => CanvasStyleProvider, CanvasToast: () => CanvasToast, ComboboxOption: () => ComboboxOption, ComboboxSearch: () => ComboboxSearch, CommandFeedbackOverlay: () => CommandFeedbackOverlay, CommandInputCollector: () => CommandInputCollector, CommandLine: () => CommandLine, CommandProvider: () => CommandProvider, CommandSearch: () => CommandSearch, ConnectedNode: () => ConnectedNode, ConnectedNodeRenderer: () => ConnectedNodeRenderer, ContextMenuAction: () => ContextMenuAction, ContextMenuDivider: () => ContextMenuDivider, Crosshairs: () => Crosshairs, DEFAULT_GESTURE_RULES: () => DEFAULT_GESTURE_RULES, DEFAULT_MAPPINGS: () => DEFAULT_MAPPINGS, DEFAULT_PORT: () => DEFAULT_PORT, DEFAULT_SHORTCUTS: () => DEFAULT_SHORTCUTS, DeleteIcon: () => DeleteIcon, EDGE_ANIMATION_DURATION: () => EDGE_ANIMATION_DURATION, EVENT_TYPE_INFO: () => EVENT_TYPE_INFO, EdgeLabel: () => EdgeLabel, EdgeLabelEditor: () => EdgeLabelEditor, EdgeOverlay: () => EdgeOverlay, EdgePath: () => EdgePath, EdgePreviewLine: () => EdgePreviewLine, EdgeRenderer: () => EdgeRenderer, EventMappingRow: () => EventMappingRow, FallbackNodeTypeComponent: () => FallbackNodeTypeComponent, FitToBoundsMode: () => FitToBoundsMode, FitToViewIcon: () => FitToViewIcon, Grid: () => Grid, GroupNode: () => GroupNode, HIT_TARGET_SIZES: () => HIT_TARGET_SIZES, InMemoryStorageAdapter: () => InMemoryStorageAdapter, LockedNodeOverlay: () => LockedNodeOverlay, Minimap: () => Minimap, Node: () => Node, NodeContextMenu: () => NodeContextMenu, NodeErrorBoundary: () => NodeErrorBoundary, NodePorts: () => NodePorts, NodeRenderer: () => NodeRenderer, NodeTypeCombobox: () => NodeTypeCombobox, NoteNode: () => NoteNode, PASTE_OFFSET: () => PASTE_OFFSET, PluginError: () => PluginError, PortBar: () => PortBar, PortHandle: () => PortHandle, ResizeHandle: () => ResizeHandle, SNAPSHOT_VERSION: () => SNAPSHOT_VERSION, SelectionOverlay: () => SelectionOverlay, SettingsEventMap: () => SettingsEventMap, SettingsPanel: () => SettingsPanel, SettingsPresets: () => SettingsPresets, SpatialGrid: () => SpatialGrid, SupabaseStorageAdapter: () => SupabaseStorageAdapter, TouchActionButton: () => TouchActionButton, VIRTUALIZATION_BUFFER: () => VIRTUALIZATION_BUFFER, Viewport: () => Viewport, ViewportControls: () => ViewportControls, 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, areNodesClose: () => areNodesClose, arePortsCompatible: () => arePortsCompatible, autoResizeGroupAtom: () => autoResizeGroupAtom, bezierHorizontal: () => bezierHorizontal, bezierSmart: () => bezierSmart, bezierVertical: () => bezierVertical, buildActionHelpers: () => buildActionHelpers, buildRuleIndex: () => buildRuleIndex, calculateBounds: () => calculateBounds, calculatePortPosition: () => calculatePortPosition, canPortAcceptConnection: () => canPortAcceptConnection, canRedoAtom: () => canRedoAtom, canUndoAtom: () => canUndoAtom, cancelCommand: () => cancelCommand, cancelSelectionAtom: () => cancelSelectionAtom, canvasIsDarkAtom: () => canvasIsDarkAtom, canvasKeys: () => canvasKeys, canvasMark: () => canvasMark, canvasSettingsAtom: () => canvasSettingsAtom, canvasStyleOverridesAtom: () => canvasStyleOverridesAtom, canvasStylesAtom: () => canvasStylesAtom, canvasToastAtom: () => canvasToastAtom, canvasVersion: () => canvasVersion, canvasWrap: () => canvasWrap, centerOnNodeAtom: () => centerOnNodeAtom, checkNodesOverlap: () => checkNodesOverlap, classifyPointer: () => classifyPointer, cleanupAllNodePositionsAtom: () => cleanupAllNodePositionsAtom, cleanupNodePositionAtom: () => cleanupNodePositionAtom, clearActions: () => clearActions, clearAlignmentGuidesAtom: () => clearAlignmentGuidesAtom, clearClipboardAtom: () => clearClipboardAtom, clearCommandErrorAtom: () => clearCommandErrorAtom, clearCustomEdgePathCalculators: () => clearCustomEdgePathCalculators, clearEdgeSelectionAtom: () => clearEdgeSelectionAtom, clearGraphOnSwitchAtom: () => clearGraphOnSwitchAtom, clearHistoryAtom: () => clearHistoryAtom, clearMutationQueueAtom: () => clearMutationQueueAtom, clearNodeTypeRegistry: () => clearNodeTypeRegistry, clearPendingState: () => clearPendingState, clearPlugins: () => clearPlugins, clearPointersAtom: () => clearPointersAtom, clearSearchAtom: () => clearSearchAtom, clearSelectionAtom: () => clearSelectionAtom, clearSelectionCommand: () => clearSelectionCommand, clipboardAtom: () => clipboardAtom, clipboardNodeCountAtom: () => clipboardNodeCountAtom, closeCommandLineAtom: () => closeCommandLineAtom, collapseGroupAtom: () => collapseGroupAtom, collapsedEdgeRemapAtom: () => collapsedEdgeRemapAtom, collapsedGroupsAtom: () => collapsedGroupsAtom, collectInput: () => collectInput, commandFeedbackAtom: () => commandFeedbackAtom, commandHistoryAtom: () => commandHistoryAtom, commandLineStateAtom: () => commandLineStateAtom, commandLineVisibleAtom: () => commandLineVisibleAtom, commandProgressAtom: () => commandProgressAtom, commandRegistry: () => commandRegistry, completeMutationAtom: () => completeMutationAtom, conditionalSnap: () => conditionalSnap, consumerGestureRulesAtom: () => consumerGestureRulesAtom, copyCommand: () => copyCommand, copyToClipboardAtom: () => copyToClipboardAtom, createActionContext: () => createActionContext, createActionContextFromReactEvent: () => createActionContextFromReactEvent, createActionContextFromTouchEvent: () => createActionContextFromTouchEvent, createCanvasAPI: () => createCanvasAPI, createComponentRegistry: () => createComponentRegistry, createDebug: () => createDebug, createFallbackComponent: () => createFallbackComponent, createGraphEdge: () => createGraphEdge, createGraphNode: () => createGraphNode, createSnapshot: () => createSnapshot, currentGraphIdAtom: () => currentGraphIdAtom, currentInputAtom: () => currentInputAtom, cutCommand: () => cutCommand, cutToClipboardAtom: () => cutToClipboardAtom, debug: () => debug, defaultDarkStyles: () => defaultDarkStyles, defaultEdgePathCalculator: () => defaultEdgePathCalculator, defaultLightStyles: () => defaultLightStyles, deleteGraphEdge: () => deleteGraphEdge, deleteGraphNode: () => deleteGraphNode, deletePresetAtom: () => deletePresetAtom, deleteSelectedCommand: () => deleteSelectedCommand, departingEdgesAtom: () => departingEdgesAtom, dequeueMutationAtom: () => dequeueMutationAtom, detectInputCapabilities: () => detectInputCapabilities, draggingNodeIdAtom: () => draggingNodeIdAtom, dropTargetNodeIdAtom: () => dropTargetNodeIdAtom, duplicateCommand: () => duplicateCommand, duplicateSelectionAtom: () => duplicateSelectionAtom, edgeCreationAtom: () => edgeCreationAtom, edgeFamilyAtom: () => edgeFamilyAtom, edgeKeysAtom: () => edgeKeysAtom, edgeKeysWithTempEdgeAtom: () => edgeKeysWithTempEdgeAtom, editingEdgeLabelAtom: () => editingEdgeLabelAtom, endNodeDragAtom: () => endNodeDragAtom, endSelectionAtom: () => endSelectionAtom, eventMappingsAtom: () => eventMappingsAtom, executeAction: () => executeAction, executeCommandInteractive: () => executeCommandInteractive, expandGroupAtom: () => expandGroupAtom, exportGraph: () => exportGraph, fetchGraphEdges: () => fetchGraphEdges, fetchGraphNode: () => fetchGraphNode, fetchGraphNodes: () => fetchGraphNodes, findAlignmentGuides: () => findAlignmentGuides, fingerCountAtom: () => fingerCountAtom, fitSelectionCommand: () => fitSelectionCommand, fitToBoundsAtom: () => fitToBoundsAtom, fitToViewCommand: () => fitToViewCommand, focusedNodeIdAtom: () => focusedNodeIdAtom, forceLayoutCommand: () => forceLayoutCommand, formatRuleLabel: () => formatRuleLabel, fuzzyMatch: () => fuzzyMatch, gestureRuleIndexAtom: () => gestureRuleIndexAtom, gestureRuleSettingsAtom: () => gestureRuleSettingsAtom, gestureRulesAtom: () => gestureRulesAtom, gestures: () => gestures_exports, getAction: () => getAction, getActionForEvent: () => getActionForEvent, getActionsByCategories: () => getActionsByCategories, getActionsByCategory: () => getActionsByCategory, getAllActions: () => getAllActions, getAllPlugins: () => getAllPlugins, getCustomEdgePathCalculatorNames: () => getCustomEdgePathCalculatorNames, getEdgePathCalculator: () => getEdgePathCalculator, getGestureThresholds: () => getGestureThresholds, getHitTargetSize: () => getHitTargetSize, getNextQueuedMutationAtom: () => getNextQueuedMutationAtom, getNodeBounds: () => getNodeBounds, getNodeCenter: () => getNodeCenter, getNodeDescendants: () => getNodeDescendants, getNodeGestureConfig: () => getNodeGestureConfig, getNodePorts: () => getNodePorts, getNodeTypeComponent: () => getNodeTypeComponent, getPendingState: () => getPendingState, getPlugin: () => getPlugin, getPluginGestureContexts: () => getPluginGestureContexts, getPluginIds: () => getPluginIds, getPortWorldPosition: () => getPortWorldPosition, getRegisteredNodeTypes: () => getRegisteredNodeTypes, getSnapGuides: () => getSnapGuides, getViewportGestureConfig: () => getViewportGestureConfig, goBackInputAtom: () => goBackInputAtom, goToLockedPageAtom: () => goToLockedPageAtom, graphAtom: () => graphAtom, graphOptions: () => graphOptions, graphUpdateVersionAtom: () => graphUpdateVersionAtom, groupChildCountAtom: () => groupChildCountAtom, groupSelectedNodesAtom: () => groupSelectedNodesAtom, handleNodePointerDownSelectionAtom: () => handleNodePointerDownSelectionAtom, handlePickedNode: () => handlePickedNode, handlePickedPoint: () => handlePickedPoint, hasAction: () => hasAction, hasClipboardContentAtom: () => hasClipboardContentAtom, hasCustomEdgePathCalculator: () => hasCustomEdgePathCalculator, hasExternalKeyboardAtom: () => hasExternalKeyboardAtom, hasFocusedNodeAtom: () => hasFocusedNodeAtom, hasLockedNodeAtom: () => hasLockedNodeAtom, hasNodeTypeComponent: () => hasNodeTypeComponent, hasPendingMutations: () => hasPendingMutations, hasPlugin: () => hasPlugin, hasSelectionAtom: () => hasSelectionAtom, hasUnsavedChangesAtom: () => hasUnsavedChangesAtom, highestZIndexAtom: () => highestZIndexAtom, highlightedSearchIndexAtom: () => highlightedSearchIndexAtom, highlightedSearchNodeIdAtom: () => highlightedSearchNodeIdAtom, historyLabelsAtom: () => historyLabelsAtom, historyStateAtom: () => historyStateAtom, hitTestNode: () => hitTestNode, hitTestPort: () => hitTestPort, importGraph: () => importGraph, incrementRetryCountAtom: () => incrementRetryCountAtom, inputCapabilitiesAtom: () => inputCapabilitiesAtom, inputModeAtom: () => inputModeAtom, interactionFeedbackAtom: () => interactionFeedbackAtom, invertDelta: () => invertDelta, invertSelectionCommand: () => invertSelectionCommand, isCommandActiveAtom: () => isCommandActiveAtom, 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, mergeWithDefaults: () => mergeWithDefaults, moveNodesToGroupAtom: () => moveNodesToGroupAtom, mutationQueueAtom: () => mutationQueueAtom, nestNodesOnDropAtom: () => nestNodesOnDropAtom, nextLockedPageAtom: () => nextLockedPageAtom, nextSearchResultAtom: () => nextSearchResultAtom, nodeChildrenAtom: () => nodeChildrenAtom, nodeFamilyAtom: () => nodeFamilyAtom, nodeKeysAtom: () => nodeKeysAtom, nodeParentAtom: () => nodeParentAtom, nodePositionAtomFamily: () => nodePositionAtomFamily, nodePositionUpdateCounterAtom: () => nodePositionUpdateCounterAtom, openCommandLineAtom: () => openCommandLineAtom, optimisticDeleteEdgeAtom: () => optimisticDeleteEdgeAtom, optimisticDeleteNodeAtom: () => optimisticDeleteNodeAtom, palmRejectionEnabledAtom: () => palmRejectionEnabledAtom, panAtom: () => panAtom, pasteCommand: () => pasteCommand, pasteFromClipboardAtom: () => pasteFromClipboardAtom, pendingInputResolverAtom: () => pendingInputResolverAtom, pendingMutationsCountAtom: () => pendingMutationsCountAtom, pendingNodeMutations: () => pendingNodeMutations, perfEnabledAtom: () => perfEnabledAtom, pointInPolygon: () => pointInPolygon, pointerDownAtom: () => pointerDownAtom, pointerUpAtom: () => pointerUpAtom, preDragNodeAttributesAtom: () => preDragNodeAttributesAtom, prefersReducedMotionAtom: () => prefersReducedMotionAtom, prevLockedPageAtom: () => prevLockedPageAtom, prevSearchResultAtom: () => prevSearchResultAtom, primaryInputSourceAtom: () => primaryInputSourceAtom, provideInputAtom: () => provideInputAtom, pushDeltaAtom: () => pushDeltaAtom, pushHistoryAtom: () => pushHistoryAtom, queueMutationAtom: () => queueMutationAtom, redoAtom: () => redoAtom, redoCommand: () => redoCommand, redoCountAtom: () => redoCountAtom, registerAction: () => registerAction, registerBuiltinCommands: () => registerBuiltinCommands, registerClipboardCommands: () => registerClipboardCommands, registerCommand: () => registerCommand, registerEdgePathCalculator: () => registerEdgePathCalculator, registerHistoryCommands: () => registerHistoryCommands, registerLayoutCommands: () => registerLayoutCommands, registerNodeType: () => registerNodeType, registerNodeTypes: () => registerNodeTypes, registerPlugin: () => registerPlugin, registerSelectionCommands: () => registerSelectionCommands, registerViewportCommands: () => registerViewportCommands, removeEdgeWithAnimationAtom: () => removeEdgeWithAnimationAtom, removeFromGroupAtom: () => removeFromGroupAtom, removeGestureRuleAtom: () => removeGestureRuleAtom, removeNodesFromSelectionAtom: () => removeNodesFromSelectionAtom, resetGestureRulesAtom: () => resetGestureRulesAtom, resetInputModeAtom: () => resetInputModeAtom, resetKeyboardInteractionModeAtom: () => resetKeyboardInteractionModeAtom, resetSettingsAtom: () => resetSettingsAtom, resetViewportAtom: () => resetViewportAtom, resetViewportCommand: () => resetViewportCommand, resolveEdgePathCalculator: () => resolveEdgePathCalculator, resolveGesture: () => resolveGesture, resolveGestureIndexed: () => resolveGestureIndexed, saveAsPresetAtom: () => saveAsPresetAtom, screenToWorldAtom: () => screenToWorldAtom, searchEdgeResultCountAtom: () => searchEdgeResultCountAtom, searchEdgeResultsAtom: () => searchEdgeResultsAtom, searchQueryAtom: () => searchQueryAtom, searchResultCountAtom: () => searchResultCountAtom, searchResultsArrayAtom: () => searchResultsArrayAtom, searchResultsAtom: () => searchResultsAtom, searchTotalResultCountAtom: () => searchTotalResultCountAtom, selectAllCommand: () => selectAllCommand, selectCommandAtom: () => selectCommandAtom, selectEdgeAtom: () => selectEdgeAtom, selectSingleNodeAtom: () => selectSingleNodeAtom, selectedEdgeIdAtom: () => selectedEdgeIdAtom, selectedNodeIdsAtom: () => selectedNodeIdsAtom, selectedNodesCountAtom: () => selectedNodesCountAtom, selectedSuggestionIndexAtom: () => selectedSuggestionIndexAtom, selectionPathAtom: () => selectionPathAtom, selectionRectAtom: () => selectionRectAtom, setCommandErrorAtom: () => setCommandErrorAtom, setEventMappingAtom: () => setEventMappingAtom, setFocusedNodeAtom: () => setFocusedNodeAtom, setGridSizeAtom: () => setGridSizeAtom, setHitTestProvider: () => setHitTestProvider, setKeyboardInteractionModeAtom: () => setKeyboardInteractionModeAtom, setNodeCenter: () => setNodeCenter, setNodeParentAtom: () => setNodeParentAtom, setOnlineStatusAtom: () => setOnlineStatusAtom, setPanelOpenAtom: () => setPanelOpenAtom, setPerfEnabled: () => setPerfEnabled, setSearchQueryAtom: () => setSearchQueryAtom, setVirtualizationEnabledAtom: () => setVirtualizationEnabledAtom, setZoomAtom: () => setZoomAtom, showToastAtom: () => showToastAtom, skipInputAtom: () => skipInputAtom, smoothStep: () => smoothStep, 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, stepHorizontal: () => stepHorizontal, stepSmart: () => stepSmart, stepVertical: () => stepVertical, storageAdapterAtom: () => storageAdapterAtom, straight: () => straight, swapEdgeAtomicAtom: () => swapEdgeAtomicAtom, syncStateAtom: () => syncStateAtom, syncStatusAtom: () => syncStatusAtom, toggleAlignmentGuidesAtom: () => toggleAlignmentGuidesAtom, toggleGroupCollapseAtom: () => toggleGroupCollapseAtom, toggleNodeInSelectionAtom: () => toggleNodeInSelectionAtom, togglePanelAtom: () => togglePanelAtom, toggleSnapAtom: () => toggleSnapAtom, toggleVirtualizationAtom: () => toggleVirtualizationAtom, trackMutationErrorAtom: () => trackMutationErrorAtom, uiNodesAtom: () => uiNodesAtom, undoAtom: () => undoAtom, undoCommand: () => undoCommand, undoCountAtom: () => undoCountAtom, ungroupNodesAtom: () => ungroupNodesAtom, unlockNodeAtom: () => unlockNodeAtom, unregisterAction: () => unregisterAction, unregisterEdgePathCalculator: () => unregisterEdgePathCalculator, unregisterNodeType: () => unregisterNodeType, unregisterPlugin: () => unregisterPlugin, updateEdgeLabelAtom: () => updateEdgeLabelAtom, updateGestureRuleAtom: () => updateGestureRuleAtom, updateGraphEdge: () => updateGraphEdge, updateGraphNode: () => updateGraphNode, updateInteractionFeedbackAtom: () => updateInteractionFeedbackAtom, updateNodePositionAtom: () => updateNodePositionAtom, updatePresetAtom: () => updatePresetAtom, updateSearchQueryAtom: () => updateSearchQueryAtom, updateSelectionAtom: () => updateSelectionAtom, useActionExecutor: () => useActionExecutor, useAnimatedLayout: () => useAnimatedLayout, useArrowKeyNavigation: () => useArrowKeyNavigation, useCanvasDrag: () => useCanvasDrag, useCanvasGraph: () => useCanvasGraph, useCanvasHistory: () => useCanvasHistory, useCanvasSelection: () => useCanvasSelection, useCanvasSettings: () => useCanvasSettings, useCanvasStyle: () => useCanvasStyle, useCanvasTheme: () => useCanvasTheme, useCanvasViewport: () => useCanvasViewport, useCommandContext: () => useCommandContext, useCommandLine: () => useCommandLine, useCreateEdge: () => useCreateEdge, useCreateNode: () => useCreateNode, useDeleteEdge: () => useDeleteEdge, useDeleteNode: () => useDeleteNode, useExecuteCommand: () => useExecuteCommand, useFitToBounds: () => useFitToBounds, useForceLayout: () => useForceLayout, useGestureResolver: () => useGestureResolver, useGetGraphBounds: () => useGetGraphBounds, useGlobalKeyboard: () => useGlobalKeyboard, useGraphEdges: () => useGraphEdges, useGraphNodes: () => useGraphNodes, useGridLayout: () => useGridLayout, useKeyState: () => useKeyState, useLayout: () => useLayout, useNodeDrag: () => useNodeDrag, useNodeResize: () => useNodeResize, useNodeSelection: () => useNodeSelection, usePlugin: () => usePlugin, usePlugins: () => usePlugins, useSelectionBounds: () => useSelectionBounds, useSplitGesture: () => useSplitGesture, useStorageAdapter: () => useStorageAdapter, useTapGesture: () => useTapGesture, useTreeLayout: () => useTreeLayout, useUpdateEdge: () => useUpdateEdge, useUpdateNode: () => useUpdateNode, useVirtualization: () => useVirtualization, useZoomTransition: () => useZoomTransition, 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, zoomInCommand: () => zoomInCommand, zoomOutCommand: () => zoomOutCommand, zoomTransitionProgressAtom: () => zoomTransitionProgressAtom }); module.exports = __toCommonJS(index_exports); init_core(); // src/hooks/useNodeSelection.ts var import_compiler_runtime2 = require("react/compiler-runtime"); var import_jotai26 = require("jotai"); init_selection_store(); function useNodeSelection(nodeId) { const $ = (0, import_compiler_runtime2.c)(13); const [selectedIds] = (0, import_jotai26.useAtom)(selectedNodeIdsAtom); const selectSingle = (0, import_jotai26.useSetAtom)(selectSingleNodeAtom); const toggleNode = (0, import_jotai26.useSetAtom)(toggleNodeInSelectionAtom); let t0; if ($[0] !== nodeId || $[1] !== selectedIds) { t0 = selectedIds.has(nodeId); $[0] = nodeId; $[1] = selectedIds; $[2] = t0; } else { t0 = $[2]; } let t1; if ($[3] !== nodeId || $[4] !== selectSingle) { t1 = () => selectSingle(nodeId); $[3] = nodeId; $[4] = selectSingle; $[5] = t1; } else { t1 = $[5]; } let t2; if ($[6] !== nodeId || $[7] !== toggleNode) { t2 = () => toggleNode(nodeId); $[6] = nodeId; $[7] = toggleNode; $[8] = t2; } else { t2 = $[8]; } let t3; if ($[9] !== t0 || $[10] !== t1 || $[11] !== t2) { t3 = { isSelected: t0, selectNode: t1, toggleNode: t2 }; $[9] = t0; $[10] = t1; $[11] = t2; $[12] = t3; } else { t3 = $[12]; } return t3; } // src/hooks/useNodeDrag.ts var import_compiler_runtime3 = require("react/compiler-runtime"); var import_jotai27 = require("jotai"); var import_react2 = require("@use-gesture/react"); var import_react3 = require("react"); init_graph_store(); init_graph_position(); init_graph_mutations(); init_viewport_store(); init_selection_store(); init_sync_store(); init_history_store(); init_group_store(); // 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/hooks/useNodeDrag.ts init_input_store(); init_mutation_queue(); init_debug(); // 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/hooks/useDragStateMachine.ts init_group_store(); function buildDragPositions(graph, selectedNodeIds) { const positions = /* @__PURE__ */ new Map(); for (const nodeId of selectedNodeIds) { if (graph.hasNode(nodeId)) { const attrs = graph.getNodeAttributes(nodeId); positions.set(nodeId, { x: attrs.x, y: attrs.y }); } } const currentKeys = Array.from(positions.keys()); for (const nodeId of currentKeys) { const descendants = getNodeDescendants(graph, nodeId); for (const descId of descendants) { if (!positions.has(descId) && graph.hasNode(descId)) { const attrs = graph.getNodeAttributes(descId); positions.set(descId, { x: attrs.x, y: attrs.y }); } } } return positions; } function computeDragUpdates(initialPositions, movementX, movementY, zoom, graph) { const deltaX = movementX / zoom; const deltaY = movementY / zoom; const updates = []; initialPositions.forEach((initialPos, nodeId) => { if (graph.hasNode(nodeId)) { updates.push({ id: nodeId, pos: { x: initialPos.x + deltaX, y: initialPos.y + deltaY } }); } }); return updates; } function isDragPrevented(target) { return !!target.closest('[data-no-drag="true"]') || target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT"; } // src/hooks/useNodeDrag.ts var debug13 = createDebug("drag"); function useNodeDrag(id, t0) { const $ = (0, import_compiler_runtime3.c)(49); let t1; if ($[0] !== t0) { t1 = t0 === void 0 ? {} : t0; $[0] = t0; $[1] = t1; } else { t1 = $[1]; } const options = t1; const { onPersist, onPersistError, heldKeys } = options; const graph = (0, import_jotai27.useAtomValue)(graphAtom); const [pan, setPan] = (0, import_jotai27.useAtom)(panAtom); const startMutation = (0, import_jotai27.useSetAtom)(startMutationAtom); const completeMutation = (0, import_jotai27.useSetAtom)(completeMutationAtom); const setStartDrag = (0, import_jotai27.useSetAtom)(startNodeDragAtom); const setEndDrag = (0, import_jotai27.useSetAtom)(endNodeDragAtom); const getPreDragAttributes = (0, import_jotai27.useAtomValue)(preDragNodeAttributesAtom); const currentZoom = (0, import_jotai27.useAtomValue)(zoomAtom); const getSelectedNodeIds2 = (0, import_jotai27.useAtomValue)(selectedNodeIdsAtom); const currentGraphId = (0, import_jotai27.useAtomValue)(currentGraphIdAtom); const edgeCreation = (0, import_jotai27.useAtomValue)(edgeCreationAtom); const setGraph = (0, import_jotai27.useSetAtom)(graphAtom); (0, import_jotai27.useSetAtom)(nodePositionUpdateCounterAtom); const pushHistory = (0, import_jotai27.useSetAtom)(pushHistoryAtom); const setDropTarget = (0, import_jotai27.useSetAtom)(dropTargetNodeIdAtom); const nestNodesOnDrop = (0, import_jotai27.useSetAtom)(nestNodesOnDropAtom); const inputSource = (0, import_jotai27.useAtomValue)(primaryInputSourceAtom); let t2; if ($[2] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t2 = (0, import_jotai27.atom)(null, _temp2); $[2] = t2; } else { t2 = $[2]; } const batchUpdatePositions = (0, import_jotai27.useSetAtom)(t2); let t3; if ($[3] !== batchUpdatePositions) { t3 = (updates_0) => { batchUpdatePositions(updates_0); }; $[3] = batchUpdatePositions; $[4] = t3; } else { t3 = $[4]; } const updateNodePositions = t3; const gestureInstanceRef = (0, import_react3.useRef)(0); let t4; if ($[5] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t4 = { x: 0, y: 0 }; $[5] = t4; } else { t4 = $[5]; } const panStartRef = (0, import_react3.useRef)(t4); const isSpaceHeld = Boolean(heldKeys?.byCode.Space || heldKeys?.byKey[" "] || heldKeys?.byKey.Spacebar); let t5; if ($[6] !== isSpaceHeld || $[7] !== pan) { t5 = (state) => { if (isDragPrevented(state.event.target)) { return; } gestureInstanceRef.current = gestureInstanceRef.current + 1; if (isSpaceHeld) { panStartRef.current = pan; } }; $[6] = isSpaceHeld; $[7] = pan; $[8] = t5; } else { t5 = $[8]; } let t6; if ($[9] !== currentZoom || $[10] !== edgeCreation || $[11] !== getSelectedNodeIds2 || $[12] !== graph || $[13] !== id || $[14] !== isSpaceHeld || $[15] !== pushHistory || $[16] !== setDropTarget || $[17] !== setPan || $[18] !== setStartDrag || $[19] !== startMutation || $[20] !== updateNodePositions) { t6 = (state_0) => { if (isDragPrevented(state_0.event.target)) { return; } if (edgeCreation.isCreating) { return; } state_0.event.stopPropagation(); if (isSpaceHeld) { if (!state_0.tap && state_0.active) { const [mx, my] = state_0.movement; setPan({ x: panStartRef.current.x + mx, y: panStartRef.current.y + my }); } return state_0.memo; } const { movement: t72, first, active, down, pinching, cancel, tap } = state_0; const [mx_0, my_0] = t72; let currentMemo = state_0.memo; if (tap || !active) { return currentMemo; } const currentGestureInstance = gestureInstanceRef.current; if (first) { const selectionSize = getSelectedNodeIds2.size; const label = selectionSize > 1 ? `Move ${selectionSize} nodes` : "Move node"; pushHistory(label); setStartDrag({ nodeId: id }); const initialPositions = buildDragPositions(graph, getSelectedNodeIds2); initialPositions.forEach(() => startMutation()); currentMemo = { initialPositions, gestureInstance: currentGestureInstance }; } if (!currentMemo || currentMemo.gestureInstance !== currentGestureInstance || !currentMemo.initialPositions) { if (cancel && !pinching && !down && !tap && !active) { cancel(); } return currentMemo; } const updates_1 = computeDragUpdates(currentMemo.initialPositions, mx_0, my_0, currentZoom, graph); if (updates_1.length > 0) { updateNodePositions(updates_1); } if (state_0.event && "clientX" in state_0.event) { const { nodeId: hoveredId } = hitTestNode(state_0.event.clientX, state_0.event.clientY); const validTarget = hoveredId && !currentMemo.initialPositions.has(hoveredId) ? hoveredId : null; setDropTarget(validTarget); } return currentMemo; }; $[9] = currentZoom; $[10] = edgeCreation; $[11] = getSelectedNodeIds2; $[12] = graph; $[13] = id; $[14] = isSpaceHeld; $[15] = pushHistory; $[16] = setDropTarget; $[17] = setPan; $[18] = setStartDrag; $[19] = startMutation; $[20] = updateNodePositions; $[21] = t6; } else { t6 = $[21]; } let t7; if ($[22] !== completeMutation || $[23] !== currentGraphId || $[24] !== currentZoom || $[25] !== edgeCreation || $[26] !== getPreDragAttributes || $[27] !== getSelectedNodeIds2 || $[28] !== graph || $[29] !== id || $[30] !== isSpaceHeld || $[31] !== nestNodesOnDrop || $[32] !== onPersist || $[33] !== onPersistError || $[34] !== setDropTarget || $[35] !== setEndDrag || $[36] !== setGraph || $[37] !== startMutation || $[38] !== updateNodePositions) { t7 = (state_1) => { if (isDragPrevented(state_1.event.target)) { return; } if (edgeCreation.isCreating) { setEndDrag({ nodeId: id }); return; } if (isSpaceHeld) { return; } state_1.event.stopPropagation(); const memo = state_1.memo; setDropTarget(null); if (state_1.event && "clientX" in state_1.event && memo?.initialPositions) { const { nodeId: hoveredId_0 } = hitTestNode(state_1.event.clientX, state_1.event.clientY); if (hoveredId_0 && !memo.initialPositions.has(hoveredId_0)) { const draggedNodeIds = Array.from(memo.initialPositions.keys()).filter((nid) => getSelectedNodeIds2.has(nid)); if (draggedNodeIds.length > 0) { nestNodesOnDrop({ nodeIds: draggedNodeIds, targetId: hoveredId_0 }); } } } if (!currentGraphId) { debug13("Cannot update node position: currentGraphId is not set"); setEndDrag({ nodeId: id }); return; } const nodesToUpdate = memo?.initialPositions ? Array.from(memo.initialPositions.keys()) : [id]; nodesToUpdate.forEach((draggedNodeId) => { if (!graph.hasNode(draggedNodeId)) { completeMutation(false); return; } const finalAttrs = graph.getNodeAttributes(draggedNodeId); const initialPos = memo?.initialPositions.get(draggedNodeId); if (!initialPos) { completeMutation(false); return; } const [mx_1, my_1] = state_1.movement; const deltaX = mx_1 / currentZoom; const deltaY = my_1 / currentZoom; const finalPosition = { x: initialPos.x + deltaX, y: initialPos.y + deltaY }; updateNodePositions([{ id: draggedNodeId, pos: finalPosition }]); if (!onPersist) { completeMutation(true); setEndDrag({ nodeId: draggedNodeId }); return; } const existingDbUiProps = typeof finalAttrs.dbData.ui_properties === "object" && finalAttrs.dbData.ui_properties !== null && !Array.isArray(finalAttrs.dbData.ui_properties) ? finalAttrs.dbData.ui_properties : {}; const newUiProperties = { ...existingDbUiProps, x: finalPosition.x, y: finalPosition.y, zIndex: finalAttrs.zIndex }; const pendingState = getPendingState(draggedNodeId); if (pendingState.inFlight) { pendingState.queuedPosition = finalPosition; pendingState.queuedUiProperties = newUiProperties; pendingState.graphId = currentGraphId; return; } pendingState.inFlight = true; pendingState.graphId = currentGraphId; const processQueuedUpdate = async (nodeId) => { const state_2 = getPendingState(nodeId); if (state_2 && state_2.queuedPosition && state_2.queuedUiProperties && state_2.graphId) { const queuedProps = state_2.queuedUiProperties; const queuedGraphId = state_2.graphId; state_2.queuedPosition = null; state_2.queuedUiProperties = null; startMutation(); ; try { await onPersist(nodeId, queuedGraphId, queuedProps); completeMutation(true); } catch (t82) { const error = t82; completeMutation(false); onPersistError?.(nodeId, error); } state_2.inFlight = false; processQueuedUpdate(nodeId); } else { if (state_2) { state_2.inFlight = false; } } }; onPersist(draggedNodeId, currentGraphId, newUiProperties).then(() => { completeMutation(true); processQueuedUpdate(draggedNodeId); }).catch((error_0) => { completeMutation(false); const state_3 = getPendingState(draggedNodeId); if (state_3) { state_3.inFlight = false; } const preDragAttrsForNode = getPreDragAttributes; if (preDragAttrsForNode && preDragAttrsForNode.dbData.id === draggedNodeId && graph.hasNode(draggedNodeId)) { graph.replaceNodeAttributes(draggedNodeId, preDragAttrsForNode); setGraph(graph); } onPersistError?.(draggedNodeId, error_0); processQueuedUpdate(draggedNodeId); }).finally(() => { setEndDrag({ nodeId: draggedNodeId }); }); }); }; $[22] = completeMutation; $[23] = currentGraphId; $[24] = currentZoom; $[25] = edgeCreation; $[26] = getPreDragAttributes; $[27] = getSelectedNodeIds2; $[28] = graph; $[29] = id; $[30] = isSpaceHeld; $[31] = nestNodesOnDrop; $[32] = onPersist; $[33] = onPersistError; $[34] = setDropTarget; $[35] = setEndDrag; $[36] = setGraph; $[37] = startMutation; $[38] = updateNodePositions; $[39] = t7; } else { t7 = $[39]; } let t8; if ($[40] !== t5 || $[41] !== t6 || $[42] !== t7) { t8 = { onPointerDown: t5, onDrag: t6, onDragEnd: t7 }; $[40] = t5; $[41] = t6; $[42] = t7; $[43] = t8; } else { t8 = $[43]; } let t9; if ($[44] !== inputSource) { t9 = getNodeGestureConfig(inputSource); $[44] = inputSource; $[45] = t9; } else { t9 = $[45]; } const bind = (0, import_react2.useGesture)(t8, t9); let t10; if ($[46] !== bind || $[47] !== updateNodePositions) { t10 = { bind, updateNodePositions }; $[46] = bind; $[47] = updateNodePositions; $[48] = t10; } else { t10 = $[48]; } return t10; } function _temp2(get, set, updates) { const graph_0 = get(graphAtom); updates.forEach((u) => { if (graph_0.hasNode(u.id)) { graph_0.setNodeAttribute(u.id, "x", u.pos.x); graph_0.setNodeAttribute(u.id, "y", u.pos.y); } }); set(nodePositionUpdateCounterAtom, _temp); } function _temp(c) { return c + 1; } // src/hooks/useNodeResize.ts var import_compiler_runtime4 = require("react/compiler-runtime"); var import_jotai28 = require("jotai"); var import_react4 = require("react"); var import_react_dom = require("react-dom"); init_graph_store(); init_graph_position(); init_viewport_store(); init_sync_store(); init_debug(); var debug14 = createDebug("resize"); function useNodeResize(t0) { const $ = (0, import_compiler_runtime4.c)(38); const { id, nodeData, updateNodePositions, options: t1 } = t0; let t2; if ($[0] !== t1) { t2 = t1 === void 0 ? {} : t1; $[0] = t1; $[1] = t2; } else { t2 = $[1]; } const options = t2; const { onPersist, onPersistError, minWidth: t3, minHeight: t4 } = options; const minWidth = t3 === void 0 ? 200 : t3; const minHeight = t4 === void 0 ? 150 : t4; const [localWidth, setLocalWidth] = (0, import_react4.useState)(nodeData.width || 500); const [localHeight, setLocalHeight] = (0, import_react4.useState)(nodeData.height || 500); const [isResizing, setIsResizing] = (0, import_react4.useState)(false); const resizeStartRef = (0, import_react4.useRef)(null); const graph = (0, import_jotai28.useAtomValue)(graphAtom); const currentZoom = (0, import_jotai28.useAtomValue)(zoomAtom); const currentGraphId = (0, import_jotai28.useAtomValue)(currentGraphIdAtom); const startMutation = (0, import_jotai28.useSetAtom)(startMutationAtom); const completeMutation = (0, import_jotai28.useSetAtom)(completeMutationAtom); const setGraphUpdateVersion = (0, import_jotai28.useSetAtom)(graphUpdateVersionAtom); const setNodePositionUpdateCounter = (0, import_jotai28.useSetAtom)(nodePositionUpdateCounterAtom); let t5; let t6; if ($[2] !== isResizing || $[3] !== nodeData.height || $[4] !== nodeData.width) { t5 = () => { if (!isResizing) { setLocalWidth(nodeData.width || 500); setLocalHeight(nodeData.height || 500); } }; t6 = [nodeData.width, nodeData.height, isResizing]; $[2] = isResizing; $[3] = nodeData.height; $[4] = nodeData.width; $[5] = t5; $[6] = t6; } else { t5 = $[5]; t6 = $[6]; } (0, import_react4.useEffect)(t5, t6); let t7; if ($[7] !== graph || $[8] !== id || $[9] !== localHeight || $[10] !== localWidth) { t7 = (direction) => (e) => { e.stopPropagation(); e.preventDefault(); setIsResizing(true); const nodeAttrs = graph.hasNode(id) ? graph.getNodeAttributes(id) : { x: 0, y: 0 }; resizeStartRef.current = { width: localWidth, height: localHeight, startX: e.clientX, startY: e.clientY, startNodeX: nodeAttrs.x, startNodeY: nodeAttrs.y, direction }; e.target.setPointerCapture(e.pointerId); }; $[7] = graph; $[8] = id; $[9] = localHeight; $[10] = localWidth; $[11] = t7; } else { t7 = $[11]; } const createResizeStart = t7; let t8; if ($[12] !== currentZoom || $[13] !== graph || $[14] !== id || $[15] !== minHeight || $[16] !== minWidth || $[17] !== setGraphUpdateVersion || $[18] !== setNodePositionUpdateCounter || $[19] !== updateNodePositions) { t8 = (e_0) => { if (!resizeStartRef.current) { return; } e_0.stopPropagation(); e_0.preventDefault(); const deltaX = (e_0.clientX - resizeStartRef.current.startX) / currentZoom; const deltaY = (e_0.clientY - resizeStartRef.current.startY) / currentZoom; const { direction: direction_0, width: startWidth, height: startHeight, startNodeX, startNodeY } = resizeStartRef.current; let newWidth = startWidth; let newHeight = startHeight; let newX = startNodeX; let newY = startNodeY; if (direction_0.includes("e")) { newWidth = Math.max(minWidth, startWidth + deltaX); } if (direction_0.includes("w")) { newWidth = Math.max(minWidth, startWidth - deltaX); newX = startNodeX + (startWidth - newWidth); } if (direction_0.includes("s")) { newHeight = Math.max(minHeight, startHeight + deltaY); } if (direction_0.includes("n")) { newHeight = Math.max(minHeight, startHeight - deltaY); newY = startNodeY + (startHeight - newHeight); } if (graph.hasNode(id)) { graph.setNodeAttribute(id, "width", newWidth); graph.setNodeAttribute(id, "height", newHeight); graph.setNodeAttribute(id, "x", newX); graph.setNodeAttribute(id, "y", newY); } (0, import_react_dom.flushSync)(() => { setLocalWidth(newWidth); setLocalHeight(newHeight); setGraphUpdateVersion(_temp3); }); if (direction_0.includes("n") || direction_0.includes("w")) { updateNodePositions([{ id, pos: { x: newX, y: newY } }]); } else { setNodePositionUpdateCounter(_temp22); } }; $[12] = currentZoom; $[13] = graph; $[14] = id; $[15] = minHeight; $[16] = minWidth; $[17] = setGraphUpdateVersion; $[18] = setNodePositionUpdateCounter; $[19] = updateNodePositions; $[20] = t8; } else { t8 = $[20]; } const handleResizeMove = t8; let t9; if ($[21] !== completeMutation || $[22] !== currentGraphId || $[23] !== graph || $[24] !== id || $[25] !== localHeight || $[26] !== localWidth || $[27] !== onPersist || $[28] !== onPersistError || $[29] !== startMutation) { t9 = (e_1) => { if (!resizeStartRef.current) { return; } e_1.stopPropagation(); e_1.target.releasePointerCapture(e_1.pointerId); setIsResizing(false); if (!currentGraphId || !resizeStartRef.current) { resizeStartRef.current = null; return; } const finalAttrs = graph.hasNode(id) ? graph.getNodeAttributes(id) : null; if (!finalAttrs) { resizeStartRef.current = null; return; } const finalWidth = finalAttrs.width || localWidth; const finalHeight = finalAttrs.height || localHeight; const finalX = finalAttrs.x; const finalY = finalAttrs.y; setLocalWidth(finalWidth); setLocalHeight(finalHeight); if (!onPersist) { resizeStartRef.current = null; return; } const existingDbUiProps = typeof finalAttrs.dbData.ui_properties === "object" && finalAttrs.dbData.ui_properties !== null && !Array.isArray(finalAttrs.dbData.ui_properties) ? finalAttrs.dbData.ui_properties : {}; const newUiProperties = { ...existingDbUiProps, width: finalWidth, height: finalHeight, x: finalX, y: finalY }; startMutation(); onPersist(id, currentGraphId, newUiProperties).then(() => { completeMutation(true); }).catch((error) => { completeMutation(false); if (resizeStartRef.current) { setLocalWidth(resizeStartRef.current.width); setLocalHeight(resizeStartRef.current.height); } onPersistError?.(id, error); }).finally(() => { resizeStartRef.current = null; }); }; $[21] = completeMutation; $[22] = currentGraphId; $[23] = graph; $[24] = id; $[25] = localHeight; $[26] = localWidth; $[27] = onPersist; $[28] = onPersistError; $[29] = startMutation; $[30] = t9; } else { t9 = $[30]; } const handleResizeEnd = t9; let t10; if ($[31] !== createResizeStart || $[32] !== handleResizeEnd || $[33] !== handleResizeMove || $[34] !== isResizing || $[35] !== localHeight || $[36] !== localWidth) { t10 = { localWidth, localHeight, isResizing, createResizeStart, handleResizeMove, handleResizeEnd }; $[31] = createResizeStart; $[32] = handleResizeEnd; $[33] = handleResizeMove; $[34] = isResizing; $[35] = localHeight; $[36] = localWidth; $[37] = t10; } else { t10 = $[37]; } return t10; } function _temp22(c) { return c + 1; } function _temp3(v) { return v + 1; } // src/hooks/useCanvasHistory.ts var import_compiler_runtime5 = require("react/compiler-runtime"); var import_jotai29 = require("jotai"); init_history_store(); init_toast_store(); function useCanvasHistory(t0) { const $ = (0, import_compiler_runtime5.c)(22); const options = t0 === void 0 ? {} : t0; const { enableKeyboardShortcuts: t1 } = options; t1 === void 0 ? false : t1; const canUndo = (0, import_jotai29.useAtomValue)(canUndoAtom); const canRedo = (0, import_jotai29.useAtomValue)(canRedoAtom); const undoCount = (0, import_jotai29.useAtomValue)(undoCountAtom); const redoCount = (0, import_jotai29.useAtomValue)(redoCountAtom); const historyLabels = (0, import_jotai29.useAtomValue)(historyLabelsAtom); const undoAction = (0, import_jotai29.useSetAtom)(undoAtom); const redoAction = (0, import_jotai29.useSetAtom)(redoAtom); const pushHistory = (0, import_jotai29.useSetAtom)(pushHistoryAtom); const clearHistory = (0, import_jotai29.useSetAtom)(clearHistoryAtom); const showToast = (0, import_jotai29.useSetAtom)(showToastAtom); const store = (0, import_jotai29.useStore)(); let t2; if ($[0] !== showToast || $[1] !== store || $[2] !== undoAction) { t2 = () => { const state = store.get(historyStateAtom); const label = state.past[state.past.length - 1]?.label; const result = undoAction(); if (result && label) { showToast(`Undo: ${label}`); } return result; }; $[0] = showToast; $[1] = store; $[2] = undoAction; $[3] = t2; } else { t2 = $[3]; } const undo = t2; let t3; if ($[4] !== redoAction || $[5] !== showToast || $[6] !== store) { t3 = () => { const state_0 = store.get(historyStateAtom); const label_0 = state_0.future[0]?.label; const result_0 = redoAction(); if (result_0 && label_0) { showToast(`Redo: ${label_0}`); } return result_0; }; $[4] = redoAction; $[5] = showToast; $[6] = store; $[7] = t3; } else { t3 = $[7]; } const redo = t3; let t4; if ($[8] !== pushHistory) { t4 = (label_1) => { pushHistory(label_1); }; $[8] = pushHistory; $[9] = t4; } else { t4 = $[9]; } const recordSnapshot = t4; let t5; if ($[10] !== clearHistory) { t5 = () => { clearHistory(); }; $[10] = clearHistory; $[11] = t5; } else { t5 = $[11]; } const clear = t5; let t6; if ($[12] !== canRedo || $[13] !== canUndo || $[14] !== clear || $[15] !== historyLabels || $[16] !== recordSnapshot || $[17] !== redo || $[18] !== redoCount || $[19] !== undo || $[20] !== undoCount) { t6 = { undo, redo, canUndo, canRedo, undoCount, redoCount, historyLabels, recordSnapshot, clear }; $[12] = canRedo; $[13] = canUndo; $[14] = clear; $[15] = historyLabels; $[16] = recordSnapshot; $[17] = redo; $[18] = redoCount; $[19] = undo; $[20] = undoCount; $[21] = t6; } else { t6 = $[21]; } return t6; } // src/hooks/useCanvasSelection.ts var import_compiler_runtime6 = require("react/compiler-runtime"); var import_jotai30 = require("jotai"); init_selection_store(); function useCanvasSelection() { const $ = (0, import_compiler_runtime6.c)(6); const selectedNodeIds = (0, import_jotai30.useAtomValue)(selectedNodeIdsAtom); const selectedEdgeId = (0, import_jotai30.useAtomValue)(selectedEdgeIdAtom); const count = (0, import_jotai30.useAtomValue)(selectedNodesCountAtom); const hasSelection = (0, import_jotai30.useAtomValue)(hasSelectionAtom); const t0 = selectedEdgeId !== null; let t1; if ($[0] !== count || $[1] !== hasSelection || $[2] !== selectedEdgeId || $[3] !== selectedNodeIds || $[4] !== t0) { t1 = { selectedNodeIds, selectedEdgeId, count, hasSelection, hasEdgeSelection: t0 }; $[0] = count; $[1] = hasSelection; $[2] = selectedEdgeId; $[3] = selectedNodeIds; $[4] = t0; $[5] = t1; } else { t1 = $[5]; } return t1; } // src/hooks/useCanvasViewport.ts var import_compiler_runtime7 = require("react/compiler-runtime"); var import_jotai31 = require("jotai"); init_viewport_store(); function useCanvasViewport() { const $ = (0, import_compiler_runtime7.c)(9); const zoom = (0, import_jotai31.useAtomValue)(zoomAtom); const pan = (0, import_jotai31.useAtomValue)(panAtom); const viewportRect = (0, import_jotai31.useAtomValue)(viewportRectAtom); const screenToWorld = (0, import_jotai31.useAtomValue)(screenToWorldAtom); const worldToScreen = (0, import_jotai31.useAtomValue)(worldToScreenAtom); const zoomFocusNodeId = (0, import_jotai31.useAtomValue)(zoomFocusNodeIdAtom); const zoomTransitionProgress = (0, import_jotai31.useAtomValue)(zoomTransitionProgressAtom); const isZoomTransitioning = (0, import_jotai31.useAtomValue)(isZoomTransitioningAtom); let t0; if ($[0] !== isZoomTransitioning || $[1] !== pan || $[2] !== screenToWorld || $[3] !== viewportRect || $[4] !== worldToScreen || $[5] !== zoom || $[6] !== zoomFocusNodeId || $[7] !== zoomTransitionProgress) { t0 = { zoom, pan, viewportRect, screenToWorld, worldToScreen, zoomFocusNodeId, zoomTransitionProgress, isZoomTransitioning, zoomTransitionThreshold: ZOOM_TRANSITION_THRESHOLD, zoomExitThreshold: ZOOM_EXIT_THRESHOLD }; $[0] = isZoomTransitioning; $[1] = pan; $[2] = screenToWorld; $[3] = viewportRect; $[4] = worldToScreen; $[5] = zoom; $[6] = zoomFocusNodeId; $[7] = zoomTransitionProgress; $[8] = t0; } else { t0 = $[8]; } return t0; } // src/hooks/useCanvasDrag.ts var import_compiler_runtime8 = require("react/compiler-runtime"); var import_jotai32 = require("jotai"); init_graph_store(); function useCanvasDrag() { const $ = (0, import_compiler_runtime8.c)(3); const draggingNodeId = (0, import_jotai32.useAtomValue)(draggingNodeIdAtom); const t0 = draggingNodeId !== null; let t1; if ($[0] !== draggingNodeId || $[1] !== t0) { t1 = { draggingNodeId, isDragging: t0 }; $[0] = draggingNodeId; $[1] = t0; $[2] = t1; } else { t1 = $[2]; } return t1; } // src/hooks/useLayout.ts var import_compiler_runtime9 = require("react/compiler-runtime"); var import_jotai33 = require("jotai"); init_graph_store(); init_graph_position(); init_graph_derived(); init_viewport_store(); init_selection_store(); init_layout(); init_layout(); var useGetGraphBounds = () => { const $ = (0, import_compiler_runtime9.c)(6); const graph = (0, import_jotai33.useAtomValue)(graphAtom); (0, import_jotai33.useAtomValue)(nodePositionUpdateCounterAtom); let nodes; let t0; if ($[0] !== graph) { nodes = graph.nodes().map((node) => { const nodeAttributes = graph.getNodeAttributes(node); return { x: nodeAttributes.x, y: nodeAttributes.y, width: nodeAttributes.width || 500, height: nodeAttributes.height || 500 }; }); t0 = calculateBounds(nodes); $[0] = graph; $[1] = nodes; $[2] = t0; } else { nodes = $[1]; t0 = $[2]; } const bounds = t0; let t1; if ($[3] !== bounds || $[4] !== nodes) { t1 = { bounds, nodes }; $[3] = bounds; $[4] = nodes; $[5] = t1; } else { t1 = $[5]; } return t1; }; var useSelectionBounds = () => { const $ = (0, import_compiler_runtime9.c)(5); const selectedNodeIds = (0, import_jotai33.useAtomValue)(selectedNodeIdsAtom); const nodes = (0, import_jotai33.useAtomValue)(uiNodesAtom); let t0; if ($[0] !== nodes || $[1] !== selectedNodeIds) { let t1; if ($[3] !== selectedNodeIds) { t1 = (node) => selectedNodeIds.has(node.id); $[3] = selectedNodeIds; $[4] = t1; } else { t1 = $[4]; } const selectedNodes = nodes.filter(t1).map(_temp4); t0 = calculateBounds(selectedNodes); $[0] = nodes; $[1] = selectedNodeIds; $[2] = t0; } else { t0 = $[2]; } return t0; }; var useFitToBounds = () => { const $ = (0, import_compiler_runtime9.c)(2); const setFitToBounds = (0, import_jotai33.useSetAtom)(fitToBoundsAtom); let t0; if ($[0] !== setFitToBounds) { const fitToBounds = (mode, t1) => { const padding = t1 === void 0 ? 20 : t1; setFitToBounds({ mode, padding }); }; t0 = { fitToBounds }; $[0] = setFitToBounds; $[1] = t0; } else { t0 = $[1]; } return t0; }; var useLayout = () => { const $ = (0, import_compiler_runtime9.c)(5); const { fitToBounds } = useFitToBounds(); const { bounds: graphBounds, nodes: graphNodes } = useGetGraphBounds(); const selectionBounds = useSelectionBounds(); let t0; if ($[0] !== fitToBounds || $[1] !== graphBounds || $[2] !== graphNodes || $[3] !== selectionBounds) { t0 = { fitToBounds, graphBounds, graphNodes, selectionBounds }; $[0] = fitToBounds; $[1] = graphBounds; $[2] = graphNodes; $[3] = selectionBounds; $[4] = t0; } else { t0 = $[4]; } return t0; }; function _temp4(node_0) { return { x: node_0.position.x, y: node_0.position.y, width: node_0.width ?? 500, height: node_0.height ?? 500 }; } // src/hooks/useForceLayout.ts var d3 = __toESM(require("d3-force")); var import_jotai34 = require("jotai"); var import_react5 = require("react"); init_graph_store(); init_graph_position(); init_graph_derived(); init_layout(); init_debug(); var debug15 = createDebug("force-layout"); var useForceLayout = (options = {}) => { const { onPositionsChanged, maxIterations = 1e3, chargeStrength = -6e3, linkStrength = 0.03 } = options; const nodes = (0, import_jotai34.useAtomValue)(uiNodesAtom); const graph = (0, import_jotai34.useAtomValue)(graphAtom); const updateNodePosition = (0, import_jotai34.useSetAtom)(updateNodePositionAtom); const isRunningRef = (0, import_react5.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) { debug15.warn("Layout already running, ignoring request."); return; } if (nodes.length === 0) { debug15.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(); debug15("Starting simulation..."); return new Promise((resolve2) => { let iterations = 0; function runSimulationStep() { if (iterations >= maxIterations) { debug15("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) { debug15("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) { debug15("Saving %d positions via callback...", positionUpdates.length); Promise.resolve(onPositionsChanged(positionUpdates)).then(() => debug15("Positions saved successfully.")).catch((err) => debug15.error("Error saving positions: %O", err)); } debug15("Layout complete."); isRunningRef.current = false; resolve2(); } requestAnimationFrame(runSimulationStep); }); }; return { applyForceLayout, isRunning: isRunningRef.current }; }; // src/hooks/useCanvasSettings.ts var import_compiler_runtime10 = require("react/compiler-runtime"); var import_jotai35 = require("jotai"); init_settings_store(); function useCanvasSettings() { const $ = (0, import_compiler_runtime10.c)(34); const mappings = (0, import_jotai35.useAtomValue)(eventMappingsAtom); const activePresetId = (0, import_jotai35.useAtomValue)(activePresetIdAtom); const activePreset = (0, import_jotai35.useAtomValue)(activePresetAtom); const allPresets = (0, import_jotai35.useAtomValue)(allPresetsAtom); const hasUnsavedChanges = (0, import_jotai35.useAtomValue)(hasUnsavedChangesAtom); const isPanelOpen = (0, import_jotai35.useAtomValue)(isPanelOpenAtom); const setEventMappingAction = (0, import_jotai35.useSetAtom)(setEventMappingAtom); const applyPresetAction = (0, import_jotai35.useSetAtom)(applyPresetAtom); const saveAsPresetAction = (0, import_jotai35.useSetAtom)(saveAsPresetAtom); const updatePresetAction = (0, import_jotai35.useSetAtom)(updatePresetAtom); const deletePresetAction = (0, import_jotai35.useSetAtom)(deletePresetAtom); const resetSettingsAction = (0, import_jotai35.useSetAtom)(resetSettingsAtom); const togglePanelAction = (0, import_jotai35.useSetAtom)(togglePanelAtom); const setPanelOpenAction = (0, import_jotai35.useSetAtom)(setPanelOpenAtom); let t0; if ($[0] !== setEventMappingAction) { t0 = (event, actionId) => { setEventMappingAction({ event, actionId }); }; $[0] = setEventMappingAction; $[1] = t0; } else { t0 = $[1]; } const setEventMapping = t0; let t1; if ($[2] !== applyPresetAction) { t1 = (presetId) => { applyPresetAction(presetId); }; $[2] = applyPresetAction; $[3] = t1; } else { t1 = $[3]; } const applyPreset = t1; let t2; if ($[4] !== saveAsPresetAction) { t2 = (name, description) => saveAsPresetAction({ name, description }); $[4] = saveAsPresetAction; $[5] = t2; } else { t2 = $[5]; } const saveAsPreset = t2; let t3; if ($[6] !== updatePresetAction) { t3 = (presetId_0) => { updatePresetAction(presetId_0); }; $[6] = updatePresetAction; $[7] = t3; } else { t3 = $[7]; } const updatePreset = t3; let t4; if ($[8] !== deletePresetAction) { t4 = (presetId_1) => { deletePresetAction(presetId_1); }; $[8] = deletePresetAction; $[9] = t4; } else { t4 = $[9]; } const deletePreset = t4; let t5; if ($[10] !== resetSettingsAction) { t5 = () => { resetSettingsAction(); }; $[10] = resetSettingsAction; $[11] = t5; } else { t5 = $[11]; } const resetSettings = t5; let t6; if ($[12] !== togglePanelAction) { t6 = () => { togglePanelAction(); }; $[12] = togglePanelAction; $[13] = t6; } else { t6 = $[13]; } const togglePanel = t6; let t7; if ($[14] !== setPanelOpenAction) { t7 = (isOpen) => { setPanelOpenAction(isOpen); }; $[14] = setPanelOpenAction; $[15] = t7; } else { t7 = $[15]; } const setPanelOpen = t7; let t8; if ($[16] !== mappings) { t8 = (event_0) => getActionForEvent(mappings, event_0); $[16] = mappings; $[17] = t8; } else { t8 = $[17]; } const getActionForEventFn = t8; let t9; if ($[18] !== activePreset || $[19] !== activePresetId || $[20] !== allPresets || $[21] !== applyPreset || $[22] !== deletePreset || $[23] !== getActionForEventFn || $[24] !== hasUnsavedChanges || $[25] !== isPanelOpen || $[26] !== mappings || $[27] !== resetSettings || $[28] !== saveAsPreset || $[29] !== setEventMapping || $[30] !== setPanelOpen || $[31] !== togglePanel || $[32] !== updatePreset) { t9 = { mappings, activePresetId, activePreset, allPresets, hasUnsavedChanges, isPanelOpen, setEventMapping, applyPreset, saveAsPreset, updatePreset, deletePreset, resetSettings, togglePanel, setPanelOpen, getActionForEvent: getActionForEventFn }; $[18] = activePreset; $[19] = activePresetId; $[20] = allPresets; $[21] = applyPreset; $[22] = deletePreset; $[23] = getActionForEventFn; $[24] = hasUnsavedChanges; $[25] = isPanelOpen; $[26] = mappings; $[27] = resetSettings; $[28] = saveAsPreset; $[29] = setEventMapping; $[30] = setPanelOpen; $[31] = togglePanel; $[32] = updatePreset; $[33] = t9; } else { t9 = $[33]; } return t9; } // src/hooks/useActionExecutor.ts var import_compiler_runtime11 = require("react/compiler-runtime"); var import_jotai36 = require("jotai"); init_action_executor(); init_settings_store(); function useActionExecutor(t0) { const $ = (0, import_compiler_runtime11.c)(13); const options = t0 === void 0 ? {} : t0; const store = (0, import_jotai36.useStore)(); const mappings = (0, import_jotai36.useAtomValue)(eventMappingsAtom); const helpers = buildActionHelpers(store, { onDeleteNode: options.onDeleteNode, onOpenContextMenu: options.onOpenContextMenu, onCreateNode: options.onCreateNode, onApplyForceLayout: options.onApplyForceLayout }); let t1; if ($[0] !== helpers) { t1 = async (actionId, context) => executeAction(actionId, context, helpers); $[0] = helpers; $[1] = t1; } else { t1 = $[1]; } const executeActionById = t1; let t2; if ($[2] !== helpers || $[3] !== mappings) { t2 = async (event, context_0) => { const actionId_0 = getActionForEvent(mappings, event); return executeAction(actionId_0, context_0, helpers); }; $[2] = helpers; $[3] = mappings; $[4] = t2; } else { t2 = $[4]; } const executeEventAction = t2; let t3; if ($[5] !== mappings) { t3 = (event_0) => getActionForEvent(mappings, event_0); $[5] = mappings; $[6] = t3; } else { t3 = $[6]; } const getActionForEventFn = t3; let t4; if ($[7] !== executeActionById || $[8] !== executeEventAction || $[9] !== getActionForEventFn || $[10] !== helpers || $[11] !== mappings) { t4 = { executeActionById, executeEventAction, getActionForEvent: getActionForEventFn, mappings, helpers }; $[7] = executeActionById; $[8] = executeEventAction; $[9] = getActionForEventFn; $[10] = helpers; $[11] = mappings; $[12] = t4; } else { t4 = $[12]; } return t4; } // src/hooks/useGestureResolver.ts var import_compiler_runtime12 = require("react/compiler-runtime"); var import_jotai37 = require("jotai"); init_gesture_rules(); init_gesture_rule_store(); init_input_store(); function useGestureResolver() { const $ = (0, import_compiler_runtime12.c)(4); const index = (0, import_jotai37.useAtomValue)(gestureRuleIndexAtom); const palmRejection = (0, import_jotai37.useAtomValue)(palmRejectionEnabledAtom); const isStylusActive = (0, import_jotai37.useAtomValue)(isStylusActiveAtom); let t0; if ($[0] !== index || $[1] !== isStylusActive || $[2] !== palmRejection) { t0 = (desc) => resolveGestureIndexed({ ...desc, isStylusActive: desc.isStylusActive ?? isStylusActive }, index, { palmRejection }); $[0] = index; $[1] = isStylusActive; $[2] = palmRejection; $[3] = t0; } else { t0 = $[3]; } return t0; } // src/hooks/useCommandLine.ts var import_compiler_runtime13 = require("react/compiler-runtime"); var import_jotai40 = require("jotai"); // src/commands/store.ts var import_jotai39 = require("jotai"); init_registry(); // src/commands/store-atoms.ts var import_jotai38 = require("jotai"); var import_utils3 = require("jotai/utils"); var inputModeAtom2 = (0, import_jotai38.atom)({ type: "normal" }); var commandLineVisibleAtom = (0, import_jotai38.atom)(false); var commandLineStateAtom = (0, import_jotai38.atom)({ phase: "idle" }); var commandFeedbackAtom = (0, import_jotai38.atom)(null); var commandHistoryAtom = (0, import_utils3.atomWithStorage)("canvas-command-history", []); var selectedSuggestionIndexAtom = (0, import_jotai38.atom)(0); var pendingInputResolverAtom2 = (0, import_jotai38.atom)(null); var isCommandActiveAtom = (0, import_jotai38.atom)((get) => { const state = get(commandLineStateAtom); return state.phase === "collecting" || state.phase === "executing"; }); var currentInputAtom = (0, import_jotai38.atom)((get) => { const state = get(commandLineStateAtom); if (state.phase !== "collecting") return null; return state.command.inputs[state.inputIndex]; }); var commandProgressAtom = (0, import_jotai38.atom)((get) => { const state = get(commandLineStateAtom); if (state.phase !== "collecting") return null; return { current: state.inputIndex + 1, total: state.command.inputs.length }; }); // src/commands/store.ts var openCommandLineAtom = (0, import_jotai39.atom)(null, (get, set) => { set(commandLineVisibleAtom, true); set(commandLineStateAtom, { phase: "searching", query: "", suggestions: commandRegistry.all() }); set(selectedSuggestionIndexAtom, 0); }); var closeCommandLineAtom = (0, import_jotai39.atom)(null, (get, set) => { set(commandLineVisibleAtom, false); set(commandLineStateAtom, { phase: "idle" }); set(inputModeAtom2, { type: "normal" }); set(commandFeedbackAtom, null); set(pendingInputResolverAtom2, null); }); var updateSearchQueryAtom = (0, import_jotai39.atom)(null, (get, set, query) => { const suggestions = commandRegistry.search(query); set(commandLineStateAtom, { phase: "searching", query, suggestions }); set(selectedSuggestionIndexAtom, 0); }); var selectCommandAtom = (0, import_jotai39.atom)(null, (get, set, command) => { const history = get(commandHistoryAtom); const newHistory = [command.name, ...history.filter((h) => h !== command.name)].slice(0, 50); set(commandHistoryAtom, newHistory); if (command.inputs.length === 0) { set(commandLineStateAtom, { phase: "executing", command }); return; } set(commandLineStateAtom, { phase: "collecting", command, inputIndex: 0, collected: {} }); const firstInput = command.inputs[0]; set(inputModeAtom2, inputDefToMode(firstInput)); }); var provideInputAtom2 = (0, import_jotai39.atom)(null, (get, set, value) => { const state = get(commandLineStateAtom); if (state.phase !== "collecting") return; const { command, inputIndex, collected } = state; const currentInput = command.inputs[inputIndex]; if (currentInput.validate) { const result = currentInput.validate(value, collected); if (result !== true) { set(commandLineStateAtom, { phase: "error", message: typeof result === "string" ? result : `Invalid value for ${currentInput.name}` }); return; } } const newCollected = { ...collected, [currentInput.name]: value }; if (inputIndex < command.inputs.length - 1) { const nextInputIndex = inputIndex + 1; const nextInput = command.inputs[nextInputIndex]; set(commandLineStateAtom, { phase: "collecting", command, inputIndex: nextInputIndex, collected: newCollected }); set(inputModeAtom2, 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(inputModeAtom2, { type: "normal" }); } }); var skipInputAtom = (0, import_jotai39.atom)(null, (get, set) => { const state = get(commandLineStateAtom); if (state.phase !== "collecting") return; const { command, inputIndex } = state; const currentInput = command.inputs[inputIndex]; if (currentInput.required !== false) { return; } const value = currentInput.default; set(provideInputAtom2, value); }); var goBackInputAtom = (0, import_jotai39.atom)(null, (get, set) => { const state = get(commandLineStateAtom); if (state.phase !== "collecting") return; const { command, inputIndex, collected } = state; if (inputIndex === 0) { set(commandLineStateAtom, { phase: "searching", query: command.name, suggestions: [command] }); set(inputModeAtom2, { 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(inputModeAtom2, inputDefToMode(prevInput, newCollected)); }); var setCommandErrorAtom = (0, import_jotai39.atom)(null, (get, set, message) => { set(commandLineStateAtom, { phase: "error", message }); set(inputModeAtom2, { type: "normal" }); }); var clearCommandErrorAtom = (0, import_jotai39.atom)(null, (get, set) => { set(commandLineStateAtom, { phase: "idle" }); }); function inputDefToMode(input, collected) { switch (input.type) { case "point": return { type: "pickPoint", prompt: input.prompt, snapToGrid: input.snapToGrid }; case "node": return { type: "pickNode", prompt: input.prompt, filter: input.filter ? (node) => input.filter(node, collected || {}) : void 0 }; case "nodes": return { type: "pickNodes", prompt: input.prompt, filter: input.filter ? (node) => input.filter(node, collected || {}) : void 0 }; case "select": return { type: "select", prompt: input.prompt, options: input.options || [] }; case "text": case "number": case "color": case "boolean": default: return { type: "text", prompt: input.prompt }; } } // src/hooks/useCommandLine.ts init_registry(); function useCommandLine() { const $ = (0, import_compiler_runtime13.c)(16); const visible = (0, import_jotai40.useAtomValue)(commandLineVisibleAtom); const state = (0, import_jotai40.useAtomValue)(commandLineStateAtom); const history = (0, import_jotai40.useAtomValue)(commandHistoryAtom); const currentInput = (0, import_jotai40.useAtomValue)(currentInputAtom); const progress = (0, import_jotai40.useAtomValue)(commandProgressAtom); const open = (0, import_jotai40.useSetAtom)(openCommandLineAtom); const close = (0, import_jotai40.useSetAtom)(closeCommandLineAtom); const updateQuery = (0, import_jotai40.useSetAtom)(updateSearchQueryAtom); const selectCommand = (0, import_jotai40.useSetAtom)(selectCommandAtom); const t0 = state.phase === "searching"; const t1 = state.phase === "collecting"; const t2 = state.phase === "executing"; const t3 = state.phase === "error"; const t4 = state.phase === "error" ? state.message : null; let t5; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t5 = commandRegistry.all(); $[0] = t5; } else { t5 = $[0]; } let t6; if ($[1] !== close || $[2] !== currentInput || $[3] !== history || $[4] !== open || $[5] !== progress || $[6] !== selectCommand || $[7] !== state || $[8] !== t0 || $[9] !== t1 || $[10] !== t2 || $[11] !== t3 || $[12] !== t4 || $[13] !== updateQuery || $[14] !== visible) { t6 = { visible, state, history, currentInput, progress, open, close, updateQuery, selectCommand, isSearching: t0, isCollecting: t1, isExecuting: t2, hasError: t3, errorMessage: t4, allCommands: t5, searchCommands: _temp5 }; $[1] = close; $[2] = currentInput; $[3] = history; $[4] = open; $[5] = progress; $[6] = selectCommand; $[7] = state; $[8] = t0; $[9] = t1; $[10] = t2; $[11] = t3; $[12] = t4; $[13] = updateQuery; $[14] = visible; $[15] = t6; } else { t6 = $[15]; } return t6; } function _temp5(query) { return commandRegistry.search(query); } // src/hooks/useVirtualization.ts var import_compiler_runtime14 = require("react/compiler-runtime"); var import_jotai41 = require("jotai"); init_virtualization_store(); init_settings_store(); function useVirtualization() { const $ = (0, import_compiler_runtime14.c)(8); const metrics = (0, import_jotai41.useAtomValue)(virtualizationMetricsAtom); const setEnabled = (0, import_jotai41.useSetAtom)(setVirtualizationEnabledAtom); const toggle = (0, import_jotai41.useSetAtom)(toggleVirtualizationAtom); let t0; let t1; if ($[0] !== setEnabled) { t0 = () => setEnabled(true); t1 = () => setEnabled(false); $[0] = setEnabled; $[1] = t0; $[2] = t1; } else { t0 = $[1]; t1 = $[2]; } let t2; if ($[3] !== metrics || $[4] !== t0 || $[5] !== t1 || $[6] !== toggle) { t2 = { ...metrics, enable: t0, disable: t1, toggle }; $[3] = metrics; $[4] = t0; $[5] = t1; $[6] = toggle; $[7] = t2; } else { t2 = $[7]; } return t2; } // src/hooks/useTapGesture.ts var import_compiler_runtime15 = require("react/compiler-runtime"); var import_react6 = require("react"); function useTapGesture(t0) { const $ = (0, import_compiler_runtime15.c)(12); let t1; if ($[0] !== t0) { t1 = t0 === void 0 ? {} : t0; $[0] = t0; $[1] = t1; } else { t1 = $[1]; } const options = t1; const { onSingleTap, onDoubleTap, onTripleTap, tapDelay: t2, tapDistance: t3 } = options; const tapDelay = t2 === void 0 ? 300 : t2; const tapDistance = t3 === void 0 ? 25 : t3; let t4; if ($[2] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t4 = { count: 0, timer: null, lastX: 0, lastY: 0, lastEvent: null }; $[2] = t4; } else { t4 = $[2]; } const stateRef = (0, import_react6.useRef)(t4); let t5; if ($[3] !== onDoubleTap || $[4] !== onSingleTap || $[5] !== onTripleTap || $[6] !== tapDelay || $[7] !== tapDistance) { t5 = (event) => { const state = stateRef.current; const dx = Math.abs(event.clientX - state.lastX); const dy = Math.abs(event.clientY - state.lastY); const isSameSpot = state.count === 0 || dx < tapDistance && dy < tapDistance; if (!isSameSpot) { if (state.timer) { clearTimeout(state.timer); } state.count = 0; } state.count = state.count + 1; state.lastX = event.clientX; state.lastY = event.clientY; state.lastEvent = event; if (state.timer) { clearTimeout(state.timer); } if (state.count >= 3) { onTripleTap?.(event); state.count = 0; state.timer = null; return; } if (state.count === 2) { onDoubleTap?.(event); state.timer = setTimeout(() => { state.count = 0; state.timer = null; }, tapDelay); return; } state.timer = setTimeout(() => { if (state.count === 1 && state.lastEvent) { onSingleTap?.(state.lastEvent); } state.count = 0; state.timer = null; }, tapDelay); }; $[3] = onDoubleTap; $[4] = onSingleTap; $[5] = onTripleTap; $[6] = tapDelay; $[7] = tapDistance; $[8] = t5; } else { t5 = $[8]; } const handleTap = t5; let t6; if ($[9] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t6 = () => { const state_0 = stateRef.current; if (state_0.timer) { clearTimeout(state_0.timer); state_0.timer = null; } state_0.count = 0; }; $[9] = t6; } else { t6 = $[9]; } const cleanup = t6; let t7; if ($[10] !== handleTap) { t7 = { handleTap, cleanup }; $[10] = handleTap; $[11] = t7; } else { t7 = $[11]; } return t7; } // src/hooks/useArrowKeyNavigation.ts var import_compiler_runtime16 = require("react/compiler-runtime"); var import_jotai42 = require("jotai"); init_selection_store(); function useArrowKeyNavigation() { const $ = (0, import_compiler_runtime16.c)(2); const focusedNodeId = (0, import_jotai42.useAtomValue)(focusedNodeIdAtom); let t0; if ($[0] !== focusedNodeId) { t0 = { focusedNodeId }; $[0] = focusedNodeId; $[1] = t0; } else { t0 = $[1]; } return t0; } // src/hooks/useCanvasGraph.ts var import_compiler_runtime17 = require("react/compiler-runtime"); var import_jotai43 = require("jotai"); init_graph_store(); init_graph_derived(); function useCanvasGraph() { const $ = (0, import_compiler_runtime17.c)(9); const graph = (0, import_jotai43.useAtomValue)(graphAtom); const nodeKeys = (0, import_jotai43.useAtomValue)(nodeKeysAtom); const edgeKeys = (0, import_jotai43.useAtomValue)(edgeKeysAtom); let t0; if ($[0] !== graph) { t0 = (id) => graph.hasNode(id) ? graph.getNodeAttributes(id) : void 0; $[0] = graph; $[1] = t0; } else { t0 = $[1]; } const getNode = t0; let t1; if ($[2] !== graph) { t1 = (id_0) => graph.hasEdge(id_0) ? graph.getEdgeAttributes(id_0) : void 0; $[2] = graph; $[3] = t1; } else { t1 = $[3]; } const getEdge = t1; let t2; if ($[4] !== edgeKeys || $[5] !== getEdge || $[6] !== getNode || $[7] !== nodeKeys) { t2 = { nodeCount: nodeKeys.length, edgeCount: edgeKeys.length, nodeKeys, edgeKeys, getNode, getEdge }; $[4] = edgeKeys; $[5] = getEdge; $[6] = getNode; $[7] = nodeKeys; $[8] = t2; } else { t2 = $[8]; } return t2; } // src/hooks/useZoomTransition.ts var import_compiler_runtime18 = require("react/compiler-runtime"); var import_react7 = require("react"); var import_jotai44 = require("jotai"); init_viewport_store(); function easeInOutCubic(t) { return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; } function useZoomTransition() { const $ = (0, import_compiler_runtime18.c)(15); const target = (0, import_jotai44.useAtomValue)(zoomAnimationTargetAtom); const setZoom = (0, import_jotai44.useSetAtom)(zoomAtom); const setPan = (0, import_jotai44.useSetAtom)(panAtom); const setTarget = (0, import_jotai44.useSetAtom)(zoomAnimationTargetAtom); const setProgress = (0, import_jotai44.useSetAtom)(zoomTransitionProgressAtom); const setFocusNode = (0, import_jotai44.useSetAtom)(zoomFocusNodeIdAtom); const progress = (0, import_jotai44.useAtomValue)(zoomTransitionProgressAtom); const rafRef = (0, import_react7.useRef)(null); let t0; if ($[0] !== setFocusNode || $[1] !== setProgress || $[2] !== setTarget) { t0 = () => { if (rafRef.current !== null) { cancelAnimationFrame(rafRef.current); rafRef.current = null; } setTarget(null); setProgress(0); setFocusNode(null); }; $[0] = setFocusNode; $[1] = setProgress; $[2] = setTarget; $[3] = t0; } else { t0 = $[3]; } const cancel = t0; let t1; let t2; if ($[4] !== setPan || $[5] !== setProgress || $[6] !== setTarget || $[7] !== setZoom || $[8] !== target) { t1 = () => { if (!target) { return; } const animate = () => { const elapsed = performance.now() - target.startTime; const rawT = Math.min(1, elapsed / target.duration); const t = easeInOutCubic(rawT); const currentZoom = target.startZoom + (target.targetZoom - target.startZoom) * t; const currentPanX = target.startPan.x + (target.targetPan.x - target.startPan.x) * t; const currentPanY = target.startPan.y + (target.targetPan.y - target.startPan.y) * t; setZoom(currentZoom); setPan({ x: currentPanX, y: currentPanY }); setProgress(t); if (rawT < 1) { rafRef.current = requestAnimationFrame(animate); } else { rafRef.current = null; setTarget(null); } }; rafRef.current = requestAnimationFrame(animate); return () => { if (rafRef.current !== null) { cancelAnimationFrame(rafRef.current); rafRef.current = null; } }; }; t2 = [target, setZoom, setPan, setTarget, setProgress]; $[4] = setPan; $[5] = setProgress; $[6] = setTarget; $[7] = setZoom; $[8] = target; $[9] = t1; $[10] = t2; } else { t1 = $[9]; t2 = $[10]; } (0, import_react7.useEffect)(t1, t2); const t3 = target !== null; let t4; if ($[11] !== cancel || $[12] !== progress || $[13] !== t3) { t4 = { isAnimating: t3, progress, cancel }; $[11] = cancel; $[12] = progress; $[13] = t3; $[14] = t4; } else { t4 = $[14]; } return t4; } // src/hooks/useSplitGesture.ts var import_compiler_runtime19 = require("react/compiler-runtime"); var import_react8 = require("react"); var import_jotai45 = require("jotai"); init_graph_mutations(); init_viewport_store(); var SPLIT_THRESHOLD = 80; function useSplitGesture(nodeId) { const $ = (0, import_compiler_runtime19.c)(9); const splitNode = (0, import_jotai45.useSetAtom)(splitNodeAtom); const screenToWorld = (0, import_jotai45.useAtomValue)(screenToWorldAtom); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ new Map(); $[0] = t0; } else { t0 = $[0]; } const pointersRef = (0, import_react8.useRef)(t0); const initialDistanceRef = (0, import_react8.useRef)(null); const splitFiredRef = (0, import_react8.useRef)(false); const getDistance = _temp6; let t1; if ($[1] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t1 = (e) => { if (e.pointerType !== "touch") { return; } pointersRef.current.set(e.pointerId, { pointerId: e.pointerId, x: e.clientX, y: e.clientY }); if (pointersRef.current.size === 2) { const [p1, p2] = Array.from(pointersRef.current.values()); initialDistanceRef.current = getDistance(p1, p2); splitFiredRef.current = false; } }; $[1] = t1; } else { t1 = $[1]; } const onPointerDown = t1; let t2; if ($[2] !== nodeId || $[3] !== screenToWorld || $[4] !== splitNode) { t2 = (e_0) => { if (e_0.pointerType !== "touch") { return; } if (!pointersRef.current.has(e_0.pointerId)) { return; } pointersRef.current.set(e_0.pointerId, { pointerId: e_0.pointerId, x: e_0.clientX, y: e_0.clientY }); if (pointersRef.current.size === 2 && initialDistanceRef.current !== null && !splitFiredRef.current) { const [p1_0, p2_0] = Array.from(pointersRef.current.values()); const currentDistance = getDistance(p1_0, p2_0); const delta = currentDistance - initialDistanceRef.current; if (delta > SPLIT_THRESHOLD) { splitFiredRef.current = true; e_0.stopPropagation(); const world1 = screenToWorld(p1_0.x, p1_0.y); const world2 = screenToWorld(p2_0.x, p2_0.y); splitNode({ nodeId, position1: world1, position2: world2 }); } } }; $[2] = nodeId; $[3] = screenToWorld; $[4] = splitNode; $[5] = t2; } else { t2 = $[5]; } const onPointerMove = t2; let t3; if ($[6] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t3 = (e_1) => { pointersRef.current.delete(e_1.pointerId); if (pointersRef.current.size < 2) { initialDistanceRef.current = null; splitFiredRef.current = false; } }; $[6] = t3; } else { t3 = $[6]; } const onPointerUp = t3; let t4; if ($[7] !== onPointerMove) { t4 = { onPointerDown, onPointerMove, onPointerUp }; $[7] = onPointerMove; $[8] = t4; } else { t4 = $[8]; } return t4; } function _temp6(a, b) { return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2); } // src/hooks/useAnimatedLayout.ts var import_jotai46 = require("jotai"); var import_react9 = require("react"); init_graph_store(); init_graph_position(); init_history_store(); init_debug(); init_reduced_motion_store(); var debug16 = createDebug("animated-layout"); function easeInOutCubic2(t) { return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; } function useAnimatedLayout(options = {}) { const { onPositionsChanged, duration = 400 } = options; const graph = (0, import_jotai46.useAtomValue)(graphAtom); const updateNodePosition = (0, import_jotai46.useSetAtom)(updateNodePositionAtom); const pushHistory = (0, import_jotai46.useSetAtom)(pushHistoryAtom); const setPositionCounter = (0, import_jotai46.useSetAtom)(nodePositionUpdateCounterAtom); const reducedMotion = (0, import_jotai46.useAtomValue)(prefersReducedMotionAtom); const isAnimatingRef = (0, import_react9.useRef)(false); const animate = async (targets, label) => { if (isAnimatingRef.current) return; if (targets.size === 0) return; if (label) pushHistory(label); isAnimatingRef.current = true; if (reducedMotion) { for (const [nodeId, target] of targets) { updateNodePosition({ nodeId, position: target }); } isAnimatingRef.current = false; setPositionCounter((c) => c + 1); if (onPositionsChanged) { const updates = []; for (const [nodeId_0, target_0] of targets) { updates.push({ nodeId: nodeId_0, position: target_0 }); } Promise.resolve(onPositionsChanged(updates)).catch((err) => debug16.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((resolve2) => { const startTime = performance.now(); function tick() { const elapsed = performance.now() - startTime; const t = Math.min(elapsed / duration, 1); const eased = easeInOutCubic2(t); for (const [nodeId_2, target_1] of targets) { const start = starts.get(nodeId_2); if (!start) continue; const x = Math.round(start.x + (target_1.x - start.x) * eased); const y = Math.round(start.y + (target_1.y - start.y) * eased); updateNodePosition({ nodeId: nodeId_2, position: { x, y } }); } if (t < 1) { requestAnimationFrame(tick); } else { isAnimatingRef.current = false; setPositionCounter((c_0) => c_0 + 1); if (onPositionsChanged) { const updates_0 = []; for (const [nodeId_3, target_2] of targets) { updates_0.push({ nodeId: nodeId_3, position: target_2 }); } Promise.resolve(onPositionsChanged(updates_0)).catch((err_0) => debug16.error("Position change callback failed: %O", err_0)); } resolve2(); } } requestAnimationFrame(tick); }); }; return { animate, isAnimating: isAnimatingRef.current }; } // src/hooks/useTreeLayout.ts var import_jotai47 = require("jotai"); var import_react10 = require("react"); init_graph_store(); init_graph_derived(); function useTreeLayout(options = {}) { const { direction = "top-down", levelGap = 200, nodeGap = 100, ...animateOptions } = options; const graph = (0, import_jotai47.useAtomValue)(graphAtom); const nodes = (0, import_jotai47.useAtomValue)(uiNodesAtom); const { animate, isAnimating } = useAnimatedLayout(animateOptions); const isRunningRef = (0, import_react10.useRef)(false); const applyLayout = async () => { if (isRunningRef.current || isAnimating) return; if (nodes.length === 0) return; isRunningRef.current = true; const nodeIds = new Set(nodes.map((n) => n.id)); const children = /* @__PURE__ */ new Map(); const hasIncoming = /* @__PURE__ */ new Set(); for (const nodeId of nodeIds) { children.set(nodeId, []); } graph.forEachEdge((_key, _attrs, source, target) => { if (nodeIds.has(source) && nodeIds.has(target) && source !== target) { children.get(source)?.push(target); hasIncoming.add(target); } }); const roots = [...nodeIds].filter((id) => !hasIncoming.has(id)); if (roots.length === 0) { roots.push(nodes[0].id); } const levels = /* @__PURE__ */ new Map(); const queue = [...roots]; for (const r of roots) levels.set(r, 0); while (queue.length > 0) { const current = queue.shift(); const level = levels.get(current); for (const child of children.get(current) || []) { if (!levels.has(child)) { levels.set(child, level + 1); queue.push(child); } } } for (const nodeId_0 of nodeIds) { if (!levels.has(nodeId_0)) levels.set(nodeId_0, 0); } const byLevel = /* @__PURE__ */ new Map(); for (const [nodeId_1, level_0] of levels) { if (!byLevel.has(level_0)) byLevel.set(level_0, []); byLevel.get(level_0).push(nodeId_1); } const targets = /* @__PURE__ */ new Map(); const maxLevel = Math.max(...byLevel.keys()); for (const [level_1, nodeIdsAtLevel] of byLevel) { const count = nodeIdsAtLevel.length; let maxNodeSize = 200; for (const nid of nodeIdsAtLevel) { if (graph.hasNode(nid)) { const attrs = graph.getNodeAttributes(nid); maxNodeSize = Math.max(maxNodeSize, attrs.width || 200); } } const totalWidth = (count - 1) * (maxNodeSize + nodeGap); const startX = -totalWidth / 2; for (let i = 0; i < count; i++) { const primary = level_1 * levelGap; const secondary = startX + i * (maxNodeSize + nodeGap); if (direction === "top-down") { targets.set(nodeIdsAtLevel[i], { x: secondary, y: primary }); } else { targets.set(nodeIdsAtLevel[i], { x: primary, y: secondary }); } } } await animate(targets, direction === "top-down" ? "Tree layout" : "Horizontal layout"); isRunningRef.current = false; }; return { applyLayout, isRunning: isRunningRef.current || isAnimating }; } // src/hooks/useGridLayout.ts var import_jotai48 = require("jotai"); var import_react11 = require("react"); init_graph_store(); init_graph_derived(); function useGridLayout(options = {}) { const { columns, gap = 80, ...animateOptions } = options; const graph = (0, import_jotai48.useAtomValue)(graphAtom); const nodes = (0, import_jotai48.useAtomValue)(uiNodesAtom); const { animate, isAnimating } = useAnimatedLayout(animateOptions); const isRunningRef = (0, import_react11.useRef)(false); const applyLayout = async () => { if (isRunningRef.current || isAnimating) return; if (nodes.length === 0) return; isRunningRef.current = true; const sorted = [...nodes].sort((a, b) => { const ay = a.position?.y ?? 0; const by = b.position?.y ?? 0; if (Math.abs(ay - by) > 50) return ay - by; return (a.position?.x ?? 0) - (b.position?.x ?? 0); }); const cols = columns ?? Math.ceil(Math.sqrt(sorted.length)); let maxW = 200; let maxH = 100; for (const node of sorted) { if (graph.hasNode(node.id)) { const attrs = graph.getNodeAttributes(node.id); maxW = Math.max(maxW, attrs.width || 200); maxH = Math.max(maxH, attrs.height || 100); } } const cellW = maxW + gap; const cellH = maxH + gap; const rows = Math.ceil(sorted.length / cols); const totalW = (cols - 1) * cellW; const totalH = (rows - 1) * cellH; const offsetX = -totalW / 2; const offsetY = -totalH / 2; const targets = /* @__PURE__ */ new Map(); for (let i = 0; i < sorted.length; i++) { const col = i % cols; const row = Math.floor(i / cols); targets.set(sorted[i].id, { x: Math.round(offsetX + col * cellW), y: Math.round(offsetY + row * cellH) }); } await animate(targets, "Grid layout"); isRunningRef.current = false; }; return { applyLayout, isRunning: isRunningRef.current || isAnimating }; } // src/hooks/usePlugin.ts var import_compiler_runtime20 = require("react/compiler-runtime"); var import_react12 = require("react"); init_plugin_registry(); function usePlugin(plugin) { const $ = (0, import_compiler_runtime20.c)(4); const registeredRef = (0, import_react12.useRef)(false); let t0; if ($[0] !== plugin) { t0 = () => { if (!hasPlugin(plugin.id)) { registerPlugin(plugin); registeredRef.current = true; } return () => { if (registeredRef.current && hasPlugin(plugin.id)) { try { unregisterPlugin(plugin.id); } catch { } registeredRef.current = false; } }; }; $[0] = plugin; $[1] = t0; } else { t0 = $[1]; } let t1; if ($[2] !== plugin.id) { t1 = [plugin.id]; $[2] = plugin.id; $[3] = t1; } else { t1 = $[3]; } (0, import_react12.useEffect)(t0, t1); } function usePlugins(plugins2) { const $ = (0, import_compiler_runtime20.c)(6); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = []; $[0] = t0; } else { t0 = $[0]; } const registeredRef = (0, import_react12.useRef)(t0); let t1; let t2; if ($[1] !== plugins2) { t1 = () => { const registered = []; for (const plugin of plugins2) { if (!hasPlugin(plugin.id)) { registerPlugin(plugin); registered.push(plugin.id); } } registeredRef.current = registered; return () => { for (const id of registeredRef.current.reverse()) { if (hasPlugin(id)) { try { unregisterPlugin(id); } catch { } } } registeredRef.current = []; }; }; t2 = plugins2.map(_temp7).join(","); $[1] = plugins2; $[2] = t1; $[3] = t2; } else { t1 = $[2]; t2 = $[3]; } let t3; if ($[4] !== t2) { t3 = [t2]; $[4] = t2; $[5] = t3; } else { t3 = $[5]; } (0, import_react12.useEffect)(t1, t3); } function _temp7(p) { return p.id; } // src/utils/index.ts init_debug(); init_mutation_queue(); // src/utils/component-registry.tsx var import_compiler_runtime21 = require("react/compiler-runtime"); var import_react13 = __toESM(require("react")); var import_jsx_runtime2 = require("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 $ = (0, import_compiler_runtime21.c)(2); let t0; if ($[0] !== props) { t0 = /* @__PURE__ */ (0, import_jsx_runtime2.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__ */ (0, import_jsx_runtime2.jsx)("div", { style: { padding: "10px", color: "orange" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: message }) }); }; } // src/utils/index.ts init_edge_path_calculators(); init_layout(); init_edge_path_registry(); // src/db/adapter.ts var import_jotai49 = require("jotai"); var InMemoryStorageAdapter = class { constructor() { __publicField(this, "nodes", /* @__PURE__ */ new Map()); __publicField(this, "edges", /* @__PURE__ */ new Map()); } async fetchNodes(graphId) { return Array.from(this.nodes.values()).filter((n) => n.graph_id === graphId); } async createNode(graphId, node) { const now = (/* @__PURE__ */ new Date()).toISOString(); const fullNode = { id: node.id ?? crypto.randomUUID(), graph_id: graphId, label: node.label ?? null, node_type: node.node_type ?? null, configuration: node.configuration ?? null, ui_properties: node.ui_properties ?? null, data: node.data ?? null, created_at: now, updated_at: now }; this.nodes.set(fullNode.id, fullNode); return fullNode; } async updateNode(nodeId, updates) { const existing = this.nodes.get(nodeId); if (!existing) throw new Error(`Node ${nodeId} not found`); const updated = { ...existing, ...updates, updated_at: (/* @__PURE__ */ new Date()).toISOString() }; this.nodes.set(nodeId, updated); return updated; } async deleteNode(nodeId) { this.nodes.delete(nodeId); for (const [edgeId, edge] of this.edges) { if (edge.source_node_id === nodeId || edge.target_node_id === nodeId) { this.edges.delete(edgeId); } } } async fetchEdges(graphId) { return Array.from(this.edges.values()).filter((e) => e.graph_id === graphId); } async createEdge(graphId, edge) { const now = (/* @__PURE__ */ new Date()).toISOString(); const fullEdge = { id: edge.id ?? crypto.randomUUID(), graph_id: graphId, source_node_id: edge.source_node_id ?? "", target_node_id: edge.target_node_id ?? "", edge_type: edge.edge_type ?? null, filter_condition: edge.filter_condition ?? null, ui_properties: edge.ui_properties ?? null, data: edge.data ?? null, created_at: now, updated_at: now }; this.edges.set(fullEdge.id, fullEdge); return fullEdge; } async updateEdge(edgeId, updates) { const existing = this.edges.get(edgeId); if (!existing) throw new Error(`Edge ${edgeId} not found`); const updated = { ...existing, ...updates, updated_at: (/* @__PURE__ */ new Date()).toISOString() }; this.edges.set(edgeId, updated); return updated; } async deleteEdge(edgeId) { this.edges.delete(edgeId); } // --- Batch Operations --- async createNodes(graphId, nodes) { return Promise.all(nodes.map((n) => this.createNode(graphId, n))); } async deleteNodes(nodeIds) { for (const id of nodeIds) { await this.deleteNode(id); } } async createEdges(graphId, edges) { return Promise.all(edges.map((e) => this.createEdge(graphId, e))); } async deleteEdges(edgeIds) { for (const id of edgeIds) { await this.deleteEdge(id); } } }; var storageAdapterAtom = (0, import_jotai49.atom)(null); // src/db/supabase-adapter.ts var SupabaseStorageAdapter = class { constructor(client) { this.client = client; } /** Get the underlying Supabase client (for advanced use or backward compat) */ getClient() { return this.client; } // --- Node Operations --- async fetchNodes(graphId) { const { data, error } = await this.client.from("nodes").select("*").eq("graph_id", graphId).order("created_at", { ascending: true }); if (error) throw error; return data || []; } async createNode(graphId, node) { const { data, error } = await this.client.from("nodes").insert({ graph_id: graphId, label: node.label ?? null, node_type: node.node_type ?? null, configuration: node.configuration ?? null, ui_properties: node.ui_properties ?? null, data: node.data ?? null }).select().single(); if (error) throw error; return data; } async updateNode(nodeId, updates) { const { data, error } = await this.client.from("nodes").update({ ...updates, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", nodeId).select().single(); if (error) throw error; return data; } async deleteNode(nodeId) { const { error } = await this.client.from("nodes").delete().eq("id", nodeId); if (error) throw error; } // --- Edge Operations --- async fetchEdges(graphId) { const { data, error } = await this.client.from("edges").select("*").eq("graph_id", graphId).order("created_at", { ascending: true }); if (error) throw error; return data || []; } async createEdge(graphId, edge) { const { data, error } = await this.client.from("edges").insert({ graph_id: graphId, source_node_id: edge.source_node_id ?? "", target_node_id: edge.target_node_id ?? "", edge_type: edge.edge_type ?? null, filter_condition: edge.filter_condition ?? null, ui_properties: edge.ui_properties ?? null, data: edge.data ?? null }).select().single(); if (error) throw error; return data; } async updateEdge(edgeId, updates) { const { data, error } = await this.client.from("edges").update({ ...updates, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", edgeId).select().single(); if (error) throw error; return data; } async deleteEdge(edgeId) { const { error } = await this.client.from("edges").delete().eq("id", edgeId); if (error) throw error; } // --- Realtime (optional) --- subscribe(graphId, onChange) { const channel = this.client.channel(`canvas-${graphId}`).on("postgres_changes", { event: "*", schema: "public", table: "nodes", filter: `graph_id=eq.${graphId}` }, (payload) => { onChange({ type: payload.eventType, table: "nodes", new: payload.new, old: payload.old }); }).on("postgres_changes", { event: "*", schema: "public", table: "edges", filter: `graph_id=eq.${graphId}` }, (payload) => { onChange({ type: payload.eventType, table: "edges", new: payload.new, old: payload.old }); }).subscribe(); return () => { channel.unsubscribe(); }; } }; // src/db/provider.tsx var import_react14 = __toESM(require("react")); var import_jotai50 = require("jotai"); var import_jotai51 = require("jotai"); var import_jsx_runtime3 = require("react/jsx-runtime"); var AdapterContext = /* @__PURE__ */ (0, import_react14.createContext)(null); function CanvasDbProvider({ adapter, children }) { const setAdapter = (0, import_jotai50.useSetAtom)(storageAdapterAtom); const prevAdapterRef = (0, import_react14.useRef)(null); const resolved = adapter === prevAdapterRef.current ? prevAdapterRef.current : adapter; prevAdapterRef.current = resolved; (0, import_react14.useEffect)(() => { setAdapter(resolved); return () => setAdapter(null); }, [resolved, setAdapter]); return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AdapterContext, { value: resolved, children }); } function useStorageAdapter() { const contextAdapter = (0, import_react14.useContext)(AdapterContext); const atomAdapter = (0, import_jotai50.useAtomValue)(storageAdapterAtom); const adapter = contextAdapter || atomAdapter; if (!adapter) { throw new Error("useStorageAdapter must be used within CanvasDbProvider"); } return adapter; } var supabaseClientAtom = (0, import_jotai51.atom)(null); function useSupabaseClient() { const client = (0, import_jotai51.useAtomValue)(supabaseClientAtom); if (!client) { throw new Error("useSupabaseClient: no Supabase client available. Use CanvasDbProvider with a SupabaseStorageAdapter."); } return client; } // src/db/queries/nodes.ts init_debug(); var debug17 = createDebug("db:nodes"); async function fetchGraphNodes(supabase, graphId) { if (!graphId) { throw new Error("graphId is required to fetch nodes"); } const { data, error } = await supabase.from("nodes").select("*").eq("graph_id", graphId).order("created_at", { ascending: true }); if (error) { debug17.error("Error fetching nodes for graph %s: %O", graphId, error); throw error; } return data || []; } async function fetchGraphNode(supabase, nodeId) { if (!nodeId) { throw new Error("nodeId is required to fetch node"); } const { data, error } = await supabase.from("nodes").select("*").eq("id", nodeId).single(); if (error) { if (error.code === "PGRST116") { return null; } debug17.error("Error fetching node %s: %O", nodeId, error); throw error; } return data; } async function createGraphNode(supabase, node) { const { data, error } = await supabase.from("nodes").insert(node).select().single(); if (error) { debug17.error("Error creating node: %O", error); throw error; } return data; } async function updateGraphNode(supabase, nodeId, updates) { const { data, error } = await supabase.from("nodes").update({ ...updates, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", nodeId).select().single(); if (error) { debug17.error("Error updating node %s: %O", nodeId, error); throw error; } return data; } async function deleteGraphNode(supabase, nodeId) { const { error } = await supabase.from("nodes").delete().eq("id", nodeId); if (error) { debug17.error("Error deleting node %s: %O", nodeId, error); throw error; } } // src/db/queries/edges.ts init_debug(); var debug18 = createDebug("db:edges"); async function fetchGraphEdges(supabase, graphId) { if (!graphId) { throw new Error("graphId is required to fetch edges"); } const { data, error } = await supabase.from("edges").select("*").eq("graph_id", graphId).order("created_at", { ascending: true }); if (error) { debug18.error("Error fetching edges for graph %s: %O", graphId, error); throw error; } return data || []; } async function createGraphEdge(supabase, edge) { const { data, error } = await supabase.from("edges").insert(edge).select().single(); if (error) { debug18.error("Error creating edge: %O", error); throw error; } return data; } async function updateGraphEdge(supabase, edgeId, updates) { const { data, error } = await supabase.from("edges").update({ ...updates, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", edgeId).select().single(); if (error) { debug18.error("Error updating edge %s: %O", edgeId, error); throw error; } return data; } async function deleteGraphEdge(supabase, edgeId) { const { error } = await supabase.from("edges").delete().eq("id", edgeId); if (error) { debug18.error("Error deleting edge %s: %O", edgeId, error); throw error; } } // src/db/hooks/keys.ts var canvasKeys = { all: ["canvas"], graphs: () => [...canvasKeys.all, "graphs"], graph: (graphId) => [...canvasKeys.graphs(), graphId], nodes: (graphId) => [...canvasKeys.graph(graphId), "nodes"], node: (graphId, nodeId) => [...canvasKeys.nodes(graphId), nodeId], edges: (graphId) => [...canvasKeys.graph(graphId), "edges"], edge: (graphId, edgeId) => [...canvasKeys.edges(graphId), edgeId] }; // src/db/hooks/useGraphNodes.ts var import_compiler_runtime22 = require("react/compiler-runtime"); var import_react_query = require("@tanstack/react-query"); function useGraphNodes(graphId, t0) { const $ = (0, import_compiler_runtime22.c)(11); let t1; if ($[0] !== t0) { t1 = t0 === void 0 ? {} : t0; $[0] = t0; $[1] = t1; } else { t1 = $[1]; } const options = t1; const { enabled: t2 } = options; const enabled = t2 === void 0 ? true : t2; const supabase = useSupabaseClient(); let t3; if ($[2] !== graphId) { t3 = canvasKeys.nodes(graphId || ""); $[2] = graphId; $[3] = t3; } else { t3 = $[3]; } let t4; if ($[4] !== graphId || $[5] !== supabase) { t4 = () => fetchGraphNodes(supabase, graphId); $[4] = graphId; $[5] = supabase; $[6] = t4; } else { t4 = $[6]; } const t5 = enabled && !!graphId; let t6; if ($[7] !== t3 || $[8] !== t4 || $[9] !== t5) { t6 = { queryKey: t3, queryFn: t4, enabled: t5 }; $[7] = t3; $[8] = t4; $[9] = t5; $[10] = t6; } else { t6 = $[10]; } return (0, import_react_query.useQuery)(t6); } // src/db/hooks/useGraphEdges.ts var import_compiler_runtime23 = require("react/compiler-runtime"); var import_react_query2 = require("@tanstack/react-query"); function useGraphEdges(graphId, t0) { const $ = (0, import_compiler_runtime23.c)(11); let t1; if ($[0] !== t0) { t1 = t0 === void 0 ? {} : t0; $[0] = t0; $[1] = t1; } else { t1 = $[1]; } const options = t1; const { enabled: t2 } = options; const enabled = t2 === void 0 ? true : t2; const supabase = useSupabaseClient(); let t3; if ($[2] !== graphId) { t3 = canvasKeys.edges(graphId || ""); $[2] = graphId; $[3] = t3; } else { t3 = $[3]; } let t4; if ($[4] !== graphId || $[5] !== supabase) { t4 = () => fetchGraphEdges(supabase, graphId); $[4] = graphId; $[5] = supabase; $[6] = t4; } else { t4 = $[6]; } const t5 = enabled && !!graphId; let t6; if ($[7] !== t3 || $[8] !== t4 || $[9] !== t5) { t6 = { queryKey: t3, queryFn: t4, enabled: t5 }; $[7] = t3; $[8] = t4; $[9] = t5; $[10] = t6; } else { t6 = $[10]; } return (0, import_react_query2.useQuery)(t6); } // src/db/hooks/useUpdateNode.ts var import_compiler_runtime24 = require("react/compiler-runtime"); var import_react_query3 = require("@tanstack/react-query"); var import_jotai52 = require("jotai"); init_sync_store(); function useUpdateNode() { const $ = (0, import_compiler_runtime24.c)(18); const queryClient = (0, import_react_query3.useQueryClient)(); const supabase = useSupabaseClient(); const startMutation = (0, import_jotai52.useSetAtom)(startMutationAtom); const completeMutation = (0, import_jotai52.useSetAtom)(completeMutationAtom); let t0; if ($[0] !== supabase) { t0 = async (t12) => { const { nodeId, updates } = t12; return updateGraphNode(supabase, nodeId, updates); }; $[0] = supabase; $[1] = t0; } else { t0 = $[1]; } let t1; if ($[2] !== queryClient || $[3] !== startMutation) { t1 = async (t22) => { const { nodeId: nodeId_0, graphId, updates: updates_0 } = t22; startMutation(); await queryClient.cancelQueries({ queryKey: canvasKeys.nodes(graphId) }); const previousNodes = queryClient.getQueryData(canvasKeys.nodes(graphId)); if (previousNodes) { queryClient.setQueryData(canvasKeys.nodes(graphId), (old) => old?.map((node) => node.id === nodeId_0 ? { ...node, ...updates_0, updated_at: (/* @__PURE__ */ new Date()).toISOString() } : node)); } return { previousNodes, graphId }; }; $[2] = queryClient; $[3] = startMutation; $[4] = t1; } else { t1 = $[4]; } let t2; if ($[5] !== completeMutation || $[6] !== queryClient) { t2 = (_err, _variables, context) => { if (context?.previousNodes) { queryClient.setQueryData(canvasKeys.nodes(context.graphId), context.previousNodes); } completeMutation(false); }; $[5] = completeMutation; $[6] = queryClient; $[7] = t2; } else { t2 = $[7]; } let t3; if ($[8] !== completeMutation) { t3 = () => { completeMutation(true); }; $[8] = completeMutation; $[9] = t3; } else { t3 = $[9]; } let t4; if ($[10] !== queryClient) { t4 = (_data, _error, variables) => { queryClient.invalidateQueries({ queryKey: canvasKeys.nodes(variables.graphId) }); }; $[10] = queryClient; $[11] = t4; } else { t4 = $[11]; } let t5; if ($[12] !== t0 || $[13] !== t1 || $[14] !== t2 || $[15] !== t3 || $[16] !== t4) { t5 = { mutationFn: t0, onMutate: t1, onError: t2, onSuccess: t3, onSettled: t4 }; $[12] = t0; $[13] = t1; $[14] = t2; $[15] = t3; $[16] = t4; $[17] = t5; } else { t5 = $[17]; } return (0, import_react_query3.useMutation)(t5); } // src/db/hooks/useCreateNode.ts var import_compiler_runtime25 = require("react/compiler-runtime"); var import_react_query4 = require("@tanstack/react-query"); var import_jotai53 = require("jotai"); init_sync_store(); function useCreateNode() { const $ = (0, import_compiler_runtime25.c)(17); const queryClient = (0, import_react_query4.useQueryClient)(); const supabase = useSupabaseClient(); const startMutation = (0, import_jotai53.useSetAtom)(startMutationAtom); const completeMutation = (0, import_jotai53.useSetAtom)(completeMutationAtom); let t0; if ($[0] !== supabase) { t0 = async (node) => createGraphNode(supabase, node); $[0] = supabase; $[1] = t0; } else { t0 = $[1]; } let t1; if ($[2] !== queryClient || $[3] !== startMutation) { t1 = async (node_0) => { startMutation(); await queryClient.cancelQueries({ queryKey: canvasKeys.nodes(node_0.graph_id) }); const previousNodes = queryClient.getQueryData(canvasKeys.nodes(node_0.graph_id)); const optimisticNode = { ...node_0, id: `temp-${Date.now()}`, created_at: (/* @__PURE__ */ new Date()).toISOString(), updated_at: (/* @__PURE__ */ new Date()).toISOString() }; queryClient.setQueryData(canvasKeys.nodes(node_0.graph_id), (old) => [...old || [], optimisticNode]); return { previousNodes, graphId: node_0.graph_id, optimisticId: optimisticNode.id }; }; $[2] = queryClient; $[3] = startMutation; $[4] = t1; } else { t1 = $[4]; } let t2; let t3; if ($[5] !== completeMutation || $[6] !== queryClient) { t2 = (_err, _variables, context) => { if (context?.previousNodes) { queryClient.setQueryData(canvasKeys.nodes(context.graphId), context.previousNodes); } completeMutation(false); }; t3 = (newNode, _variables_0, context_0) => { if (context_0) { queryClient.setQueryData(canvasKeys.nodes(context_0.graphId), (old_0) => old_0?.map((node_1) => node_1.id === context_0.optimisticId ? newNode : node_1)); } completeMutation(true); }; $[5] = completeMutation; $[6] = queryClient; $[7] = t2; $[8] = t3; } else { t2 = $[7]; t3 = $[8]; } let t4; if ($[9] !== queryClient) { t4 = (_data, _error, variables) => { queryClient.invalidateQueries({ queryKey: canvasKeys.nodes(variables.graph_id) }); }; $[9] = queryClient; $[10] = t4; } else { t4 = $[10]; } let t5; if ($[11] !== t0 || $[12] !== t1 || $[13] !== t2 || $[14] !== t3 || $[15] !== t4) { t5 = { mutationFn: t0, onMutate: t1, onError: t2, onSuccess: t3, onSettled: t4 }; $[11] = t0; $[12] = t1; $[13] = t2; $[14] = t3; $[15] = t4; $[16] = t5; } else { t5 = $[16]; } return (0, import_react_query4.useMutation)(t5); } // src/db/hooks/useDeleteNode.ts var import_compiler_runtime26 = require("react/compiler-runtime"); var import_react_query5 = require("@tanstack/react-query"); var import_jotai54 = require("jotai"); init_sync_store(); function useDeleteNode() { const $ = (0, import_compiler_runtime26.c)(18); const queryClient = (0, import_react_query5.useQueryClient)(); const supabase = useSupabaseClient(); const startMutation = (0, import_jotai54.useSetAtom)(startMutationAtom); const completeMutation = (0, import_jotai54.useSetAtom)(completeMutationAtom); let t0; if ($[0] !== supabase) { t0 = async (t12) => { const { nodeId } = t12; return deleteGraphNode(supabase, nodeId); }; $[0] = supabase; $[1] = t0; } else { t0 = $[1]; } let t1; if ($[2] !== queryClient || $[3] !== startMutation) { t1 = async (t22) => { const { nodeId: nodeId_0, graphId } = t22; startMutation(); await queryClient.cancelQueries({ queryKey: canvasKeys.nodes(graphId) }); const previousNodes = queryClient.getQueryData(canvasKeys.nodes(graphId)); if (previousNodes) { queryClient.setQueryData(canvasKeys.nodes(graphId), (old) => old?.filter((node) => node.id !== nodeId_0)); } return { previousNodes, graphId }; }; $[2] = queryClient; $[3] = startMutation; $[4] = t1; } else { t1 = $[4]; } let t2; if ($[5] !== completeMutation || $[6] !== queryClient) { t2 = (_err, _variables, context) => { if (context?.previousNodes) { queryClient.setQueryData(canvasKeys.nodes(context.graphId), context.previousNodes); } completeMutation(false); }; $[5] = completeMutation; $[6] = queryClient; $[7] = t2; } else { t2 = $[7]; } let t3; if ($[8] !== completeMutation) { t3 = () => { completeMutation(true); }; $[8] = completeMutation; $[9] = t3; } else { t3 = $[9]; } let t4; if ($[10] !== queryClient) { t4 = (_data, _error, variables) => { queryClient.invalidateQueries({ queryKey: canvasKeys.nodes(variables.graphId) }); }; $[10] = queryClient; $[11] = t4; } else { t4 = $[11]; } let t5; if ($[12] !== t0 || $[13] !== t1 || $[14] !== t2 || $[15] !== t3 || $[16] !== t4) { t5 = { mutationFn: t0, onMutate: t1, onError: t2, onSuccess: t3, onSettled: t4 }; $[12] = t0; $[13] = t1; $[14] = t2; $[15] = t3; $[16] = t4; $[17] = t5; } else { t5 = $[17]; } return (0, import_react_query5.useMutation)(t5); } // src/db/hooks/useUpdateEdge.ts var import_compiler_runtime27 = require("react/compiler-runtime"); var import_react_query6 = require("@tanstack/react-query"); var import_jotai55 = require("jotai"); init_sync_store(); function useUpdateEdge() { const $ = (0, import_compiler_runtime27.c)(18); const queryClient = (0, import_react_query6.useQueryClient)(); const supabase = useSupabaseClient(); const startMutation = (0, import_jotai55.useSetAtom)(startMutationAtom); const completeMutation = (0, import_jotai55.useSetAtom)(completeMutationAtom); let t0; if ($[0] !== supabase) { t0 = async (t12) => { const { edgeId, updates } = t12; return updateGraphEdge(supabase, edgeId, updates); }; $[0] = supabase; $[1] = t0; } else { t0 = $[1]; } let t1; if ($[2] !== queryClient || $[3] !== startMutation) { t1 = async (t22) => { const { edgeId: edgeId_0, graphId, updates: updates_0 } = t22; startMutation(); await queryClient.cancelQueries({ queryKey: canvasKeys.edges(graphId) }); const previousEdges = queryClient.getQueryData(canvasKeys.edges(graphId)); if (previousEdges) { queryClient.setQueryData(canvasKeys.edges(graphId), (old) => old?.map((edge) => edge.id === edgeId_0 ? { ...edge, ...updates_0, updated_at: (/* @__PURE__ */ new Date()).toISOString() } : edge)); } return { previousEdges, graphId }; }; $[2] = queryClient; $[3] = startMutation; $[4] = t1; } else { t1 = $[4]; } let t2; if ($[5] !== completeMutation || $[6] !== queryClient) { t2 = (_err, _variables, context) => { if (context?.previousEdges) { queryClient.setQueryData(canvasKeys.edges(context.graphId), context.previousEdges); } completeMutation(false); }; $[5] = completeMutation; $[6] = queryClient; $[7] = t2; } else { t2 = $[7]; } let t3; if ($[8] !== completeMutation) { t3 = () => { completeMutation(true); }; $[8] = completeMutation; $[9] = t3; } else { t3 = $[9]; } let t4; if ($[10] !== queryClient) { t4 = (_data, _error, variables) => { queryClient.invalidateQueries({ queryKey: canvasKeys.edges(variables.graphId) }); }; $[10] = queryClient; $[11] = t4; } else { t4 = $[11]; } let t5; if ($[12] !== t0 || $[13] !== t1 || $[14] !== t2 || $[15] !== t3 || $[16] !== t4) { t5 = { mutationFn: t0, onMutate: t1, onError: t2, onSuccess: t3, onSettled: t4 }; $[12] = t0; $[13] = t1; $[14] = t2; $[15] = t3; $[16] = t4; $[17] = t5; } else { t5 = $[17]; } return (0, import_react_query6.useMutation)(t5); } // src/db/hooks/useCreateEdge.ts var import_compiler_runtime28 = require("react/compiler-runtime"); var import_react_query7 = require("@tanstack/react-query"); var import_jotai56 = require("jotai"); init_sync_store(); function useCreateEdge() { const $ = (0, import_compiler_runtime28.c)(17); const queryClient = (0, import_react_query7.useQueryClient)(); const supabase = useSupabaseClient(); const startMutation = (0, import_jotai56.useSetAtom)(startMutationAtom); const completeMutation = (0, import_jotai56.useSetAtom)(completeMutationAtom); let t0; if ($[0] !== supabase) { t0 = async (edge) => createGraphEdge(supabase, edge); $[0] = supabase; $[1] = t0; } else { t0 = $[1]; } let t1; if ($[2] !== queryClient || $[3] !== startMutation) { t1 = async (edge_0) => { startMutation(); await queryClient.cancelQueries({ queryKey: canvasKeys.edges(edge_0.graph_id) }); const previousEdges = queryClient.getQueryData(canvasKeys.edges(edge_0.graph_id)); const optimisticEdge = { ...edge_0, id: `temp-${Date.now()}`, created_at: (/* @__PURE__ */ new Date()).toISOString(), updated_at: (/* @__PURE__ */ new Date()).toISOString() }; queryClient.setQueryData(canvasKeys.edges(edge_0.graph_id), (old) => [...old || [], optimisticEdge]); return { previousEdges, graphId: edge_0.graph_id, optimisticId: optimisticEdge.id }; }; $[2] = queryClient; $[3] = startMutation; $[4] = t1; } else { t1 = $[4]; } let t2; let t3; if ($[5] !== completeMutation || $[6] !== queryClient) { t2 = (_err, _variables, context) => { if (context?.previousEdges) { queryClient.setQueryData(canvasKeys.edges(context.graphId), context.previousEdges); } completeMutation(false); }; t3 = (newEdge, _variables_0, context_0) => { if (context_0) { queryClient.setQueryData(canvasKeys.edges(context_0.graphId), (old_0) => old_0?.map((edge_1) => edge_1.id === context_0.optimisticId ? newEdge : edge_1)); } completeMutation(true); }; $[5] = completeMutation; $[6] = queryClient; $[7] = t2; $[8] = t3; } else { t2 = $[7]; t3 = $[8]; } let t4; if ($[9] !== queryClient) { t4 = (_data, _error, variables) => { queryClient.invalidateQueries({ queryKey: canvasKeys.edges(variables.graph_id) }); }; $[9] = queryClient; $[10] = t4; } else { t4 = $[10]; } let t5; if ($[11] !== t0 || $[12] !== t1 || $[13] !== t2 || $[14] !== t3 || $[15] !== t4) { t5 = { mutationFn: t0, onMutate: t1, onError: t2, onSuccess: t3, onSettled: t4 }; $[11] = t0; $[12] = t1; $[13] = t2; $[14] = t3; $[15] = t4; $[16] = t5; } else { t5 = $[16]; } return (0, import_react_query7.useMutation)(t5); } // src/db/hooks/useDeleteEdge.ts var import_compiler_runtime29 = require("react/compiler-runtime"); var import_react_query8 = require("@tanstack/react-query"); var import_jotai57 = require("jotai"); init_sync_store(); function useDeleteEdge() { const $ = (0, import_compiler_runtime29.c)(18); const queryClient = (0, import_react_query8.useQueryClient)(); const supabase = useSupabaseClient(); const startMutation = (0, import_jotai57.useSetAtom)(startMutationAtom); const completeMutation = (0, import_jotai57.useSetAtom)(completeMutationAtom); let t0; if ($[0] !== supabase) { t0 = async (t12) => { const { edgeId } = t12; return deleteGraphEdge(supabase, edgeId); }; $[0] = supabase; $[1] = t0; } else { t0 = $[1]; } let t1; if ($[2] !== queryClient || $[3] !== startMutation) { t1 = async (t22) => { const { edgeId: edgeId_0, graphId } = t22; startMutation(); await queryClient.cancelQueries({ queryKey: canvasKeys.edges(graphId) }); const previousEdges = queryClient.getQueryData(canvasKeys.edges(graphId)); if (previousEdges) { queryClient.setQueryData(canvasKeys.edges(graphId), (old) => old?.filter((edge) => edge.id !== edgeId_0)); } return { previousEdges, graphId }; }; $[2] = queryClient; $[3] = startMutation; $[4] = t1; } else { t1 = $[4]; } let t2; if ($[5] !== completeMutation || $[6] !== queryClient) { t2 = (_err, _variables, context) => { if (context?.previousEdges) { queryClient.setQueryData(canvasKeys.edges(context.graphId), context.previousEdges); } completeMutation(false); }; $[5] = completeMutation; $[6] = queryClient; $[7] = t2; } else { t2 = $[7]; } let t3; if ($[8] !== completeMutation) { t3 = () => { completeMutation(true); }; $[8] = completeMutation; $[9] = t3; } else { t3 = $[9]; } let t4; if ($[10] !== queryClient) { t4 = (_data, _error, variables) => { queryClient.invalidateQueries({ queryKey: canvasKeys.edges(variables.graphId) }); }; $[10] = queryClient; $[11] = t4; } else { t4 = $[11]; } let t5; if ($[12] !== t0 || $[13] !== t1 || $[14] !== t2 || $[15] !== t3 || $[16] !== t4) { t5 = { mutationFn: t0, onMutate: t1, onError: t2, onSuccess: t3, onSettled: t4 }; $[12] = t0; $[13] = t1; $[14] = t2; $[15] = t3; $[16] = t4; $[17] = t5; } else { t5 = $[17]; } return (0, import_react_query8.useMutation)(t5); } // src/styles/canvas-styles.ts var defaultLightStyles = { background: { color: "#f0f0f0", gradient: { from: "#f0f0f0", to: "#e0e0e0", angle: 180 }, pattern: "none" }, grid: { visible: true, minorLineColor: "rgba(0, 0, 0, 0.05)", majorLineColor: "rgba(0, 0, 0, 0.1)", opacity: 1 }, axes: { visible: true, color: "rgba(0, 0, 0, 0.3)", labelColor: "rgba(0, 0, 0, 0.7)", tickColor: "rgba(0, 0, 0, 0.3)" }, nodes: { defaultBackground: "#FAFAFA", defaultBorderColor: "rgba(0, 0, 0, 0.06)", defaultBorderRadius: 12, selectedBorderColor: "#FF9800", selectedGlowColor: "rgba(255, 152, 0, 0.5)", shadowColor: "rgba(0, 0, 0, 0.1)", shadowOpacity: 1, draggingBackgroundColor: "#FAFAFAd9", draggingBorderColor: "#CCCCCC", resizeHandleColor: "rgba(0, 0, 0, 0.2)", resizeHandleActiveColor: "rgba(59, 130, 246, 0.8)" }, edges: { defaultColor: "#cccccc", defaultWeight: 1.5, labelColor: "#FFFFFF", labelStrokeColor: "#000000", pathType: "bezier" } }; var defaultDarkStyles = { background: { color: "#0f172a", gradient: { from: "#0f172a", to: "#1e293b", angle: 180 }, pattern: "none" }, grid: { visible: true, minorLineColor: "rgba(255, 255, 255, 0.06)", majorLineColor: "rgba(255, 255, 255, 0.12)", opacity: 1 }, axes: { visible: true, color: "rgba(255, 255, 255, 0.35)", labelColor: "rgba(255, 255, 255, 0.7)", tickColor: "rgba(255, 255, 255, 0.35)" }, nodes: { defaultBackground: "#FAFAFA", defaultBorderColor: "rgba(0, 0, 0, 0.06)", defaultBorderRadius: 12, selectedBorderColor: "#FF9800", selectedGlowColor: "rgba(255, 152, 0, 0.5)", shadowColor: "rgba(0, 0, 0, 0.1)", shadowOpacity: 1, draggingBackgroundColor: "#FAFAFAd9", draggingBorderColor: "#666666", resizeHandleColor: "rgba(255, 255, 255, 0.2)", resizeHandleActiveColor: "rgba(96, 165, 250, 0.8)" }, edges: { defaultColor: "#cccccc", defaultWeight: 1.5, labelColor: "#FFFFFF", labelStrokeColor: "#000000", pathType: "bezier" } }; function mergeWithDefaults(userStyles, isDark) { const defaults = isDark ? defaultDarkStyles : defaultLightStyles; if (!userStyles) return defaults; return { background: { ...defaults.background, ...userStyles.background }, grid: { ...defaults.grid, ...userStyles.grid }, axes: { ...defaults.axes, ...userStyles.axes }, nodes: { ...defaults.nodes, ...userStyles.nodes }, edges: { ...defaults.edges, ...userStyles.edges } }; } // src/providers/CanvasProvider.tsx var import_compiler_runtime31 = require("react/compiler-runtime"); var import_react16 = __toESM(require("react")); var import_jotai59 = require("jotai"); var import_react_query9 = require("@tanstack/react-query"); // src/providers/CanvasStyleProvider.tsx var import_compiler_runtime30 = require("react/compiler-runtime"); var import_react15 = __toESM(require("react")); var import_jotai58 = require("jotai"); var import_jsx_runtime4 = require("react/jsx-runtime"); var canvasStylesAtom = (0, import_jotai58.atom)(defaultLightStyles); var canvasIsDarkAtom = (0, import_jotai58.atom)(false); var canvasStyleOverridesAtom = (0, import_jotai58.atom)(void 0); var CanvasStyleContext = /* @__PURE__ */ (0, import_react15.createContext)(null); var CSS_VARIABLE_NAMES = ["--canvas-bg", "--node-bg", "--node-border", "--node-radius", "--node-selected-border", "--node-selected-glow", "--node-shadow", "--node-shadow-opacity", "--node-dragging-bg", "--node-dragging-border", "--node-resize-handle", "--node-resize-handle-active", "--edge-color", "--edge-weight", "--edge-label-color", "--edge-label-stroke"]; function applyStylesToElement(element, styles4) { const { nodes, edges, background } = styles4; element.style.setProperty("--canvas-bg", background.color || ""); element.style.setProperty("--node-bg", nodes.defaultBackground || ""); element.style.setProperty("--node-border", nodes.defaultBorderColor || ""); element.style.setProperty("--node-radius", nodes.defaultBorderRadius ? `${nodes.defaultBorderRadius}px` : "8px"); element.style.setProperty("--node-selected-border", nodes.selectedBorderColor || ""); element.style.setProperty("--node-selected-glow", nodes.selectedGlowColor || ""); element.style.setProperty("--node-shadow", nodes.shadowColor || ""); element.style.setProperty("--node-shadow-opacity", nodes.shadowOpacity?.toString() || "0.2"); element.style.setProperty("--node-dragging-bg", nodes.draggingBackgroundColor || ""); element.style.setProperty("--node-dragging-border", nodes.draggingBorderColor || ""); element.style.setProperty("--node-resize-handle", nodes.resizeHandleColor || ""); element.style.setProperty("--node-resize-handle-active", nodes.resizeHandleActiveColor || ""); element.style.setProperty("--edge-color", edges.defaultColor || ""); element.style.setProperty("--edge-weight", edges.defaultWeight?.toString() || "1.5"); element.style.setProperty("--edge-label-color", edges.labelColor || ""); element.style.setProperty("--edge-label-stroke", edges.labelStrokeColor || ""); } function removeStylesFromElement(element) { CSS_VARIABLE_NAMES.forEach((name) => { element.style.removeProperty(name); }); } function CanvasStyleProvider(t0) { const $ = (0, import_compiler_runtime30.c)(24); const { children, styles: propStyles, isDark: t1 } = t0; const propIsDark = t1 === void 0 ? false : t1; const containerRef = (0, import_react15.useRef)(null); const [styleOverrides, setStyleOverrides] = (0, import_jotai58.useAtom)(canvasStyleOverridesAtom); const [isDarkAtom, setIsDarkAtom] = (0, import_jotai58.useAtom)(canvasIsDarkAtom); const setCanvasStyles = (0, import_jotai58.useSetAtom)(canvasStylesAtom); const effectiveStyles = styleOverrides ?? propStyles; const effectiveIsDark = isDarkAtom || propIsDark; let t2; if ($[0] !== effectiveIsDark || $[1] !== effectiveStyles) { t2 = mergeWithDefaults(effectiveStyles, effectiveIsDark); $[0] = effectiveIsDark; $[1] = effectiveStyles; $[2] = t2; } else { t2 = $[2]; } const mergedStyles = t2; let t3; let t4; if ($[3] !== mergedStyles || $[4] !== setCanvasStyles) { t3 = () => { setCanvasStyles(mergedStyles); }; t4 = [mergedStyles, setCanvasStyles]; $[3] = mergedStyles; $[4] = setCanvasStyles; $[5] = t3; $[6] = t4; } else { t3 = $[5]; t4 = $[6]; } (0, import_react15.useLayoutEffect)(t3, t4); let t5; if ($[7] !== mergedStyles) { t5 = (node) => { containerRef.current = node; if (!node) { return; } applyStylesToElement(node, mergedStyles); return () => removeStylesFromElement(node); }; $[7] = mergedStyles; $[8] = t5; } else { t5 = $[8]; } const containerRefCallback = t5; let t6; if ($[9] !== setStyleOverrides) { t6 = (newStyles) => { setStyleOverrides(newStyles); }; $[9] = setStyleOverrides; $[10] = t6; } else { t6 = $[10]; } const setStyles = t6; let t7; if ($[11] !== setIsDarkAtom) { t7 = (isDark) => { setIsDarkAtom(isDark); }; $[11] = setIsDarkAtom; $[12] = t7; } else { t7 = $[12]; } const setDarkMode = t7; let t8; if ($[13] !== mergedStyles || $[14] !== setDarkMode || $[15] !== setStyles) { t8 = { styles: mergedStyles, containerRef, setStyles, setDarkMode }; $[13] = mergedStyles; $[14] = setDarkMode; $[15] = setStyles; $[16] = t8; } else { t8 = $[16]; } const contextValue = t8; let t9; if ($[17] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t9 = { display: "contents" }; $[17] = t9; } else { t9 = $[17]; } let t10; if ($[18] !== children || $[19] !== containerRefCallback) { t10 = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: containerRefCallback, className: "canvas-style-scope", style: t9, children }); $[18] = children; $[19] = containerRefCallback; $[20] = t10; } else { t10 = $[20]; } let t11; if ($[21] !== contextValue || $[22] !== t10) { t11 = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CanvasStyleContext, { value: contextValue, children: t10 }); $[21] = contextValue; $[22] = t10; $[23] = t11; } else { t11 = $[23]; } return t11; } function useCanvasStyle() { const context = (0, import_react15.useContext)(CanvasStyleContext); if (!context) { throw new Error("useCanvasStyle must be used within a CanvasStyleProvider"); } return context.styles; } function useCanvasTheme() { const $ = (0, import_compiler_runtime30.c)(10); const context = (0, import_react15.useContext)(CanvasStyleContext); const [isDark, setIsDarkAtom] = (0, import_jotai58.useAtom)(canvasIsDarkAtom); const [styleOverrides, setStyleOverrides] = (0, import_jotai58.useAtom)(canvasStyleOverridesAtom); if (!context) { throw new Error("useCanvasTheme must be used within a CanvasStyleProvider"); } let t0; if ($[0] !== setIsDarkAtom || $[1] !== setStyleOverrides) { t0 = () => { setStyleOverrides(void 0); setIsDarkAtom(false); }; $[0] = setIsDarkAtom; $[1] = setStyleOverrides; $[2] = t0; } else { t0 = $[2]; } let t1; if ($[3] !== context.setDarkMode || $[4] !== context.setStyles || $[5] !== context.styles || $[6] !== isDark || $[7] !== styleOverrides || $[8] !== t0) { t1 = { styles: context.styles, isDark, styleOverrides, setDarkMode: context.setDarkMode, setStyles: context.setStyles, resetStyles: t0 }; $[3] = context.setDarkMode; $[4] = context.setStyles; $[5] = context.styles; $[6] = isDark; $[7] = styleOverrides; $[8] = t0; $[9] = t1; } else { t1 = $[9]; } return t1; } // src/providers/CanvasProvider.tsx init_graph_store(); init_graph_mutations(); var import_jsx_runtime5 = require("react/jsx-runtime"); var defaultQueryClient = new import_react_query9.QueryClient({ defaultOptions: { queries: { staleTime: 1e3 * 60, // 1 minute refetchOnWindowFocus: false } } }); function GraphLoader(t0) { const $ = (0, import_compiler_runtime31.c)(19); const { graphId, onGraphLoad, onGraphError, children } = t0; const setCurrentGraphId = (0, import_jotai59.useSetAtom)(currentGraphIdAtom); const loadGraphFromDb = (0, import_jotai59.useSetAtom)(loadGraphFromDbAtom); const { data: nodes, error: nodesError, isLoading: nodesLoading } = useGraphNodes(graphId); const { data: edges, error: edgesError, isLoading: edgesLoading } = useGraphEdges(graphId); let t1; let t2; if ($[0] !== graphId || $[1] !== setCurrentGraphId) { t1 = () => { setCurrentGraphId(graphId); }; t2 = [graphId, setCurrentGraphId]; $[0] = graphId; $[1] = setCurrentGraphId; $[2] = t1; $[3] = t2; } else { t1 = $[2]; t2 = $[3]; } (0, import_react16.useEffect)(t1, t2); let t3; let t4; if ($[4] !== edges || $[5] !== edgesLoading || $[6] !== loadGraphFromDb || $[7] !== nodes || $[8] !== nodesLoading || $[9] !== onGraphLoad) { t3 = () => { if (nodes && edges && !nodesLoading && !edgesLoading) { loadGraphFromDb(nodes, edges); onGraphLoad?.(nodes, edges); } }; t4 = [nodes, edges, nodesLoading, edgesLoading, loadGraphFromDb, onGraphLoad]; $[4] = edges; $[5] = edgesLoading; $[6] = loadGraphFromDb; $[7] = nodes; $[8] = nodesLoading; $[9] = onGraphLoad; $[10] = t3; $[11] = t4; } else { t3 = $[10]; t4 = $[11]; } (0, import_react16.useEffect)(t3, t4); let t5; let t6; if ($[12] !== edgesError || $[13] !== nodesError || $[14] !== onGraphError) { t5 = () => { const error = nodesError || edgesError; if (error) { onGraphError?.(error); } }; t6 = [nodesError, edgesError, onGraphError]; $[12] = edgesError; $[13] = nodesError; $[14] = onGraphError; $[15] = t5; $[16] = t6; } else { t5 = $[15]; t6 = $[16]; } (0, import_react16.useEffect)(t5, t6); let t7; if ($[17] !== children) { t7 = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children }); $[17] = children; $[18] = t7; } else { t7 = $[18]; } return t7; } function CanvasProvider(t0) { const $ = (0, import_compiler_runtime31.c)(15); const { adapter, graphId, styles: styles4, isDark: t1, queryClient: t2, children, onGraphLoad, onGraphError } = t0; const isDark = t1 === void 0 ? false : t1; const queryClient = t2 === void 0 ? defaultQueryClient : t2; let t3; if ($[0] !== children || $[1] !== graphId || $[2] !== onGraphError || $[3] !== onGraphLoad) { t3 = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(GraphLoader, { graphId, onGraphLoad, onGraphError, children }); $[0] = children; $[1] = graphId; $[2] = onGraphError; $[3] = onGraphLoad; $[4] = t3; } else { t3 = $[4]; } let t4; if ($[5] !== isDark || $[6] !== styles4 || $[7] !== t3) { t4 = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CanvasStyleProvider, { styles: styles4, isDark, children: t3 }); $[5] = isDark; $[6] = styles4; $[7] = t3; $[8] = t4; } else { t4 = $[8]; } let t5; if ($[9] !== adapter || $[10] !== t4) { t5 = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CanvasDbProvider, { adapter, children: t4 }); $[9] = adapter; $[10] = t4; $[11] = t5; } else { t5 = $[11]; } let t6; if ($[12] !== queryClient || $[13] !== t5) { t6 = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jotai59.Provider, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_query9.QueryClientProvider, { client: queryClient, children: t5 }) }); $[12] = queryClient; $[13] = t5; $[14] = t6; } else { t6 = $[14]; } return t6; } // src/components/Canvas.tsx var import_compiler_runtime46 = require("react/compiler-runtime"); var import_react38 = __toESM(require("react")); // src/components/Viewport.tsx var import_compiler_runtime36 = require("react/compiler-runtime"); var import_react26 = __toESM(require("react")); var import_jotai65 = require("jotai"); init_viewport_store(); // src/gestures/useCanvasGestures.ts var import_react19 = require("react"); var import_jotai61 = require("jotai"); var import_react20 = require("@use-gesture/react"); init_viewport_store(); init_selection_store(); init_input_classifier(); init_input_store(); init_reduced_motion_store(); init_selection_path_store(); // src/gestures/normalize.ts init_input_classifier(); init_types2(); function extractModifiers(e) { if (!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) { return NO_MODIFIERS; } return { shift: e.shiftKey, ctrl: e.ctrlKey, alt: e.altKey, meta: e.metaKey }; } function clampButton(raw) { if (raw === 1) return 1; if (raw === 2) return 2; return 0; } function lifecycleFromEventType(type) { switch (type) { case "pointerdown": return "down"; case "pointerup": return "up"; case "pointercancel": return "cancel"; default: return "move"; } } function normalizePointer(e) { const classified = classifyPointer(e); return { pointerId: e.pointerId, lifecycle: lifecycleFromEventType(e.type), source: classified.source, button: clampButton(e.button), modifiers: extractModifiers(e), screenX: e.clientX, screenY: e.clientY, pressure: e.pressure, timestamp: e.timeStamp }; } // src/gestures/timed-state.ts var LONG_PRESS_TIMER = "long-press"; var SETTLE_TIMER = "settle"; var DEFAULT_LONG_PRESS_MS = 600; var DEFAULT_MULTI_TAP_WINDOW_MS = 300; var IDLE = { tag: "idle", tapCount: 0 }; var DEFAULT_CONFIG = { longPressMs: DEFAULT_LONG_PRESS_MS, multiTapWindowMs: DEFAULT_MULTI_TAP_WINDOW_MS }; function transition(state, event, config = DEFAULT_CONFIG) { switch (event) { case "down": return onDown(state, config); case "up": return onUp(state, config); case "move-beyond-threshold": return onMoveBeyond(state); case "cancel": return onCancel(); case "timer:long-press": return onLongPressTimer(state); case "timer:settle": return onSettleTimer(); default: return { state }; } } function onDown(state, config) { switch (state.tag) { case "idle": return { state: { tag: "pressed", tapCount: 0 }, cancelTimer: SETTLE_TIMER, scheduleTimer: { id: LONG_PRESS_TIMER, delayMs: config.longPressMs } }; case "released": return { state: { tag: "pressed", tapCount: state.tapCount }, cancelTimer: SETTLE_TIMER, scheduleTimer: { id: LONG_PRESS_TIMER, delayMs: config.longPressMs } }; default: return { state }; } } function onUp(state, config) { if (state.tag !== "pressed") { if (state.tag === "long-pressed") { return { state: IDLE, cancelTimer: LONG_PRESS_TIMER }; } return { state }; } const newCount = state.tapCount + 1; const emit = tapTypeForCount(newCount); return { state: { tag: "released", tapCount: newCount }, emit, cancelTimer: LONG_PRESS_TIMER, scheduleTimer: { id: SETTLE_TIMER, delayMs: config.multiTapWindowMs } }; } function onMoveBeyond(state) { if (state.tag === "pressed") { return { state: IDLE, cancelTimer: LONG_PRESS_TIMER }; } return { state }; } function onCancel() { return { state: IDLE, cancelTimer: LONG_PRESS_TIMER }; } function onLongPressTimer(state) { if (state.tag === "pressed") { return { state: { tag: "long-pressed", tapCount: 0 }, emit: "long-press" }; } return { state }; } function onSettleTimer() { return { state: IDLE }; } function tapTypeForCount(count) { switch (count) { case 2: return "double-tap"; case 3: return "triple-tap"; default: return "tap"; } } // src/gestures/timed-state-runner.ts var TimedStateRunner = class { constructor(config) { __publicField(this, "state", IDLE); __publicField(this, "timers", /* @__PURE__ */ new Map()); /** Called when the state machine emits a gesture (tap, double-tap, long-press, etc.) */ __publicField(this, "onEmit", null); this.config = { longPressMs: config?.longPressMs ?? DEFAULT_LONG_PRESS_MS, multiTapWindowMs: config?.multiTapWindowMs ?? DEFAULT_MULTI_TAP_WINDOW_MS }; } /** * Feed a lifecycle event into the state machine. * Returns the emitted gesture type if any (synchronous emit only). * Timer-based emits are delivered asynchronously via `onEmit`. */ feed(event) { return this.apply(event); } /** Current state tag (for debugging/testing). */ get tag() { return this.state.tag; } /** Destroy: cancel all pending timers. */ destroy() { for (const timer of this.timers.values()) { clearTimeout(timer); } this.timers.clear(); this.state = IDLE; this.onEmit = null; } apply(event) { const result = transition(this.state, event, this.config); this.state = result.state; if (result.cancelTimer) { const timer = this.timers.get(result.cancelTimer); if (timer !== void 0) { clearTimeout(timer); this.timers.delete(result.cancelTimer); } } if (result.scheduleTimer) { const { id, delayMs } = result.scheduleTimer; const existing = this.timers.get(id); if (existing !== void 0) { clearTimeout(existing); } this.timers.set(id, setTimeout(() => { this.timers.delete(id); const timerEvent = `timer:${id}`; const timerResult = transition(this.state, timerEvent, this.config); this.state = timerResult.state; if (timerResult.emit) { this.onEmit?.(timerResult.emit); } }, delayMs)); } return result.emit; } }; // src/gestures/specificity.ts init_types2(); var SCORE_TYPE = 128; var SCORE_KEY = 64; var SCORE_CODE = 64; var SCORE_SUBJECT = 32; var SCORE_MODIFIER_POSITIVE = 16; var SCORE_MODIFIER_NEGATIVE = 8; var SCORE_HELD_KEY_POSITIVE = 16; var SCORE_HELD_KEY_NEGATIVE = 8; var SCORE_SOURCE = 4; var SCORE_BUTTON = 2; function specificity(pattern, event) { let score = 0; if (pattern.kind !== void 0) { const actualKind = isKeyInputEvent(event) ? "key" : "pointer"; if (pattern.kind !== actualKind) return -1; } if (pattern.type !== void 0) { if (!isPointerGestureEvent(event) || pattern.type !== event.type) return -1; score += SCORE_TYPE; } if (pattern.subjectKind !== void 0) { if (event.subject === void 0 || pattern.subjectKind !== event.subject.kind) return -1; score += SCORE_SUBJECT; } if (pattern.phase !== void 0 && pattern.phase !== event.phase) { return -1; } if (pattern.source !== void 0) { if (!isPointerGestureEvent(event)) return -1; if (pattern.source !== event.source) return -1; score += SCORE_SOURCE; } if (pattern.button !== void 0) { if (!isPointerGestureEvent(event)) return -1; if (pattern.button !== event.button) return -1; score += SCORE_BUTTON; } if (pattern.key !== void 0) { if (!isKeyInputEvent(event) || pattern.key !== event.key) return -1; score += SCORE_KEY; } if (pattern.code !== void 0) { if (!isKeyInputEvent(event) || pattern.code !== event.code) return -1; score += SCORE_CODE; } if (pattern.modifiers !== void 0) { const ms = modifierScore(pattern.modifiers, event.modifiers); if (ms === -1) return -1; score += ms; } if (pattern.heldKeys !== void 0) { const hs = heldKeyScore(pattern.heldKeys, event.heldKeys); if (hs === -1) return -1; score += hs; } return score; } function modifierScore(pattern, actual) { let score = 0; const keys = ["shift", "ctrl", "alt", "meta"]; for (const key of keys) { const required = pattern[key]; if (required === void 0) continue; if (required !== actual[key]) return -1; score += required ? SCORE_MODIFIER_POSITIVE : SCORE_MODIFIER_NEGATIVE; } if (pattern.custom) { const actualCustom = actual.custom ?? {}; for (const [key, required] of Object.entries(pattern.custom)) { const actualVal = actualCustom[key] ?? false; if (required !== actualVal) return -1; score += required ? SCORE_MODIFIER_POSITIVE : SCORE_MODIFIER_NEGATIVE; } } return score; } function heldKeyScore(pattern, actual) { let score = 0; for (const [key, required] of Object.entries(pattern.byKey ?? {})) { const actualVal = actual.byKey[key] ?? false; if (required !== actualVal) return -1; score += required ? SCORE_HELD_KEY_POSITIVE : SCORE_HELD_KEY_NEGATIVE; } for (const [code, required] of Object.entries(pattern.byCode ?? {})) { const actualVal = actual.byCode[code] ?? false; if (required !== actualVal) return -1; score += required ? SCORE_HELD_KEY_POSITIVE : SCORE_HELD_KEY_NEGATIVE; } return score; } // src/gestures/mapper.ts init_types2(); var WILDCARD = "__wildcard__"; function getPatternBucketKey(binding) { const { pattern } = binding; if (pattern.type !== void 0) { return `pointer:${pattern.type}`; } if (pattern.kind === "key" || pattern.key !== void 0 || pattern.code !== void 0) { return pattern.phase === "down" || pattern.phase === "up" ? `key:${pattern.phase}` : null; } return null; } function getEventBucketKey(event) { return isKeyInputEvent(event) ? `key:${event.phase}` : `pointer:${event.type}`; } function indexContext(ctx) { const typed = /* @__PURE__ */ new Map(); const wildcards = []; for (const binding of ctx.bindings) { const key = getPatternBucketKey(binding); if (key === null) { wildcards.push(binding); } else { let bucket = typed.get(key); if (!bucket) { bucket = []; typed.set(key, bucket); } bucket.push(binding); } } const buckets = /* @__PURE__ */ new Map(); for (const [key, bucket] of typed) { buckets.set(key, bucket); } if (wildcards.length > 0) { buckets.set(WILDCARD, wildcards); } return { contextId: ctx.id, priority: ctx.priority, enabled: ctx.enabled, buckets }; } function buildMappingIndex(contexts) { return contexts.map(indexContext).sort((a, b) => a.priority - b.priority); } function resolve(event, index, guard) { let winner = null; for (const ctx of index) { if (!ctx.enabled) continue; const bucket = ctx.buckets.get(getEventBucketKey(event)); const wildcardBucket = ctx.buckets.get(WILDCARD); if (!bucket && !wildcardBucket) continue; let bestScore = -1; let bestBinding = null; for (const binding of [...bucket ?? [], ...wildcardBucket ?? []]) { const score = specificity(binding.pattern, event); if (score === -1) continue; if (binding.when && !binding.when(guard)) continue; if (score > bestScore) { bestScore = score; bestBinding = binding; } } if (bestBinding !== null) { const action = { actionId: bestBinding.actionId, binding: bestBinding, contextId: ctx.contextId, score: bestScore, consumed: bestBinding.consumeInput === true }; if (action.consumed) { return action; } if (winner === null) { winner = action; } } } return winner; } // src/gestures/useCanvasGestures.ts init_dispatcher(); // src/gestures/keyboard-contexts.ts var SEARCH_CONTEXT = { id: "search-active", priority: 25, enabled: true, bindings: [{ id: "search-next-enter", pattern: { kind: "key", phase: "down", key: "Enter", modifiers: { shift: false } }, actionId: "search-next-result", when: (ctx) => ctx.isSearchActive && !ctx.commandLineVisible, consumeInput: true }, { id: "search-prev-enter", pattern: { kind: "key", phase: "down", key: "Enter", modifiers: { shift: true } }, actionId: "search-prev-result", when: (ctx) => ctx.isSearchActive && !ctx.commandLineVisible, consumeInput: true }, { id: "search-next-ctrl-g", pattern: { kind: "key", phase: "down", key: "g", modifiers: { ctrl: true, shift: false } }, actionId: "search-next-result", when: (ctx) => ctx.isSearchActive, consumeInput: true }, { id: "search-prev-ctrl-g", pattern: { kind: "key", phase: "down", key: "g", modifiers: { ctrl: true, shift: true } }, actionId: "search-prev-result", when: (ctx) => ctx.isSearchActive, consumeInput: true }, { id: "search-next-meta-g", pattern: { kind: "key", phase: "down", key: "g", modifiers: { meta: true, shift: false } }, actionId: "search-next-result", when: (ctx) => ctx.isSearchActive, consumeInput: true }, { id: "search-prev-meta-g", pattern: { kind: "key", phase: "down", key: "g", modifiers: { meta: true, shift: true } }, actionId: "search-prev-result", when: (ctx) => ctx.isSearchActive, consumeInput: true }] }; var KEYBOARD_MANIPULATE_CONTEXT = { id: "keyboard:manipulate", priority: 30, enabled: true, bindings: [{ id: "manipulate-arrow-up", pattern: { kind: "key", phase: "down", key: "ArrowUp", modifiers: { shift: false } }, actionId: "nudge-selection-up", when: (ctx) => ctx.keyboardInteractionMode === "manipulate", consumeInput: true }, { id: "manipulate-arrow-down", pattern: { kind: "key", phase: "down", key: "ArrowDown", modifiers: { shift: false } }, actionId: "nudge-selection-down", when: (ctx) => ctx.keyboardInteractionMode === "manipulate", consumeInput: true }, { id: "manipulate-arrow-left", pattern: { kind: "key", phase: "down", key: "ArrowLeft", modifiers: { shift: false } }, actionId: "nudge-selection-left", when: (ctx) => ctx.keyboardInteractionMode === "manipulate", consumeInput: true }, { id: "manipulate-arrow-right", pattern: { kind: "key", phase: "down", key: "ArrowRight", modifiers: { shift: false } }, actionId: "nudge-selection-right", when: (ctx) => ctx.keyboardInteractionMode === "manipulate", consumeInput: true }, { id: "manipulate-shift-arrow-up", pattern: { kind: "key", phase: "down", key: "ArrowUp", modifiers: { shift: true } }, actionId: "nudge-selection-up-large", when: (ctx) => ctx.keyboardInteractionMode === "manipulate", consumeInput: true }, { id: "manipulate-shift-arrow-down", pattern: { kind: "key", phase: "down", key: "ArrowDown", modifiers: { shift: true } }, actionId: "nudge-selection-down-large", when: (ctx) => ctx.keyboardInteractionMode === "manipulate", consumeInput: true }, { id: "manipulate-shift-arrow-left", pattern: { kind: "key", phase: "down", key: "ArrowLeft", modifiers: { shift: true } }, actionId: "nudge-selection-left-large", when: (ctx) => ctx.keyboardInteractionMode === "manipulate", consumeInput: true }, { id: "manipulate-shift-arrow-right", pattern: { kind: "key", phase: "down", key: "ArrowRight", modifiers: { shift: true } }, actionId: "nudge-selection-right-large", when: (ctx) => ctx.keyboardInteractionMode === "manipulate", consumeInput: true }, { id: "manipulate-escape", pattern: { kind: "key", phase: "down", key: "Escape" }, actionId: "exit-keyboard-manipulate-mode", when: (ctx) => ctx.keyboardInteractionMode === "manipulate", consumeInput: true }] }; var KEYBOARD_NAVIGATE_CONTEXT = { id: "keyboard:navigate", priority: 40, enabled: true, bindings: [{ id: "navigate-arrow-up", pattern: { kind: "key", phase: "down", key: "ArrowUp" }, actionId: "navigate-focus-up", when: (ctx) => ctx.keyboardInteractionMode === "navigate", consumeInput: true }, { id: "navigate-arrow-down", pattern: { kind: "key", phase: "down", key: "ArrowDown" }, actionId: "navigate-focus-down", when: (ctx) => ctx.keyboardInteractionMode === "navigate", consumeInput: true }, { id: "navigate-arrow-left", pattern: { kind: "key", phase: "down", key: "ArrowLeft" }, actionId: "navigate-focus-left", when: (ctx) => ctx.keyboardInteractionMode === "navigate", consumeInput: true }, { id: "navigate-arrow-right", pattern: { kind: "key", phase: "down", key: "ArrowRight" }, actionId: "navigate-focus-right", when: (ctx) => ctx.keyboardInteractionMode === "navigate", consumeInput: true }, { id: "navigate-enter", pattern: { kind: "key", phase: "down", key: "Enter" }, actionId: "enter-keyboard-manipulate-mode", when: (ctx) => ctx.keyboardInteractionMode === "navigate", consumeInput: true }, { id: "navigate-space", pattern: { kind: "key", phase: "down", code: "Space" }, actionId: "activate-focused-node", when: (ctx) => ctx.keyboardInteractionMode === "navigate", consumeInput: true }, { id: "navigate-tab", pattern: { kind: "key", phase: "down", key: "Tab", modifiers: { shift: false } }, actionId: "cycle-focus-forward", when: (ctx) => ctx.keyboardInteractionMode === "navigate", consumeInput: true }, { id: "navigate-shift-tab", pattern: { kind: "key", phase: "down", key: "Tab", modifiers: { shift: true } }, actionId: "cycle-focus-backward", when: (ctx) => ctx.keyboardInteractionMode === "navigate", consumeInput: true }] }; // src/gestures/pointer-bindings.ts var POINTER_BINDINGS = [ // --- Pointer taps --- { id: "tap-node", pattern: { type: "tap", subjectKind: "node" }, actionId: "select-node" }, { id: "tap-edge", pattern: { type: "tap", subjectKind: "edge" }, actionId: "select-edge" }, { id: "tap-bg", pattern: { type: "tap", subjectKind: "background" }, actionId: "clear-selection" }, { id: "shift-tap-node", pattern: { type: "tap", subjectKind: "node", modifiers: { shift: true } }, actionId: "toggle-selection" }, // --- Right-click --- { id: "rc-node", pattern: { type: "tap", subjectKind: "node", button: 2 }, actionId: "context-menu", consumeInput: true }, { id: "rc-bg", pattern: { type: "tap", subjectKind: "background", button: 2 }, actionId: "context-menu", consumeInput: true }, // --- Double/triple tap --- { id: "dtap-node", pattern: { type: "double-tap", subjectKind: "node" }, actionId: "fit-to-view" }, { id: "ttap-node", pattern: { type: "triple-tap", subjectKind: "node" }, actionId: "toggle-lock" }, // --- Drags --- { id: "drag-node", pattern: { type: "drag", subjectKind: "node" }, actionId: "move-node" }, { id: "drag-bg-finger", pattern: { type: "drag", subjectKind: "background", source: "finger" }, actionId: "pan" }, { id: "drag-bg-mouse", pattern: { type: "drag", subjectKind: "background", source: "mouse" }, actionId: "pan" }, { id: "drag-bg-pencil", pattern: { type: "drag", subjectKind: "background", source: "pencil" }, actionId: "lasso-select" }, { id: "space-drag-pan", pattern: { type: "drag", heldKeys: { byCode: { Space: true } } }, actionId: "pan" }, { id: "shift-drag-bg", pattern: { type: "drag", subjectKind: "background", modifiers: { shift: true } }, actionId: "rect-select" }, // Right/middle drag — no-ops by default { id: "rdrag-node", pattern: { type: "drag", subjectKind: "node", button: 2 }, actionId: "none" }, { id: "rdrag-bg", pattern: { type: "drag", subjectKind: "background", button: 2 }, actionId: "none" }, // --- Long-press --- { id: "lp-node", pattern: { type: "long-press", subjectKind: "node" }, actionId: "context-menu" }, { id: "lp-bg-finger", pattern: { type: "long-press", subjectKind: "background", source: "finger" }, actionId: "create-node" }, // --- Pinch / scroll --- { id: "pinch-bg", pattern: { type: "pinch", subjectKind: "background" }, actionId: "zoom" }, { id: "scroll-any", pattern: { type: "scroll" }, actionId: "zoom" }, { id: "pinch-node", pattern: { type: "pinch", subjectKind: "node" }, actionId: "split-node" } ]; // src/gestures/keyboard-bindings.ts var isCommandShortcut = (ctx) => !ctx.commandLineVisible; var KEYBOARD_BINDINGS = [ { id: "slash-open-command-line", pattern: { kind: "key", phase: "down", code: "Slash", modifiers: { ctrl: false, meta: false, shift: false } }, actionId: "open-command-line", when: isCommandShortcut, consumeInput: true }, { id: "escape-fallback", pattern: { kind: "key", phase: "down", key: "Escape" }, actionId: "escape-input", when: isCommandShortcut, consumeInput: true }, { id: "delete-selection", pattern: { kind: "key", phase: "down", key: "Delete" }, actionId: "delete-selection", when: isCommandShortcut, consumeInput: true }, { id: "backspace-delete-selection", pattern: { kind: "key", phase: "down", key: "Backspace", modifiers: { ctrl: false, meta: false } }, actionId: "delete-selection", when: isCommandShortcut, consumeInput: true }, // Search { id: "open-search-ctrl-f", pattern: { kind: "key", phase: "down", key: "f", modifiers: { ctrl: true, shift: false } }, actionId: "open-search", consumeInput: true }, { id: "open-search-meta-f", pattern: { kind: "key", phase: "down", key: "f", modifiers: { meta: true, shift: false } }, actionId: "open-search", consumeInput: true }, // Copy / Cut / Paste { id: "copy-selection-ctrl-c", pattern: { kind: "key", phase: "down", key: "c", modifiers: { ctrl: true, shift: false } }, actionId: "copy-selection", when: isCommandShortcut, consumeInput: true }, { id: "copy-selection-meta-c", pattern: { kind: "key", phase: "down", key: "c", modifiers: { meta: true, shift: false } }, actionId: "copy-selection", when: isCommandShortcut, consumeInput: true }, { id: "cut-selection-ctrl-x", pattern: { kind: "key", phase: "down", key: "x", modifiers: { ctrl: true, shift: false } }, actionId: "cut-selection", when: isCommandShortcut, consumeInput: true }, { id: "cut-selection-meta-x", pattern: { kind: "key", phase: "down", key: "x", modifiers: { meta: true, shift: false } }, actionId: "cut-selection", when: isCommandShortcut, consumeInput: true }, { id: "paste-selection-ctrl-v", pattern: { kind: "key", phase: "down", key: "v", modifiers: { ctrl: true, shift: false } }, actionId: "paste-selection", when: isCommandShortcut, consumeInput: true }, { id: "paste-selection-meta-v", pattern: { kind: "key", phase: "down", key: "v", modifiers: { meta: true, shift: false } }, actionId: "paste-selection", when: isCommandShortcut, consumeInput: true }, // Duplicate { id: "duplicate-selection-ctrl-d", pattern: { kind: "key", phase: "down", key: "d", modifiers: { ctrl: true, shift: false } }, actionId: "duplicate-selection", when: isCommandShortcut, consumeInput: true }, { id: "duplicate-selection-meta-d", pattern: { kind: "key", phase: "down", key: "d", modifiers: { meta: true, shift: false } }, actionId: "duplicate-selection", when: isCommandShortcut, consumeInput: true }, // Select All { id: "select-all-ctrl-a", pattern: { kind: "key", phase: "down", key: "a", modifiers: { ctrl: true, shift: false } }, actionId: "select-all", when: isCommandShortcut, consumeInput: true }, { id: "select-all-meta-a", pattern: { kind: "key", phase: "down", key: "a", modifiers: { meta: true, shift: false } }, actionId: "select-all", when: isCommandShortcut, consumeInput: true }, // Merge { id: "merge-selection-ctrl-m", pattern: { kind: "key", phase: "down", key: "m", modifiers: { ctrl: true, shift: false } }, actionId: "merge-selection", when: isCommandShortcut, consumeInput: true }, { id: "merge-selection-meta-m", pattern: { kind: "key", phase: "down", key: "m", modifiers: { meta: true, shift: false } }, actionId: "merge-selection", when: isCommandShortcut, consumeInput: true }, // Undo / Redo { id: "undo-ctrl-z", pattern: { kind: "key", phase: "down", key: "z", modifiers: { ctrl: true, shift: false } }, actionId: "undo", when: isCommandShortcut, consumeInput: true }, { id: "undo-meta-z", pattern: { kind: "key", phase: "down", key: "z", modifiers: { meta: true, shift: false } }, actionId: "undo", when: isCommandShortcut, consumeInput: true }, { id: "redo-ctrl-shift-z", pattern: { kind: "key", phase: "down", key: "z", modifiers: { ctrl: true, shift: true } }, actionId: "redo", when: isCommandShortcut, consumeInput: true }, { id: "redo-meta-shift-z", pattern: { kind: "key", phase: "down", key: "z", modifiers: { meta: true, shift: true } }, actionId: "redo", when: isCommandShortcut, consumeInput: true }, { id: "redo-ctrl-y", pattern: { kind: "key", phase: "down", key: "y", modifiers: { ctrl: true } }, actionId: "redo", when: isCommandShortcut, consumeInput: true }, { id: "redo-meta-y", pattern: { kind: "key", phase: "down", key: "y", modifiers: { meta: true } }, actionId: "redo", when: isCommandShortcut, consumeInput: true } ]; // src/gestures/pointer-contexts.ts var DEFAULT_CONTEXT = { id: "default", priority: 100, enabled: true, bindings: [...POINTER_BINDINGS, ...KEYBOARD_BINDINGS] }; var PICK_NODE_CONTEXT = { id: "input-mode:pickNode", priority: 5, enabled: true, bindings: [{ id: "pick-tap-node", pattern: { type: "tap", subjectKind: "node" }, actionId: "resolve-pick-node", consumeInput: true }, { id: "pick-cancel-bg", pattern: { type: "tap", subjectKind: "background" }, actionId: "cancel-pick", consumeInput: true }, { id: "pick-cancel-key", pattern: { kind: "key", phase: "down", key: "Escape" }, actionId: "cancel-pick", consumeInput: true }] }; var PICK_NODES_CONTEXT = { id: "input-mode:pickNodes", priority: 5, enabled: true, bindings: [{ id: "pick-tap-node", pattern: { type: "tap", subjectKind: "node" }, actionId: "resolve-pick-node", consumeInput: true }, { id: "pick-done", pattern: { type: "double-tap", subjectKind: "background" }, actionId: "finish-pick-nodes", consumeInput: true }, { id: "pick-cancel-bg", pattern: { type: "tap", subjectKind: "background", button: 2 }, actionId: "cancel-pick", consumeInput: true }, { id: "pick-cancel-key", pattern: { kind: "key", phase: "down", key: "Escape" }, actionId: "cancel-pick", consumeInput: true }] }; var PICK_POINT_CONTEXT = { id: "input-mode:pickPoint", priority: 5, enabled: true, bindings: [{ id: "pick-tap", pattern: { type: "tap" }, actionId: "resolve-pick-point", consumeInput: true }, { id: "pick-cancel-key", pattern: { kind: "key", phase: "down", key: "Escape" }, actionId: "cancel-pick", consumeInput: true }] }; var INPUT_MODE_CONTEXTS = { normal: null, pickNode: PICK_NODE_CONTEXT, pickNodes: PICK_NODES_CONTEXT, pickPoint: PICK_POINT_CONTEXT, text: null, select: null }; // src/gestures/contexts.ts var PALM_REJECTION_CONTEXT = { id: "palm-rejection", priority: 0, enabled: true, bindings: [{ id: "pr-finger-tap", pattern: { type: "tap", source: "finger" }, actionId: "none", when: (ctx) => ctx.isStylusActive, consumeInput: true }, { id: "pr-finger-dtap", pattern: { type: "double-tap", source: "finger" }, actionId: "none", when: (ctx) => ctx.isStylusActive, consumeInput: true }, { id: "pr-finger-ttap", pattern: { type: "triple-tap", source: "finger" }, actionId: "none", when: (ctx) => ctx.isStylusActive, consumeInput: true }, { id: "pr-finger-lp", pattern: { type: "long-press", source: "finger" }, actionId: "none", when: (ctx) => ctx.isStylusActive, consumeInput: true }, { id: "pr-finger-drag-node", pattern: { type: "drag", subjectKind: "node", source: "finger" }, actionId: "pan", when: (ctx) => ctx.isStylusActive, consumeInput: true }] }; var ACTIVE_INTERACTION_CONTEXT = { id: "active-interaction", priority: 15, enabled: true, bindings: [{ id: "escape-cancel-active-interaction", pattern: { kind: "key", phase: "down", key: "Escape" }, actionId: "cancel-active-input", when: (ctx) => ctx.isDragging || ctx.isSplitting || Boolean(ctx.custom.isSelecting) || Boolean(ctx.custom.isCreatingEdge), consumeInput: true }] }; // src/gestures/useGuardContext.ts var import_react17 = require("react"); var import_jotai60 = require("jotai"); init_selection_store(); init_graph_store(); init_input_store(); init_interaction_store(); init_selection_path_store(); init_search_store(); init_types2(); function useGuardContext(heldKeys = NO_HELD_KEYS) { const isStylusActive = (0, import_jotai60.useAtomValue)(isStylusActiveAtom); const fingerCount = (0, import_jotai60.useAtomValue)(fingerCountAtom); const draggingNodeId = (0, import_jotai60.useAtomValue)(draggingNodeIdAtom); const focusedNodeId = (0, import_jotai60.useAtomValue)(focusedNodeIdAtom); const selectedNodeIds = (0, import_jotai60.useAtomValue)(selectedNodeIdsAtom); const inputMode = (0, import_jotai60.useAtomValue)(inputModeAtom); const keyboardInteractionMode = (0, import_jotai60.useAtomValue)(keyboardInteractionModeAtom); const selectionPath = (0, import_jotai60.useAtomValue)(selectionPathAtom); const edgeCreation = (0, import_jotai60.useAtomValue)(edgeCreationAtom); const isSearchActive = (0, import_jotai60.useAtomValue)(isFilterActiveAtom); const commandLineVisible = (0, import_jotai60.useAtomValue)(commandLineVisibleAtom); const guardRef = (0, import_react17.useRef)({ isStylusActive: false, fingerCount: 0, isDragging: false, isResizing: false, isSplitting: false, inputMode: { type: "normal" }, keyboardInteractionMode: "navigate", selectedNodeIds: /* @__PURE__ */ new Set(), focusedNodeId: null, isSearchActive: false, commandLineVisible: false, heldKeys: NO_HELD_KEYS, custom: {} }); guardRef.current = { isStylusActive, fingerCount, isDragging: draggingNodeId !== null, isResizing: false, isSplitting: false, inputMode, keyboardInteractionMode, selectedNodeIds, focusedNodeId, isSearchActive, commandLineVisible, heldKeys, custom: { isSelecting: selectionPath !== null, isCreatingEdge: edgeCreation.isCreating } }; return guardRef; } // src/gestures/useInertia.ts var import_react18 = require("react"); // src/gestures/inertia.ts var PAN_FRICTION = 0.92; var ZOOM_FRICTION = 0.88; var MIN_VELOCITY = 0.5; var ZOOM_SNAP_THRESHOLD = 0.03; var VELOCITY_SAMPLE_COUNT = 5; var VelocitySampler = class { constructor(maxSamples = VELOCITY_SAMPLE_COUNT) { __publicField(this, "samples", []); this.maxSamples = maxSamples; } sample(vx, vy, t) { if (this.samples.length >= this.maxSamples) { this.samples.shift(); } this.samples.push({ vx, vy, t }); } average() { const n = this.samples.length; if (n === 0) return { x: 0, y: 0 }; let sx = 0; let sy = 0; for (const s of this.samples) { sx += s.vx; sy += s.vy; } return { x: sx / n, y: sy / n }; } reset() { this.samples.length = 0; } }; var PanInertia = class { constructor(velocity, friction = PAN_FRICTION, minVelocity = MIN_VELOCITY) { this.friction = friction; this.minVelocity = minVelocity; this.vx = velocity.x; this.vy = velocity.y; } /** * Advance one frame. Returns the velocity delta to apply, * or null if below threshold (animation complete). */ tick() { this.vx *= this.friction; this.vy *= this.friction; if (Math.abs(this.vx) < this.minVelocity && Math.abs(this.vy) < this.minVelocity) { return null; } return { x: this.vx, y: this.vy }; } }; var ZoomInertia = class { constructor(velocity, origin, friction = ZOOM_FRICTION, minVelocity = MIN_VELOCITY, snapThreshold = ZOOM_SNAP_THRESHOLD) { this.origin = origin; this.friction = friction; this.minVelocity = minVelocity; this.snapThreshold = snapThreshold; this.v = velocity; } /** * Advance one frame. Returns zoom delta + origin, * or null if below threshold. */ tick(currentZoom) { this.v *= this.friction; if (Math.abs(this.v) < this.minVelocity) { if (Math.abs(currentZoom - 1) < this.snapThreshold) { const snapDelta = 1 - currentZoom; return Math.abs(snapDelta) > 1e-3 ? { delta: snapDelta, origin: this.origin } : null; } return null; } return { delta: this.v, origin: this.origin }; } }; // src/gestures/useInertia.ts var ZOOM_SNAP_THRESHOLD2 = 0.03; var INERTIA_MIN_SPEED = 2; var VELOCITY_SAMPLE_COUNT2 = 5; function snapZoom(z) { return Math.abs(z - 1) < ZOOM_SNAP_THRESHOLD2 ? 1 : z; } function useInertia() { const panInertiaRef = (0, import_react18.useRef)(null); const zoomInertiaRef = (0, import_react18.useRef)(null); const panSamplerRef = (0, import_react18.useRef)(new VelocitySampler(VELOCITY_SAMPLE_COUNT2)); const zoomSamplerRef = (0, import_react18.useRef)(new VelocitySampler(VELOCITY_SAMPLE_COUNT2)); const pinchPrevOrigin = (0, import_react18.useRef)(null); const lastPinchZoom = (0, import_react18.useRef)(null); const cancelPan = () => { if (panInertiaRef.current) { cancelAnimationFrame(panInertiaRef.current.anim); panInertiaRef.current = null; } }; const cancelZoom = () => { if (zoomInertiaRef.current) { cancelAnimationFrame(zoomInertiaRef.current.anim); zoomInertiaRef.current = null; } }; const cancelAll = () => { cancelPan(); cancelZoom(); }; const startPanInertia = (velocity, setPan) => { cancelPan(); const engine = new PanInertia(velocity); const animate = () => { const tick = engine.tick(); if (!tick) { panInertiaRef.current = null; return; } setPan((prev) => ({ x: prev.x + tick.x, y: prev.y + tick.y })); panInertiaRef.current.anim = requestAnimationFrame(animate); }; panInertiaRef.current = { anim: requestAnimationFrame(animate), engine }; }; const startZoomInertia = (velocity_0, origin, currentZoom, minZoom, maxZoom, setZoom, setPan_0) => { cancelZoom(); const engine_0 = new ZoomInertia(velocity_0, origin); const animate_0 = () => { const tick_0 = engine_0.tick(currentZoom); if (!tick_0) { zoomInertiaRef.current = null; return; } setZoom((prevZoom) => { const newZoom = Math.max(minZoom, Math.min(maxZoom, prevZoom + tick_0.delta)); setPan_0((prevPan) => { const worldX = (tick_0.origin.x - prevPan.x) / prevZoom; const worldY = (tick_0.origin.y - prevPan.y) / prevZoom; return { x: tick_0.origin.x - worldX * newZoom, y: tick_0.origin.y - worldY * newZoom }; }); return snapZoom(newZoom); }); zoomInertiaRef.current.anim = requestAnimationFrame(animate_0); }; zoomInertiaRef.current = { anim: requestAnimationFrame(animate_0), engine: engine_0 }; }; (0, import_react18.useEffect)(() => cancelAll, []); return { startPanInertia, startZoomInertia, cancelAll, cancelPan, cancelZoom, panSampler: panSamplerRef.current, zoomSampler: zoomSamplerRef.current, pinchPrevOrigin, lastPinchZoom }; } // src/gestures/useWheelZoom.ts function createWheelHandler(config) { return ({ event, pinching, delta: [, dy], memo }) => { if (memo === true || pinching || !config.enableZoom || !config.ref.current) return; const target = event.target; const noDrag = target.closest('[data-no-drag="true"]'); if (noDrag) { const draggableNode = target.closest('[data-draggable-node="true"]'); if (draggableNode) { const nodeId = draggableNode.getAttribute("data-node-id"); if (nodeId && config.selectedNodeIds.has(nodeId)) { let el = target; while (el && noDrag.contains(el)) { if (el.scrollHeight > el.clientHeight) { const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1; const atTop = el.scrollTop <= 1; if (!atBottom && dy > 0 || !atTop && dy < 0 || atBottom && dy < 0 || atTop && dy > 0) { return; } } el = el.parentElement; } } } } event.preventDefault(); const rect = config.ref.current.getBoundingClientRect(); const mouseX = event.clientX - rect.left; const mouseY = event.clientY - rect.top; const worldX = (mouseX - config.pan.x) / config.zoom; const worldY = (mouseY - config.pan.y) / config.zoom; const zoomDelta = -dy * config.zoomSensitivity * config.zoom; const newZoom = snapZoom(Math.max(config.minZoom, Math.min(config.maxZoom, config.zoom + zoomDelta))); config.setZoom(newZoom); config.setPan({ x: mouseX - worldX * newZoom, y: mouseY - worldY * newZoom }); }; } // src/gestures/usePinchZoom.ts function createPinchHandlers(config) { const onPinchStart = ({ origin: [ox, oy] }) => { if (!config.enableZoom || !config.ref.current) return true; config.inertia.cancelAll(); config.inertia.pinchPrevOrigin.current = { x: ox, y: oy }; config.inertia.zoomSampler.reset(); config.inertia.lastPinchZoom.current = { zoom: config.zoom, t: performance.now() }; return false; }; const onPinch = ({ offset: [d], origin: [ox, oy], event, memo }) => { if (memo === true || !config.enableZoom || !config.ref.current) return; event.preventDefault(); const rect = config.ref.current.getBoundingClientRect(); const pinchX = ox - rect.left; const pinchY = oy - rect.top; let pdx = 0; let pdy = 0; if (config.inertia.pinchPrevOrigin.current) { pdx = ox - config.inertia.pinchPrevOrigin.current.x; pdy = oy - config.inertia.pinchPrevOrigin.current.y; } config.inertia.pinchPrevOrigin.current = { x: ox, y: oy }; const worldX = (pinchX - config.pan.x) / config.zoom; const worldY = (pinchY - config.pan.y) / config.zoom; const newZoom = snapZoom(Math.max(config.minZoom, Math.min(config.maxZoom, d))); const now = performance.now(); if (config.inertia.lastPinchZoom.current) { const dt = (now - config.inertia.lastPinchZoom.current.t) / 1e3; if (dt > 0) { const v = (newZoom - config.inertia.lastPinchZoom.current.zoom) / dt; config.inertia.zoomSampler.sample(v, 0, now); } } config.inertia.lastPinchZoom.current = { zoom: newZoom, t: now }; config.setZoom(newZoom); config.setPan({ x: pinchX - worldX * newZoom + pdx, y: pinchY - worldY * newZoom + pdy }); }; const onPinchEnd = () => { const avg = config.inertia.zoomSampler.average(); const perFrameV = avg.x / 60; if (!config.reducedMotion && Math.abs(perFrameV) > 1e-3 && config.ref.current) { const rect = config.ref.current.getBoundingClientRect(); const origin = config.inertia.pinchPrevOrigin.current ?? { x: rect.width / 2, y: rect.height / 2 }; config.inertia.startZoomInertia(perFrameV, { x: origin.x - rect.left, y: origin.y - rect.top }, config.zoom, config.minZoom, config.maxZoom, config.setZoom, config.setPan); } config.inertia.pinchPrevOrigin.current = null; config.inertia.zoomSampler.reset(); config.inertia.lastPinchZoom.current = null; }; return { onPinchStart, onPinch, onPinchEnd }; } // src/gestures/useCanvasGestures.ts init_types2(); function useCanvasGestures({ ref, minZoom = 0.1, maxZoom = 5, zoomSensitivity = 15e-4, enablePan = true, enableZoom = true, mappingIndex: externalIndex, contexts: extraContexts, palmRejection = true, onAction, heldKeys = NO_HELD_KEYS }) { const [panVals, setPan] = (0, import_jotai61.useAtom)(panAtom); const [zoomVal, setZoom] = (0, import_jotai61.useAtom)(zoomAtom); const setViewportRect = (0, import_jotai61.useSetAtom)(viewportRectAtom); const selectedNodeIds = (0, import_jotai61.useAtomValue)(selectedNodeIdsAtom); const clearSelection = (0, import_jotai61.useSetAtom)(clearSelectionAtom); const screenToWorld = (0, import_jotai61.useAtomValue)(screenToWorldAtom); const setPointerDown = (0, import_jotai61.useSetAtom)(pointerDownAtom); const setPointerUp = (0, import_jotai61.useSetAtom)(pointerUpAtom); const clearPointers = (0, import_jotai61.useSetAtom)(clearPointersAtom); const primaryInputSource = (0, import_jotai61.useAtomValue)(primaryInputSourceAtom); const reducedMotion = (0, import_jotai61.useAtomValue)(prefersReducedMotionAtom); const startSelection = (0, import_jotai61.useSetAtom)(startSelectionAtom); const updateSelection = (0, import_jotai61.useSetAtom)(updateSelectionAtom); const endSelection = (0, import_jotai61.useSetAtom)(endSelectionAtom); const guardRef = useGuardContext(heldKeys); const inertia = useInertia(); const panStartRef = (0, import_react19.useRef)({ x: 0, y: 0 }); const dragOriginatedOnBg = (0, import_react19.useRef)(false); const dragSourceRef = (0, import_react19.useRef)("mouse"); const buttonRef = (0, import_react19.useRef)(0); const dragIntentRef = (0, import_react19.useRef)("none"); const timedRunner = (0, import_react19.useRef)(new TimedStateRunner()); const internalIndex = (0, import_react19.useMemo)(() => { const ctxList = []; if (palmRejection) ctxList.push(PALM_REJECTION_CONTEXT); if (extraContexts) ctxList.push(...extraContexts); ctxList.push(DEFAULT_CONTEXT); return buildMappingIndex(ctxList); }, [palmRejection, extraContexts]); const mappingIndex = externalIndex ?? internalIndex; const isBackgroundTarget = (target) => { if (!ref.current || !target) return false; const child = ref.current.firstChild; return target === ref.current || target === child; }; const makeGestureEvent = (type, phase, subject, screenX, screenY, extra) => { const worldPos = screenToWorld(screenX, screenY); return { kind: "pointer", type, phase, subject, source: dragSourceRef.current, button: buttonRef.current, modifiers: NO_MODIFIERS, heldKeys, screenPosition: { x: screenX, y: screenY }, worldPosition: worldPos, ...extra }; }; const resolveAndDispatch = (event) => { const resolution = resolve(event, mappingIndex, guardRef.current); if (resolution) { dispatch(event, resolution); onAction?.(event, resolution); } return resolution; }; (0, import_react19.useEffect)(() => { const runner = timedRunner.current; runner.onEmit = (_gestureType) => { }; return () => { runner.destroy(); }; }, []); (0, import_react19.useEffect)(() => { const el = ref.current; if (!el) { setViewportRect(null); return; } setViewportRect(el.getBoundingClientRect()); const observer = new ResizeObserver((entries) => { for (const entry of entries) { setViewportRect(entry.contentRect); } }); observer.observe(el); return () => { observer.unobserve(el); observer.disconnect(); }; }, [setViewportRect]); (0, import_react19.useEffect)(() => { return () => { inertia.cancelAll(); timedRunner.current.destroy(); }; }, []); (0, import_react19.useEffect)(() => { const handleVisChange = () => { if (document.hidden) { inertia.cancelAll(); clearPointers(); timedRunner.current.feed("cancel"); } }; document.addEventListener("visibilitychange", handleVisChange); return () => document.removeEventListener("visibilitychange", handleVisChange); }, [clearPointers]); (0, import_react20.useGesture)({ onPointerDown: (state) => { if (!ref.current) { dragOriginatedOnBg.current = false; return; } const pe = state.event; const classified = classifyPointer(pe); setPointerDown(classified); dragSourceRef.current = classified.source; buttonRef.current = pe.button ?? 0; inertia.cancelAll(); if (isBackgroundTarget(state.event.target)) { dragOriginatedOnBg.current = true; timedRunner.current.feed("down"); const startX = pe.clientX; const startY = pe.clientY; timedRunner.current.onEmit = (gestureType) => { if (gestureType === "long-press") { const worldPos_0 = screenToWorld(startX, startY); const event_0 = makeGestureEvent("long-press", "instant", { kind: "background" }, startX, startY, { modifiers: extractModifiers(pe), worldPosition: worldPos_0 }); resolveAndDispatch(event_0); } }; } else { dragOriginatedOnBg.current = false; } }, onPointerUp: (state_0) => { const pe_0 = state_0.event; setPointerUp(pe_0.pointerId); }, onDragStart: ({ event: event_1 }) => { if (!dragOriginatedOnBg.current) return; const pe_1 = event_1; const modifiers = pe_1 ? extractModifiers(pe_1) : NO_MODIFIERS; const gestureEvent = makeGestureEvent("drag", "start", { kind: "background" }, pe_1?.clientX ?? 0, pe_1?.clientY ?? 0, { modifiers }); const resolution_0 = resolve(gestureEvent, mappingIndex, guardRef.current); dragIntentRef.current = resolution_0?.actionId ?? "none"; if (dragIntentRef.current === "pan" && enablePan) { panStartRef.current = { ...panVals }; inertia.panSampler.reset(); } else if (dragIntentRef.current === "lasso-select" || dragIntentRef.current === "rect-select") { const worldPos_1 = screenToWorld(pe_1?.clientX ?? 0, pe_1?.clientY ?? 0); startSelection({ type: dragIntentRef.current === "lasso-select" ? "lasso" : "rect", point: worldPos_1 }); } if (resolution_0) { dispatch(gestureEvent, resolution_0); onAction?.(gestureEvent, resolution_0); } }, onDrag: ({ movement: [mx, my], tap, active, pinching, event: event_2, velocity: [vx, vy], direction: [dx, dy] }) => { if (tap && dragOriginatedOnBg.current) { const emitted = timedRunner.current.feed("up"); const pe_2 = event_2; const modifiers_0 = extractModifiers(pe_2); const tapType = emitted ?? "tap"; const gestureEvent_0 = makeGestureEvent(tapType, "instant", { kind: "background" }, pe_2.clientX, pe_2.clientY, { modifiers: modifiers_0 }); const resolution_1 = resolve(gestureEvent_0, mappingIndex, guardRef.current); if (resolution_1) { dispatch(gestureEvent_0, resolution_1); onAction?.(gestureEvent_0, resolution_1); } else { clearSelection(); } return; } if (!tap && active && !pinching && dragOriginatedOnBg.current) { timedRunner.current.feed("move-beyond-threshold"); const intent = dragIntentRef.current; if (intent === "pan" && enablePan) { setPan({ x: panStartRef.current.x + mx, y: panStartRef.current.y + my }); const now = performance.now(); inertia.panSampler.sample(vx * dx, vy * dy, now); } else if (intent === "lasso-select" || intent === "rect-select") { const pe_3 = event_2; const worldPos_2 = screenToWorld(pe_3.clientX, pe_3.clientY); updateSelection(worldPos_2); } } }, onDragEnd: () => { timedRunner.current.feed("up"); const intent_0 = dragIntentRef.current; if (intent_0 === "lasso-select" || intent_0 === "rect-select") { endSelection(); } if (!reducedMotion && dragOriginatedOnBg.current && dragSourceRef.current === "finger" && intent_0 === "pan") { const avg = inertia.panSampler.average(); const speed = Math.sqrt(avg.x ** 2 + avg.y ** 2); if (speed > INERTIA_MIN_SPEED) { inertia.startPanInertia(avg, setPan); } } dragOriginatedOnBg.current = false; dragIntentRef.current = "none"; inertia.panSampler.reset(); }, onWheel: createWheelHandler({ ref, minZoom, maxZoom, zoomSensitivity, enableZoom, zoom: zoomVal, pan: panVals, selectedNodeIds, setZoom, setPan }), ...createPinchHandlers({ ref, minZoom, maxZoom, enableZoom, reducedMotion, zoom: zoomVal, pan: panVals, setZoom, setPan, inertia }) }, { target: ref, eventOptions: { passive: false, capture: true }, drag: { filterTaps: true, tapsThreshold: primaryInputSource === "finger" ? 10 : 5, pointer: { touch: true, keys: false, capture: false, buttons: -1 } }, wheel: {}, pinch: { scaleBounds: () => ({ min: minZoom, max: maxZoom }), from: () => [zoomVal, 0] } }); } // src/gestures/GestureProvider.tsx var import_react24 = __toESM(require("react")); var import_jotai64 = require("jotai"); init_dispatcher(); // src/gestures/useInputModeGestureContext.ts var import_compiler_runtime32 = require("react/compiler-runtime"); var import_react21 = require("react"); var import_jotai62 = require("jotai"); init_interaction_store(); function useInputModeGestureContext(inputSystem) { const $ = (0, import_compiler_runtime32.c)(4); const inputMode = (0, import_jotai62.useAtomValue)(inputModeAtom); let t0; let t1; if ($[0] !== inputMode.type || $[1] !== inputSystem) { t0 = () => { const modeType = inputMode.type; const ctx = INPUT_MODE_CONTEXTS[modeType] ?? null; if (ctx) { inputSystem.pushContext(ctx); } else { for (const key of Object.keys(INPUT_MODE_CONTEXTS)) { const existing = INPUT_MODE_CONTEXTS[key]; if (existing) { inputSystem.removeContext(existing.id); } } } return () => { if (ctx) { inputSystem.removeContext(ctx.id); } }; }; t1 = [inputMode.type, inputSystem]; $[0] = inputMode.type; $[1] = inputSystem; $[2] = t0; $[3] = t1; } else { t0 = $[2]; t1 = $[3]; } (0, import_react21.useEffect)(t0, t1); } // src/gestures/useRegisterInputActions.ts var import_compiler_runtime33 = require("react/compiler-runtime"); var import_react22 = require("react"); var import_jotai63 = require("jotai"); init_core(); init_search_store(); init_dispatcher(); // src/gestures/input-action-helpers.ts init_core(); init_search_store(); init_interaction_store(); init_selection_path_store(); // src/gestures/modifier-helpers.ts init_core(); init_types2(); function isRepeatBlocked(event) { return isKeyInputEvent(event) && event.repeat; } function getSelectedNodeIds(store) { return Array.from(store.get(selectedNodeIdsAtom)); } function clearSelectionState(store) { store.set(clearSelectionAtom); store.set(clearEdgeSelectionAtom); } function resolveFocusableNodeId(store) { const focusedNodeId = store.get(focusedNodeIdAtom); if (focusedNodeId) return focusedNodeId; const selected = getSelectedNodeIds(store); return selected[0] ?? null; } function getCurrentSubject(store) { const draggingNodeId = store.get(draggingNodeIdAtom); if (draggingNodeId) return { kind: "node", nodeId: draggingNodeId }; const edgeCreation = store.get(edgeCreationAtom); if (edgeCreation.isCreating && edgeCreation.sourceNodeId) { return { kind: "node", nodeId: edgeCreation.sourceNodeId }; } const focusedNodeId = resolveFocusableNodeId(store); if (focusedNodeId) return { kind: "node", nodeId: focusedNodeId }; const selectedEdgeId = store.get(selectedEdgeIdAtom); if (selectedEdgeId) return { kind: "edge", edgeId: selectedEdgeId }; return { kind: "background" }; } function updateKeySubject(event, store) { if (event.kind === "key" && event.subject === void 0) { event.subject = getCurrentSubject(store); } } // src/gestures/gesture-classification.ts init_core(); function findNearestNode(currentNodeId, store, direction) { const nodes = store.get(uiNodesAtom); if (nodes.length === 0) return null; if (!currentNodeId) { const sorted = [...nodes].sort((a, b) => a.position.y - b.position.y || a.position.x - b.position.x); return sorted[0]?.id ?? null; } const currentNode = nodes.find((node) => node.id === currentNodeId); if (!currentNode) return nodes[0]?.id ?? null; const cx = currentNode.position.x + (currentNode.width ?? 200) / 2; const cy = currentNode.position.y + (currentNode.height ?? 100) / 2; let bestId = null; let bestScore = Number.POSITIVE_INFINITY; for (const candidate of nodes) { if (candidate.id === currentNode.id) continue; const nx = candidate.position.x + (candidate.width ?? 200) / 2; const ny = candidate.position.y + (candidate.height ?? 100) / 2; const dx = nx - cx; const dy = ny - cy; let isValid = false; switch (direction) { case "right": isValid = dx > 0; break; case "left": isValid = dx < 0; break; case "down": isValid = dy > 0; break; case "up": isValid = dy < 0; break; } if (!isValid) continue; const dist = Math.sqrt(dx * dx + dy * dy); const isHorizontal = direction === "left" || direction === "right"; const perpendicularDistance = isHorizontal ? Math.abs(dy) : Math.abs(dx); const score = dist + perpendicularDistance * 0.5; if (score < bestScore) { bestScore = score; bestId = candidate.id; } } return bestId; } function cycleFocus(store, direction) { const nodes = store.get(uiNodesAtom); if (nodes.length === 0) return; const focusedNodeId = store.get(focusedNodeIdAtom); const sorted = [...nodes].sort((a, b) => a.zIndex - b.zIndex); const currentIdx = focusedNodeId ? sorted.findIndex((node) => node.id === focusedNodeId) : -1; const nextIdx = direction === -1 ? currentIdx <= 0 ? sorted.length - 1 : currentIdx - 1 : (currentIdx + 1) % sorted.length; store.set(setFocusedNodeAtom, sorted[nextIdx].id); } function navigateFocus(store, direction) { const nextId = findNearestNode(resolveFocusableNodeId(store), store, direction); if (nextId) { store.set(setFocusedNodeAtom, nextId); } } function activateFocusedNode(store, enterManipulate) { const focusedNodeId = resolveFocusableNodeId(store); if (!focusedNodeId) return; store.set(setFocusedNodeAtom, focusedNodeId); store.set(selectSingleNodeAtom, focusedNodeId); if (enterManipulate) { store.set(setKeyboardInteractionModeAtom, "manipulate"); } } // src/gestures/input-action-helpers.ts var EMPTY_EDGE_CREATION = { isCreating: false, sourceNodeId: null, sourceNodePosition: null, targetPosition: null, hoveredTargetNodeId: null, sourceHandle: null, targetHandle: null, sourcePort: null, targetPort: null, snappedTargetPosition: null }; function nudgeSelection(store, dx, dy, label) { const selected = getSelectedNodeIds(store); if (selected.length === 0) { const focused = store.get(focusedNodeIdAtom); if (focused) { store.set(selectSingleNodeAtom, focused); } } const nodeIds = getSelectedNodeIds(store); if (nodeIds.length === 0) return; const graph = store.get(graphAtom).copy(); store.set(pushHistoryAtom, label); for (const nodeId of nodeIds) { if (!graph.hasNode(nodeId)) continue; const x = graph.getNodeAttribute(nodeId, "x"); const y = graph.getNodeAttribute(nodeId, "y"); graph.setNodeAttribute(nodeId, "x", x + dx); graph.setNodeAttribute(nodeId, "y", y + dy); } store.set(graphAtom, graph); store.set(nodePositionUpdateCounterAtom, (count) => count + 1); } function deleteSelection(store) { const selectedEdgeId = store.get(selectedEdgeIdAtom); if (selectedEdgeId) { store.set(optimisticDeleteEdgeAtom, { edgeKey: selectedEdgeId }); store.set(clearEdgeSelectionAtom); } const selectedNodeIds = getSelectedNodeIds(store); if (selectedNodeIds.length === 0) return; store.set(pushHistoryAtom, selectedNodeIds.length > 1 ? `Delete ${selectedNodeIds.length} nodes` : "Delete node"); const focusedNodeId = store.get(focusedNodeIdAtom); for (const nodeId of selectedNodeIds) { store.set(optimisticDeleteNodeAtom, { nodeId }); } if (focusedNodeId && selectedNodeIds.includes(focusedNodeId)) { store.set(setFocusedNodeAtom, null); } store.set(clearSelectionAtom); } function cutSelection(store) { const selectedNodeIds = getSelectedNodeIds(store); if (selectedNodeIds.length === 0) return; store.set(copyToClipboardAtom, selectedNodeIds); deleteSelection(store); } function cancelActiveInteraction(store) { let didCancel = false; if (store.get(draggingNodeIdAtom) !== null) { store.set(endNodeDragAtom); didCancel = true; } if (store.get(edgeCreationAtom).isCreating) { store.set(edgeCreationAtom, EMPTY_EDGE_CREATION); didCancel = true; } if (store.get(inputModeAtom).type === "pickPoint" || store.get(inputModeAtom).type === "pickNode" || store.get(inputModeAtom).type === "pickNodes") { store.set(resetInputModeAtom); didCancel = true; } const selectionPath = store.get(selectionPathAtom); if (selectionPath !== null) { store.set(cancelSelectionAtom); didCancel = true; } return didCancel; } function escapeInput(store) { if (cancelActiveInteraction(store)) return; if (store.get(keyboardInteractionModeAtom) === "manipulate") { store.set(resetKeyboardInteractionModeAtom); return; } if (store.get(isFilterActiveAtom)) { store.set(clearSearchAtom); return; } if (store.get(commandLineVisibleAtom)) { store.set(closeCommandLineAtom); return; } if (store.get(selectedNodeIdsAtom).size > 0 || store.get(selectedEdgeIdAtom) !== null) { clearSelectionState(store); } } function resolvePickNode(event, store) { updateKeySubject(event, store); if (event.subject?.kind !== "node") return; if (store.get(inputModeAtom).type === "pickNodes") { store.set(toggleNodeInSelectionAtom, event.subject.nodeId); store.set(setFocusedNodeAtom, event.subject.nodeId); return; } store.set(provideInputAtom, event.subject.nodeId); store.set(resetInputModeAtom); } function finishPickNodes(store) { store.set(provideInputAtom, getSelectedNodeIds(store)); store.set(resetInputModeAtom); } function resolvePickPoint(event, store) { if (event.kind !== "pointer") return; store.set(provideInputAtom, event.worldPosition); store.set(resetInputModeAtom); } function selectAll(store) { clearSelectionState(store); store.set(addNodesToSelectionAtom, store.get(nodeKeysAtom)); } // src/gestures/useRegisterInputActions.ts function register(actionId, handler) { registerAction2(actionId, handler); return () => unregisterAction2(actionId); } function useRegisterInputActions() { const $ = (0, import_compiler_runtime33.c)(3); const store = (0, import_jotai63.useStore)(); let t0; let t1; if ($[0] !== store) { t0 = () => { const unregister = [register("select-node", (event) => { updateKeySubject(event, store); if (event.subject?.kind !== "node") { return; } store.set(setFocusedNodeAtom, event.subject.nodeId); if (event.kind === "key") { store.set(selectSingleNodeAtom, event.subject.nodeId); } }), register("select-edge", (event_0) => { updateKeySubject(event_0, store); if (event_0.subject?.kind !== "edge") { return; } store.set(selectEdgeAtom, event_0.subject.edgeId); }), register("toggle-selection", (event_1) => { updateKeySubject(event_1, store); if (event_1.subject?.kind !== "node") { return; } store.set(setFocusedNodeAtom, event_1.subject.nodeId); if (event_1.kind === "key") { store.set(toggleNodeInSelectionAtom, event_1.subject.nodeId); } }), register("clear-selection", () => { clearSelectionState(store); }), register("select-all", (event_2) => { if (isRepeatBlocked(event_2)) { return; } selectAll(store); }), register("fit-to-view", (event_3) => { updateKeySubject(event_3, store); if (event_3.subject?.kind === "node") { store.set(centerOnNodeAtom, event_3.subject.nodeId); } }), register("fit-all-to-view", () => { store.set(fitToBoundsAtom, { mode: "graph" }); }), register("toggle-lock", (event_4) => { updateKeySubject(event_4, store); if (event_4.subject?.kind !== "node") { return; } if (store.get(lockedNodeIdAtom) === event_4.subject.nodeId) { store.set(unlockNodeAtom); } else { store.set(lockNodeAtom, { nodeId: event_4.subject.nodeId }); } }), register("open-command-line", (event_5) => { if (isRepeatBlocked(event_5)) { return; } store.set(openCommandLineAtom); }), register("open-search", (event_6) => { if (isRepeatBlocked(event_6)) { return; } store.set(openCommandLineAtom); }), register("search-next-result", () => { store.set(nextSearchResultAtom); }), register("search-prev-result", () => { store.set(prevSearchResultAtom); }), register("copy-selection", (event_7) => { if (isRepeatBlocked(event_7)) { return; } const selectedNodeIds = getSelectedNodeIds(store); if (selectedNodeIds.length > 0) { store.set(copyToClipboardAtom, selectedNodeIds); } }), register("cut-selection", (event_8) => { if (isRepeatBlocked(event_8)) { return; } cutSelection(store); }), register("paste-selection", (event_9) => { if (isRepeatBlocked(event_9)) { return; } store.set(pasteFromClipboardAtom); }), register("duplicate-selection", (event_10) => { if (isRepeatBlocked(event_10)) { return; } store.set(duplicateSelectionAtom); }), register("merge-selection", (event_11) => { if (isRepeatBlocked(event_11)) { return; } const nodeIds = getSelectedNodeIds(store); if (nodeIds.length >= 2) { store.set(mergeNodesAtom, { nodeIds }); } }), register("delete-selection", (event_12) => { if (isRepeatBlocked(event_12)) { return; } deleteSelection(store); }), register("undo", (event_13) => { if (isRepeatBlocked(event_13)) { return; } store.set(undoAtom); }), register("redo", (event_14) => { if (isRepeatBlocked(event_14)) { return; } store.set(redoAtom); }), register("cancel-active-input", () => { cancelActiveInteraction(store); }), register("escape-input", () => { escapeInput(store); }), register("navigate-focus-up", () => { navigateFocus(store, "up"); }), register("navigate-focus-down", () => { navigateFocus(store, "down"); }), register("navigate-focus-left", () => { navigateFocus(store, "left"); }), register("navigate-focus-right", () => { navigateFocus(store, "right"); }), register("cycle-focus-forward", (event_15) => { if (isRepeatBlocked(event_15)) { return; } cycleFocus(store, 1); }), register("cycle-focus-backward", (event_16) => { if (isRepeatBlocked(event_16)) { return; } cycleFocus(store, -1); }), register("activate-focused-node", (event_17) => { if (isRepeatBlocked(event_17)) { return; } activateFocusedNode(store, false); }), register("enter-keyboard-manipulate-mode", (event_18) => { if (isRepeatBlocked(event_18)) { return; } activateFocusedNode(store, true); }), register("exit-keyboard-manipulate-mode", () => { store.set(resetKeyboardInteractionModeAtom); }), register("nudge-selection-up", () => { nudgeSelection(store, 0, -10, "Nudge selection"); }), register("nudge-selection-down", () => { nudgeSelection(store, 0, 10, "Nudge selection"); }), register("nudge-selection-left", () => { nudgeSelection(store, -10, 0, "Nudge selection"); }), register("nudge-selection-right", () => { nudgeSelection(store, 10, 0, "Nudge selection"); }), register("nudge-selection-up-large", () => { nudgeSelection(store, 0, -50, "Nudge selection"); }), register("nudge-selection-down-large", () => { nudgeSelection(store, 0, 50, "Nudge selection"); }), register("nudge-selection-left-large", () => { nudgeSelection(store, -50, 0, "Nudge selection"); }), register("nudge-selection-right-large", () => { nudgeSelection(store, 50, 0, "Nudge selection"); }), register("resolve-pick-node", (event_19) => { resolvePickNode(event_19, store); }), register("finish-pick-nodes", () => { finishPickNodes(store); }), register("resolve-pick-point", (event_20) => { resolvePickPoint(event_20, store); }), register("cancel-pick", () => { store.set(resetInputModeAtom); })]; return () => { for (const cleanup of unregister) { cleanup(); } }; }; t1 = [store]; $[0] = store; $[1] = t0; $[2] = t1; } else { t0 = $[1]; t1 = $[2]; } (0, import_react22.useEffect)(t0, t1); } // src/gestures/useGestureSystem.ts var import_compiler_runtime34 = require("react/compiler-runtime"); var import_react23 = require("react"); init_types2(); function useInputSystem(t0) { const $ = (0, import_compiler_runtime34.c)(19); const config = t0 === void 0 ? {} : t0; const { contexts: staticContexts, palmRejection: t1 } = config; const initialPR = t1 === void 0 ? true : t1; let t2; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t2 = []; $[0] = t2; } else { t2 = $[0]; } const [dynamicContexts, setDynamicContexts] = (0, import_react23.useState)(t2); const [palmRejection, setPalmRejection] = (0, import_react23.useState)(initialPR); const [heldKeys, setHeldKeys] = (0, import_react23.useState)(NO_HELD_KEYS); const all = []; if (palmRejection) { all.push(PALM_REJECTION_CONTEXT); } all.push(ACTIVE_INTERACTION_CONTEXT, SEARCH_CONTEXT, KEYBOARD_MANIPULATE_CONTEXT, KEYBOARD_NAVIGATE_CONTEXT); if (staticContexts) { all.push(...staticContexts); } all.push(...dynamicContexts); all.push(DEFAULT_CONTEXT); const mappingIndex = buildMappingIndex(all); let t3; if ($[1] !== setDynamicContexts) { t3 = (ctx) => { setDynamicContexts((prev) => { const filtered = prev.filter((c) => c.id !== ctx.id); return [...filtered, ctx]; }); }; $[1] = setDynamicContexts; $[2] = t3; } else { t3 = $[2]; } const pushContext = t3; let t4; if ($[3] !== setDynamicContexts) { t4 = (id) => { setDynamicContexts((prev_0) => prev_0.filter((c_0) => c_0.id !== id)); }; $[3] = setDynamicContexts; $[4] = t4; } else { t4 = $[4]; } const removeContext = t4; let t5; if ($[5] !== setDynamicContexts) { t5 = (id_0, enabled) => { setDynamicContexts((prev_1) => prev_1.map((c_1) => c_1.id === id_0 ? { ...c_1, enabled } : c_1)); }; $[5] = setDynamicContexts; $[6] = t5; } else { t5 = $[6]; } const setContextEnabled = t5; let t6; if ($[7] !== setHeldKeys) { t6 = () => { setHeldKeys(NO_HELD_KEYS); }; $[7] = setHeldKeys; $[8] = t6; } else { t6 = $[8]; } const clearHeldKeys = t6; let t7; if ($[9] !== clearHeldKeys || $[10] !== heldKeys || $[11] !== mappingIndex || $[12] !== palmRejection || $[13] !== pushContext || $[14] !== removeContext || $[15] !== setContextEnabled || $[16] !== setHeldKeys || $[17] !== setPalmRejection) { t7 = { mappingIndex, pushContext, removeContext, setContextEnabled, palmRejection, setPalmRejection, heldKeys, setHeldKeys, clearHeldKeys }; $[9] = clearHeldKeys; $[10] = heldKeys; $[11] = mappingIndex; $[12] = palmRejection; $[13] = pushContext; $[14] = removeContext; $[15] = setContextEnabled; $[16] = setHeldKeys; $[17] = setPalmRejection; $[18] = t7; } else { t7 = $[18]; } return t7; } var useGestureSystem = useInputSystem; // src/gestures/gesture-provider-utils.ts init_core(); init_search_store(); init_selection_path_store(); function isEditableTarget(target) { if (!(target instanceof HTMLElement)) { return false; } if (target.isContentEditable) { return true; } const editable = target.closest('input, textarea, [contenteditable="true"], [data-no-canvas-keyboard="true"]'); return editable !== null; } function setHeldKeyValue(current, key, isHeld) { const previous = current[key] ?? false; if (previous === isHeld) { return current; } const next = { ...current }; if (isHeld) { next[key] = true; } else { delete next[key]; } return next; } function applyHeldKeyDelta(event, current) { const isHeld = event.type === "keydown"; const nextByKey = setHeldKeyValue(current.byKey, event.key, isHeld); const nextByCode = setHeldKeyValue(current.byCode, event.code, isHeld); if (nextByKey === current.byKey && nextByCode === current.byCode) { return current; } return { byKey: nextByKey, byCode: nextByCode }; } function getCurrentSubject2(store) { const draggingNodeId = store.get(draggingNodeIdAtom); if (draggingNodeId) { return { kind: "node", nodeId: draggingNodeId }; } const edgeCreation = store.get(edgeCreationAtom); if (edgeCreation.isCreating && edgeCreation.sourceNodeId) { return { kind: "node", nodeId: edgeCreation.sourceNodeId }; } const focusedNodeId = store.get(focusedNodeIdAtom); if (focusedNodeId) { return { kind: "node", nodeId: focusedNodeId }; } return { kind: "background" }; } function getSubjectPosition(store, root, subject) { const worldToScreen = store.get(worldToScreenAtom); if (subject.kind === "node") { const node = store.get(uiNodesAtom).find((entry) => entry.id === subject.nodeId); if (!node) { return {}; } const worldPosition = { x: node.position.x + (node.width ?? 200) / 2, y: node.position.y + (node.height ?? 100) / 2 }; return { worldPosition, screenPosition: worldToScreen(worldPosition.x, worldPosition.y) }; } if (subject.kind === "background" && root) { const rect = root.getBoundingClientRect(); const screenPosition = { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }; return { screenPosition, worldPosition: store.get(screenToWorldAtom)(screenPosition.x, screenPosition.y) }; } return {}; } function buildGuardContext(store, system) { const heldKeys = "heldKeys" in system ? system.heldKeys : system; const edgeCreation = store.get(edgeCreationAtom); return { isStylusActive: store.get(isStylusActiveAtom), fingerCount: store.get(fingerCountAtom), isDragging: store.get(draggingNodeIdAtom) !== null, isResizing: false, isSplitting: false, inputMode: store.get(inputModeAtom), keyboardInteractionMode: store.get(keyboardInteractionModeAtom), selectedNodeIds: store.get(selectedNodeIdsAtom), focusedNodeId: store.get(focusedNodeIdAtom), isSearchActive: store.get(isFilterActiveAtom), commandLineVisible: store.get(commandLineVisibleAtom), heldKeys, custom: { isSelecting: store.get(selectionPathAtom) !== null, isCreatingEdge: edgeCreation.isCreating } }; } // src/gestures/GestureProvider.tsx var import_jsx_runtime6 = require("react/jsx-runtime"); var nextOwnerId = 1; var activeOwnerId = null; var InputContext = /* @__PURE__ */ (0, import_react24.createContext)(null); function InputProvider({ children, gestureConfig, onAction }) { const store = (0, import_jotai64.useStore)(); const system = useInputSystem({ contexts: gestureConfig?.contexts, palmRejection: gestureConfig?.palmRejection ?? true }); useInputModeGestureContext(system); useRegisterInputActions(); const [ownerId] = (0, import_react24.useState)(() => nextOwnerId++); const [canvasRoot, setCanvasRoot] = (0, import_react24.useState)(null); const rootRef = (0, import_react24.useRef)(null); const systemRef = (0, import_react24.useRef)(system); systemRef.current = system; const registerCanvasRoot = (node) => { rootRef.current = node; setCanvasRoot(node); }; (0, import_react24.useEffect)(() => { const root = canvasRoot; if (!root) { return; } const activate = (event) => { activeOwnerId = ownerId; if (!isEditableTarget(event.target)) { root.focus({ preventScroll: true }); } }; root.addEventListener("pointerdown", activate, true); root.addEventListener("focusin", activate, true); return () => { root.removeEventListener("pointerdown", activate, true); root.removeEventListener("focusin", activate, true); if (activeOwnerId === ownerId) { activeOwnerId = null; } }; }, [canvasRoot, ownerId]); (0, import_react24.useEffect)(() => { const handleKeyboard = (nativeEvent) => { if (activeOwnerId !== ownerId) { return; } const root_0 = rootRef.current; if (!root_0 || !root_0.isConnected) { return; } const currentSystem = systemRef.current; const editableTarget = isEditableTarget(nativeEvent.target); const shouldTrackHeldKey = nativeEvent.type === "keyup" || !nativeEvent.isComposing && !editableTarget; const nextHeldKeys = shouldTrackHeldKey ? applyHeldKeyDelta(nativeEvent, currentSystem.heldKeys) : currentSystem.heldKeys; if (nextHeldKeys !== currentSystem.heldKeys) { currentSystem.setHeldKeys(nextHeldKeys); } if (nativeEvent.isComposing || editableTarget) { return; } const subject = getCurrentSubject2(store); const { screenPosition, worldPosition } = getSubjectPosition(store, root_0, subject); const inputEvent = { kind: "key", phase: nativeEvent.type === "keydown" ? "down" : "up", key: nativeEvent.key, code: nativeEvent.code, repeat: nativeEvent.repeat, modifiers: { shift: nativeEvent.shiftKey, ctrl: nativeEvent.ctrlKey, alt: nativeEvent.altKey, meta: nativeEvent.metaKey }, heldKeys: nextHeldKeys, subject, screenPosition, worldPosition, originalEvent: nativeEvent }; const resolution = resolve(inputEvent, currentSystem.mappingIndex, buildGuardContext(store, { heldKeys: nextHeldKeys })); if (!resolution) { return; } nativeEvent.preventDefault(); dispatch(inputEvent, resolution); onAction?.(inputEvent, resolution); }; const clearHeldKeys = () => { systemRef.current.clearHeldKeys(); }; window.addEventListener("keydown", handleKeyboard, true); window.addEventListener("keyup", handleKeyboard, true); window.addEventListener("blur", clearHeldKeys); document.addEventListener("visibilitychange", clearHeldKeys); return () => { window.removeEventListener("keydown", handleKeyboard, true); window.removeEventListener("keyup", handleKeyboard, true); window.removeEventListener("blur", clearHeldKeys); document.removeEventListener("visibilitychange", clearHeldKeys); }; }, [onAction, ownerId, store]); return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(InputContext.Provider, { value: { system, onAction, registerCanvasRoot }, children }); } function useInputContext() { const ctx = (0, import_react24.useContext)(InputContext); if (!ctx) { throw new Error("useInputContext must be used within an InputProvider"); } return ctx; } var GestureProvider = InputProvider; var useGestureContext = useInputContext; // src/components/Grid.tsx var import_compiler_runtime35 = require("react/compiler-runtime"); var import_react25 = __toESM(require("react")); var import_jsx_runtime7 = require("react/jsx-runtime"); function Grid(t0) { const $ = (0, import_compiler_runtime35.c)(19); const { pan, zoom, viewportWidth, viewportHeight, gridSize: t1, majorGridSize: t2 } = t0; const gridSize = t1 === void 0 ? 50 : t1; const majorGridSize = t2 === void 0 ? 250 : t2; const styles4 = useCanvasStyle(); if (!styles4.grid.visible || !viewportWidth || !viewportHeight || zoom <= 0) { return null; } let gridOpacity; let lines; if ($[0] !== gridSize || $[1] !== majorGridSize || $[2] !== pan.x || $[3] !== pan.y || $[4] !== styles4.grid.majorLineColor || $[5] !== styles4.grid.minorLineColor || $[6] !== styles4.grid.opacity || $[7] !== viewportHeight || $[8] !== viewportWidth || $[9] !== zoom) { lines = []; const minorLineColor = styles4.grid.minorLineColor; const majorLineColor = styles4.grid.majorLineColor; gridOpacity = styles4.grid.opacity; const worldLeft = -pan.x / zoom; const worldTop = -pan.y / zoom; const worldRight = (-pan.x + viewportWidth) / zoom; const worldBottom = (-pan.y + viewportHeight) / zoom; const firstVerticalLine = Math.ceil(worldLeft / gridSize) * gridSize; for (let x = firstVerticalLine; x <= worldRight; x = x + gridSize, x) { if (x % majorGridSize !== 0) { lines.push(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: x, y1: worldTop, x2: x, y2: worldBottom, stroke: minorLineColor, strokeWidth: 1 / zoom }, `v-${x}`)); } } const firstHorizontalLine = Math.ceil(worldTop / gridSize) * gridSize; for (let y = firstHorizontalLine; y <= worldBottom; y = y + gridSize, y) { if (y % majorGridSize !== 0) { lines.push(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: worldLeft, y1: y, x2: worldRight, y2: y, stroke: minorLineColor, strokeWidth: 1 / zoom }, `h-${y}`)); } } const firstMajorVerticalLine = Math.ceil(worldLeft / majorGridSize) * majorGridSize; for (let x_0 = firstMajorVerticalLine; x_0 <= worldRight; x_0 = x_0 + majorGridSize, x_0) { lines.push(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: x_0, y1: worldTop, x2: x_0, y2: worldBottom, stroke: majorLineColor, strokeWidth: 1.5 / zoom }, `major-v-${x_0}`)); } const firstMajorHorizontalLine = Math.ceil(worldTop / majorGridSize) * majorGridSize; for (let y_0 = firstMajorHorizontalLine; y_0 <= worldBottom; y_0 = y_0 + majorGridSize, y_0) { lines.push(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: worldLeft, y1: y_0, x2: worldRight, y2: y_0, stroke: majorLineColor, strokeWidth: 1.5 / zoom }, `major-h-${y_0}`)); } $[0] = gridSize; $[1] = majorGridSize; $[2] = pan.x; $[3] = pan.y; $[4] = styles4.grid.majorLineColor; $[5] = styles4.grid.minorLineColor; $[6] = styles4.grid.opacity; $[7] = viewportHeight; $[8] = viewportWidth; $[9] = zoom; $[10] = gridOpacity; $[11] = lines; } else { gridOpacity = $[10]; lines = $[11]; } let t3; if ($[12] !== gridOpacity) { t3 = { position: "absolute", top: 0, left: 0, overflow: "visible", pointerEvents: "none", opacity: gridOpacity }; $[12] = gridOpacity; $[13] = t3; } else { t3 = $[13]; } let t4; if ($[14] !== lines) { t4 = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("g", { children: lines }); $[14] = lines; $[15] = t4; } else { t4 = $[15]; } let t5; if ($[16] !== t3 || $[17] !== t4) { t5 = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { width: "100%", height: "100%", style: t3, children: t4 }); $[16] = t3; $[17] = t4; $[18] = t5; } else { t5 = $[18]; } return t5; } function Crosshairs(t0) { const $ = (0, import_compiler_runtime35.c)(31); const { pan, zoom, viewportWidth, viewportHeight, tickSize: t1, labelSize: t2 } = t0; const tickSize = t1 === void 0 ? 100 : t1; const labelSize = t2 === void 0 ? 12 : t2; const styles4 = useCanvasStyle(); if (!styles4.axes.visible || !viewportWidth || !viewportHeight || zoom <= 0) { return null; } let t3; if ($[0] !== tickSize || $[1] !== zoom) { t3 = () => { const minWorldSpacing = 80 / zoom; const niceNumbers = [50, 100, 250, 500, 1e3, 2500, 5e3, 1e4]; for (const n of niceNumbers) { if (n >= minWorldSpacing) { return Math.max(n, tickSize); } } return 1e4; }; $[0] = tickSize; $[1] = zoom; $[2] = t3; } else { t3 = $[2]; } const getAdjustedTickSize = t3; const adjustedTickSize = getAdjustedTickSize(); let elements; if ($[3] !== adjustedTickSize || $[4] !== labelSize || $[5] !== pan.x || $[6] !== pan.y || $[7] !== styles4.axes.color || $[8] !== styles4.axes.labelColor || $[9] !== viewportHeight || $[10] !== viewportWidth || $[11] !== zoom) { elements = []; const strokeWidth = 1.5 / zoom; const tickLength = 5 / zoom; const labelOffset = 8 / zoom; const finalLabelSize = labelSize / zoom; const axisColor = styles4.axes.color; const labelColor = styles4.axes.labelColor; const worldLeft = -pan.x / zoom; const worldTop = -pan.y / zoom; const worldRight = (-pan.x + viewportWidth) / zoom; const worldBottom = (-pan.y + viewportHeight) / zoom; if (worldTop <= 0 && worldBottom >= 0) { let t42; if ($[13] !== axisColor || $[14] !== strokeWidth || $[15] !== worldLeft || $[16] !== worldRight) { t42 = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: worldLeft, y1: 0, x2: worldRight, y2: 0, stroke: axisColor, strokeWidth }, "x-axis"); $[13] = axisColor; $[14] = strokeWidth; $[15] = worldLeft; $[16] = worldRight; $[17] = t42; } else { t42 = $[17]; } elements.push(t42); const firstXTick = Math.ceil(worldLeft / adjustedTickSize) * adjustedTickSize; for (let x = firstXTick; x <= worldRight; x = x + adjustedTickSize, x) { if (x === 0 && worldLeft <= 0 && worldRight >= 0) { continue; } elements.push(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: x, y1: -tickLength, x2: x, y2: tickLength, stroke: axisColor, strokeWidth: strokeWidth / 1.5 }, `x-tick-${x}`)); elements.push(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x, y: tickLength + labelOffset, fill: labelColor, fontSize: finalLabelSize, textAnchor: "middle", dominantBaseline: "hanging", children: x }, `x-label-${x}`)); } } if (worldLeft <= 0 && worldRight >= 0) { let t42; if ($[18] !== axisColor || $[19] !== strokeWidth || $[20] !== worldBottom || $[21] !== worldTop) { t42 = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: 0, y1: worldTop, x2: 0, y2: worldBottom, stroke: axisColor, strokeWidth }, "y-axis"); $[18] = axisColor; $[19] = strokeWidth; $[20] = worldBottom; $[21] = worldTop; $[22] = t42; } else { t42 = $[22]; } elements.push(t42); const firstYTick = Math.ceil(worldTop / adjustedTickSize) * adjustedTickSize; for (let y = firstYTick; y <= worldBottom; y = y + adjustedTickSize, y) { if (y === 0 && worldTop <= 0 && worldBottom >= 0) { continue; } elements.push(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: -tickLength, y1: y, x2: tickLength, y2: y, stroke: axisColor, strokeWidth: strokeWidth / 1.5 }, `y-tick-${y}`)); elements.push(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: tickLength + labelOffset, y, fill: labelColor, fontSize: finalLabelSize, textAnchor: "start", dominantBaseline: "middle", children: y }, `y-label-${y}`)); } } if (worldLeft <= 0 && worldRight >= 0 && worldTop <= 0 && worldBottom >= 0) { const t42 = -labelOffset; const t52 = finalLabelSize * 1.1; let t6; if ($[23] !== labelColor || $[24] !== labelOffset || $[25] !== t42 || $[26] !== t52) { t6 = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: labelOffset, y: t42, fill: labelColor, fontSize: t52, textAnchor: "start", dominantBaseline: "alphabetic", children: "(0,0)" }, "origin-label"); $[23] = labelColor; $[24] = labelOffset; $[25] = t42; $[26] = t52; $[27] = t6; } else { t6 = $[27]; } elements.push(t6); } $[3] = adjustedTickSize; $[4] = labelSize; $[5] = pan.x; $[6] = pan.y; $[7] = styles4.axes.color; $[8] = styles4.axes.labelColor; $[9] = viewportHeight; $[10] = viewportWidth; $[11] = zoom; $[12] = elements; } else { elements = $[12]; } let t4; if ($[28] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t4 = { position: "absolute", top: 0, left: 0, overflow: "visible", pointerEvents: "none" }; $[28] = t4; } else { t4 = $[28]; } let t5; if ($[29] !== elements) { t5 = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { width: "100%", height: "100%", style: t4, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("g", { children: elements }) }); $[29] = elements; $[30] = t5; } else { t5 = $[30]; } return t5; } // src/components/Viewport.tsx var import_jsx_runtime8 = require("react/jsx-runtime"); function Viewport(t0) { const $ = (0, import_compiler_runtime36.c)(28); const { children, minZoom: t1, maxZoom: t2, zoomSensitivity: t3, className, style, enablePan: t4, enableZoom: t5 } = t0; const minZoom = t1 === void 0 ? 0.1 : t1; const maxZoom = t2 === void 0 ? 5 : t2; const zoomSensitivity = t3 === void 0 ? 15e-4 : t3; const enablePan = t4 === void 0 ? true : t4; const enableZoom = t5 === void 0 ? true : t5; const panVals = (0, import_jotai65.useAtomValue)(panAtom); const zoomVal = (0, import_jotai65.useAtomValue)(zoomAtom); const viewportRect = (0, import_jotai65.useAtomValue)(viewportRectAtom); const viewportRef = (0, import_react26.useRef)(null); const { system, onAction, registerCanvasRoot } = useInputContext(); let t6; let t7; if ($[0] !== registerCanvasRoot) { t6 = () => { registerCanvasRoot(viewportRef.current); return () => registerCanvasRoot(null); }; t7 = [registerCanvasRoot]; $[0] = registerCanvasRoot; $[1] = t6; $[2] = t7; } else { t6 = $[1]; t7 = $[2]; } (0, import_react26.useEffect)(t6, t7); let t8; if ($[3] !== enablePan || $[4] !== enableZoom || $[5] !== maxZoom || $[6] !== minZoom || $[7] !== onAction || $[8] !== system.heldKeys || $[9] !== system.mappingIndex || $[10] !== zoomSensitivity) { t8 = { ref: viewportRef, minZoom, maxZoom, zoomSensitivity, enablePan, enableZoom, mappingIndex: system.mappingIndex, onAction, heldKeys: system.heldKeys }; $[3] = enablePan; $[4] = enableZoom; $[5] = maxZoom; $[6] = minZoom; $[7] = onAction; $[8] = system.heldKeys; $[9] = system.mappingIndex; $[10] = zoomSensitivity; $[11] = t8; } else { t8 = $[11]; } useCanvasGestures(t8); let t9; if ($[12] !== style) { t9 = { width: "100%", height: "100%", overflow: "hidden", position: "relative", touchAction: "none", userSelect: "none", WebkitUserSelect: "none", WebkitTouchCallout: "none", background: "var(--canvas-bg, #23272a)", ...style }; $[12] = style; $[13] = t9; } else { t9 = $[13]; } const t10 = `translate(${panVals.x}px, ${panVals.y}px) scale(${zoomVal})`; let t11; if ($[14] !== t10) { t11 = { transform: t10, transformOrigin: "0 0", width: "100%", height: "100%" }; $[14] = t10; $[15] = t11; } else { t11 = $[15]; } let t12; if ($[16] !== panVals || $[17] !== viewportRect || $[18] !== zoomVal) { t12 = viewportRect && viewportRect.width > 0 && viewportRect.height > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Grid, { pan: panVals, zoom: zoomVal, viewportWidth: viewportRect.width, viewportHeight: viewportRect.height }), /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Crosshairs, { pan: panVals, zoom: zoomVal, viewportWidth: viewportRect.width, viewportHeight: viewportRect.height })] }); $[16] = panVals; $[17] = viewportRect; $[18] = zoomVal; $[19] = t12; } else { t12 = $[19]; } let t13; if ($[20] !== children || $[21] !== t11 || $[22] !== t12) { t13 = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: t11, children: [t12, children] }); $[20] = children; $[21] = t11; $[22] = t12; $[23] = t13; } else { t13 = $[23]; } let t14; if ($[24] !== className || $[25] !== t13 || $[26] !== t9) { t14 = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { ref: viewportRef, id: "viewport-canvas", className, "data-viewport": "true", "data-canvas-root": "true", tabIndex: -1, style: t9, children: t13 }); $[24] = className; $[25] = t13; $[26] = t9; $[27] = t14; } else { t14 = $[27]; } return t14; } // src/components/NodeRenderer.tsx var import_compiler_runtime39 = require("react/compiler-runtime"); var import_react31 = __toESM(require("react")); var import_jotai68 = require("jotai"); init_graph_position(); init_graph_derived(); init_virtualization_store(); init_node_type_registry(); // src/components/Node.tsx var import_react29 = __toESM(require("react")); var import_jotai67 = require("jotai"); init_graph_store(); init_graph_position(); init_graph_mutations(); init_selection_store(); init_search_store(); // src/components/ResizeHandle.tsx var import_compiler_runtime37 = require("react/compiler-runtime"); var import_react27 = __toESM(require("react")); var import_jsx_runtime9 = require("react/jsx-runtime"); var HIT_AREA_SIZE = 44; var VISUAL_SIZE = 12; var VISUAL_RADIUS = "8px"; function getHitAreaStyles(direction) { const base = { position: "absolute", width: `${HIT_AREA_SIZE}px`, height: `${HIT_AREA_SIZE}px`, zIndex: 10, touchAction: "none", // Prevent iOS callout/magnifier on long-press WebkitTouchCallout: "none", WebkitUserSelect: "none", userSelect: "none" }; const halfHit = HIT_AREA_SIZE / 2; switch (direction) { case "nw": return { ...base, top: -halfHit, left: -halfHit, cursor: "nwse-resize" }; case "ne": return { ...base, top: -halfHit, right: -halfHit, cursor: "nesw-resize" }; case "sw": return { ...base, bottom: -halfHit, left: -halfHit, cursor: "nesw-resize" }; case "se": return { ...base, bottom: -halfHit, right: -halfHit, cursor: "nwse-resize" }; case "n": return { ...base, top: -halfHit, left: "50%", transform: "translateX(-50%)", cursor: "ns-resize" }; case "s": return { ...base, bottom: -halfHit, left: "50%", transform: "translateX(-50%)", cursor: "ns-resize" }; case "e": return { ...base, right: -halfHit, top: "50%", transform: "translateY(-50%)", cursor: "ew-resize" }; case "w": return { ...base, left: -halfHit, top: "50%", transform: "translateY(-50%)", cursor: "ew-resize" }; } } function getVisualStyles(direction, isResizing) { const activeColor = "var(--node-resize-handle-active, rgba(59, 130, 246, 0.8))"; const inactiveColor = "var(--node-resize-handle, rgba(0, 0, 0, 0.2))"; const borderWidth = isResizing ? "4px" : "3px"; const borderColor = isResizing ? activeColor : inactiveColor; const borderStyle = `${borderWidth} solid ${borderColor}`; const base = { position: "absolute", pointerEvents: "none", // Center the visual indicator within the hit area top: "50%", left: "50%", transform: "translate(-50%, -50%)", width: `${VISUAL_SIZE}px`, height: `${VISUAL_SIZE}px` }; switch (direction) { case "nw": return { ...base, borderTop: borderStyle, borderLeft: borderStyle, borderTopLeftRadius: VISUAL_RADIUS }; case "ne": return { ...base, borderTop: borderStyle, borderRight: borderStyle, borderTopRightRadius: VISUAL_RADIUS }; case "sw": return { ...base, borderBottom: borderStyle, borderLeft: borderStyle, borderBottomLeftRadius: VISUAL_RADIUS }; case "se": return { ...base, borderBottom: borderStyle, borderRight: borderStyle, borderBottomRightRadius: VISUAL_RADIUS }; case "n": return { ...base, borderTop: borderStyle, width: `${VISUAL_SIZE * 2}px` }; case "s": return { ...base, borderBottom: borderStyle, width: `${VISUAL_SIZE * 2}px` }; case "e": return { ...base, borderRight: borderStyle, height: `${VISUAL_SIZE * 2}px` }; case "w": return { ...base, borderLeft: borderStyle, height: `${VISUAL_SIZE * 2}px` }; } } function ResizeHandle(t0) { const $ = (0, import_compiler_runtime37.c)(26); const { direction, createResizeStart, handleResizeMove, handleResizeEnd, isResizing, alwaysVisible: t1 } = t0; const alwaysVisible = t1 === void 0 ? false : t1; let t2; if ($[0] !== createResizeStart || $[1] !== direction) { t2 = createResizeStart(direction); $[0] = createResizeStart; $[1] = direction; $[2] = t2; } else { t2 = $[2]; } let t3; if ($[3] !== direction) { t3 = getHitAreaStyles(direction); $[3] = direction; $[4] = t3; } else { t3 = $[4]; } const t4 = alwaysVisible || isResizing ? 1 : 0; let t5; if ($[5] !== t3 || $[6] !== t4) { t5 = { ...t3, opacity: t4, transition: "opacity 200ms" }; $[5] = t3; $[6] = t4; $[7] = t5; } else { t5 = $[7]; } let t6; if ($[8] !== alwaysVisible) { t6 = (e) => { if (!alwaysVisible) { e.currentTarget.style.opacity = "1"; } }; $[8] = alwaysVisible; $[9] = t6; } else { t6 = $[9]; } let t7; if ($[10] !== alwaysVisible || $[11] !== isResizing) { t7 = (e_0) => { if (!alwaysVisible && !isResizing) { e_0.currentTarget.style.opacity = "0"; } }; $[10] = alwaysVisible; $[11] = isResizing; $[12] = t7; } else { t7 = $[12]; } let t8; if ($[13] !== direction || $[14] !== isResizing) { t8 = getVisualStyles(direction, isResizing); $[13] = direction; $[14] = isResizing; $[15] = t8; } else { t8 = $[15]; } let t9; if ($[16] !== t8) { t9 = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: t8 }); $[16] = t8; $[17] = t9; } else { t9 = $[17]; } let t10; if ($[18] !== handleResizeEnd || $[19] !== handleResizeMove || $[20] !== t2 || $[21] !== t5 || $[22] !== t6 || $[23] !== t7 || $[24] !== t9) { t10 = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { "data-no-drag": "true", onPointerDown: t2, onPointerMove: handleResizeMove, onPointerUp: handleResizeEnd, onPointerCancel: handleResizeEnd, style: t5, onPointerEnter: t6, onPointerLeave: t7, children: t9 }); $[18] = handleResizeEnd; $[19] = handleResizeMove; $[20] = t2; $[21] = t5; $[22] = t6; $[23] = t7; $[24] = t9; $[25] = t10; } else { t10 = $[25]; } return t10; } // src/gestures/useNodeGestures.ts var import_react28 = require("react"); var import_jotai66 = require("jotai"); init_viewport_store(); init_input_store(); init_interaction_store(); init_selection_store(); init_graph_store(); init_search_store(); init_selection_path_store(); init_input_classifier(); init_dispatcher(); init_types2(); function useNodeGestures({ nodeId, mappingIndex, onAction, heldKeys = NO_HELD_KEYS }) { const screenToWorld = (0, import_jotai66.useAtomValue)(screenToWorldAtom); const isStylusActive = (0, import_jotai66.useAtomValue)(isStylusActiveAtom); const fingerCount = (0, import_jotai66.useAtomValue)(fingerCountAtom); const inputMode = (0, import_jotai66.useAtomValue)(inputModeAtom); const keyboardInteractionMode = (0, import_jotai66.useAtomValue)(keyboardInteractionModeAtom); const selectedNodeIds = (0, import_jotai66.useAtomValue)(selectedNodeIdsAtom); const focusedNodeId = (0, import_jotai66.useAtomValue)(focusedNodeIdAtom); const draggingNodeId = (0, import_jotai66.useAtomValue)(draggingNodeIdAtom); const edgeCreation = (0, import_jotai66.useAtomValue)(edgeCreationAtom); const selectionPath = (0, import_jotai66.useAtomValue)(selectionPathAtom); const isSearchActive = (0, import_jotai66.useAtomValue)(isFilterActiveAtom); const commandLineVisible = (0, import_jotai66.useAtomValue)(commandLineVisibleAtom); const runner = (0, import_react28.useRef)(new TimedStateRunner()); const sourceRef = (0, import_react28.useRef)("mouse"); const buttonRef = (0, import_react28.useRef)(0); const posRef = (0, import_react28.useRef)({ x: 0, y: 0 }); const modifiersRef = (0, import_react28.useRef)(NO_MODIFIERS); const rightDragSeen = (0, import_react28.useRef)(false); const guardRef = (0, import_react28.useRef)({ isStylusActive: false, fingerCount: 0, isDragging: false, isResizing: false, isSplitting: false, inputMode: { type: "normal" }, keyboardInteractionMode: "navigate", selectedNodeIds: /* @__PURE__ */ new Set(), focusedNodeId: null, isSearchActive: false, commandLineVisible: false, heldKeys: NO_HELD_KEYS, custom: {} }); guardRef.current = { isStylusActive, fingerCount, isDragging: draggingNodeId !== null, isResizing: false, isSplitting: false, inputMode, keyboardInteractionMode, selectedNodeIds, focusedNodeId, isSearchActive, commandLineVisible, heldKeys, custom: {} }; guardRef.current.custom = { isSelecting: selectionPath !== null, isCreatingEdge: edgeCreation.isCreating }; (0, import_react28.useEffect)(() => { return () => runner.current.destroy(); }, []); const resolveAndDispatch = (event) => { const resolution = resolve(event, mappingIndex, guardRef.current); if (resolution) { dispatch(event, resolution); onAction?.(event, resolution); } return resolution; }; const makeEvent = (type, phase, screenX, screenY) => { const worldPos = screenToWorld(screenX, screenY); return { kind: "pointer", type, phase, subject: { kind: "node", nodeId }, source: sourceRef.current, button: buttonRef.current, modifiers: modifiersRef.current, heldKeys, screenPosition: { x: screenX, y: screenY }, worldPosition: worldPos }; }; (0, import_react28.useEffect)(() => { runner.current.onEmit = (gestureType) => { if (gestureType === "long-press") { const event_0 = makeEvent("long-press", "instant", posRef.current.x, posRef.current.y); resolveAndDispatch(event_0); } }; }); const onPointerDown = (e) => { const classified = classifyPointer(e.nativeEvent); sourceRef.current = classified.source; buttonRef.current = e.button ?? 0; posRef.current = { x: e.clientX, y: e.clientY }; modifiersRef.current = extractModifiers(e); rightDragSeen.current = false; runner.current.feed("down"); }; const onPointerUp = (e_0) => { const emitted = runner.current.feed("up"); if (emitted) { const event_1 = makeEvent(emitted, "instant", e_0.clientX, e_0.clientY); resolveAndDispatch(event_1); } }; const onPointerCancel = (_e) => { runner.current.feed("cancel"); }; const onContextMenu = (e_1) => { e_1.preventDefault(); e_1.stopPropagation(); if (rightDragSeen.current) { rightDragSeen.current = false; return; } const event_2 = makeEvent("tap", "instant", e_1.clientX, e_1.clientY); event_2.button = 2; event_2.modifiers = extractModifiers(e_1); resolveAndDispatch(event_2); }; return { onPointerDown, onPointerUp, onPointerCancel, onContextMenu }; } // src/components/Node.tsx var import_jsx_runtime10 = require("react/jsx-runtime"); function Node({ nodeData, renderContent, onPersist, onPersistError, wrapper: Wrapper }) { const { id, zIndex } = nodeData; const graph = (0, import_jotai67.useAtomValue)(graphAtom); const setHandleNodePointerDown = (0, import_jotai67.useSetAtom)(handleNodePointerDownSelectionAtom); const { isSelected } = useNodeSelection(id); const selectedNodeIds = (0, import_jotai67.useAtomValue)(selectedNodeIdsAtom); const activeDraggingNodeId = (0, import_jotai67.useAtomValue)(draggingNodeIdAtom); const focusedNodeId = (0, import_jotai67.useAtomValue)(focusedNodeIdAtom); const isFocused = focusedNodeId === id && !isSelected; const position = (0, import_jotai67.useAtomValue)(nodePositionAtomFamily(id)); const canvasStyles = useCanvasStyle(); const isFilterActive = (0, import_jotai67.useAtomValue)(isFilterActiveAtom); const searchResults = (0, import_jotai67.useAtomValue)(searchResultsAtom); const highlightedSearchNodeId = (0, import_jotai67.useAtomValue)(highlightedSearchNodeIdAtom); const isDimmedByFilter = isFilterActive && !searchResults.has(id); const isSearchHighlighted = highlightedSearchNodeId === id; const dropTargetId = (0, import_jotai67.useAtomValue)(dropTargetNodeIdAtom); const isDropTarget = dropTargetId === id; const { system, onAction } = useInputContext(); const gestureHandlers = useNodeGestures({ nodeId: id, mappingIndex: system.mappingIndex, onAction, heldKeys: system.heldKeys }); const { bind, updateNodePositions } = useNodeDrag(id, { heldKeys: system.heldKeys, onPersist, onPersistError }); const resizeOptions = { onPersist, onPersistError }; const { localWidth, localHeight, isResizing, createResizeStart, handleResizeMove, handleResizeEnd } = useNodeResize({ id, nodeData, updateNodePositions, options: resizeOptions }); const nodeWidth = localWidth; const nodeHeight = localHeight; const splitGesture = useSplitGesture(id); const isVisuallyDragging = activeDraggingNodeId !== null && selectedNodeIds.has(id); const bindGestures = bind(); const handlePointerDown = (e) => { const target = e.target; if (target.closest('[data-no-drag="true"]') || target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT") { return; } gestureHandlers.onPointerDown(e); if (bindGestures.onPointerDown) { bindGestures.onPointerDown(e); } e.stopPropagation(); setHandleNodePointerDown({ nodeId: id, isShiftPressed: e.shiftKey }); }; const handlePointerUp = (e_0) => { gestureHandlers.onPointerUp(e_0); }; const currentZIndex = graph.hasNode(id) ? graph.getNodeAttribute(id, "zIndex") : zIndex; const borderColor = isSelected ? canvasStyles.nodes.selectedBorderColor : isFocused ? canvasStyles.nodes.selectedBorderColor : isVisuallyDragging ? canvasStyles.nodes.draggingBorderColor : canvasStyles.nodes.defaultBorderColor; const borderWidth = isSelected || isVisuallyDragging ? "1.5px" : isFocused ? "2px" : "1px"; const borderRadius = `${canvasStyles.nodes.defaultBorderRadius}px`; const defaultBoxShadow = `0px 8px 24px ${canvasStyles.nodes.shadowColor}`; const draggingBoxShadow = `0px 12px 32px ${canvasStyles.nodes.shadowColor}`; const selectedNodeBoxShadow = `0 0 10px 2px ${canvasStyles.nodes.selectedGlowColor}, ${defaultBoxShadow}`; const focusedBoxShadow = `0 0 0 2px ${canvasStyles.nodes.selectedBorderColor}, ${defaultBoxShadow}`; const currentBoxShadow = isSelected ? selectedNodeBoxShadow : isFocused ? focusedBoxShadow : isVisuallyDragging ? draggingBoxShadow : defaultBoxShadow; const nodeContent = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { "data-draggable-node": "true", "data-node-id": id, "data-focused": isFocused || void 0, className: [isSearchHighlighted && "canvas-search-highlight", isDropTarget && "canvas-drop-target"].filter(Boolean).join(" ") || void 0, ...bindGestures, onPointerDown: (e_1) => { splitGesture.onPointerDown(e_1); handlePointerDown(e_1); }, onPointerMove: splitGesture.onPointerMove, onPointerUp: (e_2) => { splitGesture.onPointerUp(e_2); handlePointerUp(e_2); }, onPointerCancel: gestureHandlers.onPointerCancel, onContextMenu: gestureHandlers.onContextMenu, style: { width: `${nodeWidth}px`, height: `${nodeHeight}px`, backgroundColor: isVisuallyDragging ? canvasStyles.nodes.draggingBackgroundColor : canvasStyles.nodes.defaultBackground, border: `${borderWidth} solid ${borderColor}`, borderRadius, boxShadow: currentBoxShadow, cursor: isVisuallyDragging ? "grabbing" : "grab", opacity: isDimmedByFilter ? 0.2 : isVisuallyDragging ? 0.9 : 1, pointerEvents: isDimmedByFilter ? "none" : "auto", transition: isVisuallyDragging ? "opacity 0.1s, transform 0.05s, background-color 0.1s, box-shadow 0.1s, border-color 0.1s" : "opacity 0.4s, border-color 0.4s, box-shadow 0.4s, background-color 0.4s, transform 0.4s ease-in-out", transform: isVisuallyDragging ? "scale(1.005)" : "scale(1)", zIndex: currentZIndex, touchAction: "manipulation", userSelect: "auto", position: "relative", overflow: "hidden" }, children: [renderContent({ node: nodeData, isSelected, isResizing, isDragging: isVisuallyDragging }), /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ResizeHandle, { direction: "nw", createResizeStart, handleResizeMove, handleResizeEnd, isResizing }), /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ResizeHandle, { direction: "ne", createResizeStart, handleResizeMove, handleResizeEnd, isResizing }), /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ResizeHandle, { direction: "sw", createResizeStart, handleResizeMove, handleResizeEnd, isResizing }), /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ResizeHandle, { direction: "se", createResizeStart, handleResizeMove, handleResizeEnd, isResizing, alwaysVisible: true }), /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ResizeHandle, { direction: "n", createResizeStart, handleResizeMove, handleResizeEnd, isResizing }), /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ResizeHandle, { direction: "s", createResizeStart, handleResizeMove, handleResizeEnd, isResizing }), /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ResizeHandle, { direction: "w", createResizeStart, handleResizeMove, handleResizeEnd, isResizing })] }); if (Wrapper) { return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Wrapper, { nodeData, children: nodeContent }); } return nodeContent; } // src/components/NodeErrorBoundary.tsx var import_compiler_runtime38 = require("react/compiler-runtime"); var import_react30 = __toESM(require("react")); init_debug(); var import_jsx_runtime11 = require("react/jsx-runtime"); var debug19 = createDebug("error-boundary"); function DefaultFallback(t0) { const $ = (0, import_compiler_runtime38.c)(11); const { error, reset } = t0; let t1; let t2; let t3; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t1 = { padding: "16px", backgroundColor: "rgba(239, 68, 68, 0.1)", border: "1px solid rgba(239, 68, 68, 0.3)", borderRadius: "8px", color: "#ef4444", fontSize: "12px", minWidth: "150px" }; t2 = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontWeight: 600, marginBottom: "8px" }, children: "\u26A0\uFE0F Node Error" }); t3 = { color: "#888", marginBottom: "8px", wordBreak: "break-word" }; $[0] = t1; $[1] = t2; $[2] = t3; } else { t1 = $[0]; t2 = $[1]; t3 = $[2]; } const t4 = error.message || "Unknown error"; let t5; if ($[3] !== t4) { t5 = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: t3, children: t4 }); $[3] = t4; $[4] = t5; } else { t5 = $[4]; } let t6; if ($[5] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t6 = { padding: "4px 8px", fontSize: "11px", backgroundColor: "rgba(239, 68, 68, 0.2)", border: "1px solid rgba(239, 68, 68, 0.3)", borderRadius: "4px", color: "#ef4444", cursor: "pointer" }; $[5] = t6; } else { t6 = $[5]; } let t7; if ($[6] !== reset) { t7 = /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { onClick: reset, style: t6, children: "Retry" }); $[6] = reset; $[7] = t7; } else { t7 = $[7]; } let t8; if ($[8] !== t5 || $[9] !== t7) { t8 = /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: t1, children: [t2, t5, t7] }); $[8] = t5; $[9] = t7; $[10] = t8; } else { t8 = $[10]; } return t8; } var NodeErrorBoundary = class extends import_react30.Component { constructor(props) { super(props); __publicField(this, "reset", () => { this.setState({ hasError: false, error: null }); }); this.state = { hasError: false, error: null }; } static getDerivedStateFromError(error) { return { hasError: true, error }; } componentDidCatch(error, errorInfo) { const { nodeId, onError } = this.props; debug19.error("Error in node %s: %O %O", nodeId, error, errorInfo); onError?.(nodeId, error, errorInfo); } render() { const { hasError, error } = this.state; const { nodeId, children, renderFallback } = this.props; if (hasError && error) { if (renderFallback) { return renderFallback({ nodeId, error, reset: this.reset }); } return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DefaultFallback, { nodeId, error, reset: this.reset }); } return children; } }; // src/components/NodeRenderer.tsx var import_jsx_runtime12 = require("react/jsx-runtime"); function NodeRenderer(t0) { const $ = (0, import_compiler_runtime39.c)(17); const { renderNode, onNodePersist, onNodePersistError, onNodeRenderError, renderErrorFallback, nodeWrapper } = t0; const nodeKeys = (0, import_jotai68.useAtomValue)(visibleNodeKeysAtom); let t1; if ($[0] !== nodeKeys || $[1] !== nodeWrapper || $[2] !== onNodePersist || $[3] !== onNodePersistError || $[4] !== onNodeRenderError || $[5] !== renderErrorFallback || $[6] !== renderNode) { let t22; if ($[8] !== nodeWrapper || $[9] !== onNodePersist || $[10] !== onNodePersistError || $[11] !== onNodeRenderError || $[12] !== renderErrorFallback || $[13] !== renderNode) { t22 = (key) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NodeItem, { nodeKey: key, renderNode, onNodePersist, onNodePersistError, onNodeRenderError, renderErrorFallback, nodeWrapper }, key); $[8] = nodeWrapper; $[9] = onNodePersist; $[10] = onNodePersistError; $[11] = onNodeRenderError; $[12] = renderErrorFallback; $[13] = renderNode; $[14] = t22; } else { t22 = $[14]; } t1 = nodeKeys.map(t22); $[0] = nodeKeys; $[1] = nodeWrapper; $[2] = onNodePersist; $[3] = onNodePersistError; $[4] = onNodeRenderError; $[5] = renderErrorFallback; $[6] = renderNode; $[7] = t1; } else { t1 = $[7]; } let t2; if ($[15] !== t1) { t2 = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, { children: t1 }); $[15] = t1; $[16] = t2; } else { t2 = $[16]; } return t2; } function resolveRenderNode(nodeData) { const Component2 = getNodeTypeComponent(nodeData.dbData.node_type) ?? FallbackNodeTypeComponent; return (props) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Component2, { nodeData: props.node, isResizing: props.isResizing }); } function NodeItem(t0) { const $ = (0, import_compiler_runtime39.c)(24); const { nodeKey, renderNode, onNodePersist, onNodePersistError, onNodeRenderError, renderErrorFallback, nodeWrapper } = t0; let t1; if ($[0] !== nodeKey) { t1 = nodeFamilyAtom(nodeKey); $[0] = nodeKey; $[1] = t1; } else { t1 = $[1]; } const nodeData = (0, import_jotai68.useAtomValue)(t1); let t2; if ($[2] !== nodeKey) { t2 = nodePositionAtomFamily(nodeKey); $[2] = nodeKey; $[3] = t2; } else { t2 = $[3]; } const position = (0, import_jotai68.useAtomValue)(t2); if (!nodeData) { return null; } let t3; if ($[4] !== nodeData || $[5] !== renderNode) { t3 = renderNode ?? resolveRenderNode(nodeData); $[4] = nodeData; $[5] = renderNode; $[6] = t3; } else { t3 = $[6]; } const effectiveRenderNode = t3; let t4; if ($[7] !== position.x || $[8] !== position.y) { t4 = { position: "absolute", left: position.x, top: position.y, transform: "translate(0, 0)" }; $[7] = position.x; $[8] = position.y; $[9] = t4; } else { t4 = $[9]; } let t5; if ($[10] !== effectiveRenderNode || $[11] !== nodeData || $[12] !== nodeWrapper || $[13] !== onNodePersist || $[14] !== onNodePersistError) { t5 = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Node, { nodeData, renderContent: effectiveRenderNode, onPersist: onNodePersist, onPersistError: onNodePersistError, wrapper: nodeWrapper }); $[10] = effectiveRenderNode; $[11] = nodeData; $[12] = nodeWrapper; $[13] = onNodePersist; $[14] = onNodePersistError; $[15] = t5; } else { t5 = $[15]; } let t6; if ($[16] !== nodeKey || $[17] !== onNodeRenderError || $[18] !== renderErrorFallback || $[19] !== t5) { t6 = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(NodeErrorBoundary, { nodeId: nodeKey, onError: onNodeRenderError, renderFallback: renderErrorFallback, children: t5 }); $[16] = nodeKey; $[17] = onNodeRenderError; $[18] = renderErrorFallback; $[19] = t5; $[20] = t6; } else { t6 = $[20]; } let t7; if ($[21] !== t4 || $[22] !== t6) { t7 = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: t4, children: t6 }); $[21] = t4; $[22] = t6; $[23] = t7; } else { t7 = $[23]; } return t7; } // src/components/EdgeRenderer.tsx var import_compiler_runtime40 = require("react/compiler-runtime"); var import_react32 = __toESM(require("react")); var import_jotai69 = require("jotai"); init_graph_derived(); init_graph_mutations(); init_virtualization_store(); init_selection_store(); init_viewport_store(); init_search_store(); init_edge_path_calculators(); var import_jsx_runtime13 = require("react/jsx-runtime"); var SELECTED_EDGE_COLOR = "#3b82f6"; function EdgeRenderer(t0) { const $ = (0, import_compiler_runtime40.c)(19); const { onEdgeClick, onEdgeDoubleClick, onEdgeRightClick, onEdgeHover, onEdgeLeave } = t0; const edgeKeys = (0, import_jotai69.useAtomValue)(visibleEdgeKeysAtom); const departingEdges = (0, import_jotai69.useAtomValue)(departingEdgesAtom); let t1; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t1 = { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none", zIndex: 0, overflow: "visible" }; $[0] = t1; } else { t1 = $[0]; } let t2; if ($[1] !== edgeKeys || $[2] !== onEdgeClick || $[3] !== onEdgeDoubleClick || $[4] !== onEdgeHover || $[5] !== onEdgeLeave || $[6] !== onEdgeRightClick) { let t32; if ($[8] !== onEdgeClick || $[9] !== onEdgeDoubleClick || $[10] !== onEdgeHover || $[11] !== onEdgeLeave || $[12] !== onEdgeRightClick) { t32 = (key) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(EdgeItem, { edgeKey: key, onEdgeClick, onEdgeDoubleClick, onEdgeRightClick, onEdgeHover, onEdgeLeave }, key); $[8] = onEdgeClick; $[9] = onEdgeDoubleClick; $[10] = onEdgeHover; $[11] = onEdgeLeave; $[12] = onEdgeRightClick; $[13] = t32; } else { t32 = $[13]; } t2 = edgeKeys.map(t32); $[1] = edgeKeys; $[2] = onEdgeClick; $[3] = onEdgeDoubleClick; $[4] = onEdgeHover; $[5] = onEdgeLeave; $[6] = onEdgeRightClick; $[7] = t2; } else { t2 = $[7]; } let t3; if ($[14] !== departingEdges) { t3 = Array.from(departingEdges.entries()).map(_temp8); $[14] = departingEdges; $[15] = t3; } else { t3 = $[15]; } let t4; if ($[16] !== t2 || $[17] !== t3) { t4 = /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("svg", { style: t1, children: [t2, t3] }); $[16] = t2; $[17] = t3; $[18] = t4; } else { t4 = $[18]; } return t4; } function _temp8(t0) { const [key_0, edgeData] = t0; return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DepartingEdgeItem, { edgeData }, `departing-${key_0}`); } function EdgeItem(t0) { const $ = (0, import_compiler_runtime40.c)(77); const { edgeKey, onEdgeClick, onEdgeDoubleClick, onEdgeRightClick, onEdgeHover, onEdgeLeave } = t0; let t1; if ($[0] !== edgeKey) { t1 = edgeFamilyAtom(edgeKey); $[0] = edgeKey; $[1] = t1; } else { t1 = $[1]; } const edgeData = (0, import_jotai69.useAtomValue)(t1); const styles4 = useCanvasStyle(); const selectedEdgeId = (0, import_jotai69.useAtomValue)(selectedEdgeIdAtom); const selectEdge = (0, import_jotai69.useSetAtom)(selectEdgeAtom); const zoom = (0, import_jotai69.useAtomValue)(zoomAtom); const setEditingEdgeLabel = (0, import_jotai69.useSetAtom)(editingEdgeLabelAtom); const isFilterActive = (0, import_jotai69.useAtomValue)(isFilterActiveAtom); const searchResults = (0, import_jotai69.useAtomValue)(searchResultsAtom); const searchEdgeResults = (0, import_jotai69.useAtomValue)(searchEdgeResultsAtom); const isSelected = selectedEdgeId === edgeKey; let color; let handleClick; let handleContextMenu; let handleDoubleClick; let handlePointerEnter; let handlePointerLeave; let label; let t2; let t3; let type; let weight; if ($[2] !== edgeData || $[3] !== edgeKey || $[4] !== onEdgeClick || $[5] !== onEdgeDoubleClick || $[6] !== onEdgeHover || $[7] !== onEdgeLeave || $[8] !== onEdgeRightClick || $[9] !== selectEdge || $[10] !== styles4.edges.pathType || $[11] !== zoom) { t3 = /* @__PURE__ */ Symbol.for("react.early_return_sentinel"); bb0: { const pathCalculator = getEdgePathCalculator(styles4.edges.pathType || "bezier"); let t42; if ($[23] !== edgeData || $[24] !== edgeKey || $[25] !== onEdgeClick || $[26] !== selectEdge) { t42 = (e) => { e.stopPropagation(); if (onEdgeClick && edgeData) { onEdgeClick(edgeKey, edgeData, e); } else { selectEdge(edgeKey); } }; $[23] = edgeData; $[24] = edgeKey; $[25] = onEdgeClick; $[26] = selectEdge; $[27] = t42; } else { t42 = $[27]; } handleClick = t42; let t52; if ($[28] !== edgeData || $[29] !== edgeKey || $[30] !== onEdgeDoubleClick) { t52 = (e_0) => { if (onEdgeDoubleClick && edgeData) { e_0.stopPropagation(); e_0.preventDefault(); onEdgeDoubleClick(edgeKey, edgeData, e_0); } }; $[28] = edgeData; $[29] = edgeKey; $[30] = onEdgeDoubleClick; $[31] = t52; } else { t52 = $[31]; } handleDoubleClick = t52; let t62; if ($[32] !== edgeData || $[33] !== edgeKey || $[34] !== onEdgeRightClick) { t62 = (e_1) => { if (onEdgeRightClick && edgeData) { e_1.preventDefault(); e_1.stopPropagation(); onEdgeRightClick(edgeKey, edgeData, e_1); } }; $[32] = edgeData; $[33] = edgeKey; $[34] = onEdgeRightClick; $[35] = t62; } else { t62 = $[35]; } handleContextMenu = t62; let t72; if ($[36] !== edgeData || $[37] !== edgeKey || $[38] !== onEdgeHover) { t72 = () => { if (onEdgeHover && edgeData) { onEdgeHover(edgeKey, edgeData); } }; $[36] = edgeData; $[37] = edgeKey; $[38] = onEdgeHover; $[39] = t72; } else { t72 = $[39]; } handlePointerEnter = t72; let t82; if ($[40] !== edgeKey || $[41] !== onEdgeLeave) { t82 = () => { if (onEdgeLeave) { onEdgeLeave(edgeKey); } }; $[40] = edgeKey; $[41] = onEdgeLeave; $[42] = t82; } else { t82 = $[42]; } handlePointerLeave = t82; if (!edgeData) { t3 = null; break bb0; } const { sourcePosition, targetPosition, color: t92, weight: t102, type: t112, label: t122, sourceEndpoint, targetEndpoint } = edgeData; color = t92; weight = t102; type = t112; label = t122; const x1 = sourceEndpoint?.x ?? sourcePosition.x + edgeData.sourceNodeWidth / 2; const y1 = sourceEndpoint?.y ?? sourcePosition.y + edgeData.sourceNodeHeight / 2; const x2 = targetEndpoint?.x ?? targetPosition.x + edgeData.targetNodeWidth / 2; const y2 = targetEndpoint?.y ?? targetPosition.y + edgeData.targetNodeHeight / 2; if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2) || zoom === 0) { t3 = null; break bb0; } t2 = pathCalculator({ x1, y1, x2, y2, sourceWidth: edgeData.sourceNodeWidth, sourceHeight: edgeData.sourceNodeHeight, targetWidth: edgeData.targetNodeWidth, targetHeight: edgeData.targetNodeHeight }); } $[2] = edgeData; $[3] = edgeKey; $[4] = onEdgeClick; $[5] = onEdgeDoubleClick; $[6] = onEdgeHover; $[7] = onEdgeLeave; $[8] = onEdgeRightClick; $[9] = selectEdge; $[10] = styles4.edges.pathType; $[11] = zoom; $[12] = color; $[13] = handleClick; $[14] = handleContextMenu; $[15] = handleDoubleClick; $[16] = handlePointerEnter; $[17] = handlePointerLeave; $[18] = label; $[19] = t2; $[20] = t3; $[21] = type; $[22] = weight; } else { color = $[12]; handleClick = $[13]; handleContextMenu = $[14]; handleDoubleClick = $[15]; handlePointerEnter = $[16]; handlePointerLeave = $[17]; label = $[18]; t2 = $[19]; t3 = $[20]; type = $[21]; weight = $[22]; } if (t3 !== /* @__PURE__ */ Symbol.for("react.early_return_sentinel")) { return t3; } const { path: pathD, labelX, labelY } = t2; const edgeColor = isSelected ? SELECTED_EDGE_COLOR : color || styles4.edges.defaultColor; const baseStrokeWidth = weight || styles4.edges.defaultWeight; const effectiveStrokeWidth = (isSelected ? baseStrokeWidth * 1.5 : baseStrokeWidth) / zoom; const effectiveFontSize = 10 / zoom; const hitboxStrokeWidth = Math.max(10, baseStrokeWidth * 3) / zoom; const isDimmedByFilter = isFilterActive && !searchResults.has(edgeData.sourceId) && !searchResults.has(edgeData.targetId) && !searchEdgeResults.has(edgeKey); const edgeOpacity = isDimmedByFilter ? 0.2 : 1; let t4; if ($[43] !== edgeOpacity) { t4 = { cursor: "pointer", opacity: edgeOpacity, transition: "opacity 150ms ease" }; $[43] = edgeOpacity; $[44] = t4; } else { t4 = $[44]; } const t5 = isDimmedByFilter ? "none" : "stroke"; let t6; if ($[45] !== t5) { t6 = { pointerEvents: t5 }; $[45] = t5; $[46] = t6; } else { t6 = $[46]; } let t7; if ($[47] !== handleClick || $[48] !== handleContextMenu || $[49] !== handleDoubleClick || $[50] !== handlePointerEnter || $[51] !== handlePointerLeave || $[52] !== hitboxStrokeWidth || $[53] !== pathD || $[54] !== t6) { t7 = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: pathD, stroke: "transparent", strokeWidth: hitboxStrokeWidth, fill: "none", style: t6, onClick: handleClick, onDoubleClick: handleDoubleClick, onContextMenu: handleContextMenu, onPointerEnter: handlePointerEnter, onPointerLeave: handlePointerLeave }); $[47] = handleClick; $[48] = handleContextMenu; $[49] = handleDoubleClick; $[50] = handlePointerEnter; $[51] = handlePointerLeave; $[52] = hitboxStrokeWidth; $[53] = pathD; $[54] = t6; $[55] = t7; } else { t7 = $[55]; } const t8 = type === "dashed" ? `calc(5px / ${zoom}) calc(5px / ${zoom})` : void 0; let t9; if ($[56] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t9 = { pointerEvents: "none" }; $[56] = t9; } else { t9 = $[56]; } let t10; if ($[57] !== edgeColor || $[58] !== effectiveStrokeWidth || $[59] !== pathD || $[60] !== t8) { t10 = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: pathD, stroke: edgeColor, strokeWidth: effectiveStrokeWidth, fill: "none", strokeDasharray: t8, style: t9 }); $[57] = edgeColor; $[58] = effectiveStrokeWidth; $[59] = pathD; $[60] = t8; $[61] = t10; } else { t10 = $[61]; } let t11; if ($[62] !== edgeKey || $[63] !== effectiveFontSize || $[64] !== label || $[65] !== labelX || $[66] !== labelY || $[67] !== setEditingEdgeLabel || $[68] !== styles4.edges.labelColor || $[69] !== styles4.edges.labelStrokeColor || $[70] !== zoom) { t11 = label && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("text", { x: labelX, y: labelY, fontSize: `${effectiveFontSize}px`, fill: styles4.edges.labelColor, stroke: styles4.edges.labelStrokeColor, strokeWidth: `${0.2 / zoom}px`, textAnchor: "middle", dy: `${-5 / zoom}px`, style: { pointerEvents: "visiblePainted", cursor: "text" }, onDoubleClick: (e_2) => { e_2.stopPropagation(); e_2.preventDefault(); setEditingEdgeLabel(edgeKey); }, children: label }); $[62] = edgeKey; $[63] = effectiveFontSize; $[64] = label; $[65] = labelX; $[66] = labelY; $[67] = setEditingEdgeLabel; $[68] = styles4.edges.labelColor; $[69] = styles4.edges.labelStrokeColor; $[70] = zoom; $[71] = t11; } else { t11 = $[71]; } let t12; if ($[72] !== t10 || $[73] !== t11 || $[74] !== t4 || $[75] !== t7) { t12 = /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("g", { style: t4, children: [t7, t10, t11] }); $[72] = t10; $[73] = t11; $[74] = t4; $[75] = t7; $[76] = t12; } else { t12 = $[76]; } return t12; } function DepartingEdgeItem(t0) { const $ = (0, import_compiler_runtime40.c)(14); const { edgeData } = t0; const styles4 = useCanvasStyle(); const zoom = (0, import_jotai69.useAtomValue)(zoomAtom); let color; let t1; let t2; let type; let weight; if ($[0] !== edgeData || $[1] !== styles4.edges.pathType || $[2] !== zoom) { t2 = /* @__PURE__ */ Symbol.for("react.early_return_sentinel"); bb0: { const pathCalculator = getEdgePathCalculator(styles4.edges.pathType || "bezier"); const { sourcePosition, targetPosition, color: t32, weight: t42, type: t52, sourceEndpoint, targetEndpoint } = edgeData; color = t32; weight = t42; type = t52; const x1 = sourceEndpoint?.x ?? sourcePosition.x + edgeData.sourceNodeWidth / 2; const y1 = sourceEndpoint?.y ?? sourcePosition.y + edgeData.sourceNodeHeight / 2; const x2 = targetEndpoint?.x ?? targetPosition.x + edgeData.targetNodeWidth / 2; const y2 = targetEndpoint?.y ?? targetPosition.y + edgeData.targetNodeHeight / 2; if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2) || zoom === 0) { t2 = null; break bb0; } t1 = pathCalculator({ x1, y1, x2, y2, sourceWidth: edgeData.sourceNodeWidth, sourceHeight: edgeData.sourceNodeHeight, targetWidth: edgeData.targetNodeWidth, targetHeight: edgeData.targetNodeHeight }); } $[0] = edgeData; $[1] = styles4.edges.pathType; $[2] = zoom; $[3] = color; $[4] = t1; $[5] = t2; $[6] = type; $[7] = weight; } else { color = $[3]; t1 = $[4]; t2 = $[5]; type = $[6]; weight = $[7]; } if (t2 !== /* @__PURE__ */ Symbol.for("react.early_return_sentinel")) { return t2; } const { path: pathD } = t1; const edgeColor = color || styles4.edges.defaultColor; const baseStrokeWidth = weight || styles4.edges.defaultWeight; const effectiveStrokeWidth = baseStrokeWidth / zoom; const t3 = type === "dashed" ? `calc(5px / ${zoom}) calc(5px / ${zoom})` : void 0; let t4; if ($[8] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t4 = { pointerEvents: "none" }; $[8] = t4; } else { t4 = $[8]; } let t5; if ($[9] !== edgeColor || $[10] !== effectiveStrokeWidth || $[11] !== pathD || $[12] !== t3) { t5 = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("g", { className: "canvas-edge-exit", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: pathD, stroke: edgeColor, strokeWidth: effectiveStrokeWidth, fill: "none", strokeDasharray: t3, style: t4 }) }); $[9] = edgeColor; $[10] = effectiveStrokeWidth; $[11] = pathD; $[12] = t3; $[13] = t5; } else { t5 = $[13]; } return t5; } // src/components/EdgeLabelEditor.tsx var import_compiler_runtime41 = require("react/compiler-runtime"); var import_react33 = __toESM(require("react")); var import_jotai70 = require("jotai"); init_graph_derived(); init_graph_mutations(); init_viewport_store(); init_edge_path_calculators(); var import_jsx_runtime14 = require("react/jsx-runtime"); function EdgeLabelEditor(t0) { const $ = (0, import_compiler_runtime41.c)(3); const { className } = t0; const editingEdgeKey = (0, import_jotai70.useAtomValue)(editingEdgeLabelAtom); if (!editingEdgeKey) { return null; } let t1; if ($[0] !== className || $[1] !== editingEdgeKey) { t1 = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(EdgeLabelInput, { edgeKey: editingEdgeKey, className }); $[0] = className; $[1] = editingEdgeKey; $[2] = t1; } else { t1 = $[2]; } return t1; } function EdgeLabelInput(t0) { const $ = (0, import_compiler_runtime41.c)(29); const { edgeKey, className } = t0; let t1; if ($[0] !== edgeKey) { t1 = edgeFamilyAtom(edgeKey); $[0] = edgeKey; $[1] = t1; } else { t1 = $[1]; } const edgeData = (0, import_jotai70.useAtomValue)(t1); const setEditingEdge = (0, import_jotai70.useSetAtom)(editingEdgeLabelAtom); const updateLabel = (0, import_jotai70.useSetAtom)(updateEdgeLabelAtom); const pan = (0, import_jotai70.useAtomValue)(panAtom); const zoom = (0, import_jotai70.useAtomValue)(zoomAtom); const styles4 = useCanvasStyle(); const inputRef = (0, import_react33.useRef)(null); let t2; let t3; if ($[2] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t2 = () => { if (inputRef.current) { inputRef.current.focus(); inputRef.current.select(); } }; t3 = []; $[2] = t2; $[3] = t3; } else { t2 = $[2]; t3 = $[3]; } (0, import_react33.useEffect)(t2, t3); if (!edgeData) { setEditingEdge(null); return null; } const { sourcePosition, targetPosition, sourceEndpoint, targetEndpoint } = edgeData; const x1 = sourceEndpoint?.x ?? sourcePosition.x + edgeData.sourceNodeWidth / 2; const y1 = sourceEndpoint?.y ?? sourcePosition.y + edgeData.sourceNodeHeight / 2; const x2 = targetEndpoint?.x ?? targetPosition.x + edgeData.targetNodeWidth / 2; const y2 = targetEndpoint?.y ?? targetPosition.y + edgeData.targetNodeHeight / 2; let t4; if ($[4] !== edgeData.sourceNodeHeight || $[5] !== edgeData.sourceNodeWidth || $[6] !== edgeData.targetNodeHeight || $[7] !== edgeData.targetNodeWidth || $[8] !== styles4.edges.pathType || $[9] !== x1 || $[10] !== x2 || $[11] !== y1 || $[12] !== y2) { const pathCalculator = getEdgePathCalculator(styles4.edges.pathType || "bezier"); t4 = pathCalculator({ x1, y1, x2, y2, sourceWidth: edgeData.sourceNodeWidth, sourceHeight: edgeData.sourceNodeHeight, targetWidth: edgeData.targetNodeWidth, targetHeight: edgeData.targetNodeHeight }); $[4] = edgeData.sourceNodeHeight; $[5] = edgeData.sourceNodeWidth; $[6] = edgeData.targetNodeHeight; $[7] = edgeData.targetNodeWidth; $[8] = styles4.edges.pathType; $[9] = x1; $[10] = x2; $[11] = y1; $[12] = y2; $[13] = t4; } else { t4 = $[13]; } const { labelX, labelY } = t4; const screenX = labelX * zoom + pan.x; const screenY = labelY * zoom + pan.y; let t5; if ($[14] !== edgeKey || $[15] !== setEditingEdge || $[16] !== updateLabel) { t5 = (e) => { updateLabel({ edgeKey, label: e.target.value }); setEditingEdge(null); }; $[14] = edgeKey; $[15] = setEditingEdge; $[16] = updateLabel; $[17] = t5; } else { t5 = $[17]; } const handleBlur = t5; let t6; if ($[18] !== setEditingEdge) { t6 = (e_0) => { if (e_0.key === "Enter") { e_0.currentTarget.blur(); } else { if (e_0.key === "Escape") { setEditingEdge(null); } } }; $[18] = setEditingEdge; $[19] = t6; } else { t6 = $[19]; } const handleKeyDown = t6; const t7 = edgeData.label || ""; let t8; if ($[20] !== screenX || $[21] !== screenY) { t8 = { position: "absolute", left: screenX, top: screenY, transform: "translate(-50%, -50%)", zIndex: 1e4, fontSize: "10px", padding: "2px 6px", border: "1px solid #3b82f6", borderRadius: "3px", outline: "none", background: "white", textAlign: "center", minWidth: "60px" }; $[20] = screenX; $[21] = screenY; $[22] = t8; } else { t8 = $[22]; } let t9; if ($[23] !== className || $[24] !== handleBlur || $[25] !== handleKeyDown || $[26] !== t7 || $[27] !== t8) { t9 = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("input", { ref: inputRef, "data-no-drag": "true", className, defaultValue: t7, onBlur: handleBlur, onKeyDown: handleKeyDown, style: t8 }); $[23] = className; $[24] = handleBlur; $[25] = handleKeyDown; $[26] = t7; $[27] = t8; $[28] = t9; } else { t9 = $[28]; } return t9; } // src/components/EdgePreviewLine.tsx var import_compiler_runtime42 = require("react/compiler-runtime"); var import_react34 = __toESM(require("react")); var import_jotai71 = require("jotai"); init_graph_store(); init_edge_path_calculators(); var import_jsx_runtime15 = require("react/jsx-runtime"); var DASH_ARRAY = "8 4"; var IDLE_COLOR = "rgba(150, 150, 150, 0.6)"; var VALID_TARGET_COLOR = "var(--canvas-edge-preview-valid, #4ade80)"; var SNAP_COLOR = "var(--canvas-edge-preview-snap, #60a5fa)"; function EdgePreviewLine() { const $ = (0, import_compiler_runtime42.c)(28); const edgeCreation = (0, import_jotai71.useAtomValue)(edgeCreationAtom); if (!edgeCreation.isCreating || !edgeCreation.sourceNodePosition) { return null; } const source = edgeCreation.sourceNodePosition; const target = edgeCreation.snappedTargetPosition ?? edgeCreation.targetPosition; if (!target) { return null; } const hasValidTarget = edgeCreation.hoveredTargetNodeId !== null; const isSnapped = edgeCreation.snappedTargetPosition !== null; let strokeColor = IDLE_COLOR; if (isSnapped) { strokeColor = SNAP_COLOR; } else { if (hasValidTarget) { strokeColor = VALID_TARGET_COLOR; } } let t0; if ($[0] !== source.x || $[1] !== source.y || $[2] !== target.x || $[3] !== target.y) { const pathInput = { x1: source.x, y1: source.y, x2: target.x, y2: target.y, sourceWidth: 0, sourceHeight: 0, targetWidth: 0, targetHeight: 0 }; t0 = bezierSmart(pathInput); $[0] = source.x; $[1] = source.y; $[2] = target.x; $[3] = target.y; $[4] = t0; } else { t0 = $[4]; } const { path } = t0; const dotRadius = isSnapped ? 6 : 4; let t1; if ($[5] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t1 = { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none", overflow: "visible", zIndex: 5 }; $[5] = t1; } else { t1 = $[5]; } const t2 = isSnapped ? 2.5 : 2; let t3; let t4; if ($[6] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t3 = { transition: "stroke 150ms, stroke-width 150ms" }; t4 = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("animate", { attributeName: "stroke-dashoffset", from: "24", to: "0", dur: "0.8s", repeatCount: "indefinite" }); $[6] = t3; $[7] = t4; } else { t3 = $[6]; t4 = $[7]; } let t5; if ($[8] !== path || $[9] !== strokeColor || $[10] !== t2) { t5 = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: path, fill: "none", stroke: strokeColor, strokeWidth: t2, strokeDasharray: DASH_ARRAY, strokeLinecap: "round", style: t3, children: t4 }); $[8] = path; $[9] = strokeColor; $[10] = t2; $[11] = t5; } else { t5 = $[11]; } let t6; if ($[12] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t6 = { transition: "fill 150ms" }; $[12] = t6; } else { t6 = $[12]; } let t7; if ($[13] !== source.x || $[14] !== source.y || $[15] !== strokeColor) { t7 = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("circle", { cx: source.x, cy: source.y, r: 4, fill: strokeColor, style: t6 }); $[13] = source.x; $[14] = source.y; $[15] = strokeColor; $[16] = t7; } else { t7 = $[16]; } const t8 = isSnapped ? SNAP_COLOR : "transparent"; let t9; if ($[17] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t9 = { transition: "r 150ms, fill 150ms" }; $[17] = t9; } else { t9 = $[17]; } let t10; if ($[18] !== dotRadius || $[19] !== strokeColor || $[20] !== t8 || $[21] !== target.x || $[22] !== target.y) { t10 = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("circle", { cx: target.x, cy: target.y, r: dotRadius, fill: t8, stroke: strokeColor, strokeWidth: 1.5, style: t9 }); $[18] = dotRadius; $[19] = strokeColor; $[20] = t8; $[21] = target.x; $[22] = target.y; $[23] = t10; } else { t10 = $[23]; } let t11; if ($[24] !== t10 || $[25] !== t5 || $[26] !== t7) { t11 = /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("svg", { style: t1, children: [t5, t7, t10] }); $[24] = t10; $[25] = t5; $[26] = t7; $[27] = t11; } else { t11 = $[27]; } return t11; } // src/hooks/useSelectionChangeEffect.ts var import_compiler_runtime43 = require("react/compiler-runtime"); var import_react35 = require("react"); var import_jotai72 = require("jotai"); init_selection_store(); function useSelectionChangeEffect(onSelectionChange) { const $ = (0, import_compiler_runtime43.c)(5); const selectedNodeIds = (0, import_jotai72.useAtomValue)(selectedNodeIdsAtom); const selectedEdgeId = (0, import_jotai72.useAtomValue)(selectedEdgeIdAtom); const isFirstRender = (0, import_react35.useRef)(true); let t0; let t1; if ($[0] !== onSelectionChange || $[1] !== selectedEdgeId || $[2] !== selectedNodeIds) { t0 = () => { if (isFirstRender.current) { isFirstRender.current = false; return; } onSelectionChange?.(selectedNodeIds, selectedEdgeId); }; t1 = [selectedNodeIds, selectedEdgeId, onSelectionChange]; $[0] = onSelectionChange; $[1] = selectedEdgeId; $[2] = selectedNodeIds; $[3] = t0; $[4] = t1; } else { t0 = $[3]; t1 = $[4]; } (0, import_react35.useEffect)(t0, t1); } // src/hooks/useViewportChangeEffect.ts var import_compiler_runtime44 = require("react/compiler-runtime"); var import_react36 = require("react"); var import_jotai73 = require("jotai"); init_viewport_store(); function useViewportChangeEffect(onViewportChange) { const $ = (0, import_compiler_runtime44.c)(5); const zoom = (0, import_jotai73.useAtomValue)(zoomAtom); const pan = (0, import_jotai73.useAtomValue)(panAtom); const rafRef = (0, import_react36.useRef)(null); const isFirstRender = (0, import_react36.useRef)(true); let t0; let t1; if ($[0] !== onViewportChange || $[1] !== pan || $[2] !== zoom) { t0 = () => { if (isFirstRender.current) { isFirstRender.current = false; return; } if (!onViewportChange) { return; } if (rafRef.current !== null) { cancelAnimationFrame(rafRef.current); } rafRef.current = requestAnimationFrame(() => { onViewportChange({ zoom, pan }); rafRef.current = null; }); return () => { if (rafRef.current !== null) { cancelAnimationFrame(rafRef.current); } }; }; t1 = [zoom, pan, onViewportChange]; $[0] = onViewportChange; $[1] = pan; $[2] = zoom; $[3] = t0; $[4] = t1; } else { t0 = $[3]; t1 = $[4]; } (0, import_react36.useEffect)(t0, t1); } // src/hooks/useDragLifecycleEffect.ts var import_compiler_runtime45 = require("react/compiler-runtime"); var import_react37 = require("react"); var import_jotai74 = require("jotai"); init_graph_store(); init_selection_store(); function useDragLifecycleEffect(onDragStart, onDragEnd) { const $ = (0, import_compiler_runtime45.c)(7); const draggingNodeId = (0, import_jotai74.useAtomValue)(draggingNodeIdAtom); const store = (0, import_jotai74.useStore)(); const prevDraggingRef = (0, import_react37.useRef)(null); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = []; $[0] = t0; } else { t0 = $[0]; } const draggedNodeIdsRef = (0, import_react37.useRef)(t0); let t1; let t2; if ($[1] !== draggingNodeId || $[2] !== onDragEnd || $[3] !== onDragStart || $[4] !== store) { t1 = () => { const prev = prevDraggingRef.current; prevDraggingRef.current = draggingNodeId; if (prev === null && draggingNodeId !== null) { const selectedIds = Array.from(store.get(selectedNodeIdsAtom)); draggedNodeIdsRef.current = selectedIds; onDragStart?.(selectedIds); } else { if (prev !== null && draggingNodeId === null) { const nodeIds = draggedNodeIdsRef.current; if (onDragEnd && nodeIds.length > 0) { const graph = store.get(graphAtom); const positions = /* @__PURE__ */ new Map(); for (const id of nodeIds) { if (graph.hasNode(id)) { const attrs = graph.getNodeAttributes(id); positions.set(id, { x: attrs.x, y: attrs.y }); } } onDragEnd(nodeIds, positions); } draggedNodeIdsRef.current = []; } } }; t2 = [draggingNodeId, store, onDragStart, onDragEnd]; $[1] = draggingNodeId; $[2] = onDragEnd; $[3] = onDragStart; $[4] = store; $[5] = t1; $[6] = t2; } else { t1 = $[5]; t2 = $[6]; } (0, import_react37.useEffect)(t1, t2); } // src/components/Canvas.tsx var import_jsx_runtime16 = require("react/jsx-runtime"); function Canvas(t0) { const $ = (0, import_compiler_runtime46.c)(44); let children; let gestureConfig; let nodeWrapper; let onAction; let onDragEnd; let onDragStart; let onEdgeClick; let onEdgeDoubleClick; let onEdgeHover; let onEdgeLeave; let onEdgeRightClick; let onNodePersist; let onNodePersistError; let onNodeRenderError; let onSelectionChange; let onViewportChange; let renderErrorFallback; let renderNode; let viewportProps; if ($[0] !== t0) { ({ renderNode, onEdgeClick, onEdgeDoubleClick, onEdgeRightClick, onEdgeHover, onEdgeLeave, onSelectionChange, onViewportChange, onDragStart, onDragEnd, onNodePersist, onNodePersistError, onNodeRenderError, renderErrorFallback, gestureConfig, onAction, nodeWrapper, children, ...viewportProps } = t0); $[0] = t0; $[1] = children; $[2] = gestureConfig; $[3] = nodeWrapper; $[4] = onAction; $[5] = onDragEnd; $[6] = onDragStart; $[7] = onEdgeClick; $[8] = onEdgeDoubleClick; $[9] = onEdgeHover; $[10] = onEdgeLeave; $[11] = onEdgeRightClick; $[12] = onNodePersist; $[13] = onNodePersistError; $[14] = onNodeRenderError; $[15] = onSelectionChange; $[16] = onViewportChange; $[17] = renderErrorFallback; $[18] = renderNode; $[19] = viewportProps; } else { children = $[1]; gestureConfig = $[2]; nodeWrapper = $[3]; onAction = $[4]; onDragEnd = $[5]; onDragStart = $[6]; onEdgeClick = $[7]; onEdgeDoubleClick = $[8]; onEdgeHover = $[9]; onEdgeLeave = $[10]; onEdgeRightClick = $[11]; onNodePersist = $[12]; onNodePersistError = $[13]; onNodeRenderError = $[14]; onSelectionChange = $[15]; onViewportChange = $[16]; renderErrorFallback = $[17]; renderNode = $[18]; viewportProps = $[19]; } useSelectionChangeEffect(onSelectionChange); useViewportChangeEffect(onViewportChange); useDragLifecycleEffect(onDragStart, onDragEnd); let t1; if ($[20] !== onEdgeClick || $[21] !== onEdgeDoubleClick || $[22] !== onEdgeHover || $[23] !== onEdgeLeave || $[24] !== onEdgeRightClick) { t1 = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(EdgeRenderer, { onEdgeClick, onEdgeDoubleClick, onEdgeRightClick, onEdgeHover, onEdgeLeave }); $[20] = onEdgeClick; $[21] = onEdgeDoubleClick; $[22] = onEdgeHover; $[23] = onEdgeLeave; $[24] = onEdgeRightClick; $[25] = t1; } else { t1 = $[25]; } let t2; if ($[26] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t2 = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(EdgePreviewLine, {}); $[26] = t2; } else { t2 = $[26]; } let t3; if ($[27] !== nodeWrapper || $[28] !== onNodePersist || $[29] !== onNodePersistError || $[30] !== onNodeRenderError || $[31] !== renderErrorFallback || $[32] !== renderNode) { t3 = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(NodeRenderer, { renderNode, onNodePersist, onNodePersistError, onNodeRenderError, renderErrorFallback, nodeWrapper }); $[27] = nodeWrapper; $[28] = onNodePersist; $[29] = onNodePersistError; $[30] = onNodeRenderError; $[31] = renderErrorFallback; $[32] = renderNode; $[33] = t3; } else { t3 = $[33]; } let t4; if ($[34] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t4 = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(EdgeLabelEditor, {}); $[34] = t4; } else { t4 = $[34]; } let t5; if ($[35] !== children || $[36] !== t1 || $[37] !== t3 || $[38] !== viewportProps) { t5 = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Viewport, { ...viewportProps, children: [t1, t2, t3, t4, children] }); $[35] = children; $[36] = t1; $[37] = t3; $[38] = viewportProps; $[39] = t5; } else { t5 = $[39]; } let t6; if ($[40] !== gestureConfig || $[41] !== onAction || $[42] !== t5) { t6 = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(InputProvider, { gestureConfig, onAction, children: t5 }); $[40] = gestureConfig; $[41] = onAction; $[42] = t5; $[43] = t6; } else { t6 = $[43]; } return t6; } // src/components/ConnectedNode.tsx var import_compiler_runtime47 = require("react/compiler-runtime"); var import_react39 = __toESM(require("react")); init_debug(); var import_jsx_runtime17 = require("react/jsx-runtime"); var debug20 = createDebug("connected-node"); function ConnectedNode(t0) { const $ = (0, import_compiler_runtime47.c)(10); const { nodeData, renderContent, onPersistError: customErrorHandler, wrapper } = t0; const { mutateAsync: updateNode } = useUpdateNode(); let t1; if ($[0] !== updateNode) { t1 = async (nodeId, graphId, uiProperties) => { debug20("Persisting node %s to graph %s", nodeId, graphId); await updateNode({ nodeId, graphId, updates: { ui_properties: uiProperties } }); }; $[0] = updateNode; $[1] = t1; } else { t1 = $[1]; } const handlePersist = t1; let t2; if ($[2] !== customErrorHandler) { t2 = (nodeId_0, error) => { debug20("Failed to persist node %s: %o", nodeId_0, error); customErrorHandler?.(nodeId_0, error); }; $[2] = customErrorHandler; $[3] = t2; } else { t2 = $[3]; } const handlePersistError = t2; let t3; if ($[4] !== handlePersist || $[5] !== handlePersistError || $[6] !== nodeData || $[7] !== renderContent || $[8] !== wrapper) { t3 = /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Node, { nodeData, renderContent, onPersist: handlePersist, onPersistError: handlePersistError, wrapper }); $[4] = handlePersist; $[5] = handlePersistError; $[6] = nodeData; $[7] = renderContent; $[8] = wrapper; $[9] = t3; } else { t3 = $[9]; } return t3; } // src/components/ConnectedNodeRenderer.tsx var import_compiler_runtime48 = require("react/compiler-runtime"); var import_react40 = __toESM(require("react")); var import_jotai75 = require("jotai"); init_graph_position(); init_graph_derived(); init_virtualization_store(); var import_jsx_runtime18 = require("react/jsx-runtime"); function ConnectedNodeRenderer(t0) { const $ = (0, import_compiler_runtime48.c)(11); const { renderNode, onNodePersistError, nodeWrapper } = t0; const nodeKeys = (0, import_jotai75.useAtomValue)(visibleNodeKeysAtom); let t1; if ($[0] !== nodeKeys || $[1] !== nodeWrapper || $[2] !== onNodePersistError || $[3] !== renderNode) { let t22; if ($[5] !== nodeWrapper || $[6] !== onNodePersistError || $[7] !== renderNode) { t22 = (key) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ConnectedNodeItem, { nodeKey: key, renderNode, onNodePersistError, nodeWrapper }, key); $[5] = nodeWrapper; $[6] = onNodePersistError; $[7] = renderNode; $[8] = t22; } else { t22 = $[8]; } t1 = nodeKeys.map(t22); $[0] = nodeKeys; $[1] = nodeWrapper; $[2] = onNodePersistError; $[3] = renderNode; $[4] = t1; } else { t1 = $[4]; } let t2; if ($[9] !== t1) { t2 = /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_jsx_runtime18.Fragment, { children: t1 }); $[9] = t1; $[10] = t2; } else { t2 = $[10]; } return t2; } function ConnectedNodeItem(t0) { const $ = (0, import_compiler_runtime48.c)(15); const { nodeKey, renderNode, onNodePersistError, nodeWrapper } = t0; let t1; if ($[0] !== nodeKey) { t1 = nodeFamilyAtom(nodeKey); $[0] = nodeKey; $[1] = t1; } else { t1 = $[1]; } const nodeData = (0, import_jotai75.useAtomValue)(t1); let t2; if ($[2] !== nodeKey) { t2 = nodePositionAtomFamily(nodeKey); $[2] = nodeKey; $[3] = t2; } else { t2 = $[3]; } const position = (0, import_jotai75.useAtomValue)(t2); if (!nodeData) { return null; } let t3; if ($[4] !== position.x || $[5] !== position.y) { t3 = { position: "absolute", left: position.x, top: position.y, transform: "translate(0, 0)" }; $[4] = position.x; $[5] = position.y; $[6] = t3; } else { t3 = $[6]; } let t4; if ($[7] !== nodeData || $[8] !== nodeWrapper || $[9] !== onNodePersistError || $[10] !== renderNode) { t4 = /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ConnectedNode, { nodeData, renderContent: renderNode, onPersistError: onNodePersistError, wrapper: nodeWrapper }); $[7] = nodeData; $[8] = nodeWrapper; $[9] = onNodePersistError; $[10] = renderNode; $[11] = t4; } else { t4 = $[11]; } let t5; if ($[12] !== t3 || $[13] !== t4) { t5 = /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: t3, children: t4 }); $[12] = t3; $[13] = t4; $[14] = t5; } else { t5 = $[14]; } return t5; } // src/components/NodeContextMenu.tsx var import_compiler_runtime51 = require("react/compiler-runtime"); var import_react43 = __toESM(require("react")); var import_jotai76 = require("jotai"); init_graph_store(); init_selection_store(); init_input_store(); // src/components/ContextMenuAction.tsx var import_compiler_runtime49 = require("react/compiler-runtime"); var import_react41 = __toESM(require("react")); var import_jsx_runtime19 = require("react/jsx-runtime"); function ContextMenuAction(t0) { const $ = (0, import_compiler_runtime49.c)(18); const { label, icon, onClick, destructive: t1, disabled: t2 } = t0; const destructive = t1 === void 0 ? false : t1; const disabled = t2 === void 0 ? false : t2; const t3 = disabled ? "not-allowed" : "pointer"; const t4 = disabled ? 0.5 : 1; const t5 = destructive ? "#ef4444" : "var(--canvas-text, #fff)"; let t6; if ($[0] !== t3 || $[1] !== t4 || $[2] !== t5) { t6 = { width: "100%", minHeight: "48px", padding: "12px 16px", display: "flex", alignItems: "center", gap: "12px", background: "none", border: "none", borderRadius: "8px", cursor: t3, opacity: t4, color: t5, fontSize: "16px", textAlign: "left", touchAction: "manipulation", WebkitTapHighlightColor: "transparent" }; $[0] = t3; $[1] = t4; $[2] = t5; $[3] = t6; } else { t6 = $[3]; } let t7; if ($[4] !== destructive || $[5] !== disabled) { t7 = (e) => { if (!disabled) { e.currentTarget.style.backgroundColor = destructive ? "rgba(239, 68, 68, 0.1)" : "rgba(255, 255, 255, 0.1)"; } }; $[4] = destructive; $[5] = disabled; $[6] = t7; } else { t7 = $[6]; } let t8; if ($[7] !== icon) { t8 = icon && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { display: "flex" }, children: icon }); $[7] = icon; $[8] = t8; } else { t8 = $[8]; } let t9; if ($[9] !== label) { t9 = /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: label }); $[9] = label; $[10] = t9; } else { t9 = $[10]; } let t10; if ($[11] !== disabled || $[12] !== onClick || $[13] !== t6 || $[14] !== t7 || $[15] !== t8 || $[16] !== t9) { t10 = /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("button", { onClick, disabled, style: t6, onPointerEnter: t7, onPointerLeave: _temp9, children: [t8, t9] }); $[11] = disabled; $[12] = onClick; $[13] = t6; $[14] = t7; $[15] = t8; $[16] = t9; $[17] = t10; } else { t10 = $[17]; } return t10; } function _temp9(e_0) { e_0.currentTarget.style.backgroundColor = "transparent"; } // src/components/ContextMenuDivider.tsx var import_compiler_runtime50 = require("react/compiler-runtime"); var import_react42 = __toESM(require("react")); var import_jsx_runtime20 = require("react/jsx-runtime"); function ContextMenuDivider(t0) { const $ = (0, import_compiler_runtime50.c)(7); const { label } = t0; const t1 = label ? "8px 12px" : "0"; let t2; if ($[0] !== t1) { t2 = { padding: t1, borderBottom: "1px solid var(--canvas-border, #333)", marginBottom: "4px" }; $[0] = t1; $[1] = t2; } else { t2 = $[1]; } let t3; if ($[2] !== label) { t3 = label && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { fontSize: "14px", fontWeight: 500, color: "var(--canvas-text, #fff)" }, children: label }); $[2] = label; $[3] = t3; } else { t3 = $[3]; } let t4; if ($[4] !== t2 || $[5] !== t3) { t4 = /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: t2, children: t3 }); $[4] = t2; $[5] = t3; $[6] = t4; } else { t4 = $[6]; } return t4; } // src/components/NodeContextMenu.tsx var import_jsx_runtime21 = require("react/jsx-runtime"); var DefaultDialog = (t0) => { const $ = (0, import_compiler_runtime51.c)(14); const { open, onOpenChange, children } = t0; const inputSource = (0, import_jotai76.useAtomValue)(primaryInputSourceAtom); const isTouchInput = inputSource === "finger" || inputSource === "pencil"; if (!open) { return null; } if (isTouchInput) { let t12; if ($[0] !== onOpenChange) { t12 = () => onOpenChange(false); $[0] = onOpenChange; $[1] = t12; } else { t12 = $[1]; } let t22; if ($[2] !== children || $[3] !== t12) { t22 = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(BottomSheet, { onClose: t12, children }); $[2] = children; $[3] = t12; $[4] = t22; } else { t22 = $[4]; } return t22; } let t1; if ($[5] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t1 = { position: "fixed", inset: 0, zIndex: 9999, display: "flex", alignItems: "center", justifyContent: "center", backgroundColor: "rgba(0, 0, 0, 0.5)" }; $[5] = t1; } else { t1 = $[5]; } let t2; if ($[6] !== onOpenChange) { t2 = () => onOpenChange(false); $[6] = onOpenChange; $[7] = t2; } else { t2 = $[7]; } let t3; if ($[8] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t3 = { backgroundColor: "var(--canvas-bg, #1a1a1a)", border: "1px solid var(--canvas-border, #333)", borderRadius: "8px", padding: "8px 0", minWidth: "200px", maxWidth: "280px", boxShadow: "0 4px 20px rgba(0, 0, 0, 0.3)" }; $[8] = t3; } else { t3 = $[8]; } let t4; if ($[9] !== children) { t4 = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: t3, onClick: _temp10, onPointerDown: _temp23, children }); $[9] = children; $[10] = t4; } else { t4 = $[10]; } let t5; if ($[11] !== t2 || $[12] !== t4) { t5 = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: t1, onClick: t2, children: t4 }); $[11] = t2; $[12] = t4; $[13] = t5; } else { t5 = $[13]; } return t5; }; function BottomSheet(t0) { const $ = (0, import_compiler_runtime51.c)(17); const { onClose, children } = t0; const sheetRef = (0, import_react43.useRef)(null); const startYRef = (0, import_react43.useRef)(null); let t1; if ($[0] !== onClose) { t1 = () => { onClose(); }; $[0] = onClose; $[1] = t1; } else { t1 = $[1]; } const handleBackdropClick = t1; let t2; if ($[2] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t2 = (e) => { startYRef.current = e.clientY; }; $[2] = t2; } else { t2 = $[2]; } const handlePointerDown = t2; let t3; if ($[3] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t3 = (e_0) => { if (startYRef.current === null || !sheetRef.current) { return; } const dy = e_0.clientY - startYRef.current; if (dy > 0) { sheetRef.current.style.transform = `translateY(${dy}px)`; } }; $[3] = t3; } else { t3 = $[3]; } const handlePointerMove = t3; let t4; if ($[4] !== onClose) { t4 = (e_1) => { if (startYRef.current === null) { return; } const dy_0 = e_1.clientY - startYRef.current; startYRef.current = null; if (dy_0 > 60) { onClose(); } else { if (sheetRef.current) { sheetRef.current.style.transform = "translateY(0)"; } } }; $[4] = onClose; $[5] = t4; } else { t4 = $[5]; } const handlePointerUp = t4; let t5; if ($[6] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t5 = { position: "fixed", inset: 0, zIndex: 9999, display: "flex", flexDirection: "column", justifyContent: "flex-end", backgroundColor: "rgba(0, 0, 0, 0.4)" }; $[6] = t5; } else { t5 = $[6]; } let t6; if ($[7] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t6 = { backgroundColor: "var(--canvas-bg, #1a1a1a)", borderTop: "1px solid var(--canvas-border, #333)", borderRadius: "16px 16px 0 0", padding: "0 0 env(safe-area-inset-bottom, 16px)", maxHeight: "60vh", overflowY: "auto", transition: "transform 200ms ease-out", WebkitOverflowScrolling: "touch" }; $[7] = t6; } else { t6 = $[7]; } let t7; if ($[8] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t7 = (e_3) => { e_3.stopPropagation(); handlePointerDown(e_3); }; $[8] = t7; } else { t7 = $[8]; } let t8; let t9; if ($[9] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t8 = (e_4) => { startYRef.current = null; if (sheetRef.current) { sheetRef.current.style.transform = "translateY(0)"; } }; t9 = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { display: "flex", justifyContent: "center", padding: "8px 0 4px", touchAction: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { width: "36px", height: "4px", borderRadius: "2px", backgroundColor: "var(--canvas-border, #666)" } }) }); $[9] = t8; $[10] = t9; } else { t8 = $[9]; t9 = $[10]; } let t10; if ($[11] !== children || $[12] !== handlePointerUp) { t10 = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { ref: sheetRef, style: t6, onClick: _temp32, onPointerDown: t7, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onPointerCancel: t8, children: [t9, children] }); $[11] = children; $[12] = handlePointerUp; $[13] = t10; } else { t10 = $[13]; } let t11; if ($[14] !== handleBackdropClick || $[15] !== t10) { t11 = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: t5, onClick: handleBackdropClick, children: t10 }); $[14] = handleBackdropClick; $[15] = t10; $[16] = t11; } else { t11 = $[16]; } return t11; } function _temp32(e_2) { return e_2.stopPropagation(); } function NodeContextMenu(t0) { const $ = (0, import_compiler_runtime51.c)(13); const { open, onOpenChange, nodeId, actions: t1, renderContent, DialogComponent: t2 } = t0; const actions = t1 === void 0 ? [] : t1; const DialogComponent = t2 === void 0 ? DefaultDialog : t2; const currentGraphId = (0, import_jotai76.useAtomValue)(currentGraphIdAtom); (0, import_jotai76.useSetAtom)(clearSelectionAtom); let t3; if ($[0] !== onOpenChange) { t3 = () => { onOpenChange(false); }; $[0] = onOpenChange; $[1] = t3; } else { t3 = $[1]; } const closeMenu = t3; const ctx = { nodeId, graphId: currentGraphId, closeMenu }; if (renderContent) { return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogComponent, { open, onOpenChange, children: renderContent(ctx) }); } let t4; let t5; if ($[2] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t4 = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ContextMenuDivider, { label: "Node Options" }); t5 = { padding: "0 4px" }; $[2] = t4; $[3] = t5; } else { t4 = $[2]; t5 = $[3]; } const t6 = actions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ContextMenuAction, { label: action.label, icon: action.icon, onClick: () => action.onClick(ctx), destructive: action.destructive, disabled: action.disabled }, action.key)); let t7; if ($[4] !== t5 || $[5] !== t6) { t7 = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: t5, children: t6 }); $[4] = t5; $[5] = t6; $[6] = t7; } else { t7 = $[6]; } let t8; if ($[7] !== DialogComponent || $[8] !== onOpenChange || $[9] !== open || $[10] !== t4 || $[11] !== t7) { t8 = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DialogComponent, { open, onOpenChange, children: [t4, t7] }); $[7] = DialogComponent; $[8] = onOpenChange; $[9] = open; $[10] = t4; $[11] = t7; $[12] = t8; } else { t8 = $[12]; } return t8; } var DeleteIcon = () => { const $ = (0, import_compiler_runtime51.c)(1); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) }); $[0] = t0; } else { t0 = $[0]; } return t0; }; var FitToViewIcon = () => { const $ = (0, import_compiler_runtime51.c)(1); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7" }) }); $[0] = t0; } else { t0 = $[0]; } return t0; }; function _temp10(e) { return e.stopPropagation(); } function _temp23(e_0) { return e_0.stopPropagation(); } // src/components/LockedNodeOverlay.tsx var import_compiler_runtime52 = require("react/compiler-runtime"); var import_react44 = __toESM(require("react")); var import_jotai77 = require("jotai"); init_locked_node_store(); var import_jsx_runtime22 = require("react/jsx-runtime"); var DefaultCloseButton = (t0) => { const $ = (0, import_compiler_runtime52.c)(4); const { onClick } = t0; let t1; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t1 = { color: "var(--hud-text, #fff)", opacity: 0.6 }; $[0] = t1; } else { t1 = $[0]; } let t2; if ($[1] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t2 = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }); $[1] = t2; } else { t2 = $[1]; } let t3; if ($[2] !== onClick) { t3 = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { onClick, className: "absolute top-4 right-4 z-10 p-2 rounded hover:bg-white/10 transition-colors", style: t1, "aria-label": "Close", children: t2 }); $[2] = onClick; $[3] = t3; } else { t3 = $[3]; } return t3; }; var DefaultFooter = (t0) => { const $ = (0, import_compiler_runtime52.c)(13); const { pages, currentIndex, onPageSelect } = t0; let t1; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t1 = { backgroundColor: "var(--hud-header-bg, rgba(20, 20, 30, 0.9))", borderColor: "var(--hud-border, rgba(255, 255, 255, 0.1))" }; $[0] = t1; } else { t1 = $[0]; } let t2; if ($[1] !== currentIndex || $[2] !== onPageSelect || $[3] !== pages) { let t32; if ($[5] !== currentIndex || $[6] !== onPageSelect) { t32 = (page, idx) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { onClick: () => onPageSelect(idx), className: "w-2.5 h-2.5 rounded-full transition-all hover:scale-110", style: { backgroundColor: idx === currentIndex ? "var(--hud-accent, #3b82f6)" : "var(--hud-border, rgba(255, 255, 255, 0.3))", transform: idx === currentIndex ? "scale(1.3)" : "scale(1)" }, title: page.label }, page.key); $[5] = currentIndex; $[6] = onPageSelect; $[7] = t32; } else { t32 = $[7]; } t2 = pages.map(t32); $[1] = currentIndex; $[2] = onPageSelect; $[3] = pages; $[4] = t2; } else { t2 = $[4]; } let t3; if ($[8] !== t2) { t3 = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex items-center gap-2", children: t2 }); $[8] = t2; $[9] = t3; } else { t3 = $[9]; } let t4; if ($[10] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t4 = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-4 text-xs", style: { color: "var(--hud-text, #fff)", opacity: 0.5 }, children: [/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: "\u2190 \u2192 Navigate" }), /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: "ESC Exit" })] }); $[10] = t4; } else { t4 = $[10]; } let t5; if ($[11] !== t3) { t5 = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("footer", { className: "flex items-center justify-center gap-8 py-4 border-t", style: t1, children: [t3, t4] }); $[11] = t3; $[12] = t5; } else { t5 = $[12]; } return t5; }; var DefaultWrapper = (t0) => { const $ = (0, import_compiler_runtime52.c)(2); const { children } = t0; let t1; if ($[0] !== children) { t1 = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_jsx_runtime22.Fragment, { children }); $[0] = children; $[1] = t1; } else { t1 = $[1]; } return t1; }; function LockedNodeOverlay(t0) { const $ = (0, import_compiler_runtime52.c)(50); const { pages, renderContent, renderCloseButton, renderFooter, WrapperComponent: t1, className: t2, style } = t0; const WrapperComponent = t1 === void 0 ? DefaultWrapper : t1; const className = t2 === void 0 ? "" : t2; const lockedNodeId = (0, import_jotai77.useAtomValue)(lockedNodeIdAtom); const nodeData = (0, import_jotai77.useAtomValue)(lockedNodeDataAtom); const pageIndex = (0, import_jotai77.useAtomValue)(lockedNodePageIndexAtom); const setPageCount = (0, import_jotai77.useSetAtom)(lockedNodePageCountAtom); const unlock = (0, import_jotai77.useSetAtom)(unlockNodeAtom); let t3; let t4; if ($[0] !== pages.length || $[1] !== setPageCount) { t3 = () => { setPageCount(pages.length); }; t4 = [pages.length, setPageCount]; $[0] = pages.length; $[1] = setPageCount; $[2] = t3; $[3] = t4; } else { t3 = $[2]; t4 = $[3]; } (0, import_react44.useEffect)(t3, t4); const pageIndexSetter = (0, import_jotai77.useSetAtom)(lockedNodePageIndexAtom); let t5; if ($[4] !== pageIndexSetter || $[5] !== pages.length) { t5 = () => { pageIndexSetter((current) => (current + 1) % pages.length); }; $[4] = pageIndexSetter; $[5] = pages.length; $[6] = t5; } else { t5 = $[6]; } const nextPage = t5; let t6; if ($[7] !== pageIndexSetter || $[8] !== pages.length) { t6 = () => { pageIndexSetter((current_0) => (current_0 - 1 + pages.length) % pages.length); }; $[7] = pageIndexSetter; $[8] = pages.length; $[9] = t6; } else { t6 = $[9]; } const prevPage = t6; let t7; if ($[10] !== pageIndexSetter || $[11] !== pages.length) { t7 = (index_0) => { if (index_0 >= 0 && index_0 < pages.length) { pageIndexSetter(index_0); } }; $[10] = pageIndexSetter; $[11] = pages.length; $[12] = t7; } else { t7 = $[12]; } const handleGoToPage = t7; let t8; let t9; if ($[13] !== lockedNodeId || $[14] !== nextPage || $[15] !== prevPage || $[16] !== unlock) { t8 = () => { if (!lockedNodeId) { return; } const handleKeyDown = (e) => { const target = e.target; if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) { return; } bb46: switch (e.key) { case "Escape": { e.preventDefault(); unlock(); break bb46; } case "ArrowRight": case "ArrowDown": { e.preventDefault(); nextPage(); break bb46; } case "ArrowLeft": case "ArrowUp": { e.preventDefault(); prevPage(); } } }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }; t9 = [lockedNodeId, unlock, nextPage, prevPage]; $[13] = lockedNodeId; $[14] = nextPage; $[15] = prevPage; $[16] = unlock; $[17] = t8; $[18] = t9; } else { t8 = $[17]; t9 = $[18]; } (0, import_react44.useEffect)(t8, t9); if (!lockedNodeId || !nodeData) { return null; } const currentPage = pages[pageIndex]?.key || pages[0]?.key; const totalPages = pages.length; let T0; let t10; let t11; let t12; let t13; if ($[19] !== WrapperComponent || $[20] !== className || $[21] !== currentPage || $[22] !== handleGoToPage || $[23] !== lockedNodeId || $[24] !== nextPage || $[25] !== nodeData || $[26] !== pageIndex || $[27] !== pages || $[28] !== prevPage || $[29] !== renderCloseButton || $[30] !== renderContent || $[31] !== renderFooter || $[32] !== style || $[33] !== totalPages || $[34] !== unlock) { const ctx = { nodeId: lockedNodeId, nodeData, currentPage, pageIndex, totalPages, close: unlock, nextPage, prevPage, goToPage: handleGoToPage }; T0 = WrapperComponent; t10 = `fixed inset-0 z-[9999] flex flex-col ${className}`; if ($[40] !== style) { t11 = { backgroundColor: "#0f0f14", isolation: "isolate", ...style }; $[40] = style; $[41] = t11; } else { t11 = $[41]; } t12 = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [renderCloseButton ? renderCloseButton(ctx) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DefaultCloseButton, { onClick: unlock }), renderContent(ctx)] }); t13 = renderFooter ? renderFooter(ctx) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DefaultFooter, { pages, currentIndex: pageIndex, onPageSelect: handleGoToPage }); $[19] = WrapperComponent; $[20] = className; $[21] = currentPage; $[22] = handleGoToPage; $[23] = lockedNodeId; $[24] = nextPage; $[25] = nodeData; $[26] = pageIndex; $[27] = pages; $[28] = prevPage; $[29] = renderCloseButton; $[30] = renderContent; $[31] = renderFooter; $[32] = style; $[33] = totalPages; $[34] = unlock; $[35] = T0; $[36] = t10; $[37] = t11; $[38] = t12; $[39] = t13; } else { T0 = $[35]; t10 = $[36]; t11 = $[37]; t12 = $[38]; t13 = $[39]; } let t14; if ($[42] !== t10 || $[43] !== t11 || $[44] !== t12 || $[45] !== t13) { t14 = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: t10, style: t11, children: [t12, t13] }); $[42] = t10; $[43] = t11; $[44] = t12; $[45] = t13; $[46] = t14; } else { t14 = $[46]; } let t15; if ($[47] !== T0 || $[48] !== t14) { t15 = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(T0, { children: t14 }); $[47] = T0; $[48] = t14; $[49] = t15; } else { t15 = $[49]; } return t15; } // src/components/EdgeOverlay.tsx var import_react47 = __toESM(require("react")); var import_jotai78 = require("jotai"); var import_react48 = require("@use-gesture/react"); init_graph_store(); init_graph_position(); init_graph_mutations(); init_graph_mutations_edges(); init_selection_store(); // src/components/EdgePath.tsx var import_compiler_runtime53 = require("react/compiler-runtime"); var import_react45 = __toESM(require("react")); var import_jsx_runtime23 = require("react/jsx-runtime"); function EdgePath(t0) { const $ = (0, import_compiler_runtime53.c)(7); const { handleRef, className, style } = t0; let t1; if ($[0] !== style) { t1 = { position: "absolute", top: "50%", right: "-22px", height: "44px", width: "44px", transform: "translateY(-50%)", cursor: "crosshair", display: "flex", alignItems: "center", justifyContent: "center", touchAction: "none", ...style }; $[0] = style; $[1] = t1; } else { t1 = $[1]; } let t2; if ($[2] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t2 = /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { height: "32px", width: "12px", borderRadius: "3px", backgroundColor: "var(--list-item-border, #666)", transition: "background-color 0.2s", pointerEvents: "none" } }); $[2] = t2; } else { t2 = $[2]; } let t3; if ($[3] !== className || $[4] !== handleRef || $[5] !== t1) { t3 = /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ref: handleRef, className, style: t1, onPointerEnter: _temp11, onPointerLeave: _temp24, children: t2 }); $[3] = className; $[4] = handleRef; $[5] = t1; $[6] = t3; } else { t3 = $[6]; } return t3; } function _temp24(e_0) { const visual_0 = e_0.currentTarget.firstChild; if (visual_0) { visual_0.style.backgroundColor = "var(--list-item-border, #666)"; } } function _temp11(e) { const visual = e.currentTarget.firstChild; if (visual) { visual.style.backgroundColor = "var(--list-item-border-active, #888)"; } } // src/components/EdgeOverlay.tsx var import_jsx_runtime25 = require("react/jsx-runtime"); // src/components/EdgeLabel.tsx var import_compiler_runtime54 = require("react/compiler-runtime"); var import_react46 = __toESM(require("react")); var import_jsx_runtime24 = require("react/jsx-runtime"); function EdgeLabel(t0) { const $ = (0, import_compiler_runtime54.c)(13); const { label, x, y, onClick, className, style } = t0; if (!label) { return null; } let t1; if ($[0] !== onClick) { t1 = (e) => { e.stopPropagation(); onClick?.(); }; $[0] = onClick; $[1] = t1; } else { t1 = $[1]; } const t2 = onClick ? "pointer" : "default"; const t3 = onClick ? "auto" : "none"; let t4; if ($[2] !== style || $[3] !== t2 || $[4] !== t3 || $[5] !== x || $[6] !== y) { t4 = { position: "absolute", left: x, top: y, transform: "translate(-50%, -50%)", padding: "2px 8px", fontSize: "12px", fontWeight: 500, color: "var(--canvas-text, #fff)", backgroundColor: "var(--canvas-bg, #1a1a1a)", border: "1px solid var(--canvas-border, #333)", borderRadius: "4px", whiteSpace: "nowrap", cursor: t2, userSelect: "none", pointerEvents: t3, ...style }; $[2] = style; $[3] = t2; $[4] = t3; $[5] = x; $[6] = y; $[7] = t4; } else { t4 = $[7]; } let t5; if ($[8] !== className || $[9] !== label || $[10] !== t1 || $[11] !== t4) { t5 = /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className, onClick: t1, style: t4, children: label }); $[8] = className; $[9] = label; $[10] = t1; $[11] = t4; $[12] = t5; } else { t5 = $[12]; } return t5; } // src/components/EdgeOverlay.tsx var defaultGetSourceHandle = (nodeId) => `widget:${nodeId}:output:data`; var defaultGetTargetHandle = (targetNodeId) => `widget:${targetNodeId}:input:default`; function EdgeOverlay({ nodeId, children, getSourceHandle = defaultGetSourceHandle, getTargetHandle = defaultGetTargetHandle, onEdgeCreate, onEdgeCreateError, onEdgeCreated, handleStyle, handleClassName, canConnect }) { const setEdgeCreationState = (0, import_jotai78.useSetAtom)(edgeCreationAtom); const graph = (0, import_jotai78.useAtomValue)(graphAtom); const addEdgeToLocalGraph = (0, import_jotai78.useSetAtom)(addEdgeToLocalGraphAtom); const removeEdgeFromLocalGraph = (0, import_jotai78.useSetAtom)(removeEdgeFromLocalGraphAtom); const swapEdgeAtomic = (0, import_jotai78.useSetAtom)(swapEdgeAtomicAtom); const addNodeToSelection = (0, import_jotai78.useSetAtom)(addNodesToSelectionAtom); const removeNodeFromSelection = (0, import_jotai78.useSetAtom)(removeNodesFromSelectionAtom); const currentGraphId = (0, import_jotai78.useAtomValue)(currentGraphIdAtom); const handleRef = (0, import_react47.useRef)(null); const wrapperRef = (0, import_react47.useRef)(null); const position = (0, import_jotai78.useAtomValue)(nodePositionAtomFamily(nodeId)); const nodeAttributes = graph.hasNode(nodeId) ? graph.getNodeAttributes(nodeId) : void 0; const width = nodeAttributes?.width ?? 500; const height = nodeAttributes?.height ?? 500; const handleDragStart = (x, y) => { if (!graph.hasNode(nodeId)) return; const sourceNodeAttributes = graph.getNodeAttributes(nodeId); if (!sourceNodeAttributes) return; const nodeRect = wrapperRef.current?.querySelector('[data-draggable-node="true"]')?.getBoundingClientRect(); if (!nodeRect) return; const sourceNodePosition = { x: sourceNodeAttributes.x + nodeRect.width / 2, y: sourceNodeAttributes.y + nodeRect.height / 2 }; const sourceHandle = getSourceHandle(nodeId); setEdgeCreationState({ isCreating: true, sourceNodeId: nodeId, sourceNodePosition, targetPosition: { x, y }, hoveredTargetNodeId: null, sourceHandle, targetHandle: null, sourcePort: null, targetPort: null, snappedTargetPosition: null }); }; const handleDrag = (x_0, y_0) => { setEdgeCreationState((prev) => { if (!prev.isCreating) return prev; const { nodeId: hitNodeId } = hitTestNode(x_0, y_0); let hoveredNodeId = null; let targetPort = null; const portInfo = hitTestPort(x_0, y_0); if (portInfo && portInfo.nodeId !== nodeId) { hoveredNodeId = portInfo.nodeId; targetPort = portInfo.portId; } else if (hitNodeId) { hoveredNodeId = hitNodeId; } if (hoveredNodeId && hoveredNodeId !== nodeId && hoveredNodeId !== prev.hoveredTargetNodeId) { if (prev.hoveredTargetNodeId) { removeNodeFromSelection([prev.hoveredTargetNodeId]); } addNodeToSelection([hoveredNodeId]); } return { ...prev, targetPosition: { x: x_0, y: y_0 }, hoveredTargetNodeId: hoveredNodeId && hoveredNodeId !== nodeId ? hoveredNodeId : null, targetPort }; }); }; const handleDragEnd = async (x_1, y_1) => { let targetNodeId = null; const { nodeId: hitNodeId_0 } = hitTestNode(x_1, y_1); if (hitNodeId_0) { targetNodeId = hitNodeId_0; } setEdgeCreationState((prev_0) => { if (prev_0.hoveredTargetNodeId) { removeNodeFromSelection([prev_0.hoveredTargetNodeId]); } return { isCreating: false, sourceNodeId: null, sourceNodePosition: null, targetPosition: null, hoveredTargetNodeId: null, sourceHandle: null, targetHandle: null, sourcePort: null, targetPort: null, snappedTargetPosition: null }; }); if (targetNodeId && targetNodeId !== nodeId && currentGraphId) { if (canConnect) { const allowed = canConnect({ nodeId, portId: null }, { nodeId: targetNodeId, portId: null }); if (!allowed) return; } const sourceHandle_0 = getSourceHandle(nodeId); const targetHandle = getTargetHandle(targetNodeId); const tempEdgeId = crypto.randomUUID(); const tempEdge = { id: tempEdgeId, graph_id: currentGraphId, source_node_id: nodeId, target_node_id: targetNodeId, edge_type: "data_connection", filter_condition: null, ui_properties: { style: "solid", color: "#999", sourceHandle: sourceHandle_0, targetHandle }, created_at: (/* @__PURE__ */ new Date()).toISOString(), updated_at: (/* @__PURE__ */ new Date()).toISOString() }; addEdgeToLocalGraph(tempEdge); if (!onEdgeCreate) { return; } try { const newEdge = await onEdgeCreate({ graphId: currentGraphId, sourceNodeId: nodeId, targetNodeId, sourceHandle: sourceHandle_0, targetHandle, tempEdge }); swapEdgeAtomic({ tempEdgeId, newEdge }); onEdgeCreated?.({ edge: newEdge, sourceNodeId: nodeId, targetNodeId, sourceHandle: sourceHandle_0, targetHandle }); } catch (error) { removeEdgeFromLocalGraph(tempEdgeId); onEdgeCreateError?.(error, tempEdgeId); } } }; (0, import_react48.useGesture)({ onDragStart: ({ event, xy: [x_2, y_2] }) => { event.stopPropagation(); handleDragStart(x_2, y_2); }, onDrag: ({ xy: [x_3, y_3] }) => { handleDrag(x_3, y_3); }, onDragEnd: ({ xy: [x_4, y_4] }) => { handleDragEnd(x_4, y_4); } }, { target: handleRef, eventOptions: { passive: false } }); if (!position || !nodeAttributes) { return null; } return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { ref: wrapperRef, style: { position: "absolute", top: position.y, left: position.x, width: `${width}px`, height: `${height}px` }, children: [children, /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(EdgePath, { handleRef, className: handleClassName, style: handleStyle })] }); } // src/components/NodeTypeCombobox.tsx var import_compiler_runtime57 = require("react/compiler-runtime"); var import_react51 = __toESM(require("react")); var import_jotai79 = require("jotai"); init_node_type_registry(); init_core(); init_debug(); // src/components/ComboboxSearch.tsx var import_compiler_runtime55 = require("react/compiler-runtime"); var import_react49 = __toESM(require("react")); var import_jsx_runtime26 = require("react/jsx-runtime"); var defaultInputStyle = { width: "100%", padding: "8px 12px", fontSize: "14px", border: "1px solid #ddd", borderRadius: "6px", outline: "none", backgroundColor: "#fff" }; function ComboboxSearch(t0) { const $ = (0, import_compiler_runtime55.c)(14); const { inputRef, value, onChange, onKeyDown, onFocus, placeholder: t1, disabled: t2, isOpen, style } = t0; const placeholder = t1 === void 0 ? "Search..." : t1; const disabled = t2 === void 0 ? false : t2; let t3; if ($[0] !== onChange) { t3 = (e) => onChange(e.target.value); $[0] = onChange; $[1] = t3; } else { t3 = $[1]; } let t4; if ($[2] !== style) { t4 = { ...defaultInputStyle, ...style }; $[2] = style; $[3] = t4; } else { t4 = $[3]; } let t5; if ($[4] !== disabled || $[5] !== inputRef || $[6] !== isOpen || $[7] !== onFocus || $[8] !== onKeyDown || $[9] !== placeholder || $[10] !== t3 || $[11] !== t4 || $[12] !== value) { t5 = /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("input", { ref: inputRef, type: "text", value, onChange: t3, onFocus, onKeyDown, placeholder, disabled, style: t4, "aria-expanded": isOpen, "aria-haspopup": "listbox", "aria-autocomplete": "list", role: "combobox" }); $[4] = disabled; $[5] = inputRef; $[6] = isOpen; $[7] = onFocus; $[8] = onKeyDown; $[9] = placeholder; $[10] = t3; $[11] = t4; $[12] = value; $[13] = t5; } else { t5 = $[13]; } return t5; } // src/components/ComboboxOption.tsx var import_compiler_runtime56 = require("react/compiler-runtime"); var import_react50 = __toESM(require("react")); var import_jsx_runtime27 = require("react/jsx-runtime"); var styles = { option: { display: "flex", alignItems: "center", gap: "8px", padding: "8px 12px", cursor: "pointer", transition: "background-color 0.1s" }, optionHighlighted: { backgroundColor: "#f0f0f0" }, icon: { fontSize: "16px", width: "20px", textAlign: "center" }, content: { display: "flex", flexDirection: "column", gap: "2px" }, label: { fontSize: "14px", fontWeight: 500 }, description: { fontSize: "12px", color: "#666" } }; function ComboboxOption(t0) { const $ = (0, import_compiler_runtime56.c)(20); const { label, icon, description, highlighted: t1, onClick, onMouseEnter } = t0; const highlighted = t1 === void 0 ? false : t1; let t2; if ($[0] !== highlighted) { t2 = highlighted ? styles.optionHighlighted : {}; $[0] = highlighted; $[1] = t2; } else { t2 = $[1]; } let t3; if ($[2] !== t2) { t3 = { ...styles.option, ...t2 }; $[2] = t2; $[3] = t3; } else { t3 = $[3]; } let t4; if ($[4] !== icon) { t4 = icon && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { style: styles.icon, children: icon }); $[4] = icon; $[5] = t4; } else { t4 = $[5]; } let t5; if ($[6] !== label) { t5 = /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { style: styles.label, children: label }); $[6] = label; $[7] = t5; } else { t5 = $[7]; } let t6; if ($[8] !== description) { t6 = description && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { style: styles.description, children: description }); $[8] = description; $[9] = t6; } else { t6 = $[9]; } let t7; if ($[10] !== t5 || $[11] !== t6) { t7 = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { style: styles.content, children: [t5, t6] }); $[10] = t5; $[11] = t6; $[12] = t7; } else { t7 = $[12]; } let t8; if ($[13] !== highlighted || $[14] !== onClick || $[15] !== onMouseEnter || $[16] !== t3 || $[17] !== t4 || $[18] !== t7) { t8 = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("li", { onClick, onMouseEnter, style: t3, role: "option", "aria-selected": highlighted, children: [t4, t7] }); $[13] = highlighted; $[14] = onClick; $[15] = onMouseEnter; $[16] = t3; $[17] = t4; $[18] = t7; $[19] = t8; } else { t8 = $[19]; } return t8; } // src/components/NodeTypeCombobox.tsx var import_jsx_runtime28 = require("react/jsx-runtime"); var debug21 = createDebug("node-type-combobox"); function NodeTypeCombobox(t0) { const $ = (0, import_compiler_runtime57.c)(53); const { nodeTypes: customNodeTypes, onNodeCreated, createPosition: t1, placeholder: t2, clearOnCreate: t3, className, style, disabled: t4 } = t0; const createPosition = t1 === void 0 ? "center" : t1; const placeholder = t2 === void 0 ? "Add node..." : t2; const clearOnCreate = t3 === void 0 ? true : t3; const disabled = t4 === void 0 ? false : t4; const [isOpen, setIsOpen] = (0, import_react51.useState)(false); const [searchQuery, setSearchQuery] = (0, import_react51.useState)(""); const [highlightedIndex, setHighlightedIndex] = (0, import_react51.useState)(0); const inputRef = (0, import_react51.useRef)(null); const listRef = (0, import_react51.useRef)(null); const createNode = useCreateNode(); const graphId = (0, import_jotai79.useAtomValue)(currentGraphIdAtom); const viewportRect = (0, import_jotai79.useAtomValue)(viewportRectAtom); const pan = (0, import_jotai79.useAtomValue)(panAtom); const zoom = (0, import_jotai79.useAtomValue)(zoomAtom); let t5; if ($[0] !== customNodeTypes || $[1] !== searchQuery) { const nodeTypes = customNodeTypes ?? getRegisteredNodeTypes().map(_temp12); bb0: { if (!searchQuery.trim()) { t5 = nodeTypes; break bb0; } const query = searchQuery.toLowerCase(); t5 = nodeTypes.filter((opt) => opt.type.toLowerCase().includes(query) || opt.label?.toLowerCase().includes(query) || opt.description?.toLowerCase().includes(query)); } $[0] = customNodeTypes; $[1] = searchQuery; $[2] = t5; } else { t5 = $[2]; } const filteredTypes = t5; let t6; if ($[3] !== createPosition || $[4] !== pan || $[5] !== viewportRect?.height || $[6] !== viewportRect?.width || $[7] !== zoom) { t6 = () => { if (typeof createPosition === "object") { return createPosition; } const vpWidth = viewportRect?.width ?? 800; const vpHeight = viewportRect?.height ?? 600; if (createPosition === "random") { const centerX = (vpWidth / 2 - pan.x) / zoom; const centerY = (vpHeight / 2 - pan.y) / zoom; const offsetX = (Math.random() - 0.5) * 400; const offsetY = (Math.random() - 0.5) * 400; return { x: centerX + offsetX, y: centerY + offsetY }; } const centerX_0 = (vpWidth / 2 - pan.x) / zoom; const centerY_0 = (vpHeight / 2 - pan.y) / zoom; return { x: centerX_0, y: centerY_0 }; }; $[3] = createPosition; $[4] = pan; $[5] = viewportRect?.height; $[6] = viewportRect?.width; $[7] = zoom; $[8] = t6; } else { t6 = $[8]; } const getCreatePosition = t6; let t7; if ($[9] !== clearOnCreate || $[10] !== createNode || $[11] !== getCreatePosition || $[12] !== graphId || $[13] !== onNodeCreated) { t7 = (option) => { if (!graphId) { debug21.warn("No graphId available"); return; } const position = getCreatePosition(); const width = option.defaultWidth ?? 200; const height = option.defaultHeight ?? 150; createNode.mutate({ graph_id: graphId, label: option.label || option.type, node_type: option.type, configuration: null, ui_properties: { x: position.x - width / 2, y: position.y - height / 2, width, height } }, { onSuccess: (data) => { onNodeCreated?.(data.id, option.type); } }); if (clearOnCreate) { setSearchQuery(""); } setIsOpen(false); }; $[9] = clearOnCreate; $[10] = createNode; $[11] = getCreatePosition; $[12] = graphId; $[13] = onNodeCreated; $[14] = t7; } else { t7 = $[14]; } const handleSelect = t7; let t8; if ($[15] !== filteredTypes || $[16] !== handleSelect || $[17] !== highlightedIndex || $[18] !== isOpen) { t8 = (e) => { if (!isOpen) { if (e.key === "ArrowDown" || e.key === "Enter") { setIsOpen(true); e.preventDefault(); } return; } bb121: switch (e.key) { case "ArrowDown": { e.preventDefault(); setHighlightedIndex((prev_0) => Math.min(prev_0 + 1, filteredTypes.length - 1)); break bb121; } case "ArrowUp": { e.preventDefault(); setHighlightedIndex(_temp25); break bb121; } case "Enter": { e.preventDefault(); if (filteredTypes[highlightedIndex]) { handleSelect(filteredTypes[highlightedIndex]); } break bb121; } case "Escape": { e.preventDefault(); setIsOpen(false); } } }; $[15] = filteredTypes; $[16] = handleSelect; $[17] = highlightedIndex; $[18] = isOpen; $[19] = t8; } else { t8 = $[19]; } const handleKeyDown = t8; let t9; if ($[20] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t9 = () => { setHighlightedIndex(0); }; $[20] = t9; } else { t9 = $[20]; } let t10; if ($[21] !== filteredTypes.length) { t10 = [filteredTypes.length]; $[21] = filteredTypes.length; $[22] = t10; } else { t10 = $[22]; } (0, import_react51.useEffect)(t9, t10); let t11; let t12; if ($[23] !== highlightedIndex || $[24] !== isOpen) { t11 = () => { if (isOpen && listRef.current) { const highlightedElement = listRef.current.children[highlightedIndex]; highlightedElement?.scrollIntoView({ block: "nearest" }); } }; t12 = [highlightedIndex, isOpen]; $[23] = highlightedIndex; $[24] = isOpen; $[25] = t11; $[26] = t12; } else { t11 = $[25]; t12 = $[26]; } (0, import_react51.useEffect)(t11, t12); let t13; let t14; if ($[27] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t13 = () => { const handleClickOutside = (e_0) => { if (inputRef.current && !inputRef.current.parentElement?.contains(e_0.target)) { setIsOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }; t14 = []; $[27] = t13; $[28] = t14; } else { t13 = $[27]; t14 = $[28]; } (0, import_react51.useEffect)(t13, t14); let t15; if ($[29] !== style) { t15 = { ...styles2.container, ...style }; $[29] = style; $[30] = t15; } else { t15 = $[30]; } let t16; let t17; if ($[31] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t16 = (v) => { setSearchQuery(v); setIsOpen(true); }; t17 = () => setIsOpen(true); $[31] = t16; $[32] = t17; } else { t16 = $[31]; t17 = $[32]; } let t18; if ($[33] !== disabled || $[34] !== handleKeyDown || $[35] !== isOpen || $[36] !== placeholder || $[37] !== searchQuery) { t18 = /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ComboboxSearch, { inputRef, value: searchQuery, onChange: t16, onFocus: t17, onKeyDown: handleKeyDown, placeholder, disabled, isOpen }); $[33] = disabled; $[34] = handleKeyDown; $[35] = isOpen; $[36] = placeholder; $[37] = searchQuery; $[38] = t18; } else { t18 = $[38]; } let t19; if ($[39] !== filteredTypes || $[40] !== handleSelect || $[41] !== highlightedIndex || $[42] !== isOpen) { t19 = isOpen && filteredTypes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("ul", { ref: listRef, style: styles2.dropdown, role: "listbox", children: filteredTypes.map((option_0, index) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ComboboxOption, { type: option_0.type, label: option_0.label || option_0.type, icon: option_0.icon, description: option_0.description, highlighted: index === highlightedIndex, onClick: () => handleSelect(option_0), onMouseEnter: () => setHighlightedIndex(index) }, option_0.type)) }); $[39] = filteredTypes; $[40] = handleSelect; $[41] = highlightedIndex; $[42] = isOpen; $[43] = t19; } else { t19 = $[43]; } let t20; if ($[44] !== filteredTypes.length || $[45] !== isOpen) { t20 = isOpen && filteredTypes.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { style: styles2.emptyState, children: "No node types found" }); $[44] = filteredTypes.length; $[45] = isOpen; $[46] = t20; } else { t20 = $[46]; } let t21; if ($[47] !== className || $[48] !== t15 || $[49] !== t18 || $[50] !== t19 || $[51] !== t20) { t21 = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className, style: t15, children: [t18, t19, t20] }); $[47] = className; $[48] = t15; $[49] = t18; $[50] = t19; $[51] = t20; $[52] = t21; } else { t21 = $[52]; } return t21; } function _temp25(prev) { return Math.max(prev - 1, 0); } function _temp12(type) { return { type, label: formatTypeLabel(type) }; } function formatTypeLabel(type) { return type.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" "); } var styles2 = { container: { position: "relative", width: "200px" }, input: { width: "100%", padding: "8px 12px", fontSize: "14px", border: "1px solid #ddd", borderRadius: "6px", outline: "none", backgroundColor: "#fff" }, dropdown: { position: "absolute", top: "100%", left: 0, right: 0, marginTop: "4px", padding: "4px 0", backgroundColor: "#fff", border: "1px solid #ddd", borderRadius: "6px", boxShadow: "0 4px 12px rgba(0,0,0,0.15)", maxHeight: "240px", overflowY: "auto", zIndex: 1e3, listStyle: "none" }, option: { display: "flex", alignItems: "center", gap: "8px", padding: "8px 12px", cursor: "pointer", transition: "background-color 0.1s" }, optionHighlighted: { backgroundColor: "#f0f0f0" }, icon: { fontSize: "16px", width: "20px", textAlign: "center" }, optionContent: { display: "flex", flexDirection: "column", gap: "2px" }, optionLabel: { fontSize: "14px", fontWeight: 500 }, optionDescription: { fontSize: "12px", color: "#666" }, emptyState: { position: "absolute", top: "100%", left: 0, right: 0, marginTop: "4px", padding: "12px", backgroundColor: "#fff", border: "1px solid #ddd", borderRadius: "6px", boxShadow: "0 4px 12px rgba(0,0,0,0.15)", color: "#666", fontSize: "14px", textAlign: "center" } }; // src/components/SettingsPanel.tsx var import_compiler_runtime60 = require("react/compiler-runtime"); var import_react54 = __toESM(require("react")); // src/components/SettingsPresets.tsx var import_compiler_runtime58 = require("react/compiler-runtime"); var import_react52 = __toESM(require("react")); var import_jsx_runtime29 = require("react/jsx-runtime"); function SettingsPresets(t0) { const $ = (0, import_compiler_runtime58.c)(36); const { className, selectClassName, buttonClassName, buttonsClassName, inputClassName } = t0; const { allPresets, activePresetId, hasUnsavedChanges, applyPreset, saveAsPreset, deletePreset, resetSettings } = useCanvasSettings(); const [showSaveDialog, setShowSaveDialog] = (0, import_react52.useState)(false); const [newPresetName, setNewPresetName] = (0, import_react52.useState)(""); let t1; if ($[0] !== newPresetName || $[1] !== saveAsPreset) { t1 = () => { if (newPresetName.trim()) { saveAsPreset(newPresetName.trim()); setNewPresetName(""); setShowSaveDialog(false); } }; $[0] = newPresetName; $[1] = saveAsPreset; $[2] = t1; } else { t1 = $[2]; } const handleSave = t1; let t2; if ($[3] !== activePresetId || $[4] !== allPresets || $[5] !== deletePreset) { t2 = () => { if (activePresetId && !allPresets.find((p) => p.id === activePresetId)?.isBuiltIn) { deletePreset(activePresetId); } }; $[3] = activePresetId; $[4] = allPresets; $[5] = deletePreset; $[6] = t2; } else { t2 = $[6]; } const handleDelete = t2; const t3 = activePresetId || ""; let t4; if ($[7] !== applyPreset) { t4 = (e) => e.target.value && applyPreset(e.target.value); $[7] = applyPreset; $[8] = t4; } else { t4 = $[8]; } const t5 = hasUnsavedChanges ? "Custom (modified)" : "Select preset..."; let t6; if ($[9] !== t5) { t6 = /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("option", { value: "", disabled: true, children: t5 }); $[9] = t5; $[10] = t6; } else { t6 = $[10]; } let t7; if ($[11] !== allPresets) { t7 = allPresets.map(_temp13); $[11] = allPresets; $[12] = t7; } else { t7 = $[12]; } let t8; if ($[13] !== selectClassName || $[14] !== t3 || $[15] !== t4 || $[16] !== t6 || $[17] !== t7) { t8 = /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("select", { className: selectClassName, value: t3, onChange: t4, children: [t6, t7] }); $[13] = selectClassName; $[14] = t3; $[15] = t4; $[16] = t6; $[17] = t7; $[18] = t8; } else { t8 = $[18]; } let t9; if ($[19] !== activePresetId || $[20] !== allPresets || $[21] !== buttonClassName || $[22] !== handleDelete || $[23] !== handleSave || $[24] !== inputClassName || $[25] !== newPresetName || $[26] !== resetSettings || $[27] !== showSaveDialog) { t9 = showSaveDialog ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("input", { type: "text", className: inputClassName, placeholder: "Preset name", value: newPresetName, onChange: (e_0) => setNewPresetName(e_0.target.value), onKeyDown: (e_1) => e_1.key === "Enter" && handleSave(), autoFocus: true }), /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { className: buttonClassName, onClick: handleSave, children: "Save" }), /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { className: buttonClassName, onClick: () => setShowSaveDialog(false), children: "Cancel" })] }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { className: buttonClassName, onClick: () => setShowSaveDialog(true), children: "Save as..." }), activePresetId && !allPresets.find((p_0) => p_0.id === activePresetId)?.isBuiltIn && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { className: buttonClassName, onClick: handleDelete, children: "Delete" }), /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { className: buttonClassName, onClick: resetSettings, children: "Reset" })] }); $[19] = activePresetId; $[20] = allPresets; $[21] = buttonClassName; $[22] = handleDelete; $[23] = handleSave; $[24] = inputClassName; $[25] = newPresetName; $[26] = resetSettings; $[27] = showSaveDialog; $[28] = t9; } else { t9 = $[28]; } let t10; if ($[29] !== buttonsClassName || $[30] !== t9) { t10 = /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: buttonsClassName, children: t9 }); $[29] = buttonsClassName; $[30] = t9; $[31] = t10; } else { t10 = $[31]; } let t11; if ($[32] !== className || $[33] !== t10 || $[34] !== t8) { t11 = /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className, children: [t8, t10] }); $[32] = className; $[33] = t10; $[34] = t8; $[35] = t11; } else { t11 = $[35]; } return t11; } function _temp13(preset) { return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("option", { value: preset.id, children: [preset.name, preset.isBuiltIn ? "" : " (custom)"] }, preset.id); } // src/components/SettingsEventMap.tsx var import_compiler_runtime59 = require("react/compiler-runtime"); var import_react53 = __toESM(require("react")); init_settings_types(); init_action_registry(); var import_jsx_runtime30 = require("react/jsx-runtime"); function EventMappingRow(t0) { const $ = (0, import_compiler_runtime59.c)(28); const { event, currentActionId, onActionChange, rowClassName, labelClassName, selectClassName } = t0; let t1; let t2; let t3; let t4; let t5; let t6; if ($[0] !== currentActionId || $[1] !== event.label || $[2] !== event.type || $[3] !== labelClassName || $[4] !== onActionChange || $[5] !== rowClassName || $[6] !== selectClassName) { const categories = getActionsByCategories(); t5 = rowClassName; if ($[13] !== event.label || $[14] !== labelClassName) { t6 = /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: labelClassName, children: event.label }); $[13] = event.label; $[14] = labelClassName; $[15] = t6; } else { t6 = $[15]; } t1 = selectClassName; t2 = currentActionId; if ($[16] !== event.type || $[17] !== onActionChange) { t3 = (e) => onActionChange(event.type, e.target.value); $[16] = event.type; $[17] = onActionChange; $[18] = t3; } else { t3 = $[18]; } t4 = categories.map(_temp26); $[0] = currentActionId; $[1] = event.label; $[2] = event.type; $[3] = labelClassName; $[4] = onActionChange; $[5] = rowClassName; $[6] = selectClassName; $[7] = t1; $[8] = t2; $[9] = t3; $[10] = t4; $[11] = t5; $[12] = t6; } else { t1 = $[7]; t2 = $[8]; t3 = $[9]; t4 = $[10]; t5 = $[11]; t6 = $[12]; } let t7; if ($[19] !== t1 || $[20] !== t2 || $[21] !== t3 || $[22] !== t4) { t7 = /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("select", { className: t1, value: t2, onChange: t3, children: t4 }); $[19] = t1; $[20] = t2; $[21] = t3; $[22] = t4; $[23] = t7; } else { t7 = $[23]; } let t8; if ($[24] !== t5 || $[25] !== t6 || $[26] !== t7) { t8 = /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: t5, children: [t6, t7] }); $[24] = t5; $[25] = t6; $[26] = t7; $[27] = t8; } else { t8 = $[27]; } return t8; } function _temp26(category) { return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("optgroup", { label: category.label, children: category.actions.map(_temp14) }, category.category); } function _temp14(action) { return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("option", { value: action.id, children: action.label }, action.id); } function SettingsEventMap(t0) { const $ = (0, import_compiler_runtime59.c)(17); const { mappings, onActionChange, contentClassName, groupClassName, groupHeadingClassName, rowClassName, labelClassName, selectClassName } = t0; let t1; if ($[0] !== contentClassName || $[1] !== groupClassName || $[2] !== groupHeadingClassName || $[3] !== labelClassName || $[4] !== mappings || $[5] !== onActionChange || $[6] !== rowClassName || $[7] !== selectClassName) { const nodeEvents = Object.values(CanvasEventType).filter(_temp33); const edgeEvents = Object.values(CanvasEventType).filter(_temp42); const backgroundEvents = Object.values(CanvasEventType).filter(_temp52); let t2; if ($[9] !== groupClassName || $[10] !== groupHeadingClassName || $[11] !== labelClassName || $[12] !== mappings || $[13] !== onActionChange || $[14] !== rowClassName || $[15] !== selectClassName) { t2 = (heading, events) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: groupClassName, children: [/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("h3", { className: groupHeadingClassName, children: heading }), events.map((event_2) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(EventMappingRow, { event: EVENT_TYPE_INFO[event_2], currentActionId: mappings[event_2], onActionChange, rowClassName, labelClassName, selectClassName }, event_2))] }); $[9] = groupClassName; $[10] = groupHeadingClassName; $[11] = labelClassName; $[12] = mappings; $[13] = onActionChange; $[14] = rowClassName; $[15] = selectClassName; $[16] = t2; } else { t2 = $[16]; } const renderGroup = t2; t1 = /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: contentClassName, children: [renderGroup("Node Events", nodeEvents), renderGroup("Edge Events", edgeEvents), renderGroup("Background Events", backgroundEvents)] }); $[0] = contentClassName; $[1] = groupClassName; $[2] = groupHeadingClassName; $[3] = labelClassName; $[4] = mappings; $[5] = onActionChange; $[6] = rowClassName; $[7] = selectClassName; $[8] = t1; } else { t1 = $[8]; } return t1; } function _temp52(event_1) { return EVENT_TYPE_INFO[event_1].category === "background"; } function _temp42(event_0) { return EVENT_TYPE_INFO[event_0].category === "edge"; } function _temp33(event) { return EVENT_TYPE_INFO[event].category === "node"; } // src/components/SettingsPanel.tsx var import_jsx_runtime31 = require("react/jsx-runtime"); function QuickActions(t0) { const $ = (0, import_compiler_runtime60.c)(17); const { className, checkboxClassName, virtualizationClassName, virtualizationStatsClassName } = t0; const { enabled, toggle, visibleNodes, totalNodes, culledNodes } = useVirtualization(); let t1; if ($[0] !== checkboxClassName || $[1] !== enabled || $[2] !== toggle) { t1 = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("input", { type: "checkbox", className: checkboxClassName, checked: enabled, onChange: toggle }); $[0] = checkboxClassName; $[1] = enabled; $[2] = toggle; $[3] = t1; } else { t1 = $[3]; } let t2; if ($[4] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t2 = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: "Virtualization" }); $[4] = t2; } else { t2 = $[4]; } let t3; if ($[5] !== t1) { t3 = /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { children: [t1, t2] }); $[5] = t1; $[6] = t3; } else { t3 = $[6]; } const t4 = enabled ? `${visibleNodes}/${totalNodes} (${culledNodes} culled)` : "Off"; let t5; if ($[7] !== t4 || $[8] !== virtualizationStatsClassName) { t5 = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: virtualizationStatsClassName, children: t4 }); $[7] = t4; $[8] = virtualizationStatsClassName; $[9] = t5; } else { t5 = $[9]; } let t6; if ($[10] !== t3 || $[11] !== t5 || $[12] !== virtualizationClassName) { t6 = /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: virtualizationClassName, children: [t3, t5] }); $[10] = t3; $[11] = t5; $[12] = virtualizationClassName; $[13] = t6; } else { t6 = $[13]; } let t7; if ($[14] !== className || $[15] !== t6) { t7 = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className, children: t6 }); $[14] = className; $[15] = t6; $[16] = t7; } else { t7 = $[16]; } return t7; } function SettingsPanel(t0) { const $ = (0, import_compiler_runtime60.c)(35); const { className, headerClassName, contentClassName, footerClassName, rowClassName, labelClassName, selectClassName, buttonClassName, presetClassName, presetSelectClassName, presetButtonsClassName, checkboxClassName, groupClassName, groupHeadingClassName, quickActionsClassName, virtualizationClassName, virtualizationStatsClassName, inputClassName, renderHeader, renderFooter, onClose } = t0; const { mappings, setEventMapping } = useCanvasSettings(); let t1; if ($[0] !== buttonClassName || $[1] !== headerClassName || $[2] !== onClose || $[3] !== renderHeader) { t1 = renderHeader ? renderHeader() : /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: headerClassName, children: [/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("h2", { children: "Canvas Settings" }), onClose && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("button", { className: buttonClassName, onClick: onClose, children: "\xD7" })] }); $[0] = buttonClassName; $[1] = headerClassName; $[2] = onClose; $[3] = renderHeader; $[4] = t1; } else { t1 = $[4]; } const t2 = presetSelectClassName || selectClassName; let t3; if ($[5] !== buttonClassName || $[6] !== inputClassName || $[7] !== presetButtonsClassName || $[8] !== presetClassName || $[9] !== t2) { t3 = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(SettingsPresets, { className: presetClassName, selectClassName: t2, buttonClassName, buttonsClassName: presetButtonsClassName, inputClassName }); $[5] = buttonClassName; $[6] = inputClassName; $[7] = presetButtonsClassName; $[8] = presetClassName; $[9] = t2; $[10] = t3; } else { t3 = $[10]; } let t4; if ($[11] !== checkboxClassName || $[12] !== quickActionsClassName || $[13] !== virtualizationClassName || $[14] !== virtualizationStatsClassName) { t4 = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(QuickActions, { className: quickActionsClassName, checkboxClassName, virtualizationClassName, virtualizationStatsClassName }); $[11] = checkboxClassName; $[12] = quickActionsClassName; $[13] = virtualizationClassName; $[14] = virtualizationStatsClassName; $[15] = t4; } else { t4 = $[15]; } let t5; if ($[16] !== contentClassName || $[17] !== groupClassName || $[18] !== groupHeadingClassName || $[19] !== labelClassName || $[20] !== mappings || $[21] !== rowClassName || $[22] !== selectClassName || $[23] !== setEventMapping) { t5 = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(SettingsEventMap, { mappings, onActionChange: setEventMapping, contentClassName, groupClassName, groupHeadingClassName, rowClassName, labelClassName, selectClassName }); $[16] = contentClassName; $[17] = groupClassName; $[18] = groupHeadingClassName; $[19] = labelClassName; $[20] = mappings; $[21] = rowClassName; $[22] = selectClassName; $[23] = setEventMapping; $[24] = t5; } else { t5 = $[24]; } let t6; if ($[25] !== footerClassName || $[26] !== renderFooter) { t6 = renderFooter ? renderFooter() : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: footerClassName, children: "Changes auto-saved to localStorage." }); $[25] = footerClassName; $[26] = renderFooter; $[27] = t6; } else { t6 = $[27]; } let t7; if ($[28] !== className || $[29] !== t1 || $[30] !== t3 || $[31] !== t4 || $[32] !== t5 || $[33] !== t6) { t7 = /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className, children: [t1, t3, t4, t5, t6] }); $[28] = className; $[29] = t1; $[30] = t3; $[31] = t4; $[32] = t5; $[33] = t6; $[34] = t7; } else { t7 = $[34]; } return t7; } // src/components/CommandLine/CommandLine.tsx var import_compiler_runtime65 = require("react/compiler-runtime"); var import_react59 = __toESM(require("react")); var import_jotai82 = require("jotai"); init_registry(); // src/components/CommandLine/CommandSearch.tsx var import_compiler_runtime61 = require("react/compiler-runtime"); var import_react55 = __toESM(require("react")); var import_jotai80 = require("jotai"); init_registry(); var import_jsx_runtime32 = require("react/jsx-runtime"); function CommandSearch() { const $ = (0, import_compiler_runtime61.c)(24); const [state] = (0, import_jotai80.useAtom)(commandLineStateAtom); const updateSearchQuery = (0, import_jotai80.useSetAtom)(updateSearchQueryAtom); const selectCommand = (0, import_jotai80.useSetAtom)(selectCommandAtom); (0, import_jotai80.useAtomValue)(commandHistoryAtom); const [selectedIndex, setSelectedIndex] = (0, import_jotai80.useAtom)(selectedSuggestionIndexAtom); const inputRef = (0, import_react55.useRef)(null); const [inputValue, setInputValue] = (0, import_react55.useState)(""); let t0; let t1; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = () => { inputRef.current?.focus(); }; t1 = []; $[0] = t0; $[1] = t1; } else { t0 = $[0]; t1 = $[1]; } (0, import_react55.useEffect)(t0, t1); let t2; if ($[2] !== state.phase || $[3] !== state.suggestions) { t2 = state.phase === "searching" ? state.suggestions : commandRegistry.all(); $[2] = state.phase; $[3] = state.suggestions; $[4] = t2; } else { t2 = $[4]; } const suggestions = t2; let t3; if ($[5] !== updateSearchQuery) { t3 = (e) => { const value = e.target.value; setInputValue(value); updateSearchQuery(value); }; $[5] = updateSearchQuery; $[6] = t3; } else { t3 = $[6]; } const handleChange = t3; let t4; if ($[7] !== selectCommand || $[8] !== selectedIndex || $[9] !== setSelectedIndex || $[10] !== suggestions || $[11] !== updateSearchQuery) { t4 = (e_0) => { bb15: switch (e_0.key) { case "ArrowDown": { e_0.preventDefault(); setSelectedIndex((prev_0) => Math.min(prev_0 + 1, suggestions.length - 1)); break bb15; } case "ArrowUp": { e_0.preventDefault(); setSelectedIndex(_temp15); break bb15; } case "Enter": { e_0.preventDefault(); if (suggestions[selectedIndex]) { selectCommand(suggestions[selectedIndex]); } break bb15; } case "Tab": { e_0.preventDefault(); if (suggestions[selectedIndex]) { setInputValue(suggestions[selectedIndex].name); updateSearchQuery(suggestions[selectedIndex].name); } } } }; $[7] = selectCommand; $[8] = selectedIndex; $[9] = setSelectedIndex; $[10] = suggestions; $[11] = updateSearchQuery; $[12] = t4; } else { t4 = $[12]; } const handleKeyDown = t4; let t5; if ($[13] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t5 = { color: "var(--cmd-input-text)" }; $[13] = t5; } else { t5 = $[13]; } let t6; if ($[14] !== handleChange || $[15] !== handleKeyDown || $[16] !== inputValue) { t6 = /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("input", { ref: inputRef, type: "text", value: inputValue, onChange: handleChange, onKeyDown: handleKeyDown, placeholder: "Type command name...", className: "flex-1 bg-transparent outline-none", style: t5, autoComplete: "off", autoCorrect: "off", autoCapitalize: "off", spellCheck: "false" }); $[14] = handleChange; $[15] = handleKeyDown; $[16] = inputValue; $[17] = t6; } else { t6 = $[17]; } let t7; if ($[18] !== inputValue || $[19] !== suggestions) { t7 = inputValue && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "ml-2 text-xs", style: { color: "var(--hud-text-secondary)", opacity: 0.6 }, children: [suggestions.length, " matches"] }); $[18] = inputValue; $[19] = suggestions; $[20] = t7; } else { t7 = $[20]; } let t8; if ($[21] !== t6 || $[22] !== t7) { t8 = /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-1 items-center", children: [t6, t7] }); $[21] = t6; $[22] = t7; $[23] = t8; } else { t8 = $[23]; } return t8; } function _temp15(prev) { return Math.max(prev - 1, 0); } // src/components/CommandLine/CommandInputCollector.tsx var import_compiler_runtime64 = require("react/compiler-runtime"); var import_react58 = __toESM(require("react")); var import_jotai81 = require("jotai"); init_viewport_store(); // src/components/CommandLine/CollectorInputPhase.tsx var import_compiler_runtime62 = require("react/compiler-runtime"); var import_react56 = __toESM(require("react")); var import_jsx_runtime33 = require("react/jsx-runtime"); function CollectorInputPhase(t0) { const $ = (0, import_compiler_runtime62.c)(15); const { command, currentInput, progress, inputModeType } = t0; const isPickMode = inputModeType === "pickPoint" || inputModeType === "pickNode" || inputModeType === "pickNodes"; let t1; if ($[0] !== command || $[1] !== progress) { t1 = progress && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center gap-2 text-xs", style: { color: "var(--hud-text-secondary)" }, children: [/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "font-medium", style: { color: "var(--hud-text)" }, children: command.name }), /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { children: "\u2022" }), /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { children: ["Step ", progress.current, "/", progress.total] })] }); $[0] = command; $[1] = progress; $[2] = t1; } else { t1 = $[2]; } let t2; if ($[3] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t2 = { color: "var(--hud-text-secondary)" }; $[3] = t2; } else { t2 = $[3]; } let t3; if ($[4] !== currentInput.prompt) { t3 = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { style: t2, children: [currentInput.prompt, ":"] }); $[4] = currentInput.prompt; $[5] = t3; } else { t3 = $[5]; } let t4; if ($[6] !== inputModeType || $[7] !== isPickMode) { t4 = isPickMode && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "animate-pulse text-sm", style: { color: "var(--badge-info-text)" }, children: [inputModeType === "pickPoint" && "Click on canvas or enter x,y", inputModeType === "pickNode" && "Click on a node", inputModeType === "pickNodes" && "Click nodes (Enter when done)"] }); $[6] = inputModeType; $[7] = isPickMode; $[8] = t4; } else { t4 = $[8]; } let t5; if ($[9] !== t3 || $[10] !== t4) { t5 = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center gap-2", children: [t3, t4] }); $[9] = t3; $[10] = t4; $[11] = t5; } else { t5 = $[11]; } let t6; if ($[12] !== t1 || $[13] !== t5) { t6 = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [t1, t5] }); $[12] = t1; $[13] = t5; $[14] = t6; } else { t6 = $[14]; } return t6; } // src/components/CommandLine/CollectorSelectInput.tsx var import_compiler_runtime63 = require("react/compiler-runtime"); var import_react57 = __toESM(require("react")); var import_jsx_runtime34 = require("react/jsx-runtime"); function ShortcutButton(t0) { const $ = (0, import_compiler_runtime63.c)(13); const { shortcut, label, description, onClick } = t0; let t1; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t1 = { backgroundColor: "var(--list-item-bg-hover)", color: "var(--hud-text)" }; $[0] = t1; } else { t1 = $[0]; } let t2; if ($[1] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t2 = { backgroundColor: "var(--cmd-bg)", color: "var(--badge-info-text)" }; $[1] = t2; } else { t2 = $[1]; } let t3; if ($[2] !== shortcut) { t3 = shortcut.toUpperCase(); $[2] = shortcut; $[3] = t3; } else { t3 = $[3]; } let t4; if ($[4] !== t3) { t4 = /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("kbd", { className: "rounded px-1 py-0.5 font-mono text-xs", style: t2, children: t3 }); $[4] = t3; $[5] = t4; } else { t4 = $[5]; } let t5; if ($[6] !== label) { t5 = /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { children: label }); $[6] = label; $[7] = t5; } else { t5 = $[7]; } let t6; if ($[8] !== description || $[9] !== onClick || $[10] !== t4 || $[11] !== t5) { t6 = /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("button", { onClick, className: "flex items-center gap-1 rounded px-2 py-0.5 text-sm transition-colors", style: t1, onMouseEnter: _temp16, onMouseLeave: _temp27, title: description, children: [t4, t5] }); $[8] = description; $[9] = onClick; $[10] = t4; $[11] = t5; $[12] = t6; } else { t6 = $[12]; } return t6; } function _temp27(e_0) { return e_0.currentTarget.style.backgroundColor = "var(--list-item-bg-hover)"; } function _temp16(e) { return e.currentTarget.style.backgroundColor = "var(--list-item-border)"; } function CollectorSelectInput(t0) { const $ = (0, import_compiler_runtime63.c)(7); const { options, onSelect } = t0; let t1; if ($[0] !== onSelect || $[1] !== options) { let t22; if ($[3] !== onSelect) { t22 = (option) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ShortcutButton, { shortcut: option.shortcut, label: option.label, description: option.description, onClick: () => onSelect(option.value) }, option.value); $[3] = onSelect; $[4] = t22; } else { t22 = $[4]; } t1 = options.map(t22); $[0] = onSelect; $[1] = options; $[2] = t1; } else { t1 = $[2]; } let t2; if ($[5] !== t1) { t2 = /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex items-center gap-2", children: t1 }); $[5] = t1; $[6] = t2; } else { t2 = $[6]; } return t2; } function CollectorBooleanInput(t0) { const $ = (0, import_compiler_runtime63.c)(7); const { onSelect } = t0; let t1; if ($[0] !== onSelect) { t1 = /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ShortcutButton, { shortcut: "Y", label: "Yes", onClick: () => onSelect(true) }); $[0] = onSelect; $[1] = t1; } else { t1 = $[1]; } let t2; if ($[2] !== onSelect) { t2 = /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ShortcutButton, { shortcut: "N", label: "No", onClick: () => onSelect(false) }); $[2] = onSelect; $[3] = t2; } else { t2 = $[3]; } let t3; if ($[4] !== t1 || $[5] !== t2) { t3 = /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-2", children: [t1, t2] }); $[4] = t1; $[5] = t2; $[6] = t3; } else { t3 = $[6]; } return t3; } // src/components/CommandLine/CommandInputCollector.tsx var import_jsx_runtime35 = require("react/jsx-runtime"); function CommandInputCollector() { const $ = (0, import_compiler_runtime64.c)(71); const [state] = (0, import_jotai81.useAtom)(commandLineStateAtom); const currentInput = (0, import_jotai81.useAtomValue)(currentInputAtom); const progress = (0, import_jotai81.useAtomValue)(commandProgressAtom); const inputMode = (0, import_jotai81.useAtomValue)(inputModeAtom2); const provideInput = (0, import_jotai81.useSetAtom)(provideInputAtom2); const skipInput = (0, import_jotai81.useSetAtom)(skipInputAtom); const goBack = (0, import_jotai81.useSetAtom)(goBackInputAtom); const inputRef = (0, import_react58.useRef)(null); const [textValue, setTextValue] = (0, import_react58.useState)(""); const zoom = (0, import_jotai81.useAtomValue)(zoomAtom); const pan = (0, import_jotai81.useAtomValue)(panAtom); const viewportRect = (0, import_jotai81.useAtomValue)(viewportRectAtom); let t0; let t1; if ($[0] !== currentInput) { t0 = () => { if (currentInput && (currentInput.type === "text" || currentInput.type === "number" || currentInput.type === "point")) { inputRef.current?.focus(); } }; t1 = [currentInput]; $[0] = currentInput; $[1] = t0; $[2] = t1; } else { t0 = $[1]; t1 = $[2]; } (0, import_react58.useEffect)(t0, t1); let t2; if ($[3] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t2 = () => { setTextValue(""); }; $[3] = t2; } else { t2 = $[3]; } const t3 = currentInput?.name; let t4; if ($[4] !== t3) { t4 = [t3]; $[4] = t3; $[5] = t4; } else { t4 = $[5]; } (0, import_react58.useEffect)(t2, t4); let t5; if ($[6] !== pan.x || $[7] !== pan.y || $[8] !== viewportRect || $[9] !== zoom) { t5 = () => { const screenCenterX = viewportRect ? viewportRect.width / 2 : window.innerWidth / 2; const screenCenterY = viewportRect ? viewportRect.height / 2 : window.innerHeight / 2; return { x: Math.round((screenCenterX - pan.x) / zoom), y: Math.round((screenCenterY - pan.y) / zoom) }; }; $[6] = pan.x; $[7] = pan.y; $[8] = viewportRect; $[9] = zoom; $[10] = t5; } else { t5 = $[10]; } const getWorldCenter = t5; let t6; let t7; if ($[11] !== currentInput || $[12] !== getWorldCenter || $[13] !== provideInput) { t6 = () => { if (!currentInput) { return; } const handleGlobalKeyDown = (e) => { const target = e.target; const isInputField = target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable; if (e.key === "Enter" && !isInputField) { if (currentInput.type === "point") { e.preventDefault(); provideInput(getWorldCenter()); return; } if (currentInput.default !== void 0) { e.preventDefault(); provideInput(currentInput.default); return; } } if (currentInput.type === "select" && currentInput.options) { const option = currentInput.options.find((opt) => opt.shortcut.toLowerCase() === e.key.toLowerCase()); if (option) { e.preventDefault(); provideInput(option.value); } } if (currentInput.type === "boolean") { if (e.key.toLowerCase() === "y") { e.preventDefault(); provideInput(true); } if (e.key.toLowerCase() === "n") { e.preventDefault(); provideInput(false); } } }; window.addEventListener("keydown", handleGlobalKeyDown); return () => window.removeEventListener("keydown", handleGlobalKeyDown); }; t7 = [currentInput, provideInput, getWorldCenter]; $[11] = currentInput; $[12] = getWorldCenter; $[13] = provideInput; $[14] = t6; $[15] = t7; } else { t6 = $[14]; t7 = $[15]; } (0, import_react58.useEffect)(t6, t7); let t8; if ($[16] !== currentInput || $[17] !== provideInput || $[18] !== textValue) { t8 = (e_0) => { e_0.preventDefault(); if (!currentInput) { return; } if (textValue === "" && currentInput.default !== void 0) { provideInput(currentInput.default); setTextValue(""); return; } if (currentInput.type === "number") { const num = parseFloat(textValue); if (!isNaN(num)) { provideInput(num); } } else { provideInput(textValue); } setTextValue(""); }; $[16] = currentInput; $[17] = provideInput; $[18] = textValue; $[19] = t8; } else { t8 = $[19]; } const handleSubmit = t8; let t9; if ($[20] !== currentInput || $[21] !== goBack || $[22] !== progress || $[23] !== provideInput || $[24] !== skipInput || $[25] !== textValue) { t9 = (e_1) => { if (e_1.key === "Escape") { e_1.preventDefault(); if (currentInput?.required === false) { skipInput(); } else { if (progress && progress.current > 1) { goBack(); } } return; } if (e_1.key === "Backspace" && textValue === "" && progress && progress.current > 1) { e_1.preventDefault(); goBack(); return; } if (currentInput?.type === "select" && currentInput.options) { const option_0 = currentInput.options.find((opt_0) => opt_0.shortcut.toLowerCase() === e_1.key.toLowerCase()); if (option_0) { e_1.preventDefault(); provideInput(option_0.value); } } }; $[20] = currentInput; $[21] = goBack; $[22] = progress; $[23] = provideInput; $[24] = skipInput; $[25] = textValue; $[26] = t9; } else { t9 = $[26]; } const handleKeyDown = t9; if (state.phase !== "collecting" || !currentInput) { return null; } const isSelectMode = currentInput.type === "select"; let t10; if ($[27] !== currentInput || $[28] !== inputMode.type || $[29] !== progress || $[30] !== state.command) { t10 = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(CollectorInputPhase, { command: state.command, currentInput, progress, inputModeType: inputMode.type }); $[27] = currentInput; $[28] = inputMode.type; $[29] = progress; $[30] = state.command; $[31] = t10; } else { t10 = $[31]; } let t11; if ($[32] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t11 = { color: "var(--hud-text-secondary)" }; $[32] = t11; } else { t11 = $[32]; } let t12; if ($[33] !== currentInput.prompt) { t12 = /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { style: t11, children: [currentInput.prompt, ":"] }); $[33] = currentInput.prompt; $[34] = t12; } else { t12 = $[34]; } let t13; if ($[35] !== currentInput.options || $[36] !== isSelectMode || $[37] !== provideInput) { t13 = isSelectMode && currentInput.options && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(CollectorSelectInput, { options: currentInput.options, onSelect: (v) => provideInput(v) }); $[35] = currentInput.options; $[36] = isSelectMode; $[37] = provideInput; $[38] = t13; } else { t13 = $[38]; } let t14; if ($[39] !== currentInput.default || $[40] !== currentInput.type || $[41] !== handleKeyDown || $[42] !== handleSubmit || $[43] !== textValue) { t14 = (currentInput.type === "text" || currentInput.type === "number") && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("form", { onSubmit: handleSubmit, className: "flex flex-1 items-center", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("input", { ref: inputRef, type: currentInput.type === "number" ? "number" : "text", value: textValue, onChange: (e_2) => setTextValue(e_2.target.value), onKeyDown: handleKeyDown, placeholder: currentInput.default !== void 0 ? `Default: ${currentInput.default}` : void 0, className: "flex-1 bg-transparent outline-none", style: { color: "var(--cmd-input-text)" }, autoComplete: "off", autoCorrect: "off", autoCapitalize: "off", spellCheck: "false" }) }); $[39] = currentInput.default; $[40] = currentInput.type; $[41] = handleKeyDown; $[42] = handleSubmit; $[43] = textValue; $[44] = t14; } else { t14 = $[44]; } let t15; if ($[45] !== currentInput.type || $[46] !== getWorldCenter || $[47] !== handleKeyDown || $[48] !== provideInput || $[49] !== textValue) { t15 = currentInput.type === "point" && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("form", { onSubmit: (e_3) => { e_3.preventDefault(); if (textValue.trim() === "") { provideInput(getWorldCenter()); setTextValue(""); return; } const match = textValue.match(/^\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*$/); if (match) { provideInput({ x: parseFloat(match[1]), y: parseFloat(match[2]) }); setTextValue(""); } }, className: "flex flex-1 items-center", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("input", { ref: inputRef, type: "text", value: textValue, onChange: (e_4) => setTextValue(e_4.target.value), onKeyDown: handleKeyDown, placeholder: "x, y or click on canvas (Enter for center)", className: "flex-1 bg-transparent outline-none", style: { color: "var(--cmd-input-text)" }, autoComplete: "off" }) }); $[45] = currentInput.type; $[46] = getWorldCenter; $[47] = handleKeyDown; $[48] = provideInput; $[49] = textValue; $[50] = t15; } else { t15 = $[50]; } let t16; if ($[51] !== currentInput.type || $[52] !== provideInput) { t16 = currentInput.type === "boolean" && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(CollectorBooleanInput, { onSelect: (v_0) => provideInput(v_0) }); $[51] = currentInput.type; $[52] = provideInput; $[53] = t16; } else { t16 = $[53]; } let t17; if ($[54] !== currentInput.required || $[55] !== skipInput) { t17 = currentInput.required === false && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("button", { onClick: () => skipInput(), className: "rounded px-2 py-0.5 text-xs transition-colors", style: { color: "var(--hud-text-secondary)", opacity: 0.8 }, onMouseEnter: _temp17, onMouseLeave: _temp28, children: "Skip (Esc)" }); $[54] = currentInput.required; $[55] = skipInput; $[56] = t17; } else { t17 = $[56]; } let t18; if ($[57] !== goBack || $[58] !== progress) { t18 = progress && progress.current > 1 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("button", { onClick: () => goBack(), className: "rounded px-2 py-0.5 text-xs transition-colors", style: { color: "var(--hud-text-secondary)", opacity: 0.8 }, onMouseEnter: _temp34, onMouseLeave: _temp43, children: "Back" }); $[57] = goBack; $[58] = progress; $[59] = t18; } else { t18 = $[59]; } let t19; if ($[60] !== t12 || $[61] !== t13 || $[62] !== t14 || $[63] !== t15 || $[64] !== t16 || $[65] !== t17 || $[66] !== t18) { t19 = /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center gap-2", children: [t12, t13, t14, t15, t16, t17, t18] }); $[60] = t12; $[61] = t13; $[62] = t14; $[63] = t15; $[64] = t16; $[65] = t17; $[66] = t18; $[67] = t19; } else { t19 = $[67]; } let t20; if ($[68] !== t10 || $[69] !== t19) { t20 = /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-1 flex-col gap-1", children: [t10, t19] }); $[68] = t10; $[69] = t19; $[70] = t20; } else { t20 = $[70]; } return t20; } function _temp43(e_8) { e_8.currentTarget.style.backgroundColor = "transparent"; e_8.currentTarget.style.color = "var(--hud-text-secondary)"; } function _temp34(e_7) { e_7.currentTarget.style.backgroundColor = "var(--list-item-bg-hover)"; e_7.currentTarget.style.color = "var(--hud-text)"; } function _temp28(e_6) { e_6.currentTarget.style.backgroundColor = "transparent"; e_6.currentTarget.style.color = "var(--hud-text-secondary)"; } function _temp17(e_5) { e_5.currentTarget.style.backgroundColor = "var(--list-item-bg-hover)"; e_5.currentTarget.style.color = "var(--hud-text)"; } // src/components/CommandLine/CommandLine.tsx var import_jsx_runtime36 = require("react/jsx-runtime"); var MIN_HEIGHT = 60; var MAX_HEIGHT = 400; var DEFAULT_HEIGHT = 200; function CommandLine(t0) { const $ = (0, import_compiler_runtime65.c)(58); const { isModalOpen } = t0; const visible = (0, import_jotai82.useAtomValue)(commandLineVisibleAtom); const [state, setState] = (0, import_jotai82.useAtom)(commandLineStateAtom); (0, import_jotai82.useSetAtom)(closeCommandLineAtom); const progress = (0, import_jotai82.useAtomValue)(commandProgressAtom); const selectedIndex = (0, import_jotai82.useAtomValue)(selectedSuggestionIndexAtom); const setInputMode = (0, import_jotai82.useSetAtom)(inputModeAtom2); const containerRef = (0, import_react59.useRef)(null); const [height, setHeight] = (0, import_react59.useState)(DEFAULT_HEIGHT); const [isDragging, setIsDragging] = (0, import_react59.useState)(false); const dragStartY = (0, import_react59.useRef)(0); const dragStartHeight = (0, import_react59.useRef)(DEFAULT_HEIGHT); let t1; let t2; if ($[0] !== isModalOpen || $[1] !== setInputMode || $[2] !== setState || $[3] !== state.phase || $[4] !== visible) { t1 = () => { if (!visible) { return; } const handleKeyDown = (e) => { if (e.key === "Escape") { if (isModalOpen?.()) { return; } e.preventDefault(); e.stopPropagation(); if (state.phase === "collecting" || state.phase === "executing" || state.phase === "error") { setState({ phase: "searching", query: "", suggestions: commandRegistry.all() }); setInputMode({ type: "normal" }); } } }; window.addEventListener("keydown", handleKeyDown, { capture: true }); return () => window.removeEventListener("keydown", handleKeyDown, { capture: true }); }; t2 = [visible, state.phase, setState, setInputMode, isModalOpen]; $[0] = isModalOpen; $[1] = setInputMode; $[2] = setState; $[3] = state.phase; $[4] = visible; $[5] = t1; $[6] = t2; } else { t1 = $[5]; t2 = $[6]; } (0, import_react59.useEffect)(t1, t2); let t3; if ($[7] !== height) { t3 = (e_0) => { e_0.preventDefault(); setIsDragging(true); dragStartY.current = e_0.clientY; dragStartHeight.current = height; }; $[7] = height; $[8] = t3; } else { t3 = $[8]; } const handleDragStart = t3; let t4; let t5; if ($[9] !== isDragging) { t4 = () => { if (!isDragging) { return; } const handleMouseMove = (e_1) => { const deltaY = dragStartY.current - e_1.clientY; const newHeight = Math.min(MAX_HEIGHT, Math.max(MIN_HEIGHT, dragStartHeight.current + deltaY)); setHeight(newHeight); }; const handleMouseUp = () => { setIsDragging(false); }; window.addEventListener("mousemove", handleMouseMove); window.addEventListener("mouseup", handleMouseUp); return () => { window.removeEventListener("mousemove", handleMouseMove); window.removeEventListener("mouseup", handleMouseUp); }; }; t5 = [isDragging]; $[9] = isDragging; $[10] = t4; $[11] = t5; } else { t4 = $[10]; t5 = $[11]; } (0, import_react59.useEffect)(t4, t5); if (!visible) { return null; } typeof window !== "undefined" ? Math.min(400, window.innerHeight * 0.5) : 300; const t6 = `${height}px`; let t7; if ($[12] !== t6) { t7 = { height: t6, maxHeight: "50vh", backgroundColor: "var(--cmd-bg)" }; $[12] = t6; $[13] = t7; } else { t7 = $[13]; } const t8 = isDragging ? "var(--cmd-resize-hover)" : "var(--cmd-resize-bg)"; let t9; if ($[14] !== t8) { t9 = { borderColor: "var(--cmd-border)", backgroundColor: t8 }; $[14] = t8; $[15] = t9; } else { t9 = $[15]; } let t10; let t11; if ($[16] !== isDragging) { t10 = (e_2) => !isDragging && (e_2.currentTarget.style.backgroundColor = "var(--cmd-resize-hover)"); t11 = (e_3) => !isDragging && (e_3.currentTarget.style.backgroundColor = "var(--cmd-resize-bg)"); $[16] = isDragging; $[17] = t10; $[18] = t11; } else { t10 = $[17]; t11 = $[18]; } let t12; if ($[19] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t12 = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "h-1 w-12 rounded-full opacity-50", style: { backgroundColor: "var(--hud-icon)" } }); $[19] = t12; } else { t12 = $[19]; } let t13; if ($[20] !== handleDragStart || $[21] !== t10 || $[22] !== t11 || $[23] !== t9) { t13 = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { onMouseDown: handleDragStart, className: "flex h-3 w-full shrink-0 cursor-ns-resize items-center justify-center border-t transition-colors", style: t9, onMouseEnter: t10, onMouseLeave: t11, children: t12 }); $[20] = handleDragStart; $[21] = t10; $[22] = t11; $[23] = t9; $[24] = t13; } else { t13 = $[24]; } let t14; if ($[25] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t14 = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "mr-2 text-lg font-bold select-none", style: { color: "var(--badge-info-text)" }, children: ">" }); $[25] = t14; } else { t14 = $[25]; } let t15; if ($[26] !== state.phase) { t15 = (state.phase === "idle" || state.phase === "searching") && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CommandSearch, {}); $[26] = state.phase; $[27] = t15; } else { t15 = $[27]; } let t16; if ($[28] !== state.phase) { t16 = state.phase === "collecting" && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CommandInputCollector, {}); $[28] = state.phase; $[29] = t16; } else { t16 = $[29]; } let t17; if ($[30] !== state.command || $[31] !== state.phase) { t17 = state.phase === "executing" && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex items-center gap-2", style: { color: "var(--badge-warning-text)" }, children: [/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("svg", { className: "h-5 w-5 animate-spin", viewBox: "0 0 24 24", children: [/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", fill: "none" }), /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }), /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "text-base", children: ["Executing ", state.command.name, "..."] })] }); $[30] = state.command; $[31] = state.phase; $[32] = t17; } else { t17 = $[32]; } let t18; if ($[33] !== setState || $[34] !== state.message || $[35] !== state.phase) { t18 = state.phase === "error" && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex items-center gap-2", style: { color: "var(--badge-error-text)" }, children: [/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("svg", { className: "h-5 w-5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("line", { x1: "15", y1: "9", x2: "9", y2: "15" }), /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("line", { x1: "9", y1: "9", x2: "15", y2: "15" })] }), /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-base", children: state.message }), /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("button", { onClick: () => setState({ phase: "idle" }), className: "ml-2 rounded px-2 py-1 text-sm transition-colors", style: { backgroundColor: "var(--list-item-bg-hover)", color: "var(--hud-text-secondary)" }, onMouseEnter: _temp18, onMouseLeave: _temp29, children: "Try again" })] }); $[33] = setState; $[34] = state.message; $[35] = state.phase; $[36] = t18; } else { t18 = $[36]; } let t19; if ($[37] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t19 = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex-1" }); $[37] = t19; } else { t19 = $[37]; } let t20; if ($[38] !== progress) { t20 = progress && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "mr-4 rounded px-2 py-1 text-sm", style: { backgroundColor: "var(--list-item-bg-hover)", color: "var(--hud-text-secondary)" }, children: ["Step ", progress.current, "/", progress.total] }); $[38] = progress; $[39] = t20; } else { t20 = $[39]; } let t21; if ($[40] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t21 = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-sm select-none", style: { color: "var(--hud-text-secondary)", opacity: 0.6 }, children: "/ to close \u2022 ESC to cancel" }); $[40] = t21; } else { t21 = $[40]; } let t22; if ($[41] !== t15 || $[42] !== t16 || $[43] !== t17 || $[44] !== t18 || $[45] !== t20) { t22 = /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex items-center px-4 py-3 font-mono text-sm", children: [t14, t15, t16, t17, t18, t19, t20, t21] }); $[41] = t15; $[42] = t16; $[43] = t17; $[44] = t18; $[45] = t20; $[46] = t22; } else { t22 = $[46]; } let t23; if ($[47] !== selectedIndex || $[48] !== state.phase || $[49] !== state.suggestions) { t23 = state.phase === "searching" && state.suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "min-h-0 shrink overflow-y-auto border-t", style: { maxHeight: "40vh", borderColor: "var(--cmd-border)" }, children: state.suggestions.map((cmd, index) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CommandSuggestionItem, { command: cmd, isHighlighted: index === selectedIndex }, cmd.name)) }); $[47] = selectedIndex; $[48] = state.phase; $[49] = state.suggestions; $[50] = t23; } else { t23 = $[50]; } let t24; if ($[51] !== t22 || $[52] !== t23) { t24 = /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: [t22, t23] }); $[51] = t22; $[52] = t23; $[53] = t24; } else { t24 = $[53]; } let t25; if ($[54] !== t13 || $[55] !== t24 || $[56] !== t7) { t25 = /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { ref: containerRef, className: "fixed right-0 bottom-0 left-0 z-50 flex flex-col shadow-2xl", style: t7, children: [t13, t24] }); $[54] = t13; $[55] = t24; $[56] = t7; $[57] = t25; } else { t25 = $[57]; } return t25; } function _temp29(e_5) { return e_5.currentTarget.style.color = "var(--hud-text-secondary)"; } function _temp18(e_4) { return e_4.currentTarget.style.color = "var(--hud-text)"; } function CommandSuggestionItem(t0) { const $ = (0, import_compiler_runtime65.c)(24); const { command, isHighlighted } = t0; const selectCommand = (0, import_jotai82.useSetAtom)(selectCommandAtom); let t1; if ($[0] !== command || $[1] !== selectCommand) { t1 = () => selectCommand(command); $[0] = command; $[1] = selectCommand; $[2] = t1; } else { t1 = $[2]; } const t2 = isHighlighted ? "var(--list-item-bg-hover)" : "transparent"; let t3; if ($[3] !== t2) { t3 = { backgroundColor: t2 }; $[3] = t2; $[4] = t3; } else { t3 = $[4]; } let t4; let t5; if ($[5] !== isHighlighted) { t4 = (e) => !isHighlighted && (e.currentTarget.style.backgroundColor = "var(--list-item-bg-hover)"); t5 = (e_0) => !isHighlighted && (e_0.currentTarget.style.backgroundColor = "transparent"); $[5] = isHighlighted; $[6] = t4; $[7] = t5; } else { t4 = $[6]; t5 = $[7]; } let t6; if ($[8] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t6 = { color: "var(--hud-text)" }; $[8] = t6; } else { t6 = $[8]; } let t7; if ($[9] !== command.name) { t7 = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "font-mono", style: t6, children: command.name }); $[9] = command.name; $[10] = t7; } else { t7 = $[10]; } let t8; if ($[11] !== command.aliases) { t8 = command.aliases && command.aliases.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "text-xs", style: { color: "var(--hud-text-secondary)", opacity: 0.5 }, children: ["(", command.aliases.join(", "), ")"] }); $[11] = command.aliases; $[12] = t8; } else { t8 = $[12]; } let t9; if ($[13] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t9 = { color: "var(--hud-text-secondary)" }; $[13] = t9; } else { t9 = $[13]; } let t10; if ($[14] !== command.description) { t10 = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-sm", style: t9, children: command.description }); $[14] = command.description; $[15] = t10; } else { t10 = $[15]; } let t11; if ($[16] !== t1 || $[17] !== t10 || $[18] !== t3 || $[19] !== t4 || $[20] !== t5 || $[21] !== t7 || $[22] !== t8) { t11 = /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("button", { onClick: t1, className: "flex w-full items-center gap-3 px-4 py-2 text-left transition-colors", style: t3, onMouseEnter: t4, onMouseLeave: t5, children: [t7, t8, t10] }); $[16] = t1; $[17] = t10; $[18] = t3; $[19] = t4; $[20] = t5; $[21] = t7; $[22] = t8; $[23] = t11; } else { t11 = $[23]; } return t11; } // src/components/CommandFeedbackOverlay.tsx var import_compiler_runtime66 = require("react/compiler-runtime"); var import_jotai83 = require("jotai"); init_viewport_store(); var import_jsx_runtime37 = require("react/jsx-runtime"); function CommandFeedbackOverlay() { const $ = (0, import_compiler_runtime66.c)(27); const inputMode = (0, import_jotai83.useAtomValue)(inputModeAtom2); const feedback = (0, import_jotai83.useAtomValue)(commandFeedbackAtom); const currentInput = (0, import_jotai83.useAtomValue)(currentInputAtom); const zoom = (0, import_jotai83.useAtomValue)(zoomAtom); const pan = (0, import_jotai83.useAtomValue)(panAtom); if (inputMode.type === "normal") { return null; } let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = {}; $[0] = t0; } else { t0 = $[0]; } let t1; if ($[1] !== feedback || $[2] !== inputMode.type || $[3] !== pan || $[4] !== zoom) { t1 = (inputMode.type === "pickPoint" || feedback?.crosshair) && feedback?.cursorWorldPos && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Crosshair, { position: feedback.cursorWorldPos, zoom, pan }); $[1] = feedback; $[2] = inputMode.type; $[3] = pan; $[4] = zoom; $[5] = t1; } else { t1 = $[5]; } let t2; if ($[6] !== feedback || $[7] !== pan || $[8] !== zoom) { t2 = feedback?.hoveredNodeId && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(NodeHighlight, { nodeId: feedback.hoveredNodeId, zoom, pan }); $[6] = feedback; $[7] = pan; $[8] = zoom; $[9] = t2; } else { t2 = $[9]; } let t3; if ($[10] !== feedback || $[11] !== pan || $[12] !== zoom) { t3 = feedback?.previewEdge && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PreviewEdge, { edge: feedback.previewEdge, zoom, pan }); $[10] = feedback; $[11] = pan; $[12] = zoom; $[13] = t3; } else { t3 = $[13]; } let t4; if ($[14] !== feedback || $[15] !== pan || $[16] !== zoom) { t4 = feedback?.ghostNode && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(GhostNode, { ghost: feedback.ghostNode, zoom, pan }); $[14] = feedback; $[15] = pan; $[16] = zoom; $[17] = t4; } else { t4 = $[17]; } const t5 = currentInput?.prompt; let t6; if ($[18] !== inputMode || $[19] !== t5) { t6 = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ModeIndicator, { inputMode, prompt: t5 }); $[18] = inputMode; $[19] = t5; $[20] = t6; } else { t6 = $[20]; } let t7; if ($[21] !== t1 || $[22] !== t2 || $[23] !== t3 || $[24] !== t4 || $[25] !== t6) { t7 = /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "pointer-events-none fixed inset-0 z-50", style: t0, children: [t1, t2, t3, t4, t6] }); $[21] = t1; $[22] = t2; $[23] = t3; $[24] = t4; $[25] = t6; $[26] = t7; } else { t7 = $[26]; } return t7; } function Crosshair(t0) { const $ = (0, import_compiler_runtime66.c)(10); const { position, zoom, pan } = t0; const screenX = position.x * zoom + pan.x; const screenY = position.y * zoom + pan.y; const t1 = screenX - 20; const t2 = screenY - 20; let t3; if ($[0] !== t1 || $[1] !== t2) { t3 = { left: t1, top: t2, width: 40, height: 40 }; $[0] = t1; $[1] = t2; $[2] = t3; } else { t3 = $[2]; } let t4; let t5; let t6; let t7; let t8; if ($[3] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t4 = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("line", { x1: "0", y1: "20", x2: "15", y2: "20", stroke: "var(--list-item-border-active)", strokeWidth: "2" }); t5 = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("line", { x1: "25", y1: "20", x2: "40", y2: "20", stroke: "var(--list-item-border-active)", strokeWidth: "2" }); t6 = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("line", { x1: "20", y1: "0", x2: "20", y2: "15", stroke: "var(--list-item-border-active)", strokeWidth: "2" }); t7 = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("line", { x1: "20", y1: "25", x2: "20", y2: "40", stroke: "var(--list-item-border-active)", strokeWidth: "2" }); t8 = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("circle", { cx: "20", cy: "20", r: "3", fill: "var(--list-item-border-active)" }); $[3] = t4; $[4] = t5; $[5] = t6; $[6] = t7; $[7] = t8; } else { t4 = $[3]; t5 = $[4]; t6 = $[5]; t7 = $[6]; t8 = $[7]; } let t9; if ($[8] !== t3) { t9 = /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("svg", { className: "pointer-events-none absolute", style: t3, children: [t4, t5, t6, t7, t8] }); $[8] = t3; $[9] = t9; } else { t9 = $[9]; } return t9; } function NodeHighlight({ nodeId, zoom, pan }) { return null; } function PreviewEdge(t0) { const $ = (0, import_compiler_runtime66.c)(15); const { edge, zoom, pan } = t0; const t1 = edge.from.x * zoom + pan.x; const t2 = edge.from.y * zoom + pan.y; let t3; if ($[0] !== t1 || $[1] !== t2) { t3 = { x: t1, y: t2 }; $[0] = t1; $[1] = t2; $[2] = t3; } else { t3 = $[2]; } const fromScreen = t3; const t4 = edge.to.x * zoom + pan.x; const t5 = edge.to.y * zoom + pan.y; let t6; if ($[3] !== t4 || $[4] !== t5) { t6 = { x: t4, y: t5 }; $[3] = t4; $[4] = t5; $[5] = t6; } else { t6 = $[5]; } const toScreen = t6; let t7; if ($[6] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t7 = { width: "100%", height: "100%" }; $[6] = t7; } else { t7 = $[6]; } let t8; if ($[7] !== fromScreen.x || $[8] !== fromScreen.y || $[9] !== toScreen.x || $[10] !== toScreen.y) { t8 = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("line", { x1: fromScreen.x, y1: fromScreen.y, x2: toScreen.x, y2: toScreen.y, stroke: "var(--list-item-border-active)", strokeWidth: "2", strokeDasharray: "5,5", markerEnd: "url(#arrowhead)" }); $[7] = fromScreen.x; $[8] = fromScreen.y; $[9] = toScreen.x; $[10] = toScreen.y; $[11] = t8; } else { t8 = $[11]; } let t9; if ($[12] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t9 = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("marker", { id: "arrowhead", markerWidth: "10", markerHeight: "7", refX: "9", refY: "3.5", orient: "auto", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("polygon", { points: "0 0, 10 3.5, 0 7", fill: "var(--list-item-border-active)" }) }) }); $[12] = t9; } else { t9 = $[12]; } let t10; if ($[13] !== t8) { t10 = /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("svg", { className: "pointer-events-none absolute inset-0", style: t7, children: [t8, t9] }); $[13] = t8; $[14] = t10; } else { t10 = $[14]; } return t10; } function GhostNode(t0) { const $ = (0, import_compiler_runtime66.c)(7); const { ghost, zoom, pan } = t0; const t1 = ghost.position.x * zoom + pan.x; const t2 = ghost.position.y * zoom + pan.y; let t3; if ($[0] !== t1 || $[1] !== t2) { t3 = { x: t1, y: t2 }; $[0] = t1; $[1] = t2; $[2] = t3; } else { t3 = $[2]; } const screenPos = t3; const t4 = screenPos.x - 50; const t5 = screenPos.y - 50; let t6; if ($[3] !== ghost.opacity || $[4] !== t4 || $[5] !== t5) { t6 = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "absolute rounded-lg border-2 border-dashed", style: { left: t4, top: t5, width: 100, height: 100, opacity: ghost.opacity, borderColor: "var(--badge-info-border)", backgroundColor: "var(--badge-info-bg)" } }); $[3] = ghost.opacity; $[4] = t4; $[5] = t5; $[6] = t6; } else { t6 = $[6]; } return t6; } function ModeIndicator(t0) { const $ = (0, import_compiler_runtime66.c)(7); const { inputMode, prompt } = t0; const displayPrompt = prompt || inputMode.prompt || "Waiting for input..."; let modeLabel; bb0: switch (inputMode.type) { case "pickPoint": { modeLabel = "Click to pick point"; break bb0; } case "pickNode": { modeLabel = "Click a node to select"; break bb0; } case "pickNodes": { modeLabel = "Click nodes to select (Shift+click for multiple)"; break bb0; } case "select": { modeLabel = "Choose an option"; break bb0; } case "text": { modeLabel = "Type your input"; break bb0; } default: { modeLabel = inputMode.type; } } let t1; let t2; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t1 = { backgroundColor: "var(--modal-bg)", borderColor: "var(--modal-border)", color: "var(--modal-text-primary)", opacity: 0.9 }; t2 = { color: "var(--badge-info-text)" }; $[0] = t1; $[1] = t2; } else { t1 = $[0]; t2 = $[1]; } let t3; if ($[2] !== modeLabel) { t3 = /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { style: t2, children: [modeLabel, ":"] }); $[2] = modeLabel; $[3] = t3; } else { t3 = $[3]; } let t4; if ($[4] !== displayPrompt || $[5] !== t3) { t4 = /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "fixed bottom-16 left-1/2 -translate-x-1/2 rounded-lg border px-4 py-2 font-mono text-sm shadow-lg", style: t1, children: [t3, " ", displayPrompt] }); $[4] = displayPrompt; $[5] = t3; $[6] = t4; } else { t4 = $[6]; } return t4; } // src/components/NodePorts.tsx var import_compiler_runtime69 = require("react/compiler-runtime"); var import_react62 = __toESM(require("react")); init_port_types(); // src/components/PortBar.tsx var import_compiler_runtime68 = require("react/compiler-runtime"); var import_react61 = __toESM(require("react")); var import_jotai84 = require("jotai"); init_port_types(); init_graph_store(); // src/components/PortHandle.tsx var import_compiler_runtime67 = require("react/compiler-runtime"); var import_react60 = __toESM(require("react")); var import_jsx_runtime38 = require("react/jsx-runtime"); var PORT_ROW_HEIGHT = 24; var TRANSITION_DURATION = "150ms"; function PortHandle(t0) { const $ = (0, import_compiler_runtime67.c)(38); const { port, side, isHovered, showExpanded, isCompatible, isDragTarget, onClick, onPointerDown, onMouseEnter, onMouseLeave } = t0; const canvasStyles = useCanvasStyle(); const t1 = isDragTarget && isCompatible ? port.id : void 0; let t2; if ($[0] !== onClick || $[1] !== port) { t2 = () => onClick(port.id, port); $[0] = onClick; $[1] = port; $[2] = t2; } else { t2 = $[2]; } let t3; if ($[3] !== onPointerDown || $[4] !== port) { t3 = (e) => onPointerDown(port.id, port, e); $[3] = onPointerDown; $[4] = port; $[5] = t3; } else { t3 = $[5]; } let t4; if ($[6] !== onMouseEnter || $[7] !== port) { t4 = () => onMouseEnter(port.id, port); $[6] = onMouseEnter; $[7] = port; $[8] = t4; } else { t4 = $[8]; } const t5 = side === "top" || side === "bottom" ? `${PORT_ROW_HEIGHT}px` : void 0; const t6 = isCompatible ? "pointer" : "not-allowed"; const t7 = isHovered && isCompatible ? canvasStyles.nodes.selectedBorderColor : isDragTarget && !isCompatible ? "rgba(255, 0, 0, 0.1)" : "transparent"; const t8 = isDragTarget && !isCompatible ? 0.5 : 1; let t9; if ($[9] !== t5 || $[10] !== t6 || $[11] !== t7 || $[12] !== t8) { t9 = { flex: 1, minHeight: `${PORT_ROW_HEIGHT}px`, minWidth: t5, display: "flex", alignItems: "center", justifyContent: "center", cursor: t6, backgroundColor: t7, transition: `background-color ${TRANSITION_DURATION}`, opacity: t8 }; $[9] = t5; $[10] = t6; $[11] = t7; $[12] = t8; $[13] = t9; } else { t9 = $[13]; } const t10 = port.color || canvasStyles.nodes.selectedBorderColor; const t11 = isHovered ? "scale(1.5)" : isDragTarget && isCompatible ? "scale(1.3)" : "scale(1)"; const t12 = isHovered && isDragTarget && isCompatible ? `0 0 8px ${port.color || canvasStyles.nodes.selectedBorderColor}` : "none"; let t13; if ($[14] !== t10 || $[15] !== t11 || $[16] !== t12) { t13 = /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { style: { width: "6px", height: "6px", borderRadius: "50%", backgroundColor: t10, transition: `transform ${TRANSITION_DURATION}, box-shadow ${TRANSITION_DURATION}`, transform: t11, boxShadow: t12 } }); $[14] = t10; $[15] = t11; $[16] = t12; $[17] = t13; } else { t13 = $[17]; } let t14; if ($[18] !== canvasStyles || $[19] !== isCompatible || $[20] !== isDragTarget || $[21] !== isHovered || $[22] !== port.id || $[23] !== port.label || $[24] !== showExpanded || $[25] !== side) { t14 = showExpanded && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { style: { marginLeft: side === "left" || side === "right" ? "4px" : 0, marginTop: side === "top" || side === "bottom" ? "4px" : 0, fontSize: "10px", color: isHovered ? "#fff" : isCompatible ? canvasStyles.nodes.defaultBorderColor : "rgba(128, 128, 128, 0.5)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", textDecoration: isDragTarget && !isCompatible ? "line-through" : "none", transition: `opacity ${TRANSITION_DURATION}`, opacity: showExpanded ? 1 : 0 }, children: port.label || port.id }); $[18] = canvasStyles; $[19] = isCompatible; $[20] = isDragTarget; $[21] = isHovered; $[22] = port.id; $[23] = port.label; $[24] = showExpanded; $[25] = side; $[26] = t14; } else { t14 = $[26]; } let t15; if ($[27] !== onMouseLeave || $[28] !== port.id || $[29] !== port.type || $[30] !== t1 || $[31] !== t13 || $[32] !== t14 || $[33] !== t2 || $[34] !== t3 || $[35] !== t4 || $[36] !== t9) { t15 = /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { "data-port-id": port.id, "data-port-type": port.type, "data-drag-port-id": t1, onClick: t2, onPointerDown: t3, onMouseEnter: t4, onMouseLeave, style: t9, children: [t13, t14] }); $[27] = onMouseLeave; $[28] = port.id; $[29] = port.type; $[30] = t1; $[31] = t13; $[32] = t14; $[33] = t2; $[34] = t3; $[35] = t4; $[36] = t9; $[37] = t15; } else { t15 = $[37]; } return t15; } // src/components/PortBar.tsx var import_jsx_runtime39 = require("react/jsx-runtime"); var COLLAPSED_WIDTH = 8; var EXPANDED_WIDTH = 80; var TRANSITION_DURATION2 = "150ms"; function PortBar(t0) { const $ = (0, import_compiler_runtime68.c)(66); const { nodeId, side, ports, nodeWidth, nodeHeight, forceExpanded: t1, onPortClick, onPortHover, isDragTarget: t2, dragSourcePort } = t0; const forceExpanded = t1 === void 0 ? false : t1; const isDragTarget = t2 === void 0 ? false : t2; const [isExpanded, setIsExpanded] = (0, import_react61.useState)(false); const [hoveredPortId, setHoveredPortId] = (0, import_react61.useState)(null); const canvasStyles = useCanvasStyle(); const setEdgeCreation = (0, import_jotai84.useSetAtom)(edgeCreationAtom); const showExpanded = isExpanded || forceExpanded || isDragTarget; let t3; bb0: { const t42 = side === "top" || side === "bottom" ? "row" : "column"; let t52; if ($[0] !== canvasStyles.nodes.defaultBackground || $[1] !== t42) { t52 = { position: "absolute", display: "flex", flexDirection: t42, alignItems: "stretch", transition: `all ${TRANSITION_DURATION2} ease-out`, backgroundColor: canvasStyles.nodes.defaultBackground, borderRadius: "4px", overflow: "hidden", zIndex: 10 }; $[0] = canvasStyles.nodes.defaultBackground; $[1] = t42; $[2] = t52; } else { t52 = $[2]; } const base = t52; const barSize = showExpanded ? EXPANDED_WIDTH : COLLAPSED_WIDTH; const barLength = side === "left" || side === "right" ? Math.max(ports.length * PORT_ROW_HEIGHT, nodeHeight * 0.6) : Math.max(ports.length * PORT_ROW_HEIGHT, nodeWidth * 0.6); switch (side) { case "left": { const t62 = -barSize; const t72 = `${barSize}px`; const t82 = `${barLength}px`; const t92 = `1px solid ${canvasStyles.nodes.defaultBorderColor}`; let t10; if ($[3] !== base || $[4] !== t62 || $[5] !== t72 || $[6] !== t82 || $[7] !== t92) { t10 = { ...base, left: t62, top: "50%", transform: "translateY(-50%)", width: t72, height: t82, borderRight: t92, borderTopRightRadius: 0, borderBottomRightRadius: 0 }; $[3] = base; $[4] = t62; $[5] = t72; $[6] = t82; $[7] = t92; $[8] = t10; } else { t10 = $[8]; } t3 = t10; break bb0; } case "right": { const t62 = -barSize; const t72 = `${barSize}px`; const t82 = `${barLength}px`; const t92 = `1px solid ${canvasStyles.nodes.defaultBorderColor}`; let t10; if ($[9] !== base || $[10] !== t62 || $[11] !== t72 || $[12] !== t82 || $[13] !== t92) { t10 = { ...base, right: t62, top: "50%", transform: "translateY(-50%)", width: t72, height: t82, borderLeft: t92, borderTopLeftRadius: 0, borderBottomLeftRadius: 0 }; $[9] = base; $[10] = t62; $[11] = t72; $[12] = t82; $[13] = t92; $[14] = t10; } else { t10 = $[14]; } t3 = t10; break bb0; } case "top": { const t62 = -barSize; const t72 = `${barSize}px`; const t82 = `${barLength}px`; const t92 = `1px solid ${canvasStyles.nodes.defaultBorderColor}`; let t10; if ($[15] !== base || $[16] !== t62 || $[17] !== t72 || $[18] !== t82 || $[19] !== t92) { t10 = { ...base, top: t62, left: "50%", transform: "translateX(-50%)", height: t72, width: t82, borderBottom: t92, borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }; $[15] = base; $[16] = t62; $[17] = t72; $[18] = t82; $[19] = t92; $[20] = t10; } else { t10 = $[20]; } t3 = t10; break bb0; } case "bottom": { const t62 = -barSize; const t72 = `${barSize}px`; const t82 = `${barLength}px`; const t92 = `1px solid ${canvasStyles.nodes.defaultBorderColor}`; let t10; if ($[21] !== base || $[22] !== t62 || $[23] !== t72 || $[24] !== t82 || $[25] !== t92) { t10 = { ...base, bottom: t62, left: "50%", transform: "translateX(-50%)", height: t72, width: t82, borderTop: t92, borderTopLeftRadius: 0, borderTopRightRadius: 0 }; $[21] = base; $[22] = t62; $[23] = t72; $[24] = t82; $[25] = t92; $[26] = t10; } else { t10 = $[26]; } t3 = t10; break bb0; } } t3 = void 0; } const barStyle = t3; let t4; if ($[27] !== dragSourcePort || $[28] !== isDragTarget) { t4 = (port) => { if (!isDragTarget || !dragSourcePort) { return true; } return arePortsCompatible(dragSourcePort, port); }; $[27] = dragSourcePort; $[28] = isDragTarget; $[29] = t4; } else { t4 = $[29]; } const isPortCompatible = t4; let t5; if ($[30] !== isDragTarget) { t5 = () => { if (!isDragTarget) { setIsExpanded(true); } }; $[30] = isDragTarget; $[31] = t5; } else { t5 = $[31]; } const handleMouseEnter = t5; let t6; if ($[32] !== forceExpanded || $[33] !== onPortHover) { t6 = () => { if (!forceExpanded) { setIsExpanded(false); setHoveredPortId(null); onPortHover?.(null, null); } }; $[32] = forceExpanded; $[33] = onPortHover; $[34] = t6; } else { t6 = $[34]; } const handleMouseLeave = t6; let t7; if ($[35] !== nodeHeight || $[36] !== nodeId || $[37] !== nodeWidth || $[38] !== setEdgeCreation) { t7 = (portId, port_0, e) => { if (e.button !== 0) { return; } e.stopPropagation(); e.preventDefault(); const portPos = calculatePortPosition(0, 0, nodeWidth, nodeHeight, port_0); setEdgeCreation({ isCreating: true, sourceNodeId: nodeId, sourceNodePosition: { x: portPos.x, y: portPos.y }, targetPosition: { x: portPos.x, y: portPos.y }, hoveredTargetNodeId: null, sourceHandle: `widget:${nodeId}:${port_0.type === "input" ? "input" : "output"}:${port_0.id}`, targetHandle: null, sourcePort: portId, targetPort: null, snappedTargetPosition: null }); }; $[35] = nodeHeight; $[36] = nodeId; $[37] = nodeWidth; $[38] = setEdgeCreation; $[39] = t7; } else { t7 = $[39]; } const handlePortPointerDown = t7; let t8; if ($[40] !== handlePortPointerDown || $[41] !== hoveredPortId || $[42] !== isDragTarget || $[43] !== isPortCompatible || $[44] !== onPortClick || $[45] !== onPortHover || $[46] !== ports || $[47] !== showExpanded || $[48] !== side) { let t92; if ($[50] !== handlePortPointerDown || $[51] !== hoveredPortId || $[52] !== isDragTarget || $[53] !== isPortCompatible || $[54] !== onPortClick || $[55] !== onPortHover || $[56] !== showExpanded || $[57] !== side) { t92 = (port_1) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(PortHandle, { port: port_1, side, isHovered: hoveredPortId === port_1.id, showExpanded, isCompatible: isPortCompatible(port_1), isDragTarget, onClick: (id, p) => onPortClick?.(id, p), onPointerDown: handlePortPointerDown, onMouseEnter: (id_0, p_0) => { setHoveredPortId(id_0); onPortHover?.(id_0, p_0); }, onMouseLeave: () => { setHoveredPortId(null); onPortHover?.(null, null); } }, port_1.id); $[50] = handlePortPointerDown; $[51] = hoveredPortId; $[52] = isDragTarget; $[53] = isPortCompatible; $[54] = onPortClick; $[55] = onPortHover; $[56] = showExpanded; $[57] = side; $[58] = t92; } else { t92 = $[58]; } t8 = ports.map(t92); $[40] = handlePortPointerDown; $[41] = hoveredPortId; $[42] = isDragTarget; $[43] = isPortCompatible; $[44] = onPortClick; $[45] = onPortHover; $[46] = ports; $[47] = showExpanded; $[48] = side; $[49] = t8; } else { t8 = $[49]; } let t9; if ($[59] !== barStyle || $[60] !== handleMouseEnter || $[61] !== handleMouseLeave || $[62] !== nodeId || $[63] !== side || $[64] !== t8) { t9 = /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { style: barStyle, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, "data-port-bar": side, "data-node-id": nodeId, children: t8 }); $[59] = barStyle; $[60] = handleMouseEnter; $[61] = handleMouseLeave; $[62] = nodeId; $[63] = side; $[64] = t8; $[65] = t9; } else { t9 = $[65]; } return t9; } // src/components/NodePorts.tsx var import_jsx_runtime40 = require("react/jsx-runtime"); function NodePorts(t0) { const $ = (0, import_compiler_runtime69.c)(25); const { nodeId, nodeWidth, nodeHeight, ports: portsProp, sides, onStartConnection, onPortHover, isDragTarget: t1, dragSourcePort } = t0; const isDragTarget = t1 === void 0 ? false : t1; let portsBySide; if ($[0] !== portsProp) { const ports = getNodePorts(portsProp); portsBySide = { top: [], right: [], bottom: [], left: [] }; for (const port of ports) { portsBySide[port.side].push(port); } $[0] = portsProp; $[1] = portsBySide; } else { portsBySide = $[1]; } let t2; if ($[2] !== sides) { t2 = sides || ["left", "right", "top", "bottom"]; $[2] = sides; $[3] = t2; } else { t2 = $[3]; } const sidesToRender = t2; let t3; if ($[4] !== dragSourcePort || $[5] !== isDragTarget || $[6] !== nodeHeight || $[7] !== nodeId || $[8] !== nodeWidth || $[9] !== onPortHover || $[10] !== onStartConnection || $[11] !== portsBySide || $[12] !== sidesToRender) { let t42; if ($[14] !== dragSourcePort || $[15] !== isDragTarget || $[16] !== nodeHeight || $[17] !== nodeId || $[18] !== nodeWidth || $[19] !== onPortHover || $[20] !== onStartConnection || $[21] !== portsBySide) { t42 = (side) => { const sidePorts = portsBySide[side]; if (sidePorts.length === 0) { return null; } return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(PortBar, { nodeId, side, ports: sidePorts, nodeWidth, nodeHeight, onPortClick: (portId, port_0) => onStartConnection?.(nodeId, portId, port_0), onPortHover: (portId_0, port_1) => onPortHover?.(nodeId, portId_0, port_1), isDragTarget, dragSourcePort }, side); }; $[14] = dragSourcePort; $[15] = isDragTarget; $[16] = nodeHeight; $[17] = nodeId; $[18] = nodeWidth; $[19] = onPortHover; $[20] = onStartConnection; $[21] = portsBySide; $[22] = t42; } else { t42 = $[22]; } t3 = sidesToRender.map(t42); $[4] = dragSourcePort; $[5] = isDragTarget; $[6] = nodeHeight; $[7] = nodeId; $[8] = nodeWidth; $[9] = onPortHover; $[10] = onStartConnection; $[11] = portsBySide; $[12] = sidesToRender; $[13] = t3; } else { t3 = $[13]; } let t4; if ($[23] !== t3) { t4 = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_jsx_runtime40.Fragment, { children: t3 }); $[23] = t3; $[24] = t4; } else { t4 = $[24]; } return t4; } function getPortWorldPosition(nodeX, nodeY, nodeWidth, nodeHeight, port) { return calculatePortPosition(nodeX, nodeY, nodeWidth, nodeHeight, port); } // src/components/ViewportControls.tsx var import_compiler_runtime70 = require("react/compiler-runtime"); var import_react63 = __toESM(require("react")); var import_jotai85 = require("jotai"); init_viewport_store(); init_input_store(); var import_jsx_runtime41 = require("react/jsx-runtime"); function ViewportControls(t0) { const $ = (0, import_compiler_runtime70.c)(39); const { minZoom: t1, maxZoom: t2, zoomStep: t3, position: t4, alwaysVisible: t5, className, style } = t0; const minZoom = t1 === void 0 ? 0.1 : t1; const maxZoom = t2 === void 0 ? 5 : t2; const zoomStep = t3 === void 0 ? 0.25 : t3; const position = t4 === void 0 ? "bottom-right" : t4; const alwaysVisible = t5 === void 0 ? false : t5; const [zoom, setZoom] = (0, import_jotai85.useAtom)(zoomAtom); (0, import_jotai85.useAtom)(panAtom); const isTouchDevice = (0, import_jotai85.useAtomValue)(isTouchDeviceAtom); const resetViewport = (0, import_jotai85.useSetAtom)(resetViewportAtom); let t6; if ($[0] !== maxZoom || $[1] !== setZoom || $[2] !== zoom || $[3] !== zoomStep) { t6 = () => { setZoom(Math.min(maxZoom, zoom + zoomStep)); }; $[0] = maxZoom; $[1] = setZoom; $[2] = zoom; $[3] = zoomStep; $[4] = t6; } else { t6 = $[4]; } const handleZoomIn = t6; let t7; if ($[5] !== minZoom || $[6] !== setZoom || $[7] !== zoom || $[8] !== zoomStep) { t7 = () => { setZoom(Math.max(minZoom, zoom - zoomStep)); }; $[5] = minZoom; $[6] = setZoom; $[7] = zoom; $[8] = zoomStep; $[9] = t7; } else { t7 = $[9]; } const handleZoomOut = t7; let t8; if ($[10] !== resetViewport) { t8 = () => { resetViewport(); }; $[10] = resetViewport; $[11] = t8; } else { t8 = $[11]; } const handleReset = t8; if (!alwaysVisible && !isTouchDevice) { return null; } let t9; if ($[12] !== position) { t9 = getPositionStyles(position); $[12] = position; $[13] = t9; } else { t9 = $[13]; } const positionStyles = t9; let t10; if ($[14] !== zoom) { t10 = Math.round(zoom * 100); $[14] = zoom; $[15] = t10; } else { t10 = $[15]; } const zoomPercent = t10; let t11; if ($[16] !== positionStyles || $[17] !== style) { t11 = { position: "absolute", ...positionStyles, display: "flex", flexDirection: "column", gap: "4px", zIndex: 100, pointerEvents: "auto", ...style }; $[16] = positionStyles; $[17] = style; $[18] = t11; } else { t11 = $[18]; } const t12 = zoom >= maxZoom; let t13; if ($[19] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t13 = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PlusIcon, {}); $[19] = t13; } else { t13 = $[19]; } let t14; if ($[20] !== handleZoomIn || $[21] !== t12) { t14 = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ControlButton, { onClick: handleZoomIn, disabled: t12, "aria-label": "Zoom in", children: t13 }); $[20] = handleZoomIn; $[21] = t12; $[22] = t14; } else { t14 = $[22]; } const t15 = `Reset zoom (${zoomPercent}%)`; const t16 = `${zoomPercent}%`; let t17; if ($[23] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t17 = { fontSize: "10px", fontWeight: 600 }; $[23] = t17; } else { t17 = $[23]; } let t18; if ($[24] !== handleReset || $[25] !== t15 || $[26] !== t16 || $[27] !== zoomPercent) { t18 = /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(ControlButton, { onClick: handleReset, "aria-label": t15, title: t16, style: t17, children: [zoomPercent, "%"] }); $[24] = handleReset; $[25] = t15; $[26] = t16; $[27] = zoomPercent; $[28] = t18; } else { t18 = $[28]; } const t19 = zoom <= minZoom; let t20; if ($[29] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t20 = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(MinusIcon, {}); $[29] = t20; } else { t20 = $[29]; } let t21; if ($[30] !== handleZoomOut || $[31] !== t19) { t21 = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ControlButton, { onClick: handleZoomOut, disabled: t19, "aria-label": "Zoom out", children: t20 }); $[30] = handleZoomOut; $[31] = t19; $[32] = t21; } else { t21 = $[32]; } let t22; if ($[33] !== className || $[34] !== t11 || $[35] !== t14 || $[36] !== t18 || $[37] !== t21) { t22 = /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className, style: t11, children: [t14, t18, t21] }); $[33] = className; $[34] = t11; $[35] = t14; $[36] = t18; $[37] = t21; $[38] = t22; } else { t22 = $[38]; } return t22; } function ControlButton(t0) { const $ = (0, import_compiler_runtime70.c)(21); let children; let disabled; let onClick; let props; let style; if ($[0] !== t0) { ({ children, style, disabled, onClick, ...props } = t0); $[0] = t0; $[1] = children; $[2] = disabled; $[3] = onClick; $[4] = props; $[5] = style; } else { children = $[1]; disabled = $[2]; onClick = $[3]; props = $[4]; style = $[5]; } const [pulsing, setPulsing] = (0, import_react63.useState)(false); let t1; if ($[6] !== disabled || $[7] !== onClick) { t1 = (e) => { if (!disabled) { setPulsing(true); } onClick?.(e); }; $[6] = disabled; $[7] = onClick; $[8] = t1; } else { t1 = $[8]; } const handleClick = t1; const t2 = pulsing ? "canvas-tap-pulse" : void 0; let t3; if ($[9] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t3 = () => setPulsing(false); $[9] = t3; } else { t3 = $[9]; } const t4 = disabled ? "default" : "pointer"; const t5 = disabled ? 0.4 : 1; let t6; if ($[10] !== style || $[11] !== t4 || $[12] !== t5) { t6 = { width: "44px", height: "44px", borderRadius: "12px", border: "1px solid var(--canvas-control-border, rgba(255,255,255,0.1))", backgroundColor: "var(--canvas-control-bg, rgba(30,30,30,0.85))", color: "var(--canvas-control-fg, rgba(255,255,255,0.9))", display: "flex", alignItems: "center", justifyContent: "center", cursor: t4, opacity: t5, touchAction: "manipulation", WebkitTapHighlightColor: "transparent", userSelect: "none", transition: "background-color 150ms, opacity 150ms", backdropFilter: "blur(12px)", WebkitBackdropFilter: "blur(12px)", padding: 0, ...style }; $[10] = style; $[11] = t4; $[12] = t5; $[13] = t6; } else { t6 = $[13]; } let t7; if ($[14] !== children || $[15] !== disabled || $[16] !== handleClick || $[17] !== props || $[18] !== t2 || $[19] !== t6) { t7 = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { ...props, disabled, className: t2, onAnimationEnd: t3, onClick: handleClick, style: t6, "data-no-drag": "true", children }); $[14] = children; $[15] = disabled; $[16] = handleClick; $[17] = props; $[18] = t2; $[19] = t6; $[20] = t7; } else { t7 = $[20]; } return t7; } function PlusIcon() { const $ = (0, import_compiler_runtime70.c)(1); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("line", { x1: "9", y1: "4", x2: "9", y2: "14" }), /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("line", { x1: "4", y1: "9", x2: "14", y2: "9" })] }); $[0] = t0; } else { t0 = $[0]; } return t0; } function MinusIcon() { const $ = (0, import_compiler_runtime70.c)(1); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("line", { x1: "4", y1: "9", x2: "14", y2: "9" }) }); $[0] = t0; } else { t0 = $[0]; } return t0; } function getPositionStyles(position) { switch (position) { case "bottom-right": return { bottom: "max(16px, env(safe-area-inset-bottom, 16px))", right: "max(16px, env(safe-area-inset-right, 16px))" }; case "bottom-left": return { bottom: "max(16px, env(safe-area-inset-bottom, 16px))", left: "max(16px, env(safe-area-inset-left, 16px))" }; case "top-right": return { top: "max(16px, env(safe-area-inset-top, 16px))", right: "max(16px, env(safe-area-inset-right, 16px))" }; case "top-left": return { top: "max(16px, env(safe-area-inset-top, 16px))", left: "max(16px, env(safe-area-inset-left, 16px))" }; default: return { bottom: "max(16px, env(safe-area-inset-bottom, 16px))", right: "max(16px, env(safe-area-inset-right, 16px))" }; } } // src/components/SelectionOverlay.tsx var import_compiler_runtime71 = require("react/compiler-runtime"); var import_react64 = __toESM(require("react")); var import_jotai86 = require("jotai"); init_selection_path_store(); init_viewport_store(); var import_jsx_runtime42 = require("react/jsx-runtime"); function SelectionOverlay(t0) { const $ = (0, import_compiler_runtime71.c)(18); const { fillColor: t1, strokeColor: t2, strokeWidth: t3 } = t0; const fillColor = t1 === void 0 ? "rgba(99, 102, 241, 0.15)" : t1; const strokeColor = t2 === void 0 ? "rgba(99, 102, 241, 0.6)" : t2; const strokeWidth = t3 === void 0 ? 1.5 : t3; const path = (0, import_jotai86.useAtomValue)(selectionPathAtom); const selectionRect = (0, import_jotai86.useAtomValue)(selectionRectAtom); (0, import_jotai86.useAtomValue)(panAtom); const zoom = (0, import_jotai86.useAtomValue)(zoomAtom); if (!path) { return null; } let t4; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t4 = { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none", overflow: "visible", zIndex: 9998 }; $[0] = t4; } else { t4 = $[0]; } let t5; if ($[1] !== fillColor || $[2] !== path.type || $[3] !== selectionRect || $[4] !== strokeColor || $[5] !== strokeWidth || $[6] !== zoom) { t5 = path.type === "rect" && selectionRect && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("rect", { x: selectionRect.x, y: selectionRect.y, width: selectionRect.width, height: selectionRect.height, fill: fillColor, stroke: strokeColor, strokeWidth: strokeWidth / zoom, strokeDasharray: `${4 / zoom} ${4 / zoom}` }); $[1] = fillColor; $[2] = path.type; $[3] = selectionRect; $[4] = strokeColor; $[5] = strokeWidth; $[6] = zoom; $[7] = t5; } else { t5 = $[7]; } let t6; if ($[8] !== fillColor || $[9] !== path.points || $[10] !== path.type || $[11] !== strokeColor || $[12] !== strokeWidth || $[13] !== zoom) { t6 = path.type === "lasso" && path.points.length >= 2 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("polygon", { points: path.points.map(_temp19).join(" "), fill: fillColor, stroke: strokeColor, strokeWidth: strokeWidth / zoom, strokeLinejoin: "round" }); $[8] = fillColor; $[9] = path.points; $[10] = path.type; $[11] = strokeColor; $[12] = strokeWidth; $[13] = zoom; $[14] = t6; } else { t6 = $[14]; } let t7; if ($[15] !== t5 || $[16] !== t6) { t7 = /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("svg", { style: t4, children: [t5, t6] }); $[15] = t5; $[16] = t6; $[17] = t7; } else { t7 = $[17]; } return t7; } function _temp19(p) { return `${p.x},${p.y}`; } // src/components/Minimap.tsx var import_compiler_runtime72 = require("react/compiler-runtime"); var import_react65 = __toESM(require("react")); var import_jotai87 = require("jotai"); init_graph_derived(); init_graph_store(); init_viewport_store(); init_selection_store(); // src/components/minimap-utils.ts function computeGraphBounds(nodes) { if (nodes.length === 0) return null; let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity; for (const node of nodes) { const x = node.position.x; const y = node.position.y; const w = node.width ?? 200; const h = node.height ?? 100; if (x < minX) minX = x; if (y < minY) minY = y; if (x + w > maxX) maxX = x + w; if (y + h > maxY) maxY = y + h; } return { minX, minY, maxX, maxY }; } function getMinimapTransform(bounds, minimapWidth, minimapHeight, padding = 20) { const graphW = bounds.maxX - bounds.minX; const graphH = bounds.maxY - bounds.minY; if (graphW <= 0 || graphH <= 0) return null; const scaleX = (minimapWidth - padding * 2) / graphW; const scaleY = (minimapHeight - padding * 2) / graphH; const scale = Math.min(scaleX, scaleY); const offsetX = padding + (minimapWidth - padding * 2 - graphW * scale) / 2; const offsetY = padding + (minimapHeight - padding * 2 - graphH * scale) / 2; return { scale, offsetX, offsetY, minX: bounds.minX, minY: bounds.minY }; } function minimapToWorld(mx, my, transform) { return { x: (mx - transform.offsetX) / transform.scale + transform.minX, y: (my - transform.offsetY) / transform.scale + transform.minY }; } // src/components/Minimap.tsx var import_jsx_runtime43 = require("react/jsx-runtime"); function Minimap(t0) { const $ = (0, import_compiler_runtime72.c)(60); const { width: t1, height: t2, backgroundColor: t3, nodeColor: t4, selectedNodeColor: t5, viewportColor: t6, borderColor: t7, edgeColor: t8, position: t9, offset: t10, className } = t0; const width = t1 === void 0 ? 200 : t1; const height = t2 === void 0 ? 150 : t2; const backgroundColor = t3 === void 0 ? "rgba(0, 0, 0, 0.7)" : t3; const nodeColor = t4 === void 0 ? "rgba(148, 163, 184, 0.6)" : t4; const selectedNodeColor = t5 === void 0 ? "rgba(99, 102, 241, 0.8)" : t5; const viewportColor = t6 === void 0 ? "rgba(255, 255, 255, 0.3)" : t6; const borderColor = t7 === void 0 ? "rgba(255, 255, 255, 0.2)" : t7; const edgeColor = t8 === void 0 ? "rgba(148, 163, 184, 0.3)" : t8; const position = t9 === void 0 ? "bottom-right" : t9; const offset = t10 === void 0 ? 12 : t10; const canvasRef = (0, import_react65.useRef)(null); const nodes = (0, import_jotai87.useAtomValue)(uiNodesAtom); const graph = (0, import_jotai87.useAtomValue)(graphAtom); const pan = (0, import_jotai87.useAtomValue)(panAtom); const zoom = (0, import_jotai87.useAtomValue)(zoomAtom); const viewportRect = (0, import_jotai87.useAtomValue)(viewportRectAtom); const selectedNodeIds = (0, import_jotai87.useAtomValue)(selectedNodeIdsAtom); const setPan = (0, import_jotai87.useSetAtom)(panAtom); const isDragging = (0, import_react65.useRef)(false); let t11; if ($[0] !== nodes) { t11 = computeGraphBounds(nodes); $[0] = nodes; $[1] = t11; } else { t11 = $[1]; } const graphBounds = t11; let t12; if ($[2] !== graphBounds || $[3] !== height || $[4] !== width) { t12 = (mx, my) => { if (!graphBounds) { return null; } const t = getMinimapTransform(graphBounds, width, height); if (!t) { return null; } return minimapToWorld(mx, my, t); }; $[2] = graphBounds; $[3] = height; $[4] = width; $[5] = t12; } else { t12 = $[5]; } const minimapToWorldCoords = t12; let t13; if ($[6] !== setPan || $[7] !== viewportRect || $[8] !== zoom) { t13 = (worldX, worldY) => { if (!viewportRect) { return; } const newPanX = -worldX * zoom + viewportRect.width / 2; const newPanY = -worldY * zoom + viewportRect.height / 2; setPan({ x: newPanX, y: newPanY }); }; $[6] = setPan; $[7] = viewportRect; $[8] = zoom; $[9] = t13; } else { t13 = $[9]; } const panToWorld = t13; let t14; if ($[10] !== backgroundColor || $[11] !== borderColor || $[12] !== edgeColor || $[13] !== graph || $[14] !== graphBounds || $[15] !== height || $[16] !== nodeColor || $[17] !== nodes || $[18] !== pan || $[19] !== selectedNodeColor || $[20] !== selectedNodeIds || $[21] !== viewportColor || $[22] !== viewportRect || $[23] !== width || $[24] !== zoom) { t14 = () => { const canvas = canvasRef.current; if (!canvas) { return; } const ctx = canvas.getContext("2d"); if (!ctx) { return; } const dpr = window.devicePixelRatio || 1; canvas.width = width * dpr; canvas.height = height * dpr; ctx.scale(dpr, dpr); ctx.fillStyle = backgroundColor; ctx.fillRect(0, 0, width, height); const t_0 = graphBounds ? getMinimapTransform(graphBounds, width, height) : null; if (!t_0) { return; } const nodePositions = /* @__PURE__ */ new Map(); for (const node of nodes) { const x = (node.position.x - t_0.minX) * t_0.scale + t_0.offsetX; const y = (node.position.y - t_0.minY) * t_0.scale + t_0.offsetY; const w = Math.max((node.width ?? 200) * t_0.scale, 2); const h = Math.max((node.height ?? 100) * t_0.scale, 2); nodePositions.set(node.id, { cx: x + w / 2, cy: y + h / 2 }); } ctx.strokeStyle = edgeColor; ctx.lineWidth = 1; graph.forEachEdge((_key, _attrs, source, target) => { const from = nodePositions.get(source); const to = nodePositions.get(target); if (!from || !to) { return; } ctx.beginPath(); ctx.moveTo(from.cx, from.cy); ctx.lineTo(to.cx, to.cy); ctx.stroke(); }); for (const node_0 of nodes) { const pos = nodePositions.get(node_0.id); if (!pos) { continue; } const w_0 = Math.max((node_0.width ?? 200) * t_0.scale, 2); const h_0 = Math.max((node_0.height ?? 100) * t_0.scale, 2); ctx.fillStyle = selectedNodeIds.has(node_0.id) ? selectedNodeColor : nodeColor; ctx.fillRect(pos.cx - w_0 / 2, pos.cy - h_0 / 2, w_0, h_0); } if (viewportRect && viewportRect.width > 0) { const vpWorldMinX = -pan.x / zoom; const vpWorldMinY = -pan.y / zoom; const vpWorldW = viewportRect.width / zoom; const vpWorldH = viewportRect.height / zoom; const vpX = (vpWorldMinX - t_0.minX) * t_0.scale + t_0.offsetX; const vpY = (vpWorldMinY - t_0.minY) * t_0.scale + t_0.offsetY; const vpW = vpWorldW * t_0.scale; const vpH = vpWorldH * t_0.scale; ctx.strokeStyle = viewportColor; ctx.lineWidth = 1.5; ctx.strokeRect(vpX, vpY, vpW, vpH); ctx.fillStyle = viewportColor.replace(/[\d.]+\)$/, "0.1)"); ctx.fillRect(vpX, vpY, vpW, vpH); } ctx.strokeStyle = borderColor; ctx.lineWidth = 1; ctx.strokeRect(0, 0, width, height); }; $[10] = backgroundColor; $[11] = borderColor; $[12] = edgeColor; $[13] = graph; $[14] = graphBounds; $[15] = height; $[16] = nodeColor; $[17] = nodes; $[18] = pan; $[19] = selectedNodeColor; $[20] = selectedNodeIds; $[21] = viewportColor; $[22] = viewportRect; $[23] = width; $[24] = zoom; $[25] = t14; } else { t14 = $[25]; } let t15; if ($[26] !== backgroundColor || $[27] !== borderColor || $[28] !== edgeColor || $[29] !== graph || $[30] !== height || $[31] !== nodeColor || $[32] !== nodes || $[33] !== pan || $[34] !== selectedNodeColor || $[35] !== selectedNodeIds || $[36] !== viewportColor || $[37] !== viewportRect || $[38] !== width || $[39] !== zoom) { t15 = [nodes, graph, pan, zoom, viewportRect, selectedNodeIds, width, height, backgroundColor, nodeColor, selectedNodeColor, viewportColor, borderColor, edgeColor, getMinimapTransform]; $[26] = backgroundColor; $[27] = borderColor; $[28] = edgeColor; $[29] = graph; $[30] = height; $[31] = nodeColor; $[32] = nodes; $[33] = pan; $[34] = selectedNodeColor; $[35] = selectedNodeIds; $[36] = viewportColor; $[37] = viewportRect; $[38] = width; $[39] = zoom; $[40] = t15; } else { t15 = $[40]; } (0, import_react65.useEffect)(t14, t15); let t16; if ($[41] !== minimapToWorldCoords || $[42] !== panToWorld) { t16 = (e) => { e.stopPropagation(); e.preventDefault(); isDragging.current = true; e.target.setPointerCapture(e.pointerId); const rect = canvasRef.current?.getBoundingClientRect(); if (!rect) { return; } const mx_0 = e.clientX - rect.left; const my_0 = e.clientY - rect.top; const worldPos = minimapToWorldCoords(mx_0, my_0); if (worldPos) { panToWorld(worldPos.x, worldPos.y); } }; $[41] = minimapToWorldCoords; $[42] = panToWorld; $[43] = t16; } else { t16 = $[43]; } const handlePointerDown = t16; let t17; if ($[44] !== minimapToWorldCoords || $[45] !== panToWorld) { t17 = (e_0) => { if (!isDragging.current) { return; } e_0.stopPropagation(); const rect_0 = canvasRef.current?.getBoundingClientRect(); if (!rect_0) { return; } const mx_1 = e_0.clientX - rect_0.left; const my_1 = e_0.clientY - rect_0.top; const worldPos_0 = minimapToWorldCoords(mx_1, my_1); if (worldPos_0) { panToWorld(worldPos_0.x, worldPos_0.y); } }; $[44] = minimapToWorldCoords; $[45] = panToWorld; $[46] = t17; } else { t17 = $[46]; } const handlePointerMove = t17; let t18; if ($[47] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t18 = (e_1) => { isDragging.current = false; e_1.target.releasePointerCapture(e_1.pointerId); }; $[47] = t18; } else { t18 = $[47]; } const handlePointerUp = t18; let t19; if ($[48] !== offset || $[49] !== position) { t19 = { position: "absolute", zIndex: 9999, ...position.includes("top") ? { top: offset } : { bottom: offset }, ...position.includes("left") ? { left: offset } : { right: offset } }; $[48] = offset; $[49] = position; $[50] = t19; } else { t19 = $[50]; } const positionStyles = t19; let t20; if ($[51] !== height || $[52] !== positionStyles || $[53] !== width) { t20 = { ...positionStyles, width, height, borderRadius: 6, cursor: "pointer", touchAction: "none" }; $[51] = height; $[52] = positionStyles; $[53] = width; $[54] = t20; } else { t20 = $[54]; } let t21; if ($[55] !== className || $[56] !== handlePointerDown || $[57] !== handlePointerMove || $[58] !== t20) { t21 = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("canvas", { ref: canvasRef, className, style: t20, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp }); $[55] = className; $[56] = handlePointerDown; $[57] = handlePointerMove; $[58] = t20; $[59] = t21; } else { t21 = $[59]; } return t21; } // src/components/GroupNode.tsx var import_compiler_runtime73 = require("react/compiler-runtime"); var import_react66 = __toESM(require("react")); var import_jotai88 = require("jotai"); init_group_store(); var import_jsx_runtime44 = require("react/jsx-runtime"); function GroupNode(t0) { const $ = (0, import_compiler_runtime73.c)(42); const { node, isSelected, renderHeader, className, style } = t0; const collapsed = (0, import_jotai88.useAtomValue)(collapsedGroupsAtom); const toggleCollapse = (0, import_jotai88.useSetAtom)(toggleGroupCollapseAtom); const getChildCount = (0, import_jotai88.useAtomValue)(groupChildCountAtom); let childCount; let isCollapsed; let t1; let t2; let t3; let t4; let t5; let t6; let t7; if ($[0] !== className || $[1] !== collapsed || $[2] !== getChildCount || $[3] !== isSelected || $[4] !== node || $[5] !== renderHeader || $[6] !== style || $[7] !== toggleCollapse) { isCollapsed = collapsed.has(node.id); childCount = getChildCount(node.id); let t82; if ($[17] !== node.id || $[18] !== toggleCollapse) { t82 = (e) => { e.stopPropagation(); toggleCollapse(node.id); }; $[17] = node.id; $[18] = toggleCollapse; $[19] = t82; } else { t82 = $[19]; } const handleToggle = t82; t6 = className; const t92 = `2px ${isSelected ? "solid" : "dashed"} ${isSelected ? "#4A90D9" : "#666"}`; const t102 = isCollapsed ? "rgba(100, 100, 100, 0.15)" : "rgba(100, 100, 100, 0.05)"; if ($[20] !== style || $[21] !== t102 || $[22] !== t92) { t7 = { width: "100%", height: "100%", border: t92, borderRadius: "8px", background: t102, display: "flex", flexDirection: "column", overflow: "hidden", ...style }; $[20] = style; $[21] = t102; $[22] = t92; $[23] = t7; } else { t7 = $[23]; } if ($[24] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t1 = { display: "flex", alignItems: "center", gap: "6px", padding: "6px 10px", background: "rgba(100, 100, 100, 0.2)", borderBottom: "1px solid rgba(100, 100, 100, 0.2)", cursor: "pointer", userSelect: "none", fontSize: "13px", color: "#ccc" }; $[24] = t1; } else { t1 = $[24]; } t2 = handleToggle; t3 = "true"; let t11; if ($[25] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t11 = { fontSize: "10px", width: "14px", textAlign: "center" }; $[25] = t11; } else { t11 = $[25]; } const t12 = isCollapsed ? "\u25B6" : "\u25BC"; if ($[26] !== t12) { t4 = /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { style: t11, children: t12 }); $[26] = t12; $[27] = t4; } else { t4 = $[27]; } t5 = renderHeader ? renderHeader({ node, isCollapsed, childCount }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { style: { fontWeight: 500 }, children: node.label || "Group" }), /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { style: { opacity: 0.6, marginLeft: "auto" }, children: [childCount, " node", childCount !== 1 ? "s" : ""] })] }); $[0] = className; $[1] = collapsed; $[2] = getChildCount; $[3] = isSelected; $[4] = node; $[5] = renderHeader; $[6] = style; $[7] = toggleCollapse; $[8] = childCount; $[9] = isCollapsed; $[10] = t1; $[11] = t2; $[12] = t3; $[13] = t4; $[14] = t5; $[15] = t6; $[16] = t7; } else { childCount = $[8]; isCollapsed = $[9]; t1 = $[10]; t2 = $[11]; t3 = $[12]; t4 = $[13]; t5 = $[14]; t6 = $[15]; t7 = $[16]; } let t8; if ($[28] !== t1 || $[29] !== t2 || $[30] !== t3 || $[31] !== t4 || $[32] !== t5) { t8 = /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { style: t1, onClick: t2, "data-no-drag": t3, children: [t4, t5] }); $[28] = t1; $[29] = t2; $[30] = t3; $[31] = t4; $[32] = t5; $[33] = t8; } else { t8 = $[33]; } let t9; if ($[34] !== childCount || $[35] !== isCollapsed) { t9 = isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", color: "#888", fontSize: "12px", fontStyle: "italic" }, children: [childCount, " collapsed node", childCount !== 1 ? "s" : ""] }); $[34] = childCount; $[35] = isCollapsed; $[36] = t9; } else { t9 = $[36]; } let t10; if ($[37] !== t6 || $[38] !== t7 || $[39] !== t8 || $[40] !== t9) { t10 = /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: t6, style: t7, children: [t8, t9] }); $[37] = t6; $[38] = t7; $[39] = t8; $[40] = t9; $[41] = t10; } else { t10 = $[41]; } return t10; } // src/components/CanvasAnimations.tsx var import_compiler_runtime74 = require("react/compiler-runtime"); var import_react67 = __toESM(require("react")); var import_jsx_runtime45 = require("react/jsx-runtime"); var CANVAS_ANIMATION_STYLES = ` @keyframes canvas-search-pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.5); } 50% { box-shadow: 0 0 0 4px rgba(245, 158, 11, 0.3); } } .canvas-search-highlight { animation: canvas-search-pulse 1.5s ease-in-out infinite; } @keyframes canvas-edge-fade-in { from { opacity: 0; } to { opacity: 1; } } @keyframes canvas-edge-fade-out { from { opacity: 1; } to { opacity: 0; } } .canvas-edge-enter { animation: canvas-edge-fade-in 300ms ease-out forwards; } .canvas-edge-exit { animation: canvas-edge-fade-out 300ms ease-out forwards; pointer-events: none; } @keyframes canvas-drop-target-pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.5); } 50% { box-shadow: 0 0 0 6px rgba(59, 130, 246, 0.3); } } .canvas-drop-target { animation: canvas-drop-target-pulse 1s ease-in-out infinite; outline: 2px dashed #3b82f6; outline-offset: 2px; } @keyframes canvas-tap-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.08); } } .canvas-tap-pulse { animation: canvas-tap-pulse 250ms ease-out; } /* iPad / touch: hide cursor indicators that aren't useful on coarse pointers */ @media (pointer: coarse) { [data-canvas-root], [data-canvas-root] * { cursor: default !important; } } /* Respect prefers-reduced-motion: disable canvas CSS animations */ @media (prefers-reduced-motion: reduce) { .canvas-search-highlight, .canvas-drop-target, .canvas-tap-pulse { animation: none !important; } .canvas-edge-enter { animation: none !important; opacity: 1; } .canvas-edge-exit { animation: none !important; opacity: 0; } } `; function CanvasAnimations() { const $ = (0, import_compiler_runtime74.c)(1); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("style", { dangerouslySetInnerHTML: { __html: CANVAS_ANIMATION_STYLES } }); $[0] = t0; } else { t0 = $[0]; } return t0; } // src/components/TouchActionButton/index.tsx var import_compiler_runtime77 = require("react/compiler-runtime"); var import_react70 = __toESM(require("react")); var import_jotai89 = require("jotai"); init_input_store(); init_external_keyboard_store(); init_selection_store(); init_graph_derived(); init_graph_mutations(); init_clipboard_store(); init_history_store(); // src/components/TouchActionButton/icons.tsx var import_compiler_runtime75 = require("react/compiler-runtime"); var import_react68 = __toESM(require("react")); var import_jsx_runtime46 = require("react/jsx-runtime"); function PlusIcon2() { const $ = (0, import_compiler_runtime75.c)(1); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", children: [/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("line", { x1: "12", y1: "6", x2: "12", y2: "18" }), /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("line", { x1: "6", y1: "12", x2: "18", y2: "12" })] }); $[0] = t0; } else { t0 = $[0]; } return t0; } function MergeIcon() { const $ = (0, import_compiler_runtime75.c)(1); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("path", { d: "M6 4 L10 10 L6 16" }), /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("path", { d: "M14 4 L10 10 L14 16" })] }); $[0] = t0; } else { t0 = $[0]; } return t0; } function DuplicateIcon() { const $ = (0, import_compiler_runtime75.c)(1); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("rect", { x: "3", y: "5", width: "10", height: "10", rx: "2" }), /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("rect", { x: "7", y: "5", width: "10", height: "10", rx: "2" })] }); $[0] = t0; } else { t0 = $[0]; } return t0; } function SelectAllIcon() { const $ = (0, import_compiler_runtime75.c)(1); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("rect", { x: "3", y: "3", width: "14", height: "14", rx: "2", strokeDasharray: "3 2" }), /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("polyline", { points: "7 10 9 12 13 8" })] }); $[0] = t0; } else { t0 = $[0]; } return t0; } function SearchIcon() { const $ = (0, import_compiler_runtime75.c)(1); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("circle", { cx: "9", cy: "9", r: "5" }), /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("line", { x1: "13", y1: "13", x2: "17", y2: "17" })] }); $[0] = t0; } else { t0 = $[0]; } return t0; } function UndoIcon() { const $ = (0, import_compiler_runtime75.c)(1); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("polyline", { points: "5 9 2 6 5 3" }), /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("path", { d: "M2 6h10a5 5 0 0 1 0 10H8" })] }); $[0] = t0; } else { t0 = $[0]; } return t0; } function RedoIcon() { const $ = (0, import_compiler_runtime75.c)(1); let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("polyline", { points: "15 9 18 6 15 3" }), /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("path", { d: "M18 6H8a5 5 0 0 0 0 10h4" })] }); $[0] = t0; } else { t0 = $[0]; } return t0; } // src/components/TouchActionButton/RadialMenu.tsx var import_compiler_runtime76 = require("react/compiler-runtime"); var import_react69 = __toESM(require("react")); var import_jsx_runtime47 = require("react/jsx-runtime"); var FAB_SIZE = 56; var ITEM_SIZE = 48; var STAGGER_MS = 35; function getRadialPositions(count, radius, position) { let startAngle; let endAngle; switch (position) { case "bottom-left": startAngle = -20; endAngle = -160; break; case "bottom-right": startAngle = -160; endAngle = -20; break; case "top-left": startAngle = 20; endAngle = 160; break; case "top-right": startAngle = 160; endAngle = 20; break; default: startAngle = -20; endAngle = -160; } const positions = []; for (let i = 0; i < count; i++) { const t = count === 1 ? 0.5 : i / (count - 1); const angleDeg = startAngle + (endAngle - startAngle) * t; const angleRad = angleDeg * Math.PI / 180; positions.push({ dx: Math.cos(angleRad) * radius, dy: Math.sin(angleRad) * radius, angle: angleDeg }); } return positions; } function getPositionStyles2(position) { switch (position) { case "bottom-left": return { bottom: "max(16px, env(safe-area-inset-bottom, 16px))", left: "max(16px, env(safe-area-inset-left, 16px))" }; case "bottom-right": return { bottom: "max(16px, env(safe-area-inset-bottom, 16px))", right: "max(16px, env(safe-area-inset-right, 16px))" }; case "top-left": return { top: "max(16px, env(safe-area-inset-top, 16px))", left: "max(16px, env(safe-area-inset-left, 16px))" }; case "top-right": return { top: "max(16px, env(safe-area-inset-top, 16px))", right: "max(16px, env(safe-area-inset-right, 16px))" }; default: return { bottom: "max(16px, env(safe-area-inset-bottom, 16px))", left: "max(16px, env(safe-area-inset-left, 16px))" }; } } function RadialMenuItem(t0) { const $ = (0, import_compiler_runtime76.c)(31); const { item, position: pos, index: i, totalItems, isOpen, isHighlighted, onTap } = t0; const deployed = isOpen; const t1 = deployed ? `translate(${pos.dx}px, ${pos.dy}px) scale(1)` : "translate(0, 0) scale(0)"; const t2 = deployed ? item.disabled ? 0.35 : 1 : 0; const t3 = deployed ? `transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1) ${i * STAGGER_MS}ms, opacity 200ms ease ${i * STAGGER_MS}ms` : `transform 200ms cubic-bezier(0.55, 0, 1, 0.45) ${(totalItems - 1 - i) * 20}ms, opacity 150ms ease ${(totalItems - 1 - i) * 20}ms`; const t4 = deployed ? "auto" : "none"; let t5; if ($[0] !== t1 || $[1] !== t2 || $[2] !== t3 || $[3] !== t4) { t5 = { position: "absolute", left: FAB_SIZE / 2 - ITEM_SIZE / 2, top: FAB_SIZE / 2 - ITEM_SIZE / 2, transform: t1, opacity: t2, transition: t3, pointerEvents: t4, willChange: "transform, opacity" }; $[0] = t1; $[1] = t2; $[2] = t3; $[3] = t4; $[4] = t5; } else { t5 = $[4]; } let t6; if ($[5] !== item || $[6] !== onTap) { t6 = () => onTap(item); $[5] = item; $[6] = onTap; $[7] = t6; } else { t6 = $[7]; } const t7 = `1.5px solid ${isHighlighted ? "rgba(99,160,255,0.5)" : "var(--canvas-control-border, rgba(255,255,255,0.12))"}`; const t8 = isHighlighted ? "var(--canvas-control-active-bg, rgba(59,130,246,0.9))" : "var(--canvas-control-bg, rgba(30,30,30,0.88))"; const t9 = item.disabled ? "default" : "pointer"; const t10 = isHighlighted ? "scale(1.18)" : "scale(1)"; const t11 = isHighlighted ? "0 0 0 3px rgba(59,130,246,0.25), 0 4px 16px rgba(59,130,246,0.35)" : "0 2px 8px rgba(0,0,0,0.35)"; let t12; if ($[8] !== t10 || $[9] !== t11 || $[10] !== t7 || $[11] !== t8 || $[12] !== t9) { t12 = { width: ITEM_SIZE, height: ITEM_SIZE, borderRadius: "50%", border: t7, backgroundColor: t8, color: "var(--canvas-control-fg, rgba(255,255,255,0.9))", display: "flex", alignItems: "center", justifyContent: "center", cursor: t9, touchAction: "manipulation", WebkitTapHighlightColor: "transparent", userSelect: "none", backdropFilter: "blur(16px) saturate(1.5)", WebkitBackdropFilter: "blur(16px) saturate(1.5)", padding: 0, transition: "background-color 180ms ease, border-color 180ms ease, transform 180ms cubic-bezier(0.34, 1.2, 0.64, 1), box-shadow 250ms ease", transform: t10, boxShadow: t11 }; $[8] = t10; $[9] = t11; $[10] = t7; $[11] = t8; $[12] = t9; $[13] = t12; } else { t12 = $[13]; } let t13; if ($[14] !== item.disabled || $[15] !== item.icon || $[16] !== item.label || $[17] !== t12 || $[18] !== t6) { t13 = /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("button", { onPointerUp: t6, disabled: item.disabled, "aria-label": item.label, "data-no-drag": "true", style: t12, children: item.icon }); $[14] = item.disabled; $[15] = item.icon; $[16] = item.label; $[17] = t12; $[18] = t6; $[19] = t13; } else { t13 = $[19]; } const t14 = isHighlighted ? 1 : 0; const t15 = isHighlighted ? "translateX(-50%) translateY(0)" : "translateX(-50%) translateY(4px)"; let t16; if ($[20] !== t14 || $[21] !== t15) { t16 = { position: "absolute", top: -24, left: "50%", fontSize: "11px", fontWeight: 600, letterSpacing: "0.02em", color: "var(--canvas-control-fg, rgba(255,255,255,0.95))", backgroundColor: "var(--canvas-control-bg, rgba(20,20,20,0.92))", backdropFilter: "blur(12px)", WebkitBackdropFilter: "blur(12px)", padding: "3px 9px", borderRadius: "7px", whiteSpace: "nowrap", pointerEvents: "none", opacity: t14, transform: t15, transition: "opacity 150ms ease, transform 150ms ease" }; $[20] = t14; $[21] = t15; $[22] = t16; } else { t16 = $[22]; } let t17; if ($[23] !== item.label || $[24] !== t16) { t17 = /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { style: t16, children: item.label }); $[23] = item.label; $[24] = t16; $[25] = t17; } else { t17 = $[25]; } let t18; if ($[26] !== item.id || $[27] !== t13 || $[28] !== t17 || $[29] !== t5) { t18 = /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { style: t5, children: [t13, t17] }, item.id); $[26] = item.id; $[27] = t13; $[28] = t17; $[29] = t5; $[30] = t18; } else { t18 = $[30]; } return t18; } // src/components/TouchActionButton/index.tsx var import_jsx_runtime48 = require("react/jsx-runtime"); function TouchActionButton(t0) { const $ = (0, import_compiler_runtime77.c)(95); const { position: t1, alwaysVisible: t2, className, style, holdDelay: t3, radius: t4 } = t0; const position = t1 === void 0 ? "bottom-left" : t1; const alwaysVisible = t2 === void 0 ? false : t2; const holdDelay = t3 === void 0 ? 250 : t3; const radius = t4 === void 0 ? 80 : t4; const isTouchDevice = (0, import_jotai89.useAtomValue)(isTouchDeviceAtom); const hasExternalKeyboard = (0, import_jotai89.useAtomValue)(hasExternalKeyboardAtom); const [isOpen, setIsOpen] = (0, import_react70.useState)(false); const [highlightedId, setHighlightedId] = (0, import_react70.useState)(null); const selectedNodeIds = (0, import_jotai89.useAtomValue)(selectedNodeIdsAtom); const nodeKeys = (0, import_jotai89.useAtomValue)(nodeKeysAtom); const mergeNodes = (0, import_jotai89.useSetAtom)(mergeNodesAtom); const duplicateSelection = (0, import_jotai89.useSetAtom)(duplicateSelectionAtom); const clearSelection = (0, import_jotai89.useSetAtom)(clearSelectionAtom); const addNodesToSelection = (0, import_jotai89.useSetAtom)(addNodesToSelectionAtom); const openCommandLine = (0, import_jotai89.useSetAtom)(openCommandLineAtom); const undo = (0, import_jotai89.useSetAtom)(undoAtom); const redo = (0, import_jotai89.useSetAtom)(redoAtom); const canUndo = (0, import_jotai89.useAtomValue)(canUndoAtom); const canRedo = (0, import_jotai89.useAtomValue)(canRedoAtom); const holdTimerRef = (0, import_react70.useRef)(null); const containerRef = (0, import_react70.useRef)(null); const fabCenterRef = (0, import_react70.useRef)(null); const didOpenRef = (0, import_react70.useRef)(false); if (!alwaysVisible && (!isTouchDevice || hasExternalKeyboard)) { return null; } const hasSelection = selectedNodeIds.size > 0; const canMerge = selectedNodeIds.size >= 2; let t5; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t5 = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(UndoIcon, {}); $[0] = t5; } else { t5 = $[0]; } const t6 = !canUndo; let t7; if ($[1] !== undo) { t7 = () => undo(); $[1] = undo; $[2] = t7; } else { t7 = $[2]; } let t8; if ($[3] !== t6 || $[4] !== t7) { t8 = { id: "undo", label: "Undo", icon: t5, disabled: t6, action: t7 }; $[3] = t6; $[4] = t7; $[5] = t8; } else { t8 = $[5]; } let t9; if ($[6] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t9 = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(RedoIcon, {}); $[6] = t9; } else { t9 = $[6]; } const t10 = !canRedo; let t11; if ($[7] !== redo) { t11 = () => redo(); $[7] = redo; $[8] = t11; } else { t11 = $[8]; } let t12; if ($[9] !== t10 || $[10] !== t11) { t12 = { id: "redo", label: "Redo", icon: t9, disabled: t10, action: t11 }; $[9] = t10; $[10] = t11; $[11] = t12; } else { t12 = $[11]; } let t13; if ($[12] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t13 = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(MergeIcon, {}); $[12] = t13; } else { t13 = $[12]; } const t14 = !canMerge; let t15; if ($[13] !== canMerge || $[14] !== mergeNodes || $[15] !== selectedNodeIds) { t15 = () => { if (canMerge) { mergeNodes({ nodeIds: Array.from(selectedNodeIds) }); } }; $[13] = canMerge; $[14] = mergeNodes; $[15] = selectedNodeIds; $[16] = t15; } else { t15 = $[16]; } let t16; if ($[17] !== t14 || $[18] !== t15) { t16 = { id: "merge", label: "Merge", icon: t13, disabled: t14, action: t15 }; $[17] = t14; $[18] = t15; $[19] = t16; } else { t16 = $[19]; } let t17; if ($[20] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t17 = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(DuplicateIcon, {}); $[20] = t17; } else { t17 = $[20]; } const t18 = !hasSelection; let t19; if ($[21] !== duplicateSelection || $[22] !== hasSelection) { t19 = () => { if (hasSelection) { duplicateSelection(); } }; $[21] = duplicateSelection; $[22] = hasSelection; $[23] = t19; } else { t19 = $[23]; } let t20; if ($[24] !== t18 || $[25] !== t19) { t20 = { id: "duplicate", label: "Duplicate", icon: t17, disabled: t18, action: t19 }; $[24] = t18; $[25] = t19; $[26] = t20; } else { t20 = $[26]; } let t21; if ($[27] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t21 = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(SelectAllIcon, {}); $[27] = t21; } else { t21 = $[27]; } let t22; if ($[28] !== addNodesToSelection || $[29] !== clearSelection || $[30] !== nodeKeys) { t22 = { id: "select-all", label: "Select All", icon: t21, action: () => { clearSelection(); addNodesToSelection(nodeKeys); } }; $[28] = addNodesToSelection; $[29] = clearSelection; $[30] = nodeKeys; $[31] = t22; } else { t22 = $[31]; } let t23; if ($[32] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t23 = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(SearchIcon, {}); $[32] = t23; } else { t23 = $[32]; } let t24; if ($[33] !== openCommandLine) { t24 = { id: "search", label: "Search", icon: t23, action: () => openCommandLine() }; $[33] = openCommandLine; $[34] = t24; } else { t24 = $[34]; } let t25; if ($[35] !== t12 || $[36] !== t16 || $[37] !== t20 || $[38] !== t22 || $[39] !== t24 || $[40] !== t8) { t25 = [t8, t12, t16, t20, t22, t24]; $[35] = t12; $[36] = t16; $[37] = t20; $[38] = t22; $[39] = t24; $[40] = t8; $[41] = t25; } else { t25 = $[41]; } const actions = t25; let t26; if ($[42] !== actions.length || $[43] !== position || $[44] !== radius) { t26 = getRadialPositions(actions.length, radius, position); $[42] = actions.length; $[43] = position; $[44] = radius; $[45] = t26; } else { t26 = $[45]; } const itemPositions = t26; let t27; if ($[46] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t27 = () => { if (holdTimerRef.current) { clearTimeout(holdTimerRef.current); holdTimerRef.current = null; } }; $[46] = t27; } else { t27 = $[46]; } const clearHoldTimer = t27; let t28; if ($[47] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t28 = () => { const container = containerRef.current; if (!container) { return null; } const fab = container.querySelector("[data-fab-button]"); if (!fab) { return null; } const rect = fab.getBoundingClientRect(); return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }; }; $[47] = t28; } else { t28 = $[47]; } const getFabCenter = t28; let t29; if ($[48] !== actions || $[49] !== itemPositions) { t29 = (clientX, clientY) => { const center = fabCenterRef.current; if (!center) { return null; } let closest = null; let closestDist = Infinity; const hitRadius = ITEM_SIZE * 0.75; for (let i = 0; i < actions.length; i++) { const pos = itemPositions[i]; const itemX = center.x + pos.dx; const itemY = center.y + pos.dy; const dist = Math.hypot(clientX - itemX, clientY - itemY); if (dist < hitRadius && dist < closestDist && !actions[i].disabled) { closest = actions[i].id; closestDist = dist; } } return closest; }; $[48] = actions; $[49] = itemPositions; $[50] = t29; } else { t29 = $[50]; } const findClosestItem = t29; let t30; if ($[51] !== holdDelay) { t30 = (e) => { e.target.setPointerCapture(e.pointerId); fabCenterRef.current = getFabCenter(); didOpenRef.current = false; holdTimerRef.current = setTimeout(() => { setIsOpen(true); didOpenRef.current = true; holdTimerRef.current = null; }, holdDelay); }; $[51] = holdDelay; $[52] = t30; } else { t30 = $[52]; } const handlePointerDown = t30; let t31; if ($[53] !== findClosestItem) { t31 = (e_0) => { if (!didOpenRef.current) { return; } setHighlightedId(findClosestItem(e_0.clientX, e_0.clientY)); }; $[53] = findClosestItem; $[54] = t31; } else { t31 = $[54]; } const handlePointerMove = t31; let t32; if ($[55] !== actions || $[56] !== findClosestItem) { t32 = (e_1) => { clearHoldTimer(); if (didOpenRef.current) { const id = findClosestItem(e_1.clientX, e_1.clientY); if (id) { const item = actions.find((a) => a.id === id); if (item && !item.disabled) { item.action(); } } setIsOpen(false); setHighlightedId(null); didOpenRef.current = false; } else { clearHoldTimer(); setIsOpen((o) => { if (o) { setHighlightedId(null); return false; } didOpenRef.current = true; fabCenterRef.current = getFabCenter(); return true; }); } }; $[55] = actions; $[56] = findClosestItem; $[57] = t32; } else { t32 = $[57]; } const handlePointerUp = t32; let t33; if ($[58] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t33 = () => { clearHoldTimer(); setIsOpen(false); setHighlightedId(null); didOpenRef.current = false; }; $[58] = t33; } else { t33 = $[58]; } const handlePointerCancel = t33; let t34; if ($[59] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t34 = (item_0) => { if (item_0.disabled) { return; } item_0.action(); setIsOpen(false); setHighlightedId(null); didOpenRef.current = false; }; $[59] = t34; } else { t34 = $[59]; } const handleItemTap = t34; let t35; if ($[60] !== position) { t35 = getPositionStyles2(position); $[60] = position; $[61] = t35; } else { t35 = $[61]; } const positionStyles = t35; let t36; if ($[62] !== positionStyles || $[63] !== style) { t36 = { position: "absolute", ...positionStyles, zIndex: 100, pointerEvents: "auto", ...style }; $[62] = positionStyles; $[63] = style; $[64] = t36; } else { t36 = $[64]; } let t37; if ($[65] !== actions || $[66] !== highlightedId || $[67] !== isOpen || $[68] !== itemPositions) { t37 = actions.map((item_1, i_0) => /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(RadialMenuItem, { item: item_1, position: itemPositions[i_0], index: i_0, totalItems: actions.length, isOpen, isHighlighted: highlightedId === item_1.id, onTap: handleItemTap }, item_1.id)); $[65] = actions; $[66] = highlightedId; $[67] = isOpen; $[68] = itemPositions; $[69] = t37; } else { t37 = $[69]; } const t38 = radius * 2 + ITEM_SIZE; const t39 = radius * 2 + ITEM_SIZE; const t40 = isOpen ? "translate(-50%, -50%) scale(1)" : "translate(-50%, -50%) scale(0)"; const t41 = isOpen ? "transform 400ms cubic-bezier(0.34, 1.3, 0.64, 1), opacity 300ms ease" : "transform 250ms ease, opacity 150ms ease"; const t42 = isOpen ? 1 : 0; let t43; if ($[70] !== t38 || $[71] !== t39 || $[72] !== t40 || $[73] !== t41 || $[74] !== t42) { t43 = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { style: { position: "absolute", left: FAB_SIZE / 2, top: FAB_SIZE / 2, width: t38, height: t39, borderRadius: "50%", transform: t40, background: "radial-gradient(circle, rgba(255,255,255,0.03) 0%, transparent 70%)", transition: t41, opacity: t42, pointerEvents: "none" } }); $[70] = t38; $[71] = t39; $[72] = t40; $[73] = t41; $[74] = t42; $[75] = t43; } else { t43 = $[75]; } const t44 = isOpen ? "Close actions" : "Hold for actions"; const t45 = `1.5px solid ${isOpen ? "rgba(99,160,255,0.4)" : "var(--canvas-control-border, rgba(255,255,255,0.15))"}`; const t46 = isOpen ? "var(--canvas-control-active-bg, rgba(59,130,246,0.9))" : "var(--canvas-control-bg, rgba(30,30,30,0.88))"; const t47 = isOpen ? "rotate(45deg) scale(1.05)" : "rotate(0deg) scale(1)"; const t48 = isOpen ? "0 0 0 4px rgba(59,130,246,0.15), 0 6px 20px rgba(0,0,0,0.35)" : "0 4px 12px rgba(0,0,0,0.3)"; let t49; if ($[76] !== t45 || $[77] !== t46 || $[78] !== t47 || $[79] !== t48) { t49 = { position: "relative", width: FAB_SIZE, height: FAB_SIZE, borderRadius: "50%", border: t45, backgroundColor: t46, color: "var(--canvas-control-fg, rgba(255,255,255,0.95))", display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer", touchAction: "none", WebkitTapHighlightColor: "transparent", userSelect: "none", backdropFilter: "blur(16px) saturate(1.5)", WebkitBackdropFilter: "blur(16px) saturate(1.5)", padding: 0, transition: "background-color 250ms ease, border-color 250ms ease, transform 350ms cubic-bezier(0.34, 1.3, 0.64, 1), box-shadow 300ms ease", transform: t47, boxShadow: t48 }; $[76] = t45; $[77] = t46; $[78] = t47; $[79] = t48; $[80] = t49; } else { t49 = $[80]; } let t50; if ($[81] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t50 = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(PlusIcon2, {}); $[81] = t50; } else { t50 = $[81]; } let t51; if ($[82] !== handlePointerDown || $[83] !== handlePointerMove || $[84] !== handlePointerUp || $[85] !== isOpen || $[86] !== t44 || $[87] !== t49) { t51 = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { "data-fab-button": "true", onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onPointerCancel: handlePointerCancel, "aria-label": t44, "aria-expanded": isOpen, "data-no-drag": "true", style: t49, children: t50 }); $[82] = handlePointerDown; $[83] = handlePointerMove; $[84] = handlePointerUp; $[85] = isOpen; $[86] = t44; $[87] = t49; $[88] = t51; } else { t51 = $[88]; } let t52; if ($[89] !== className || $[90] !== t36 || $[91] !== t37 || $[92] !== t43 || $[93] !== t51) { t52 = /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { ref: containerRef, className, "data-no-drag": "true", style: t36, children: [t37, t43, t51] }); $[89] = className; $[90] = t36; $[91] = t37; $[92] = t43; $[93] = t51; $[94] = t52; } else { t52 = $[94]; } return t52; } // src/components/CanvasToast.tsx var import_compiler_runtime78 = require("react/compiler-runtime"); var import_react71 = __toESM(require("react")); var import_jotai90 = require("jotai"); init_toast_store(); var import_jsx_runtime49 = require("react/jsx-runtime"); function CanvasToast(t0) { const $ = (0, import_compiler_runtime78.c)(12); const { className, style } = t0; const toast = (0, import_jotai90.useAtomValue)(canvasToastAtom); let t1; if ($[0] !== style) { t1 = { position: "absolute", bottom: "24px", left: "50%", transform: "translateX(-50%)", zIndex: 200, pointerEvents: "none", ...style }; $[0] = style; $[1] = t1; } else { t1 = $[1]; } const t2 = toast ? 1 : 0; const t3 = toast ? "translateY(0)" : "translateY(8px)"; let t4; if ($[2] !== t2 || $[3] !== t3) { t4 = { fontSize: "13px", fontWeight: 500, color: "var(--canvas-control-fg, rgba(255,255,255,0.95))", backgroundColor: "var(--canvas-control-bg, rgba(30,30,30,0.92))", backdropFilter: "blur(12px)", WebkitBackdropFilter: "blur(12px)", border: "1px solid var(--canvas-control-border, rgba(255,255,255,0.1))", padding: "6px 16px", borderRadius: "8px", whiteSpace: "nowrap", opacity: t2, transform: t3, transition: "opacity 200ms ease, transform 200ms ease" }; $[2] = t2; $[3] = t3; $[4] = t4; } else { t4 = $[4]; } const t5 = toast?.message ?? ""; let t6; if ($[5] !== t4 || $[6] !== t5) { t6 = /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { style: t4, children: t5 }); $[5] = t4; $[6] = t5; $[7] = t6; } else { t6 = $[7]; } let t7; if ($[8] !== className || $[9] !== t1 || $[10] !== t6) { t7 = /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className, style: t1, children: t6 }); $[8] = className; $[9] = t1; $[10] = t6; $[11] = t7; } else { t7 = $[11]; } return t7; } // src/components/AlignmentGuides.tsx var import_compiler_runtime79 = require("react/compiler-runtime"); var import_react72 = __toESM(require("react")); var import_jotai91 = require("jotai"); init_snap_store(); init_viewport_store(); var import_jsx_runtime50 = require("react/jsx-runtime"); var GUIDE_COLOR = "rgba(99, 160, 255, 0.6)"; function AlignmentGuides() { const $ = (0, import_compiler_runtime79.c)(22); const guides = (0, import_jotai91.useAtomValue)(alignmentGuidesAtom); const pan = (0, import_jotai91.useAtomValue)(panAtom); const zoom = (0, import_jotai91.useAtomValue)(zoomAtom); const viewportRect = (0, import_jotai91.useAtomValue)(viewportRectAtom); const hasGuides = guides.verticalGuides.length > 0 || guides.horizontalGuides.length > 0; if (!hasGuides || !viewportRect) { return null; } const worldMinX = -pan.x / zoom; const worldMinY = -pan.y / zoom; const worldMaxX = worldMinX + viewportRect.width / zoom; const worldMaxY = worldMinY + viewportRect.height / zoom; let t0; if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) { t0 = { position: "absolute", left: 0, top: 0, width: "100%", height: "100%", pointerEvents: "none", overflow: "visible", zIndex: 999 }; $[0] = t0; } else { t0 = $[0]; } let t1; if ($[1] !== guides.verticalGuides || $[2] !== worldMaxY || $[3] !== worldMinY || $[4] !== zoom) { let t22; if ($[6] !== worldMaxY || $[7] !== worldMinY || $[8] !== zoom) { t22 = (x, i) => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("line", { x1: x, y1: worldMinY, x2: x, y2: worldMaxY, stroke: GUIDE_COLOR, strokeWidth: 1 / zoom, strokeDasharray: `${4 / zoom} ${4 / zoom}` }, `v-${i}`); $[6] = worldMaxY; $[7] = worldMinY; $[8] = zoom; $[9] = t22; } else { t22 = $[9]; } t1 = guides.verticalGuides.map(t22); $[1] = guides.verticalGuides; $[2] = worldMaxY; $[3] = worldMinY; $[4] = zoom; $[5] = t1; } else { t1 = $[5]; } let t2; if ($[10] !== guides.horizontalGuides || $[11] !== worldMaxX || $[12] !== worldMinX || $[13] !== zoom) { let t32; if ($[15] !== worldMaxX || $[16] !== worldMinX || $[17] !== zoom) { t32 = (y, i_0) => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("line", { x1: worldMinX, y1: y, x2: worldMaxX, y2: y, stroke: GUIDE_COLOR, strokeWidth: 1 / zoom, strokeDasharray: `${4 / zoom} ${4 / zoom}` }, `h-${i_0}`); $[15] = worldMaxX; $[16] = worldMinX; $[17] = zoom; $[18] = t32; } else { t32 = $[18]; } t2 = guides.horizontalGuides.map(t32); $[10] = guides.horizontalGuides; $[11] = worldMaxX; $[12] = worldMinX; $[13] = zoom; $[14] = t2; } else { t2 = $[14]; } let t3; if ($[19] !== t1 || $[20] !== t2) { t3 = /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("svg", { style: t0, children: [t1, t2] }); $[19] = t1; $[20] = t2; $[21] = t3; } else { t3 = $[21]; } return t3; } // src/nodes/NoteNode/NoteNode.tsx var import_react73 = __toESM(require("react")); var import_react74 = require("@blocknote/react"); var import_shadcn = require("@blocknote/shadcn"); var import_locales = require("@blocknote/core/locales"); init_debug(); var import_inter = require("@blocknote/core/fonts/inter.css"); var import_style = require("@blocknote/shadcn/style.css"); var import_jsx_runtime51 = require("react/jsx-runtime"); var debug22 = createDebug("note-node"); function NoteNode({ nodeData, storage, theme = "light", placeholder = "Start typing...", isResizing }) { const isSyncingFromStorage = (0, import_react73.useRef)(false); const isSyncingToStorage = (0, import_react73.useRef)(false); const lastStorageContent = (0, import_react73.useRef)(""); const editor = (0, import_react74.useCreateBlockNote)({ initialContent: [{ id: crypto.randomUUID(), type: "paragraph", content: "" }], dictionary: { ...import_locales.en, placeholders: { ...import_locales.en.placeholders, emptyDocument: placeholder, default: placeholder, heading: "Enter a heading" } } }, []); (0, import_react74.useEditorChange)(() => { if (isSyncingFromStorage.current) { return; } try { isSyncingToStorage.current = true; const html = editor.blocksToFullHTML(editor.document); if (html !== lastStorageContent.current) { lastStorageContent.current = html; storage.onChange(html); } } catch (err) { debug22.error("Failed to sync to storage: %O", err); } finally { isSyncingToStorage.current = false; } }, editor); (0, import_react73.useEffect)(() => { if (!editor) return; const handleStorageChange = (newContent) => { if (isSyncingToStorage.current) { return; } queueMicrotask(() => { if (newContent === lastStorageContent.current) return; lastStorageContent.current = newContent; if (newContent && newContent.trim()) { try { isSyncingFromStorage.current = true; const blocks = editor.tryParseHTMLToBlocks(newContent); editor.replaceBlocks(editor.document, blocks); } catch (err_0) { debug22.error("Failed to parse HTML from storage: %O", err_0); } finally { isSyncingFromStorage.current = false; } } }); }; const unsubscribe = storage.subscribe?.(handleStorageChange); if (storage.content && storage.content !== lastStorageContent.current) { handleStorageChange(storage.content); } return () => { unsubscribe?.(); }; }, [editor, storage]); if (storage.isLoading) { return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "note-node note-node--loading", style: styles3.loading, children: "Loading..." }); } return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "note-node", style: styles3.container, "data-no-drag": "true", onClick: (e) => { e.stopPropagation(); }, children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_shadcn.BlockNoteView, { editor, theme }) }); } var styles3 = { container: { height: "100%", width: "100%", overflow: "auto", touchAction: "auto" }, loading: { display: "flex", alignItems: "center", justifyContent: "center", height: "100%", color: "#666" } }; // src/commands/index.ts init_registry(); // 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(provideInputAtom2, point); } function handlePickedNode(set, node) { set(provideInputAtom2, node); } function cancelCommand(set) { set(closeCommandLineAtom); } // src/commands/CommandProvider.tsx var import_compiler_runtime80 = require("react/compiler-runtime"); var import_react75 = __toESM(require("react")); var import_jotai92 = require("jotai"); var import_jotai93 = require("jotai"); init_graph_store(); init_selection_store(); init_viewport_store(); init_history_store(); init_registry(); var import_jsx_runtime52 = require("react/jsx-runtime"); var CommandContextContext = /* @__PURE__ */ (0, import_react75.createContext)(null); function CommandProvider(t0) { const $ = (0, import_compiler_runtime80.c)(52); const { children, onCreateNode, onUpdateNode, onDeleteNode, onCreateEdge, onDeleteEdge, onForceLayoutPersist } = t0; const store = (0, import_jotai93.useStore)(); const currentGraphId = (0, import_jotai92.useAtomValue)(currentGraphIdAtom); const selectedNodeIds = (0, import_jotai92.useAtomValue)(selectedNodeIdsAtom); const zoom = (0, import_jotai92.useAtomValue)(zoomAtom); const pan = (0, import_jotai92.useAtomValue)(panAtom); const undo = (0, import_jotai92.useSetAtom)(undoAtom); const redo = (0, import_jotai92.useSetAtom)(redoAtom); const { fitToBounds } = useFitToBounds(); let t1; if ($[0] !== onForceLayoutPersist) { t1 = onForceLayoutPersist ? async (updates) => { await onForceLayoutPersist(updates.map(_temp20)); } : 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 = (0, import_jotai92.useSetAtom)(closeCommandLineAtom); const setCommandError = (0, import_jotai92.useSetAtom)(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 = _temp210; const [commandState, setCommandState] = (0, import_jotai92.useAtom)(commandLineStateAtom); const isExecutingRef = (0, import_react75.useRef)(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(inputModeAtom2, { 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]; } (0, import_react75.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__ */ (0, import_jsx_runtime52.jsx)(CommandContextContext, { value, children }); $[49] = children; $[50] = value; $[51] = t11; } else { t11 = $[51]; } return t11; } function _temp210(name) { return commandRegistry.has(name); } function _temp20(u) { return { nodeId: u.nodeId, x: u.position.x, y: u.position.y }; } function useCommandContext() { const context = (0, import_react75.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(); } // src/gestures/index.ts var gestures_exports = {}; __export(gestures_exports, { ACTIVE_INTERACTION_CONTEXT: () => ACTIVE_INTERACTION_CONTEXT, DEFAULT_CONTEXT: () => DEFAULT_CONTEXT, GestureProvider: () => GestureProvider, IDLE: () => IDLE, INPUT_MODE_CONTEXTS: () => INPUT_MODE_CONTEXTS, InputProvider: () => InputProvider, KEYBOARD_MANIPULATE_CONTEXT: () => KEYBOARD_MANIPULATE_CONTEXT, KEYBOARD_NAVIGATE_CONTEXT: () => KEYBOARD_NAVIGATE_CONTEXT, LONG_PRESS_TIMER: () => LONG_PRESS_TIMER, NO_HELD_KEYS: () => NO_HELD_KEYS, NO_MODIFIERS: () => NO_MODIFIERS, PALM_REJECTION_CONTEXT: () => PALM_REJECTION_CONTEXT, PICK_NODES_CONTEXT: () => PICK_NODES_CONTEXT, PICK_NODE_CONTEXT: () => PICK_NODE_CONTEXT, PICK_POINT_CONTEXT: () => PICK_POINT_CONTEXT, PanInertia: () => PanInertia, SEARCH_CONTEXT: () => SEARCH_CONTEXT, SETTLE_TIMER: () => SETTLE_TIMER, TimedStateRunner: () => TimedStateRunner, VelocitySampler: () => VelocitySampler, ZoomInertia: () => ZoomInertia, activateFocusedNode: () => activateFocusedNode, buildMappingIndex: () => buildMappingIndex, cancelActiveInteraction: () => cancelActiveInteraction, clearHandlers: () => clearHandlers, createPinchHandlers: () => createPinchHandlers, createWheelHandler: () => createWheelHandler, cutSelection: () => cutSelection, cycleFocus: () => cycleFocus, deleteSelection: () => deleteSelection, dispatch: () => dispatch, escapeInput: () => escapeInput, extractModifiers: () => extractModifiers, findNearestNode: () => findNearestNode, getCurrentSubject: () => getCurrentSubject, getHandler: () => getHandler, indexContext: () => indexContext, isKeyInputEvent: () => isKeyInputEvent, isPointerGestureEvent: () => isPointerGestureEvent, navigateFocus: () => navigateFocus, normalizePointer: () => normalizePointer, nudgeSelection: () => nudgeSelection, registerAction: () => registerAction2, resolve: () => resolve, snapZoom: () => snapZoom, specificity: () => specificity, transition: () => transition, unregisterAction: () => unregisterAction2, useCanvasGestures: () => useCanvasGestures, useGestureContext: () => useGestureContext, useGestureSystem: () => useGestureSystem, useGuardContext: () => useGuardContext, useInertia: () => useInertia, useInputContext: () => useInputContext, useInputModeGestureContext: () => useInputModeGestureContext, useInputSystem: () => useInputSystem, useNodeGestures: () => useNodeGestures, useRegisterInputActions: () => useRegisterInputActions }); init_types2(); init_dispatcher(); // src/index.ts var CANVAS_VERSION = "2.0.0"; var canvasVersion = { version: CANVAS_VERSION, major: 2, minor: 0, patch: 0, /** Check if current version is at least the specified version */ isAtLeast: (major, minor = 0, patch = 0) => { if (canvasVersion.major > major) return true; if (canvasVersion.major < major) return false; if (canvasVersion.minor > minor) return true; if (canvasVersion.minor < minor) return false; return canvasVersion.patch >= patch; } }; //# sourceMappingURL=index.js.map