UNPKG

tanuki-ui

Version:

HTML-first React component library with semantic elements, multi-OS themes, and production-ready visual node editor

1,698 lines 216 kB
var jt = Object.defineProperty; var Kt = (e, t, n) => t in e ? jt(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n; var ae = (e, t, n) => Kt(e, typeof t != "symbol" ? t + "" : t, n); import { jsx as c, jsxs as I, Fragment as re } from "react/jsx-runtime"; import * as l from "react"; import { c as j } from "../vendor/classNames-DxK1wTlm.js"; const $e = (e) => { const t = []; return e.ctrl && t.push("ctrl"), e.shift && t.push("shift"), e.alt && t.push("alt"), e.meta && t.push("meta"), t.push(e.key.toLowerCase()), t.join("+"); }, Wt = (e, t) => e.key.toLowerCase() === t.key.toLowerCase() && !!e.ctrlKey == !!t.ctrl && !!e.shiftKey == !!t.shift && !!e.altKey == !!t.alt && !!e.metaKey == !!t.meta, Zt = (e, t) => { switch (t.type) { case "REGISTER_SHORTCUT": { const n = $e(t.payload.shortcut), o = new Map(e.shortcuts); return o.set(n, t.payload.handler), { ...e, shortcuts: o }; } case "UNREGISTER_SHORTCUT": { const n = $e(t.payload.shortcut), o = new Map(e.shortcuts); return o.delete(n), { ...e, shortcuts: o }; } case "ENABLE_SHORTCUTS": return { ...e, isEnabled: !0 }; case "DISABLE_SHORTCUTS": return { ...e, isEnabled: !1 }; default: return e; } }, qt = { shortcuts: /* @__PURE__ */ new Map(), isEnabled: !0 }, Ae = { registerShortcut: (e, t) => ({ type: "REGISTER_SHORTCUT", payload: { shortcut: e, handler: t } }), unregisterShortcut: (e) => ({ type: "UNREGISTER_SHORTCUT", payload: { shortcut: e } }), enableShortcuts: () => ({ type: "ENABLE_SHORTCUTS" }), disableShortcuts: () => ({ type: "DISABLE_SHORTCUTS" }) }, qe = l.createContext(null), Jt = ({ children: e, initialState: t }) => { const [n, o] = l.useReducer( Zt, { ...qt, ...t } ); l.useEffect(() => { const r = (d) => { if (!n.isEnabled) return; const h = d.target; if (!(h.tagName === "INPUT" || h.tagName === "TEXTAREA" || h.isContentEditable)) for (const [u, p] of n.shortcuts) { const f = u.split("+"), y = f[f.length - 1], C = f.slice(0, -1), m = { key: y, ctrl: C.includes("ctrl"), shift: C.includes("shift"), alt: C.includes("alt"), meta: C.includes("meta") }; if (Wt(d, m)) { d.preventDefault(), d.stopPropagation(), p(d); break; } } }; return document.addEventListener("keydown", r), () => document.removeEventListener("keydown", r); }, [n.shortcuts, n.isEnabled]); const i = l.useCallback( (r, d) => { o(Ae.registerShortcut(r, d)); }, [o] ), s = l.useCallback( (r) => { o(Ae.unregisterShortcut(r)); }, [o] ), a = { state: n, dispatch: o, actions: Ae, registerShortcut: i, unregisterShortcut: s }; return /* @__PURE__ */ c(qe.Provider, { value: a, children: e }); }, Qt = () => { const e = l.useContext(qe); if (!e) throw new Error("useKeyboardShortcut must be used within a KeyboardShortcutProvider"); return e; }, ie = (e, t, n = []) => { const { registerShortcut: o, unregisterShortcut: i } = Qt(); l.useEffect(() => (o(e, t), () => i(e)), [o, i, ...n]); }, en = (e, t) => { switch (t.type) { case "ADD_NODE": { const n = we(), o = { ...t.payload.node, id: n }; return o.ports && (o.ports = o.ports.map((i) => ({ ...i, nodeId: n }))), { ...e, nodes: { ...e.nodes, [n]: o } }; } case "UPDATE_NODE": { const { nodeId: n, updates: o } = t.payload, i = e.nodes[n]; return i ? { ...e, nodes: { ...e.nodes, [n]: { ...i, ...o } } } : e; } case "DELETE_NODE": { const { nodeId: n } = t.payload, { [n]: o, ...i } = e.nodes, s = Object.entries(e.connections).reduce( (a, [r, d]) => (d.fromNodeId !== n && d.toNodeId !== n && (a[r] = d), a), {} ); return { ...e, nodes: i, connections: s }; } case "MOVE_NODE": { const { nodeId: n, position: o } = t.payload, i = e.nodes[n]; return i ? { ...e, nodes: { ...e.nodes, [n]: { ...i, position: o } } } : e; } case "MOVE_NODES": { const { updates: n } = t.payload, o = { ...e.nodes }; return Object.entries(n).forEach(([i, s]) => { const a = o[i]; a && (o[i] = { ...a, position: s }); }), { ...e, nodes: o }; } case "ADD_CONNECTION": { const { connection: n } = t.payload, o = we(), i = Object.entries(e.connections).filter( ([a, r]) => r.toNodeId === n.toNodeId && r.toPortId === n.toPortId ), s = { ...e.connections }; return i.forEach(([a]) => { delete s[a]; }), { ...e, connections: { ...s, [o]: { ...n, id: o } } }; } case "DELETE_CONNECTION": { const { connectionId: n } = t.payload, { [n]: o, ...i } = e.connections; return { ...e, connections: i }; } case "SET_NODE_DATA": return t.payload.data; case "RESTORE_STATE": return t.payload.data; case "DUPLICATE_NODES": { const { nodeIds: n } = t.payload; if (n.length === 0) return e; const o = { ...e.nodes }, i = []; return n.forEach((s) => { var h; const a = e.nodes[s]; if (!a) return; const r = we(); i.push(r); const d = { ...a, id: r, position: { x: a.position.x + 50, y: a.position.y + 50 }, data: { ...a.data, title: a.data.title ? `${a.data.title} Copy` : "Node Copy", createdAt: Date.now() // Track creation time for selection }, // Duplicate ports with new IDs ports: ((h = a.ports) == null ? void 0 : h.map((u) => ({ ...u, id: `${u.id}-copy-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, nodeId: r }))) || [] }; d.type === "group" && (d.children = []), o[r] = d; }), { ...e, nodes: o, // Store duplicated node IDs for selection lastDuplicatedNodeIds: i }; } case "GROUP_NODES": { const { nodeIds: n, groupId: o = we() } = t.payload; if (n.length === 0) return e; let i = 1 / 0, s = 1 / 0, a = -1 / 0, r = -1 / 0; n.forEach((h) => { var p, f; const u = e.nodes[h]; u && (i = Math.min(i, u.position.x), s = Math.min(s, u.position.y), a = Math.max(a, u.position.x + (((p = u.size) == null ? void 0 : p.width) || 100)), r = Math.max(r, u.position.y + (((f = u.size) == null ? void 0 : f.height) || 50))); }); const d = { id: o, type: "group", position: { x: i - 20, y: s - 40 }, size: { width: a - i + 40, height: r - s + 60 }, data: { title: "Group" }, children: n, expanded: !0 }; return { ...e, nodes: { ...e.nodes, [o]: d } }; } case "UNGROUP_NODE": { const { groupId: n } = t.payload, o = e.nodes[n]; if (!o || o.type !== "group") return e; const { [n]: i, ...s } = e.nodes; return { ...e, nodes: s }; } case "UPDATE_GROUP_MEMBERSHIP": { const { updates: n } = t.payload, o = { ...e.nodes }; return Object.entries(n).forEach(([i, s]) => { const a = o[i]; a && (o[i] = { ...a, ...s }); }), { ...e, nodes: o }; } case "MOVE_GROUP_WITH_CHILDREN": { const { groupId: n, delta: o } = t.payload, i = { ...e.nodes }, s = i[n]; return s && (i[n] = { ...s, position: { x: s.position.x + o.x, y: s.position.y + o.y } }, Object.values(i).forEach((a) => { a.parentId === n && (i[a.id] = { ...a, position: { x: a.position.x + o.x, y: a.position.y + o.y } }); })), { ...e, nodes: i }; } case "AUTO_LAYOUT": { const { layoutType: n, selectedOnly: o = !1 } = t.payload; return o && (Object.fromEntries( Object.entries(e.nodes).filter( ([i, s]) => ( // This will be filtered by selected nodes in the hook !0 ) ) ), e.connections), e; } default: return e; } }; function we() { return Math.random().toString(36).slice(2, 10); } const Xe = { nodes: {}, connections: {} }, tn = { addNode: (e) => ({ type: "ADD_NODE", payload: { node: e } }), updateNode: (e, t) => ({ type: "UPDATE_NODE", payload: { nodeId: e, updates: t } }), deleteNode: (e) => ({ type: "DELETE_NODE", payload: { nodeId: e } }), moveNode: (e, t) => ({ type: "MOVE_NODE", payload: { nodeId: e, position: t } }), moveNodes: (e) => ({ type: "MOVE_NODES", payload: { updates: e } }), addConnection: (e) => ({ type: "ADD_CONNECTION", payload: { connection: e } }), deleteConnection: (e) => ({ type: "DELETE_CONNECTION", payload: { connectionId: e } }), setNodeData: (e) => ({ type: "SET_NODE_DATA", payload: { data: e } }), restoreState: (e) => ({ type: "RESTORE_STATE", payload: { data: e } }), duplicateNodes: (e) => ({ type: "DUPLICATE_NODES", payload: { nodeIds: e } }), groupNodes: (e, t) => ({ type: "GROUP_NODES", payload: { nodeIds: e, groupId: t } }), ungroupNode: (e) => ({ type: "UNGROUP_NODE", payload: { groupId: e } }), updateGroupMembership: (e) => ({ type: "UPDATE_GROUP_MEMBERSHIP", payload: { updates: e } }), moveGroupWithChildren: (e, t) => ({ type: "MOVE_GROUP_WITH_CHILDREN", payload: { groupId: e, delta: t } }), autoLayout: (e, t) => ({ type: "AUTO_LAYOUT", payload: { layoutType: e, selectedOnly: t } }) }, Je = l.createContext(null), nn = ({ children: e, initialState: t }) => { const n = l.useMemo(() => ({ nodes: (t == null ? void 0 : t.nodes) || Xe.nodes, connections: (t == null ? void 0 : t.connections) || Xe.connections }), [t]), [o, i] = l.useReducer( en, n ), s = { state: o, dispatch: i, actions: tn }; return /* @__PURE__ */ c(Je.Provider, { value: s, children: e }); }, W = () => { const e = l.useContext(Je); if (!e) throw new Error("useNodeEditor must be used within a NodeEditorProvider"); return e; }, on = (e, t) => { switch (t.type) { case "SELECT_NODE": { const { nodeId: n, multiple: o } = t.payload; if (o) { const i = e.selectedNodeIds.includes(n); return { ...e, selectedNodeIds: i ? e.selectedNodeIds.filter((s) => s !== n) : [...e.selectedNodeIds, n] }; } return { ...e, selectedNodeIds: [n], selectedConnectionIds: [] // Clear connection selection }; } case "SELECT_CONNECTION": { const { connectionId: n, multiple: o } = t.payload; if (o) { const i = e.selectedConnectionIds.includes(n); return { ...e, selectedConnectionIds: i ? e.selectedConnectionIds.filter((s) => s !== n) : [...e.selectedConnectionIds, n] }; } return { ...e, selectedConnectionIds: [n], selectedNodeIds: [] // Clear node selection }; } case "CLEAR_SELECTION": return { ...e, selectedNodeIds: [], selectedConnectionIds: [], selectionBox: null }; case "SELECT_ALL_NODES": return { ...e, selectedNodeIds: t.payload.nodeIds, selectedConnectionIds: [] }; case "SET_SELECTION_BOX": return { ...e, selectionBox: t.payload.box }; case "START_NODE_DRAG": { const { nodeIds: n, startPosition: o, initialPositions: i, affectedChildNodes: s } = t.payload; return { ...e, dragState: { nodeIds: n, startPosition: o, offset: { x: 0, y: 0 }, initialPositions: i, affectedChildNodes: s } }; } case "UPDATE_NODE_DRAG": return e.dragState ? { ...e, dragState: { ...e.dragState, offset: t.payload.offset } } : e; case "END_NODE_DRAG": return { ...e, dragState: null }; case "SET_HOVERED_NODE": return { ...e, hoveredNodeId: t.payload.nodeId }; case "SET_HOVERED_CONNECTION": return { ...e, hoveredConnectionId: t.payload.connectionId }; case "START_CONNECTION_DRAG": return { ...e, connectionDragState: { fromPort: t.payload.fromPort, toPosition: { x: 0, y: 0 }, validTarget: null, candidatePort: null } }; case "UPDATE_CONNECTION_DRAG": return e.connectionDragState ? { ...e, connectionDragState: { ...e.connectionDragState, toPosition: t.payload.toPosition, candidatePort: t.payload.candidatePort } } : e; case "END_CONNECTION_DRAG": return { ...e, connectionDragState: null }; case "START_CONNECTION_DISCONNECT": return { ...e, connectionDisconnectState: { connectionId: t.payload.originalConnection.id, fixedPort: t.payload.fixedPort, draggingEnd: t.payload.disconnectedEnd, draggingPosition: t.payload.draggingPosition, originalConnection: t.payload.originalConnection, disconnectedEnd: t.payload.disconnectedEnd, candidatePort: null } }; case "UPDATE_CONNECTION_DISCONNECT": return e.connectionDisconnectState ? { ...e, connectionDisconnectState: { ...e.connectionDisconnectState, draggingPosition: t.payload.draggingPosition, candidatePort: t.payload.candidatePort } } : e; case "END_CONNECTION_DISCONNECT": return { ...e, connectionDisconnectState: null }; case "SET_HOVERED_PORT": return { ...e, hoveredPort: t.payload.port }; case "UPDATE_CONNECTED_PORTS": return { ...e, connectedPorts: t.payload.connectedPorts }; case "START_NODE_RESIZE": { const { nodeId: n, startPosition: o, startSize: i, handle: s } = t.payload; return { ...e, resizeState: { nodeId: n, startPosition: o, startSize: i, currentSize: i, currentPosition: o, handle: s } }; } case "UPDATE_NODE_RESIZE": return e.resizeState ? { ...e, resizeState: { ...e.resizeState, currentSize: t.payload.currentSize } } : e; case "END_NODE_RESIZE": return { ...e, resizeState: null }; case "SHOW_CONTEXT_MENU": return { ...e, contextMenu: { visible: !0, position: t.payload.position, canvasPosition: t.payload.canvasPosition, nodeId: t.payload.nodeId } }; case "HIDE_CONTEXT_MENU": return { ...e, contextMenu: { visible: !1, position: { x: 0, y: 0 }, canvasPosition: void 0, nodeId: void 0 } }; default: return e; } }, sn = { selectedNodeIds: [], selectedConnectionIds: [], selectionBox: null, dragState: null, resizeState: null, hoveredNodeId: null, hoveredConnectionId: null, connectionDragState: null, connectionDisconnectState: null, hoveredPort: null, connectedPorts: /* @__PURE__ */ new Set(), contextMenu: { visible: !1, position: { x: 0, y: 0 }, canvasPosition: void 0, nodeId: void 0 } }, an = { selectNode: (e, t = !1) => ({ type: "SELECT_NODE", payload: { nodeId: e, multiple: t } }), selectConnection: (e, t = !1) => ({ type: "SELECT_CONNECTION", payload: { connectionId: e, multiple: t } }), clearSelection: () => ({ type: "CLEAR_SELECTION" }), selectAllNodes: (e) => ({ type: "SELECT_ALL_NODES", payload: { nodeIds: e } }), setSelectionBox: (e) => ({ type: "SET_SELECTION_BOX", payload: { box: e } }), startNodeDrag: (e, t, n, o) => ({ type: "START_NODE_DRAG", payload: { nodeIds: e, startPosition: t, initialPositions: n, affectedChildNodes: o } }), updateNodeDrag: (e) => ({ type: "UPDATE_NODE_DRAG", payload: { offset: e } }), endNodeDrag: () => ({ type: "END_NODE_DRAG" }), setHoveredNode: (e) => ({ type: "SET_HOVERED_NODE", payload: { nodeId: e } }), setHoveredConnection: (e) => ({ type: "SET_HOVERED_CONNECTION", payload: { connectionId: e } }), startConnectionDrag: (e) => ({ type: "START_CONNECTION_DRAG", payload: { fromPort: e } }), updateConnectionDrag: (e, t) => ({ type: "UPDATE_CONNECTION_DRAG", payload: { toPosition: e, candidatePort: t } }), endConnectionDrag: () => ({ type: "END_CONNECTION_DRAG" }), setHoveredPort: (e) => ({ type: "SET_HOVERED_PORT", payload: { port: e } }), updateConnectedPorts: (e) => ({ type: "UPDATE_CONNECTED_PORTS", payload: { connectedPorts: e } }), startConnectionDisconnect: (e, t, n, o) => ({ type: "START_CONNECTION_DISCONNECT", payload: { originalConnection: e, disconnectedEnd: t, fixedPort: n, draggingPosition: o } }), updateConnectionDisconnect: (e, t) => ({ type: "UPDATE_CONNECTION_DISCONNECT", payload: { draggingPosition: e, candidatePort: t } }), endConnectionDisconnect: () => ({ type: "END_CONNECTION_DISCONNECT" }), startNodeResize: (e, t, n, o) => ({ type: "START_NODE_RESIZE", payload: { nodeId: e, startPosition: t, startSize: n, handle: o } }), updateNodeResize: (e) => ({ type: "UPDATE_NODE_RESIZE", payload: { currentSize: e } }), endNodeResize: () => ({ type: "END_NODE_RESIZE" }), showContextMenu: (e, t, n) => ({ type: "SHOW_CONTEXT_MENU", payload: { position: e, nodeId: t, canvasPosition: n } }), hideContextMenu: () => ({ type: "HIDE_CONTEXT_MENU" }) }, Qe = l.createContext(null), rn = ({ children: e, initialState: t }) => { const [n, o] = l.useReducer( on, { ...sn, ...t } ), i = { state: n, dispatch: o, actions: an }; return /* @__PURE__ */ c(Qe.Provider, { value: i, children: e }); }, Z = () => { const e = l.useContext(Qe); if (!e) throw new Error("useEditorActionState must be used within an EditorActionStateProvider"); return e; }, cn = (e, t) => { switch (t.type) { case "PUSH_ENTRY": { if (!e.isRecording) return e; const { action: n, data: o } = t.payload, i = { id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, timestamp: Date.now(), action: n, data: JSON.parse(JSON.stringify(o)) // Deep clone }, r = [...e.entries.slice(0, e.currentIndex + 1), i].slice(-e.maxEntries); return { ...e, entries: r, currentIndex: r.length - 1 }; } case "UNDO": return e.currentIndex <= 0 ? e : { ...e, currentIndex: e.currentIndex - 1 }; case "REDO": return e.currentIndex >= e.entries.length - 1 ? e : { ...e, currentIndex: e.currentIndex + 1 }; case "CLEAR_HISTORY": return { ...e, entries: [], currentIndex: -1 }; case "SET_RECORDING": return { ...e, isRecording: t.payload.isRecording }; case "SET_MAX_ENTRIES": { const { maxEntries: n } = t.payload, o = e.entries.slice(-n); return { ...e, maxEntries: n, entries: o, currentIndex: Math.min(e.currentIndex, o.length - 1) }; } default: return e; } }, ln = { entries: [], currentIndex: -1, maxEntries: 50, isRecording: !0 }, Ee = { pushEntry: (e, t) => ({ type: "PUSH_ENTRY", payload: { action: e, data: t } }), undo: () => ({ type: "UNDO" }), redo: () => ({ type: "REDO" }), clearHistory: () => ({ type: "CLEAR_HISTORY" }), setRecording: (e) => ({ type: "SET_RECORDING", payload: { isRecording: e } }), setMaxEntries: (e) => ({ type: "SET_MAX_ENTRIES", payload: { maxEntries: e } }) }, et = l.createContext(null), dn = ({ children: e, initialState: t }) => { const [n, o] = l.useReducer( cn, { ...ln, ...t } ), i = n.currentIndex > 0, s = n.currentIndex < n.entries.length - 1, a = n.currentIndex >= 0 ? n.entries[n.currentIndex] : null, r = l.useCallback( (p, f) => { o(Ee.pushEntry(p, f)); }, [o] ), d = l.useCallback(() => i ? (o(Ee.undo()), n.entries[n.currentIndex - 1] || null) : null, [i, n.entries, n.currentIndex, o]), h = l.useCallback(() => s ? (o(Ee.redo()), n.entries[n.currentIndex + 1] || null) : null, [s, n.entries, n.currentIndex, o]), u = { state: n, dispatch: o, actions: Ee, canUndo: i, canRedo: s, currentEntry: a, pushEntry: r, undo: d, redo: h }; return /* @__PURE__ */ c(et.Provider, { value: u, children: e }); }, un = () => { const e = l.useContext(et); if (!e) throw new Error("useHistory must be used within a HistoryProvider"); return e; }, pn = () => { const { state: e, dispatch: t, actions: n } = W(), { pushEntry: o, undo: i, redo: s, canUndo: a, canRedo: r } = un(), d = l.useRef(e), h = l.useRef(null); l.useEffect(() => { const y = e, C = d.current; if (C === y || JSON.stringify(C) === JSON.stringify(y)) return; const m = h.current || "Unknown Action"; o(m, C), d.current = y, h.current = null; }, [e, o]); const u = l.useCallback((y) => { y && typeof y == "object" && y.type && (h.current = y.type), t(y); }, [t]), p = l.useCallback(() => { if (!a) return !1; const y = i(); return y ? (h.current = null, d.current = y.data, t(n.restoreState(y.data)), !0) : !1; }, [a, i, t, n]), f = l.useCallback(() => { if (!r) return !1; const y = s(); return y ? (h.current = null, d.current = y.data, t(n.restoreState(y.data)), !0) : !1; }, [r, s, t, n]); return { dispatchWithHistory: u, performUndo: p, performRedo: f, canUndo: a, canRedo: r }; }, hn = { iterations: 100, springLength: 150, springStrength: 0.3, repulsionStrength: 1e3, dampening: 0.9, maxForce: 50, padding: 100 }; function gn(e, t = {}) { const n = { ...hn, ...t }, o = Object.values(e.nodes), i = Object.values(e.connections); if (o.length === 0) return { nodePositions: {}, iterations: 0 }; if (o.length === 1) return { nodePositions: { [o[0].id]: { x: 0, y: 0 } }, iterations: 0 }; const s = {}, a = {}; o.forEach((u, p) => { if (u.position) s[u.id] = { ...u.position }; else { const f = p / o.length * 2 * Math.PI, y = Math.sqrt(o.length) * 50; s[u.id] = { x: Math.cos(f) * y, y: Math.sin(f) * y }; } a[u.id] = { x: 0, y: 0 }; }); const r = {}; o.forEach((u) => { r[u.id] = /* @__PURE__ */ new Set(); }), i.forEach((u) => { r[u.fromNodeId] && r[u.toNodeId] && (r[u.fromNodeId].add(u.toNodeId), r[u.toNodeId].add(u.fromNodeId)); }); let d = 0; for (let u = 0; u < n.iterations; u++) { d++; const p = {}; o.forEach((C) => { p[C.id] = { x: 0, y: 0 }; }); for (let C = 0; C < o.length; C++) for (let m = C + 1; m < o.length; m++) { const b = o[C], v = o[m], D = s[b.id], P = s[v.id], N = P.x - D.x, S = P.y - D.y, g = Math.sqrt(N * N + S * S); if (g < 0.01) continue; const w = n.repulsionStrength / (g * g), E = N / g * w, T = S / g * w; p[b.id].x -= E, p[b.id].y -= T, p[v.id].x += E, p[v.id].y += T; } i.forEach((C) => { const m = s[C.fromNodeId], b = s[C.toNodeId]; if (!m || !b) return; const v = b.x - m.x, D = b.y - m.y, P = Math.sqrt(v * v + D * D); if (P < 0.01) return; const N = n.springStrength * (P - n.springLength), S = v / P * N, g = D / P * N; p[C.fromNodeId].x += S, p[C.fromNodeId].y += g, p[C.toNodeId].x -= S, p[C.toNodeId].y -= g; }); let f = 0; if (o.forEach((C) => { const m = p[C.id], b = Math.sqrt(m.x * m.x + m.y * m.y); b > n.maxForce && (m.x = m.x / b * n.maxForce, m.y = m.y / b * n.maxForce), a[C.id].x = a[C.id].x * n.dampening + m.x, a[C.id].y = a[C.id].y * n.dampening + m.y; const v = { ...s[C.id] }; s[C.id].x += a[C.id].x, s[C.id].y += a[C.id].y; const D = Math.sqrt( Math.pow(s[C.id].x - v.x, 2) + Math.pow(s[C.id].y - v.y, 2) ); f += D; }), f / o.length < 0.1) break; } const h = Object.keys(s); if (h.length > 0) { let u = 1 / 0, p = -1 / 0, f = 1 / 0, y = -1 / 0; h.forEach((b) => { const v = s[b]; u = Math.min(u, v.x), p = Math.max(p, v.x), f = Math.min(f, v.y), y = Math.max(y, v.y); }); const C = (u + p) / 2, m = (f + y) / 2; h.forEach((b) => { s[b].x = s[b].x - C + n.padding, s[b].y = s[b].y - m + n.padding; }); } return { nodePositions: s, iterations: d }; } function fn(e, t = {}) { const { spacing: n = 200, layerHeight: o = 150 } = t, i = Object.values(e.nodes), s = Object.values(e.connections); if (i.length === 0) return { nodePositions: {}, iterations: 0 }; const a = {}, r = {}; i.forEach((m) => { a[m.id] = /* @__PURE__ */ new Set(), r[m.id] = /* @__PURE__ */ new Set(); }), s.forEach((m) => { a[m.fromNodeId] && r[m.toNodeId] && (a[m.fromNodeId].add(m.toNodeId), r[m.toNodeId].add(m.fromNodeId)); }); const d = i.filter((m) => r[m.id].size === 0); if (d.length === 0) { const m = Math.min(...i.map((b) => r[b.id].size)); d.push(...i.filter((b) => r[b.id].size === m)); } const h = [], u = {}, p = /* @__PURE__ */ new Set(); h[0] = d.map((m) => m.id), d.forEach((m) => { u[m.id] = 0, p.add(m.id); }); let f = 0; for (; h[f] && h[f].length > 0; ) { const m = []; if (h[f].forEach((b) => { a[b].forEach((v) => { p.has(v) || (m.push(v), u[v] = f + 1, p.add(v)); }); }), m.length > 0) h[f + 1] = m, f++; else break; } const y = i.filter((m) => !p.has(m.id)); y.length > 0 && (h[f + 1] || (h[f + 1] = []), h[f + 1].push(...y.map((m) => m.id)), y.forEach((m) => { u[m.id] = f + 1; })); const C = {}; return h.forEach((m, b) => { const v = b * o, P = -((m.length - 1) * n) / 2; m.forEach((N, S) => { C[N] = { x: P + S * n, y: v }; }); }), { nodePositions: C, iterations: h.length }; } function mn(e, t = {}) { const { spacing: n = 200, columns: o } = t, i = Object.values(e.nodes); if (i.length === 0) return { nodePositions: {}, iterations: 0 }; const s = o || Math.ceil(Math.sqrt(i.length)), a = Math.ceil(i.length / s), r = {}; return i.forEach((d, h) => { const u = Math.floor(h / s), p = h % s, f = (s - 1) * n, y = (a - 1) * n; r[d.id] = { x: p * n - f / 2, y: u * n - y / 2 }; }), { nodePositions: r, iterations: 0 }; } const yn = () => { const { state: e, dispatch: t, actions: n } = W(), { state: o } = Z(), i = l.useCallback((d = !1) => { console.log("Applying force-directed layout"); const u = { nodes: d ? Object.fromEntries( Object.entries(e.nodes).filter( ([f]) => o.selectedNodeIds.includes(f) ) ) : e.nodes, connections: e.connections }, p = gn(u, { iterations: 150, springLength: 200, springStrength: 0.4, repulsionStrength: 2e3, dampening: 0.85 }); Object.keys(p.nodePositions).length > 0 && t(n.moveNodes(p.nodePositions)); }, [e, o.selectedNodeIds, t, n]), s = l.useCallback((d = !1) => { console.log("Applying hierarchical layout"); const u = { nodes: d ? Object.fromEntries( Object.entries(e.nodes).filter( ([f]) => o.selectedNodeIds.includes(f) ) ) : e.nodes, connections: e.connections }, p = fn(u, { spacing: 250, layerHeight: 200 }); Object.keys(p.nodePositions).length > 0 && t(n.moveNodes(p.nodePositions)); }, [e, o.selectedNodeIds, t, n]), a = l.useCallback((d = !1) => { console.log("Applying grid layout"); const h = d ? Object.fromEntries( Object.entries(e.nodes).filter( ([f]) => o.selectedNodeIds.includes(f) ) ) : e.nodes, u = { nodes: h, connections: e.connections }, p = mn(u, { spacing: 250, columns: Math.ceil(Math.sqrt(Object.keys(h).length)) }); Object.keys(p.nodePositions).length > 0 && t(n.moveNodes(p.nodePositions)); }, [e, o.selectedNodeIds, t, n]); return { applyLayout: l.useCallback((d, h = !1) => { switch (d) { case "force": i(h); break; case "hierarchical": s(h); break; case "grid": a(h); break; default: console.warn(`Unknown layout type: ${d}`); } }, [i, s, a]), applyForceLayout: i, applyHierarchicalLayout: s, applyGridLayout: a }; }, vn = () => { const { state: e, dispatch: t, actions: n } = W(), { state: o, dispatch: i, actions: s } = Z(), { performUndo: a, performRedo: r, canUndo: d, canRedo: h } = pn(), { applyLayout: u } = yn(); ie( { key: "Delete" }, l.useCallback(() => { console.log("Delete shortcut triggered"), o.selectedNodeIds.length > 0 ? (o.selectedNodeIds.forEach((p) => { t(n.deleteNode(p)); }), i(s.clearSelection())) : o.selectedConnectionIds.length > 0 && (o.selectedConnectionIds.forEach((p) => { t(n.deleteConnection(p)); }), i(s.clearSelection())); }, [o.selectedNodeIds, o.selectedConnectionIds, t, n, i, s]) ), ie( { key: "Backspace" }, l.useCallback(() => { console.log("Backspace shortcut triggered"), o.selectedNodeIds.length > 0 ? (o.selectedNodeIds.forEach((p) => { t(n.deleteNode(p)); }), i(s.clearSelection())) : o.selectedConnectionIds.length > 0 && (o.selectedConnectionIds.forEach((p) => { t(n.deleteConnection(p)); }), i(s.clearSelection())); }, [o.selectedNodeIds, o.selectedConnectionIds, t, n, i, s]) ), ie( { key: "a", ctrl: !0 }, l.useCallback(() => { console.log("Select All shortcut triggered"); const p = Object.keys(e.nodes); i(s.selectAllNodes(p)); }, [e.nodes, i, s]) ), ie( { key: "Escape" }, l.useCallback(() => { console.log("Escape shortcut triggered"), i(s.clearSelection()); }, [i, s]) ), ie( { key: "n", ctrl: !0 }, l.useCallback(() => { console.log("Add Node shortcut triggered"); const p = `node-${Date.now()}`, f = { title: "New Node", type: "default", position: { x: 100, y: 100 }, data: { title: "New Node" }, ports: [ { id: `port-input-${Date.now()}`, type: "input", label: "Input", position: "left", nodeId: p }, { id: `port-output-${Date.now()}`, type: "output", label: "Output", position: "right", nodeId: p } ] }; t(n.addNode(f)); }, [t, n]) ), ie( { key: "d", ctrl: !0 }, l.useCallback(() => { console.log("Duplicate shortcut triggered"), o.selectedNodeIds.length > 0 && t(n.duplicateNodes(o.selectedNodeIds)); }, [o.selectedNodeIds, t, n]) ), l.useEffect(() => { e.lastDuplicatedNodeIds && e.lastDuplicatedNodeIds.length > 0 && (i(s.selectAllNodes(e.lastDuplicatedNodeIds)), t(n.setNodeData({ ...e, lastDuplicatedNodeIds: void 0 }))); }, [e.lastDuplicatedNodeIds, i, s, t, n, e]), ie( { key: "s", ctrl: !0 }, l.useCallback((p) => { console.log("Save shortcut triggered"), console.log("Save not yet implemented"); }, []) ), ie( { key: "l", ctrl: !0 }, l.useCallback(() => { console.log("Auto Layout shortcut triggered"); const p = o.selectedNodeIds.length > 0; u("force", p); }, [o.selectedNodeIds, u]) ), ie( { key: "z", ctrl: !0 }, l.useCallback(() => { console.log("Undo shortcut triggered"), d && a(); }, [d, a]) ), ie( { key: "z", ctrl: !0, shift: !0 }, l.useCallback(() => { console.log("Redo shortcut triggered"), h && r(); }, [h, r]) ), ie( { key: "y", ctrl: !0 }, l.useCallback(() => { console.log("Redo (Ctrl+Y) shortcut triggered"), h && r(); }, [h, r]) ); }, bn = "enenodedibas", Nn = "enecnt", Cn = "enecanvas", Sn = "enepanning", xn = "enespapan", wn = "enenodes", En = "enenode", In = "enelocked", Dn = "enesel", kn = "enedrg", Pn = "enenodehdr", Tn = "eneintdra", Mn = "enenod", On = "enelockico", zn = "enenodectn", An = "enegrpNode", Rn = "enecold", Vn = "enegrpcold", Ln = "enegrpexped", Hn = "enenodeinf", _n = "enenodesuc", Bn = "enenodewrn", Gn = "enenodeerr", $n = "enenodedis", Xn = "enetbr", Un = "enetoolbtn", Yn = "eneconmen", Fn = "eneconmenrax", jn = "enegrid", Kn = "eneins", Wn = "eneinscIz", Zn = "eneinszAn", qn = "eneinssec", Jn = "eneinsrow", Qn = "eneediwitins", eo = "eneedimai", to = "eneextdat", no = "enedragui", oo = "enedraguiVp7", so = "enedraguiPHl", io = "enedragui-eW", ao = "enesnatar", ro = "enedis", co = "enedislin", lo = "enestabar", uo = "enestasec", po = "enesta", ho = "enestaval", go = "enestamod", fo = "enenodePort", mo = "eneportinp", yo = "eneporout", vo = "eneporinn", bo = "eneportlbl", No = "eneportLeft", Co = "eneporrig", So = "eneportTop", xo = "eneporbot", wo = "enecon", Eo = "eneinstab", Io = "eneinstab5EW", Do = "eneact", ko = "eneinsfie", Po = "eneinsj42", To = "eneinskYN", Mo = "eneinspos", Oo = "eneinsnum", zo = "eneinsreaonlfie", Ao = "eneinssta", Ro = "enecan", Vo = "eneboxsel", Lo = "enegridSvg", Ho = "eneedi", _o = "eneediA9y", Bo = "eneediPTx", Go = "eneedi-tX", $o = "enetoptbr", Xo = "enenodlay", Uo = "enenodeView", Yo = "enenodtit", Fo = "enenodtitpsF", jo = "enegrpaccdro", Ko = "enegrpPulse", Wo = "enegrphaschi", Zo = "enenodpor", qo = "eneport", Jo = "eneporhov", Qo = "eneporcon", es = "enepulse", ts = "eneporcon7Fy", ns = "eneporlef", os = "eneporrigtpL", ss = "eneportop", is = "eneporbotsMk", as = "eneconlay", rs = "eneconhUO", cs = "enecongay", ls = "eneconhov", ds = "eneconDHx", us = "enedracon", ps = "enedasani", hs = "eneselbox", gs = "eneseldzq", fs = "eneldgovr", ms = "eneldgind", ys = "enecusnod", vs = "eneres", bs = "eneresVnb", Ns = "enenodseamen", Cs = "enemenfadin", Ss = "enesea", xs = "eneseaEM8", ws = "eneseahin", Es = "eneseares", Is = "enecatlis", Ds = "enecat", ks = "enecatoHZ", Ps = "eneselcat", Ts = "enecatnam", Ms = "enenodcou", Os = "enenodeList", zs = "enenodeitm", As = "eneselNode", Rs = "enenodeico", Vs = "enenodeName", Ls = "enenoddes", Hs = "enenodeType", _s = "enenores", Bs = "enenoresgSZ", Gs = "eneseaqFY", $s = "eneselWi_", Xs = "enemin", Us = "eneminimap", Ys = "enemintit", Fs = "eneminont", js = "eneminvie", Ks = "eneminnod", Ws = "enedarthe", Zs = "enesmoani", qs = "enestasav", Js = "eneuiovrcnt", x = { nodeEditorBase: bn, container: Nn, canvas: Cn, panning: Sn, spacePanning: xn, nodes: wn, node: En, locked: In, selected: Dn, dragging: kn, nodeHeader: Pn, interactiveDragHandle: Tn, nodeHeaderInput: Mn, lockIcon: On, nodeContent: zn, groupNode: An, collapsed: Rn, groupCollapsed: Vn, groupExpanded: Ln, nodeInfo: Hn, nodeSuccess: _n, nodeWarning: Bn, nodeError: Gn, nodeDisabled: $n, toolbar: Xn, toolButton: Un, contextMenu: Yn, contextMenuItem: Fn, grid: jn, inspectorPanel: Kn, inspectorHeader: Wn, inspectorContent: Zn, inspectorSection: qn, inspectorRow: Jn, editorWithInspector: Qn, editorMain: eo, externalDataInfo: to, dragGuides: no, dragGuide: oo, dragGuideVertical: so, dragGuideHorizontal: io, snapTarget: ao, distanceIndicator: ro, distanceLine: co, statusBar: lo, statusSection: uo, statusLabel: po, statusValue: ho, statusMode: go, nodePort: fo, portInput: mo, portOutput: yo, portInner: vo, portLabel: bo, portLeft: No, portRight: Co, portTop: So, portBottom: xo, connections: wo, inspectorTabs: Eo, inspectorTab: Io, active: Do, inspectorField: ko, inspectorInput: Po, inspectorTextarea: To, inspectorPositionInputs: Mo, inspectorNumberInput: Oo, inspectorReadOnlyField: zo, inspectorEmptyState: Ao, canvasContainer: Ro, boxSelecting: Vo, gridSvg: Lo, editorLayout: Ho, editorToolbar: _o, editorContent: Bo, editorSidebar: Go, topToolbar: $o, nodeLayer: Xo, nodeView: Uo, nodeTitle: Yo, nodeTitleInput: Fo, groupAcceptingDrop: jo, groupPulse: Ko, groupHasChildren: Wo, nodePorts: Zo, port: qo, portHovered: Jo, portConnecting: Qo, pulse: es, portConnected: ts, portLabelLeft: ns, portLabelRight: os, portLabelTop: ss, portLabelBottom: is, connectionLayer: as, connectionGroup: rs, connectionSelected: cs, connectionHovered: ls, connectionDragging: ds, dragConnection: us, dashAnimation: ps, selectionBoxOverlay: hs, selectionOverlay: gs, loadingOverlay: fs, loadingIndicator: ms, customNodeContent: ys, resizeHandle: vs, resizeHandleActive: bs, nodeSearchMenu: Ns, menuFadeIn: Cs, searchHeader: Ss, searchInput: xs, searchHint: ws, searchResults: Es, categoryList: Is, categoryGroup: Ds, categoryHeader: ks, selectedCategory: Ps, categoryName: Ts, nodeCount: Ms, nodeList: Os, nodeItem: zs, selectedNode: As, nodeIcon: Rs, nodeName: Vs, nodeDescription: Ls, nodeType: Hs, noResults: _s, noResultsIcon: Bs, searchFooter: Gs, selectionInfo: $s, minimapContainer: Xs, minimap: Us, minimapTitle: Ys, minimapContent: Fs, minimapViewport: js, minimapNodes: Ks, darkTheme: Ws, smoothAnimations: Zs, statusSaving: qs, uiOverlayContainer: Js }, tt = ({ children: e, className: t, "data-testid": n = "node-editor-base" }) => (vn(), /* @__PURE__ */ c( "div", { className: j(x.nodeEditorBase, t), "data-testid": n, tabIndex: 0, children: e } )); tt.displayName = "NodeEditorBase"; const Qs = (e, t) => { switch (t.type) { case "SET_VIEWPORT": return { ...e, viewport: t.payload.viewport }; case "PAN_VIEWPORT": { const { delta: n } = t.payload; return { ...e, viewport: { ...e.viewport, offset: { x: e.viewport.offset.x + n.x, y: e.viewport.offset.y + n.y } } }; } case "ZOOM_VIEWPORT": { const { scale: n, center: o } = t.payload, i = Math.max(0.01, Math.min(64, n)); if (o) { const s = i / e.viewport.scale, a = { x: o.x - (o.x - e.viewport.offset.x) * s, y: o.y - (o.y - e.viewport.offset.y) * s }; return { ...e, viewport: { offset: a, scale: i } }; } return { ...e, viewport: { ...e.viewport, scale: i } }; } case "RESET_VIEWPORT": return { ...e, viewport: { offset: { x: 0, y: 0 }, scale: 1 } }; case "UPDATE_GRID_SETTINGS": return { ...e, gridSettings: { ...e.gridSettings, ...t.payload.settings } }; case "SET_SPACE_PANNING": return { ...e, isSpacePanning: t.payload.isSpacePanning }; case "START_PAN": return { ...e, panState: { isPanning: !0, startPosition: t.payload.position } }; case "UPDATE_PAN": { if (!e.panState.isPanning || !e.panState.startPosition) return e; const n = t.payload.position.x - e.panState.startPosition.x, o = t.payload.position.y - e.panState.startPosition.y; return { ...e, viewport: { ...e.viewport, offset: { x: e.viewport.offset.x + n, y: e.viewport.offset.y + o } }, panState: { ...e.panState, startPosition: t.payload.position } }; } case "END_PAN": return { ...e, panState: { isPanning: !1, startPosition: null } }; default: return e; } }, ei = { viewport: { offset: { x: 0, y: 0 }, scale: 1 }, gridSettings: { enabled: !1, size: 20, showGrid: !0, snapToGrid: !1, snapThreshold: 8 }, isSpacePanning: !1, panState: { isPanning: !1, startPosition: null } }, ti = { setViewport: (e) => ({ type: "SET_VIEWPORT", payload: { viewport: e } }), panViewport: (e) => ({ type: "PAN_VIEWPORT", payload: { delta: e } }), zoomViewport: (e, t) => ({ type: "ZOOM_VIEWPORT", payload: { scale: e, center: t } }), resetViewport: () => ({ type: "RESET_VIEWPORT" }), updateGridSettings: (e) => ({ type: "UPDATE_GRID_SETTINGS", payload: { settings: e } }), setSpacePanning: (e) => ({ type: "SET_SPACE_PANNING", payload: { isSpacePanning: e } }), startPan: (e) => ({ type: "START_PAN", payload: { position: e } }), updatePan: (e) => ({ type: "UPDATE_PAN", payload: { position: e } }), endPan: () => ({ type: "END_PAN" }) }, ni = (e, t) => ({ // Convert screen coordinates to canvas coordinates screenToCanvas: (n, o) => { if (!e.current) return console.warn("Canvas ref is not available for coordinate conversion"), { x: n, y: o }; const i = e.current.getBoundingClientRect(); return { x: (n - i.left - t.offset.x) / t.scale, y: (o - i.top - t.offset.y) / t.scale }; }, // Convert canvas coordinates to screen coordinates canvasToScreen: (n, o) => { if (!e.current) return console.warn("Canvas ref is not available for coordinate conversion"), { x: n, y: o }; const i = e.current.getBoundingClientRect(); return { x: n * t.scale + t.offset.x + i.left, y: o * t.scale + t.offset.y + i.top }; } }), nt = l.createContext(null), oi = ({ children: e, initialState: t }) => { const [n, o] = l.useReducer(Qs, { ...ei, ...t }), i = l.useRef(null), s = l.useMemo(() => ni(i, n.viewport), [n.viewport]), a = { state: n, dispatch: o, actions: ti, canvasRef: i, utils: s }; return /* @__PURE__ */ c(nt.Provider, { value: a, children: e }); }, ne = () => { const e = l.useContext(nt); if (!e) throw new Error("useNodeCanvas must be used within a NodeCanvasProvider"); return e; }, si = "enenodtrelis", ii = "enehdr", ai = "enetitle", ri = "enenodcouU5z", ci = "enetreecnt", li = "eneempState", di = "enetreeitm", ui = "eneseligQ", pi = "enenodeNamegua", hi = "enenodeicovQJ", gi = "eneexpbtn", fi = "enevis", mi = "enedrgvgz", yi = "enedraoveins", vi = "enedropind", bi = "enedropul", J = { nodeTreeList: si, header: ii, title: ai, nodeCount: ri, treeContainer: ci, emptyState: li, treeItem: di, selected: ui, nodeName: pi, nodeIcon: hi, expandButton: gi, visibilityButton: fi, dragging: mi, dragOverInside: yi, dropIndicator: vi, dropIndicatorPulse: bi }, Ni = ({ node: e, level: t, isSelected: n, onSelect: o, onToggleVisibility: i, onToggleExpand: s, childNodes: a, dragState: r, onNodeDrop: d, onDragStateChange: h }) => { var T; const u = e.type === "group" && a.length > 0, p = e.type === "group" && e.expanded !== !1, f = e.type === "group", y = r.draggingNodeId === e.id, C = r.dragOverNodeId === e.id, m = C ? r.dragOverPosition : null, b = (k) => { k.stopPropagation(), o(e.id, k.ctrlKey || k.metaKey); }, v = (k) => { k.stopPropagation(), s && u && s(e.id); }, D = (k) => { k.stopPropagation(), i && i(e.id); }, P = () => { switch (e.type) { case "group": return "📁"; case "input": return "📥"; case "output": return "📤"; case "process": return "⚙️"; default: return "📦"; } }, N = (k) => { k.dataTransfer.effectAllowed = "move", k.dataTransfer.setData("nodeId", e.id), h({ draggingNodeId: e.id }); }, S = () => { h({ draggingNodeId: null, dragOverNodeId: null, dragOverPosition: null }); }, g = (k) => { if (k.preventDefault(), k.stopPropagation(), r.draggingNodeId === e.id) return; const z = k.currentTarget.getBoundingClientRect(), M = k.clientY - z.top, O = z.height; let R; f && M > O * 0.25 && M < O * 0.75 ? R = "inside" : M < O / 2 ? R = "before" : R = "after", (r.dragOverNodeId !== e.id || r.dragOverPosition !== R) && h({ dragOverNodeId: e.id, dragOverPosition: R }); }, w = (k) => { k.currentTarget === k.target && h({ dragOverNodeId: null, dragOverPosition: null }); }, E = (k) => { k.preventDefault(), k.stopPropagation(); const z = k.dataTransfer.getData("nodeId"); z && r.dragOverPosition && d(z, e.id, r.dragOverPosition), h({ draggingNodeId: null, dragOverNodeId: null, dragOverPosition: null }); }; return /* @__PURE__ */ I(re, { children: [ C && m === "before" && /* @__PURE__ */ c("div", { className: J.dropIndicator, style: { marginLeft: `${t * 16 + 8}px` } }), /* @__PURE__ */ I( "div", { className: `${J.treeItem} ${n ? J.selected : ""} ${y ? J.dragging : ""} ${C && m === "inside" ? J.dragOverInside : ""}`, style: { paddingLeft: `${t * 16 + 8}px` }, onClick: b, draggable: !0, onDragStart: N, onDragEnd: S, onDragOver: g, onDragLeave: w, onDrop: E, children: [ u && /* @__PURE__ */ c( "button", { className: J.expandButton, onClick: v, "aria-label": p ? "Collapse" : "Expand", children: /* @__PURE__ */ c( "svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "currentColor", style: { transform: p ? "rotate(90deg)" : "rotate(0deg)", transition: "transform 0.2s" }, children: /* @__PURE__ */ c("path", { d: "M2 1l4 3-4 3V1z" }) } ) } ), /* @__PURE__ */ c("span", { className: J.nodeIcon, children: P() }), /* @__PURE__ */ c("span", { className: J.nodeName, children: ((T = e.data) == null ? void 0 : T.title) || e.type }), /* @__PURE__ */ c( "button", { className: J.visibilityButton, onClick: D, "aria-label": e.visible !== !1 ? "Hide" : "Show", children: /* @__PURE__ */ c("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: e.visible !== !1 ? /* @__PURE__ */ c("path", { d: "M8 3C4.5 3 1.73 5.11 1 8c.73 2.89 3.5 5 7 5s6.27-2.11 7-5c-.73-2.89-3.5-5-7-5zm0 8.5a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-5.5a2 2 0 1 0 0 4 2 2 0 0 0 0-4z" }) : /* @__PURE__ */ I(re, { children: [ /* @__PURE__ */ c("path", { d: "M8 3C4.5 3 1.73 5.11 1 8c.73 2.89 3.5 5 7 5s6.27-2.11 7-5c-.73-2.89-3.5-5-7-5zm0 8.5a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-5.5a2 2 0 1 0 0 4 2 2 0 0 0 0-4z", opacity: "0.3" }), /* @__PURE__ */ c("path", { d: "M2 2l12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) ] }) }) } ) ] } ), u && p && a.map((k) => /* @__PURE__ */ c( ot, { nodeId: k.id, level: t + 1, dragState: r, onNodeDrop: d, onDragStateChange: h }, k.id )), C && m === "after" && /* @__PURE__ */ c("div", { className: J.dropIndicator, style: { marginLeft: `${t * 16 + 8}px` } }) ] }); }, ot = ({ nodeId: e, level: t, dragState: n, onNodeDrop: o, onDragStateChange: i }) => { const { state: s, dispatch: a, actions: r } = W(), { state: d, dispatch: h, actions: u } = Z(), p = s.nodes[e]; if (!p) return null; const f = d.selectedNodeIds.includes(e), y = Object.values(s.nodes).filter((v) => v.parentId === e), C = l.useCallback((v, D) => { h(u.selectNode(v, D)); }, [h, u]), m = l.useCallback((v) => { const D = s.nodes[v]; D && a(r.updateNode(v, { visible: D.visible === !1 })); }, [s.nodes, a, r]), b = l.useCallback((v) => { const D = s.nodes[v]; D && D.type === "group" && a(r.updateNode(v, { expanded: !D.expanded })); }, [s.nodes, a, r]); return /* @__PURE__ */ c( Ni, { node: p, level: t, isSelected: f, onSelect: C, onToggleVisibility: m, onToggleExpand: b, childNodes: y, dragState: n, onNodeDrop: o, onDragStateChange: i } ); }, Ci = ({ className: e }) => { const { state: t, dispatch: n, actions: o } = W(), { dispatch: