@lv-x-software-house/x_view 1.2.4 → 1.2.5-dev.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1984 -865
- package/dist/index.mjs +1991 -866
- package/package.json +52 -43
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
// src/XViewScene.jsx
|
|
2
|
-
import React25, {
|
|
2
|
+
import React25, {
|
|
3
|
+
useCallback as useCallback4,
|
|
4
|
+
useEffect as useEffect22,
|
|
5
|
+
useRef as useRef19,
|
|
6
|
+
useState as useState25,
|
|
7
|
+
useMemo as useMemo12
|
|
8
|
+
} from "react";
|
|
3
9
|
import { useRouter, useSearchParams } from "next/navigation";
|
|
4
10
|
import { useSession } from "next-auth/react";
|
|
5
11
|
import CryptoJS from "crypto-js";
|
|
@@ -105,6 +111,7 @@ function ContextMenu({
|
|
|
105
111
|
const [menuView, setMenuView] = useState("main");
|
|
106
112
|
const [selectedAncestry, setSelectedAncestry] = useState(null);
|
|
107
113
|
const [versionSubMenu, setVersionSubMenu] = useState(null);
|
|
114
|
+
const [labelSubMenu, setLabelSubMenu] = useState(null);
|
|
108
115
|
const [isLinkCopied, setIsLinkCopied] = useState(false);
|
|
109
116
|
const [selectedQuestStatus, setSelectedQuestStatus] = useState(null);
|
|
110
117
|
const ability = useMemo(() => defineAbilityFor(userRole), [userRole]);
|
|
@@ -113,6 +120,7 @@ function ContextMenu({
|
|
|
113
120
|
setMenuView("main");
|
|
114
121
|
setSelectedAncestry(null);
|
|
115
122
|
setVersionSubMenu(null);
|
|
123
|
+
setLabelSubMenu(null);
|
|
116
124
|
setSelectedQuestStatus(null);
|
|
117
125
|
}
|
|
118
126
|
}, [data.visible, (_a = data.nodeData) == null ? void 0 : _a.id]);
|
|
@@ -128,7 +136,7 @@ function ContextMenu({
|
|
|
128
136
|
if (left + w + 8 > vw) left = Math.max(8, vw - w - 8);
|
|
129
137
|
if (top + h + 8 > vh) top = Math.max(8, vh - h - 8);
|
|
130
138
|
setMenuPos({ left, top });
|
|
131
|
-
}, [data, menuView, versionSubMenu, selectedQuestStatus]);
|
|
139
|
+
}, [data, menuView, versionSubMenu, labelSubMenu, selectedQuestStatus]);
|
|
132
140
|
useEffect(() => {
|
|
133
141
|
if (!data.visible) return;
|
|
134
142
|
const handleClickOutside = (e) => {
|
|
@@ -180,7 +188,21 @@ function ContextMenu({
|
|
|
180
188
|
var _a2;
|
|
181
189
|
return (_a2 = c.targetNode) == null ? void 0 : _a2.is_quest;
|
|
182
190
|
});
|
|
183
|
-
const
|
|
191
|
+
const getLabelForConnection = (conn) => {
|
|
192
|
+
if (conn.direction === "outgoing") return conn.link.source_label || null;
|
|
193
|
+
if (conn.direction === "incoming") return conn.link.target_label || null;
|
|
194
|
+
return null;
|
|
195
|
+
};
|
|
196
|
+
const commonWithLabel = commonConnections.filter((c) => getLabelForConnection(c));
|
|
197
|
+
const commonWithoutLabel = commonConnections.filter((c) => !getLabelForConnection(c));
|
|
198
|
+
const labelGroups = commonWithLabel.reduce((acc, conn) => {
|
|
199
|
+
const label = getLabelForConnection(conn);
|
|
200
|
+
if (!acc[label]) acc[label] = [];
|
|
201
|
+
acc[label].push(conn);
|
|
202
|
+
return acc;
|
|
203
|
+
}, {});
|
|
204
|
+
const labelGroupEntries = Object.entries(labelGroups).sort(([a], [b]) => a.localeCompare(b));
|
|
205
|
+
const groupedConnections = commonWithoutLabel.reduce((acc, conn) => {
|
|
184
206
|
var _a2;
|
|
185
207
|
const { targetNode } = conn;
|
|
186
208
|
const groupingKey = ((_a2 = targetNode.version_node) == null ? void 0 : _a2.is_version) ? targetNode.version_node.parent_node : targetNode.id;
|
|
@@ -308,11 +330,32 @@ function ContextMenu({
|
|
|
308
330
|
/* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
|
|
309
331
|
))));
|
|
310
332
|
};
|
|
333
|
+
const renderLabelSubMenuView = () => {
|
|
334
|
+
const group = labelSubMenu;
|
|
335
|
+
const isScrollable = group.connections.length > 10;
|
|
336
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React.createElement("button", { onClick: () => {
|
|
337
|
+
setLabelSubMenu(null);
|
|
338
|
+
setMenuView("connections");
|
|
339
|
+
}, className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ React.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-violet-300/80 truncate", title: group.label }, group.label))), group.connections.length > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => handleExpandAndClose(group.connections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, group.connections.map((conn) => /* @__PURE__ */ React.createElement(
|
|
340
|
+
"button",
|
|
341
|
+
{
|
|
342
|
+
key: conn.targetNode.id,
|
|
343
|
+
onClick: () => handleExpandAndClose([conn.link]),
|
|
344
|
+
className: baseButtonClass,
|
|
345
|
+
title: `Expandir conex\xE3o com ${conn.targetNode.name}`
|
|
346
|
+
},
|
|
347
|
+
/* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }), /* @__PURE__ */ React.createElement("polyline", { points: "12 5 19 12 12 19" })),
|
|
348
|
+
/* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
|
|
349
|
+
))));
|
|
350
|
+
};
|
|
311
351
|
const renderConnectionsView = () => {
|
|
312
352
|
if (versionSubMenu) {
|
|
313
353
|
return renderVersionSubMenuView();
|
|
314
354
|
}
|
|
315
|
-
|
|
355
|
+
if (labelSubMenu) {
|
|
356
|
+
return renderLabelSubMenuView();
|
|
357
|
+
}
|
|
358
|
+
const totalItems = availableAncestries.length + labelGroupEntries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
|
|
316
359
|
const isScrollable = totalItems > 10;
|
|
317
360
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("main"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Conex\xF5es")), commonConnections.length > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => handleExpandAndClose(commonConnections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ React.createElement("div", { className: `flex flex-col ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, /* @__PURE__ */ React.createElement("div", { className: "px-2 py-1 text-[11px] uppercase tracking-wider text-indigo-400/90" }, "Ancestralidades Salvas"), availableAncestries.map((anc) => /* @__PURE__ */ React.createElement(
|
|
318
361
|
"button",
|
|
@@ -324,7 +367,20 @@ function ContextMenu({
|
|
|
324
367
|
},
|
|
325
368
|
/* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 3v4a2 2 0 0 0 2 2h4" }), /* @__PURE__ */ React.createElement("path", { d: "M18 3v4a2 2 0 0 1-2 2h-4" }), /* @__PURE__ */ React.createElement("circle", { cx: "6", cy: "3", r: "2" }), /* @__PURE__ */ React.createElement("circle", { cx: "18", cy: "3", r: "2" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "11", r: "2" }), /* @__PURE__ */ React.createElement("path", { d: "M12 13v6" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "21", r: "2" })),
|
|
326
369
|
/* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, anc.name || `Ancestralidade #${anc.ancestry_id.substring(0, 8)}`)
|
|
327
|
-
))),
|
|
370
|
+
))), labelGroupEntries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${availableAncestries.length > 0 ? "mt-2" : ""} ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), labelGroupEntries.map(([label, conns]) => /* @__PURE__ */ React.createElement(
|
|
371
|
+
"button",
|
|
372
|
+
{
|
|
373
|
+
key: label,
|
|
374
|
+
onClick: () => {
|
|
375
|
+
setLabelSubMenu({ label, connections: conns });
|
|
376
|
+
},
|
|
377
|
+
className: baseButtonClass,
|
|
378
|
+
title: `Ver grupo: ${label}`
|
|
379
|
+
},
|
|
380
|
+
/* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400" }, /* @__PURE__ */ React.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })),
|
|
381
|
+
/* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, label),
|
|
382
|
+
/* @__PURE__ */ React.createElement("span", { className: "text-xs px-2 py-0.5 bg-violet-500/20 text-violet-300 rounded-full" }, conns.length)
|
|
383
|
+
))), finalRenderableConnections.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, (availableAncestries.length > 0 || labelGroupEntries.length > 0) && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
|
|
328
384
|
if (group.isVersionGroup) {
|
|
329
385
|
return /* @__PURE__ */ React.createElement(
|
|
330
386
|
"button",
|
|
@@ -402,11 +458,18 @@ function ContextMenu({
|
|
|
402
458
|
))));
|
|
403
459
|
};
|
|
404
460
|
const renderAncestryActionsView = () => {
|
|
461
|
+
var _a2, _b2;
|
|
405
462
|
const ancestryTitle = (selectedAncestry == null ? void 0 : selectedAncestry.name) || `Ancestralidade #${selectedAncestry == null ? void 0 : selectedAncestry.ancestry_id.substring(0, 8)}`;
|
|
406
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400 truncate", title: ancestryTitle }, ancestryTitle))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, ability.can("read", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => {
|
|
407
|
-
onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry);
|
|
463
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400 truncate", title: ancestryTitle }, ancestryTitle))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, ability.can("read", "Ancestry") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("button", { onClick: () => {
|
|
464
|
+
onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry, "full");
|
|
465
|
+
onClose();
|
|
466
|
+
}, className: baseButtonClass, title: "Renderizar Ancestralidade Completa" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ React.createElement("span", null, "Renderizar Ancestralidade")), /* @__PURE__ */ React.createElement("button", { onClick: () => {
|
|
467
|
+
onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry, "ancestry_only");
|
|
468
|
+
onClose();
|
|
469
|
+
}, className: baseButtonClass, title: "Renderizar apenas a \xC1rvore de Ancestralidade (Radial)" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M12 2v3m0 14v3m10-10h-3m-14 0H2m15.66-6.34-2.12 2.12m-9.08 9.08-2.12 2.12m13.32 0-2.12-2.12m-9.08-9.08-2.12-2.12" })), /* @__PURE__ */ React.createElement("span", null, "\xC1rvore de Ancestralidade")), ((_b2 = (_a2 = selectedAncestry == null ? void 0 : selectedAncestry.abstraction_tree) == null ? void 0 : _a2.children) == null ? void 0 : _b2.length) > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => {
|
|
470
|
+
onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry, "abstraction_only");
|
|
408
471
|
onClose();
|
|
409
|
-
}, className: baseButtonClass, title: "Renderizar
|
|
472
|
+
}, className: baseButtonClass, title: "Renderizar apenas a \xC1rvore de Abstra\xE7\xE3o (Vertical)" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ React.createElement("rect", { x: "14", y: "3", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ React.createElement("rect", { x: "14", y: "14", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ React.createElement("rect", { x: "3", y: "14", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ React.createElement("path", { d: "M10 6.5h4" }), /* @__PURE__ */ React.createElement("path", { d: "M10 17.5h4" }), /* @__PURE__ */ React.createElement("path", { d: "M6.5 10v4" }), /* @__PURE__ */ React.createElement("path", { d: "M17.5 10v4" })), /* @__PURE__ */ React.createElement("span", null, "\xC1rvore de Abstra\xE7\xE3o"))), ability.can("update", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => {
|
|
410
473
|
onEditAncestry == null ? void 0 : onEditAncestry(selectedAncestry);
|
|
411
474
|
onClose();
|
|
412
475
|
}, className: baseButtonClass, title: "Editar Ancestralidade" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M12 20h9" }), /* @__PURE__ */ React.createElement("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" })), /* @__PURE__ */ React.createElement("span", null, "Editar Ancestralidade")), (ability.can("update", "Ancestry") || ability.can("delete", "Ancestry")) && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), ability.can("delete", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => {
|
|
@@ -653,7 +716,7 @@ function XViewSidebar({
|
|
|
653
716
|
"div",
|
|
654
717
|
{
|
|
655
718
|
ref: containerRef,
|
|
656
|
-
className: "ui-overlay fixed left-0 top-0 h-
|
|
719
|
+
className: "ui-overlay fixed left-0 top-0 h-[100dvh] w-[min(92vw,320px)] z-40 overflow-hidden",
|
|
657
720
|
onPointerDown: swallow,
|
|
658
721
|
onPointerMove: swallow,
|
|
659
722
|
onPointerUp: swallow,
|
|
@@ -662,7 +725,7 @@ function XViewSidebar({
|
|
|
662
725
|
onContextMenu: swallow,
|
|
663
726
|
onDoubleClick: swallow
|
|
664
727
|
},
|
|
665
|
-
/* @__PURE__ */ React2.createElement("div", { className: "h-full flex flex-col bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ React2.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React2.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ React2.createElement(
|
|
728
|
+
/* @__PURE__ */ React2.createElement("div", { className: "h-full flex flex-col overflow-hidden bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ React2.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React2.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ React2.createElement(
|
|
666
729
|
"button",
|
|
667
730
|
{
|
|
668
731
|
className: "ml-auto p-2 rounded-md text-slate-400 hover:text-white hover:bg-white/10 transition-colors",
|
|
@@ -719,7 +782,7 @@ function XViewSidebar({
|
|
|
719
782
|
autoComplete: "off"
|
|
720
783
|
}
|
|
721
784
|
)
|
|
722
|
-
), showList && /* @__PURE__ */ React2.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[
|
|
785
|
+
), showList && /* @__PURE__ */ React2.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[min(448px,50dvh)] overflow-y-auto rounded-lg bg-slate-900/95 border border-white/10 shadow-xl" }, filteredAndSorted.length > 0 ? filteredAndSorted.map((n) => {
|
|
723
786
|
const inView = isNodeInView(n.id);
|
|
724
787
|
const active = selectedNodeId === n.id;
|
|
725
788
|
const typeLabel = Array.isArray(n.type) ? n.type.join(", ") : n.type;
|
|
@@ -3095,6 +3158,7 @@ function calculateNodePositions(nodes) {
|
|
|
3095
3158
|
return positions;
|
|
3096
3159
|
}
|
|
3097
3160
|
function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, isSceneBusy, parentData, ancestryData }) {
|
|
3161
|
+
var _a, _b;
|
|
3098
3162
|
if (!tooltipEl || !camera || !mountEl) return;
|
|
3099
3163
|
let content = "";
|
|
3100
3164
|
let positionTarget = null;
|
|
@@ -3107,17 +3171,21 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
|
|
|
3107
3171
|
content = generateTooltipHtml(hoveredNode.userData, parentData, ancestryData);
|
|
3108
3172
|
}
|
|
3109
3173
|
} else if (hoveredLink && !isSceneBusy) {
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3174
|
+
const linkData = hoveredLink.userData.isAncestryLink ? hoveredLink.userData.relationship || {} : hoveredLink.userData;
|
|
3175
|
+
const hasContent = ((_a = linkData.name) == null ? void 0 : _a.trim()) || ((_b = linkData.description) == null ? void 0 : _b.trim());
|
|
3176
|
+
if (hasContent) {
|
|
3177
|
+
currentId = `link_${hoveredLink.userData.id}`;
|
|
3178
|
+
if (hoveredLink.userData.isCurved) {
|
|
3179
|
+
const positions = hoveredLink.geometry.attributes.position.array;
|
|
3180
|
+
const midIndex = Math.floor(positions.length / 2 / 3) * 3;
|
|
3181
|
+
positionTarget = new THREE2.Vector3(positions[midIndex], positions[midIndex + 1], positions[midIndex + 2]);
|
|
3182
|
+
} else {
|
|
3183
|
+
positionTarget = new THREE2.Vector3().addVectors(hoveredLink.userData.sourceNode.position, hoveredLink.userData.targetNode.position).multiplyScalar(0.5);
|
|
3184
|
+
}
|
|
3185
|
+
isLink = true;
|
|
3186
|
+
if (tooltipEl.dataset.currentId !== currentId) {
|
|
3187
|
+
content = generateLinkTooltipHtml(linkData, parentData, ancestryData);
|
|
3188
|
+
}
|
|
3121
3189
|
}
|
|
3122
3190
|
}
|
|
3123
3191
|
if (positionTarget) {
|
|
@@ -3426,9 +3494,9 @@ function CustomPropertyDisplay({
|
|
|
3426
3494
|
};
|
|
3427
3495
|
const handleRemoveListItem = (j) => setTempProp((p) => ({ ...p, value: p.value.filter((_, k) => k !== j) }));
|
|
3428
3496
|
const handleListItemChange = (j, f, v) => setTempProp((p) => {
|
|
3429
|
-
const
|
|
3430
|
-
|
|
3431
|
-
return { ...p, value:
|
|
3497
|
+
const newValue2 = [...p.value];
|
|
3498
|
+
newValue2[j] = { ...newValue2[j], [f]: v };
|
|
3499
|
+
return { ...p, value: newValue2 };
|
|
3432
3500
|
});
|
|
3433
3501
|
const baseInput = "w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60 transition-all duration-150";
|
|
3434
3502
|
const renderEditView = () => {
|
|
@@ -3482,14 +3550,14 @@ function CustomPropertyDisplay({
|
|
|
3482
3550
|
const inputClass = `${baseInput} ${noSpinnerClass}`;
|
|
3483
3551
|
const handleDateTypeChange = (newDateType) => {
|
|
3484
3552
|
var _a3, _b2, _c2;
|
|
3485
|
-
let
|
|
3553
|
+
let newValue2 = { type: newDateType };
|
|
3486
3554
|
if (newDateType === "Date Interval") {
|
|
3487
|
-
|
|
3488
|
-
|
|
3555
|
+
newValue2.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
|
|
3556
|
+
newValue2.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
|
|
3489
3557
|
} else {
|
|
3490
|
-
|
|
3558
|
+
newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
|
|
3491
3559
|
}
|
|
3492
|
-
handlePropChange("value",
|
|
3560
|
+
handlePropChange("value", newValue2);
|
|
3493
3561
|
};
|
|
3494
3562
|
const handleDateValueChange = (dateField, dateValue) => {
|
|
3495
3563
|
handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
|
|
@@ -4337,7 +4405,7 @@ ${space}${bullet} `);
|
|
|
4337
4405
|
}
|
|
4338
4406
|
),
|
|
4339
4407
|
/* @__PURE__ */ React5.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0 shrink-0" }),
|
|
4340
|
-
/* @__PURE__ */ React5.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React5.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80" }), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-slate-200" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ React5.createElement("button", { onClick: handleClose, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")),
|
|
4408
|
+
/* @__PURE__ */ React5.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React5.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shrink-0" }), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-slate-200 truncate" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ React5.createElement("button", { onClick: handleClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")),
|
|
4341
4409
|
/* @__PURE__ */ React5.createElement("div", { className: "px-6 py-3 flex flex-col gap-3 border-b border-white/5 bg-white/5 shrink-0" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 flex-wrap" }, /* @__PURE__ */ React5.createElement(
|
|
4342
4410
|
"button",
|
|
4343
4411
|
{
|
|
@@ -5466,7 +5534,7 @@ function AncestryRelationshipPanel({
|
|
|
5466
5534
|
onImageClick: handleImageClickFromText,
|
|
5467
5535
|
onSaveDescription: handleSaveDescriptionInline
|
|
5468
5536
|
}
|
|
5469
|
-
) : /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React8.createElement("div",
|
|
5537
|
+
) : /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React8.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-cyan-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ React8.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o de Ancestralidade")), /* @__PURE__ */ React8.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Editar Rela\xE7\xE3o")), /* @__PURE__ */ React8.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pb-4 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ React8.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React8.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o da Rela\xE7\xE3o"), /* @__PURE__ */ React8.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React8.createElement(
|
|
5470
5538
|
DescriptionDisplay,
|
|
5471
5539
|
{
|
|
5472
5540
|
description,
|
|
@@ -5788,6 +5856,7 @@ function CreateAncestryPanel({
|
|
|
5788
5856
|
} = ancestryMode;
|
|
5789
5857
|
const [isSaving, setIsSaving] = useState11(false);
|
|
5790
5858
|
const [isLinkCopied, setIsLinkCopied] = useState11(false);
|
|
5859
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState11(false);
|
|
5791
5860
|
const [showDeleteBranchConfirm, setShowDeleteBranchConfirm] = useState11(false);
|
|
5792
5861
|
const handleCopyLink = (e) => {
|
|
5793
5862
|
e.stopPropagation();
|
|
@@ -5855,12 +5924,14 @@ function CreateAncestryPanel({
|
|
|
5855
5924
|
};
|
|
5856
5925
|
const handleSelectAncestryParent = (nodeId, isAbstraction = false) => {
|
|
5857
5926
|
setAncestryMode((prev) => isAbstraction ? { ...prev, selectedAbstractionParentId: nodeId } : { ...prev, selectedParentId: nodeId });
|
|
5927
|
+
setHasUnsavedChanges(true);
|
|
5858
5928
|
};
|
|
5859
5929
|
const handleToggleAddMode = (isAbstraction = false) => {
|
|
5860
5930
|
if (isAbstraction && !ancestryMode.isAddingAbstractionNodes) {
|
|
5861
5931
|
setTargetRenderNodeId(null);
|
|
5862
5932
|
}
|
|
5863
5933
|
setAncestryMode((prev) => isAbstraction ? { ...prev, isAddingAbstractionNodes: !prev.isAddingAbstractionNodes } : { ...prev, isAddingNodes: !prev.isAddingNodes });
|
|
5934
|
+
setHasUnsavedChanges(true);
|
|
5864
5935
|
};
|
|
5865
5936
|
const handleRemoveNode = useCallback2((pathToRemove, isAbstraction = false) => {
|
|
5866
5937
|
if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) return;
|
|
@@ -5878,6 +5949,7 @@ function CreateAncestryPanel({
|
|
|
5878
5949
|
const indexToRemove = pathToRemove[pathToRemove.length - 1];
|
|
5879
5950
|
if (currentParent.children && currentParent.children.length > indexToRemove) {
|
|
5880
5951
|
currentParent.children.splice(indexToRemove, 1);
|
|
5952
|
+
setHasUnsavedChanges(true);
|
|
5881
5953
|
}
|
|
5882
5954
|
return { ...prev, [treeKey]: newTree };
|
|
5883
5955
|
});
|
|
@@ -5936,6 +6008,7 @@ function CreateAncestryPanel({
|
|
|
5936
6008
|
updateGlobalTree(rootTreeClone);
|
|
5937
6009
|
}
|
|
5938
6010
|
setAncestryMode((prev) => ({ ...prev, [treeKey]: rootTreeClone }));
|
|
6011
|
+
setHasUnsavedChanges(true);
|
|
5939
6012
|
} else {
|
|
5940
6013
|
alert("N\xE3o \xE9 poss\xEDvel mover um node para dentro de seus pr\xF3prios descendentes.");
|
|
5941
6014
|
}
|
|
@@ -6008,6 +6081,7 @@ function CreateAncestryPanel({
|
|
|
6008
6081
|
const handleAddProp = () => {
|
|
6009
6082
|
const newProp = createNewCustomProperty(customProps);
|
|
6010
6083
|
setCustomProps((p) => [...p, newProp]);
|
|
6084
|
+
setHasUnsavedChanges(true);
|
|
6011
6085
|
setTimeout(() => {
|
|
6012
6086
|
var _a;
|
|
6013
6087
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -6016,11 +6090,13 @@ function CreateAncestryPanel({
|
|
|
6016
6090
|
const handleRemoveProp = (i) => {
|
|
6017
6091
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
6018
6092
|
setCustomProps(newProps);
|
|
6093
|
+
setHasUnsavedChanges(true);
|
|
6019
6094
|
};
|
|
6020
6095
|
const handleUpdateProp = (index, updatedProp) => {
|
|
6021
6096
|
const newProps = [...customProps];
|
|
6022
6097
|
newProps[index] = updatedProp;
|
|
6023
6098
|
setCustomProps(newProps);
|
|
6099
|
+
setHasUnsavedChanges(true);
|
|
6024
6100
|
};
|
|
6025
6101
|
const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
|
|
6026
6102
|
useEffect10(() => {
|
|
@@ -6130,6 +6206,7 @@ function CreateAncestryPanel({
|
|
|
6130
6206
|
updateGlobalTree(rootTreeClone);
|
|
6131
6207
|
setBranchStack([...branchStack]);
|
|
6132
6208
|
setIsPickerOpen(false);
|
|
6209
|
+
setHasUnsavedChanges(true);
|
|
6133
6210
|
try {
|
|
6134
6211
|
setIsSaving(true);
|
|
6135
6212
|
const rootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6143,6 +6220,7 @@ function CreateAncestryPanel({
|
|
|
6143
6220
|
rootExtras
|
|
6144
6221
|
);
|
|
6145
6222
|
setLastSavedSnapshot(takeSnapshot(rootTreeClone, ancestryName, description, processedSections, [], isPrivate, ancestryMode.abstraction_tree));
|
|
6223
|
+
setHasUnsavedChanges(false);
|
|
6146
6224
|
if (onRenderFullAncestry) {
|
|
6147
6225
|
const fullTreePayload = {
|
|
6148
6226
|
ancestry_id: ancestryMode.currentAncestryId || "temp_root",
|
|
@@ -6185,6 +6263,7 @@ function CreateAncestryPanel({
|
|
|
6185
6263
|
if (branchIndex !== -1) {
|
|
6186
6264
|
foundParentPath.node.parallel_branches.splice(branchIndex, 1);
|
|
6187
6265
|
updateGlobalTree(rootTreeClone);
|
|
6266
|
+
setHasUnsavedChanges(true);
|
|
6188
6267
|
try {
|
|
6189
6268
|
setIsSaving(true);
|
|
6190
6269
|
const currentRootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6206,6 +6285,7 @@ function CreateAncestryPanel({
|
|
|
6206
6285
|
isPrivate,
|
|
6207
6286
|
ancestryMode.abstraction_tree
|
|
6208
6287
|
));
|
|
6288
|
+
setHasUnsavedChanges(false);
|
|
6209
6289
|
if (onClearAncestryVisuals) {
|
|
6210
6290
|
onClearAncestryVisuals(currentStep.branchId);
|
|
6211
6291
|
}
|
|
@@ -6238,6 +6318,7 @@ function CreateAncestryPanel({
|
|
|
6238
6318
|
if (branchIndex !== -1) {
|
|
6239
6319
|
foundParentPath.node.parallel_branches.splice(branchIndex, 1);
|
|
6240
6320
|
updateGlobalTree(rootTreeClone);
|
|
6321
|
+
setHasUnsavedChanges(true);
|
|
6241
6322
|
try {
|
|
6242
6323
|
setIsSaving(true);
|
|
6243
6324
|
const currentRootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6259,6 +6340,7 @@ function CreateAncestryPanel({
|
|
|
6259
6340
|
isPrivate,
|
|
6260
6341
|
ancestryMode.abstraction_tree
|
|
6261
6342
|
));
|
|
6343
|
+
setHasUnsavedChanges(false);
|
|
6262
6344
|
if (onClearAncestryVisuals) {
|
|
6263
6345
|
onClearAncestryVisuals(currentStep.branchId);
|
|
6264
6346
|
}
|
|
@@ -6520,6 +6602,7 @@ function CreateAncestryPanel({
|
|
|
6520
6602
|
}
|
|
6521
6603
|
setBranchStack(parentStack);
|
|
6522
6604
|
setTargetScrollSectionId(targetFocusId);
|
|
6605
|
+
setHasUnsavedChanges(true);
|
|
6523
6606
|
if (onRenderFullAncestry) {
|
|
6524
6607
|
const parentStack2 = currentStack;
|
|
6525
6608
|
const rotation = parentStack2.reduce((acc, step) => {
|
|
@@ -6581,7 +6664,6 @@ function CreateAncestryPanel({
|
|
|
6581
6664
|
direction,
|
|
6582
6665
|
tree: {
|
|
6583
6666
|
node: nodeData,
|
|
6584
|
-
node_id: nodeId,
|
|
6585
6667
|
children: [],
|
|
6586
6668
|
relationship: {}
|
|
6587
6669
|
}
|
|
@@ -6603,6 +6685,7 @@ function CreateAncestryPanel({
|
|
|
6603
6685
|
savedMaxIndex: parentIndexToSave,
|
|
6604
6686
|
entryDirection: direction
|
|
6605
6687
|
}]);
|
|
6688
|
+
setHasUnsavedChanges(true);
|
|
6606
6689
|
if (branch && branch.tree && onRenderFullAncestry) {
|
|
6607
6690
|
const branchAncestryObj = {
|
|
6608
6691
|
ancestry_id: branch.id,
|
|
@@ -6653,6 +6736,10 @@ function CreateAncestryPanel({
|
|
|
6653
6736
|
const currentInputName = overrides.ancestryName !== void 0 ? overrides.ancestryName : ancestryName;
|
|
6654
6737
|
const currentInputDesc = overrides.description !== void 0 ? overrides.description : description;
|
|
6655
6738
|
const currentInputSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
6739
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
6740
|
+
onClose();
|
|
6741
|
+
return;
|
|
6742
|
+
}
|
|
6656
6743
|
if (!currentInputName.trim()) {
|
|
6657
6744
|
alert("O nome n\xE3o pode estar vazio.");
|
|
6658
6745
|
return;
|
|
@@ -6660,11 +6747,7 @@ function CreateAncestryPanel({
|
|
|
6660
6747
|
setIsSaving(true);
|
|
6661
6748
|
const processedSections = processDescriptionForSave(currentInputDesc, currentInputSections);
|
|
6662
6749
|
setExistingSections(processedSections);
|
|
6663
|
-
const updatedRootTree =
|
|
6664
|
-
ancestryMode.tree,
|
|
6665
|
-
currentInputDesc,
|
|
6666
|
-
processedSections
|
|
6667
|
-
);
|
|
6750
|
+
const updatedRootTree = JSON.parse(JSON.stringify(ancestryMode.tree));
|
|
6668
6751
|
const extrasObj = {
|
|
6669
6752
|
...toObjectFromCustomProps(customProps.filter((p) => !p.isEditing)),
|
|
6670
6753
|
is_private: isPrivate
|
|
@@ -6706,6 +6789,7 @@ function CreateAncestryPanel({
|
|
|
6706
6789
|
isPrivate,
|
|
6707
6790
|
ancestryMode.abstraction_tree
|
|
6708
6791
|
));
|
|
6792
|
+
setHasUnsavedChanges(false);
|
|
6709
6793
|
if (onRenderFullAncestry) {
|
|
6710
6794
|
const rotation = branchStack.reduce((acc, step) => {
|
|
6711
6795
|
return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
|
|
@@ -6757,6 +6841,7 @@ function CreateAncestryPanel({
|
|
|
6757
6841
|
updatedRootTree,
|
|
6758
6842
|
extrasObj
|
|
6759
6843
|
);
|
|
6844
|
+
setHasUnsavedChanges(false);
|
|
6760
6845
|
setLastSavedSnapshot(takeSnapshot(
|
|
6761
6846
|
updatedRootTree,
|
|
6762
6847
|
currentInputName,
|
|
@@ -6779,6 +6864,7 @@ function CreateAncestryPanel({
|
|
|
6779
6864
|
const newTreeString = JSON.stringify(newRootTree);
|
|
6780
6865
|
if (currentTreeString !== newTreeString) {
|
|
6781
6866
|
updateGlobalTree(newRootTree);
|
|
6867
|
+
setHasUnsavedChanges(true);
|
|
6782
6868
|
}
|
|
6783
6869
|
}, [description, existingSections]);
|
|
6784
6870
|
const handleTriggerFullRender = () => {
|
|
@@ -6801,6 +6887,7 @@ function CreateAncestryPanel({
|
|
|
6801
6887
|
};
|
|
6802
6888
|
const handleSaveDescriptionInline = (newDesc) => {
|
|
6803
6889
|
setDescription(newDesc);
|
|
6890
|
+
setHasUnsavedChanges(true);
|
|
6804
6891
|
handleLocalSave(true, { description: newDesc });
|
|
6805
6892
|
};
|
|
6806
6893
|
const swallow = (e) => e.stopPropagation();
|
|
@@ -6930,7 +7017,11 @@ function CreateAncestryPanel({
|
|
|
6930
7017
|
{
|
|
6931
7018
|
type: "text",
|
|
6932
7019
|
value: ancestryName,
|
|
6933
|
-
onChange: (e) =>
|
|
7020
|
+
onChange: (e) => {
|
|
7021
|
+
setAncestryName(e.target.value);
|
|
7022
|
+
setHasUnsavedChanges(true);
|
|
7023
|
+
},
|
|
7024
|
+
readOnly: isContextLinked,
|
|
6934
7025
|
placeholder: "Nome da Ancestralidade",
|
|
6935
7026
|
className: "text-xl sm:text-2xl font-semibold tracking-tight bg-transparent border-none p-0 focus:ring-2 focus:ring-indigo-500 rounded-md -ml-1.5 px-1.5 w-full outline-none transition-all focus:bg-slate-800/70"
|
|
6936
7027
|
}
|
|
@@ -7579,9 +7670,9 @@ function InSceneCreationForm({
|
|
|
7579
7670
|
};
|
|
7580
7671
|
const handleToggleImageMode = () => {
|
|
7581
7672
|
var _a2, _b;
|
|
7582
|
-
const
|
|
7583
|
-
setUseImageAsTexture(
|
|
7584
|
-
if (
|
|
7673
|
+
const newValue2 = !useImageAsTexture;
|
|
7674
|
+
setUseImageAsTexture(newValue2);
|
|
7675
|
+
if (newValue2) {
|
|
7585
7676
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
7586
7677
|
if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
|
|
7587
7678
|
const url = firstImageProp.value[0].value;
|
|
@@ -7879,9 +7970,9 @@ function InSceneVersionForm({
|
|
|
7879
7970
|
};
|
|
7880
7971
|
const handleToggleImageMode = () => {
|
|
7881
7972
|
var _a, _b;
|
|
7882
|
-
const
|
|
7883
|
-
setUseImageAsTexture(
|
|
7884
|
-
if (
|
|
7973
|
+
const newValue2 = !useImageAsTexture;
|
|
7974
|
+
setUseImageAsTexture(newValue2);
|
|
7975
|
+
if (newValue2) {
|
|
7885
7976
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
7886
7977
|
if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
|
|
7887
7978
|
const url = firstImageProp.value[0].value;
|
|
@@ -8301,6 +8392,7 @@ function NodeDetailsPanel({
|
|
|
8301
8392
|
return !!(node == null ? void 0 : node.useImageAsTexture);
|
|
8302
8393
|
});
|
|
8303
8394
|
const [selectedImageUrl, setSelectedImageUrl] = useState17((node == null ? void 0 : node.textureImageUrl) ?? null);
|
|
8395
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState17(false);
|
|
8304
8396
|
const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
|
|
8305
8397
|
const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
|
|
8306
8398
|
initialWidth: isReadMode ? 700 : 440,
|
|
@@ -8338,6 +8430,7 @@ function NodeDetailsPanel({
|
|
|
8338
8430
|
else if ((node == null ? void 0 : node.useImageAsTexture) === "false") setUseImageAsTexture(false);
|
|
8339
8431
|
else setUseImageAsTexture(!!(node == null ? void 0 : node.useImageAsTexture));
|
|
8340
8432
|
setSelectedImageUrl((node == null ? void 0 : node.textureImageUrl) ?? null);
|
|
8433
|
+
setHasUnsavedChanges(false);
|
|
8341
8434
|
}
|
|
8342
8435
|
}, [node]);
|
|
8343
8436
|
const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
|
|
@@ -8364,6 +8457,7 @@ function NodeDetailsPanel({
|
|
|
8364
8457
|
setIntensity(val);
|
|
8365
8458
|
onIntensityChange == null ? void 0 : onIntensityChange(node.id, val);
|
|
8366
8459
|
onDataUpdate == null ? void 0 : onDataUpdate({ ...node, intensity: val });
|
|
8460
|
+
setHasUnsavedChanges(true);
|
|
8367
8461
|
};
|
|
8368
8462
|
const handleCopyLink = () => {
|
|
8369
8463
|
if (!(node == null ? void 0 : node.id)) return;
|
|
@@ -8381,14 +8475,17 @@ function NodeDetailsPanel({
|
|
|
8381
8475
|
const v = e.target.value;
|
|
8382
8476
|
setName(v);
|
|
8383
8477
|
onNameChange == null ? void 0 : onNameChange(node.id, v);
|
|
8478
|
+
setHasUnsavedChanges(true);
|
|
8384
8479
|
};
|
|
8385
8480
|
const handleColorChange = (val) => {
|
|
8386
8481
|
setColor(val);
|
|
8387
8482
|
onColorChange == null ? void 0 : onColorChange(node.id, val);
|
|
8483
|
+
setHasUnsavedChanges(true);
|
|
8388
8484
|
};
|
|
8389
8485
|
const handleSizeChange = (newSize) => {
|
|
8390
8486
|
setSize(newSize);
|
|
8391
8487
|
onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
|
|
8488
|
+
setHasUnsavedChanges(true);
|
|
8392
8489
|
};
|
|
8393
8490
|
const handleAddType = (newType) => {
|
|
8394
8491
|
const trimmed = newType.trim();
|
|
@@ -8396,10 +8493,12 @@ function NodeDetailsPanel({
|
|
|
8396
8493
|
setTypes([...types, trimmed]);
|
|
8397
8494
|
setTypeInput("");
|
|
8398
8495
|
setShowTypeSuggestions(false);
|
|
8496
|
+
setHasUnsavedChanges(true);
|
|
8399
8497
|
}
|
|
8400
8498
|
};
|
|
8401
8499
|
const handleRemoveType = (indexToRemove) => {
|
|
8402
8500
|
setTypes(types.filter((_, index) => index !== indexToRemove));
|
|
8501
|
+
setHasUnsavedChanges(true);
|
|
8403
8502
|
};
|
|
8404
8503
|
const handleTypeInputKeyDown = (e) => {
|
|
8405
8504
|
if (e.key === "Enter") {
|
|
@@ -8412,6 +8511,7 @@ function NodeDetailsPanel({
|
|
|
8412
8511
|
const handleAddProp = () => {
|
|
8413
8512
|
const newProp = createNewCustomProperty(customProps);
|
|
8414
8513
|
setCustomProps((p) => [...p, newProp]);
|
|
8514
|
+
setHasUnsavedChanges(true);
|
|
8415
8515
|
setTimeout(() => {
|
|
8416
8516
|
var _a;
|
|
8417
8517
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -8420,19 +8520,21 @@ function NodeDetailsPanel({
|
|
|
8420
8520
|
const handleRemoveProp = (i) => {
|
|
8421
8521
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
8422
8522
|
setCustomProps(newProps);
|
|
8523
|
+
setHasUnsavedChanges(true);
|
|
8423
8524
|
triggerAutoSave({ customProps: newProps });
|
|
8424
8525
|
};
|
|
8425
8526
|
const handleUpdateProp = (index, updatedProp) => {
|
|
8426
8527
|
const newProps = [...customProps];
|
|
8427
8528
|
newProps[index] = updatedProp;
|
|
8428
8529
|
setCustomProps(newProps);
|
|
8530
|
+
setHasUnsavedChanges(true);
|
|
8429
8531
|
if (!updatedProp.isEditing) {
|
|
8430
8532
|
triggerAutoSave({ customProps: newProps });
|
|
8431
8533
|
}
|
|
8432
8534
|
};
|
|
8433
8535
|
const handleToggleImageMode = () => {
|
|
8434
|
-
const newValue = !useImageAsTexture;
|
|
8435
8536
|
setUseImageAsTexture(newValue);
|
|
8537
|
+
setHasUnsavedChanges(true);
|
|
8436
8538
|
let activeUrl = null;
|
|
8437
8539
|
if (newValue) {
|
|
8438
8540
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
@@ -8454,6 +8556,7 @@ function NodeDetailsPanel({
|
|
|
8454
8556
|
};
|
|
8455
8557
|
const handleSelectTexture = (url) => {
|
|
8456
8558
|
setSelectedImageUrl(url);
|
|
8559
|
+
setHasUnsavedChanges(true);
|
|
8457
8560
|
onImageChange == null ? void 0 : onImageChange(true, url, color);
|
|
8458
8561
|
onDataUpdate == null ? void 0 : onDataUpdate({
|
|
8459
8562
|
...node,
|
|
@@ -8464,6 +8567,7 @@ function NodeDetailsPanel({
|
|
|
8464
8567
|
};
|
|
8465
8568
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
8466
8569
|
setDescription(newDescription);
|
|
8570
|
+
setHasUnsavedChanges(true);
|
|
8467
8571
|
onDataUpdate({ ...node, description: newDescription });
|
|
8468
8572
|
triggerAutoSave({ description: newDescription });
|
|
8469
8573
|
};
|
|
@@ -8474,6 +8578,10 @@ function NodeDetailsPanel({
|
|
|
8474
8578
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
8475
8579
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
8476
8580
|
const currentIntensity = overrides.intensity !== void 0 ? overrides.intensity : intensity;
|
|
8581
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
8582
|
+
onClose();
|
|
8583
|
+
return;
|
|
8584
|
+
}
|
|
8477
8585
|
if (!currentName.trim() || currentTypes.length === 0) {
|
|
8478
8586
|
alert("O campo 'Nome' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
|
|
8479
8587
|
return;
|
|
@@ -8498,6 +8606,7 @@ function NodeDetailsPanel({
|
|
|
8498
8606
|
};
|
|
8499
8607
|
await onSave(dataToSave, keepOpen);
|
|
8500
8608
|
onDataUpdate(dataToSave);
|
|
8609
|
+
setHasUnsavedChanges(false);
|
|
8501
8610
|
if (!keepOpen) {
|
|
8502
8611
|
onClose();
|
|
8503
8612
|
}
|
|
@@ -8564,7 +8673,7 @@ function NodeDetailsPanel({
|
|
|
8564
8673
|
onImageClick: handleImageClickFromText,
|
|
8565
8674
|
onSaveDescription: handleSaveDescriptionInline
|
|
8566
8675
|
}
|
|
8567
|
-
) : /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React16.createElement("div",
|
|
8676
|
+
) : /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React16.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React16.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React16.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shadow-[0_0_18px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React16.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ React16.createElement(
|
|
8568
8677
|
"button",
|
|
8569
8678
|
{
|
|
8570
8679
|
onClick: handleCopyLink,
|
|
@@ -8572,7 +8681,7 @@ function NodeDetailsPanel({
|
|
|
8572
8681
|
title: isLinkCopied ? "Link Copiado!" : "Copiar link para este Node"
|
|
8573
8682
|
},
|
|
8574
8683
|
isLinkCopied ? /* @__PURE__ */ React16.createElement(FiCheck10, { size: 12 }) : /* @__PURE__ */ React16.createElement(FiLink5, { size: 12 })
|
|
8575
|
-
)), /* @__PURE__ */ React16.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ React16.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React16.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React16.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ React16.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React16.createElement("span", { key: index, className: "flex items-center gap-1 bg-indigo-500/30 text-indigo-100 px-1.5 py-0.5 rounded-md text-xs font-medium border border-indigo-500/20" }, t, canEdit && /* @__PURE__ */ React16.createElement(
|
|
8684
|
+
)), /* @__PURE__ */ React16.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ React16.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React16.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React16.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ React16.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React16.createElement("span", { key: index, className: "flex items-center gap-1 bg-indigo-500/30 text-indigo-100 px-1.5 py-0.5 rounded-md text-xs font-medium border border-indigo-500/20" }, t, canEdit && /* @__PURE__ */ React16.createElement(
|
|
8576
8685
|
"button",
|
|
8577
8686
|
{
|
|
8578
8687
|
type: "button",
|
|
@@ -8734,6 +8843,7 @@ function NodeDetailsPanel({
|
|
|
8734
8843
|
initialValue: description,
|
|
8735
8844
|
onSave: (newDescription) => {
|
|
8736
8845
|
setDescription(newDescription);
|
|
8846
|
+
setHasUnsavedChanges(true);
|
|
8737
8847
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
8738
8848
|
triggerAutoSave({ description: newDescription });
|
|
8739
8849
|
},
|
|
@@ -8793,6 +8903,7 @@ function QuestDetailsPanel({
|
|
|
8793
8903
|
const [existingSections, setExistingSections] = useState18((node == null ? void 0 : node.description_sections) || []);
|
|
8794
8904
|
const [isSaving, setIsSaving] = useState18(false);
|
|
8795
8905
|
const [isLinkCopied, setIsLinkCopied] = useState18(false);
|
|
8906
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState18(false);
|
|
8796
8907
|
const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
|
|
8797
8908
|
const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
|
|
8798
8909
|
initialWidth: isReadMode ? 700 : 440,
|
|
@@ -8824,6 +8935,7 @@ function QuestDetailsPanel({
|
|
|
8824
8935
|
setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
|
|
8825
8936
|
setExistingSections((node == null ? void 0 : node.description_sections) || []);
|
|
8826
8937
|
setCustomProps(extractCustomPropsFromNode(node || {}));
|
|
8938
|
+
setHasUnsavedChanges(false);
|
|
8827
8939
|
}
|
|
8828
8940
|
}, [node]);
|
|
8829
8941
|
useEffect16(() => {
|
|
@@ -8855,16 +8967,19 @@ function QuestDetailsPanel({
|
|
|
8855
8967
|
setRawTitle(val);
|
|
8856
8968
|
const newStandardName = questPrefix ? `${questPrefix} - \xBB ${val || "Sem t\xEDtulo"}` : val;
|
|
8857
8969
|
onNameChange == null ? void 0 : onNameChange(node.id, newStandardName, val);
|
|
8970
|
+
setHasUnsavedChanges(true);
|
|
8858
8971
|
};
|
|
8859
8972
|
const handleSizeChange = (newSize) => {
|
|
8860
8973
|
setSize(newSize);
|
|
8861
8974
|
onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
|
|
8975
|
+
setHasUnsavedChanges(true);
|
|
8862
8976
|
};
|
|
8863
8977
|
const handleStatusChange = (newStatus) => {
|
|
8864
8978
|
setStatus(newStatus);
|
|
8865
8979
|
const newColor = QUEST_STATUS_COLORS3[newStatus];
|
|
8866
8980
|
onColorChange == null ? void 0 : onColorChange(node.id, newColor);
|
|
8867
8981
|
onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor });
|
|
8982
|
+
setHasUnsavedChanges(true);
|
|
8868
8983
|
};
|
|
8869
8984
|
const handleAddType = (newType) => {
|
|
8870
8985
|
const trimmed = newType.trim();
|
|
@@ -8872,11 +8987,13 @@ function QuestDetailsPanel({
|
|
|
8872
8987
|
setTypes([...types, trimmed]);
|
|
8873
8988
|
setTypeInput("");
|
|
8874
8989
|
setShowTypeSuggestions(false);
|
|
8990
|
+
setHasUnsavedChanges(true);
|
|
8875
8991
|
}
|
|
8876
8992
|
};
|
|
8877
8993
|
const handleRemoveType = (indexToRemove) => {
|
|
8878
8994
|
if (types[indexToRemove] === "quest") return;
|
|
8879
8995
|
setTypes(types.filter((_, index) => index !== indexToRemove));
|
|
8996
|
+
setHasUnsavedChanges(true);
|
|
8880
8997
|
};
|
|
8881
8998
|
const handleTypeInputKeyDown = (e) => {
|
|
8882
8999
|
if (e.key === "Enter") {
|
|
@@ -8889,6 +9006,7 @@ function QuestDetailsPanel({
|
|
|
8889
9006
|
const handleAddProp = () => {
|
|
8890
9007
|
const newProp = createNewCustomProperty(customProps);
|
|
8891
9008
|
setCustomProps((p) => [...p, newProp]);
|
|
9009
|
+
setHasUnsavedChanges(true);
|
|
8892
9010
|
setTimeout(() => {
|
|
8893
9011
|
var _a2;
|
|
8894
9012
|
(_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -8897,18 +9015,21 @@ function QuestDetailsPanel({
|
|
|
8897
9015
|
const handleRemoveProp = (i) => {
|
|
8898
9016
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
8899
9017
|
setCustomProps(newProps);
|
|
9018
|
+
setHasUnsavedChanges(true);
|
|
8900
9019
|
triggerAutoSave({ customProps: newProps });
|
|
8901
9020
|
};
|
|
8902
9021
|
const handleUpdateProp = (index, updatedProp) => {
|
|
8903
9022
|
const newProps = [...customProps];
|
|
8904
9023
|
newProps[index] = updatedProp;
|
|
8905
9024
|
setCustomProps(newProps);
|
|
9025
|
+
setHasUnsavedChanges(true);
|
|
8906
9026
|
if (!updatedProp.isEditing) {
|
|
8907
9027
|
triggerAutoSave({ customProps: newProps });
|
|
8908
9028
|
}
|
|
8909
9029
|
};
|
|
8910
9030
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
8911
9031
|
setDescription(newDescription);
|
|
9032
|
+
setHasUnsavedChanges(true);
|
|
8912
9033
|
onDataUpdate({ ...node, description: newDescription });
|
|
8913
9034
|
triggerAutoSave({ description: newDescription });
|
|
8914
9035
|
};
|
|
@@ -8920,6 +9041,10 @@ function QuestDetailsPanel({
|
|
|
8920
9041
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
8921
9042
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
8922
9043
|
const currentStatus = overrides.status !== void 0 ? overrides.status : status;
|
|
9044
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
9045
|
+
onClose();
|
|
9046
|
+
return;
|
|
9047
|
+
}
|
|
8923
9048
|
if (!currentRawTitle.trim() || currentTypes.length === 0) {
|
|
8924
9049
|
alert("O campo 'T\xEDtulo' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
|
|
8925
9050
|
return;
|
|
@@ -8949,6 +9074,7 @@ function QuestDetailsPanel({
|
|
|
8949
9074
|
};
|
|
8950
9075
|
await onSave(dataToSave, keepOpen);
|
|
8951
9076
|
onDataUpdate(dataToSave);
|
|
9077
|
+
setHasUnsavedChanges(false);
|
|
8952
9078
|
if (!keepOpen) {
|
|
8953
9079
|
onClose();
|
|
8954
9080
|
}
|
|
@@ -9006,7 +9132,7 @@ function QuestDetailsPanel({
|
|
|
9006
9132
|
onImageClick: handleImageClickFromText,
|
|
9007
9133
|
onSaveDescription: handleSaveDescriptionInline
|
|
9008
9134
|
}
|
|
9009
|
-
) : /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React17.createElement("div",
|
|
9135
|
+
) : /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React17.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React17.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React17.createElement(FiTarget2, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ React17.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ React17.createElement("button", { onClick: handleCopyLink, className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-sky-400"}`, title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Quest" }, isLinkCopied ? /* @__PURE__ */ React17.createElement(FiCheck11, { size: 12 }) : /* @__PURE__ */ React17.createElement(FiLink6, { size: 12 }))), /* @__PURE__ */ React17.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, standardizedName || (node == null ? void 0 : node.name))), /* @__PURE__ */ React17.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "T\xEDtulo da Quest"), /* @__PURE__ */ React17.createElement(
|
|
9010
9136
|
"input",
|
|
9011
9137
|
{
|
|
9012
9138
|
type: "text",
|
|
@@ -9180,9 +9306,12 @@ function RelationshipDetailsPanel({
|
|
|
9180
9306
|
const [description, setDescription] = useState20((link == null ? void 0 : link.description) ?? "");
|
|
9181
9307
|
const [customProps, setCustomProps] = useState20(() => extractCustomPropsFromNode(link || {}));
|
|
9182
9308
|
const [existingSections, setExistingSections] = useState20((link == null ? void 0 : link.description_sections) || []);
|
|
9309
|
+
const [sourceLabel, setSourceLabel] = useState20((link == null ? void 0 : link.source_label) ?? "");
|
|
9310
|
+
const [targetLabel, setTargetLabel] = useState20((link == null ? void 0 : link.target_label) ?? "");
|
|
9183
9311
|
const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState20(false);
|
|
9184
9312
|
const [isSaving, setIsSaving] = useState20(false);
|
|
9185
9313
|
const [isReadMode, setIsReadMode] = useState20(false);
|
|
9314
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState20(false);
|
|
9186
9315
|
const propsEndRef = useRef16(null);
|
|
9187
9316
|
const canEdit = useMemo9(() => {
|
|
9188
9317
|
const ability = defineAbilityFor(userRole);
|
|
@@ -9193,12 +9322,16 @@ function RelationshipDetailsPanel({
|
|
|
9193
9322
|
setDescription((link == null ? void 0 : link.description) ?? "");
|
|
9194
9323
|
setExistingSections((link == null ? void 0 : link.description_sections) || []);
|
|
9195
9324
|
setCustomProps(extractCustomPropsFromNode(link || {}));
|
|
9325
|
+
setSourceLabel((link == null ? void 0 : link.source_label) ?? "");
|
|
9326
|
+
setTargetLabel((link == null ? void 0 : link.target_label) ?? "");
|
|
9327
|
+
setHasUnsavedChanges(false);
|
|
9196
9328
|
}, [link]);
|
|
9197
9329
|
const swallow = (e) => e.stopPropagation();
|
|
9198
9330
|
const handleAddProp = () => {
|
|
9199
9331
|
if (!canEdit) return;
|
|
9200
9332
|
const newProp = createNewCustomProperty(customProps);
|
|
9201
9333
|
setCustomProps((p) => [...p, newProp]);
|
|
9334
|
+
setHasUnsavedChanges(true);
|
|
9202
9335
|
setTimeout(() => {
|
|
9203
9336
|
var _a;
|
|
9204
9337
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -9210,6 +9343,12 @@ function RelationshipDetailsPanel({
|
|
|
9210
9343
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
9211
9344
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
9212
9345
|
const currentName = overrides.name !== void 0 ? overrides.name : name;
|
|
9346
|
+
const currentSourceLabel = overrides.sourceLabel !== void 0 ? overrides.sourceLabel : sourceLabel;
|
|
9347
|
+
const currentTargetLabel = overrides.targetLabel !== void 0 ? overrides.targetLabel : targetLabel;
|
|
9348
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
9349
|
+
onClose();
|
|
9350
|
+
return;
|
|
9351
|
+
}
|
|
9213
9352
|
setIsSaving(true);
|
|
9214
9353
|
try {
|
|
9215
9354
|
const extrasObj = toObjectFromCustomProps(currentCustomProps.filter((p) => !p.isEditing));
|
|
@@ -9225,8 +9364,11 @@ function RelationshipDetailsPanel({
|
|
|
9225
9364
|
isCurved: link.isCurved,
|
|
9226
9365
|
curveOffset: link.curveOffset
|
|
9227
9366
|
};
|
|
9367
|
+
if (currentSourceLabel.trim()) dataToSave.source_label = currentSourceLabel.trim();
|
|
9368
|
+
if (currentTargetLabel.trim()) dataToSave.target_label = currentTargetLabel.trim();
|
|
9228
9369
|
await onSave(dataToSave, keepOpen);
|
|
9229
9370
|
onDataUpdate(dataToSave);
|
|
9371
|
+
setHasUnsavedChanges(false);
|
|
9230
9372
|
if (!keepOpen) {
|
|
9231
9373
|
onClose();
|
|
9232
9374
|
}
|
|
@@ -9240,18 +9382,21 @@ function RelationshipDetailsPanel({
|
|
|
9240
9382
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
9241
9383
|
if (!canEdit) return;
|
|
9242
9384
|
setDescription(newDescription);
|
|
9385
|
+
setHasUnsavedChanges(true);
|
|
9243
9386
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
9244
9387
|
triggerAutoSave({ description: newDescription });
|
|
9245
9388
|
};
|
|
9246
9389
|
const handleRemoveProp = (i) => {
|
|
9247
9390
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
9248
9391
|
setCustomProps(newProps);
|
|
9392
|
+
setHasUnsavedChanges(true);
|
|
9249
9393
|
triggerAutoSave({ customProps: newProps });
|
|
9250
9394
|
};
|
|
9251
9395
|
const handleUpdateProp = (index, updatedProp) => {
|
|
9252
9396
|
const newProps = [...customProps];
|
|
9253
9397
|
newProps[index] = updatedProp;
|
|
9254
9398
|
setCustomProps(newProps);
|
|
9399
|
+
setHasUnsavedChanges(true);
|
|
9255
9400
|
if (!updatedProp.isEditing) {
|
|
9256
9401
|
triggerAutoSave({ customProps: newProps });
|
|
9257
9402
|
}
|
|
@@ -9299,19 +9444,52 @@ function RelationshipDetailsPanel({
|
|
|
9299
9444
|
onImageClick: handleImageClickFromText,
|
|
9300
9445
|
onSaveDescription: handleSaveDescriptionInline
|
|
9301
9446
|
}
|
|
9302
|
-
) : /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React19.createElement("div",
|
|
9447
|
+
) : /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React19.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React19.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-teal-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ React19.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o")), /* @__PURE__ */ React19.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || "Rela\xE7\xE3o")), /* @__PURE__ */ React19.createElement("button", { onClick: onClose, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Rela\xE7\xE3o (Opcional)"), /* @__PURE__ */ React19.createElement(
|
|
9303
9448
|
"input",
|
|
9304
9449
|
{
|
|
9305
9450
|
type: "text",
|
|
9306
9451
|
value: name,
|
|
9307
|
-
onChange: (e) =>
|
|
9452
|
+
onChange: (e) => {
|
|
9453
|
+
setName(e.target.value);
|
|
9454
|
+
setHasUnsavedChanges(true);
|
|
9455
|
+
},
|
|
9308
9456
|
placeholder: "Ex: Controla, Pertence a, Fornece...",
|
|
9309
9457
|
disabled: !canEdit,
|
|
9310
9458
|
className: `w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60
|
|
9311
9459
|
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9312
9460
|
`
|
|
9313
9461
|
}
|
|
9314
|
-
)), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.
|
|
9462
|
+
)), /* @__PURE__ */ React19.createElement("div", { className: "rounded-xl border border-white/8 bg-slate-800/30 p-3 space-y-3" }, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React19.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ React19.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React19.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ React19.createElement("p", { className: "text-xs font-medium text-violet-300/80 uppercase tracking-wider" }, "Labels de Agrupamento")), /* @__PURE__ */ React19.createElement("p", { className: "text-[11px] text-slate-400 leading-relaxed" }, "Labels agrupam conex\xF5es no menu de Conex\xF5es. Cada lado da conex\xE3o pode ter sua pr\xF3pria label."), /* @__PURE__ */ React19.createElement("div", { className: "grid grid-cols-2 gap-2" }, /* @__PURE__ */ React19.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Source"), /* @__PURE__ */ React19.createElement(
|
|
9463
|
+
"input",
|
|
9464
|
+
{
|
|
9465
|
+
type: "text",
|
|
9466
|
+
value: sourceLabel,
|
|
9467
|
+
onChange: (e) => {
|
|
9468
|
+
setSourceLabel(e.target.value);
|
|
9469
|
+
setHasUnsavedChanges(true);
|
|
9470
|
+
},
|
|
9471
|
+
placeholder: "Ex: Conceitos",
|
|
9472
|
+
disabled: !canEdit,
|
|
9473
|
+
className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
|
|
9474
|
+
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9475
|
+
`
|
|
9476
|
+
}
|
|
9477
|
+
)), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Target"), /* @__PURE__ */ React19.createElement(
|
|
9478
|
+
"input",
|
|
9479
|
+
{
|
|
9480
|
+
type: "text",
|
|
9481
|
+
value: targetLabel,
|
|
9482
|
+
onChange: (e) => {
|
|
9483
|
+
setTargetLabel(e.target.value);
|
|
9484
|
+
setHasUnsavedChanges(true);
|
|
9485
|
+
},
|
|
9486
|
+
placeholder: "Ex: Refer\xEAncias",
|
|
9487
|
+
disabled: !canEdit,
|
|
9488
|
+
className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
|
|
9489
|
+
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9490
|
+
`
|
|
9491
|
+
}
|
|
9492
|
+
)))), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React19.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React19.createElement(
|
|
9315
9493
|
DescriptionDisplay,
|
|
9316
9494
|
{
|
|
9317
9495
|
description,
|
|
@@ -9381,6 +9559,7 @@ function RelationshipDetailsPanel({
|
|
|
9381
9559
|
onSave: (newDescription) => {
|
|
9382
9560
|
if (!canEdit) return;
|
|
9383
9561
|
setDescription(newDescription);
|
|
9562
|
+
setHasUnsavedChanges(true);
|
|
9384
9563
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
9385
9564
|
triggerAutoSave({ description: newDescription });
|
|
9386
9565
|
},
|
|
@@ -9828,7 +10007,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
|
|
|
9828
10007
|
onMentionClick,
|
|
9829
10008
|
onImageClick: handleImageClickFromText
|
|
9830
10009
|
}
|
|
9831
|
-
) : /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React23.createElement("div",
|
|
10010
|
+
) : /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React23.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React23.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React23.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ React23.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ React23.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ React23.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ React23.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ React23.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ React23.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ React23.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React23.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React23.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React23.createElement(
|
|
9832
10011
|
"button",
|
|
9833
10012
|
{
|
|
9834
10013
|
onClick: () => setIsReadMode(true),
|
|
@@ -9890,7 +10069,6 @@ var GroupItem = ({
|
|
|
9890
10069
|
onPlayAncestry,
|
|
9891
10070
|
availableIds,
|
|
9892
10071
|
canEdit
|
|
9893
|
-
// [NOVO] Recebe permissão de edição
|
|
9894
10072
|
}) => {
|
|
9895
10073
|
const canIndent = index > 0;
|
|
9896
10074
|
const isPickingForThisGroup = pickingGroupId === group.id;
|
|
@@ -9905,107 +10083,126 @@ var GroupItem = ({
|
|
|
9905
10083
|
useEffect21(() => {
|
|
9906
10084
|
adjustHeight();
|
|
9907
10085
|
}, [group.text]);
|
|
9908
|
-
return /* @__PURE__ */ React24.createElement("div", { className: "flex flex-col gap-2 mb-3 pl-3 border-l border-white/10 relative group/item animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React24.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ React24.createElement(
|
|
10086
|
+
return /* @__PURE__ */ React24.createElement("div", { className: "flex flex-col gap-2 mb-3 pl-3 border-l border-white/10 relative group/item animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React24.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ React24.createElement(
|
|
10087
|
+
"div",
|
|
10088
|
+
{
|
|
10089
|
+
className: `
|
|
9909
10090
|
flex flex-col gap-2 py-2 px-3 transition-all duration-200
|
|
9910
10091
|
${isPickingForThisGroup ? "bg-indigo-500/10 border-l-2 border-indigo-500" : "hover:bg-white/5 border-l-2 border-transparent hover:border-white/20"}
|
|
9911
|
-
`
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
className: `w-full bg-transparent text-sm text-slate-200 placeholder-slate-600 resize-none focus:outline-none focus:placeholder-slate-500 overflow-y-auto ${!canEdit ? "cursor-default" : ""}`,
|
|
9916
|
-
rows: 1,
|
|
9917
|
-
style: {
|
|
9918
|
-
minHeight: "1.5rem",
|
|
9919
|
-
maxHeight: "250px"
|
|
9920
|
-
},
|
|
9921
|
-
placeholder: canEdit ? "Escreva sobre este grupo..." : "",
|
|
9922
|
-
value: group.text,
|
|
9923
|
-
readOnly: !canEdit,
|
|
9924
|
-
onChange: (e) => {
|
|
9925
|
-
if (canEdit) onUpdate(group.id, { ...group, text: e.target.value });
|
|
9926
|
-
}
|
|
9927
|
-
}
|
|
9928
|
-
), group.ancestries && group.ancestries.length > 0 && /* @__PURE__ */ React24.createElement("div", { className: "flex flex-wrap gap-2 mt-1" }, group.ancestries.map((anc) => {
|
|
9929
|
-
const isValid = availableIds.has(String(anc.ancestry_id));
|
|
9930
|
-
return /* @__PURE__ */ React24.createElement(
|
|
9931
|
-
"div",
|
|
10092
|
+
`
|
|
10093
|
+
},
|
|
10094
|
+
/* @__PURE__ */ React24.createElement(
|
|
10095
|
+
"textarea",
|
|
9932
10096
|
{
|
|
9933
|
-
|
|
9934
|
-
className: `
|
|
10097
|
+
ref: textareaRef,
|
|
10098
|
+
className: `w-full bg-transparent text-sm text-slate-200 placeholder-slate-600 resize-none focus:outline-none focus:placeholder-slate-500 overflow-y-auto ${!canEdit ? "cursor-default" : ""}`,
|
|
10099
|
+
rows: 1,
|
|
10100
|
+
style: {
|
|
10101
|
+
minHeight: "1.5rem",
|
|
10102
|
+
maxHeight: "250px"
|
|
10103
|
+
},
|
|
10104
|
+
placeholder: canEdit ? "Escreva sobre este grupo..." : "",
|
|
10105
|
+
value: group.text,
|
|
10106
|
+
readOnly: !canEdit,
|
|
10107
|
+
onChange: (e) => {
|
|
10108
|
+
if (canEdit) onUpdate(group.id, { ...group, text: e.target.value });
|
|
10109
|
+
}
|
|
10110
|
+
}
|
|
10111
|
+
),
|
|
10112
|
+
group.ancestries && group.ancestries.length > 0 && /* @__PURE__ */ React24.createElement("div", { className: "flex flex-wrap gap-2 mt-1" }, group.ancestries.map((anc) => {
|
|
10113
|
+
const isValid = availableIds.has(String(anc.ancestry_id));
|
|
10114
|
+
return /* @__PURE__ */ React24.createElement(
|
|
10115
|
+
"div",
|
|
10116
|
+
{
|
|
10117
|
+
key: anc.ancestry_id,
|
|
10118
|
+
className: `
|
|
9935
10119
|
flex items-center gap-2 rounded-md px-3 py-1.5 text-xs transition-all group/card border
|
|
9936
10120
|
${isValid ? "bg-slate-800/60 border-white/10 hover:border-indigo-500/30 text-slate-200" : "bg-red-900/10 border-red-500/30 text-red-300/70 hover:bg-red-900/20"}
|
|
9937
10121
|
`,
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
10122
|
+
title: isValid ? "" : "Esta ancestralidade foi removida ou n\xE3o existe mais."
|
|
10123
|
+
},
|
|
10124
|
+
isValid ? (
|
|
10125
|
+
// [MANTIDO] Botão Play visível para todos
|
|
10126
|
+
/* @__PURE__ */ React24.createElement(
|
|
10127
|
+
"button",
|
|
10128
|
+
{
|
|
10129
|
+
onClick: () => onPlayAncestry(anc.ancestry_id),
|
|
10130
|
+
className: "text-indigo-400 hover:text-white hover:bg-indigo-500 p-1 rounded-full transition-colors",
|
|
10131
|
+
title: "Renderizar no cen\xE1rio"
|
|
10132
|
+
},
|
|
10133
|
+
/* @__PURE__ */ React24.createElement(FiPlay, { size: 10, className: "ml-0.5 fill-current" })
|
|
10134
|
+
)
|
|
10135
|
+
) : /* @__PURE__ */ React24.createElement("div", { className: "p-1 text-red-500 cursor-not-allowed" }, /* @__PURE__ */ React24.createElement(FiAlertTriangle, { size: 10 })),
|
|
9942
10136
|
/* @__PURE__ */ React24.createElement(
|
|
10137
|
+
"span",
|
|
10138
|
+
{
|
|
10139
|
+
className: `font-medium truncate max-w-[150px] ${!isValid && "line-through decoration-red-500/50"}`
|
|
10140
|
+
},
|
|
10141
|
+
anc.name
|
|
10142
|
+
),
|
|
10143
|
+
canEdit && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement(
|
|
10144
|
+
"div",
|
|
10145
|
+
{
|
|
10146
|
+
className: `w-px h-3 mx-0.5 ${isValid ? "bg-white/10" : "bg-red-500/20"}`
|
|
10147
|
+
}
|
|
10148
|
+
), /* @__PURE__ */ React24.createElement(
|
|
9943
10149
|
"button",
|
|
9944
10150
|
{
|
|
9945
|
-
onClick: () =>
|
|
9946
|
-
className: "text-
|
|
9947
|
-
title: "
|
|
10151
|
+
onClick: () => onRemoveAncestry(group.id, anc.ancestry_id),
|
|
10152
|
+
className: `${isValid ? "text-slate-500 hover:text-red-400" : "text-red-400 hover:text-red-200"} p-0.5 rounded transition-colors`,
|
|
10153
|
+
title: "Remover men\xE7\xE3o"
|
|
9948
10154
|
},
|
|
9949
|
-
/* @__PURE__ */ React24.createElement(
|
|
9950
|
-
)
|
|
9951
|
-
)
|
|
9952
|
-
|
|
9953
|
-
|
|
9954
|
-
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
title: "Remover men\xE7\xE3o"
|
|
9959
|
-
},
|
|
9960
|
-
/* @__PURE__ */ React24.createElement(FiX7, { size: 12 })
|
|
9961
|
-
))
|
|
9962
|
-
);
|
|
9963
|
-
})), canEdit && /* @__PURE__ */ React24.createElement("div", { className: "flex items-center justify-between pt-2 mt-1 border-t border-white/5 opacity-40 group-hover/item:opacity-100 transition-opacity" }, /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React24.createElement(
|
|
9964
|
-
"button",
|
|
9965
|
-
{
|
|
9966
|
-
onClick: () => onRequestPickAncestry(group.id),
|
|
9967
|
-
className: `
|
|
10155
|
+
/* @__PURE__ */ React24.createElement(FiX7, { size: 12 })
|
|
10156
|
+
))
|
|
10157
|
+
);
|
|
10158
|
+
})),
|
|
10159
|
+
canEdit && /* @__PURE__ */ React24.createElement("div", { className: "flex items-center justify-between pt-2 mt-1 border-t border-white/5 opacity-40 group-hover/item:opacity-100 transition-opacity" }, /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React24.createElement(
|
|
10160
|
+
"button",
|
|
10161
|
+
{
|
|
10162
|
+
onClick: () => onRequestPickAncestry(group.id),
|
|
10163
|
+
className: `
|
|
9968
10164
|
flex items-center gap-1.5 px-2 py-1 rounded text-xs font-medium transition-colors
|
|
9969
10165
|
${isPickingForThisGroup ? "text-indigo-300 animate-pulse" : "text-slate-500 hover:text-indigo-300 hover:bg-indigo-500/10"}
|
|
9970
10166
|
`,
|
|
9971
|
-
|
|
9972
|
-
|
|
9973
|
-
|
|
9974
|
-
|
|
9975
|
-
|
|
9976
|
-
|
|
9977
|
-
|
|
9978
|
-
|
|
9979
|
-
|
|
9980
|
-
|
|
9981
|
-
|
|
9982
|
-
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10167
|
+
title: "Adicionar Ancestralidade a este grupo"
|
|
10168
|
+
},
|
|
10169
|
+
isPickingForThisGroup ? /* @__PURE__ */ React24.createElement(FiCheckCircle, { size: 12 }) : /* @__PURE__ */ React24.createElement(FiSearch4, { size: 12 }),
|
|
10170
|
+
isPickingForThisGroup ? "Selecionando..." : "Adicionar"
|
|
10171
|
+
), /* @__PURE__ */ React24.createElement(
|
|
10172
|
+
"button",
|
|
10173
|
+
{
|
|
10174
|
+
onClick: () => onAddSubgroup(group.id),
|
|
10175
|
+
className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
|
|
10176
|
+
title: "Criar Subgrupo"
|
|
10177
|
+
},
|
|
10178
|
+
/* @__PURE__ */ React24.createElement(FiPlus9, { size: 14 })
|
|
10179
|
+
)), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React24.createElement(
|
|
10180
|
+
"button",
|
|
10181
|
+
{
|
|
10182
|
+
onClick: () => onIndent(group.id),
|
|
10183
|
+
disabled: !canIndent,
|
|
10184
|
+
className: `p-1.5 rounded transition-colors ${!canIndent ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`,
|
|
10185
|
+
title: "Aninhar no grupo acima"
|
|
10186
|
+
},
|
|
10187
|
+
/* @__PURE__ */ React24.createElement(FiArrowRight, { size: 14 })
|
|
10188
|
+
), /* @__PURE__ */ React24.createElement(
|
|
10189
|
+
"button",
|
|
10190
|
+
{
|
|
10191
|
+
onClick: () => onOutdent(group.id),
|
|
10192
|
+
className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
|
|
10193
|
+
title: "Desaninhar"
|
|
10194
|
+
},
|
|
10195
|
+
/* @__PURE__ */ React24.createElement(FiArrowLeft3, { size: 14 })
|
|
10196
|
+
), /* @__PURE__ */ React24.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), /* @__PURE__ */ React24.createElement(
|
|
10197
|
+
"button",
|
|
10198
|
+
{
|
|
10199
|
+
onClick: () => onDelete(group.id),
|
|
10200
|
+
className: "p-1.5 text-slate-600 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors",
|
|
10201
|
+
title: "Remover Grupo"
|
|
10202
|
+
},
|
|
10203
|
+
/* @__PURE__ */ React24.createElement(FiTrash23, { size: 14 })
|
|
10204
|
+
)))
|
|
10205
|
+
), group.children && group.children.length > 0 && /* @__PURE__ */ React24.createElement("div", { className: "ml-2" }, group.children.map((childGroup, idx) => /* @__PURE__ */ React24.createElement(
|
|
10009
10206
|
GroupItem,
|
|
10010
10207
|
{
|
|
10011
10208
|
key: childGroup.id,
|
|
@@ -10214,7 +10411,9 @@ function AncestryBoard({
|
|
|
10214
10411
|
const addRecursive = (list) => {
|
|
10215
10412
|
return list.map((g) => {
|
|
10216
10413
|
if (g.id === pickingGroupId) {
|
|
10217
|
-
const exists = g.ancestries.some(
|
|
10414
|
+
const exists = g.ancestries.some(
|
|
10415
|
+
(a) => a.ancestry_id === ancestry.ancestry_id
|
|
10416
|
+
);
|
|
10218
10417
|
if (exists) return g;
|
|
10219
10418
|
return { ...g, ancestries: [...g.ancestries, ancestry] };
|
|
10220
10419
|
}
|
|
@@ -10225,12 +10424,16 @@ function AncestryBoard({
|
|
|
10225
10424
|
});
|
|
10226
10425
|
setPickingGroupId(null);
|
|
10227
10426
|
} else {
|
|
10228
|
-
const fullAncestry = availableAncestries.find(
|
|
10427
|
+
const fullAncestry = availableAncestries.find(
|
|
10428
|
+
(a) => a.ancestry_id === ancestry.ancestry_id
|
|
10429
|
+
) || ancestry;
|
|
10229
10430
|
onSelect(fullAncestry);
|
|
10230
10431
|
}
|
|
10231
10432
|
};
|
|
10232
10433
|
const handlePlayFromGroup = (ancestryId) => {
|
|
10233
|
-
const fullAncestry = availableAncestries.find(
|
|
10434
|
+
const fullAncestry = availableAncestries.find(
|
|
10435
|
+
(a) => a.ancestry_id === ancestryId
|
|
10436
|
+
);
|
|
10234
10437
|
if (fullAncestry && onSelect) {
|
|
10235
10438
|
onSelect(fullAncestry);
|
|
10236
10439
|
}
|
|
@@ -10240,7 +10443,12 @@ function AncestryBoard({
|
|
|
10240
10443
|
const removeRecursive = (list) => {
|
|
10241
10444
|
return list.map((g) => {
|
|
10242
10445
|
if (g.id === groupId) {
|
|
10243
|
-
return {
|
|
10446
|
+
return {
|
|
10447
|
+
...g,
|
|
10448
|
+
ancestries: g.ancestries.filter(
|
|
10449
|
+
(a) => a.ancestry_id !== ancestryId
|
|
10450
|
+
)
|
|
10451
|
+
};
|
|
10244
10452
|
}
|
|
10245
10453
|
return { ...g, children: removeRecursive(g.children) };
|
|
10246
10454
|
});
|
|
@@ -10261,7 +10469,13 @@ function AncestryBoard({
|
|
|
10261
10469
|
className: "bg-slate-950 border border-white/10 rounded-xl w-[98vw] h-[97vh] flex flex-col shadow-2xl overflow-hidden animate-in fade-in zoom-in-95 duration-200",
|
|
10262
10470
|
onClick: (e) => e.stopPropagation()
|
|
10263
10471
|
},
|
|
10264
|
-
/* @__PURE__ */ React24.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React24.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ React24.createElement(FiLayers6, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React24.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-slate-900/50 border border-white/5" }, saveStatus === "saving" && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement(
|
|
10472
|
+
/* @__PURE__ */ React24.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React24.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ React24.createElement(FiLayers6, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React24.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-slate-900/50 border border-white/5" }, saveStatus === "saving" && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement(
|
|
10473
|
+
FiLoader5,
|
|
10474
|
+
{
|
|
10475
|
+
className: "animate-spin text-indigo-400",
|
|
10476
|
+
size: 12
|
|
10477
|
+
}
|
|
10478
|
+
), /* @__PURE__ */ React24.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement(FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ React24.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ React24.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-3" }, pickingGroupId && /* @__PURE__ */ React24.createElement("span", { className: "text-xs text-indigo-300 font-medium animate-pulse hidden sm:inline-block mr-2" }, "Selecione na lateral..."), canEdit && /* @__PURE__ */ React24.createElement(
|
|
10265
10479
|
"button",
|
|
10266
10480
|
{
|
|
10267
10481
|
onClick: handleAddRootGroup,
|
|
@@ -10277,57 +10491,75 @@ function AncestryBoard({
|
|
|
10277
10491
|
},
|
|
10278
10492
|
"\xD7"
|
|
10279
10493
|
))),
|
|
10280
|
-
/* @__PURE__ */ React24.createElement("div", { className: "flex flex-1 overflow-hidden" }, /* @__PURE__ */ React24.createElement(
|
|
10494
|
+
/* @__PURE__ */ React24.createElement("div", { className: "flex flex-1 overflow-hidden" }, /* @__PURE__ */ React24.createElement(
|
|
10495
|
+
"div",
|
|
10496
|
+
{
|
|
10497
|
+
className: `
|
|
10281
10498
|
flex flex-col border-r border-white/10 transition-all duration-300 flex-none
|
|
10282
10499
|
${pickingGroupId ? "w-[25%] border-indigo-500/30" : "w-[20%]"}
|
|
10283
10500
|
min-w-[280px] max-w-[500px] bg-slate-900
|
|
10284
|
-
`
|
|
10285
|
-
|
|
10286
|
-
{
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
10501
|
+
`
|
|
10502
|
+
},
|
|
10503
|
+
/* @__PURE__ */ React24.createElement("div", { className: "p-3 border-b border-white/5 bg-slate-900/50" }, /* @__PURE__ */ React24.createElement("div", { className: "relative group" }, /* @__PURE__ */ React24.createElement(
|
|
10504
|
+
FiSearch4,
|
|
10505
|
+
{
|
|
10506
|
+
className: `absolute left-3 top-1/2 -translate-y-1/2 transition-colors ${pickingGroupId ? "text-indigo-400" : "text-slate-500 group-focus-within:text-indigo-400"}`
|
|
10507
|
+
}
|
|
10508
|
+
), /* @__PURE__ */ React24.createElement(
|
|
10509
|
+
"input",
|
|
10510
|
+
{
|
|
10511
|
+
type: "text",
|
|
10512
|
+
placeholder: pickingGroupId ? "Pesquise para adicionar..." : "Pesquisar ancestralidade...",
|
|
10513
|
+
className: `
|
|
10290
10514
|
w-full rounded-md pl-9 pr-4 py-2 text-sm transition-all focus:outline-none focus:ring-1 focus:ring-indigo-500 border
|
|
10291
10515
|
${pickingGroupId ? "bg-indigo-950/30 border-indigo-500/30 text-white placeholder-indigo-300/50" : "bg-slate-950 border-white/10 text-slate-200 placeholder-slate-600"}
|
|
10292
10516
|
`,
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10517
|
+
value: searchTerm,
|
|
10518
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
10519
|
+
autoFocus: !pickingGroupId
|
|
10520
|
+
}
|
|
10521
|
+
))),
|
|
10522
|
+
/* @__PURE__ */ React24.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-3 space-y-2" }, filtered.map((anc) => {
|
|
10523
|
+
const parentNodeName = nodeNamesMap.get(String(anc.ancestral_node)) || "Node Desconhecido";
|
|
10524
|
+
const isPicking = !!pickingGroupId;
|
|
10525
|
+
return /* @__PURE__ */ React24.createElement(
|
|
10526
|
+
"div",
|
|
10527
|
+
{
|
|
10528
|
+
key: anc.ancestry_id,
|
|
10529
|
+
onClick: () => {
|
|
10530
|
+
if (isPicking) handleSelectAncestry(anc);
|
|
10531
|
+
},
|
|
10532
|
+
className: `
|
|
10308
10533
|
group relative flex items-start gap-3 p-3 text-left rounded-lg border transition-all duration-200
|
|
10309
10534
|
${isPicking ? "border-indigo-500/30 bg-indigo-500/5 hover:bg-indigo-500/20 hover:border-indigo-400 cursor-pointer" : "border-white/5 bg-slate-800/40 hover:bg-indigo-600/10 hover:border-indigo-500/30 cursor-default"}
|
|
10310
10535
|
`
|
|
10311
|
-
|
|
10312
|
-
|
|
10536
|
+
},
|
|
10537
|
+
/* @__PURE__ */ React24.createElement(
|
|
10538
|
+
"div",
|
|
10539
|
+
{
|
|
10540
|
+
className: `
|
|
10313
10541
|
mt-0.5 w-8 h-8 rounded-md grid place-content-center shrink-0 border transition-all shadow-lg
|
|
10314
10542
|
${isPicking ? "bg-indigo-500 text-white border-indigo-400" : "bg-slate-800 text-indigo-400 border-white/5 group-hover:bg-indigo-500 group-hover:text-white"}
|
|
10315
|
-
`
|
|
10316
|
-
/* @__PURE__ */ React24.createElement("div", { className: "flex-1 min-w-0 pb-2" }, /* @__PURE__ */ React24.createElement("div", { className: "flex items-center justify-between gap-2" }, /* @__PURE__ */ React24.createElement("h4", { className: "text-sm font-medium text-slate-200 group-hover:text-white truncate transition-colors" }, anc.name || "Sem Nome"), anc.is_private && /* @__PURE__ */ React24.createElement("span", { className: "text-[9px] px-1 py-0.5 rounded bg-amber-500/10 text-amber-300 border border-amber-500/20" }, "Priv")), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1.5 mt-0.5 text-[11px] text-slate-500 group-hover:text-indigo-200/70 transition-colors" }, /* @__PURE__ */ React24.createElement(FiCornerUpRight4, { size: 10 }), /* @__PURE__ */ React24.createElement("span", { className: "truncate max-w-[120px]" }, parentNodeName)), anc.description && /* @__PURE__ */ React24.createElement("p", { className: "mt-1.5 text-[11px] text-slate-400 line-clamp-2 leading-relaxed opacity-80" }, anc.description)),
|
|
10317
|
-
!isPicking && /* @__PURE__ */ React24.createElement(
|
|
10318
|
-
"button",
|
|
10319
|
-
{
|
|
10320
|
-
onClick: (e) => {
|
|
10321
|
-
e.stopPropagation();
|
|
10322
|
-
handleSelectAncestry(anc);
|
|
10543
|
+
`
|
|
10323
10544
|
},
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
},
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
10545
|
+
isPicking ? /* @__PURE__ */ React24.createElement(FiPlus9, { size: 16 }) : /* @__PURE__ */ React24.createElement(FiLayers6, { size: 14 })
|
|
10546
|
+
),
|
|
10547
|
+
/* @__PURE__ */ React24.createElement("div", { className: "flex-1 min-w-0 pb-2" }, /* @__PURE__ */ React24.createElement("div", { className: "flex items-center justify-between gap-2" }, /* @__PURE__ */ React24.createElement("h4", { className: "text-sm font-medium text-slate-200 group-hover:text-white truncate transition-colors" }, anc.name || "Sem Nome"), anc.is_private && /* @__PURE__ */ React24.createElement("span", { className: "text-[9px] px-1 py-0.5 rounded bg-amber-500/10 text-amber-300 border border-amber-500/20" }, "Priv")), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1.5 mt-0.5 text-[11px] text-slate-500 group-hover:text-indigo-200/70 transition-colors" }, /* @__PURE__ */ React24.createElement(FiCornerUpRight4, { size: 10 }), /* @__PURE__ */ React24.createElement("span", { className: "truncate max-w-[120px]" }, parentNodeName)), anc.description && /* @__PURE__ */ React24.createElement("p", { className: "mt-1.5 text-[11px] text-slate-400 line-clamp-2 leading-relaxed opacity-80" }, anc.description)),
|
|
10548
|
+
!isPicking && /* @__PURE__ */ React24.createElement(
|
|
10549
|
+
"button",
|
|
10550
|
+
{
|
|
10551
|
+
onClick: (e) => {
|
|
10552
|
+
e.stopPropagation();
|
|
10553
|
+
handleSelectAncestry(anc);
|
|
10554
|
+
},
|
|
10555
|
+
className: "absolute right-2 bottom-2 opacity-0 group-hover:opacity-100 transition-all duration-300 transform translate-y-2 group-hover:translate-y-0 z-10",
|
|
10556
|
+
title: "Renderizar Ancestralidade"
|
|
10557
|
+
},
|
|
10558
|
+
/* @__PURE__ */ React24.createElement("div", { className: "bg-indigo-500 text-white p-2 rounded-full shadow-lg hover:bg-indigo-400 hover:scale-110 transition-all" }, /* @__PURE__ */ React24.createElement(FiPlay, { size: 14, className: "ml-0.5" }))
|
|
10559
|
+
)
|
|
10560
|
+
);
|
|
10561
|
+
}))
|
|
10562
|
+
), /* @__PURE__ */ React24.createElement("div", { className: "flex flex-col flex-1 bg-slate-950/30" }, /* @__PURE__ */ React24.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-6 space-y-4" }, groups.length === 0 ? /* @__PURE__ */ React24.createElement("div", { className: "flex flex-col items-center justify-center h-full text-slate-500 gap-3 border-2 border-dashed border-white/5 rounded-xl m-4 bg-slate-900/20" }, /* @__PURE__ */ React24.createElement(FiLayers6, { size: 24, className: "opacity-20" }), /* @__PURE__ */ React24.createElement("p", { className: "text-xs text-center px-4" }, canEdit ? /* @__PURE__ */ React24.createElement(React24.Fragment, null, "Nenhum grupo criado.", /* @__PURE__ */ React24.createElement("br", null), 'Use o bot\xE3o "Novo Grupo" acima.') : /* @__PURE__ */ React24.createElement(React24.Fragment, null, "Nenhum grupo dispon\xEDvel para visualiza\xE7\xE3o."))) : groups.map((group, index) => /* @__PURE__ */ React24.createElement(
|
|
10331
10563
|
GroupItem,
|
|
10332
10564
|
{
|
|
10333
10565
|
key: group.id,
|
|
@@ -10406,7 +10638,10 @@ var findNodePath3 = (tree, targetNodeId, currentPath = []) => {
|
|
|
10406
10638
|
}
|
|
10407
10639
|
if (tree.children) {
|
|
10408
10640
|
for (let i = 0; i < tree.children.length; i++) {
|
|
10409
|
-
const res = findNodePath3(tree.children[i], targetNodeId, [
|
|
10641
|
+
const res = findNodePath3(tree.children[i], targetNodeId, [
|
|
10642
|
+
...currentPath,
|
|
10643
|
+
i
|
|
10644
|
+
]);
|
|
10410
10645
|
if (res) return res;
|
|
10411
10646
|
}
|
|
10412
10647
|
}
|
|
@@ -10499,29 +10734,73 @@ function XViewScene({
|
|
|
10499
10734
|
const [userPermissionRole, setUserPermissionRole] = useState25(null);
|
|
10500
10735
|
const [isInitialized, setIsInitialized] = useState25(false);
|
|
10501
10736
|
const [sceneVersion, setSceneVersion] = useState25(0);
|
|
10502
|
-
const [contextMenu, setContextMenu] = useState25({
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
|
|
10737
|
+
const [contextMenu, setContextMenu] = useState25({
|
|
10738
|
+
visible: false,
|
|
10739
|
+
x: 0,
|
|
10740
|
+
y: 0,
|
|
10741
|
+
nodeData: null
|
|
10742
|
+
});
|
|
10743
|
+
const [multiContextMenu, setMultiContextMenu] = useState25({
|
|
10744
|
+
visible: false,
|
|
10745
|
+
x: 0,
|
|
10746
|
+
y: 0,
|
|
10747
|
+
nodeIds: null
|
|
10748
|
+
});
|
|
10749
|
+
const [relationshipMenu, setRelationshipMenu] = useState25({
|
|
10750
|
+
visible: false,
|
|
10751
|
+
x: 0,
|
|
10752
|
+
y: 0,
|
|
10753
|
+
linkObject: null
|
|
10754
|
+
});
|
|
10755
|
+
const [creationMode, setCreationMode] = useState25({
|
|
10756
|
+
isActive: false,
|
|
10757
|
+
sourceNodeData: null
|
|
10758
|
+
});
|
|
10759
|
+
const [versionMode, setVersionMode] = useState25({
|
|
10760
|
+
isActive: false,
|
|
10761
|
+
sourceNodeData: null
|
|
10762
|
+
});
|
|
10507
10763
|
const [questMode, setQuestMode] = useState25({ isActive: false });
|
|
10508
10764
|
const [hasFocusedInitial, setHasFocusedInitial] = useState25(false);
|
|
10509
10765
|
const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = useState25(false);
|
|
10510
|
-
const [ancestryMode, setAncestryMode] = useState25({
|
|
10766
|
+
const [ancestryMode, setAncestryMode] = useState25({
|
|
10767
|
+
isActive: false,
|
|
10768
|
+
tree: null,
|
|
10769
|
+
selectedParentId: null,
|
|
10770
|
+
isEditMode: false,
|
|
10771
|
+
currentAncestryId: null,
|
|
10772
|
+
ancestryName: "",
|
|
10773
|
+
ancestryDescription: "",
|
|
10774
|
+
ancestryDescriptionSections: [],
|
|
10775
|
+
isAddingNodes: false
|
|
10776
|
+
});
|
|
10511
10777
|
const [readingMode, setReadingMode] = useState25({
|
|
10512
10778
|
isActive: false,
|
|
10513
10779
|
ancestry: null,
|
|
10514
10780
|
branchStack: [],
|
|
10515
10781
|
autoAbstraction: false
|
|
10516
10782
|
});
|
|
10517
|
-
const [formPosition, setFormPosition] = useState25({
|
|
10783
|
+
const [formPosition, setFormPosition] = useState25({
|
|
10784
|
+
left: 16,
|
|
10785
|
+
top: 16,
|
|
10786
|
+
opacity: 0
|
|
10787
|
+
});
|
|
10518
10788
|
const [detailsNode, setDetailsNode] = useState25(null);
|
|
10519
10789
|
const [detailsLink, setDetailsLink] = useState25(null);
|
|
10520
10790
|
const [ancestryLinkDetails, setAncestryLinkDetails] = useState25(null);
|
|
10521
|
-
const [imageViewer, setImageViewer] = useState25({
|
|
10522
|
-
|
|
10791
|
+
const [imageViewer, setImageViewer] = useState25({
|
|
10792
|
+
visible: false,
|
|
10793
|
+
images: [],
|
|
10794
|
+
startIndex: 0
|
|
10795
|
+
});
|
|
10796
|
+
const [editingAncestryRel, setEditingAncestryRel] = useState25({
|
|
10797
|
+
visible: false,
|
|
10798
|
+
data: null,
|
|
10799
|
+
path: null
|
|
10800
|
+
});
|
|
10523
10801
|
const [isImportModalOpen, setIsImportModalOpen] = useState25(false);
|
|
10524
10802
|
const [importSuccessMessage, setImportSuccessMessage] = useState25("");
|
|
10803
|
+
const [invalidTargetError, setInvalidTargetError] = useState25(null);
|
|
10525
10804
|
const [highlightedNodeId, setHighlightedNodeId] = useState25(null);
|
|
10526
10805
|
const [isAncestryBoardOpen, setIsAncestryBoardOpen] = useState25(false);
|
|
10527
10806
|
const [ancestryBoardData, setAncestryBoardData] = useState25([]);
|
|
@@ -10559,8 +10838,23 @@ function XViewScene({
|
|
|
10559
10838
|
ghostElements: { node: null, line: null, aura: null },
|
|
10560
10839
|
creation: { isActive: false, sourceNodeData: null },
|
|
10561
10840
|
connection: { isActive: false, sourceNodeData: null, line: null },
|
|
10562
|
-
relink: {
|
|
10563
|
-
|
|
10841
|
+
relink: {
|
|
10842
|
+
isActive: false,
|
|
10843
|
+
end: null,
|
|
10844
|
+
fixedNodeId: null,
|
|
10845
|
+
originalLine: null,
|
|
10846
|
+
line: null
|
|
10847
|
+
},
|
|
10848
|
+
ancestry: {
|
|
10849
|
+
isActive: false,
|
|
10850
|
+
tree: null,
|
|
10851
|
+
selectedParentId: null,
|
|
10852
|
+
isEditMode: false,
|
|
10853
|
+
currentAncestryId: null,
|
|
10854
|
+
ancestryName: "",
|
|
10855
|
+
ancestryDescription: "",
|
|
10856
|
+
isAddingNodes: false
|
|
10857
|
+
},
|
|
10564
10858
|
glowTexture: null,
|
|
10565
10859
|
nodeIdToParentFileMap: null,
|
|
10566
10860
|
maxAncestryRenderIndex: 0,
|
|
@@ -10569,7 +10863,11 @@ function XViewScene({
|
|
|
10569
10863
|
highlightedNodeId: null
|
|
10570
10864
|
});
|
|
10571
10865
|
const maxReadPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
|
|
10572
|
-
const {
|
|
10866
|
+
const {
|
|
10867
|
+
width: readModeWidth,
|
|
10868
|
+
isResizing: isReadModeResizing,
|
|
10869
|
+
handlePointerDown: handleReadModeResize
|
|
10870
|
+
} = useResizablePanel({
|
|
10573
10871
|
initialWidth: 700,
|
|
10574
10872
|
minWidth: 320,
|
|
10575
10873
|
maxWidth: maxReadPanelW
|
|
@@ -10586,7 +10884,9 @@ function XViewScene({
|
|
|
10586
10884
|
for (const parentFileId in allParentData) {
|
|
10587
10885
|
if (allParentData.hasOwnProperty(parentFileId)) {
|
|
10588
10886
|
const parentFile = allParentData[parentFileId];
|
|
10589
|
-
const parentDbInfo = parentDbsArray.find(
|
|
10887
|
+
const parentDbInfo = parentDbsArray.find(
|
|
10888
|
+
(db) => String(db.db_id) === String(parentFileId)
|
|
10889
|
+
);
|
|
10590
10890
|
const ownerId2 = (parentDbInfo == null ? void 0 : parentDbInfo.owner_id) || null;
|
|
10591
10891
|
const datasetName = parentFile.dataset_name || `Dataset #${parentFileId.substring(0, 6)}`;
|
|
10592
10892
|
if (parentFile.nodes && ownerId2) {
|
|
@@ -10622,7 +10922,10 @@ function XViewScene({
|
|
|
10622
10922
|
if (files.length > 0 && get_single_parent_file && save_view_data) {
|
|
10623
10923
|
for (const file of files) {
|
|
10624
10924
|
try {
|
|
10625
|
-
const parentFileData = await get_single_parent_file(
|
|
10925
|
+
const parentFileData = await get_single_parent_file(
|
|
10926
|
+
file.id,
|
|
10927
|
+
session
|
|
10928
|
+
);
|
|
10626
10929
|
if (parentFileData.success && parentFileData.data) {
|
|
10627
10930
|
parentDataRef.current = {
|
|
10628
10931
|
...parentDataRef.current,
|
|
@@ -10633,7 +10936,11 @@ function XViewScene({
|
|
|
10633
10936
|
owner_id: session.user.id
|
|
10634
10937
|
};
|
|
10635
10938
|
updatedParentDbs.push(newParentDbObject);
|
|
10636
|
-
await add_new_parent_file_to_scene_at_firebase_action(
|
|
10939
|
+
await add_new_parent_file_to_scene_at_firebase_action(
|
|
10940
|
+
sceneConfigId,
|
|
10941
|
+
session,
|
|
10942
|
+
file.id
|
|
10943
|
+
);
|
|
10637
10944
|
importedIds.push(file.id);
|
|
10638
10945
|
successCount++;
|
|
10639
10946
|
}
|
|
@@ -10645,7 +10952,10 @@ function XViewScene({
|
|
|
10645
10952
|
if (viewToImport && get_ancestry_file) {
|
|
10646
10953
|
try {
|
|
10647
10954
|
const targetViewOwnerId = ((_b2 = (_a2 = viewToImport.members) == null ? void 0 : _a2.find((m) => m.permission === "owner")) == null ? void 0 : _b2.id) || session.user.id;
|
|
10648
|
-
const ancestryResponse = await get_ancestry_file(
|
|
10955
|
+
const ancestryResponse = await get_ancestry_file(
|
|
10956
|
+
viewToImport.id,
|
|
10957
|
+
targetViewOwnerId
|
|
10958
|
+
);
|
|
10649
10959
|
if (ancestryResponse.success && Array.isArray(ancestryResponse.data)) {
|
|
10650
10960
|
const viewSpecificAncestries = ancestryResponse.data.filter(
|
|
10651
10961
|
(anc) => anc._source_file_id === viewToImport.id && !anc.is_private
|
|
@@ -10656,14 +10966,21 @@ function XViewScene({
|
|
|
10656
10966
|
_imported_from_view_owner_id: targetViewOwnerId,
|
|
10657
10967
|
_source_file_id: viewToImport.id,
|
|
10658
10968
|
_source_owner_id: targetViewOwnerId,
|
|
10659
|
-
_origin_db_ids: (viewToImport.selected_databases || []).map(
|
|
10969
|
+
_origin_db_ids: (viewToImport.selected_databases || []).map(
|
|
10970
|
+
(db) => db.db_id
|
|
10971
|
+
)
|
|
10660
10972
|
}));
|
|
10661
10973
|
const currentAncestries = ancestryDataRef.current || [];
|
|
10662
10974
|
const newAncestries = processedAncestries.filter(
|
|
10663
|
-
(newAnc) => !currentAncestries.some(
|
|
10975
|
+
(newAnc) => !currentAncestries.some(
|
|
10976
|
+
(curr) => String(curr.ancestry_id) === String(newAnc.ancestry_id)
|
|
10977
|
+
)
|
|
10664
10978
|
);
|
|
10665
10979
|
if (newAncestries.length > 0) {
|
|
10666
|
-
ancestryDataRef.current = [
|
|
10980
|
+
ancestryDataRef.current = [
|
|
10981
|
+
...currentAncestries,
|
|
10982
|
+
...newAncestries
|
|
10983
|
+
];
|
|
10667
10984
|
ancestriesWereImported = true;
|
|
10668
10985
|
}
|
|
10669
10986
|
}
|
|
@@ -10683,7 +11000,9 @@ function XViewScene({
|
|
|
10683
11000
|
if (ancestry_save_url) {
|
|
10684
11001
|
await save_view_data(ancestry_save_url, ancestryDataRef.current);
|
|
10685
11002
|
} else {
|
|
10686
|
-
console.error(
|
|
11003
|
+
console.error(
|
|
11004
|
+
"Erro: URL de salvamento de ancestralidade n\xE3o definida."
|
|
11005
|
+
);
|
|
10687
11006
|
}
|
|
10688
11007
|
}
|
|
10689
11008
|
setSceneVersion((v) => v + 1);
|
|
@@ -10691,114 +11010,137 @@ function XViewScene({
|
|
|
10691
11010
|
setImportSuccessMessage("Importa\xE7\xE3o conclu\xEDda com sucesso.");
|
|
10692
11011
|
setTimeout(() => setImportSuccessMessage(""), 5e3);
|
|
10693
11012
|
} else if (viewToImport && !ancestriesWereImported) {
|
|
10694
|
-
console.warn(
|
|
11013
|
+
console.warn(
|
|
11014
|
+
"Importa\xE7\xE3o finalizada, mas nenhum dado novo foi adicionado."
|
|
11015
|
+
);
|
|
10695
11016
|
}
|
|
10696
11017
|
},
|
|
10697
|
-
[
|
|
11018
|
+
[
|
|
11019
|
+
get_single_parent_file,
|
|
11020
|
+
get_ancestry_file,
|
|
11021
|
+
save_view_data,
|
|
11022
|
+
session,
|
|
11023
|
+
sceneSaveUrl,
|
|
11024
|
+
ancestry_save_url,
|
|
11025
|
+
sceneConfigId,
|
|
11026
|
+
add_new_parent_file_to_scene_at_firebase_action
|
|
11027
|
+
]
|
|
10698
11028
|
);
|
|
10699
11029
|
const handleOpenImageViewer = (images, startIndex) => {
|
|
10700
11030
|
setImageViewer({ visible: true, images, startIndex });
|
|
10701
11031
|
};
|
|
10702
|
-
const tweenToTarget = useCallback4(
|
|
10703
|
-
|
|
10704
|
-
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
|
|
10708
|
-
|
|
10709
|
-
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
|
|
10713
|
-
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
11032
|
+
const tweenToTarget = useCallback4(
|
|
11033
|
+
(target, zoomFactor = 1, forcedDirection = null) => {
|
|
11034
|
+
const { camera, controls, tweenGroup } = stateRef.current;
|
|
11035
|
+
if (!camera || !controls || !tweenGroup) return;
|
|
11036
|
+
const targetPos = target instanceof THREE3.Mesh ? target.getWorldPosition(new THREE3.Vector3()) : target;
|
|
11037
|
+
const controlsTween = new Tween2(controls.target).to(targetPos, 1500).easing(Easing2.Cubic.Out);
|
|
11038
|
+
tweenGroup.add(controlsTween);
|
|
11039
|
+
controlsTween.start();
|
|
11040
|
+
let offset;
|
|
11041
|
+
if (forcedDirection) {
|
|
11042
|
+
offset = forcedDirection.clone().normalize().multiplyScalar(x_view_config.CAMERA_ZOOM_DISTANCE / zoomFactor);
|
|
11043
|
+
} else {
|
|
11044
|
+
offset = camera.position.clone().sub(controls.target).normalize().multiplyScalar(x_view_config.CAMERA_ZOOM_DISTANCE / zoomFactor);
|
|
11045
|
+
}
|
|
11046
|
+
const targetCameraPos = targetPos.clone().add(offset);
|
|
11047
|
+
const cameraTween = new Tween2(camera.position).to(targetCameraPos, 1500).easing(Easing2.Cubic.Out);
|
|
11048
|
+
tweenGroup.add(cameraTween);
|
|
11049
|
+
cameraTween.start();
|
|
11050
|
+
},
|
|
11051
|
+
[]
|
|
11052
|
+
);
|
|
10720
11053
|
const isFromUiOverlay = (event) => {
|
|
10721
11054
|
const t = event == null ? void 0 : event.target;
|
|
10722
11055
|
if (!t || typeof t.closest !== "function") return false;
|
|
10723
11056
|
return !!t.closest(".ui-overlay");
|
|
10724
11057
|
};
|
|
10725
|
-
const buildFullAncestryTree = useCallback4(
|
|
10726
|
-
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
10731
|
-
|
|
10732
|
-
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
};
|
|
10736
|
-
}
|
|
10737
|
-
let nodeId = treeItem.node_id;
|
|
10738
|
-
if (!nodeId && treeItem.node) nodeId = treeItem.node.id;
|
|
10739
|
-
const fullNode = nodeMap.get(String(nodeId));
|
|
10740
|
-
const effectiveNode = fullNode || treeItem.node;
|
|
10741
|
-
let processedBranches = [];
|
|
10742
|
-
if (treeItem.parallel_branches && Array.isArray(treeItem.parallel_branches)) {
|
|
10743
|
-
processedBranches = treeItem.parallel_branches.map((branch) => {
|
|
10744
|
-
if (branch.linked_ancestry_id) {
|
|
10745
|
-
const linkedAncestry = ancestryMap.get(String(branch.linked_ancestry_id));
|
|
10746
|
-
if (linkedAncestry && linkedAncestry.tree) {
|
|
10747
|
-
const graftedTree = recursiveBuild(linkedAncestry.tree);
|
|
10748
|
-
return {
|
|
10749
|
-
...branch,
|
|
10750
|
-
name: linkedAncestry.name,
|
|
10751
|
-
description: linkedAncestry.description,
|
|
10752
|
-
description_sections: linkedAncestry.description_sections,
|
|
10753
|
-
tree: graftedTree,
|
|
10754
|
-
isLinked: true
|
|
10755
|
-
};
|
|
10756
|
-
}
|
|
10757
|
-
}
|
|
11058
|
+
const buildFullAncestryTree = useCallback4(
|
|
11059
|
+
(idTree, nodes, ancestries = []) => {
|
|
11060
|
+
if (!idTree) return null;
|
|
11061
|
+
const nodeMap = new Map(nodes.map((n) => [String(n.id), n]));
|
|
11062
|
+
const ancestryMap = new Map(
|
|
11063
|
+
ancestries.map((a) => [String(a.ancestry_id), a])
|
|
11064
|
+
);
|
|
11065
|
+
const recursiveBuild = (treeItem) => {
|
|
11066
|
+
if (!treeItem) return null;
|
|
11067
|
+
if (treeItem.is_section) {
|
|
10758
11068
|
return {
|
|
10759
|
-
...
|
|
10760
|
-
|
|
11069
|
+
...treeItem,
|
|
11070
|
+
children: (treeItem.children || []).map(recursiveBuild).filter(Boolean)
|
|
10761
11071
|
};
|
|
10762
|
-
}
|
|
10763
|
-
|
|
10764
|
-
|
|
10765
|
-
|
|
10766
|
-
|
|
10767
|
-
|
|
10768
|
-
parallel_branches
|
|
10769
|
-
|
|
10770
|
-
|
|
10771
|
-
|
|
10772
|
-
|
|
10773
|
-
|
|
10774
|
-
|
|
10775
|
-
|
|
10776
|
-
|
|
10777
|
-
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
|
|
10781
|
-
|
|
10782
|
-
|
|
10783
|
-
|
|
10784
|
-
|
|
10785
|
-
return {
|
|
10786
|
-
...branch,
|
|
10787
|
-
name: linkedAncestry.name,
|
|
10788
|
-
description: linkedAncestry.description,
|
|
10789
|
-
description_sections: linkedAncestry.description_sections,
|
|
10790
|
-
tree: recursiveBuild(linkedAncestry.tree),
|
|
10791
|
-
isLinked: true
|
|
10792
|
-
};
|
|
11072
|
+
}
|
|
11073
|
+
let nodeId = treeItem.node_id;
|
|
11074
|
+
if (!nodeId && treeItem.node) nodeId = treeItem.node.id;
|
|
11075
|
+
const fullNode = nodeMap.get(String(nodeId));
|
|
11076
|
+
const effectiveNode = fullNode || treeItem.node;
|
|
11077
|
+
let processedBranches = [];
|
|
11078
|
+
if (treeItem.parallel_branches && Array.isArray(treeItem.parallel_branches)) {
|
|
11079
|
+
processedBranches = treeItem.parallel_branches.map((branch) => {
|
|
11080
|
+
if (branch.linked_ancestry_id) {
|
|
11081
|
+
const linkedAncestry = ancestryMap.get(
|
|
11082
|
+
String(branch.linked_ancestry_id)
|
|
11083
|
+
);
|
|
11084
|
+
if (linkedAncestry && linkedAncestry.tree) {
|
|
11085
|
+
const graftedTree = recursiveBuild(linkedAncestry.tree);
|
|
11086
|
+
return {
|
|
11087
|
+
...branch,
|
|
11088
|
+
name: linkedAncestry.name,
|
|
11089
|
+
description: linkedAncestry.description,
|
|
11090
|
+
description_sections: linkedAncestry.description_sections,
|
|
11091
|
+
tree: graftedTree,
|
|
11092
|
+
isLinked: true
|
|
11093
|
+
};
|
|
11094
|
+
}
|
|
10793
11095
|
}
|
|
10794
|
-
|
|
10795
|
-
|
|
10796
|
-
|
|
11096
|
+
return {
|
|
11097
|
+
...branch,
|
|
11098
|
+
tree: recursiveBuild(branch.tree)
|
|
11099
|
+
};
|
|
11100
|
+
});
|
|
11101
|
+
}
|
|
11102
|
+
return {
|
|
11103
|
+
...effectiveNode ? { node: effectiveNode } : { node: { id: nodeId, name: "Unknown" } },
|
|
11104
|
+
relationship: treeItem.relationship || {},
|
|
11105
|
+
children: (treeItem.children || []).map(recursiveBuild).filter(Boolean),
|
|
11106
|
+
parallel_branches: processedBranches
|
|
11107
|
+
};
|
|
10797
11108
|
};
|
|
10798
|
-
|
|
10799
|
-
|
|
10800
|
-
|
|
10801
|
-
|
|
11109
|
+
let rootId = idTree.node_id;
|
|
11110
|
+
if (!rootId && idTree.node) rootId = idTree.node.id;
|
|
11111
|
+
if (rootId) {
|
|
11112
|
+
const rootNode = nodeMap.get(String(rootId));
|
|
11113
|
+
const effectiveRoot = rootNode || idTree.node;
|
|
11114
|
+
if (!effectiveRoot) return null;
|
|
11115
|
+
return {
|
|
11116
|
+
node: effectiveRoot,
|
|
11117
|
+
relationship: idTree.relationship || {},
|
|
11118
|
+
children: (idTree.children || []).map(recursiveBuild).filter(Boolean),
|
|
11119
|
+
parallel_branches: (idTree.parallel_branches || []).map((branch) => {
|
|
11120
|
+
if (branch.linked_ancestry_id) {
|
|
11121
|
+
const linkedAncestry = ancestryMap.get(
|
|
11122
|
+
String(branch.linked_ancestry_id)
|
|
11123
|
+
);
|
|
11124
|
+
if (linkedAncestry && linkedAncestry.tree) {
|
|
11125
|
+
return {
|
|
11126
|
+
...branch,
|
|
11127
|
+
name: linkedAncestry.name,
|
|
11128
|
+
description: linkedAncestry.description,
|
|
11129
|
+
description_sections: linkedAncestry.description_sections,
|
|
11130
|
+
tree: recursiveBuild(linkedAncestry.tree),
|
|
11131
|
+
isLinked: true
|
|
11132
|
+
};
|
|
11133
|
+
}
|
|
11134
|
+
}
|
|
11135
|
+
return { ...branch, tree: recursiveBuild(branch.tree) };
|
|
11136
|
+
})
|
|
11137
|
+
};
|
|
11138
|
+
}
|
|
11139
|
+
return recursiveBuild(idTree);
|
|
11140
|
+
},
|
|
11141
|
+
[]
|
|
11142
|
+
);
|
|
11143
|
+
const handleActivateTimeline = useCallback4(() => {
|
|
10802
11144
|
const { nodeObjects, tweenGroup, timelineIntervalsGroup } = stateRef.current;
|
|
10803
11145
|
if (!nodeObjects || !tweenGroup || !timelineIntervalsGroup) return;
|
|
10804
11146
|
while (timelineIntervalsGroup.children.length > 0) {
|
|
@@ -10878,10 +11220,12 @@ function XViewScene({
|
|
|
10878
11220
|
if (timelineNodes.length === 0) return;
|
|
10879
11221
|
const sortedTimePoints = Array.from(allTimePoints).sort((a, b) => a - b);
|
|
10880
11222
|
const maxTimeIndex = sortedTimePoints.length - 1;
|
|
10881
|
-
const timeToYMap = new Map(
|
|
10882
|
-
time,
|
|
10883
|
-
|
|
10884
|
-
|
|
11223
|
+
const timeToYMap = new Map(
|
|
11224
|
+
sortedTimePoints.map((time, index) => [
|
|
11225
|
+
time,
|
|
11226
|
+
(index - maxTimeIndex) * TIMELINE_LAYER_SPACING_Y
|
|
11227
|
+
])
|
|
11228
|
+
);
|
|
10885
11229
|
timelineNodes.sort((a, b) => {
|
|
10886
11230
|
if (a.isUndated && b.isUndated) return 0;
|
|
10887
11231
|
if (a.isUndated) return 1;
|
|
@@ -10924,7 +11268,12 @@ function XViewScene({
|
|
|
10924
11268
|
if (type === "interval") {
|
|
10925
11269
|
const endY = timeToYMap.get(endDate.getTime());
|
|
10926
11270
|
if (endY > y) {
|
|
10927
|
-
const barGeometry = new THREE3.CylinderGeometry(
|
|
11271
|
+
const barGeometry = new THREE3.CylinderGeometry(
|
|
11272
|
+
TIMELINE_INTERVAL_BAR_RADIUS,
|
|
11273
|
+
TIMELINE_INTERVAL_BAR_RADIUS,
|
|
11274
|
+
1,
|
|
11275
|
+
16
|
|
11276
|
+
);
|
|
10928
11277
|
const barMaterial = new THREE3.MeshStandardMaterial({
|
|
10929
11278
|
color: TIMELINE_GOLD_COLOR,
|
|
10930
11279
|
emissive: 12092939,
|
|
@@ -10953,7 +11302,8 @@ function XViewScene({
|
|
|
10953
11302
|
}, []);
|
|
10954
11303
|
const handleVersionTimeline = useCallback4((sourceMesh, versionMeshes) => {
|
|
10955
11304
|
const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
|
|
10956
|
-
if (!tweenGroup || !timelineIntervalsGroup || versionMeshes.length === 0)
|
|
11305
|
+
if (!tweenGroup || !timelineIntervalsGroup || versionMeshes.length === 0)
|
|
11306
|
+
return;
|
|
10957
11307
|
versionMeshes.forEach((mesh) => {
|
|
10958
11308
|
const oldLabel = mesh.getObjectByName("timelineLabel");
|
|
10959
11309
|
if (oldLabel) {
|
|
@@ -10969,7 +11319,8 @@ function XViewScene({
|
|
|
10969
11319
|
}
|
|
10970
11320
|
if (mesh.userData.timelineEndLabel) {
|
|
10971
11321
|
timelineIntervalsGroup.remove(mesh.userData.timelineEndLabel);
|
|
10972
|
-
if (mesh.userData.timelineEndLabel.material.map)
|
|
11322
|
+
if (mesh.userData.timelineEndLabel.material.map)
|
|
11323
|
+
mesh.userData.timelineEndLabel.material.map.dispose();
|
|
10973
11324
|
mesh.userData.timelineEndLabel.material.dispose();
|
|
10974
11325
|
delete mesh.userData.timelineEndLabel;
|
|
10975
11326
|
}
|
|
@@ -11029,8 +11380,15 @@ function XViewScene({
|
|
|
11029
11380
|
});
|
|
11030
11381
|
if (timelineNodes.length === 0) return;
|
|
11031
11382
|
const sortedTimePoints = Array.from(allTimePoints).sort((a, b) => a - b);
|
|
11032
|
-
const timeToYMap = new Map(
|
|
11033
|
-
|
|
11383
|
+
const timeToYMap = new Map(
|
|
11384
|
+
sortedTimePoints.map((time, index) => [
|
|
11385
|
+
time,
|
|
11386
|
+
index * TIMELINE_LAYER_SPACING_Y
|
|
11387
|
+
])
|
|
11388
|
+
);
|
|
11389
|
+
timelineNodes.sort(
|
|
11390
|
+
(a, b) => a.startDate - b.startDate || a.endDate - b.endDate
|
|
11391
|
+
);
|
|
11034
11392
|
const baseX = sourceMesh.position.x + TIMELINE_NODE_SPACING_X;
|
|
11035
11393
|
const baseY = sourceMesh.position.y;
|
|
11036
11394
|
const maxNodeIndex = timelineNodes.length - 1;
|
|
@@ -11048,7 +11406,12 @@ function XViewScene({
|
|
|
11048
11406
|
if (type === "interval") {
|
|
11049
11407
|
const relativeEndY = timeToYMap.get(endDate.getTime()) - timeToYMap.get(startDate.getTime());
|
|
11050
11408
|
if (relativeEndY > 0) {
|
|
11051
|
-
const barGeometry = new THREE3.CylinderGeometry(
|
|
11409
|
+
const barGeometry = new THREE3.CylinderGeometry(
|
|
11410
|
+
TIMELINE_INTERVAL_BAR_RADIUS,
|
|
11411
|
+
TIMELINE_INTERVAL_BAR_RADIUS,
|
|
11412
|
+
1,
|
|
11413
|
+
16
|
|
11414
|
+
);
|
|
11052
11415
|
const barMaterial = new THREE3.MeshStandardMaterial({
|
|
11053
11416
|
color: TIMELINE_GOLD_COLOR,
|
|
11054
11417
|
emissive: 12092939,
|
|
@@ -11085,15 +11448,31 @@ function XViewScene({
|
|
|
11085
11448
|
try {
|
|
11086
11449
|
const typeStr = (viewParams == null ? void 0 : viewParams.type) || "";
|
|
11087
11450
|
const sceneType = typeStr.toLowerCase().includes("database") ? "database" : "view";
|
|
11088
|
-
const scenePromise = get_scene_view_data(
|
|
11451
|
+
const scenePromise = get_scene_view_data(
|
|
11452
|
+
configPath,
|
|
11453
|
+
ownerId2,
|
|
11454
|
+
typeStr,
|
|
11455
|
+
session,
|
|
11456
|
+
focusNodeId,
|
|
11457
|
+
focusAncestryId
|
|
11458
|
+
);
|
|
11089
11459
|
const boardPromise = get_ancestry_board_action && session ? get_ancestry_board_action(configPath, sceneType, session, ownerId2) : Promise.resolve({ success: false, data: [] });
|
|
11090
|
-
const [sceneResponse, boardResponse] = await Promise.all([
|
|
11460
|
+
const [sceneResponse, boardResponse] = await Promise.all([
|
|
11461
|
+
scenePromise,
|
|
11462
|
+
boardPromise
|
|
11463
|
+
]);
|
|
11091
11464
|
if ((sceneResponse == null ? void 0 : sceneResponse.success) && ((_a2 = sceneResponse.data) == null ? void 0 : _a2.scene) && ((_b2 = sceneResponse.data) == null ? void 0 : _b2.parent)) {
|
|
11092
11465
|
if (focusNodeId) {
|
|
11093
|
-
let targetNode = sceneResponse.data.scene.nodes.find(
|
|
11466
|
+
let targetNode = sceneResponse.data.scene.nodes.find(
|
|
11467
|
+
(n) => String(n.id) === String(focusNodeId)
|
|
11468
|
+
);
|
|
11094
11469
|
if (!targetNode) {
|
|
11095
|
-
const allParentNodes = Object.values(
|
|
11096
|
-
|
|
11470
|
+
const allParentNodes = Object.values(
|
|
11471
|
+
sceneResponse.data.parent
|
|
11472
|
+
).flatMap((f) => f.nodes || []);
|
|
11473
|
+
targetNode = allParentNodes.find(
|
|
11474
|
+
(n) => String(n.id) === String(focusNodeId)
|
|
11475
|
+
);
|
|
11097
11476
|
}
|
|
11098
11477
|
if (targetNode) {
|
|
11099
11478
|
sceneResponse.data.scene.nodes = [targetNode];
|
|
@@ -11106,12 +11485,24 @@ function XViewScene({
|
|
|
11106
11485
|
sceneDataRef.current = sceneResponse.data.scene;
|
|
11107
11486
|
parentDataRef.current = sceneResponse.data.parent;
|
|
11108
11487
|
ancestryDataRef.current = sceneResponse.data.ancestry;
|
|
11109
|
-
console.log(
|
|
11110
|
-
|
|
11111
|
-
|
|
11488
|
+
console.log(
|
|
11489
|
+
"Console de sceneResponse.data.scene:",
|
|
11490
|
+
sceneResponse.data.scene
|
|
11491
|
+
);
|
|
11492
|
+
console.log(
|
|
11493
|
+
"Console de sceneResponse.data.parent:",
|
|
11494
|
+
sceneResponse.data.parent
|
|
11495
|
+
);
|
|
11496
|
+
console.log(
|
|
11497
|
+
"Console de sceneResponse.data.ancestry:",
|
|
11498
|
+
sceneResponse.data.ancestry
|
|
11499
|
+
);
|
|
11112
11500
|
setIsInitialized(true);
|
|
11113
11501
|
} else {
|
|
11114
|
-
console.error(
|
|
11502
|
+
console.error(
|
|
11503
|
+
"Falha ao buscar dados da cena:",
|
|
11504
|
+
(sceneResponse == null ? void 0 : sceneResponse.error) || "Resposta inv\xE1lida."
|
|
11505
|
+
);
|
|
11115
11506
|
}
|
|
11116
11507
|
if (boardResponse == null ? void 0 : boardResponse.success) {
|
|
11117
11508
|
setAncestryBoardData(boardResponse.data);
|
|
@@ -11130,7 +11521,9 @@ function XViewScene({
|
|
|
11130
11521
|
console.error("Usu\xE1rio n\xE3o autenticado. Acesso negado.");
|
|
11131
11522
|
setIsLoading(false);
|
|
11132
11523
|
} else if (!sceneConfigId && status !== "loading") {
|
|
11133
|
-
console.warn(
|
|
11524
|
+
console.warn(
|
|
11525
|
+
"Nenhum par\xE2metro de cena encontrado na URL ou falha na decripta\xE7\xE3o."
|
|
11526
|
+
);
|
|
11134
11527
|
setIsLoading(false);
|
|
11135
11528
|
}
|
|
11136
11529
|
}, [
|
|
@@ -11151,33 +11544,46 @@ function XViewScene({
|
|
|
11151
11544
|
const objs = stateRef.current.nodeObjects || {};
|
|
11152
11545
|
return !!objs[key];
|
|
11153
11546
|
}, []);
|
|
11154
|
-
const addOrUpdateNodeMesh = useCallback4(
|
|
11155
|
-
|
|
11156
|
-
|
|
11157
|
-
|
|
11158
|
-
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
tweenGroup
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
|
|
11170
|
-
|
|
11547
|
+
const addOrUpdateNodeMesh = useCallback4(
|
|
11548
|
+
(nodeData, position, suppressVersionUpdate = false) => {
|
|
11549
|
+
const {
|
|
11550
|
+
graphGroup,
|
|
11551
|
+
nodeObjects,
|
|
11552
|
+
clickableNodes,
|
|
11553
|
+
glowTexture,
|
|
11554
|
+
tweenGroup
|
|
11555
|
+
} = stateRef.current;
|
|
11556
|
+
const nodeId = String(nodeData.id);
|
|
11557
|
+
if (nodeObjects[nodeId]) {
|
|
11558
|
+
const existingMesh = nodeObjects[nodeId];
|
|
11559
|
+
if (position) {
|
|
11560
|
+
const updateTween = new Tween2(existingMesh.position).to(position, 800).easing(Easing2.Cubic.Out);
|
|
11561
|
+
tweenGroup.add(updateTween);
|
|
11562
|
+
updateTween.start();
|
|
11563
|
+
}
|
|
11564
|
+
return existingMesh;
|
|
11565
|
+
}
|
|
11566
|
+
const mesh = createNodeMesh(
|
|
11567
|
+
nodeData,
|
|
11568
|
+
position || new THREE3.Vector3(),
|
|
11569
|
+
glowTexture
|
|
11570
|
+
);
|
|
11571
|
+
graphGroup.add(mesh);
|
|
11572
|
+
if (mesh.userData.labelObject) {
|
|
11573
|
+
if (mesh.userData.labelOffset) {
|
|
11574
|
+
mesh.userData.labelObject.position.copy(mesh.position).add(mesh.userData.labelOffset);
|
|
11575
|
+
}
|
|
11576
|
+
graphGroup.add(mesh.userData.labelObject);
|
|
11171
11577
|
}
|
|
11172
|
-
|
|
11173
|
-
|
|
11174
|
-
|
|
11175
|
-
|
|
11176
|
-
|
|
11177
|
-
|
|
11178
|
-
}
|
|
11179
|
-
|
|
11180
|
-
|
|
11578
|
+
nodeObjects[nodeId] = mesh;
|
|
11579
|
+
clickableNodes.push(mesh);
|
|
11580
|
+
if (!suppressVersionUpdate) {
|
|
11581
|
+
setSceneVersion((v) => v + 1);
|
|
11582
|
+
}
|
|
11583
|
+
return mesh;
|
|
11584
|
+
},
|
|
11585
|
+
[]
|
|
11586
|
+
);
|
|
11181
11587
|
useEffect22(() => {
|
|
11182
11588
|
if (!isInitialized || !sceneDataRef.current) return;
|
|
11183
11589
|
const currentMount = mountRef.current;
|
|
@@ -11192,7 +11598,12 @@ function XViewScene({
|
|
|
11192
11598
|
const scene = new THREE3.Scene();
|
|
11193
11599
|
scene.background = new THREE3.Color(0);
|
|
11194
11600
|
stateRef.current.scene = scene;
|
|
11195
|
-
const camera = new THREE3.PerspectiveCamera(
|
|
11601
|
+
const camera = new THREE3.PerspectiveCamera(
|
|
11602
|
+
75,
|
|
11603
|
+
currentMount.clientWidth / currentMount.clientHeight,
|
|
11604
|
+
0.1,
|
|
11605
|
+
2e3
|
|
11606
|
+
);
|
|
11196
11607
|
camera.position.set(0, 60, 120);
|
|
11197
11608
|
stateRef.current.camera = camera;
|
|
11198
11609
|
const renderer = new THREE3.WebGLRenderer({ antialias: true });
|
|
@@ -11213,7 +11624,12 @@ function XViewScene({
|
|
|
11213
11624
|
directionalLight.position.set(50, 50, 50);
|
|
11214
11625
|
scene.add(directionalLight);
|
|
11215
11626
|
const renderScene = new RenderPass(scene, camera);
|
|
11216
|
-
const bloomPass = new UnrealBloomPass(
|
|
11627
|
+
const bloomPass = new UnrealBloomPass(
|
|
11628
|
+
new THREE3.Vector2(currentMount.clientWidth, currentMount.clientHeight),
|
|
11629
|
+
x_view_config.BLOOM_EFFECT.strength,
|
|
11630
|
+
x_view_config.BLOOM_EFFECT.radius,
|
|
11631
|
+
x_view_config.BLOOM_EFFECT.threshold
|
|
11632
|
+
);
|
|
11217
11633
|
const composer = new EffectComposer(renderer);
|
|
11218
11634
|
composer.addPass(renderScene);
|
|
11219
11635
|
composer.addPass(bloomPass);
|
|
@@ -11256,8 +11672,16 @@ function XViewScene({
|
|
|
11256
11672
|
const sourceNode = nodeObjects[String(linksArray[0].source)];
|
|
11257
11673
|
const targetNode = nodeObjects[String(linksArray[0].target)];
|
|
11258
11674
|
if (sourceNode && targetNode) {
|
|
11259
|
-
const resolution = new THREE3.Vector2(
|
|
11260
|
-
|
|
11675
|
+
const resolution = new THREE3.Vector2(
|
|
11676
|
+
currentMount.clientWidth,
|
|
11677
|
+
currentMount.clientHeight
|
|
11678
|
+
);
|
|
11679
|
+
const newLinks = createMultipleLinkLines(
|
|
11680
|
+
linksArray,
|
|
11681
|
+
sourceNode,
|
|
11682
|
+
targetNode,
|
|
11683
|
+
resolution
|
|
11684
|
+
);
|
|
11261
11685
|
newLinks.forEach((line, idx) => {
|
|
11262
11686
|
const meta = linksArray[idx];
|
|
11263
11687
|
if (meta) {
|
|
@@ -11298,7 +11722,10 @@ function XViewScene({
|
|
|
11298
11722
|
function tryPickNode() {
|
|
11299
11723
|
raycaster.setFromCamera(mouse, camera);
|
|
11300
11724
|
raycaster.layers.enable(GHOST_BLOOM_LAYER);
|
|
11301
|
-
const intersects = raycaster.intersectObjects(
|
|
11725
|
+
const intersects = raycaster.intersectObjects(
|
|
11726
|
+
stateRef.current.clickableNodes,
|
|
11727
|
+
false
|
|
11728
|
+
);
|
|
11302
11729
|
return intersects.length > 0 ? intersects[0].object : null;
|
|
11303
11730
|
}
|
|
11304
11731
|
function findClosestLinkToRay() {
|
|
@@ -11310,7 +11737,10 @@ function XViewScene({
|
|
|
11310
11737
|
x: (mouse.x * 0.5 + 0.5) * clientWidth,
|
|
11311
11738
|
y: (-mouse.y * 0.5 + 0.5) * clientHeight
|
|
11312
11739
|
};
|
|
11313
|
-
const allVisibleLinks = [
|
|
11740
|
+
const allVisibleLinks = [
|
|
11741
|
+
...stateRef.current.allLinks,
|
|
11742
|
+
...stateRef.current.ancestryLinks
|
|
11743
|
+
];
|
|
11314
11744
|
const THRESH = x_view_config.LINE_HOVER_THRESHOLD_PX + 4;
|
|
11315
11745
|
const tmpP1 = new THREE3.Vector3();
|
|
11316
11746
|
const tmpP2 = new THREE3.Vector3();
|
|
@@ -11325,19 +11755,35 @@ function XViewScene({
|
|
|
11325
11755
|
const up = new THREE3.Vector3(0, 1, 0);
|
|
11326
11756
|
const normal = new THREE3.Vector3().crossVectors(dir, up).normalize();
|
|
11327
11757
|
const controlPoint = mid.add(normal.multiplyScalar(curveOffset || 0));
|
|
11328
|
-
const curve = new THREE3.QuadraticBezierCurve3(
|
|
11758
|
+
const curve = new THREE3.QuadraticBezierCurve3(
|
|
11759
|
+
start,
|
|
11760
|
+
controlPoint,
|
|
11761
|
+
end
|
|
11762
|
+
);
|
|
11329
11763
|
const points = curve.getPoints(x_view_config.CURVE_SEGMENTS || 32);
|
|
11330
11764
|
for (let i = 0; i < points.length - 1; i++) {
|
|
11331
11765
|
tmpP1.copy(points[i]).project(camera);
|
|
11332
11766
|
tmpP2.copy(points[i + 1]).project(camera);
|
|
11333
|
-
const p1_px = {
|
|
11334
|
-
|
|
11767
|
+
const p1_px = {
|
|
11768
|
+
x: (tmpP1.x * 0.5 + 0.5) * clientWidth,
|
|
11769
|
+
y: (-tmpP1.y * 0.5 + 0.5) * clientHeight
|
|
11770
|
+
};
|
|
11771
|
+
const p2_px = {
|
|
11772
|
+
x: (tmpP2.x * 0.5 + 0.5) * clientWidth,
|
|
11773
|
+
y: (-tmpP2.y * 0.5 + 0.5) * clientHeight
|
|
11774
|
+
};
|
|
11335
11775
|
const L2 = (p2_px.x - p1_px.x) ** 2 + (p2_px.y - p1_px.y) ** 2;
|
|
11336
11776
|
if (L2 === 0) continue;
|
|
11337
11777
|
let t = ((mousePixels.x - p1_px.x) * (p2_px.x - p1_px.x) + (mousePixels.y - p1_px.y) * (p2_px.y - p1_px.y)) / L2;
|
|
11338
11778
|
t = Math.max(0, Math.min(1, t));
|
|
11339
|
-
const closestPoint = {
|
|
11340
|
-
|
|
11779
|
+
const closestPoint = {
|
|
11780
|
+
x: p1_px.x + t * (p2_px.x - p1_px.x),
|
|
11781
|
+
y: p1_px.y + t * (p2_px.y - p1_px.y)
|
|
11782
|
+
};
|
|
11783
|
+
const dist = Math.hypot(
|
|
11784
|
+
mousePixels.x - closestPoint.x,
|
|
11785
|
+
mousePixels.y - closestPoint.y
|
|
11786
|
+
);
|
|
11341
11787
|
if (dist < THRESH && dist < minDistance) {
|
|
11342
11788
|
minDistance = dist;
|
|
11343
11789
|
closestLink = link;
|
|
@@ -11346,14 +11792,26 @@ function XViewScene({
|
|
|
11346
11792
|
} else {
|
|
11347
11793
|
const p1 = new THREE3.Vector3().copy(sourceNode.position).project(camera);
|
|
11348
11794
|
const p2 = new THREE3.Vector3().copy(targetNode.position).project(camera);
|
|
11349
|
-
const p1_px = {
|
|
11350
|
-
|
|
11795
|
+
const p1_px = {
|
|
11796
|
+
x: (p1.x * 0.5 + 0.5) * clientWidth,
|
|
11797
|
+
y: (-p1.y * 0.5 + 0.5) * clientHeight
|
|
11798
|
+
};
|
|
11799
|
+
const p2_px = {
|
|
11800
|
+
x: (p2.x * 0.5 + 0.5) * clientWidth,
|
|
11801
|
+
y: (-p2.y * 0.5 + 0.5) * clientHeight
|
|
11802
|
+
};
|
|
11351
11803
|
const L2 = (p2_px.x - p1_px.x) ** 2 + (p2_px.y - p1_px.y) ** 2;
|
|
11352
11804
|
if (L2 === 0) return;
|
|
11353
11805
|
let t = ((mousePixels.x - p1_px.x) * (p2_px.x - p1_px.x) + (mousePixels.y - p1_px.y) * (p2_px.y - p1_px.y)) / L2;
|
|
11354
11806
|
t = Math.max(0, Math.min(1, t));
|
|
11355
|
-
const closestPoint = {
|
|
11356
|
-
|
|
11807
|
+
const closestPoint = {
|
|
11808
|
+
x: p1_px.x + t * (p2_px.x - p1_px.x),
|
|
11809
|
+
y: p1_px.y + t * (p2_px.y - p1_px.y)
|
|
11810
|
+
};
|
|
11811
|
+
const dist = Math.hypot(
|
|
11812
|
+
mousePixels.x - closestPoint.x,
|
|
11813
|
+
mousePixels.y - closestPoint.y
|
|
11814
|
+
);
|
|
11357
11815
|
if (dist < THRESH && dist < minDistance) {
|
|
11358
11816
|
minDistance = dist;
|
|
11359
11817
|
closestLink = link;
|
|
@@ -11388,7 +11846,11 @@ function XViewScene({
|
|
|
11388
11846
|
if (picked && !stateRef.current.ancestry.isActive) {
|
|
11389
11847
|
stateRef.current.controls.enabled = false;
|
|
11390
11848
|
}
|
|
11391
|
-
stateRef.current.pointerDown = {
|
|
11849
|
+
stateRef.current.pointerDown = {
|
|
11850
|
+
isDown: true,
|
|
11851
|
+
x: event.clientX,
|
|
11852
|
+
y: event.clientY
|
|
11853
|
+
};
|
|
11392
11854
|
stateRef.current.dragCandidate = picked;
|
|
11393
11855
|
}
|
|
11394
11856
|
function onPointerMove(event) {
|
|
@@ -11399,7 +11861,10 @@ function XViewScene({
|
|
|
11399
11861
|
const raycaster2 = new THREE3.Raycaster();
|
|
11400
11862
|
raycaster2.setFromCamera(mouse, camera);
|
|
11401
11863
|
camera.getWorldDirection(plane.normal);
|
|
11402
|
-
plane.setFromNormalAndCoplanarPoint(
|
|
11864
|
+
plane.setFromNormalAndCoplanarPoint(
|
|
11865
|
+
plane.normal,
|
|
11866
|
+
stateRef.current.draggedNode.position
|
|
11867
|
+
);
|
|
11403
11868
|
if (raycaster2.ray.intersectPlane(plane, intersectionPoint)) {
|
|
11404
11869
|
const draggedNode = stateRef.current.draggedNode;
|
|
11405
11870
|
draggedNode.position.copy(intersectionPoint);
|
|
@@ -11408,7 +11873,8 @@ function XViewScene({
|
|
|
11408
11873
|
}
|
|
11409
11874
|
if (stateRef.current.connection.isActive || stateRef.current.relink.isActive || stateRef.current.ancestry.isActive) {
|
|
11410
11875
|
const newHoveredNode2 = tryPickNode();
|
|
11411
|
-
if (stateRef.current.hoveredNode !== newHoveredNode2)
|
|
11876
|
+
if (stateRef.current.hoveredNode !== newHoveredNode2)
|
|
11877
|
+
stateRef.current.hoveredNode = newHoveredNode2;
|
|
11412
11878
|
if (currentMount) {
|
|
11413
11879
|
let fixedId = null;
|
|
11414
11880
|
if (stateRef.current.connection.isActive) {
|
|
@@ -11439,14 +11905,20 @@ function XViewScene({
|
|
|
11439
11905
|
stateRef.current.controls.enabled = false;
|
|
11440
11906
|
if (currentMount) currentMount.style.cursor = "grabbing";
|
|
11441
11907
|
camera.getWorldDirection(plane.normal);
|
|
11442
|
-
plane.setFromNormalAndCoplanarPoint(
|
|
11908
|
+
plane.setFromNormalAndCoplanarPoint(
|
|
11909
|
+
plane.normal,
|
|
11910
|
+
stateRef.current.draggedNode.position
|
|
11911
|
+
);
|
|
11443
11912
|
}
|
|
11444
11913
|
}
|
|
11445
11914
|
const newHoveredNode = tryPickNode();
|
|
11446
11915
|
const newHoveredLink = !newHoveredNode ? findClosestLinkToRay() : null;
|
|
11447
|
-
if (stateRef.current.hoveredNode !== newHoveredNode)
|
|
11448
|
-
|
|
11449
|
-
if (
|
|
11916
|
+
if (stateRef.current.hoveredNode !== newHoveredNode)
|
|
11917
|
+
stateRef.current.hoveredNode = newHoveredNode;
|
|
11918
|
+
if (stateRef.current.hoveredLink !== newHoveredLink)
|
|
11919
|
+
stateRef.current.hoveredLink = newHoveredLink;
|
|
11920
|
+
if (currentMount)
|
|
11921
|
+
currentMount.style.cursor = newHoveredNode || newHoveredLink ? "pointer" : "grab";
|
|
11450
11922
|
}
|
|
11451
11923
|
const isNodeInTree = (tree, nodeId) => {
|
|
11452
11924
|
if (!tree) return false;
|
|
@@ -11461,7 +11933,10 @@ function XViewScene({
|
|
|
11461
11933
|
const context = actionHandlerContext;
|
|
11462
11934
|
if (connection.isActive) {
|
|
11463
11935
|
if (hoveredNode && String(hoveredNode.userData.id) !== String(connection.sourceNodeData.id)) {
|
|
11464
|
-
await userActionHandlers.handleCompleteConnection(
|
|
11936
|
+
await userActionHandlers.handleCompleteConnection(
|
|
11937
|
+
context,
|
|
11938
|
+
hoveredNode.userData
|
|
11939
|
+
);
|
|
11465
11940
|
} else {
|
|
11466
11941
|
userActionHandlers.handleCancelConnection(context);
|
|
11467
11942
|
}
|
|
@@ -11469,7 +11944,10 @@ function XViewScene({
|
|
|
11469
11944
|
}
|
|
11470
11945
|
if (relink.isActive) {
|
|
11471
11946
|
if (hoveredNode && String(hoveredNode.userData.id) !== String(relink.fixedNodeId)) {
|
|
11472
|
-
await userActionHandlers.handleCompleteRelink(
|
|
11947
|
+
await userActionHandlers.handleCompleteRelink(
|
|
11948
|
+
context,
|
|
11949
|
+
hoveredNode.userData
|
|
11950
|
+
);
|
|
11473
11951
|
} else {
|
|
11474
11952
|
userActionHandlers.handleCancelRelink(context);
|
|
11475
11953
|
}
|
|
@@ -11487,7 +11965,9 @@ function XViewScene({
|
|
|
11487
11965
|
const clickedNodeId = String(clickedNode.userData.id);
|
|
11488
11966
|
const parentId = String(currentSelectedParent);
|
|
11489
11967
|
if (clickedNodeId === parentId) {
|
|
11490
|
-
alert(
|
|
11968
|
+
alert(
|
|
11969
|
+
"Erro: N\xE3o \xE9 poss\xEDvel adicionar um Node como filho dele mesmo."
|
|
11970
|
+
);
|
|
11491
11971
|
return;
|
|
11492
11972
|
}
|
|
11493
11973
|
const parentInfo = stateRef.current.nodeIdToParentFileMap.get(clickedNodeId);
|
|
@@ -11497,16 +11977,33 @@ function XViewScene({
|
|
|
11497
11977
|
const addChildToNode = (current, targetParentId, childNode) => {
|
|
11498
11978
|
const currentId = current.is_section ? current.id || current.section_id : String(current.node.id);
|
|
11499
11979
|
if (String(currentId) === String(targetParentId)) {
|
|
11500
|
-
const alreadyExists = current.children.some(
|
|
11980
|
+
const alreadyExists = current.children.some(
|
|
11981
|
+
(child) => !child.is_section && String(child.node.id) === String(childNode.id)
|
|
11982
|
+
);
|
|
11501
11983
|
if (alreadyExists) return current;
|
|
11502
|
-
return {
|
|
11984
|
+
return {
|
|
11985
|
+
...current,
|
|
11986
|
+
children: [
|
|
11987
|
+
...current.children,
|
|
11988
|
+
{ node: childNode, children: [], relationship: {} }
|
|
11989
|
+
]
|
|
11990
|
+
};
|
|
11503
11991
|
}
|
|
11504
|
-
return {
|
|
11992
|
+
return {
|
|
11993
|
+
...current,
|
|
11994
|
+
children: current.children.map(
|
|
11995
|
+
(c) => addChildToNode(c, targetParentId, childNode)
|
|
11996
|
+
)
|
|
11997
|
+
};
|
|
11505
11998
|
};
|
|
11506
11999
|
setAncestryMode((prev) => {
|
|
11507
12000
|
const treeKey = isAbstraction ? "abstraction_tree" : "tree";
|
|
11508
12001
|
if (!prev[treeKey]) return prev;
|
|
11509
|
-
const newTree = addChildToNode(
|
|
12002
|
+
const newTree = addChildToNode(
|
|
12003
|
+
prev[treeKey],
|
|
12004
|
+
parentId,
|
|
12005
|
+
fullNodeData
|
|
12006
|
+
);
|
|
11510
12007
|
return { ...prev, [treeKey]: newTree };
|
|
11511
12008
|
});
|
|
11512
12009
|
}
|
|
@@ -11520,7 +12017,8 @@ function XViewScene({
|
|
|
11520
12017
|
stateRef.current.dragCandidate = null;
|
|
11521
12018
|
stateRef.current.pointerDown.isDown = false;
|
|
11522
12019
|
stateRef.current.controls.enabled = true;
|
|
11523
|
-
if (currentMount)
|
|
12020
|
+
if (currentMount)
|
|
12021
|
+
currentMount.style.cursor = stateRef.current.hoveredNode || stateRef.current.hoveredLink ? "pointer" : "grab";
|
|
11524
12022
|
return;
|
|
11525
12023
|
}
|
|
11526
12024
|
const dragDistance = Math.hypot(
|
|
@@ -11568,16 +12066,21 @@ function XViewScene({
|
|
|
11568
12066
|
}
|
|
11569
12067
|
function handleDoubleClick(event) {
|
|
11570
12068
|
if (stateRef.current.camera) stateRef.current.camera.layers.enableAll();
|
|
11571
|
-
if (isFromUiOverlay(event) || stateRef.current.isDragging || stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive)
|
|
11572
|
-
|
|
12069
|
+
if (isFromUiOverlay(event) || stateRef.current.isDragging || stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive)
|
|
12070
|
+
return;
|
|
12071
|
+
if (stateRef.current.hoveredNode)
|
|
12072
|
+
tweenToTarget(stateRef.current.hoveredNode);
|
|
11573
12073
|
}
|
|
11574
12074
|
function handleContextMenu(event) {
|
|
11575
12075
|
if (stateRef.current.camera) stateRef.current.camera.layers.enableAll();
|
|
11576
12076
|
if (isFromUiOverlay(event)) return;
|
|
11577
12077
|
event.preventDefault();
|
|
11578
|
-
if (stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive)
|
|
12078
|
+
if (stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive)
|
|
12079
|
+
return;
|
|
11579
12080
|
setMouseFromEvent(event);
|
|
11580
|
-
setContextMenu(
|
|
12081
|
+
setContextMenu(
|
|
12082
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
12083
|
+
);
|
|
11581
12084
|
setMultiContextMenu((prev) => ({ ...prev, visible: false }));
|
|
11582
12085
|
setRelationshipMenu((prev) => ({ ...prev, visible: false }));
|
|
11583
12086
|
const pickedNode = tryPickNode();
|
|
@@ -11608,7 +12111,12 @@ function XViewScene({
|
|
|
11608
12111
|
return;
|
|
11609
12112
|
}
|
|
11610
12113
|
stateRef.current.selectedNodes.clear();
|
|
11611
|
-
setRelationshipMenu({
|
|
12114
|
+
setRelationshipMenu({
|
|
12115
|
+
visible: true,
|
|
12116
|
+
x: event.clientX,
|
|
12117
|
+
y: event.clientY,
|
|
12118
|
+
linkObject: pickedLink
|
|
12119
|
+
});
|
|
11612
12120
|
return;
|
|
11613
12121
|
}
|
|
11614
12122
|
stateRef.current.selectedNodes.clear();
|
|
@@ -11640,7 +12148,10 @@ function XViewScene({
|
|
|
11640
12148
|
}
|
|
11641
12149
|
});
|
|
11642
12150
|
}
|
|
11643
|
-
const allRenderedLinks = [
|
|
12151
|
+
const allRenderedLinks = [
|
|
12152
|
+
...stateRef.current.allLinks,
|
|
12153
|
+
...stateRef.current.ancestryLinks
|
|
12154
|
+
];
|
|
11644
12155
|
allRenderedLinks.forEach((line) => {
|
|
11645
12156
|
const { sourceNode, targetNode, isCurved, curveOffset } = line.userData;
|
|
11646
12157
|
if (sourceNode && targetNode) {
|
|
@@ -11652,13 +12163,20 @@ function XViewScene({
|
|
|
11652
12163
|
const up = new THREE3.Vector3(0, 1, 0);
|
|
11653
12164
|
const normal = new THREE3.Vector3().crossVectors(dir, up).normalize();
|
|
11654
12165
|
const controlPoint = mid.add(normal.multiplyScalar(curveOffset));
|
|
11655
|
-
const curve = new THREE3.QuadraticBezierCurve3(
|
|
12166
|
+
const curve = new THREE3.QuadraticBezierCurve3(
|
|
12167
|
+
start,
|
|
12168
|
+
controlPoint,
|
|
12169
|
+
end
|
|
12170
|
+
);
|
|
11656
12171
|
const points = curve.getPoints(x_view_config.CURVE_SEGMENTS);
|
|
11657
12172
|
const positions = [];
|
|
11658
12173
|
points.forEach((p) => positions.push(p.x, p.y, p.z));
|
|
11659
12174
|
line.geometry.setPositions(positions);
|
|
11660
12175
|
} else {
|
|
11661
|
-
line.geometry.setPositions([
|
|
12176
|
+
line.geometry.setPositions([
|
|
12177
|
+
...sourceNode.position.toArray(),
|
|
12178
|
+
...targetNode.position.toArray()
|
|
12179
|
+
]);
|
|
11662
12180
|
}
|
|
11663
12181
|
}
|
|
11664
12182
|
});
|
|
@@ -11671,7 +12189,11 @@ function XViewScene({
|
|
|
11671
12189
|
const startPos = node.position;
|
|
11672
12190
|
const distance = startPos.distanceTo(endPos);
|
|
11673
12191
|
bar.scale.y = distance;
|
|
11674
|
-
const midpoint = new THREE3.Vector3().lerpVectors(
|
|
12192
|
+
const midpoint = new THREE3.Vector3().lerpVectors(
|
|
12193
|
+
startPos,
|
|
12194
|
+
endPos,
|
|
12195
|
+
0.5
|
|
12196
|
+
);
|
|
11675
12197
|
bar.position.copy(midpoint);
|
|
11676
12198
|
const direction = new THREE3.Vector3().subVectors(endPos, startPos).normalize();
|
|
11677
12199
|
const upVector = new THREE3.Vector3(0, 1, 0);
|
|
@@ -11682,7 +12204,11 @@ function XViewScene({
|
|
|
11682
12204
|
const { ghostElements, creation, connection, relink } = stateRef.current;
|
|
11683
12205
|
if (creation.isActive && ghostElements.node && ghostElements.line) {
|
|
11684
12206
|
const srcMesh = stateRef.current.nodeObjects[String(creation.sourceNodeData.id)];
|
|
11685
|
-
if (srcMesh)
|
|
12207
|
+
if (srcMesh)
|
|
12208
|
+
ghostElements.line.geometry.setPositions([
|
|
12209
|
+
...srcMesh.position.toArray(),
|
|
12210
|
+
...ghostElements.node.position.toArray()
|
|
12211
|
+
]);
|
|
11686
12212
|
}
|
|
11687
12213
|
if (connection.isActive && connection.line) {
|
|
11688
12214
|
const srcMesh = stateRef.current.nodeObjects[String(connection.sourceNodeData.id)];
|
|
@@ -11691,7 +12217,11 @@ function XViewScene({
|
|
|
11691
12217
|
raycaster2.setFromCamera(mouse, camera);
|
|
11692
12218
|
camera.getWorldDirection(plane.normal);
|
|
11693
12219
|
plane.setFromNormalAndCoplanarPoint(plane.normal, srcMesh.position);
|
|
11694
|
-
if (raycaster2.ray.intersectPlane(plane, intersectionPoint))
|
|
12220
|
+
if (raycaster2.ray.intersectPlane(plane, intersectionPoint))
|
|
12221
|
+
connection.line.geometry.setPositions([
|
|
12222
|
+
...srcMesh.position.toArray(),
|
|
12223
|
+
...intersectionPoint.toArray()
|
|
12224
|
+
]);
|
|
11695
12225
|
}
|
|
11696
12226
|
}
|
|
11697
12227
|
if (relink.isActive && relink.line) {
|
|
@@ -11701,7 +12231,11 @@ function XViewScene({
|
|
|
11701
12231
|
raycaster2.setFromCamera(mouse, camera);
|
|
11702
12232
|
camera.getWorldDirection(plane.normal);
|
|
11703
12233
|
plane.setFromNormalAndCoplanarPoint(plane.normal, fixedMesh.position);
|
|
11704
|
-
if (raycaster2.ray.intersectPlane(plane, intersectionPoint))
|
|
12234
|
+
if (raycaster2.ray.intersectPlane(plane, intersectionPoint))
|
|
12235
|
+
relink.line.geometry.setPositions([
|
|
12236
|
+
...fixedMesh.position.toArray(),
|
|
12237
|
+
...intersectionPoint.toArray()
|
|
12238
|
+
]);
|
|
11705
12239
|
}
|
|
11706
12240
|
}
|
|
11707
12241
|
Object.values(stateRef.current.nodeObjects).forEach((node) => {
|
|
@@ -11745,11 +12279,18 @@ function XViewScene({
|
|
|
11745
12279
|
renderer.setSize(clientWidth, clientHeight);
|
|
11746
12280
|
composer.setSize(clientWidth, clientHeight);
|
|
11747
12281
|
const resVec = new THREE3.Vector2(clientWidth, clientHeight);
|
|
11748
|
-
stateRef.current.allLinks.forEach(
|
|
11749
|
-
|
|
11750
|
-
|
|
11751
|
-
|
|
11752
|
-
|
|
12282
|
+
stateRef.current.allLinks.forEach(
|
|
12283
|
+
(line) => line.material.resolution.copy(resVec)
|
|
12284
|
+
);
|
|
12285
|
+
stateRef.current.ancestryLinks.forEach(
|
|
12286
|
+
(line) => line.material.resolution.copy(resVec)
|
|
12287
|
+
);
|
|
12288
|
+
if (stateRef.current.ghostElements.line)
|
|
12289
|
+
stateRef.current.ghostElements.line.material.resolution.copy(resVec);
|
|
12290
|
+
if (stateRef.current.connection.line)
|
|
12291
|
+
stateRef.current.connection.line.material.resolution.copy(resVec);
|
|
12292
|
+
if (stateRef.current.relink.line)
|
|
12293
|
+
stateRef.current.relink.line.material.resolution.copy(resVec);
|
|
11753
12294
|
}
|
|
11754
12295
|
window.addEventListener("resize", handleResize);
|
|
11755
12296
|
return () => {
|
|
@@ -11769,14 +12310,16 @@ function XViewScene({
|
|
|
11769
12310
|
ancestryGroup.traverse((obj) => {
|
|
11770
12311
|
if (obj.geometry) obj.geometry.dispose();
|
|
11771
12312
|
if (obj.material) {
|
|
11772
|
-
if (Array.isArray(obj.material))
|
|
12313
|
+
if (Array.isArray(obj.material))
|
|
12314
|
+
obj.material.forEach((m) => m.dispose());
|
|
11773
12315
|
else obj.material.dispose();
|
|
11774
12316
|
}
|
|
11775
12317
|
});
|
|
11776
12318
|
graphGroup.traverse((obj) => {
|
|
11777
12319
|
if (obj.geometry) obj.geometry.dispose();
|
|
11778
12320
|
if (obj.material) {
|
|
11779
|
-
if (Array.isArray(obj.material))
|
|
12321
|
+
if (Array.isArray(obj.material))
|
|
12322
|
+
obj.material.forEach((m) => m.dispose());
|
|
11780
12323
|
else obj.material.dispose();
|
|
11781
12324
|
}
|
|
11782
12325
|
});
|
|
@@ -11787,9 +12330,22 @@ function XViewScene({
|
|
|
11787
12330
|
currentMount.removeChild(renderer.domElement);
|
|
11788
12331
|
}
|
|
11789
12332
|
};
|
|
11790
|
-
}, [
|
|
12333
|
+
}, [
|
|
12334
|
+
isInitialized,
|
|
12335
|
+
tweenToTarget,
|
|
12336
|
+
dbSaveUrl,
|
|
12337
|
+
isNodeInView,
|
|
12338
|
+
addOrUpdateNodeMesh,
|
|
12339
|
+
handleActivateTimeline,
|
|
12340
|
+
get_scene_view_data,
|
|
12341
|
+
save_view_data
|
|
12342
|
+
]);
|
|
11791
12343
|
const handleGhostNodeImageChange = useCallback4((useImage, imageUrl) => {
|
|
11792
|
-
const {
|
|
12344
|
+
const {
|
|
12345
|
+
node: ghostNode,
|
|
12346
|
+
line: ghostLine,
|
|
12347
|
+
aura: ghostAura
|
|
12348
|
+
} = stateRef.current.ghostElements;
|
|
11793
12349
|
const { graphGroup, glowTexture } = stateRef.current;
|
|
11794
12350
|
if (!ghostNode || !graphGroup) return;
|
|
11795
12351
|
const currentData = {
|
|
@@ -11800,13 +12356,15 @@ function XViewScene({
|
|
|
11800
12356
|
const position = ghostNode.position.clone();
|
|
11801
12357
|
if (ghostNode.userData.labelObject) {
|
|
11802
12358
|
graphGroup.remove(ghostNode.userData.labelObject);
|
|
11803
|
-
if (ghostNode.userData.labelObject.material.map)
|
|
12359
|
+
if (ghostNode.userData.labelObject.material.map)
|
|
12360
|
+
ghostNode.userData.labelObject.material.map.dispose();
|
|
11804
12361
|
ghostNode.userData.labelObject.material.dispose();
|
|
11805
12362
|
}
|
|
11806
12363
|
graphGroup.remove(ghostNode);
|
|
11807
12364
|
if (ghostNode.geometry) ghostNode.geometry.dispose();
|
|
11808
12365
|
if (ghostNode.material) {
|
|
11809
|
-
if (Array.isArray(ghostNode.material))
|
|
12366
|
+
if (Array.isArray(ghostNode.material))
|
|
12367
|
+
ghostNode.material.forEach((m) => m.dispose());
|
|
11810
12368
|
else ghostNode.material.dispose();
|
|
11811
12369
|
}
|
|
11812
12370
|
const newGhostNode = createNodeMesh(currentData, position, glowTexture);
|
|
@@ -11851,28 +12409,31 @@ function XViewScene({
|
|
|
11851
12409
|
ghostAura.material.opacity = Math.min(0.8, newIntensity * 0.15);
|
|
11852
12410
|
}
|
|
11853
12411
|
}, []);
|
|
11854
|
-
const handleDetailNodeIntensityChange = useCallback4(
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
borderMesh.material
|
|
12412
|
+
const handleDetailNodeIntensityChange = useCallback4(
|
|
12413
|
+
(nodeId, newIntensity) => {
|
|
12414
|
+
const mesh = stateRef.current.nodeObjects[String(nodeId)];
|
|
12415
|
+
if (!mesh) return;
|
|
12416
|
+
const adjustedIntensity = newIntensity + MIN_VISIBILITY_INTENSITY;
|
|
12417
|
+
mesh.userData.intensity = newIntensity;
|
|
12418
|
+
mesh.userData._baseEmissiveIntensity = adjustedIntensity;
|
|
12419
|
+
const isImageNode = mesh.userData.useImageAsTexture === true;
|
|
12420
|
+
if (isImageNode) {
|
|
12421
|
+
const borderMesh = mesh.getObjectByName("borderRing");
|
|
12422
|
+
if (borderMesh && borderMesh.material) {
|
|
12423
|
+
borderMesh.material.emissiveIntensity = adjustedIntensity;
|
|
12424
|
+
}
|
|
12425
|
+
} else {
|
|
12426
|
+
if (mesh.material) {
|
|
12427
|
+
mesh.material.emissiveIntensity = adjustedIntensity;
|
|
12428
|
+
}
|
|
11865
12429
|
}
|
|
11866
|
-
|
|
11867
|
-
if (
|
|
11868
|
-
|
|
12430
|
+
const aura = mesh.getObjectByName("aura");
|
|
12431
|
+
if (aura && aura.material) {
|
|
12432
|
+
aura.material.opacity = Math.min(0.8, newIntensity * 0.15);
|
|
11869
12433
|
}
|
|
11870
|
-
}
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
aura.material.opacity = Math.min(0.8, newIntensity * 0.15);
|
|
11874
|
-
}
|
|
11875
|
-
}, []);
|
|
12434
|
+
},
|
|
12435
|
+
[]
|
|
12436
|
+
);
|
|
11876
12437
|
const handleGhostNodeColorChange = (newColor) => {
|
|
11877
12438
|
const { node: ghostNode, aura: ghostAura } = stateRef.current.ghostElements;
|
|
11878
12439
|
if (!ghostNode) return;
|
|
@@ -11983,7 +12544,9 @@ function XViewScene({
|
|
|
11983
12544
|
mesh.userData.size = newSize;
|
|
11984
12545
|
};
|
|
11985
12546
|
const handleStartAncestryCreation = (nodeData) => {
|
|
11986
|
-
setContextMenu(
|
|
12547
|
+
setContextMenu(
|
|
12548
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
12549
|
+
);
|
|
11987
12550
|
stateRef.current.maxAncestryRenderIndex = 0;
|
|
11988
12551
|
setAncestryMode({
|
|
11989
12552
|
isActive: true,
|
|
@@ -12008,9 +12571,12 @@ function XViewScene({
|
|
|
12008
12571
|
const newTreeStr = JSON.stringify(newTree);
|
|
12009
12572
|
let metaChanged = false;
|
|
12010
12573
|
if (extraData) {
|
|
12011
|
-
if (extraData.ancestryName !== void 0 && extraData.ancestryName !== prev.ancestryName)
|
|
12012
|
-
|
|
12013
|
-
if (extraData.
|
|
12574
|
+
if (extraData.ancestryName !== void 0 && extraData.ancestryName !== prev.ancestryName)
|
|
12575
|
+
metaChanged = true;
|
|
12576
|
+
if (extraData.ancestryDescription !== void 0 && extraData.ancestryDescription !== prev.ancestryDescription)
|
|
12577
|
+
metaChanged = true;
|
|
12578
|
+
if (extraData.ancestryDescriptionSections !== void 0 && JSON.stringify(extraData.ancestryDescriptionSections) !== JSON.stringify(prev.ancestryDescriptionSections))
|
|
12579
|
+
metaChanged = true;
|
|
12014
12580
|
}
|
|
12015
12581
|
if (prevTreeStr === newTreeStr && !metaChanged) {
|
|
12016
12582
|
return prev;
|
|
@@ -12088,13 +12654,15 @@ function XViewScene({
|
|
|
12088
12654
|
if (ghostElements.node && graphGroup) {
|
|
12089
12655
|
if (ghostElements.node.userData.labelObject) {
|
|
12090
12656
|
graphGroup.remove(ghostElements.node.userData.labelObject);
|
|
12091
|
-
if (ghostElements.node.userData.labelObject.material.map)
|
|
12657
|
+
if (ghostElements.node.userData.labelObject.material.map)
|
|
12658
|
+
ghostElements.node.userData.labelObject.material.map.dispose();
|
|
12092
12659
|
ghostElements.node.userData.labelObject.material.dispose();
|
|
12093
12660
|
}
|
|
12094
12661
|
graphGroup.remove(ghostElements.node);
|
|
12095
12662
|
ghostElements.node.traverse((child) => {
|
|
12096
12663
|
if (child.material) {
|
|
12097
|
-
if (Array.isArray(child.material))
|
|
12664
|
+
if (Array.isArray(child.material))
|
|
12665
|
+
child.material.forEach((m) => m.dispose());
|
|
12098
12666
|
else child.material.dispose();
|
|
12099
12667
|
}
|
|
12100
12668
|
if (child.geometry) child.geometry.dispose();
|
|
@@ -12104,7 +12672,17 @@ function XViewScene({
|
|
|
12104
12672
|
setQuestMode({ isActive: false });
|
|
12105
12673
|
}, []);
|
|
12106
12674
|
const handleSaveQuestNode = async (context, newQuestData) => {
|
|
12107
|
-
const {
|
|
12675
|
+
const {
|
|
12676
|
+
graphDataRef,
|
|
12677
|
+
sceneDataRef: sceneDataRef2,
|
|
12678
|
+
stateRef: stateRef2,
|
|
12679
|
+
setters,
|
|
12680
|
+
actions,
|
|
12681
|
+
sceneSaveUrl: sceneSaveUrl2,
|
|
12682
|
+
viewType,
|
|
12683
|
+
sceneConfigId: sceneConfigId2,
|
|
12684
|
+
ownerId: ownerId2
|
|
12685
|
+
} = context;
|
|
12108
12686
|
if (!graphDataRef.current || (viewType == null ? void 0 : viewType.toLowerCase()) !== "view") return;
|
|
12109
12687
|
const currentCounter = sceneDataRef2.current.quest_counter || 1;
|
|
12110
12688
|
const newNode = {
|
|
@@ -12137,7 +12715,8 @@ function XViewScene({
|
|
|
12137
12715
|
const finalPosition = stateRef2.current.ghostElements.node ? stateRef2.current.ghostElements.node.position.clone() : stateRef2.current.controls.target.clone();
|
|
12138
12716
|
const { graphGroup, ghostElements } = stateRef2.current;
|
|
12139
12717
|
if (ghostElements.node && graphGroup) {
|
|
12140
|
-
if (ghostElements.node.userData.labelObject)
|
|
12718
|
+
if (ghostElements.node.userData.labelObject)
|
|
12719
|
+
graphGroup.remove(ghostElements.node.userData.labelObject);
|
|
12141
12720
|
graphGroup.remove(ghostElements.node);
|
|
12142
12721
|
}
|
|
12143
12722
|
stateRef2.current.ghostElements = { node: null, line: null, aura: null };
|
|
@@ -12151,14 +12730,33 @@ function XViewScene({
|
|
|
12151
12730
|
}
|
|
12152
12731
|
};
|
|
12153
12732
|
userActionHandlers.handleCompleteConnection = async (context, targetNodeData) => {
|
|
12154
|
-
const {
|
|
12733
|
+
const {
|
|
12734
|
+
stateRef: stateRef2,
|
|
12735
|
+
graphDataRef,
|
|
12736
|
+
sceneDataRef: sceneDataRef2,
|
|
12737
|
+
sceneConfigId: sceneConfigId2,
|
|
12738
|
+
sceneSaveUrl: sceneSaveUrl2,
|
|
12739
|
+
ownerId: ownerId2
|
|
12740
|
+
} = context;
|
|
12155
12741
|
const { sourceNodeData } = stateRef2.current.connection;
|
|
12156
12742
|
if (!graphDataRef.current || !sceneDataRef2.current || !sourceNodeData || !targetNodeData) {
|
|
12157
12743
|
userActionHandlers.handleCancelConnection(context);
|
|
12158
12744
|
return;
|
|
12159
12745
|
}
|
|
12160
|
-
const sourceParentInfo = getParentFileInfoForNode(
|
|
12161
|
-
|
|
12746
|
+
const sourceParentInfo = getParentFileInfoForNode(
|
|
12747
|
+
graphDataRef.current,
|
|
12748
|
+
sceneDataRef2.current,
|
|
12749
|
+
sourceNodeData.id,
|
|
12750
|
+
sceneConfigId2,
|
|
12751
|
+
ownerId2
|
|
12752
|
+
);
|
|
12753
|
+
const targetParentInfo = getParentFileInfoForNode(
|
|
12754
|
+
graphDataRef.current,
|
|
12755
|
+
sceneDataRef2.current,
|
|
12756
|
+
targetNodeData.id,
|
|
12757
|
+
sceneConfigId2,
|
|
12758
|
+
ownerId2
|
|
12759
|
+
);
|
|
12162
12760
|
let parentInfoToSave = sourceParentInfo;
|
|
12163
12761
|
const isSourceQuest = sourceParentInfo.parentFileId === sceneConfigId2;
|
|
12164
12762
|
const isTargetQuest = targetParentInfo.parentFileId === sceneConfigId2;
|
|
@@ -12184,10 +12782,15 @@ function XViewScene({
|
|
|
12184
12782
|
};
|
|
12185
12783
|
await context.actions.save_view_data(sceneSaveUrl2, viewFilePayload);
|
|
12186
12784
|
} else {
|
|
12187
|
-
const specificParentData = JSON.parse(
|
|
12785
|
+
const specificParentData = JSON.parse(
|
|
12786
|
+
JSON.stringify(graphDataRef.current[parentFileIdToSave])
|
|
12787
|
+
);
|
|
12188
12788
|
specificParentData.links.push(newLink);
|
|
12189
12789
|
const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
|
|
12190
|
-
await context.actions.save_view_data(
|
|
12790
|
+
await context.actions.save_view_data(
|
|
12791
|
+
filenameForSpecificParent,
|
|
12792
|
+
specificParentData
|
|
12793
|
+
);
|
|
12191
12794
|
graphDataRef.current[parentFileIdToSave] = specificParentData;
|
|
12192
12795
|
}
|
|
12193
12796
|
addNewLinkToScene(stateRef2.current, newLink);
|
|
@@ -12199,7 +12802,9 @@ function XViewScene({
|
|
|
12199
12802
|
};
|
|
12200
12803
|
const handleClearAncestryVisuals = useCallback4((ancestryId) => {
|
|
12201
12804
|
const { renderedAncestries, ancestryGroup } = stateRef.current;
|
|
12202
|
-
const renderIndex = renderedAncestries.findIndex(
|
|
12805
|
+
const renderIndex = renderedAncestries.findIndex(
|
|
12806
|
+
(a) => String(a.id) === String(ancestryId)
|
|
12807
|
+
);
|
|
12203
12808
|
if (renderIndex !== -1) {
|
|
12204
12809
|
const toRemove = renderedAncestries[renderIndex];
|
|
12205
12810
|
toRemove.lines.forEach((line) => {
|
|
@@ -12208,12 +12813,16 @@ function XViewScene({
|
|
|
12208
12813
|
if (line.material) line.material.dispose();
|
|
12209
12814
|
});
|
|
12210
12815
|
renderedAncestries.splice(renderIndex, 1);
|
|
12211
|
-
stateRef.current.ancestryLinks = renderedAncestries.flatMap(
|
|
12816
|
+
stateRef.current.ancestryLinks = renderedAncestries.flatMap(
|
|
12817
|
+
(a) => a.lines
|
|
12818
|
+
);
|
|
12212
12819
|
}
|
|
12213
12820
|
}, []);
|
|
12214
12821
|
const handleRenderAncestry = useCallback4(
|
|
12215
12822
|
async (ancestryObject, allowedSectionIds = null, activeSectionIdForFocus = null, baseRotation = 0, forceReprocess = true) => {
|
|
12216
|
-
setContextMenu(
|
|
12823
|
+
setContextMenu(
|
|
12824
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
12825
|
+
);
|
|
12217
12826
|
if (!ancestryObject || !ancestryObject.tree) {
|
|
12218
12827
|
return;
|
|
12219
12828
|
}
|
|
@@ -12240,14 +12849,16 @@ function XViewScene({
|
|
|
12240
12849
|
if (numId !== void 0 && normalizedAllowedIds.has(numId)) return true;
|
|
12241
12850
|
if (strId && normalizedAllowedIds.has(strId)) return true;
|
|
12242
12851
|
if (strSecId && normalizedAllowedIds.has(strSecId)) return true;
|
|
12243
|
-
if (numId !== void 0 && normalizedAllowedIds.has(String(numId)))
|
|
12852
|
+
if (numId !== void 0 && normalizedAllowedIds.has(String(numId)))
|
|
12853
|
+
return true;
|
|
12244
12854
|
return false;
|
|
12245
12855
|
};
|
|
12246
12856
|
const checkIsActive = (section, targetId) => {
|
|
12247
12857
|
if (targetId === null || targetId === void 0) return false;
|
|
12248
12858
|
const sTarget = String(targetId);
|
|
12249
12859
|
if (section.id && String(section.id) === sTarget) return true;
|
|
12250
|
-
if (section.section_id && String(section.section_id) === sTarget)
|
|
12860
|
+
if (section.section_id && String(section.section_id) === sTarget)
|
|
12861
|
+
return true;
|
|
12251
12862
|
if (section.section_numeric_id !== void 0) {
|
|
12252
12863
|
if (String(section.section_numeric_id) === sTarget) return true;
|
|
12253
12864
|
}
|
|
@@ -12263,7 +12874,9 @@ function XViewScene({
|
|
|
12263
12874
|
traverse(sectionTree);
|
|
12264
12875
|
return ids;
|
|
12265
12876
|
};
|
|
12266
|
-
const existingIndex = renderedAncestries.findIndex(
|
|
12877
|
+
const existingIndex = renderedAncestries.findIndex(
|
|
12878
|
+
(a) => String(a.id) === String(ancestryObject.ancestry_id)
|
|
12879
|
+
);
|
|
12267
12880
|
let skipGeneration = false;
|
|
12268
12881
|
let ancestryEntry = null;
|
|
12269
12882
|
if (existingIndex !== -1) {
|
|
@@ -12318,9 +12931,15 @@ function XViewScene({
|
|
|
12318
12931
|
if (line.material) line.material.dispose();
|
|
12319
12932
|
});
|
|
12320
12933
|
}
|
|
12321
|
-
const allParentNodes = Object.values(parentDataRef.current).flatMap(
|
|
12934
|
+
const allParentNodes = Object.values(parentDataRef.current).flatMap(
|
|
12935
|
+
(fileData) => fileData.nodes
|
|
12936
|
+
);
|
|
12322
12937
|
const allAncestries = ancestryDataRef.current || [];
|
|
12323
|
-
const fullTree = buildFullAncestryTree(
|
|
12938
|
+
const fullTree = buildFullAncestryTree(
|
|
12939
|
+
ancestryObject.tree,
|
|
12940
|
+
allParentNodes,
|
|
12941
|
+
allAncestries
|
|
12942
|
+
);
|
|
12324
12943
|
if (!fullTree) return;
|
|
12325
12944
|
const rootNodeId = String(ancestryObject.ancestral_node);
|
|
12326
12945
|
let rootNodeMesh = nodeObjects[rootNodeId];
|
|
@@ -12363,7 +12982,10 @@ function XViewScene({
|
|
|
12363
12982
|
const colorIndex = stateRef.current.ancestryRenderCounter % 3;
|
|
12364
12983
|
colorHex = ANCESTRY_COLORS[colorIndex];
|
|
12365
12984
|
}
|
|
12366
|
-
const resolution = new THREE3.Vector2(
|
|
12985
|
+
const resolution = new THREE3.Vector2(
|
|
12986
|
+
renderer.domElement.clientWidth,
|
|
12987
|
+
renderer.domElement.clientHeight
|
|
12988
|
+
);
|
|
12367
12989
|
const cleanupLinesForNode = (nodeId) => {
|
|
12368
12990
|
if (!ancestryEntry || !ancestryEntry.lines) return;
|
|
12369
12991
|
const linesKeep = [];
|
|
@@ -12403,7 +13025,13 @@ function XViewScene({
|
|
|
12403
13025
|
}
|
|
12404
13026
|
if (originMesh && rootNodeMesh) {
|
|
12405
13027
|
cleanupLinesForNode(rootNodeId);
|
|
12406
|
-
const branchLine = createAncestryLinkLine(
|
|
13028
|
+
const branchLine = createAncestryLinkLine(
|
|
13029
|
+
originMesh,
|
|
13030
|
+
rootNodeMesh,
|
|
13031
|
+
resolution,
|
|
13032
|
+
{},
|
|
13033
|
+
colorHex
|
|
13034
|
+
);
|
|
12407
13035
|
ancestryGroup.add(branchLine);
|
|
12408
13036
|
ancestryEntry.lines.push(branchLine);
|
|
12409
13037
|
}
|
|
@@ -12414,8 +13042,14 @@ function XViewScene({
|
|
|
12414
13042
|
if (!children || children.length === 0) return null;
|
|
12415
13043
|
let lastRenderedMesh = null;
|
|
12416
13044
|
const numChildren = children.length;
|
|
12417
|
-
const forwardVec = new THREE3.Vector3(0, 0, 1).applyAxisAngle(
|
|
12418
|
-
|
|
13045
|
+
const forwardVec = new THREE3.Vector3(0, 0, 1).applyAxisAngle(
|
|
13046
|
+
new THREE3.Vector3(0, 1, 0),
|
|
13047
|
+
currentAngle
|
|
13048
|
+
);
|
|
13049
|
+
const rightVec = new THREE3.Vector3(1, 0, 0).applyAxisAngle(
|
|
13050
|
+
new THREE3.Vector3(0, 1, 0),
|
|
13051
|
+
currentAngle
|
|
13052
|
+
);
|
|
12419
13053
|
const angleRange = numChildren > 3 ? Math.PI : Math.PI / 1.5;
|
|
12420
13054
|
children.forEach((childItem, index) => {
|
|
12421
13055
|
if (childItem.is_section) return;
|
|
@@ -12436,14 +13070,30 @@ function XViewScene({
|
|
|
12436
13070
|
targetPositionsCache.set(sNodeId, childPosition.clone());
|
|
12437
13071
|
cleanupLinesForNode(sNodeId);
|
|
12438
13072
|
}
|
|
12439
|
-
const childMesh = addOrUpdateNodeMesh(
|
|
13073
|
+
const childMesh = addOrUpdateNodeMesh(
|
|
13074
|
+
childItem.node,
|
|
13075
|
+
childPosition,
|
|
13076
|
+
true
|
|
13077
|
+
);
|
|
12440
13078
|
allRenderedNodePositions.push(childPosition);
|
|
12441
|
-
const line = createAncestryLinkLine(
|
|
13079
|
+
const line = createAncestryLinkLine(
|
|
13080
|
+
parentMesh,
|
|
13081
|
+
childMesh,
|
|
13082
|
+
resolution,
|
|
13083
|
+
childItem.relationship,
|
|
13084
|
+
colorHex
|
|
13085
|
+
);
|
|
12442
13086
|
ancestryGroup.add(line);
|
|
12443
13087
|
ancestryEntry.lines.push(line);
|
|
12444
13088
|
lastRenderedMesh = childMesh;
|
|
12445
13089
|
if (childItem.children && childItem.children.length > 0) {
|
|
12446
|
-
const lastDescendant = renderCluster(
|
|
13090
|
+
const lastDescendant = renderCluster(
|
|
13091
|
+
childItem.children,
|
|
13092
|
+
childMesh,
|
|
13093
|
+
childPosition,
|
|
13094
|
+
level + 1,
|
|
13095
|
+
currentAngle
|
|
13096
|
+
);
|
|
12447
13097
|
if (lastDescendant) lastRenderedMesh = lastDescendant;
|
|
12448
13098
|
}
|
|
12449
13099
|
});
|
|
@@ -12457,9 +13107,13 @@ function XViewScene({
|
|
|
12457
13107
|
else looseNodes.push(child);
|
|
12458
13108
|
});
|
|
12459
13109
|
}
|
|
12460
|
-
sections.sort(
|
|
13110
|
+
sections.sort(
|
|
13111
|
+
(a, b) => (a.section_numeric_id || 0) - (b.section_numeric_id || 0)
|
|
13112
|
+
);
|
|
12461
13113
|
let combinedStartNodes = [...looseNodes];
|
|
12462
|
-
let session0Index = sections.findIndex(
|
|
13114
|
+
let session0Index = sections.findIndex(
|
|
13115
|
+
(s) => s.section_numeric_id === 0 || s.name === "Sess\xE3o 0"
|
|
13116
|
+
);
|
|
12463
13117
|
if (session0Index !== -1) {
|
|
12464
13118
|
const session0 = sections[session0Index];
|
|
12465
13119
|
if (checkShouldRender(session0) && session0.children) {
|
|
@@ -12468,7 +13122,13 @@ function XViewScene({
|
|
|
12468
13122
|
sections.splice(session0Index, 1);
|
|
12469
13123
|
}
|
|
12470
13124
|
const rootTargetPos2 = targetPositionsCache.get(rootNodeId) || rootNodeMesh.position;
|
|
12471
|
-
const lastStartMesh = renderCluster(
|
|
13125
|
+
const lastStartMesh = renderCluster(
|
|
13126
|
+
combinedStartNodes,
|
|
13127
|
+
rootNodeMesh,
|
|
13128
|
+
rootTargetPos2,
|
|
13129
|
+
1,
|
|
13130
|
+
baseRotation
|
|
13131
|
+
);
|
|
12472
13132
|
if (lastStartMesh) {
|
|
12473
13133
|
currentAnchorMesh = lastStartMesh;
|
|
12474
13134
|
}
|
|
@@ -12479,10 +13139,18 @@ function XViewScene({
|
|
|
12479
13139
|
const sectionNodes = section.children || [];
|
|
12480
13140
|
if (sectionNodes.length > 0) {
|
|
12481
13141
|
const parentAngle = nodeRotationMap.get(String(currentAnchorMesh.userData.id)) ?? baseRotation;
|
|
12482
|
-
const lastMeshOfThisSection = renderCluster(
|
|
13142
|
+
const lastMeshOfThisSection = renderCluster(
|
|
13143
|
+
sectionNodes,
|
|
13144
|
+
currentAnchorMesh,
|
|
13145
|
+
currentAnchorPosition,
|
|
13146
|
+
1,
|
|
13147
|
+
parentAngle
|
|
13148
|
+
);
|
|
12483
13149
|
if (lastMeshOfThisSection) {
|
|
12484
13150
|
currentAnchorMesh = lastMeshOfThisSection;
|
|
12485
|
-
currentAnchorPosition = targetPositionsCache.get(
|
|
13151
|
+
currentAnchorPosition = targetPositionsCache.get(
|
|
13152
|
+
String(lastMeshOfThisSection.userData.id)
|
|
13153
|
+
) || lastMeshOfThisSection.position;
|
|
12486
13154
|
}
|
|
12487
13155
|
}
|
|
12488
13156
|
}
|
|
@@ -12511,7 +13179,10 @@ function XViewScene({
|
|
|
12511
13179
|
} else {
|
|
12512
13180
|
const directChildren = fullTree.children ? fullTree.children.filter((c) => !c.is_section) : [];
|
|
12513
13181
|
if (directChildren.length > 0) {
|
|
12514
|
-
targetSectionForZone = {
|
|
13182
|
+
targetSectionForZone = {
|
|
13183
|
+
name: "In\xEDcio",
|
|
13184
|
+
children: directChildren
|
|
13185
|
+
};
|
|
12515
13186
|
}
|
|
12516
13187
|
}
|
|
12517
13188
|
} else {
|
|
@@ -12585,7 +13256,10 @@ function XViewScene({
|
|
|
12585
13256
|
stateRef.current.ancestryLinks = stateRef.current.renderedAncestries.flatMap((a) => a.lines);
|
|
12586
13257
|
focusTargetPosition = center;
|
|
12587
13258
|
focusTargetRotation = baseRotation;
|
|
12588
|
-
const desiredDistance = Math.max(
|
|
13259
|
+
const desiredDistance = Math.max(
|
|
13260
|
+
x_view_config.CAMERA_ZOOM_DISTANCE,
|
|
13261
|
+
radius * 2.8
|
|
13262
|
+
);
|
|
12589
13263
|
focusZoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
|
|
12590
13264
|
} else {
|
|
12591
13265
|
const anchorId = String(currentAnchorMesh.userData.id);
|
|
@@ -12600,13 +13274,18 @@ function XViewScene({
|
|
|
12600
13274
|
}
|
|
12601
13275
|
if (!focusTargetPosition && !allowedSectionIds) {
|
|
12602
13276
|
if (allRenderedNodePositions.length > 0) {
|
|
12603
|
-
const boundingBox = new THREE3.Box3().setFromPoints(
|
|
13277
|
+
const boundingBox = new THREE3.Box3().setFromPoints(
|
|
13278
|
+
allRenderedNodePositions
|
|
13279
|
+
);
|
|
12604
13280
|
const center = new THREE3.Vector3();
|
|
12605
13281
|
boundingBox.getCenter(center);
|
|
12606
13282
|
const size = new THREE3.Vector3();
|
|
12607
13283
|
boundingBox.getSize(size);
|
|
12608
13284
|
const maxDim = Math.max(size.x, size.y, size.z);
|
|
12609
|
-
const fitZoom = Math.min(
|
|
13285
|
+
const fitZoom = Math.min(
|
|
13286
|
+
1,
|
|
13287
|
+
x_view_config.CAMERA_ZOOM_DISTANCE / (maxDim * 1.5)
|
|
13288
|
+
);
|
|
12610
13289
|
const defaultBase = new THREE3.Vector3(-50, 40, 20);
|
|
12611
13290
|
const rotatedCinematicAngle = defaultBase.clone().applyAxisAngle(new THREE3.Vector3(0, 1, 0), baseRotation);
|
|
12612
13291
|
tweenToTarget(center, fitZoom, rotatedCinematicAngle);
|
|
@@ -12625,210 +13304,280 @@ function XViewScene({
|
|
|
12625
13304
|
tweenToTarget(targetPos, focusZoomFactor, rotatedCinematicAngle);
|
|
12626
13305
|
}
|
|
12627
13306
|
},
|
|
12628
|
-
[
|
|
13307
|
+
[
|
|
13308
|
+
addOrUpdateNodeMesh,
|
|
13309
|
+
tweenToTarget,
|
|
13310
|
+
buildFullAncestryTree,
|
|
13311
|
+
readingMode.isActive,
|
|
13312
|
+
ancestryMode.isActive
|
|
13313
|
+
]
|
|
12629
13314
|
);
|
|
12630
|
-
const handleRenderAbstractionTree = useCallback4(
|
|
12631
|
-
|
|
12632
|
-
|
|
12633
|
-
|
|
12634
|
-
|
|
12635
|
-
|
|
12636
|
-
|
|
12637
|
-
|
|
12638
|
-
|
|
12639
|
-
|
|
12640
|
-
|
|
12641
|
-
|
|
12642
|
-
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
13315
|
+
const handleRenderAbstractionTree = useCallback4(
|
|
13316
|
+
(ancestryObject, targetNodeId = null) => {
|
|
13317
|
+
setContextMenu(
|
|
13318
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
13319
|
+
);
|
|
13320
|
+
if (!ancestryObject || !ancestryObject.abstraction_tree) return;
|
|
13321
|
+
const { ancestryGroup, nodeObjects, renderer, renderedAncestries } = stateRef.current;
|
|
13322
|
+
const allParentNodes = Object.values(parentDataRef.current).flatMap(
|
|
13323
|
+
(f) => f.nodes
|
|
13324
|
+
);
|
|
13325
|
+
let fullTree = buildFullAncestryTree(
|
|
13326
|
+
ancestryObject.abstraction_tree,
|
|
13327
|
+
allParentNodes,
|
|
13328
|
+
ancestryDataRef.current
|
|
13329
|
+
);
|
|
13330
|
+
if (!fullTree || !fullTree.node) return;
|
|
13331
|
+
if (targetNodeId) {
|
|
13332
|
+
const pruneTreeToPath = (treeNode, targetId) => {
|
|
13333
|
+
var _a2;
|
|
13334
|
+
if (!treeNode) return null;
|
|
13335
|
+
const currentId = treeNode.is_section ? treeNode.section_id : String((_a2 = treeNode.node) == null ? void 0 : _a2.id);
|
|
13336
|
+
if (String(currentId) === String(targetId)) {
|
|
13337
|
+
return { ...treeNode, children: [] };
|
|
12651
13338
|
}
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
}
|
|
12658
|
-
const absId = ancestryObject.ancestry_id + "_abs";
|
|
12659
|
-
handleClearAncestryVisuals(absId);
|
|
12660
|
-
const colorHex = 9133302;
|
|
12661
|
-
const resolution = new THREE3.Vector2(renderer.domElement.clientWidth, renderer.domElement.clientHeight);
|
|
12662
|
-
const ancestryEntry = { id: absId, lines: [], isFullRender: true };
|
|
12663
|
-
renderedAncestries.push(ancestryEntry);
|
|
12664
|
-
const rootNodeId = String(fullTree.node.id);
|
|
12665
|
-
let rootNodeMesh = nodeObjects[rootNodeId];
|
|
12666
|
-
let rootTargetPos = rootNodeMesh ? rootNodeMesh.position.clone() : new THREE3.Vector3(0, 0, 0);
|
|
12667
|
-
if (!rootNodeMesh) {
|
|
12668
|
-
rootNodeMesh = addOrUpdateNodeMesh(fullTree.node, rootTargetPos, true);
|
|
12669
|
-
}
|
|
12670
|
-
const SPACING_Y = -40;
|
|
12671
|
-
const SPACING_X = 55;
|
|
12672
|
-
const renderVertical = (treeNode, parentMesh, parentPos, level) => {
|
|
12673
|
-
if (!treeNode.children || treeNode.children.length === 0) return;
|
|
12674
|
-
const totalSiblings = treeNode.children.length;
|
|
12675
|
-
treeNode.children.forEach((childItem, i) => {
|
|
12676
|
-
if (!childItem.node) return;
|
|
12677
|
-
const childX = parentPos.x + (i - (totalSiblings - 1) / 2) * (SPACING_X / Math.max(1, level * 0.4));
|
|
12678
|
-
const childY = parentPos.y + SPACING_Y;
|
|
12679
|
-
const childPos = new THREE3.Vector3(childX, childY, parentPos.z);
|
|
12680
|
-
const childMesh = addOrUpdateNodeMesh(childItem.node, childPos, true);
|
|
12681
|
-
const line = createAncestryLinkLine(parentMesh, childMesh, resolution, {}, colorHex);
|
|
12682
|
-
ancestryGroup.add(line);
|
|
12683
|
-
ancestryEntry.lines.push(line);
|
|
12684
|
-
renderVertical(childItem, childMesh, childPos, level + 1);
|
|
12685
|
-
});
|
|
12686
|
-
};
|
|
12687
|
-
renderVertical(fullTree, rootNodeMesh, rootTargetPos, 1);
|
|
12688
|
-
stateRef.current.ancestryLinks = renderedAncestries.flatMap((a) => a.lines);
|
|
12689
|
-
tweenToTarget(rootTargetPos, 0.7);
|
|
12690
|
-
}, [addOrUpdateNodeMesh, tweenToTarget, buildFullAncestryTree, handleClearAncestryVisuals]);
|
|
12691
|
-
const handleReadModeBranchNav = useCallback4((nodeId, action, direction = "right") => {
|
|
12692
|
-
const { ancestry, branchStack } = readingMode;
|
|
12693
|
-
if (!ancestry || !ancestry.tree) return;
|
|
12694
|
-
const allAncestries = ancestryDataRef.current || [];
|
|
12695
|
-
const fullTree = buildFullAncestryTree(ancestry.tree, Object.values(parentDataRef.current).flatMap((f) => f.nodes), allAncestries);
|
|
12696
|
-
if (action === "open") {
|
|
12697
|
-
let currentPtr = fullTree;
|
|
12698
|
-
for (const step of branchStack) {
|
|
12699
|
-
const found = findNodePath3(currentPtr, step.nodeId);
|
|
12700
|
-
if (found && found.node.parallel_branches) {
|
|
12701
|
-
const branch = found.node.parallel_branches.find((b) => b.id === step.branchId);
|
|
12702
|
-
if (branch) currentPtr = branch.tree;
|
|
12703
|
-
}
|
|
12704
|
-
}
|
|
12705
|
-
const foundTarget = findNodePath3(currentPtr, nodeId);
|
|
12706
|
-
if (foundTarget && foundTarget.node && foundTarget.node.parallel_branches && foundTarget.node.parallel_branches.length > 0) {
|
|
12707
|
-
const branchToOpen = foundTarget.node.parallel_branches.find((b) => (b.direction || "right") === direction);
|
|
12708
|
-
if (!branchToOpen) return;
|
|
12709
|
-
if (branchToOpen && branchToOpen.tree) {
|
|
12710
|
-
const parentIndexToSave = stateRef.current.readMode.currentMaxIndex;
|
|
12711
|
-
const savedBranchProgress = 0;
|
|
12712
|
-
stateRef.current.readMode.currentMaxIndex = savedBranchProgress;
|
|
12713
|
-
stateRef.current.maxAncestryRenderIndex = savedBranchProgress;
|
|
12714
|
-
const newStack = [...branchStack, {
|
|
12715
|
-
nodeId,
|
|
12716
|
-
branchId: branchToOpen.id,
|
|
12717
|
-
savedMaxIndex: parentIndexToSave,
|
|
12718
|
-
entryDirection: direction
|
|
12719
|
-
}];
|
|
12720
|
-
setReadingMode((prev) => ({ ...prev, branchStack: newStack, initialSectionId: null }));
|
|
12721
|
-
const branchAncestryObj = {
|
|
12722
|
-
ancestry_id: branchToOpen.id,
|
|
12723
|
-
ancestral_node: branchToOpen.tree.node.id,
|
|
12724
|
-
name: branchToOpen.name,
|
|
12725
|
-
description: branchToOpen.description,
|
|
12726
|
-
description_sections: branchToOpen.description_sections,
|
|
12727
|
-
tree: branchToOpen.tree,
|
|
12728
|
-
_originNodeId: nodeId,
|
|
12729
|
-
_branchDirection: direction
|
|
12730
|
-
};
|
|
12731
|
-
const allowedIds = /* @__PURE__ */ new Set(["preamble", 0, "0"]);
|
|
12732
|
-
const branchSections = parseDescriptionSections(branchToOpen.description || "", branchToOpen.description_sections || []);
|
|
12733
|
-
for (let i = 0; i <= savedBranchProgress; i++) {
|
|
12734
|
-
if (branchSections[i]) {
|
|
12735
|
-
if (branchSections[i].id) allowedIds.add(String(branchSections[i].id));
|
|
12736
|
-
if (branchSections[i].numericId !== void 0 && branchSections[i].numericId !== null) {
|
|
12737
|
-
allowedIds.add(branchSections[i].numericId);
|
|
12738
|
-
allowedIds.add(String(branchSections[i].numericId));
|
|
13339
|
+
if (treeNode.children && treeNode.children.length > 0) {
|
|
13340
|
+
for (let child of treeNode.children) {
|
|
13341
|
+
const prunedChild = pruneTreeToPath(child, targetId);
|
|
13342
|
+
if (prunedChild) {
|
|
13343
|
+
return { ...treeNode, children: [prunedChild] };
|
|
12739
13344
|
}
|
|
12740
13345
|
}
|
|
12741
13346
|
}
|
|
12742
|
-
|
|
12743
|
-
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
|
|
13347
|
+
return null;
|
|
13348
|
+
};
|
|
13349
|
+
const pruned = pruneTreeToPath(fullTree, targetNodeId);
|
|
13350
|
+
if (pruned) fullTree = pruned;
|
|
13351
|
+
}
|
|
13352
|
+
const absId = ancestryObject.ancestry_id + "_abs";
|
|
13353
|
+
handleClearAncestryVisuals(absId);
|
|
13354
|
+
const colorHex = 9133302;
|
|
13355
|
+
const resolution = new THREE3.Vector2(
|
|
13356
|
+
renderer.domElement.clientWidth,
|
|
13357
|
+
renderer.domElement.clientHeight
|
|
13358
|
+
);
|
|
13359
|
+
const ancestryEntry = { id: absId, lines: [], isFullRender: true };
|
|
13360
|
+
renderedAncestries.push(ancestryEntry);
|
|
13361
|
+
const rootNodeId = String(fullTree.node.id);
|
|
13362
|
+
let rootNodeMesh = nodeObjects[rootNodeId];
|
|
13363
|
+
let rootTargetPos = rootNodeMesh ? rootNodeMesh.position.clone() : new THREE3.Vector3(0, 0, 0);
|
|
13364
|
+
if (!rootNodeMesh) {
|
|
13365
|
+
rootNodeMesh = addOrUpdateNodeMesh(fullTree.node, rootTargetPos, true);
|
|
12754
13366
|
}
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
const
|
|
12758
|
-
|
|
12759
|
-
|
|
12760
|
-
|
|
12761
|
-
|
|
12762
|
-
|
|
12763
|
-
|
|
12764
|
-
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
|
|
12769
|
-
|
|
12770
|
-
|
|
13367
|
+
const SPACING_Y = -40;
|
|
13368
|
+
const SPACING_X = 55;
|
|
13369
|
+
const renderVertical = (treeNode, parentMesh, parentPos, level) => {
|
|
13370
|
+
if (!treeNode.children || treeNode.children.length === 0) return;
|
|
13371
|
+
const totalSiblings = treeNode.children.length;
|
|
13372
|
+
treeNode.children.forEach((childItem, i) => {
|
|
13373
|
+
if (!childItem.node) return;
|
|
13374
|
+
const childX = parentPos.x + (i - (totalSiblings - 1) / 2) * (SPACING_X / Math.max(1, level * 0.4));
|
|
13375
|
+
const childY = parentPos.y + SPACING_Y;
|
|
13376
|
+
const childPos = new THREE3.Vector3(childX, childY, parentPos.z);
|
|
13377
|
+
const childMesh = addOrUpdateNodeMesh(childItem.node, childPos, true);
|
|
13378
|
+
const line = createAncestryLinkLine(
|
|
13379
|
+
parentMesh,
|
|
13380
|
+
childMesh,
|
|
13381
|
+
resolution,
|
|
13382
|
+
{},
|
|
13383
|
+
colorHex
|
|
13384
|
+
);
|
|
13385
|
+
ancestryGroup.add(line);
|
|
13386
|
+
ancestryEntry.lines.push(line);
|
|
13387
|
+
renderVertical(childItem, childMesh, childPos, level + 1);
|
|
13388
|
+
});
|
|
13389
|
+
};
|
|
13390
|
+
renderVertical(fullTree, rootNodeMesh, rootTargetPos, 1);
|
|
13391
|
+
stateRef.current.ancestryLinks = renderedAncestries.flatMap(
|
|
13392
|
+
(a) => a.lines
|
|
13393
|
+
);
|
|
13394
|
+
tweenToTarget(rootTargetPos, 0.7);
|
|
13395
|
+
},
|
|
13396
|
+
[
|
|
13397
|
+
addOrUpdateNodeMesh,
|
|
13398
|
+
tweenToTarget,
|
|
13399
|
+
buildFullAncestryTree,
|
|
13400
|
+
handleClearAncestryVisuals
|
|
13401
|
+
]
|
|
13402
|
+
);
|
|
13403
|
+
const handleReadModeBranchNav = useCallback4(
|
|
13404
|
+
(nodeId, action, direction = "right") => {
|
|
13405
|
+
const { ancestry, branchStack } = readingMode;
|
|
13406
|
+
if (!ancestry || !ancestry.tree) return;
|
|
13407
|
+
const allAncestries = ancestryDataRef.current || [];
|
|
13408
|
+
const fullTree = buildFullAncestryTree(
|
|
13409
|
+
ancestry.tree,
|
|
13410
|
+
Object.values(parentDataRef.current).flatMap((f) => f.nodes),
|
|
13411
|
+
allAncestries
|
|
13412
|
+
);
|
|
13413
|
+
if (action === "open") {
|
|
13414
|
+
let currentPtr = fullTree;
|
|
13415
|
+
for (const step of branchStack) {
|
|
13416
|
+
const found = findNodePath3(currentPtr, step.nodeId);
|
|
12771
13417
|
if (found && found.node.parallel_branches) {
|
|
12772
|
-
const branch = found.node.parallel_branches.find(
|
|
12773
|
-
|
|
12774
|
-
|
|
12775
|
-
|
|
13418
|
+
const branch = found.node.parallel_branches.find(
|
|
13419
|
+
(b) => b.id === step.branchId
|
|
13420
|
+
);
|
|
13421
|
+
if (branch) currentPtr = branch.tree;
|
|
13422
|
+
}
|
|
13423
|
+
}
|
|
13424
|
+
const foundTarget = findNodePath3(currentPtr, nodeId);
|
|
13425
|
+
if (foundTarget && foundTarget.node && foundTarget.node.parallel_branches && foundTarget.node.parallel_branches.length > 0) {
|
|
13426
|
+
const branchToOpen = foundTarget.node.parallel_branches.find(
|
|
13427
|
+
(b) => (b.direction || "right") === direction
|
|
13428
|
+
);
|
|
13429
|
+
if (!branchToOpen) return;
|
|
13430
|
+
if (branchToOpen && branchToOpen.tree) {
|
|
13431
|
+
const parentIndexToSave = stateRef.current.readMode.currentMaxIndex;
|
|
13432
|
+
const savedBranchProgress = 0;
|
|
13433
|
+
stateRef.current.readMode.currentMaxIndex = savedBranchProgress;
|
|
13434
|
+
stateRef.current.maxAncestryRenderIndex = savedBranchProgress;
|
|
13435
|
+
const newStack = [
|
|
13436
|
+
...branchStack,
|
|
13437
|
+
{
|
|
13438
|
+
nodeId,
|
|
13439
|
+
branchId: branchToOpen.id,
|
|
13440
|
+
savedMaxIndex: parentIndexToSave,
|
|
13441
|
+
entryDirection: direction
|
|
13442
|
+
}
|
|
13443
|
+
];
|
|
13444
|
+
setReadingMode((prev) => ({
|
|
13445
|
+
...prev,
|
|
13446
|
+
branchStack: newStack,
|
|
13447
|
+
initialSectionId: null
|
|
13448
|
+
}));
|
|
13449
|
+
const branchAncestryObj = {
|
|
13450
|
+
ancestry_id: branchToOpen.id,
|
|
13451
|
+
ancestral_node: branchToOpen.tree.node.id,
|
|
13452
|
+
name: branchToOpen.name,
|
|
13453
|
+
description: branchToOpen.description,
|
|
13454
|
+
description_sections: branchToOpen.description_sections,
|
|
13455
|
+
tree: branchToOpen.tree,
|
|
13456
|
+
_originNodeId: nodeId,
|
|
13457
|
+
_branchDirection: direction
|
|
13458
|
+
};
|
|
13459
|
+
const allowedIds = /* @__PURE__ */ new Set(["preamble", 0, "0"]);
|
|
13460
|
+
const branchSections = parseDescriptionSections(
|
|
13461
|
+
branchToOpen.description || "",
|
|
13462
|
+
branchToOpen.description_sections || []
|
|
13463
|
+
);
|
|
13464
|
+
for (let i = 0; i <= savedBranchProgress; i++) {
|
|
13465
|
+
if (branchSections[i]) {
|
|
13466
|
+
if (branchSections[i].id)
|
|
13467
|
+
allowedIds.add(String(branchSections[i].id));
|
|
13468
|
+
if (branchSections[i].numericId !== void 0 && branchSections[i].numericId !== null) {
|
|
13469
|
+
allowedIds.add(branchSections[i].numericId);
|
|
13470
|
+
allowedIds.add(String(branchSections[i].numericId));
|
|
13471
|
+
}
|
|
13472
|
+
}
|
|
12776
13473
|
}
|
|
13474
|
+
const rotation = newStack.reduce((acc, step) => {
|
|
13475
|
+
return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
|
|
13476
|
+
}, 0);
|
|
13477
|
+
const initialFocusId = "preamble";
|
|
13478
|
+
handleRenderAncestry(
|
|
13479
|
+
branchAncestryObj,
|
|
13480
|
+
allowedIds,
|
|
13481
|
+
initialFocusId,
|
|
13482
|
+
rotation,
|
|
13483
|
+
false
|
|
13484
|
+
);
|
|
12777
13485
|
}
|
|
12778
13486
|
}
|
|
12779
|
-
|
|
12780
|
-
|
|
12781
|
-
|
|
12782
|
-
|
|
12783
|
-
|
|
12784
|
-
|
|
12785
|
-
|
|
12786
|
-
|
|
12787
|
-
|
|
12788
|
-
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
|
|
12792
|
-
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
13487
|
+
} else if (action === "back") {
|
|
13488
|
+
if (branchStack.length === 0) return;
|
|
13489
|
+
const newStack = [...branchStack];
|
|
13490
|
+
const popped = newStack.pop();
|
|
13491
|
+
if (popped && popped.branchId) {
|
|
13492
|
+
stateRef.current.readMode.progressMap[popped.branchId] = stateRef.current.readMode.currentMaxIndex;
|
|
13493
|
+
}
|
|
13494
|
+
const parentSavedIndex = popped.savedMaxIndex !== void 0 ? popped.savedMaxIndex : 0;
|
|
13495
|
+
stateRef.current.readMode.currentMaxIndex = parentSavedIndex;
|
|
13496
|
+
stateRef.current.maxAncestryRenderIndex = parentSavedIndex;
|
|
13497
|
+
let targetTreeToRender = fullTree;
|
|
13498
|
+
let targetAncestryInfo = ancestry;
|
|
13499
|
+
if (newStack.length > 0) {
|
|
13500
|
+
let ptr = fullTree;
|
|
13501
|
+
for (const step of newStack) {
|
|
13502
|
+
const found = findNodePath3(ptr, step.nodeId);
|
|
13503
|
+
if (found && found.node.parallel_branches) {
|
|
13504
|
+
const branch = found.node.parallel_branches.find(
|
|
13505
|
+
(b) => b.id === step.branchId
|
|
13506
|
+
);
|
|
13507
|
+
if (branch) {
|
|
13508
|
+
ptr = branch.tree;
|
|
13509
|
+
targetAncestryInfo = {
|
|
13510
|
+
...branch,
|
|
13511
|
+
ancestry_id: branch.id,
|
|
13512
|
+
ancestral_node: branch.tree.node.id
|
|
13513
|
+
};
|
|
13514
|
+
}
|
|
13515
|
+
}
|
|
13516
|
+
}
|
|
13517
|
+
targetTreeToRender = ptr;
|
|
13518
|
+
}
|
|
13519
|
+
const activeParentStackItem = newStack.length > 0 ? newStack[newStack.length - 1] : null;
|
|
13520
|
+
const parentAncestryObj = {
|
|
13521
|
+
...targetAncestryInfo,
|
|
13522
|
+
tree: targetTreeToRender,
|
|
13523
|
+
_originNodeId: activeParentStackItem ? activeParentStackItem.nodeId : null,
|
|
13524
|
+
_branchDirection: activeParentStackItem ? activeParentStackItem.entryDirection : null
|
|
13525
|
+
};
|
|
13526
|
+
const descriptionText = targetAncestryInfo.description || "";
|
|
13527
|
+
const savedSections = targetAncestryInfo.description_sections || [];
|
|
13528
|
+
const sections = parseDescriptionSections(
|
|
13529
|
+
descriptionText,
|
|
13530
|
+
savedSections
|
|
13531
|
+
);
|
|
13532
|
+
let focusTargetId = null;
|
|
13533
|
+
if (popped && popped.nodeId) {
|
|
13534
|
+
const mentionTag = `[[MENTION:node:${popped.nodeId}]]`;
|
|
13535
|
+
for (let i = 0; i < sections.length; i++) {
|
|
13536
|
+
if (sections[i].content && sections[i].content.includes(mentionTag)) {
|
|
13537
|
+
const section = sections[i];
|
|
13538
|
+
focusTargetId = section.numericId !== void 0 ? section.numericId : section.id;
|
|
13539
|
+
break;
|
|
13540
|
+
}
|
|
12799
13541
|
}
|
|
12800
13542
|
}
|
|
12801
|
-
|
|
12802
|
-
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
|
|
12806
|
-
|
|
12807
|
-
|
|
12808
|
-
|
|
12809
|
-
|
|
12810
|
-
|
|
12811
|
-
|
|
13543
|
+
const allowedIds = /* @__PURE__ */ new Set();
|
|
13544
|
+
allowedIds.add("preamble");
|
|
13545
|
+
allowedIds.add(0);
|
|
13546
|
+
allowedIds.add("0");
|
|
13547
|
+
for (let i = 0; i <= parentSavedIndex; i++) {
|
|
13548
|
+
if (sections[i]) {
|
|
13549
|
+
if (sections[i].id) allowedIds.add(String(sections[i].id));
|
|
13550
|
+
if (sections[i].numericId !== void 0 && sections[i].numericId !== null) {
|
|
13551
|
+
allowedIds.add(sections[i].numericId);
|
|
13552
|
+
allowedIds.add(String(sections[i].numericId));
|
|
13553
|
+
}
|
|
12812
13554
|
}
|
|
12813
13555
|
}
|
|
12814
|
-
|
|
12815
|
-
|
|
12816
|
-
|
|
12817
|
-
|
|
12818
|
-
|
|
12819
|
-
|
|
12820
|
-
|
|
12821
|
-
|
|
12822
|
-
|
|
13556
|
+
const rotation = newStack.reduce((acc, step) => {
|
|
13557
|
+
return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
|
|
13558
|
+
}, 0);
|
|
13559
|
+
handleRenderAncestry(
|
|
13560
|
+
parentAncestryObj,
|
|
13561
|
+
allowedIds,
|
|
13562
|
+
focusTargetId,
|
|
13563
|
+
rotation,
|
|
13564
|
+
false
|
|
13565
|
+
);
|
|
13566
|
+
if (popped && popped.nodeId) {
|
|
13567
|
+
const nodeMesh = stateRef.current.nodeObjects[String(popped.nodeId)];
|
|
13568
|
+
if (nodeMesh) {
|
|
13569
|
+
tweenToTarget(nodeMesh);
|
|
13570
|
+
}
|
|
12823
13571
|
}
|
|
13572
|
+
setReadingMode((prev) => ({
|
|
13573
|
+
...prev,
|
|
13574
|
+
branchStack: newStack,
|
|
13575
|
+
initialSectionId: focusTargetId
|
|
13576
|
+
}));
|
|
12824
13577
|
}
|
|
12825
|
-
|
|
12826
|
-
|
|
12827
|
-
|
|
12828
|
-
initialSectionId: focusTargetId
|
|
12829
|
-
}));
|
|
12830
|
-
}
|
|
12831
|
-
}, [readingMode, handleRenderAncestry, buildFullAncestryTree, tweenToTarget]);
|
|
13578
|
+
},
|
|
13579
|
+
[readingMode, handleRenderAncestry, buildFullAncestryTree, tweenToTarget]
|
|
13580
|
+
);
|
|
12832
13581
|
const handleReadModeHighlight = useCallback4((nodeId) => {
|
|
12833
13582
|
if (stateRef.current.highlightedNodeId !== nodeId) {
|
|
12834
13583
|
stateRef.current.highlightedNodeId = nodeId;
|
|
@@ -12836,7 +13585,8 @@ function XViewScene({
|
|
|
12836
13585
|
setHighlightedNodeId(nodeId);
|
|
12837
13586
|
}, []);
|
|
12838
13587
|
const activeNodeBranches = useMemo12(() => {
|
|
12839
|
-
if (!highlightedNodeId || !readingMode.ancestry || !readingMode.ancestry.tree)
|
|
13588
|
+
if (!highlightedNodeId || !readingMode.ancestry || !readingMode.ancestry.tree)
|
|
13589
|
+
return null;
|
|
12840
13590
|
const fullTree = buildFullAncestryTree(
|
|
12841
13591
|
readingMode.ancestry.tree,
|
|
12842
13592
|
Object.values(parentDataRef.current).flatMap((f) => f.nodes),
|
|
@@ -12871,7 +13621,13 @@ function XViewScene({
|
|
|
12871
13621
|
};
|
|
12872
13622
|
}
|
|
12873
13623
|
return null;
|
|
12874
|
-
}, [
|
|
13624
|
+
}, [
|
|
13625
|
+
highlightedNodeId,
|
|
13626
|
+
readingMode.ancestry,
|
|
13627
|
+
buildFullAncestryTree,
|
|
13628
|
+
readingMode.branchStack,
|
|
13629
|
+
ancestryDataRef.current
|
|
13630
|
+
]);
|
|
12875
13631
|
const backNavigationInfo = useMemo12(() => {
|
|
12876
13632
|
const { branchStack } = readingMode;
|
|
12877
13633
|
if (!branchStack || branchStack.length === 0) return null;
|
|
@@ -12907,7 +13663,9 @@ function XViewScene({
|
|
|
12907
13663
|
for (const step of branchStack) {
|
|
12908
13664
|
const found = findNodePath3(currentPtr, step.nodeId);
|
|
12909
13665
|
if (found && found.node.parallel_branches) {
|
|
12910
|
-
const branch = found.node.parallel_branches.find(
|
|
13666
|
+
const branch = found.node.parallel_branches.find(
|
|
13667
|
+
(b) => b.id === step.branchId
|
|
13668
|
+
);
|
|
12911
13669
|
if (branch) {
|
|
12912
13670
|
currentPtr = branch.tree;
|
|
12913
13671
|
currentMeta = branch;
|
|
@@ -12928,17 +13686,26 @@ function XViewScene({
|
|
|
12928
13686
|
if (!readingMode.isActive || !readingMode.ancestry || !readingMode.ancestry.abstraction_tree) {
|
|
12929
13687
|
return null;
|
|
12930
13688
|
}
|
|
12931
|
-
const allNodes = Object.values(parentDataRef.current || {}).flatMap(
|
|
13689
|
+
const allNodes = Object.values(parentDataRef.current || {}).flatMap(
|
|
13690
|
+
(f) => f.nodes || []
|
|
13691
|
+
);
|
|
12932
13692
|
const allAncestries = ancestryDataRef.current || [];
|
|
12933
13693
|
return buildFullAncestryTree(
|
|
12934
13694
|
readingMode.ancestry.abstraction_tree,
|
|
12935
13695
|
allNodes,
|
|
12936
13696
|
allAncestries
|
|
12937
13697
|
);
|
|
12938
|
-
}, [
|
|
13698
|
+
}, [
|
|
13699
|
+
readingMode.isActive,
|
|
13700
|
+
readingMode.ancestry,
|
|
13701
|
+
buildFullAncestryTree,
|
|
13702
|
+
sceneVersion
|
|
13703
|
+
]);
|
|
12939
13704
|
const handleStartReadingAncestry = useCallback4(
|
|
12940
|
-
async (ancestryObject) => {
|
|
12941
|
-
setContextMenu(
|
|
13705
|
+
async (ancestryObject, renderMode = "full") => {
|
|
13706
|
+
setContextMenu(
|
|
13707
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
13708
|
+
);
|
|
12942
13709
|
if (!ancestryObject || !ancestryObject.tree) {
|
|
12943
13710
|
console.warn("Ancestralidade inv\xE1lida para leitura.");
|
|
12944
13711
|
return;
|
|
@@ -12951,14 +13718,20 @@ function XViewScene({
|
|
|
12951
13718
|
const hasDescription = ancestryObject.description && ancestryObject.description.trim() !== "";
|
|
12952
13719
|
const hasMainTreeNodes = ancestryObject.tree.children && ancestryObject.tree.children.length > 0;
|
|
12953
13720
|
const hasAbstractionNodes = ancestryObject.abstraction_tree && ancestryObject.abstraction_tree.children && ancestryObject.abstraction_tree.children.length > 0;
|
|
12954
|
-
const
|
|
13721
|
+
const isFull = renderMode === "full";
|
|
13722
|
+
const isAncestryOnly = renderMode === "ancestry_only";
|
|
13723
|
+
const isAbstractionOnly = renderMode === "abstraction_only";
|
|
13724
|
+
let shouldRenderAbstraction = isAbstractionOnly;
|
|
13725
|
+
if (isFull) {
|
|
13726
|
+
shouldRenderAbstraction = !hasDescription && !hasMainTreeNodes && hasAbstractionNodes;
|
|
13727
|
+
}
|
|
12955
13728
|
setReadingMode({
|
|
12956
|
-
isActive:
|
|
13729
|
+
isActive: isFull ? hasDescription : false,
|
|
12957
13730
|
ancestry: ancestryObject,
|
|
12958
13731
|
branchStack: [],
|
|
12959
|
-
autoAbstraction:
|
|
13732
|
+
autoAbstraction: shouldRenderAbstraction
|
|
12960
13733
|
});
|
|
12961
|
-
if (
|
|
13734
|
+
if (shouldRenderAbstraction) {
|
|
12962
13735
|
handleRenderAbstractionTree(ancestryObject, null);
|
|
12963
13736
|
} else {
|
|
12964
13737
|
const initialSections = /* @__PURE__ */ new Set(["preamble", 0, "0"]);
|
|
@@ -12971,148 +13744,190 @@ function XViewScene({
|
|
|
12971
13744
|
},
|
|
12972
13745
|
[handleRenderAncestry, handleRenderAbstractionTree]
|
|
12973
13746
|
);
|
|
12974
|
-
const handleReadModeSectionChange = useCallback4(
|
|
12975
|
-
|
|
12976
|
-
|
|
12977
|
-
|
|
12978
|
-
|
|
12979
|
-
|
|
12980
|
-
|
|
12981
|
-
|
|
12982
|
-
|
|
12983
|
-
|
|
12984
|
-
|
|
12985
|
-
|
|
12986
|
-
|
|
12987
|
-
|
|
12988
|
-
|
|
12989
|
-
|
|
12990
|
-
|
|
12991
|
-
|
|
12992
|
-
|
|
12993
|
-
|
|
12994
|
-
|
|
13747
|
+
const handleReadModeSectionChange = useCallback4(
|
|
13748
|
+
(activeSectionId) => {
|
|
13749
|
+
const { ancestry, branchStack } = readingMode;
|
|
13750
|
+
if (!ancestry || !readingMode.isActive) return;
|
|
13751
|
+
let targetObj = ancestry;
|
|
13752
|
+
let targetTree = ancestry.tree;
|
|
13753
|
+
if (branchStack.length > 0) {
|
|
13754
|
+
const allNodes = Object.values(parentDataRef.current).flatMap(
|
|
13755
|
+
(f) => f.nodes
|
|
13756
|
+
);
|
|
13757
|
+
const fullTree = buildFullAncestryTree(
|
|
13758
|
+
ancestry.tree,
|
|
13759
|
+
allNodes,
|
|
13760
|
+
ancestryDataRef.current
|
|
13761
|
+
);
|
|
13762
|
+
let currentPtr = fullTree;
|
|
13763
|
+
for (const step of branchStack) {
|
|
13764
|
+
const found = findNodePath3(currentPtr, step.nodeId);
|
|
13765
|
+
if (found && found.node && found.node.parallel_branches) {
|
|
13766
|
+
const branch = found.node.parallel_branches.find(
|
|
13767
|
+
(b) => b.id === step.branchId
|
|
13768
|
+
);
|
|
13769
|
+
if (branch) {
|
|
13770
|
+
targetObj = branch;
|
|
13771
|
+
targetTree = branch.tree;
|
|
13772
|
+
currentPtr = branch.tree;
|
|
13773
|
+
}
|
|
12995
13774
|
}
|
|
12996
13775
|
}
|
|
12997
13776
|
}
|
|
12998
|
-
|
|
12999
|
-
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13003
|
-
|
|
13004
|
-
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13008
|
-
|
|
13009
|
-
|
|
13010
|
-
|
|
13011
|
-
|
|
13012
|
-
|
|
13013
|
-
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
allowedIds.add(String(sections[i].
|
|
13777
|
+
const descriptionText = targetObj.description || "";
|
|
13778
|
+
const savedSections = targetObj.description_sections || [];
|
|
13779
|
+
const sections = parseDescriptionSections(descriptionText, savedSections);
|
|
13780
|
+
let activeIndex = sections.findIndex(
|
|
13781
|
+
(s) => String(s.id) === String(activeSectionId)
|
|
13782
|
+
);
|
|
13783
|
+
if (activeIndex === -1 && !isNaN(parseInt(activeSectionId))) {
|
|
13784
|
+
activeIndex = sections.findIndex(
|
|
13785
|
+
(s) => s.numericId === parseInt(activeSectionId)
|
|
13786
|
+
);
|
|
13787
|
+
}
|
|
13788
|
+
if (activeIndex === -1) activeIndex = 0;
|
|
13789
|
+
stateRef.current.readMode.currentMaxIndex = activeIndex;
|
|
13790
|
+
stateRef.current.maxAncestryRenderIndex = activeIndex;
|
|
13791
|
+
const renderLimitIndex = stateRef.current.maxAncestryRenderIndex;
|
|
13792
|
+
const allowedIds = /* @__PURE__ */ new Set();
|
|
13793
|
+
allowedIds.add("preamble");
|
|
13794
|
+
allowedIds.add(0);
|
|
13795
|
+
allowedIds.add("0");
|
|
13796
|
+
for (let i = 0; i <= renderLimitIndex; i++) {
|
|
13797
|
+
if (sections[i]) {
|
|
13798
|
+
if (sections[i].id) allowedIds.add(String(sections[i].id));
|
|
13799
|
+
if (sections[i].numericId !== void 0 && sections[i].numericId !== null) {
|
|
13800
|
+
allowedIds.add(sections[i].numericId);
|
|
13801
|
+
allowedIds.add(String(sections[i].numericId));
|
|
13802
|
+
}
|
|
13020
13803
|
}
|
|
13021
13804
|
}
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
13025
|
-
|
|
13026
|
-
|
|
13027
|
-
|
|
13028
|
-
|
|
13029
|
-
|
|
13030
|
-
|
|
13031
|
-
|
|
13032
|
-
|
|
13033
|
-
|
|
13034
|
-
|
|
13035
|
-
|
|
13036
|
-
|
|
13037
|
-
|
|
13038
|
-
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13805
|
+
const activeSection = sections[activeIndex];
|
|
13806
|
+
let focusTargetId = activeSectionId;
|
|
13807
|
+
if (activeSection && activeSection.numericId !== void 0 && activeSection.numericId !== null) {
|
|
13808
|
+
focusTargetId = activeSection.numericId;
|
|
13809
|
+
}
|
|
13810
|
+
const currentStackItem = branchStack.length > 0 ? branchStack[branchStack.length - 1] : null;
|
|
13811
|
+
const renderPayload = {
|
|
13812
|
+
...targetObj,
|
|
13813
|
+
ancestry_id: targetObj.ancestry_id || targetObj.id,
|
|
13814
|
+
ancestral_node: targetTree.node ? targetTree.node.id : targetTree.node_id,
|
|
13815
|
+
tree: targetTree,
|
|
13816
|
+
_originNodeId: currentStackItem ? currentStackItem.nodeId : null,
|
|
13817
|
+
_branchDirection: currentStackItem ? currentStackItem.entryDirection : null,
|
|
13818
|
+
_forceUpdate: true
|
|
13819
|
+
};
|
|
13820
|
+
const rotation = branchStack.reduce((acc, step) => {
|
|
13821
|
+
return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
|
|
13822
|
+
}, 0);
|
|
13823
|
+
handleRenderAncestry(renderPayload, allowedIds, focusTargetId, rotation);
|
|
13824
|
+
},
|
|
13825
|
+
[
|
|
13826
|
+
readingMode,
|
|
13827
|
+
handleRenderAncestry,
|
|
13828
|
+
buildFullAncestryTree,
|
|
13829
|
+
ancestryDataRef.current
|
|
13830
|
+
]
|
|
13831
|
+
);
|
|
13043
13832
|
const handleCloseReadMode = useCallback4(() => {
|
|
13044
13833
|
setReadingMode({ isActive: false, ancestry: null, branchStack: [] });
|
|
13045
13834
|
}, []);
|
|
13046
|
-
const handleAncestrySectionChange = useCallback4(
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
|
|
13059
|
-
|
|
13060
|
-
|
|
13061
|
-
|
|
13062
|
-
|
|
13063
|
-
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13067
|
-
|
|
13068
|
-
|
|
13069
|
-
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
|
|
13074
|
-
|
|
13075
|
-
|
|
13076
|
-
|
|
13077
|
-
|
|
13078
|
-
|
|
13079
|
-
|
|
13080
|
-
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
|
|
13084
|
-
|
|
13085
|
-
|
|
13835
|
+
const handleAncestrySectionChange = useCallback4(
|
|
13836
|
+
(activeSectionId, ancestryOverride = null, rotation = 0) => {
|
|
13837
|
+
var _a2, _b2;
|
|
13838
|
+
const currentMode = stateRef.current.ancestry;
|
|
13839
|
+
let targetObj = ancestryOverride;
|
|
13840
|
+
if (!targetObj) {
|
|
13841
|
+
const currentAncestryId = currentMode.currentAncestryId;
|
|
13842
|
+
const ancestryObj = (ancestryDataRef.current || []).find(
|
|
13843
|
+
(a) => String(a.ancestry_id) === String(currentAncestryId)
|
|
13844
|
+
);
|
|
13845
|
+
targetObj = ancestryObj || (currentMode.isActive ? {
|
|
13846
|
+
...currentMode,
|
|
13847
|
+
ancestry_id: "temp_creating",
|
|
13848
|
+
ancestral_node: (_b2 = (_a2 = currentMode.tree) == null ? void 0 : _a2.node) == null ? void 0 : _b2.id
|
|
13849
|
+
} : null);
|
|
13850
|
+
}
|
|
13851
|
+
if (!targetObj) return;
|
|
13852
|
+
const targetId = targetObj.ancestry_id || targetObj.id;
|
|
13853
|
+
if (stateRef.current.lastRenderedAncestryId !== targetId) {
|
|
13854
|
+
stateRef.current.maxAncestryRenderIndex = 0;
|
|
13855
|
+
stateRef.current.lastRenderedAncestryId = targetId;
|
|
13856
|
+
}
|
|
13857
|
+
const descriptionText = (ancestryOverride ? targetObj.description : currentMode.ancestryDescription) || targetObj.description || "";
|
|
13858
|
+
const savedSections = (ancestryOverride ? targetObj.description_sections : currentMode.ancestryDescriptionSections) || targetObj.description_sections || [];
|
|
13859
|
+
const sections = parseDescriptionSections(descriptionText, savedSections);
|
|
13860
|
+
let activeIndex = sections.findIndex(
|
|
13861
|
+
(s) => String(s.id) === String(activeSectionId)
|
|
13862
|
+
);
|
|
13863
|
+
if (activeIndex === -1 && !isNaN(parseInt(activeSectionId))) {
|
|
13864
|
+
activeIndex = sections.findIndex(
|
|
13865
|
+
(s) => s.numericId === parseInt(activeSectionId)
|
|
13866
|
+
);
|
|
13867
|
+
}
|
|
13868
|
+
if (activeIndex === -1) activeIndex = 0;
|
|
13869
|
+
if (stateRef.current.maxAncestryRenderIndex === void 0)
|
|
13870
|
+
stateRef.current.maxAncestryRenderIndex = 0;
|
|
13871
|
+
stateRef.current.maxAncestryRenderIndex = activeIndex;
|
|
13872
|
+
const renderLimitIndex = stateRef.current.maxAncestryRenderIndex;
|
|
13873
|
+
const allowedIds = /* @__PURE__ */ new Set();
|
|
13874
|
+
allowedIds.add("preamble");
|
|
13875
|
+
allowedIds.add(0);
|
|
13876
|
+
allowedIds.add("0");
|
|
13877
|
+
for (let i = 0; i <= renderLimitIndex; i++) {
|
|
13878
|
+
if (sections[i]) {
|
|
13879
|
+
if (sections[i].id) allowedIds.add(String(sections[i].id));
|
|
13880
|
+
if (sections[i].numericId !== void 0 && sections[i].numericId !== null) {
|
|
13881
|
+
allowedIds.add(sections[i].numericId);
|
|
13882
|
+
allowedIds.add(String(sections[i].numericId));
|
|
13883
|
+
}
|
|
13086
13884
|
}
|
|
13087
13885
|
}
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
|
|
13886
|
+
const activeSection = sections[activeIndex];
|
|
13887
|
+
let focusTargetId = activeSectionId;
|
|
13888
|
+
if (activeSection && activeSection.numericId !== void 0 && activeSection.numericId !== null) {
|
|
13889
|
+
focusTargetId = activeSection.numericId;
|
|
13890
|
+
}
|
|
13891
|
+
const treeToRender = ancestryOverride ? ancestryOverride.tree : currentMode.isActive && currentMode.tree ? currentMode.tree : targetObj.tree;
|
|
13892
|
+
const renderPayload = { ...targetObj, tree: treeToRender };
|
|
13893
|
+
handleRenderAncestry(renderPayload, allowedIds, focusTargetId, rotation);
|
|
13894
|
+
},
|
|
13895
|
+
[handleRenderAncestry]
|
|
13896
|
+
);
|
|
13098
13897
|
const handleEditAncestry = useCallback4(
|
|
13099
13898
|
async (ancestryObject) => {
|
|
13100
|
-
setContextMenu(
|
|
13899
|
+
setContextMenu(
|
|
13900
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
13901
|
+
);
|
|
13101
13902
|
if (!ancestryObject || !ancestryObject.tree) {
|
|
13102
|
-
alert(
|
|
13903
|
+
alert(
|
|
13904
|
+
"N\xE3o foi poss\xEDvel carregar os dados desta ancestralidade para edi\xE7\xE3o."
|
|
13905
|
+
);
|
|
13103
13906
|
return;
|
|
13104
13907
|
}
|
|
13105
13908
|
stateRef.current.maxAncestryRenderIndex = 0;
|
|
13106
13909
|
const initialSections = /* @__PURE__ */ new Set(["preamble", 0]);
|
|
13107
13910
|
await handleRenderAncestry(ancestryObject, initialSections);
|
|
13108
|
-
const allParentNodes = Object.values(parentDataRef.current).flatMap(
|
|
13911
|
+
const allParentNodes = Object.values(parentDataRef.current).flatMap(
|
|
13912
|
+
(fileData) => fileData.nodes
|
|
13913
|
+
);
|
|
13109
13914
|
const allAncestries = ancestryDataRef.current || [];
|
|
13110
|
-
const fullTree = buildFullAncestryTree(
|
|
13915
|
+
const fullTree = buildFullAncestryTree(
|
|
13916
|
+
ancestryObject.tree,
|
|
13917
|
+
allParentNodes,
|
|
13918
|
+
allAncestries
|
|
13919
|
+
);
|
|
13111
13920
|
if (!fullTree) {
|
|
13112
|
-
alert(
|
|
13921
|
+
alert(
|
|
13922
|
+
"Falha ao reconstruir a \xE1rvore de ancestralidade. Alguns Nodes podem estar faltando."
|
|
13923
|
+
);
|
|
13113
13924
|
return;
|
|
13114
13925
|
}
|
|
13115
|
-
const fullAbstractionTree = ancestryObject.abstraction_tree ? buildFullAncestryTree(
|
|
13926
|
+
const fullAbstractionTree = ancestryObject.abstraction_tree ? buildFullAncestryTree(
|
|
13927
|
+
ancestryObject.abstraction_tree,
|
|
13928
|
+
allParentNodes,
|
|
13929
|
+
allAncestries
|
|
13930
|
+
) : { node: fullTree.node, children: [] };
|
|
13116
13931
|
setAncestryMode({
|
|
13117
13932
|
isActive: true,
|
|
13118
13933
|
...ancestryObject,
|
|
@@ -13164,7 +13979,9 @@ function XViewScene({
|
|
|
13164
13979
|
const treeToUse = treeOverride || ancestryMode.tree;
|
|
13165
13980
|
const { isEditMode, currentAncestryId } = ancestryMode;
|
|
13166
13981
|
if (!treeToUse || !treeToUse.node) {
|
|
13167
|
-
alert(
|
|
13982
|
+
alert(
|
|
13983
|
+
"Erro: A estrutura da ancestralidade \xE9 inv\xE1lida (Node raiz ausente)."
|
|
13984
|
+
);
|
|
13168
13985
|
return;
|
|
13169
13986
|
}
|
|
13170
13987
|
if (!save_view_data || !stateRef.current.nodeIdToParentFileMap) return;
|
|
@@ -13197,14 +14014,18 @@ function XViewScene({
|
|
|
13197
14014
|
};
|
|
13198
14015
|
};
|
|
13199
14016
|
const treeWithIds = convertTreeToIds(treeToUse);
|
|
13200
|
-
const abstractionTreeWithIds = convertTreeToIds(
|
|
14017
|
+
const abstractionTreeWithIds = convertTreeToIds(
|
|
14018
|
+
ancestryMode.abstraction_tree
|
|
14019
|
+
);
|
|
13201
14020
|
if (!treeWithIds) {
|
|
13202
14021
|
alert("Erro ao processar a \xE1rvore da ancestralidade.");
|
|
13203
14022
|
return;
|
|
13204
14023
|
}
|
|
13205
14024
|
let originalAncestryObj = null;
|
|
13206
14025
|
if (isEditMode && currentAncestryId) {
|
|
13207
|
-
originalAncestryObj = (ancestryDataRef.current || []).find(
|
|
14026
|
+
originalAncestryObj = (ancestryDataRef.current || []).find(
|
|
14027
|
+
(anc) => String(anc.ancestry_id) === String(currentAncestryId)
|
|
14028
|
+
);
|
|
13208
14029
|
}
|
|
13209
14030
|
const ancestryObjectToSave = {
|
|
13210
14031
|
ancestry_id: isEditMode && currentAncestryId ? currentAncestryId : `${short2.generate()}`,
|
|
@@ -13287,14 +14108,20 @@ function XViewScene({
|
|
|
13287
14108
|
try {
|
|
13288
14109
|
if (isExternalSave) {
|
|
13289
14110
|
try {
|
|
13290
|
-
const remoteResponse = await get_ancestry_file(
|
|
14111
|
+
const remoteResponse = await get_ancestry_file(
|
|
14112
|
+
targetFileIdForCache,
|
|
14113
|
+
targetOwnerIdForCache
|
|
14114
|
+
);
|
|
13291
14115
|
if (remoteResponse.success && Array.isArray(remoteResponse.data)) {
|
|
13292
14116
|
masterAncestryList = remoteResponse.data;
|
|
13293
14117
|
} else {
|
|
13294
14118
|
masterAncestryList = [];
|
|
13295
14119
|
}
|
|
13296
14120
|
} catch (fetchErr) {
|
|
13297
|
-
console.warn(
|
|
14121
|
+
console.warn(
|
|
14122
|
+
"Arquivo de destino n\xE3o existe ou erro ao buscar, criando novo:",
|
|
14123
|
+
fetchErr
|
|
14124
|
+
);
|
|
13298
14125
|
masterAncestryList = [];
|
|
13299
14126
|
}
|
|
13300
14127
|
} else {
|
|
@@ -13314,7 +14141,9 @@ function XViewScene({
|
|
|
13314
14141
|
const result = await save_view_data(finalSaveUrl, masterAncestryList);
|
|
13315
14142
|
if (result.success) {
|
|
13316
14143
|
const localList = [...ancestryDataRef.current || []];
|
|
13317
|
-
const localIndex = localList.findIndex(
|
|
14144
|
+
const localIndex = localList.findIndex(
|
|
14145
|
+
(a) => String(a.ancestry_id) === String(ancestryObjectToSave.ancestry_id)
|
|
14146
|
+
);
|
|
13318
14147
|
if (localIndex !== -1) {
|
|
13319
14148
|
localList[localIndex] = ancestryObjectToSave;
|
|
13320
14149
|
} else {
|
|
@@ -13342,11 +14171,29 @@ function XViewScene({
|
|
|
13342
14171
|
return;
|
|
13343
14172
|
}
|
|
13344
14173
|
if (!keepOpen) {
|
|
13345
|
-
setAncestryMode({
|
|
14174
|
+
setAncestryMode({
|
|
14175
|
+
isActive: false,
|
|
14176
|
+
tree: null,
|
|
14177
|
+
selectedParentId: null,
|
|
14178
|
+
isEditMode: false,
|
|
14179
|
+
currentAncestryId: null,
|
|
14180
|
+
ancestryName: "",
|
|
14181
|
+
ancestryDescription: "",
|
|
14182
|
+
ancestryDescriptionSections: [],
|
|
14183
|
+
isAddingNodes: false
|
|
14184
|
+
});
|
|
13346
14185
|
if (mountRef.current) mountRef.current.style.cursor = "grab";
|
|
13347
14186
|
}
|
|
13348
14187
|
},
|
|
13349
|
-
[
|
|
14188
|
+
[
|
|
14189
|
+
ancestryMode,
|
|
14190
|
+
ancestry_save_url,
|
|
14191
|
+
handleRenderAncestry,
|
|
14192
|
+
save_view_data,
|
|
14193
|
+
sceneConfigId,
|
|
14194
|
+
ownerId,
|
|
14195
|
+
get_ancestry_file
|
|
14196
|
+
]
|
|
13350
14197
|
);
|
|
13351
14198
|
const handleOpenAncestryRelEditor = (path, currentData) => {
|
|
13352
14199
|
setEditingAncestryRel({ visible: true, data: currentData, path });
|
|
@@ -13376,7 +14223,9 @@ function XViewScene({
|
|
|
13376
14223
|
if (ancestryToDelete && delete_file_action) {
|
|
13377
14224
|
const urls = extractFileUrlsFromProperties(ancestryToDelete);
|
|
13378
14225
|
if (urls.length > 0) {
|
|
13379
|
-
Promise.all(urls.map((url) => delete_file_action(url))).catch(
|
|
14226
|
+
Promise.all(urls.map((url) => delete_file_action(url))).catch(
|
|
14227
|
+
(err) => console.error("Erro ao deletar arquivos da ancestralidade:", err)
|
|
14228
|
+
);
|
|
13380
14229
|
}
|
|
13381
14230
|
}
|
|
13382
14231
|
if (!ancestryToDelete) {
|
|
@@ -13386,7 +14235,9 @@ function XViewScene({
|
|
|
13386
14235
|
const sourceFileId = ancestryToDelete._source_file_id;
|
|
13387
14236
|
const sourceOwnerId = ancestryToDelete._source_owner_id;
|
|
13388
14237
|
if (!sourceFileId || !sourceOwnerId) {
|
|
13389
|
-
alert(
|
|
14238
|
+
alert(
|
|
14239
|
+
"N\xE3o foi poss\xEDvel identificar o arquivo de origem desta ancestralidade."
|
|
14240
|
+
);
|
|
13390
14241
|
return;
|
|
13391
14242
|
}
|
|
13392
14243
|
const finalSaveUrl = `x_view_ancestry/${sourceOwnerId}/${sourceFileId}`;
|
|
@@ -13394,13 +14245,18 @@ function XViewScene({
|
|
|
13394
14245
|
(anc) => anc._source_file_id === sourceFileId && String(anc.ancestry_id) !== String(ancestryIdToDelete)
|
|
13395
14246
|
);
|
|
13396
14247
|
try {
|
|
13397
|
-
const result = await save_view_data(
|
|
14248
|
+
const result = await save_view_data(
|
|
14249
|
+
finalSaveUrl,
|
|
14250
|
+
updatedAncestriesForFile
|
|
14251
|
+
);
|
|
13398
14252
|
if (result.success) {
|
|
13399
14253
|
ancestryDataRef.current = (ancestryDataRef.current || []).filter(
|
|
13400
14254
|
(ancestry) => String(ancestry.ancestry_id) !== String(ancestryIdToDelete)
|
|
13401
14255
|
);
|
|
13402
14256
|
const { renderedAncestries, ancestryGroup } = stateRef.current;
|
|
13403
|
-
const renderIndex = renderedAncestries.findIndex(
|
|
14257
|
+
const renderIndex = renderedAncestries.findIndex(
|
|
14258
|
+
(a) => String(a.id) === String(ancestryIdToDelete)
|
|
14259
|
+
);
|
|
13404
14260
|
if (renderIndex !== -1) {
|
|
13405
14261
|
const toRemove = renderedAncestries[renderIndex];
|
|
13406
14262
|
toRemove.lines.forEach((line) => {
|
|
@@ -13409,18 +14265,29 @@ function XViewScene({
|
|
|
13409
14265
|
if (line.material) line.material.dispose();
|
|
13410
14266
|
});
|
|
13411
14267
|
renderedAncestries.splice(renderIndex, 1);
|
|
13412
|
-
stateRef.current.ancestryLinks = renderedAncestries.flatMap(
|
|
14268
|
+
stateRef.current.ancestryLinks = renderedAncestries.flatMap(
|
|
14269
|
+
(a) => a.lines
|
|
14270
|
+
);
|
|
13413
14271
|
}
|
|
13414
14272
|
setSceneVersion((v) => v + 1);
|
|
13415
14273
|
} else {
|
|
13416
|
-
throw new Error(
|
|
14274
|
+
throw new Error(
|
|
14275
|
+
result.error || "Ocorreu um erro desconhecido ao excluir."
|
|
14276
|
+
);
|
|
13417
14277
|
}
|
|
13418
14278
|
} catch (error) {
|
|
13419
14279
|
console.error("Falha ao excluir a ancestralidade:", error);
|
|
13420
14280
|
alert(`Erro ao excluir a ancestralidade: ${error.message}`);
|
|
13421
14281
|
return;
|
|
13422
14282
|
}
|
|
13423
|
-
setAncestryMode({
|
|
14283
|
+
setAncestryMode({
|
|
14284
|
+
isActive: false,
|
|
14285
|
+
tree: null,
|
|
14286
|
+
selectedParentId: null,
|
|
14287
|
+
isEditMode: false,
|
|
14288
|
+
currentAncestryId: null,
|
|
14289
|
+
ancestryName: ""
|
|
14290
|
+
});
|
|
13424
14291
|
if (mountRef.current) mountRef.current.style.cursor = "grab";
|
|
13425
14292
|
},
|
|
13426
14293
|
[save_view_data, delete_file_action]
|
|
@@ -13428,24 +14295,38 @@ function XViewScene({
|
|
|
13428
14295
|
const handleOpenAncestryBoard = useCallback4(() => {
|
|
13429
14296
|
setIsAncestryBoardOpen(true);
|
|
13430
14297
|
}, []);
|
|
13431
|
-
const handleSelectAncestryFromBoard = useCallback4(
|
|
13432
|
-
|
|
13433
|
-
|
|
13434
|
-
|
|
13435
|
-
|
|
13436
|
-
|
|
13437
|
-
|
|
13438
|
-
|
|
13439
|
-
|
|
13440
|
-
|
|
14298
|
+
const handleSelectAncestryFromBoard = useCallback4(
|
|
14299
|
+
(ancestry) => {
|
|
14300
|
+
setIsAncestryBoardOpen(false);
|
|
14301
|
+
setIsSidebarOpen(false);
|
|
14302
|
+
handleStartReadingAncestry(ancestry);
|
|
14303
|
+
},
|
|
14304
|
+
[handleStartReadingAncestry]
|
|
14305
|
+
);
|
|
14306
|
+
const handleSaveAncestryBoard = useCallback4(
|
|
14307
|
+
async (groups) => {
|
|
14308
|
+
if (!sceneConfigId || !viewParams || !session) return;
|
|
14309
|
+
const sceneType = (viewParams.type || "").toLowerCase().includes("database") ? "database" : "view";
|
|
14310
|
+
await save_ancestry_board_action(
|
|
14311
|
+
sceneConfigId,
|
|
14312
|
+
sceneType,
|
|
14313
|
+
groups,
|
|
14314
|
+
session,
|
|
14315
|
+
ownerId
|
|
14316
|
+
);
|
|
14317
|
+
},
|
|
14318
|
+
[sceneConfigId, viewParams, session, save_ancestry_board_action, ownerId]
|
|
14319
|
+
);
|
|
13441
14320
|
const existingNodeTypes = useMemo12(() => {
|
|
13442
14321
|
if (!parentDataRef.current) {
|
|
13443
14322
|
return [];
|
|
13444
14323
|
}
|
|
13445
|
-
const allTypes = Object.values(parentDataRef.current).flatMap(
|
|
13446
|
-
|
|
13447
|
-
|
|
13448
|
-
|
|
14324
|
+
const allTypes = Object.values(parentDataRef.current).flatMap(
|
|
14325
|
+
(fileData) => fileData.nodes.flatMap((node) => {
|
|
14326
|
+
if (Array.isArray(node.type)) return node.type;
|
|
14327
|
+
return [node.type];
|
|
14328
|
+
})
|
|
14329
|
+
).filter((t) => Boolean(t) && String(t).toLowerCase() !== "quest");
|
|
13449
14330
|
return [...new Set(allTypes)];
|
|
13450
14331
|
}, [parentDataRef.current, sceneVersion]);
|
|
13451
14332
|
const searchableDbNodes = useMemo12(() => {
|
|
@@ -13459,7 +14340,10 @@ function XViewScene({
|
|
|
13459
14340
|
}, [parentDataRef.current, sceneVersion]);
|
|
13460
14341
|
const handleAddExistingNode = useCallback4(
|
|
13461
14342
|
(nodeId) => {
|
|
13462
|
-
return userActionHandlers.handleAddExistingNodeById(
|
|
14343
|
+
return userActionHandlers.handleAddExistingNodeById(
|
|
14344
|
+
actionHandlerContext,
|
|
14345
|
+
nodeId
|
|
14346
|
+
);
|
|
13463
14347
|
},
|
|
13464
14348
|
[actionHandlerContext]
|
|
13465
14349
|
);
|
|
@@ -13467,7 +14351,9 @@ function XViewScene({
|
|
|
13467
14351
|
var _a2, _b2, _c2;
|
|
13468
14352
|
const { nodeObjects, allLinks } = stateRef.current;
|
|
13469
14353
|
if (!nodeObjects || !allLinks || !sceneSaveUrl || !parentDataRef.current) {
|
|
13470
|
-
console.warn(
|
|
14354
|
+
console.warn(
|
|
14355
|
+
"N\xE3o \xE9 poss\xEDvel salvar a cena: estado n\xE3o inicializado ou URL de salvamento ausente."
|
|
14356
|
+
);
|
|
13471
14357
|
return;
|
|
13472
14358
|
}
|
|
13473
14359
|
if (!save_view_data) return;
|
|
@@ -13504,48 +14390,68 @@ function XViewScene({
|
|
|
13504
14390
|
}, [sceneSaveUrl, save_view_data, sceneConfigId, viewParams == null ? void 0 : viewParams.type]);
|
|
13505
14391
|
const allAvailableNodes = useMemo12(() => {
|
|
13506
14392
|
if (!parentDataRef.current) return [];
|
|
13507
|
-
return Object.values(parentDataRef.current).flatMap(
|
|
14393
|
+
return Object.values(parentDataRef.current).flatMap(
|
|
14394
|
+
(fileData) => fileData.nodes || []
|
|
14395
|
+
);
|
|
13508
14396
|
}, [sceneVersion, isInitialized]);
|
|
13509
14397
|
const allAvailableAncestries = useMemo12(() => {
|
|
13510
14398
|
return ancestryDataRef.current || [];
|
|
13511
14399
|
}, [sceneVersion, isInitialized]);
|
|
13512
|
-
const handleOpenReference = useCallback4(
|
|
13513
|
-
|
|
13514
|
-
|
|
13515
|
-
|
|
13516
|
-
|
|
13517
|
-
|
|
13518
|
-
|
|
13519
|
-
|
|
13520
|
-
|
|
13521
|
-
|
|
13522
|
-
|
|
14400
|
+
const handleOpenReference = useCallback4(
|
|
14401
|
+
(referenceData) => {
|
|
14402
|
+
const { type, id } = referenceData;
|
|
14403
|
+
if (type === "node") {
|
|
14404
|
+
const targetNode = allAvailableNodes.find(
|
|
14405
|
+
(n) => String(n.id) === String(id)
|
|
14406
|
+
);
|
|
14407
|
+
if (targetNode) {
|
|
14408
|
+
setAncestryLinkDetails(null);
|
|
14409
|
+
setDetailsLink(null);
|
|
14410
|
+
setDetailsNode(targetNode);
|
|
14411
|
+
const sceneMesh = stateRef.current.nodeObjects[String(id)];
|
|
14412
|
+
if (sceneMesh) {
|
|
14413
|
+
tweenToTarget(sceneMesh);
|
|
14414
|
+
}
|
|
14415
|
+
} else {
|
|
14416
|
+
alert("Node original n\xE3o encontrado neste contexto.");
|
|
14417
|
+
}
|
|
14418
|
+
} else if (type === "ancestry") {
|
|
14419
|
+
const targetAncestry = allAvailableAncestries.find(
|
|
14420
|
+
(a) => String(a.ancestry_id) === String(id)
|
|
14421
|
+
);
|
|
14422
|
+
if (targetAncestry) {
|
|
14423
|
+
setDetailsNode(null);
|
|
14424
|
+
setDetailsLink(null);
|
|
14425
|
+
setAncestryLinkDetails(null);
|
|
14426
|
+
handleEditAncestry(targetAncestry);
|
|
14427
|
+
} else {
|
|
14428
|
+
alert("Ancestralidade original n\xE3o encontrada neste contexto.");
|
|
13523
14429
|
}
|
|
13524
|
-
} else {
|
|
13525
|
-
alert("Node original n\xE3o encontrado neste contexto.");
|
|
13526
|
-
}
|
|
13527
|
-
} else if (type === "ancestry") {
|
|
13528
|
-
const targetAncestry = allAvailableAncestries.find((a) => String(a.ancestry_id) === String(id));
|
|
13529
|
-
if (targetAncestry) {
|
|
13530
|
-
setDetailsNode(null);
|
|
13531
|
-
setDetailsLink(null);
|
|
13532
|
-
setAncestryLinkDetails(null);
|
|
13533
|
-
handleEditAncestry(targetAncestry);
|
|
13534
|
-
} else {
|
|
13535
|
-
alert("Ancestralidade original n\xE3o encontrada neste contexto.");
|
|
13536
14430
|
}
|
|
13537
|
-
}
|
|
13538
|
-
|
|
14431
|
+
},
|
|
14432
|
+
[
|
|
14433
|
+
allAvailableNodes,
|
|
14434
|
+
allAvailableAncestries,
|
|
14435
|
+
handleEditAncestry,
|
|
14436
|
+
tweenToTarget
|
|
14437
|
+
]
|
|
14438
|
+
);
|
|
13539
14439
|
const handleToggleAncestryAddMode = useCallback4(() => {
|
|
13540
|
-
setAncestryMode((prev) => ({
|
|
14440
|
+
setAncestryMode((prev) => ({
|
|
14441
|
+
...prev,
|
|
14442
|
+
isAddingNodes: !prev.isAddingNodes
|
|
14443
|
+
}));
|
|
13541
14444
|
}, []);
|
|
13542
|
-
const handleFocusNode = useCallback4(
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
|
|
13546
|
-
|
|
13547
|
-
|
|
13548
|
-
|
|
14445
|
+
const handleFocusNode = useCallback4(
|
|
14446
|
+
(nodeData) => {
|
|
14447
|
+
if (!nodeData) return;
|
|
14448
|
+
const nodeMesh = stateRef.current.nodeObjects[String(nodeData.id)];
|
|
14449
|
+
if (nodeMesh) {
|
|
14450
|
+
tweenToTarget(nodeMesh, 1.2);
|
|
14451
|
+
}
|
|
14452
|
+
},
|
|
14453
|
+
[tweenToTarget]
|
|
14454
|
+
);
|
|
13549
14455
|
const availableDatasets = useMemo12(() => {
|
|
13550
14456
|
if (!sceneDataRef.current || !parentDataRef.current) return [];
|
|
13551
14457
|
return sceneDataRef.current.parent_dbs.map((db) => {
|
|
@@ -13556,7 +14462,9 @@ function XViewScene({
|
|
|
13556
14462
|
};
|
|
13557
14463
|
});
|
|
13558
14464
|
}, [sceneVersion, isInitialized]);
|
|
13559
|
-
const sourceNodeDatasetId = creationMode.sourceNodeData ? (_b = stateRef.current.nodeIdToParentFileMap.get(
|
|
14465
|
+
const sourceNodeDatasetId = creationMode.sourceNodeData ? (_b = stateRef.current.nodeIdToParentFileMap.get(
|
|
14466
|
+
String(creationMode.sourceNodeData.id)
|
|
14467
|
+
)) == null ? void 0 : _b.parentFileId : null;
|
|
13560
14468
|
const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
|
|
13561
14469
|
useEffect22(() => {
|
|
13562
14470
|
if (isInitialized && focusNodeId && !hasFocusedInitial) {
|
|
@@ -13569,13 +14477,24 @@ function XViewScene({
|
|
|
13569
14477
|
}, 300);
|
|
13570
14478
|
} else {
|
|
13571
14479
|
setHasFocusedInitial(true);
|
|
14480
|
+
setInvalidTargetError(
|
|
14481
|
+
"O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
|
|
14482
|
+
);
|
|
13572
14483
|
}
|
|
13573
14484
|
}
|
|
13574
|
-
}, [
|
|
14485
|
+
}, [
|
|
14486
|
+
isInitialized,
|
|
14487
|
+
sceneVersion,
|
|
14488
|
+
focusNodeId,
|
|
14489
|
+
hasFocusedInitial,
|
|
14490
|
+
tweenToTarget
|
|
14491
|
+
]);
|
|
13575
14492
|
useEffect22(() => {
|
|
13576
14493
|
if (isInitialized && focusAncestryId && !hasOpenedInitialAncestry) {
|
|
13577
14494
|
const ancestries = ancestryDataRef.current || [];
|
|
13578
|
-
const targetAncestry = ancestries.find(
|
|
14495
|
+
const targetAncestry = ancestries.find(
|
|
14496
|
+
(a) => String(a.ancestry_id) === String(focusAncestryId)
|
|
14497
|
+
);
|
|
13579
14498
|
if (targetAncestry) {
|
|
13580
14499
|
setTimeout(() => {
|
|
13581
14500
|
handleStartReadingAncestry(targetAncestry);
|
|
@@ -13583,20 +14502,43 @@ function XViewScene({
|
|
|
13583
14502
|
}, 300);
|
|
13584
14503
|
} else {
|
|
13585
14504
|
setHasOpenedInitialAncestry(true);
|
|
14505
|
+
setInvalidTargetError(
|
|
14506
|
+
"O link aponta para uma ancestralidade que n\xE3o foi encontrada ou foi exclu\xEDda."
|
|
14507
|
+
);
|
|
13586
14508
|
}
|
|
13587
14509
|
}
|
|
13588
|
-
}, [
|
|
14510
|
+
}, [
|
|
14511
|
+
isInitialized,
|
|
14512
|
+
sceneVersion,
|
|
14513
|
+
focusAncestryId,
|
|
14514
|
+
hasOpenedInitialAncestry,
|
|
14515
|
+
handleStartReadingAncestry
|
|
14516
|
+
]);
|
|
13589
14517
|
useEffect22(() => {
|
|
13590
14518
|
function handleKeyDown(event) {
|
|
13591
14519
|
var _a2, _b2, _c2;
|
|
13592
14520
|
const context = actionHandlerContext;
|
|
13593
14521
|
if (event.key === "Escape") {
|
|
13594
|
-
if (stateRef.current.connection.isActive)
|
|
13595
|
-
|
|
13596
|
-
if (stateRef.current.
|
|
13597
|
-
|
|
14522
|
+
if (stateRef.current.connection.isActive)
|
|
14523
|
+
userActionHandlers.handleCancelConnection(context);
|
|
14524
|
+
if (stateRef.current.relink.isActive)
|
|
14525
|
+
userActionHandlers.handleCancelRelink(context);
|
|
14526
|
+
if (stateRef.current.creation.isActive)
|
|
14527
|
+
userActionHandlers.handleCancelCreation(context);
|
|
14528
|
+
if ((_a2 = stateRef.current.versionMode) == null ? void 0 : _a2.isActive)
|
|
14529
|
+
userActionHandlers.handleCancelVersioning(context);
|
|
13598
14530
|
if (stateRef.current.ancestry.isActive) {
|
|
13599
|
-
setAncestryMode({
|
|
14531
|
+
setAncestryMode({
|
|
14532
|
+
isActive: false,
|
|
14533
|
+
tree: null,
|
|
14534
|
+
selectedParentId: null,
|
|
14535
|
+
isEditMode: false,
|
|
14536
|
+
currentAncestryId: null,
|
|
14537
|
+
ancestryName: "",
|
|
14538
|
+
ancestryDescription: "",
|
|
14539
|
+
ancestryDescriptionSections: [],
|
|
14540
|
+
isAddingNodes: false
|
|
14541
|
+
});
|
|
13600
14542
|
if (mountRef.current) mountRef.current.style.cursor = "grab";
|
|
13601
14543
|
}
|
|
13602
14544
|
if (questMode.isActive) {
|
|
@@ -13605,7 +14547,9 @@ function XViewScene({
|
|
|
13605
14547
|
if (stateRef.current.selectedNodes.size > 0) {
|
|
13606
14548
|
stateRef.current.selectedNodes.clear();
|
|
13607
14549
|
}
|
|
13608
|
-
setContextMenu(
|
|
14550
|
+
setContextMenu(
|
|
14551
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
14552
|
+
);
|
|
13609
14553
|
setMultiContextMenu((prev) => ({ ...prev, visible: false }));
|
|
13610
14554
|
setRelationshipMenu((prev) => ({ ...prev, visible: false }));
|
|
13611
14555
|
}
|
|
@@ -13624,7 +14568,9 @@ function XViewScene({
|
|
|
13624
14568
|
let attempts = 0;
|
|
13625
14569
|
const MIN_CLEARANCE = 15;
|
|
13626
14570
|
while (isOccupied && attempts < 30) {
|
|
13627
|
-
isOccupied = existingNodes.some(
|
|
14571
|
+
isOccupied = existingNodes.some(
|
|
14572
|
+
(mesh) => mesh.position.distanceTo(ghostPosition) < MIN_CLEARANCE
|
|
14573
|
+
);
|
|
13628
14574
|
if (isOccupied) {
|
|
13629
14575
|
ghostPosition.x = controls.target.x + Math.cos(angle) * radius;
|
|
13630
14576
|
ghostPosition.y = controls.target.y + (Math.random() - 0.5) * 8;
|
|
@@ -13643,7 +14589,11 @@ function XViewScene({
|
|
|
13643
14589
|
intensity: 0,
|
|
13644
14590
|
type: ["quest"]
|
|
13645
14591
|
};
|
|
13646
|
-
const ghostNode = createNodeMesh(
|
|
14592
|
+
const ghostNode = createNodeMesh(
|
|
14593
|
+
ghostData,
|
|
14594
|
+
ghostPosition,
|
|
14595
|
+
glowTexture
|
|
14596
|
+
);
|
|
13647
14597
|
ghostNode.traverse((child) => {
|
|
13648
14598
|
if (child.isMesh) {
|
|
13649
14599
|
child.material.transparent = true;
|
|
@@ -13690,13 +14640,49 @@ function XViewScene({
|
|
|
13690
14640
|
return /* @__PURE__ */ React25.createElement(LoadingScreen, null);
|
|
13691
14641
|
}
|
|
13692
14642
|
if (permissionStatus === "denied") {
|
|
13693
|
-
return /* @__PURE__ */ React25.createElement("div", { className: "flex flex-col items-center justify-center min-h-screen w-full bg-slate-950 text-white" }, /* @__PURE__ */ React25.createElement("div", { className: "bg-slate-900/50 p-8 rounded-2xl border border-slate-800 shadow-2xl text-center max-w-md" }, /* @__PURE__ */ React25.createElement("div", { className: "mb-4 text-red-500" }, /* @__PURE__ */ React25.createElement(
|
|
14643
|
+
return /* @__PURE__ */ React25.createElement("div", { className: "flex flex-col items-center justify-center min-h-screen w-full bg-slate-950 text-white" }, /* @__PURE__ */ React25.createElement("div", { className: "bg-slate-900/50 p-8 rounded-2xl border border-slate-800 shadow-2xl text-center max-w-md" }, /* @__PURE__ */ React25.createElement("div", { className: "mb-4 text-red-500" }, /* @__PURE__ */ React25.createElement(
|
|
14644
|
+
"svg",
|
|
14645
|
+
{
|
|
14646
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
14647
|
+
fill: "none",
|
|
14648
|
+
viewBox: "0 0 24 24",
|
|
14649
|
+
strokeWidth: 1.5,
|
|
14650
|
+
stroke: "currentColor",
|
|
14651
|
+
className: "w-16 h-16 mx-auto"
|
|
14652
|
+
},
|
|
14653
|
+
/* @__PURE__ */ React25.createElement(
|
|
14654
|
+
"path",
|
|
14655
|
+
{
|
|
14656
|
+
strokeLinecap: "round",
|
|
14657
|
+
strokeLinejoin: "round",
|
|
14658
|
+
d: "M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
|
|
14659
|
+
}
|
|
14660
|
+
)
|
|
14661
|
+
)), /* @__PURE__ */ React25.createElement("h2", { className: "text-2xl font-bold mb-2" }, "Acesso Negado"), /* @__PURE__ */ React25.createElement("p", { className: "text-slate-400 mb-6" }, "Voc\xEA n\xE3o tem permiss\xE3o para acessar este conte\xFAdo. Solicite acesso ao propriet\xE1rio ou verifique se est\xE1 na conta correta."), /* @__PURE__ */ React25.createElement(
|
|
13694
14662
|
"button",
|
|
13695
14663
|
{
|
|
13696
14664
|
onClick: () => router.push("/dashboard/scenes"),
|
|
13697
14665
|
className: "flex items-center justify-center gap-2 w-full py-3 px-4 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors font-medium"
|
|
13698
14666
|
},
|
|
13699
|
-
/* @__PURE__ */ React25.createElement(
|
|
14667
|
+
/* @__PURE__ */ React25.createElement(
|
|
14668
|
+
"svg",
|
|
14669
|
+
{
|
|
14670
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
14671
|
+
fill: "none",
|
|
14672
|
+
viewBox: "0 0 24 24",
|
|
14673
|
+
strokeWidth: 2,
|
|
14674
|
+
stroke: "currentColor",
|
|
14675
|
+
className: "w-5 h-5"
|
|
14676
|
+
},
|
|
14677
|
+
/* @__PURE__ */ React25.createElement(
|
|
14678
|
+
"path",
|
|
14679
|
+
{
|
|
14680
|
+
strokeLinecap: "round",
|
|
14681
|
+
strokeLinejoin: "round",
|
|
14682
|
+
d: "M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"
|
|
14683
|
+
}
|
|
14684
|
+
)
|
|
14685
|
+
),
|
|
13700
14686
|
"Voltar para Scenes"
|
|
13701
14687
|
)));
|
|
13702
14688
|
}
|
|
@@ -13743,7 +14729,14 @@ function XViewScene({
|
|
|
13743
14729
|
onImageChange: handleGhostNodeImageChange,
|
|
13744
14730
|
onOpenImageViewer: handleOpenImageViewer,
|
|
13745
14731
|
onMentionClick: handleAddExistingNode,
|
|
13746
|
-
style: {
|
|
14732
|
+
style: {
|
|
14733
|
+
position: "absolute",
|
|
14734
|
+
left: `${formPosition.left}px`,
|
|
14735
|
+
top: `${formPosition.top}px`,
|
|
14736
|
+
opacity: formPosition.opacity,
|
|
14737
|
+
zIndex: 20,
|
|
14738
|
+
transition: "opacity 200ms ease-out"
|
|
14739
|
+
},
|
|
13747
14740
|
refEl: formRef,
|
|
13748
14741
|
existingTypes: existingNodeTypes,
|
|
13749
14742
|
initialColor: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.color,
|
|
@@ -13767,7 +14760,14 @@ function XViewScene({
|
|
|
13767
14760
|
onImageChange: handleGhostNodeImageChange,
|
|
13768
14761
|
onOpenImageViewer: handleOpenImageViewer,
|
|
13769
14762
|
onMentionClick: handleAddExistingNode,
|
|
13770
|
-
style: {
|
|
14763
|
+
style: {
|
|
14764
|
+
position: "absolute",
|
|
14765
|
+
left: `${formPosition.left}px`,
|
|
14766
|
+
top: `${formPosition.top}px`,
|
|
14767
|
+
opacity: formPosition.opacity,
|
|
14768
|
+
zIndex: 20,
|
|
14769
|
+
transition: "opacity 200ms ease-out"
|
|
14770
|
+
},
|
|
13771
14771
|
refEl: formRef,
|
|
13772
14772
|
fixedType: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.type,
|
|
13773
14773
|
fixedColor: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.color,
|
|
@@ -13784,7 +14784,13 @@ function XViewScene({
|
|
|
13784
14784
|
onNameChange: handleGhostNodeNameChange,
|
|
13785
14785
|
onColorChange: handleGhostNodeColorChange,
|
|
13786
14786
|
onSizeChange: handleGhostNodeSizeChange,
|
|
13787
|
-
style: {
|
|
14787
|
+
style: {
|
|
14788
|
+
position: "absolute",
|
|
14789
|
+
left: `16px`,
|
|
14790
|
+
top: `16px`,
|
|
14791
|
+
zIndex: 20,
|
|
14792
|
+
transition: "opacity 200ms ease-out"
|
|
14793
|
+
},
|
|
13788
14794
|
refEl: formRef,
|
|
13789
14795
|
onOpenImageViewer: handleOpenImageViewer,
|
|
13790
14796
|
onMentionClick: handleAddExistingNode,
|
|
@@ -13799,7 +14805,14 @@ function XViewScene({
|
|
|
13799
14805
|
"div",
|
|
13800
14806
|
{
|
|
13801
14807
|
className: `ui-overlay absolute group rounded-2xl border border-white/10 bg-slate-950/70 backdrop-blur-xl shadow-[0_20px_80px_rgba(0,0,0,0.6)] ring-1 ring-white/10 text-white overflow-hidden flex flex-col ${isReadModeResizing ? "transition-none" : "transition-all duration-300 ease-out"}`,
|
|
13802
|
-
style: {
|
|
14808
|
+
style: {
|
|
14809
|
+
top: 16,
|
|
14810
|
+
right: 16,
|
|
14811
|
+
zIndex: 1100,
|
|
14812
|
+
maxHeight: "calc(100vh - 32px)",
|
|
14813
|
+
width: `${readModeWidth}px`,
|
|
14814
|
+
maxWidth: "92vw"
|
|
14815
|
+
}
|
|
13803
14816
|
},
|
|
13804
14817
|
/* @__PURE__ */ React25.createElement(
|
|
13805
14818
|
"div",
|
|
@@ -13857,7 +14870,16 @@ function XViewScene({
|
|
|
13857
14870
|
onSave: handleSaveAncestry,
|
|
13858
14871
|
onEditRelationship: handleOpenAncestryRelEditor,
|
|
13859
14872
|
onClose: () => {
|
|
13860
|
-
setAncestryMode({
|
|
14873
|
+
setAncestryMode({
|
|
14874
|
+
isActive: false,
|
|
14875
|
+
tree: null,
|
|
14876
|
+
selectedParentId: null,
|
|
14877
|
+
isEditMode: false,
|
|
14878
|
+
currentAncestryId: null,
|
|
14879
|
+
ancestryName: "",
|
|
14880
|
+
ancestryDescription: "",
|
|
14881
|
+
isAddingNodes: false
|
|
14882
|
+
});
|
|
13861
14883
|
if (mountRef.current) mountRef.current.style.cursor = "grab";
|
|
13862
14884
|
},
|
|
13863
14885
|
availableNodes: allAvailableNodes,
|
|
@@ -13922,7 +14944,12 @@ function XViewScene({
|
|
|
13922
14944
|
onOpenImageViewer: handleOpenImageViewer,
|
|
13923
14945
|
existingTypes: existingNodeTypes,
|
|
13924
14946
|
onImageChange: (useImage, url, currentColorOverride) => {
|
|
13925
|
-
const updatedNode = {
|
|
14947
|
+
const updatedNode = {
|
|
14948
|
+
...detailsNode,
|
|
14949
|
+
useImageAsTexture: useImage,
|
|
14950
|
+
textureImageUrl: url,
|
|
14951
|
+
color: currentColorOverride || detailsNode.color
|
|
14952
|
+
};
|
|
13926
14953
|
updateExistingNodeVisuals(stateRef.current, updatedNode);
|
|
13927
14954
|
setDetailsNode(updatedNode);
|
|
13928
14955
|
},
|
|
@@ -13996,7 +15023,9 @@ function XViewScene({
|
|
|
13996
15023
|
parentData: parentDataRef.current,
|
|
13997
15024
|
sceneData: sceneDataRef.current,
|
|
13998
15025
|
ancestryData: ancestryDataRef.current,
|
|
13999
|
-
onClose: () => setContextMenu(
|
|
15026
|
+
onClose: () => setContextMenu(
|
|
15027
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
15028
|
+
),
|
|
14000
15029
|
onStartCreation: (data) => userActionHandlers.handleStartCreation(actionHandlerContext, data),
|
|
14001
15030
|
onStartConnection: (data) => userActionHandlers.handleStartConnection(actionHandlerContext, data),
|
|
14002
15031
|
onStartVersioning: handleStartVersioning,
|
|
@@ -14004,7 +15033,11 @@ function XViewScene({
|
|
|
14004
15033
|
onDeleteNode: (data) => userActionHandlers.handleDeleteNode(actionHandlerContext, data),
|
|
14005
15034
|
onDismissNode: (data) => userActionHandlers.handleDismissNode(actionHandlerContext, data),
|
|
14006
15035
|
onDismissOtherNodes: (data) => userActionHandlers.handleDismissOtherNodes(actionHandlerContext, data),
|
|
14007
|
-
onExpandConnections: (sourceNode, links) => userActionHandlers.handleExpandConnections(
|
|
15036
|
+
onExpandConnections: (sourceNode, links) => userActionHandlers.handleExpandConnections(
|
|
15037
|
+
actionHandlerContext,
|
|
15038
|
+
sourceNode,
|
|
15039
|
+
links
|
|
15040
|
+
),
|
|
14008
15041
|
onRenderAncestry: handleStartReadingAncestry,
|
|
14009
15042
|
onEditAncestry: handleEditAncestry,
|
|
14010
15043
|
onDeleteAncestry: (ancestryId) => handleDeleteAncestry(ancestryId),
|
|
@@ -14017,9 +15050,18 @@ function XViewScene({
|
|
|
14017
15050
|
data: multiContextMenu,
|
|
14018
15051
|
userRole: userPermissionRole,
|
|
14019
15052
|
onClose: () => setMultiContextMenu((prev) => ({ ...prev, visible: false })),
|
|
14020
|
-
onDismissNodes: (ids) => userActionHandlers.handleDismissMultipleNodes(
|
|
14021
|
-
|
|
14022
|
-
|
|
15053
|
+
onDismissNodes: (ids) => userActionHandlers.handleDismissMultipleNodes(
|
|
15054
|
+
actionHandlerContext,
|
|
15055
|
+
ids
|
|
15056
|
+
),
|
|
15057
|
+
onDismissOtherNodes: (ids) => userActionHandlers.handleDismissOtherMultipleNodes(
|
|
15058
|
+
actionHandlerContext,
|
|
15059
|
+
ids
|
|
15060
|
+
),
|
|
15061
|
+
onDeleteNodes: (ids) => userActionHandlers.handleDeleteMultipleNodes(
|
|
15062
|
+
actionHandlerContext,
|
|
15063
|
+
ids
|
|
15064
|
+
)
|
|
14023
15065
|
}
|
|
14024
15066
|
),
|
|
14025
15067
|
/* @__PURE__ */ React25.createElement(
|
|
@@ -14040,7 +15082,13 @@ function XViewScene({
|
|
|
14040
15082
|
onDelete: (data) => userActionHandlers.handleDeleteLink(actionHandlerContext, data)
|
|
14041
15083
|
}
|
|
14042
15084
|
),
|
|
14043
|
-
/* @__PURE__ */ React25.createElement(
|
|
15085
|
+
/* @__PURE__ */ React25.createElement(
|
|
15086
|
+
ImageViewer,
|
|
15087
|
+
{
|
|
15088
|
+
data: imageViewer,
|
|
15089
|
+
onClose: () => setImageViewer({ ...imageViewer, visible: false })
|
|
15090
|
+
}
|
|
15091
|
+
),
|
|
14044
15092
|
/* @__PURE__ */ React25.createElement(
|
|
14045
15093
|
AncestryBoard,
|
|
14046
15094
|
{
|
|
@@ -14066,6 +15114,83 @@ function XViewScene({
|
|
|
14066
15114
|
currentViewName: viewParams == null ? void 0 : viewParams.name,
|
|
14067
15115
|
currentAncestries: ancestryDataRef.current || []
|
|
14068
15116
|
}
|
|
15117
|
+
),
|
|
15118
|
+
invalidTargetError && /* @__PURE__ */ React25.createElement(
|
|
15119
|
+
"div",
|
|
15120
|
+
{
|
|
15121
|
+
className: "ui-overlay",
|
|
15122
|
+
style: {
|
|
15123
|
+
position: "fixed",
|
|
15124
|
+
top: "24px",
|
|
15125
|
+
left: "50%",
|
|
15126
|
+
transform: "translateX(-50%)",
|
|
15127
|
+
zIndex: 1e4,
|
|
15128
|
+
padding: "16px 24px",
|
|
15129
|
+
background: "rgba(30, 20, 20, 0.85)",
|
|
15130
|
+
backdropFilter: "blur(12px)",
|
|
15131
|
+
WebkitBackdropFilter: "blur(12px)",
|
|
15132
|
+
border: "1px solid rgba(255, 70, 70, 0.35)",
|
|
15133
|
+
borderRadius: "16px",
|
|
15134
|
+
boxShadow: "0 12px 40px rgba(0,0,0,0.5), 0 0 30px rgba(255, 50, 50, 0.1)",
|
|
15135
|
+
color: "#ffa0a0",
|
|
15136
|
+
display: "flex",
|
|
15137
|
+
alignItems: "center",
|
|
15138
|
+
gap: "16px",
|
|
15139
|
+
fontFamily: "Inter, sans-serif",
|
|
15140
|
+
animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
|
|
15141
|
+
}
|
|
15142
|
+
},
|
|
15143
|
+
/* @__PURE__ */ React25.createElement("style", null, `
|
|
15144
|
+
@keyframes fadeInDown {
|
|
15145
|
+
from { opacity: 0; transform: translate(-50%, -20px); }
|
|
15146
|
+
to { opacity: 1; transform: translate(-50%, 0); }
|
|
15147
|
+
}
|
|
15148
|
+
`),
|
|
15149
|
+
/* @__PURE__ */ React25.createElement(
|
|
15150
|
+
"svg",
|
|
15151
|
+
{
|
|
15152
|
+
width: "20",
|
|
15153
|
+
height: "20",
|
|
15154
|
+
viewBox: "0 0 24 24",
|
|
15155
|
+
fill: "none",
|
|
15156
|
+
stroke: "currentColor",
|
|
15157
|
+
strokeWidth: "2",
|
|
15158
|
+
strokeLinecap: "round",
|
|
15159
|
+
strokeLinejoin: "round"
|
|
15160
|
+
},
|
|
15161
|
+
/* @__PURE__ */ React25.createElement("circle", { cx: "12", cy: "12", r: "10" }),
|
|
15162
|
+
/* @__PURE__ */ React25.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
|
|
15163
|
+
/* @__PURE__ */ React25.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
|
|
15164
|
+
),
|
|
15165
|
+
/* @__PURE__ */ React25.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
|
|
15166
|
+
/* @__PURE__ */ React25.createElement(
|
|
15167
|
+
"button",
|
|
15168
|
+
{
|
|
15169
|
+
onClick: () => setInvalidTargetError(null),
|
|
15170
|
+
style: {
|
|
15171
|
+
background: "rgba(255, 255, 255, 0.1)",
|
|
15172
|
+
border: "none",
|
|
15173
|
+
color: "white",
|
|
15174
|
+
padding: "4px 10px",
|
|
15175
|
+
borderRadius: "8px",
|
|
15176
|
+
cursor: "pointer",
|
|
15177
|
+
fontSize: "12px",
|
|
15178
|
+
fontWeight: 600,
|
|
15179
|
+
transition: "all 0.2s",
|
|
15180
|
+
marginLeft: "8px",
|
|
15181
|
+
border: "1px solid rgba(255,255,255,0.1)"
|
|
15182
|
+
},
|
|
15183
|
+
onMouseOver: (e) => {
|
|
15184
|
+
e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
|
|
15185
|
+
e.currentTarget.style.transform = "translateY(-1px)";
|
|
15186
|
+
},
|
|
15187
|
+
onMouseOut: (e) => {
|
|
15188
|
+
e.currentTarget.style.background = "rgba(255, 255, 255, 0.1)";
|
|
15189
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
15190
|
+
}
|
|
15191
|
+
},
|
|
15192
|
+
"Fechar"
|
|
15193
|
+
)
|
|
14069
15194
|
)
|
|
14070
15195
|
);
|
|
14071
15196
|
}
|