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
JavaScript
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: