@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.js
CHANGED
|
@@ -149,6 +149,7 @@ function ContextMenu({
|
|
|
149
149
|
const [menuView, setMenuView] = (0, import_react.useState)("main");
|
|
150
150
|
const [selectedAncestry, setSelectedAncestry] = (0, import_react.useState)(null);
|
|
151
151
|
const [versionSubMenu, setVersionSubMenu] = (0, import_react.useState)(null);
|
|
152
|
+
const [labelSubMenu, setLabelSubMenu] = (0, import_react.useState)(null);
|
|
152
153
|
const [isLinkCopied, setIsLinkCopied] = (0, import_react.useState)(false);
|
|
153
154
|
const [selectedQuestStatus, setSelectedQuestStatus] = (0, import_react.useState)(null);
|
|
154
155
|
const ability = (0, import_react.useMemo)(() => defineAbilityFor(userRole), [userRole]);
|
|
@@ -157,6 +158,7 @@ function ContextMenu({
|
|
|
157
158
|
setMenuView("main");
|
|
158
159
|
setSelectedAncestry(null);
|
|
159
160
|
setVersionSubMenu(null);
|
|
161
|
+
setLabelSubMenu(null);
|
|
160
162
|
setSelectedQuestStatus(null);
|
|
161
163
|
}
|
|
162
164
|
}, [data.visible, (_a = data.nodeData) == null ? void 0 : _a.id]);
|
|
@@ -172,7 +174,7 @@ function ContextMenu({
|
|
|
172
174
|
if (left + w + 8 > vw) left = Math.max(8, vw - w - 8);
|
|
173
175
|
if (top + h + 8 > vh) top = Math.max(8, vh - h - 8);
|
|
174
176
|
setMenuPos({ left, top });
|
|
175
|
-
}, [data, menuView, versionSubMenu, selectedQuestStatus]);
|
|
177
|
+
}, [data, menuView, versionSubMenu, labelSubMenu, selectedQuestStatus]);
|
|
176
178
|
(0, import_react.useEffect)(() => {
|
|
177
179
|
if (!data.visible) return;
|
|
178
180
|
const handleClickOutside = (e) => {
|
|
@@ -224,7 +226,21 @@ function ContextMenu({
|
|
|
224
226
|
var _a2;
|
|
225
227
|
return (_a2 = c.targetNode) == null ? void 0 : _a2.is_quest;
|
|
226
228
|
});
|
|
227
|
-
const
|
|
229
|
+
const getLabelForConnection = (conn) => {
|
|
230
|
+
if (conn.direction === "outgoing") return conn.link.source_label || null;
|
|
231
|
+
if (conn.direction === "incoming") return conn.link.target_label || null;
|
|
232
|
+
return null;
|
|
233
|
+
};
|
|
234
|
+
const commonWithLabel = commonConnections.filter((c) => getLabelForConnection(c));
|
|
235
|
+
const commonWithoutLabel = commonConnections.filter((c) => !getLabelForConnection(c));
|
|
236
|
+
const labelGroups = commonWithLabel.reduce((acc, conn) => {
|
|
237
|
+
const label = getLabelForConnection(conn);
|
|
238
|
+
if (!acc[label]) acc[label] = [];
|
|
239
|
+
acc[label].push(conn);
|
|
240
|
+
return acc;
|
|
241
|
+
}, {});
|
|
242
|
+
const labelGroupEntries = Object.entries(labelGroups).sort(([a], [b]) => a.localeCompare(b));
|
|
243
|
+
const groupedConnections = commonWithoutLabel.reduce((acc, conn) => {
|
|
228
244
|
var _a2;
|
|
229
245
|
const { targetNode } = conn;
|
|
230
246
|
const groupingKey = ((_a2 = targetNode.version_node) == null ? void 0 : _a2.is_version) ? targetNode.version_node.parent_node : targetNode.id;
|
|
@@ -352,11 +368,32 @@ function ContextMenu({
|
|
|
352
368
|
/* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
|
|
353
369
|
))));
|
|
354
370
|
};
|
|
371
|
+
const renderLabelSubMenuView = () => {
|
|
372
|
+
const group = labelSubMenu;
|
|
373
|
+
const isScrollable = group.connections.length > 10;
|
|
374
|
+
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
|
|
375
|
+
setLabelSubMenu(null);
|
|
376
|
+
setMenuView("connections");
|
|
377
|
+
}, className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.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__ */ import_react.default.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-violet-300/80 truncate", title: group.label }, group.label))), group.connections.length > 0 && /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("div", { className: `flex flex-col gap-1 ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, group.connections.map((conn) => /* @__PURE__ */ import_react.default.createElement(
|
|
378
|
+
"button",
|
|
379
|
+
{
|
|
380
|
+
key: conn.targetNode.id,
|
|
381
|
+
onClick: () => handleExpandAndClose([conn.link]),
|
|
382
|
+
className: baseButtonClass,
|
|
383
|
+
title: `Expandir conex\xE3o com ${conn.targetNode.name}`
|
|
384
|
+
},
|
|
385
|
+
/* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }), /* @__PURE__ */ import_react.default.createElement("polyline", { points: "12 5 19 12 12 19" })),
|
|
386
|
+
/* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
|
|
387
|
+
))));
|
|
388
|
+
};
|
|
355
389
|
const renderConnectionsView = () => {
|
|
356
390
|
if (versionSubMenu) {
|
|
357
391
|
return renderVersionSubMenuView();
|
|
358
392
|
}
|
|
359
|
-
|
|
393
|
+
if (labelSubMenu) {
|
|
394
|
+
return renderLabelSubMenuView();
|
|
395
|
+
}
|
|
396
|
+
const totalItems = availableAncestries.length + labelGroupEntries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
|
|
360
397
|
const isScrollable = totalItems > 10;
|
|
361
398
|
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("main"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Conex\xF5es")), commonConnections.length > 0 && /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("div", { className: `flex flex-col ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: `flex flex-col gap-1 ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, /* @__PURE__ */ import_react.default.createElement("div", { className: "px-2 py-1 text-[11px] uppercase tracking-wider text-indigo-400/90" }, "Ancestralidades Salvas"), availableAncestries.map((anc) => /* @__PURE__ */ import_react.default.createElement(
|
|
362
399
|
"button",
|
|
@@ -368,7 +405,20 @@ function ContextMenu({
|
|
|
368
405
|
},
|
|
369
406
|
/* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("path", { d: "M6 3v4a2 2 0 0 0 2 2h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 3v4a2 2 0 0 1-2 2h-4" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "3", r: "2" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "3", r: "2" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "11", r: "2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 13v6" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "21", r: "2" })),
|
|
370
407
|
/* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, anc.name || `Ancestralidade #${anc.ancestry_id.substring(0, 8)}`)
|
|
371
|
-
))),
|
|
408
|
+
))), labelGroupEntries.length > 0 && /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), labelGroupEntries.map(([label, conns]) => /* @__PURE__ */ import_react.default.createElement(
|
|
409
|
+
"button",
|
|
410
|
+
{
|
|
411
|
+
key: label,
|
|
412
|
+
onClick: () => {
|
|
413
|
+
setLabelSubMenu({ label, connections: conns });
|
|
414
|
+
},
|
|
415
|
+
className: baseButtonClass,
|
|
416
|
+
title: `Ver grupo: ${label}`
|
|
417
|
+
},
|
|
418
|
+
/* @__PURE__ */ import_react.default.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__ */ import_react.default.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__ */ import_react.default.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })),
|
|
419
|
+
/* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, label),
|
|
420
|
+
/* @__PURE__ */ import_react.default.createElement("span", { className: "text-xs px-2 py-0.5 bg-violet-500/20 text-violet-300 rounded-full" }, conns.length)
|
|
421
|
+
))), finalRenderableConnections.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, (availableAncestries.length > 0 || labelGroupEntries.length > 0) && /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
|
|
372
422
|
if (group.isVersionGroup) {
|
|
373
423
|
return /* @__PURE__ */ import_react.default.createElement(
|
|
374
424
|
"button",
|
|
@@ -446,11 +496,18 @@ function ContextMenu({
|
|
|
446
496
|
))));
|
|
447
497
|
};
|
|
448
498
|
const renderAncestryActionsView = () => {
|
|
499
|
+
var _a2, _b2;
|
|
449
500
|
const ancestryTitle = (selectedAncestry == null ? void 0 : selectedAncestry.name) || `Ancestralidade #${selectedAncestry == null ? void 0 : selectedAncestry.ancestry_id.substring(0, 8)}`;
|
|
450
|
-
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("connections"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400 truncate", title: ancestryTitle }, ancestryTitle))), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, ability.can("read", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
|
|
451
|
-
onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry);
|
|
501
|
+
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("connections"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400 truncate", title: ancestryTitle }, ancestryTitle))), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, ability.can("read", "Ancestry") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
|
|
502
|
+
onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry, "full");
|
|
503
|
+
onClose();
|
|
504
|
+
}, className: baseButtonClass, title: "Renderizar Ancestralidade Completa" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("path", { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Renderizar Ancestralidade")), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
|
|
505
|
+
onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry, "ancestry_only");
|
|
506
|
+
onClose();
|
|
507
|
+
}, className: baseButtonClass, title: "Renderizar apenas a \xC1rvore de Ancestralidade (Radial)" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" }), /* @__PURE__ */ import_react.default.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__ */ import_react.default.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__ */ import_react.default.createElement("button", { onClick: () => {
|
|
508
|
+
onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry, "abstraction_only");
|
|
452
509
|
onClose();
|
|
453
|
-
}, className: baseButtonClass, title: "Renderizar
|
|
510
|
+
}, className: baseButtonClass, title: "Renderizar apenas a \xC1rvore de Abstra\xE7\xE3o (Vertical)" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ import_react.default.createElement("rect", { x: "14", y: "3", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ import_react.default.createElement("rect", { x: "14", y: "14", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ import_react.default.createElement("rect", { x: "3", y: "14", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 6.5h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 17.5h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M6.5 10v4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M17.5 10v4" })), /* @__PURE__ */ import_react.default.createElement("span", null, "\xC1rvore de Abstra\xE7\xE3o"))), ability.can("update", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
|
|
454
511
|
onEditAncestry == null ? void 0 : onEditAncestry(selectedAncestry);
|
|
455
512
|
onClose();
|
|
456
513
|
}, className: baseButtonClass, title: "Editar Ancestralidade" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("path", { d: "M12 20h9" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Editar Ancestralidade")), (ability.can("update", "Ancestry") || ability.can("delete", "Ancestry")) && /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), ability.can("delete", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
|
|
@@ -697,7 +754,7 @@ function XViewSidebar({
|
|
|
697
754
|
"div",
|
|
698
755
|
{
|
|
699
756
|
ref: containerRef,
|
|
700
|
-
className: "ui-overlay fixed left-0 top-0 h-
|
|
757
|
+
className: "ui-overlay fixed left-0 top-0 h-[100dvh] w-[min(92vw,320px)] z-40 overflow-hidden",
|
|
701
758
|
onPointerDown: swallow,
|
|
702
759
|
onPointerMove: swallow,
|
|
703
760
|
onPointerUp: swallow,
|
|
@@ -706,7 +763,7 @@ function XViewSidebar({
|
|
|
706
763
|
onContextMenu: swallow,
|
|
707
764
|
onDoubleClick: swallow
|
|
708
765
|
},
|
|
709
|
-
/* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ import_react2.default.createElement(
|
|
766
|
+
/* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ import_react2.default.createElement(
|
|
710
767
|
"button",
|
|
711
768
|
{
|
|
712
769
|
className: "ml-auto p-2 rounded-md text-slate-400 hover:text-white hover:bg-white/10 transition-colors",
|
|
@@ -763,7 +820,7 @@ function XViewSidebar({
|
|
|
763
820
|
autoComplete: "off"
|
|
764
821
|
}
|
|
765
822
|
)
|
|
766
|
-
), showList && /* @__PURE__ */ import_react2.default.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[
|
|
823
|
+
), showList && /* @__PURE__ */ import_react2.default.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) => {
|
|
767
824
|
const inView = isNodeInView(n.id);
|
|
768
825
|
const active = selectedNodeId === n.id;
|
|
769
826
|
const typeLabel = Array.isArray(n.type) ? n.type.join(", ") : n.type;
|
|
@@ -3139,6 +3196,7 @@ function calculateNodePositions(nodes) {
|
|
|
3139
3196
|
return positions;
|
|
3140
3197
|
}
|
|
3141
3198
|
function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, isSceneBusy, parentData, ancestryData }) {
|
|
3199
|
+
var _a, _b;
|
|
3142
3200
|
if (!tooltipEl || !camera || !mountEl) return;
|
|
3143
3201
|
let content = "";
|
|
3144
3202
|
let positionTarget = null;
|
|
@@ -3151,17 +3209,21 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
|
|
|
3151
3209
|
content = generateTooltipHtml(hoveredNode.userData, parentData, ancestryData);
|
|
3152
3210
|
}
|
|
3153
3211
|
} else if (hoveredLink && !isSceneBusy) {
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3212
|
+
const linkData = hoveredLink.userData.isAncestryLink ? hoveredLink.userData.relationship || {} : hoveredLink.userData;
|
|
3213
|
+
const hasContent = ((_a = linkData.name) == null ? void 0 : _a.trim()) || ((_b = linkData.description) == null ? void 0 : _b.trim());
|
|
3214
|
+
if (hasContent) {
|
|
3215
|
+
currentId = `link_${hoveredLink.userData.id}`;
|
|
3216
|
+
if (hoveredLink.userData.isCurved) {
|
|
3217
|
+
const positions = hoveredLink.geometry.attributes.position.array;
|
|
3218
|
+
const midIndex = Math.floor(positions.length / 2 / 3) * 3;
|
|
3219
|
+
positionTarget = new THREE2.Vector3(positions[midIndex], positions[midIndex + 1], positions[midIndex + 2]);
|
|
3220
|
+
} else {
|
|
3221
|
+
positionTarget = new THREE2.Vector3().addVectors(hoveredLink.userData.sourceNode.position, hoveredLink.userData.targetNode.position).multiplyScalar(0.5);
|
|
3222
|
+
}
|
|
3223
|
+
isLink = true;
|
|
3224
|
+
if (tooltipEl.dataset.currentId !== currentId) {
|
|
3225
|
+
content = generateLinkTooltipHtml(linkData, parentData, ancestryData);
|
|
3226
|
+
}
|
|
3165
3227
|
}
|
|
3166
3228
|
}
|
|
3167
3229
|
if (positionTarget) {
|
|
@@ -3470,9 +3532,9 @@ function CustomPropertyDisplay({
|
|
|
3470
3532
|
};
|
|
3471
3533
|
const handleRemoveListItem = (j) => setTempProp((p) => ({ ...p, value: p.value.filter((_, k) => k !== j) }));
|
|
3472
3534
|
const handleListItemChange = (j, f, v) => setTempProp((p) => {
|
|
3473
|
-
const
|
|
3474
|
-
|
|
3475
|
-
return { ...p, value:
|
|
3535
|
+
const newValue2 = [...p.value];
|
|
3536
|
+
newValue2[j] = { ...newValue2[j], [f]: v };
|
|
3537
|
+
return { ...p, value: newValue2 };
|
|
3476
3538
|
});
|
|
3477
3539
|
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";
|
|
3478
3540
|
const renderEditView = () => {
|
|
@@ -3526,14 +3588,14 @@ function CustomPropertyDisplay({
|
|
|
3526
3588
|
const inputClass = `${baseInput} ${noSpinnerClass}`;
|
|
3527
3589
|
const handleDateTypeChange = (newDateType) => {
|
|
3528
3590
|
var _a3, _b2, _c2;
|
|
3529
|
-
let
|
|
3591
|
+
let newValue2 = { type: newDateType };
|
|
3530
3592
|
if (newDateType === "Date Interval") {
|
|
3531
|
-
|
|
3532
|
-
|
|
3593
|
+
newValue2.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
|
|
3594
|
+
newValue2.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
|
|
3533
3595
|
} else {
|
|
3534
|
-
|
|
3596
|
+
newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
|
|
3535
3597
|
}
|
|
3536
|
-
handlePropChange("value",
|
|
3598
|
+
handlePropChange("value", newValue2);
|
|
3537
3599
|
};
|
|
3538
3600
|
const handleDateValueChange = (dateField, dateValue) => {
|
|
3539
3601
|
handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
|
|
@@ -4381,7 +4443,7 @@ ${space}${bullet} `);
|
|
|
4381
4443
|
}
|
|
4382
4444
|
),
|
|
4383
4445
|
/* @__PURE__ */ import_react6.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0 shrink-0" }),
|
|
4384
|
-
/* @__PURE__ */ import_react6.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-sm font-medium text-slate-200" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ import_react6.default.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")),
|
|
4446
|
+
/* @__PURE__ */ import_react6.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shrink-0" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-sm font-medium text-slate-200 truncate" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ import_react6.default.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")),
|
|
4385
4447
|
/* @__PURE__ */ import_react6.default.createElement("div", { className: "px-6 py-3 flex flex-col gap-3 border-b border-white/5 bg-white/5 shrink-0" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-2 flex-wrap" }, /* @__PURE__ */ import_react6.default.createElement(
|
|
4386
4448
|
"button",
|
|
4387
4449
|
{
|
|
@@ -5496,7 +5558,7 @@ function AncestryRelationshipPanel({
|
|
|
5496
5558
|
onImageClick: handleImageClickFromText,
|
|
5497
5559
|
onSaveDescription: handleSaveDescriptionInline
|
|
5498
5560
|
}
|
|
5499
|
-
) : /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react9.default.createElement("div",
|
|
5561
|
+
) : /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react9.default.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__ */ import_react9.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o de Ancestralidade")), /* @__PURE__ */ import_react9.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Editar Rela\xE7\xE3o")), /* @__PURE__ */ import_react9.default.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__ */ import_react9.default.createElement("div", { className: "px-6 pb-4 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react9.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o da Rela\xE7\xE3o"), /* @__PURE__ */ import_react9.default.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__ */ import_react9.default.createElement(
|
|
5500
5562
|
DescriptionDisplay,
|
|
5501
5563
|
{
|
|
5502
5564
|
description,
|
|
@@ -5801,6 +5863,7 @@ function CreateAncestryPanel({
|
|
|
5801
5863
|
} = ancestryMode;
|
|
5802
5864
|
const [isSaving, setIsSaving] = (0, import_react11.useState)(false);
|
|
5803
5865
|
const [isLinkCopied, setIsLinkCopied] = (0, import_react11.useState)(false);
|
|
5866
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react11.useState)(false);
|
|
5804
5867
|
const [showDeleteBranchConfirm, setShowDeleteBranchConfirm] = (0, import_react11.useState)(false);
|
|
5805
5868
|
const handleCopyLink = (e) => {
|
|
5806
5869
|
e.stopPropagation();
|
|
@@ -5868,12 +5931,14 @@ function CreateAncestryPanel({
|
|
|
5868
5931
|
};
|
|
5869
5932
|
const handleSelectAncestryParent = (nodeId, isAbstraction = false) => {
|
|
5870
5933
|
setAncestryMode((prev) => isAbstraction ? { ...prev, selectedAbstractionParentId: nodeId } : { ...prev, selectedParentId: nodeId });
|
|
5934
|
+
setHasUnsavedChanges(true);
|
|
5871
5935
|
};
|
|
5872
5936
|
const handleToggleAddMode = (isAbstraction = false) => {
|
|
5873
5937
|
if (isAbstraction && !ancestryMode.isAddingAbstractionNodes) {
|
|
5874
5938
|
setTargetRenderNodeId(null);
|
|
5875
5939
|
}
|
|
5876
5940
|
setAncestryMode((prev) => isAbstraction ? { ...prev, isAddingAbstractionNodes: !prev.isAddingAbstractionNodes } : { ...prev, isAddingNodes: !prev.isAddingNodes });
|
|
5941
|
+
setHasUnsavedChanges(true);
|
|
5877
5942
|
};
|
|
5878
5943
|
const handleRemoveNode = (0, import_react11.useCallback)((pathToRemove, isAbstraction = false) => {
|
|
5879
5944
|
if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) return;
|
|
@@ -5891,6 +5956,7 @@ function CreateAncestryPanel({
|
|
|
5891
5956
|
const indexToRemove = pathToRemove[pathToRemove.length - 1];
|
|
5892
5957
|
if (currentParent.children && currentParent.children.length > indexToRemove) {
|
|
5893
5958
|
currentParent.children.splice(indexToRemove, 1);
|
|
5959
|
+
setHasUnsavedChanges(true);
|
|
5894
5960
|
}
|
|
5895
5961
|
return { ...prev, [treeKey]: newTree };
|
|
5896
5962
|
});
|
|
@@ -5949,6 +6015,7 @@ function CreateAncestryPanel({
|
|
|
5949
6015
|
updateGlobalTree(rootTreeClone);
|
|
5950
6016
|
}
|
|
5951
6017
|
setAncestryMode((prev) => ({ ...prev, [treeKey]: rootTreeClone }));
|
|
6018
|
+
setHasUnsavedChanges(true);
|
|
5952
6019
|
} else {
|
|
5953
6020
|
alert("N\xE3o \xE9 poss\xEDvel mover um node para dentro de seus pr\xF3prios descendentes.");
|
|
5954
6021
|
}
|
|
@@ -6021,6 +6088,7 @@ function CreateAncestryPanel({
|
|
|
6021
6088
|
const handleAddProp = () => {
|
|
6022
6089
|
const newProp = createNewCustomProperty(customProps);
|
|
6023
6090
|
setCustomProps((p) => [...p, newProp]);
|
|
6091
|
+
setHasUnsavedChanges(true);
|
|
6024
6092
|
setTimeout(() => {
|
|
6025
6093
|
var _a;
|
|
6026
6094
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -6029,11 +6097,13 @@ function CreateAncestryPanel({
|
|
|
6029
6097
|
const handleRemoveProp = (i) => {
|
|
6030
6098
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
6031
6099
|
setCustomProps(newProps);
|
|
6100
|
+
setHasUnsavedChanges(true);
|
|
6032
6101
|
};
|
|
6033
6102
|
const handleUpdateProp = (index, updatedProp) => {
|
|
6034
6103
|
const newProps = [...customProps];
|
|
6035
6104
|
newProps[index] = updatedProp;
|
|
6036
6105
|
setCustomProps(newProps);
|
|
6106
|
+
setHasUnsavedChanges(true);
|
|
6037
6107
|
};
|
|
6038
6108
|
const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
|
|
6039
6109
|
(0, import_react11.useEffect)(() => {
|
|
@@ -6143,6 +6213,7 @@ function CreateAncestryPanel({
|
|
|
6143
6213
|
updateGlobalTree(rootTreeClone);
|
|
6144
6214
|
setBranchStack([...branchStack]);
|
|
6145
6215
|
setIsPickerOpen(false);
|
|
6216
|
+
setHasUnsavedChanges(true);
|
|
6146
6217
|
try {
|
|
6147
6218
|
setIsSaving(true);
|
|
6148
6219
|
const rootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6156,6 +6227,7 @@ function CreateAncestryPanel({
|
|
|
6156
6227
|
rootExtras
|
|
6157
6228
|
);
|
|
6158
6229
|
setLastSavedSnapshot(takeSnapshot(rootTreeClone, ancestryName, description, processedSections, [], isPrivate, ancestryMode.abstraction_tree));
|
|
6230
|
+
setHasUnsavedChanges(false);
|
|
6159
6231
|
if (onRenderFullAncestry) {
|
|
6160
6232
|
const fullTreePayload = {
|
|
6161
6233
|
ancestry_id: ancestryMode.currentAncestryId || "temp_root",
|
|
@@ -6198,6 +6270,7 @@ function CreateAncestryPanel({
|
|
|
6198
6270
|
if (branchIndex !== -1) {
|
|
6199
6271
|
foundParentPath.node.parallel_branches.splice(branchIndex, 1);
|
|
6200
6272
|
updateGlobalTree(rootTreeClone);
|
|
6273
|
+
setHasUnsavedChanges(true);
|
|
6201
6274
|
try {
|
|
6202
6275
|
setIsSaving(true);
|
|
6203
6276
|
const currentRootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6219,6 +6292,7 @@ function CreateAncestryPanel({
|
|
|
6219
6292
|
isPrivate,
|
|
6220
6293
|
ancestryMode.abstraction_tree
|
|
6221
6294
|
));
|
|
6295
|
+
setHasUnsavedChanges(false);
|
|
6222
6296
|
if (onClearAncestryVisuals) {
|
|
6223
6297
|
onClearAncestryVisuals(currentStep.branchId);
|
|
6224
6298
|
}
|
|
@@ -6251,6 +6325,7 @@ function CreateAncestryPanel({
|
|
|
6251
6325
|
if (branchIndex !== -1) {
|
|
6252
6326
|
foundParentPath.node.parallel_branches.splice(branchIndex, 1);
|
|
6253
6327
|
updateGlobalTree(rootTreeClone);
|
|
6328
|
+
setHasUnsavedChanges(true);
|
|
6254
6329
|
try {
|
|
6255
6330
|
setIsSaving(true);
|
|
6256
6331
|
const currentRootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6272,6 +6347,7 @@ function CreateAncestryPanel({
|
|
|
6272
6347
|
isPrivate,
|
|
6273
6348
|
ancestryMode.abstraction_tree
|
|
6274
6349
|
));
|
|
6350
|
+
setHasUnsavedChanges(false);
|
|
6275
6351
|
if (onClearAncestryVisuals) {
|
|
6276
6352
|
onClearAncestryVisuals(currentStep.branchId);
|
|
6277
6353
|
}
|
|
@@ -6533,6 +6609,7 @@ function CreateAncestryPanel({
|
|
|
6533
6609
|
}
|
|
6534
6610
|
setBranchStack(parentStack);
|
|
6535
6611
|
setTargetScrollSectionId(targetFocusId);
|
|
6612
|
+
setHasUnsavedChanges(true);
|
|
6536
6613
|
if (onRenderFullAncestry) {
|
|
6537
6614
|
const parentStack2 = currentStack;
|
|
6538
6615
|
const rotation = parentStack2.reduce((acc, step) => {
|
|
@@ -6594,7 +6671,6 @@ function CreateAncestryPanel({
|
|
|
6594
6671
|
direction,
|
|
6595
6672
|
tree: {
|
|
6596
6673
|
node: nodeData,
|
|
6597
|
-
node_id: nodeId,
|
|
6598
6674
|
children: [],
|
|
6599
6675
|
relationship: {}
|
|
6600
6676
|
}
|
|
@@ -6616,6 +6692,7 @@ function CreateAncestryPanel({
|
|
|
6616
6692
|
savedMaxIndex: parentIndexToSave,
|
|
6617
6693
|
entryDirection: direction
|
|
6618
6694
|
}]);
|
|
6695
|
+
setHasUnsavedChanges(true);
|
|
6619
6696
|
if (branch && branch.tree && onRenderFullAncestry) {
|
|
6620
6697
|
const branchAncestryObj = {
|
|
6621
6698
|
ancestry_id: branch.id,
|
|
@@ -6666,6 +6743,10 @@ function CreateAncestryPanel({
|
|
|
6666
6743
|
const currentInputName = overrides.ancestryName !== void 0 ? overrides.ancestryName : ancestryName;
|
|
6667
6744
|
const currentInputDesc = overrides.description !== void 0 ? overrides.description : description;
|
|
6668
6745
|
const currentInputSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
6746
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
6747
|
+
onClose();
|
|
6748
|
+
return;
|
|
6749
|
+
}
|
|
6669
6750
|
if (!currentInputName.trim()) {
|
|
6670
6751
|
alert("O nome n\xE3o pode estar vazio.");
|
|
6671
6752
|
return;
|
|
@@ -6673,11 +6754,7 @@ function CreateAncestryPanel({
|
|
|
6673
6754
|
setIsSaving(true);
|
|
6674
6755
|
const processedSections = processDescriptionForSave(currentInputDesc, currentInputSections);
|
|
6675
6756
|
setExistingSections(processedSections);
|
|
6676
|
-
const updatedRootTree =
|
|
6677
|
-
ancestryMode.tree,
|
|
6678
|
-
currentInputDesc,
|
|
6679
|
-
processedSections
|
|
6680
|
-
);
|
|
6757
|
+
const updatedRootTree = JSON.parse(JSON.stringify(ancestryMode.tree));
|
|
6681
6758
|
const extrasObj = {
|
|
6682
6759
|
...toObjectFromCustomProps(customProps.filter((p) => !p.isEditing)),
|
|
6683
6760
|
is_private: isPrivate
|
|
@@ -6719,6 +6796,7 @@ function CreateAncestryPanel({
|
|
|
6719
6796
|
isPrivate,
|
|
6720
6797
|
ancestryMode.abstraction_tree
|
|
6721
6798
|
));
|
|
6799
|
+
setHasUnsavedChanges(false);
|
|
6722
6800
|
if (onRenderFullAncestry) {
|
|
6723
6801
|
const rotation = branchStack.reduce((acc, step) => {
|
|
6724
6802
|
return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
|
|
@@ -6770,6 +6848,7 @@ function CreateAncestryPanel({
|
|
|
6770
6848
|
updatedRootTree,
|
|
6771
6849
|
extrasObj
|
|
6772
6850
|
);
|
|
6851
|
+
setHasUnsavedChanges(false);
|
|
6773
6852
|
setLastSavedSnapshot(takeSnapshot(
|
|
6774
6853
|
updatedRootTree,
|
|
6775
6854
|
currentInputName,
|
|
@@ -6792,6 +6871,7 @@ function CreateAncestryPanel({
|
|
|
6792
6871
|
const newTreeString = JSON.stringify(newRootTree);
|
|
6793
6872
|
if (currentTreeString !== newTreeString) {
|
|
6794
6873
|
updateGlobalTree(newRootTree);
|
|
6874
|
+
setHasUnsavedChanges(true);
|
|
6795
6875
|
}
|
|
6796
6876
|
}, [description, existingSections]);
|
|
6797
6877
|
const handleTriggerFullRender = () => {
|
|
@@ -6814,6 +6894,7 @@ function CreateAncestryPanel({
|
|
|
6814
6894
|
};
|
|
6815
6895
|
const handleSaveDescriptionInline = (newDesc) => {
|
|
6816
6896
|
setDescription(newDesc);
|
|
6897
|
+
setHasUnsavedChanges(true);
|
|
6817
6898
|
handleLocalSave(true, { description: newDesc });
|
|
6818
6899
|
};
|
|
6819
6900
|
const swallow = (e) => e.stopPropagation();
|
|
@@ -6943,7 +7024,11 @@ function CreateAncestryPanel({
|
|
|
6943
7024
|
{
|
|
6944
7025
|
type: "text",
|
|
6945
7026
|
value: ancestryName,
|
|
6946
|
-
onChange: (e) =>
|
|
7027
|
+
onChange: (e) => {
|
|
7028
|
+
setAncestryName(e.target.value);
|
|
7029
|
+
setHasUnsavedChanges(true);
|
|
7030
|
+
},
|
|
7031
|
+
readOnly: isContextLinked,
|
|
6947
7032
|
placeholder: "Nome da Ancestralidade",
|
|
6948
7033
|
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"
|
|
6949
7034
|
}
|
|
@@ -7592,9 +7677,9 @@ function InSceneCreationForm({
|
|
|
7592
7677
|
};
|
|
7593
7678
|
const handleToggleImageMode = () => {
|
|
7594
7679
|
var _a2, _b;
|
|
7595
|
-
const
|
|
7596
|
-
setUseImageAsTexture(
|
|
7597
|
-
if (
|
|
7680
|
+
const newValue2 = !useImageAsTexture;
|
|
7681
|
+
setUseImageAsTexture(newValue2);
|
|
7682
|
+
if (newValue2) {
|
|
7598
7683
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
7599
7684
|
if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
|
|
7600
7685
|
const url = firstImageProp.value[0].value;
|
|
@@ -7892,9 +7977,9 @@ function InSceneVersionForm({
|
|
|
7892
7977
|
};
|
|
7893
7978
|
const handleToggleImageMode = () => {
|
|
7894
7979
|
var _a, _b;
|
|
7895
|
-
const
|
|
7896
|
-
setUseImageAsTexture(
|
|
7897
|
-
if (
|
|
7980
|
+
const newValue2 = !useImageAsTexture;
|
|
7981
|
+
setUseImageAsTexture(newValue2);
|
|
7982
|
+
if (newValue2) {
|
|
7898
7983
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
7899
7984
|
if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
|
|
7900
7985
|
const url = firstImageProp.value[0].value;
|
|
@@ -8314,6 +8399,7 @@ function NodeDetailsPanel({
|
|
|
8314
8399
|
return !!(node == null ? void 0 : node.useImageAsTexture);
|
|
8315
8400
|
});
|
|
8316
8401
|
const [selectedImageUrl, setSelectedImageUrl] = (0, import_react17.useState)((node == null ? void 0 : node.textureImageUrl) ?? null);
|
|
8402
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react17.useState)(false);
|
|
8317
8403
|
const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
|
|
8318
8404
|
const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
|
|
8319
8405
|
initialWidth: isReadMode ? 700 : 440,
|
|
@@ -8351,6 +8437,7 @@ function NodeDetailsPanel({
|
|
|
8351
8437
|
else if ((node == null ? void 0 : node.useImageAsTexture) === "false") setUseImageAsTexture(false);
|
|
8352
8438
|
else setUseImageAsTexture(!!(node == null ? void 0 : node.useImageAsTexture));
|
|
8353
8439
|
setSelectedImageUrl((node == null ? void 0 : node.textureImageUrl) ?? null);
|
|
8440
|
+
setHasUnsavedChanges(false);
|
|
8354
8441
|
}
|
|
8355
8442
|
}, [node]);
|
|
8356
8443
|
const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
|
|
@@ -8377,6 +8464,7 @@ function NodeDetailsPanel({
|
|
|
8377
8464
|
setIntensity(val);
|
|
8378
8465
|
onIntensityChange == null ? void 0 : onIntensityChange(node.id, val);
|
|
8379
8466
|
onDataUpdate == null ? void 0 : onDataUpdate({ ...node, intensity: val });
|
|
8467
|
+
setHasUnsavedChanges(true);
|
|
8380
8468
|
};
|
|
8381
8469
|
const handleCopyLink = () => {
|
|
8382
8470
|
if (!(node == null ? void 0 : node.id)) return;
|
|
@@ -8394,14 +8482,17 @@ function NodeDetailsPanel({
|
|
|
8394
8482
|
const v = e.target.value;
|
|
8395
8483
|
setName(v);
|
|
8396
8484
|
onNameChange == null ? void 0 : onNameChange(node.id, v);
|
|
8485
|
+
setHasUnsavedChanges(true);
|
|
8397
8486
|
};
|
|
8398
8487
|
const handleColorChange = (val) => {
|
|
8399
8488
|
setColor(val);
|
|
8400
8489
|
onColorChange == null ? void 0 : onColorChange(node.id, val);
|
|
8490
|
+
setHasUnsavedChanges(true);
|
|
8401
8491
|
};
|
|
8402
8492
|
const handleSizeChange = (newSize) => {
|
|
8403
8493
|
setSize(newSize);
|
|
8404
8494
|
onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
|
|
8495
|
+
setHasUnsavedChanges(true);
|
|
8405
8496
|
};
|
|
8406
8497
|
const handleAddType = (newType) => {
|
|
8407
8498
|
const trimmed = newType.trim();
|
|
@@ -8409,10 +8500,12 @@ function NodeDetailsPanel({
|
|
|
8409
8500
|
setTypes([...types, trimmed]);
|
|
8410
8501
|
setTypeInput("");
|
|
8411
8502
|
setShowTypeSuggestions(false);
|
|
8503
|
+
setHasUnsavedChanges(true);
|
|
8412
8504
|
}
|
|
8413
8505
|
};
|
|
8414
8506
|
const handleRemoveType = (indexToRemove) => {
|
|
8415
8507
|
setTypes(types.filter((_, index) => index !== indexToRemove));
|
|
8508
|
+
setHasUnsavedChanges(true);
|
|
8416
8509
|
};
|
|
8417
8510
|
const handleTypeInputKeyDown = (e) => {
|
|
8418
8511
|
if (e.key === "Enter") {
|
|
@@ -8425,6 +8518,7 @@ function NodeDetailsPanel({
|
|
|
8425
8518
|
const handleAddProp = () => {
|
|
8426
8519
|
const newProp = createNewCustomProperty(customProps);
|
|
8427
8520
|
setCustomProps((p) => [...p, newProp]);
|
|
8521
|
+
setHasUnsavedChanges(true);
|
|
8428
8522
|
setTimeout(() => {
|
|
8429
8523
|
var _a;
|
|
8430
8524
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -8433,19 +8527,21 @@ function NodeDetailsPanel({
|
|
|
8433
8527
|
const handleRemoveProp = (i) => {
|
|
8434
8528
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
8435
8529
|
setCustomProps(newProps);
|
|
8530
|
+
setHasUnsavedChanges(true);
|
|
8436
8531
|
triggerAutoSave({ customProps: newProps });
|
|
8437
8532
|
};
|
|
8438
8533
|
const handleUpdateProp = (index, updatedProp) => {
|
|
8439
8534
|
const newProps = [...customProps];
|
|
8440
8535
|
newProps[index] = updatedProp;
|
|
8441
8536
|
setCustomProps(newProps);
|
|
8537
|
+
setHasUnsavedChanges(true);
|
|
8442
8538
|
if (!updatedProp.isEditing) {
|
|
8443
8539
|
triggerAutoSave({ customProps: newProps });
|
|
8444
8540
|
}
|
|
8445
8541
|
};
|
|
8446
8542
|
const handleToggleImageMode = () => {
|
|
8447
|
-
const newValue = !useImageAsTexture;
|
|
8448
8543
|
setUseImageAsTexture(newValue);
|
|
8544
|
+
setHasUnsavedChanges(true);
|
|
8449
8545
|
let activeUrl = null;
|
|
8450
8546
|
if (newValue) {
|
|
8451
8547
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
@@ -8467,6 +8563,7 @@ function NodeDetailsPanel({
|
|
|
8467
8563
|
};
|
|
8468
8564
|
const handleSelectTexture = (url) => {
|
|
8469
8565
|
setSelectedImageUrl(url);
|
|
8566
|
+
setHasUnsavedChanges(true);
|
|
8470
8567
|
onImageChange == null ? void 0 : onImageChange(true, url, color);
|
|
8471
8568
|
onDataUpdate == null ? void 0 : onDataUpdate({
|
|
8472
8569
|
...node,
|
|
@@ -8477,6 +8574,7 @@ function NodeDetailsPanel({
|
|
|
8477
8574
|
};
|
|
8478
8575
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
8479
8576
|
setDescription(newDescription);
|
|
8577
|
+
setHasUnsavedChanges(true);
|
|
8480
8578
|
onDataUpdate({ ...node, description: newDescription });
|
|
8481
8579
|
triggerAutoSave({ description: newDescription });
|
|
8482
8580
|
};
|
|
@@ -8487,6 +8585,10 @@ function NodeDetailsPanel({
|
|
|
8487
8585
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
8488
8586
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
8489
8587
|
const currentIntensity = overrides.intensity !== void 0 ? overrides.intensity : intensity;
|
|
8588
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
8589
|
+
onClose();
|
|
8590
|
+
return;
|
|
8591
|
+
}
|
|
8490
8592
|
if (!currentName.trim() || currentTypes.length === 0) {
|
|
8491
8593
|
alert("O campo 'Nome' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
|
|
8492
8594
|
return;
|
|
@@ -8511,6 +8613,7 @@ function NodeDetailsPanel({
|
|
|
8511
8613
|
};
|
|
8512
8614
|
await onSave(dataToSave, keepOpen);
|
|
8513
8615
|
onDataUpdate(dataToSave);
|
|
8616
|
+
setHasUnsavedChanges(false);
|
|
8514
8617
|
if (!keepOpen) {
|
|
8515
8618
|
onClose();
|
|
8516
8619
|
}
|
|
@@ -8577,7 +8680,7 @@ function NodeDetailsPanel({
|
|
|
8577
8680
|
onImageClick: handleImageClickFromText,
|
|
8578
8681
|
onSaveDescription: handleSaveDescriptionInline
|
|
8579
8682
|
}
|
|
8580
|
-
) : /* @__PURE__ */ import_react17.default.createElement(import_react17.default.Fragment, null, /* @__PURE__ */ import_react17.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ import_react17.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react17.default.createElement("div",
|
|
8683
|
+
) : /* @__PURE__ */ import_react17.default.createElement(import_react17.default.Fragment, null, /* @__PURE__ */ import_react17.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ import_react17.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react17.default.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__ */ import_react17.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ import_react17.default.createElement(
|
|
8581
8684
|
"button",
|
|
8582
8685
|
{
|
|
8583
8686
|
onClick: handleCopyLink,
|
|
@@ -8585,7 +8688,7 @@ function NodeDetailsPanel({
|
|
|
8585
8688
|
title: isLinkCopied ? "Link Copiado!" : "Copiar link para este Node"
|
|
8586
8689
|
},
|
|
8587
8690
|
isLinkCopied ? /* @__PURE__ */ import_react17.default.createElement(import_fi15.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react17.default.createElement(import_fi15.FiLink, { size: 12 })
|
|
8588
|
-
)), /* @__PURE__ */ import_react17.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react17.default.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__ */ import_react17.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react17.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ import_react17.default.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__ */ import_react17.default.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__ */ import_react17.default.createElement(
|
|
8691
|
+
)), /* @__PURE__ */ import_react17.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react17.default.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__ */ import_react17.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react17.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ import_react17.default.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__ */ import_react17.default.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__ */ import_react17.default.createElement(
|
|
8589
8692
|
"button",
|
|
8590
8693
|
{
|
|
8591
8694
|
type: "button",
|
|
@@ -8747,6 +8850,7 @@ function NodeDetailsPanel({
|
|
|
8747
8850
|
initialValue: description,
|
|
8748
8851
|
onSave: (newDescription) => {
|
|
8749
8852
|
setDescription(newDescription);
|
|
8853
|
+
setHasUnsavedChanges(true);
|
|
8750
8854
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
8751
8855
|
triggerAutoSave({ description: newDescription });
|
|
8752
8856
|
},
|
|
@@ -8806,6 +8910,7 @@ function QuestDetailsPanel({
|
|
|
8806
8910
|
const [existingSections, setExistingSections] = (0, import_react18.useState)((node == null ? void 0 : node.description_sections) || []);
|
|
8807
8911
|
const [isSaving, setIsSaving] = (0, import_react18.useState)(false);
|
|
8808
8912
|
const [isLinkCopied, setIsLinkCopied] = (0, import_react18.useState)(false);
|
|
8913
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react18.useState)(false);
|
|
8809
8914
|
const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
|
|
8810
8915
|
const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
|
|
8811
8916
|
initialWidth: isReadMode ? 700 : 440,
|
|
@@ -8837,6 +8942,7 @@ function QuestDetailsPanel({
|
|
|
8837
8942
|
setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
|
|
8838
8943
|
setExistingSections((node == null ? void 0 : node.description_sections) || []);
|
|
8839
8944
|
setCustomProps(extractCustomPropsFromNode(node || {}));
|
|
8945
|
+
setHasUnsavedChanges(false);
|
|
8840
8946
|
}
|
|
8841
8947
|
}, [node]);
|
|
8842
8948
|
(0, import_react18.useEffect)(() => {
|
|
@@ -8868,16 +8974,19 @@ function QuestDetailsPanel({
|
|
|
8868
8974
|
setRawTitle(val);
|
|
8869
8975
|
const newStandardName = questPrefix ? `${questPrefix} - \xBB ${val || "Sem t\xEDtulo"}` : val;
|
|
8870
8976
|
onNameChange == null ? void 0 : onNameChange(node.id, newStandardName, val);
|
|
8977
|
+
setHasUnsavedChanges(true);
|
|
8871
8978
|
};
|
|
8872
8979
|
const handleSizeChange = (newSize) => {
|
|
8873
8980
|
setSize(newSize);
|
|
8874
8981
|
onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
|
|
8982
|
+
setHasUnsavedChanges(true);
|
|
8875
8983
|
};
|
|
8876
8984
|
const handleStatusChange = (newStatus) => {
|
|
8877
8985
|
setStatus(newStatus);
|
|
8878
8986
|
const newColor = QUEST_STATUS_COLORS3[newStatus];
|
|
8879
8987
|
onColorChange == null ? void 0 : onColorChange(node.id, newColor);
|
|
8880
8988
|
onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor });
|
|
8989
|
+
setHasUnsavedChanges(true);
|
|
8881
8990
|
};
|
|
8882
8991
|
const handleAddType = (newType) => {
|
|
8883
8992
|
const trimmed = newType.trim();
|
|
@@ -8885,11 +8994,13 @@ function QuestDetailsPanel({
|
|
|
8885
8994
|
setTypes([...types, trimmed]);
|
|
8886
8995
|
setTypeInput("");
|
|
8887
8996
|
setShowTypeSuggestions(false);
|
|
8997
|
+
setHasUnsavedChanges(true);
|
|
8888
8998
|
}
|
|
8889
8999
|
};
|
|
8890
9000
|
const handleRemoveType = (indexToRemove) => {
|
|
8891
9001
|
if (types[indexToRemove] === "quest") return;
|
|
8892
9002
|
setTypes(types.filter((_, index) => index !== indexToRemove));
|
|
9003
|
+
setHasUnsavedChanges(true);
|
|
8893
9004
|
};
|
|
8894
9005
|
const handleTypeInputKeyDown = (e) => {
|
|
8895
9006
|
if (e.key === "Enter") {
|
|
@@ -8902,6 +9013,7 @@ function QuestDetailsPanel({
|
|
|
8902
9013
|
const handleAddProp = () => {
|
|
8903
9014
|
const newProp = createNewCustomProperty(customProps);
|
|
8904
9015
|
setCustomProps((p) => [...p, newProp]);
|
|
9016
|
+
setHasUnsavedChanges(true);
|
|
8905
9017
|
setTimeout(() => {
|
|
8906
9018
|
var _a2;
|
|
8907
9019
|
(_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -8910,18 +9022,21 @@ function QuestDetailsPanel({
|
|
|
8910
9022
|
const handleRemoveProp = (i) => {
|
|
8911
9023
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
8912
9024
|
setCustomProps(newProps);
|
|
9025
|
+
setHasUnsavedChanges(true);
|
|
8913
9026
|
triggerAutoSave({ customProps: newProps });
|
|
8914
9027
|
};
|
|
8915
9028
|
const handleUpdateProp = (index, updatedProp) => {
|
|
8916
9029
|
const newProps = [...customProps];
|
|
8917
9030
|
newProps[index] = updatedProp;
|
|
8918
9031
|
setCustomProps(newProps);
|
|
9032
|
+
setHasUnsavedChanges(true);
|
|
8919
9033
|
if (!updatedProp.isEditing) {
|
|
8920
9034
|
triggerAutoSave({ customProps: newProps });
|
|
8921
9035
|
}
|
|
8922
9036
|
};
|
|
8923
9037
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
8924
9038
|
setDescription(newDescription);
|
|
9039
|
+
setHasUnsavedChanges(true);
|
|
8925
9040
|
onDataUpdate({ ...node, description: newDescription });
|
|
8926
9041
|
triggerAutoSave({ description: newDescription });
|
|
8927
9042
|
};
|
|
@@ -8933,6 +9048,10 @@ function QuestDetailsPanel({
|
|
|
8933
9048
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
8934
9049
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
8935
9050
|
const currentStatus = overrides.status !== void 0 ? overrides.status : status;
|
|
9051
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
9052
|
+
onClose();
|
|
9053
|
+
return;
|
|
9054
|
+
}
|
|
8936
9055
|
if (!currentRawTitle.trim() || currentTypes.length === 0) {
|
|
8937
9056
|
alert("O campo 'T\xEDtulo' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
|
|
8938
9057
|
return;
|
|
@@ -8962,6 +9081,7 @@ function QuestDetailsPanel({
|
|
|
8962
9081
|
};
|
|
8963
9082
|
await onSave(dataToSave, keepOpen);
|
|
8964
9083
|
onDataUpdate(dataToSave);
|
|
9084
|
+
setHasUnsavedChanges(false);
|
|
8965
9085
|
if (!keepOpen) {
|
|
8966
9086
|
onClose();
|
|
8967
9087
|
}
|
|
@@ -9019,7 +9139,7 @@ function QuestDetailsPanel({
|
|
|
9019
9139
|
onImageClick: handleImageClickFromText,
|
|
9020
9140
|
onSaveDescription: handleSaveDescriptionInline
|
|
9021
9141
|
}
|
|
9022
|
-
) : /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ import_react18.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react18.default.createElement("div",
|
|
9142
|
+
) : /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ import_react18.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ import_react18.default.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__ */ import_react18.default.createElement(import_fi16.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiLink, { size: 12 }))), /* @__PURE__ */ import_react18.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, standardizedName || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react18.default.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__ */ import_react18.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "T\xEDtulo da Quest"), /* @__PURE__ */ import_react18.default.createElement(
|
|
9023
9143
|
"input",
|
|
9024
9144
|
{
|
|
9025
9145
|
type: "text",
|
|
@@ -9193,9 +9313,12 @@ function RelationshipDetailsPanel({
|
|
|
9193
9313
|
const [description, setDescription] = (0, import_react20.useState)((link == null ? void 0 : link.description) ?? "");
|
|
9194
9314
|
const [customProps, setCustomProps] = (0, import_react20.useState)(() => extractCustomPropsFromNode(link || {}));
|
|
9195
9315
|
const [existingSections, setExistingSections] = (0, import_react20.useState)((link == null ? void 0 : link.description_sections) || []);
|
|
9316
|
+
const [sourceLabel, setSourceLabel] = (0, import_react20.useState)((link == null ? void 0 : link.source_label) ?? "");
|
|
9317
|
+
const [targetLabel, setTargetLabel] = (0, import_react20.useState)((link == null ? void 0 : link.target_label) ?? "");
|
|
9196
9318
|
const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react20.useState)(false);
|
|
9197
9319
|
const [isSaving, setIsSaving] = (0, import_react20.useState)(false);
|
|
9198
9320
|
const [isReadMode, setIsReadMode] = (0, import_react20.useState)(false);
|
|
9321
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react20.useState)(false);
|
|
9199
9322
|
const propsEndRef = (0, import_react20.useRef)(null);
|
|
9200
9323
|
const canEdit = (0, import_react20.useMemo)(() => {
|
|
9201
9324
|
const ability = defineAbilityFor(userRole);
|
|
@@ -9206,12 +9329,16 @@ function RelationshipDetailsPanel({
|
|
|
9206
9329
|
setDescription((link == null ? void 0 : link.description) ?? "");
|
|
9207
9330
|
setExistingSections((link == null ? void 0 : link.description_sections) || []);
|
|
9208
9331
|
setCustomProps(extractCustomPropsFromNode(link || {}));
|
|
9332
|
+
setSourceLabel((link == null ? void 0 : link.source_label) ?? "");
|
|
9333
|
+
setTargetLabel((link == null ? void 0 : link.target_label) ?? "");
|
|
9334
|
+
setHasUnsavedChanges(false);
|
|
9209
9335
|
}, [link]);
|
|
9210
9336
|
const swallow = (e) => e.stopPropagation();
|
|
9211
9337
|
const handleAddProp = () => {
|
|
9212
9338
|
if (!canEdit) return;
|
|
9213
9339
|
const newProp = createNewCustomProperty(customProps);
|
|
9214
9340
|
setCustomProps((p) => [...p, newProp]);
|
|
9341
|
+
setHasUnsavedChanges(true);
|
|
9215
9342
|
setTimeout(() => {
|
|
9216
9343
|
var _a;
|
|
9217
9344
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -9223,6 +9350,12 @@ function RelationshipDetailsPanel({
|
|
|
9223
9350
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
9224
9351
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
9225
9352
|
const currentName = overrides.name !== void 0 ? overrides.name : name;
|
|
9353
|
+
const currentSourceLabel = overrides.sourceLabel !== void 0 ? overrides.sourceLabel : sourceLabel;
|
|
9354
|
+
const currentTargetLabel = overrides.targetLabel !== void 0 ? overrides.targetLabel : targetLabel;
|
|
9355
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
9356
|
+
onClose();
|
|
9357
|
+
return;
|
|
9358
|
+
}
|
|
9226
9359
|
setIsSaving(true);
|
|
9227
9360
|
try {
|
|
9228
9361
|
const extrasObj = toObjectFromCustomProps(currentCustomProps.filter((p) => !p.isEditing));
|
|
@@ -9238,8 +9371,11 @@ function RelationshipDetailsPanel({
|
|
|
9238
9371
|
isCurved: link.isCurved,
|
|
9239
9372
|
curveOffset: link.curveOffset
|
|
9240
9373
|
};
|
|
9374
|
+
if (currentSourceLabel.trim()) dataToSave.source_label = currentSourceLabel.trim();
|
|
9375
|
+
if (currentTargetLabel.trim()) dataToSave.target_label = currentTargetLabel.trim();
|
|
9241
9376
|
await onSave(dataToSave, keepOpen);
|
|
9242
9377
|
onDataUpdate(dataToSave);
|
|
9378
|
+
setHasUnsavedChanges(false);
|
|
9243
9379
|
if (!keepOpen) {
|
|
9244
9380
|
onClose();
|
|
9245
9381
|
}
|
|
@@ -9253,18 +9389,21 @@ function RelationshipDetailsPanel({
|
|
|
9253
9389
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
9254
9390
|
if (!canEdit) return;
|
|
9255
9391
|
setDescription(newDescription);
|
|
9392
|
+
setHasUnsavedChanges(true);
|
|
9256
9393
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
9257
9394
|
triggerAutoSave({ description: newDescription });
|
|
9258
9395
|
};
|
|
9259
9396
|
const handleRemoveProp = (i) => {
|
|
9260
9397
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
9261
9398
|
setCustomProps(newProps);
|
|
9399
|
+
setHasUnsavedChanges(true);
|
|
9262
9400
|
triggerAutoSave({ customProps: newProps });
|
|
9263
9401
|
};
|
|
9264
9402
|
const handleUpdateProp = (index, updatedProp) => {
|
|
9265
9403
|
const newProps = [...customProps];
|
|
9266
9404
|
newProps[index] = updatedProp;
|
|
9267
9405
|
setCustomProps(newProps);
|
|
9406
|
+
setHasUnsavedChanges(true);
|
|
9268
9407
|
if (!updatedProp.isEditing) {
|
|
9269
9408
|
triggerAutoSave({ customProps: newProps });
|
|
9270
9409
|
}
|
|
@@ -9312,19 +9451,52 @@ function RelationshipDetailsPanel({
|
|
|
9312
9451
|
onImageClick: handleImageClickFromText,
|
|
9313
9452
|
onSaveDescription: handleSaveDescriptionInline
|
|
9314
9453
|
}
|
|
9315
|
-
) : /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ import_react20.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react20.default.createElement("div",
|
|
9454
|
+
) : /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ import_react20.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o")), /* @__PURE__ */ import_react20.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || "Rela\xE7\xE3o")), /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Rela\xE7\xE3o (Opcional)"), /* @__PURE__ */ import_react20.default.createElement(
|
|
9316
9455
|
"input",
|
|
9317
9456
|
{
|
|
9318
9457
|
type: "text",
|
|
9319
9458
|
value: name,
|
|
9320
|
-
onChange: (e) =>
|
|
9459
|
+
onChange: (e) => {
|
|
9460
|
+
setName(e.target.value);
|
|
9461
|
+
setHasUnsavedChanges(true);
|
|
9462
|
+
},
|
|
9321
9463
|
placeholder: "Ex: Controla, Pertence a, Fornece...",
|
|
9322
9464
|
disabled: !canEdit,
|
|
9323
9465
|
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
|
|
9324
9466
|
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9325
9467
|
`
|
|
9326
9468
|
}
|
|
9327
|
-
)), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.
|
|
9469
|
+
)), /* @__PURE__ */ import_react20.default.createElement("div", { className: "rounded-xl border border-white/8 bg-slate-800/30 p-3 space-y-3" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.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__ */ import_react20.default.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ import_react20.default.createElement("p", { className: "text-xs font-medium text-violet-300/80 uppercase tracking-wider" }, "Labels de Agrupamento")), /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.createElement("div", { className: "grid grid-cols-2 gap-2" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Source"), /* @__PURE__ */ import_react20.default.createElement(
|
|
9470
|
+
"input",
|
|
9471
|
+
{
|
|
9472
|
+
type: "text",
|
|
9473
|
+
value: sourceLabel,
|
|
9474
|
+
onChange: (e) => {
|
|
9475
|
+
setSourceLabel(e.target.value);
|
|
9476
|
+
setHasUnsavedChanges(true);
|
|
9477
|
+
},
|
|
9478
|
+
placeholder: "Ex: Conceitos",
|
|
9479
|
+
disabled: !canEdit,
|
|
9480
|
+
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
|
|
9481
|
+
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9482
|
+
`
|
|
9483
|
+
}
|
|
9484
|
+
)), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Target"), /* @__PURE__ */ import_react20.default.createElement(
|
|
9485
|
+
"input",
|
|
9486
|
+
{
|
|
9487
|
+
type: "text",
|
|
9488
|
+
value: targetLabel,
|
|
9489
|
+
onChange: (e) => {
|
|
9490
|
+
setTargetLabel(e.target.value);
|
|
9491
|
+
setHasUnsavedChanges(true);
|
|
9492
|
+
},
|
|
9493
|
+
placeholder: "Ex: Refer\xEAncias",
|
|
9494
|
+
disabled: !canEdit,
|
|
9495
|
+
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
|
|
9496
|
+
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9497
|
+
`
|
|
9498
|
+
}
|
|
9499
|
+
)))), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.createElement(
|
|
9328
9500
|
DescriptionDisplay,
|
|
9329
9501
|
{
|
|
9330
9502
|
description,
|
|
@@ -9394,6 +9566,7 @@ function RelationshipDetailsPanel({
|
|
|
9394
9566
|
onSave: (newDescription) => {
|
|
9395
9567
|
if (!canEdit) return;
|
|
9396
9568
|
setDescription(newDescription);
|
|
9569
|
+
setHasUnsavedChanges(true);
|
|
9397
9570
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
9398
9571
|
triggerAutoSave({ description: newDescription });
|
|
9399
9572
|
},
|
|
@@ -9841,7 +10014,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
|
|
|
9841
10014
|
onMentionClick,
|
|
9842
10015
|
onImageClick: handleImageClickFromText
|
|
9843
10016
|
}
|
|
9844
|
-
) : /* @__PURE__ */ import_react24.default.createElement(import_react24.default.Fragment, null, /* @__PURE__ */ import_react24.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ import_react24.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react24.default.createElement("div",
|
|
10017
|
+
) : /* @__PURE__ */ import_react24.default.createElement(import_react24.default.Fragment, null, /* @__PURE__ */ import_react24.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ import_react24.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react24.default.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__ */ import_react24.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ import_react24.default.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ import_react24.default.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ import_react24.default.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ import_react24.default.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ import_react24.default.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__ */ import_react24.default.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ import_react24.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react24.default.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react24.default.createElement(
|
|
9845
10018
|
"button",
|
|
9846
10019
|
{
|
|
9847
10020
|
onClick: () => setIsReadMode(true),
|
|
@@ -9890,7 +10063,6 @@ var GroupItem = ({
|
|
|
9890
10063
|
onPlayAncestry,
|
|
9891
10064
|
availableIds,
|
|
9892
10065
|
canEdit
|
|
9893
|
-
// [NOVO] Recebe permissão de edição
|
|
9894
10066
|
}) => {
|
|
9895
10067
|
const canIndent = index > 0;
|
|
9896
10068
|
const isPickingForThisGroup = pickingGroupId === group.id;
|
|
@@ -9905,107 +10077,126 @@ var GroupItem = ({
|
|
|
9905
10077
|
(0, import_react25.useEffect)(() => {
|
|
9906
10078
|
adjustHeight();
|
|
9907
10079
|
}, [group.text]);
|
|
9908
|
-
return /* @__PURE__ */ import_react25.default.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__ */ import_react25.default.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ import_react25.default.createElement(
|
|
10080
|
+
return /* @__PURE__ */ import_react25.default.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__ */ import_react25.default.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ import_react25.default.createElement(
|
|
10081
|
+
"div",
|
|
10082
|
+
{
|
|
10083
|
+
className: `
|
|
9909
10084
|
flex flex-col gap-2 py-2 px-3 transition-all duration-200
|
|
9910
10085
|
${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__ */ import_react25.default.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__ */ import_react25.default.createElement(
|
|
9931
|
-
"div",
|
|
10086
|
+
`
|
|
10087
|
+
},
|
|
10088
|
+
/* @__PURE__ */ import_react25.default.createElement(
|
|
10089
|
+
"textarea",
|
|
9932
10090
|
{
|
|
9933
|
-
|
|
9934
|
-
className: `
|
|
10091
|
+
ref: textareaRef,
|
|
10092
|
+
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" : ""}`,
|
|
10093
|
+
rows: 1,
|
|
10094
|
+
style: {
|
|
10095
|
+
minHeight: "1.5rem",
|
|
10096
|
+
maxHeight: "250px"
|
|
10097
|
+
},
|
|
10098
|
+
placeholder: canEdit ? "Escreva sobre este grupo..." : "",
|
|
10099
|
+
value: group.text,
|
|
10100
|
+
readOnly: !canEdit,
|
|
10101
|
+
onChange: (e) => {
|
|
10102
|
+
if (canEdit) onUpdate(group.id, { ...group, text: e.target.value });
|
|
10103
|
+
}
|
|
10104
|
+
}
|
|
10105
|
+
),
|
|
10106
|
+
group.ancestries && group.ancestries.length > 0 && /* @__PURE__ */ import_react25.default.createElement("div", { className: "flex flex-wrap gap-2 mt-1" }, group.ancestries.map((anc) => {
|
|
10107
|
+
const isValid = availableIds.has(String(anc.ancestry_id));
|
|
10108
|
+
return /* @__PURE__ */ import_react25.default.createElement(
|
|
10109
|
+
"div",
|
|
10110
|
+
{
|
|
10111
|
+
key: anc.ancestry_id,
|
|
10112
|
+
className: `
|
|
9935
10113
|
flex items-center gap-2 rounded-md px-3 py-1.5 text-xs transition-all group/card border
|
|
9936
10114
|
${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
10115
|
`,
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
10116
|
+
title: isValid ? "" : "Esta ancestralidade foi removida ou n\xE3o existe mais."
|
|
10117
|
+
},
|
|
10118
|
+
isValid ? (
|
|
10119
|
+
// [MANTIDO] Botão Play visível para todos
|
|
10120
|
+
/* @__PURE__ */ import_react25.default.createElement(
|
|
10121
|
+
"button",
|
|
10122
|
+
{
|
|
10123
|
+
onClick: () => onPlayAncestry(anc.ancestry_id),
|
|
10124
|
+
className: "text-indigo-400 hover:text-white hover:bg-indigo-500 p-1 rounded-full transition-colors",
|
|
10125
|
+
title: "Renderizar no cen\xE1rio"
|
|
10126
|
+
},
|
|
10127
|
+
/* @__PURE__ */ import_react25.default.createElement(import_fi19.FiPlay, { size: 10, className: "ml-0.5 fill-current" })
|
|
10128
|
+
)
|
|
10129
|
+
) : /* @__PURE__ */ import_react25.default.createElement("div", { className: "p-1 text-red-500 cursor-not-allowed" }, /* @__PURE__ */ import_react25.default.createElement(import_fi19.FiAlertTriangle, { size: 10 })),
|
|
9942
10130
|
/* @__PURE__ */ import_react25.default.createElement(
|
|
10131
|
+
"span",
|
|
10132
|
+
{
|
|
10133
|
+
className: `font-medium truncate max-w-[150px] ${!isValid && "line-through decoration-red-500/50"}`
|
|
10134
|
+
},
|
|
10135
|
+
anc.name
|
|
10136
|
+
),
|
|
10137
|
+
canEdit && /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, /* @__PURE__ */ import_react25.default.createElement(
|
|
10138
|
+
"div",
|
|
10139
|
+
{
|
|
10140
|
+
className: `w-px h-3 mx-0.5 ${isValid ? "bg-white/10" : "bg-red-500/20"}`
|
|
10141
|
+
}
|
|
10142
|
+
), /* @__PURE__ */ import_react25.default.createElement(
|
|
9943
10143
|
"button",
|
|
9944
10144
|
{
|
|
9945
|
-
onClick: () =>
|
|
9946
|
-
className: "text-
|
|
9947
|
-
title: "
|
|
10145
|
+
onClick: () => onRemoveAncestry(group.id, anc.ancestry_id),
|
|
10146
|
+
className: `${isValid ? "text-slate-500 hover:text-red-400" : "text-red-400 hover:text-red-200"} p-0.5 rounded transition-colors`,
|
|
10147
|
+
title: "Remover men\xE7\xE3o"
|
|
9948
10148
|
},
|
|
9949
|
-
/* @__PURE__ */ import_react25.default.createElement(import_fi19.
|
|
9950
|
-
)
|
|
9951
|
-
)
|
|
9952
|
-
|
|
9953
|
-
|
|
9954
|
-
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
title: "Remover men\xE7\xE3o"
|
|
9959
|
-
},
|
|
9960
|
-
/* @__PURE__ */ import_react25.default.createElement(import_fi19.FiX, { size: 12 })
|
|
9961
|
-
))
|
|
9962
|
-
);
|
|
9963
|
-
})), canEdit && /* @__PURE__ */ import_react25.default.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__ */ import_react25.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react25.default.createElement(
|
|
9964
|
-
"button",
|
|
9965
|
-
{
|
|
9966
|
-
onClick: () => onRequestPickAncestry(group.id),
|
|
9967
|
-
className: `
|
|
10149
|
+
/* @__PURE__ */ import_react25.default.createElement(import_fi19.FiX, { size: 12 })
|
|
10150
|
+
))
|
|
10151
|
+
);
|
|
10152
|
+
})),
|
|
10153
|
+
canEdit && /* @__PURE__ */ import_react25.default.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__ */ import_react25.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react25.default.createElement(
|
|
10154
|
+
"button",
|
|
10155
|
+
{
|
|
10156
|
+
onClick: () => onRequestPickAncestry(group.id),
|
|
10157
|
+
className: `
|
|
9968
10158
|
flex items-center gap-1.5 px-2 py-1 rounded text-xs font-medium transition-colors
|
|
9969
10159
|
${isPickingForThisGroup ? "text-indigo-300 animate-pulse" : "text-slate-500 hover:text-indigo-300 hover:bg-indigo-500/10"}
|
|
9970
10160
|
`,
|
|
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
|
-
|
|
10161
|
+
title: "Adicionar Ancestralidade a este grupo"
|
|
10162
|
+
},
|
|
10163
|
+
isPickingForThisGroup ? /* @__PURE__ */ import_react25.default.createElement(import_fi19.FiCheckCircle, { size: 12 }) : /* @__PURE__ */ import_react25.default.createElement(import_fi19.FiSearch, { size: 12 }),
|
|
10164
|
+
isPickingForThisGroup ? "Selecionando..." : "Adicionar"
|
|
10165
|
+
), /* @__PURE__ */ import_react25.default.createElement(
|
|
10166
|
+
"button",
|
|
10167
|
+
{
|
|
10168
|
+
onClick: () => onAddSubgroup(group.id),
|
|
10169
|
+
className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
|
|
10170
|
+
title: "Criar Subgrupo"
|
|
10171
|
+
},
|
|
10172
|
+
/* @__PURE__ */ import_react25.default.createElement(import_fi19.FiPlus, { size: 14 })
|
|
10173
|
+
)), /* @__PURE__ */ import_react25.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react25.default.createElement(
|
|
10174
|
+
"button",
|
|
10175
|
+
{
|
|
10176
|
+
onClick: () => onIndent(group.id),
|
|
10177
|
+
disabled: !canIndent,
|
|
10178
|
+
className: `p-1.5 rounded transition-colors ${!canIndent ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`,
|
|
10179
|
+
title: "Aninhar no grupo acima"
|
|
10180
|
+
},
|
|
10181
|
+
/* @__PURE__ */ import_react25.default.createElement(import_fi19.FiArrowRight, { size: 14 })
|
|
10182
|
+
), /* @__PURE__ */ import_react25.default.createElement(
|
|
10183
|
+
"button",
|
|
10184
|
+
{
|
|
10185
|
+
onClick: () => onOutdent(group.id),
|
|
10186
|
+
className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
|
|
10187
|
+
title: "Desaninhar"
|
|
10188
|
+
},
|
|
10189
|
+
/* @__PURE__ */ import_react25.default.createElement(import_fi19.FiArrowLeft, { size: 14 })
|
|
10190
|
+
), /* @__PURE__ */ import_react25.default.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), /* @__PURE__ */ import_react25.default.createElement(
|
|
10191
|
+
"button",
|
|
10192
|
+
{
|
|
10193
|
+
onClick: () => onDelete(group.id),
|
|
10194
|
+
className: "p-1.5 text-slate-600 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors",
|
|
10195
|
+
title: "Remover Grupo"
|
|
10196
|
+
},
|
|
10197
|
+
/* @__PURE__ */ import_react25.default.createElement(import_fi19.FiTrash2, { size: 14 })
|
|
10198
|
+
)))
|
|
10199
|
+
), group.children && group.children.length > 0 && /* @__PURE__ */ import_react25.default.createElement("div", { className: "ml-2" }, group.children.map((childGroup, idx) => /* @__PURE__ */ import_react25.default.createElement(
|
|
10009
10200
|
GroupItem,
|
|
10010
10201
|
{
|
|
10011
10202
|
key: childGroup.id,
|
|
@@ -10214,7 +10405,9 @@ function AncestryBoard({
|
|
|
10214
10405
|
const addRecursive = (list) => {
|
|
10215
10406
|
return list.map((g) => {
|
|
10216
10407
|
if (g.id === pickingGroupId) {
|
|
10217
|
-
const exists = g.ancestries.some(
|
|
10408
|
+
const exists = g.ancestries.some(
|
|
10409
|
+
(a) => a.ancestry_id === ancestry.ancestry_id
|
|
10410
|
+
);
|
|
10218
10411
|
if (exists) return g;
|
|
10219
10412
|
return { ...g, ancestries: [...g.ancestries, ancestry] };
|
|
10220
10413
|
}
|
|
@@ -10225,12 +10418,16 @@ function AncestryBoard({
|
|
|
10225
10418
|
});
|
|
10226
10419
|
setPickingGroupId(null);
|
|
10227
10420
|
} else {
|
|
10228
|
-
const fullAncestry = availableAncestries.find(
|
|
10421
|
+
const fullAncestry = availableAncestries.find(
|
|
10422
|
+
(a) => a.ancestry_id === ancestry.ancestry_id
|
|
10423
|
+
) || ancestry;
|
|
10229
10424
|
onSelect(fullAncestry);
|
|
10230
10425
|
}
|
|
10231
10426
|
};
|
|
10232
10427
|
const handlePlayFromGroup = (ancestryId) => {
|
|
10233
|
-
const fullAncestry = availableAncestries.find(
|
|
10428
|
+
const fullAncestry = availableAncestries.find(
|
|
10429
|
+
(a) => a.ancestry_id === ancestryId
|
|
10430
|
+
);
|
|
10234
10431
|
if (fullAncestry && onSelect) {
|
|
10235
10432
|
onSelect(fullAncestry);
|
|
10236
10433
|
}
|
|
@@ -10240,7 +10437,12 @@ function AncestryBoard({
|
|
|
10240
10437
|
const removeRecursive = (list) => {
|
|
10241
10438
|
return list.map((g) => {
|
|
10242
10439
|
if (g.id === groupId) {
|
|
10243
|
-
return {
|
|
10440
|
+
return {
|
|
10441
|
+
...g,
|
|
10442
|
+
ancestries: g.ancestries.filter(
|
|
10443
|
+
(a) => a.ancestry_id !== ancestryId
|
|
10444
|
+
)
|
|
10445
|
+
};
|
|
10244
10446
|
}
|
|
10245
10447
|
return { ...g, children: removeRecursive(g.children) };
|
|
10246
10448
|
});
|
|
@@ -10261,7 +10463,13 @@ function AncestryBoard({
|
|
|
10261
10463
|
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
10464
|
onClick: (e) => e.stopPropagation()
|
|
10263
10465
|
},
|
|
10264
|
-
/* @__PURE__ */ import_react25.default.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ import_react25.default.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ import_react25.default.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ import_react25.default.createElement(import_fi19.FiLayers, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ import_react25.default.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ import_react25.default.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ import_react25.default.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__ */ import_react25.default.createElement(import_react25.default.Fragment, null, /* @__PURE__ */ import_react25.default.createElement(
|
|
10466
|
+
/* @__PURE__ */ import_react25.default.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ import_react25.default.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ import_react25.default.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ import_react25.default.createElement(import_fi19.FiLayers, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ import_react25.default.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ import_react25.default.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ import_react25.default.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__ */ import_react25.default.createElement(import_react25.default.Fragment, null, /* @__PURE__ */ import_react25.default.createElement(
|
|
10467
|
+
import_fi19.FiLoader,
|
|
10468
|
+
{
|
|
10469
|
+
className: "animate-spin text-indigo-400",
|
|
10470
|
+
size: 12
|
|
10471
|
+
}
|
|
10472
|
+
), /* @__PURE__ */ import_react25.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, /* @__PURE__ */ import_react25.default.createElement(import_fi19.FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ import_react25.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, /* @__PURE__ */ import_react25.default.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ import_react25.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ import_react25.default.createElement("div", { className: "flex items-center gap-3" }, pickingGroupId && /* @__PURE__ */ import_react25.default.createElement("span", { className: "text-xs text-indigo-300 font-medium animate-pulse hidden sm:inline-block mr-2" }, "Selecione na lateral..."), canEdit && /* @__PURE__ */ import_react25.default.createElement(
|
|
10265
10473
|
"button",
|
|
10266
10474
|
{
|
|
10267
10475
|
onClick: handleAddRootGroup,
|
|
@@ -10277,57 +10485,75 @@ function AncestryBoard({
|
|
|
10277
10485
|
},
|
|
10278
10486
|
"\xD7"
|
|
10279
10487
|
))),
|
|
10280
|
-
/* @__PURE__ */ import_react25.default.createElement("div", { className: "flex flex-1 overflow-hidden" }, /* @__PURE__ */ import_react25.default.createElement(
|
|
10488
|
+
/* @__PURE__ */ import_react25.default.createElement("div", { className: "flex flex-1 overflow-hidden" }, /* @__PURE__ */ import_react25.default.createElement(
|
|
10489
|
+
"div",
|
|
10490
|
+
{
|
|
10491
|
+
className: `
|
|
10281
10492
|
flex flex-col border-r border-white/10 transition-all duration-300 flex-none
|
|
10282
10493
|
${pickingGroupId ? "w-[25%] border-indigo-500/30" : "w-[20%]"}
|
|
10283
10494
|
min-w-[280px] max-w-[500px] bg-slate-900
|
|
10284
|
-
`
|
|
10285
|
-
|
|
10286
|
-
{
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
10495
|
+
`
|
|
10496
|
+
},
|
|
10497
|
+
/* @__PURE__ */ import_react25.default.createElement("div", { className: "p-3 border-b border-white/5 bg-slate-900/50" }, /* @__PURE__ */ import_react25.default.createElement("div", { className: "relative group" }, /* @__PURE__ */ import_react25.default.createElement(
|
|
10498
|
+
import_fi19.FiSearch,
|
|
10499
|
+
{
|
|
10500
|
+
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"}`
|
|
10501
|
+
}
|
|
10502
|
+
), /* @__PURE__ */ import_react25.default.createElement(
|
|
10503
|
+
"input",
|
|
10504
|
+
{
|
|
10505
|
+
type: "text",
|
|
10506
|
+
placeholder: pickingGroupId ? "Pesquise para adicionar..." : "Pesquisar ancestralidade...",
|
|
10507
|
+
className: `
|
|
10290
10508
|
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
10509
|
${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
10510
|
`,
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10511
|
+
value: searchTerm,
|
|
10512
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
10513
|
+
autoFocus: !pickingGroupId
|
|
10514
|
+
}
|
|
10515
|
+
))),
|
|
10516
|
+
/* @__PURE__ */ import_react25.default.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-3 space-y-2" }, filtered.map((anc) => {
|
|
10517
|
+
const parentNodeName = nodeNamesMap.get(String(anc.ancestral_node)) || "Node Desconhecido";
|
|
10518
|
+
const isPicking = !!pickingGroupId;
|
|
10519
|
+
return /* @__PURE__ */ import_react25.default.createElement(
|
|
10520
|
+
"div",
|
|
10521
|
+
{
|
|
10522
|
+
key: anc.ancestry_id,
|
|
10523
|
+
onClick: () => {
|
|
10524
|
+
if (isPicking) handleSelectAncestry(anc);
|
|
10525
|
+
},
|
|
10526
|
+
className: `
|
|
10308
10527
|
group relative flex items-start gap-3 p-3 text-left rounded-lg border transition-all duration-200
|
|
10309
10528
|
${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
10529
|
`
|
|
10311
|
-
|
|
10312
|
-
|
|
10530
|
+
},
|
|
10531
|
+
/* @__PURE__ */ import_react25.default.createElement(
|
|
10532
|
+
"div",
|
|
10533
|
+
{
|
|
10534
|
+
className: `
|
|
10313
10535
|
mt-0.5 w-8 h-8 rounded-md grid place-content-center shrink-0 border transition-all shadow-lg
|
|
10314
10536
|
${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__ */ import_react25.default.createElement("div", { className: "flex-1 min-w-0 pb-2" }, /* @__PURE__ */ import_react25.default.createElement("div", { className: "flex items-center justify-between gap-2" }, /* @__PURE__ */ import_react25.default.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__ */ import_react25.default.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__ */ import_react25.default.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__ */ import_react25.default.createElement(import_fi19.FiCornerUpRight, { size: 10 }), /* @__PURE__ */ import_react25.default.createElement("span", { className: "truncate max-w-[120px]" }, parentNodeName)), anc.description && /* @__PURE__ */ import_react25.default.createElement("p", { className: "mt-1.5 text-[11px] text-slate-400 line-clamp-2 leading-relaxed opacity-80" }, anc.description)),
|
|
10317
|
-
!isPicking && /* @__PURE__ */ import_react25.default.createElement(
|
|
10318
|
-
"button",
|
|
10319
|
-
{
|
|
10320
|
-
onClick: (e) => {
|
|
10321
|
-
e.stopPropagation();
|
|
10322
|
-
handleSelectAncestry(anc);
|
|
10537
|
+
`
|
|
10323
10538
|
},
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
},
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
10539
|
+
isPicking ? /* @__PURE__ */ import_react25.default.createElement(import_fi19.FiPlus, { size: 16 }) : /* @__PURE__ */ import_react25.default.createElement(import_fi19.FiLayers, { size: 14 })
|
|
10540
|
+
),
|
|
10541
|
+
/* @__PURE__ */ import_react25.default.createElement("div", { className: "flex-1 min-w-0 pb-2" }, /* @__PURE__ */ import_react25.default.createElement("div", { className: "flex items-center justify-between gap-2" }, /* @__PURE__ */ import_react25.default.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__ */ import_react25.default.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__ */ import_react25.default.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__ */ import_react25.default.createElement(import_fi19.FiCornerUpRight, { size: 10 }), /* @__PURE__ */ import_react25.default.createElement("span", { className: "truncate max-w-[120px]" }, parentNodeName)), anc.description && /* @__PURE__ */ import_react25.default.createElement("p", { className: "mt-1.5 text-[11px] text-slate-400 line-clamp-2 leading-relaxed opacity-80" }, anc.description)),
|
|
10542
|
+
!isPicking && /* @__PURE__ */ import_react25.default.createElement(
|
|
10543
|
+
"button",
|
|
10544
|
+
{
|
|
10545
|
+
onClick: (e) => {
|
|
10546
|
+
e.stopPropagation();
|
|
10547
|
+
handleSelectAncestry(anc);
|
|
10548
|
+
},
|
|
10549
|
+
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",
|
|
10550
|
+
title: "Renderizar Ancestralidade"
|
|
10551
|
+
},
|
|
10552
|
+
/* @__PURE__ */ import_react25.default.createElement("div", { className: "bg-indigo-500 text-white p-2 rounded-full shadow-lg hover:bg-indigo-400 hover:scale-110 transition-all" }, /* @__PURE__ */ import_react25.default.createElement(import_fi19.FiPlay, { size: 14, className: "ml-0.5" }))
|
|
10553
|
+
)
|
|
10554
|
+
);
|
|
10555
|
+
}))
|
|
10556
|
+
), /* @__PURE__ */ import_react25.default.createElement("div", { className: "flex flex-col flex-1 bg-slate-950/30" }, /* @__PURE__ */ import_react25.default.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-6 space-y-4" }, groups.length === 0 ? /* @__PURE__ */ import_react25.default.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__ */ import_react25.default.createElement(import_fi19.FiLayers, { size: 24, className: "opacity-20" }), /* @__PURE__ */ import_react25.default.createElement("p", { className: "text-xs text-center px-4" }, canEdit ? /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, "Nenhum grupo criado.", /* @__PURE__ */ import_react25.default.createElement("br", null), 'Use o bot\xE3o "Novo Grupo" acima.') : /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, "Nenhum grupo dispon\xEDvel para visualiza\xE7\xE3o."))) : groups.map((group, index) => /* @__PURE__ */ import_react25.default.createElement(
|
|
10331
10557
|
GroupItem,
|
|
10332
10558
|
{
|
|
10333
10559
|
key: group.id,
|
|
@@ -10406,7 +10632,10 @@ var findNodePath3 = (tree, targetNodeId, currentPath = []) => {
|
|
|
10406
10632
|
}
|
|
10407
10633
|
if (tree.children) {
|
|
10408
10634
|
for (let i = 0; i < tree.children.length; i++) {
|
|
10409
|
-
const res = findNodePath3(tree.children[i], targetNodeId, [
|
|
10635
|
+
const res = findNodePath3(tree.children[i], targetNodeId, [
|
|
10636
|
+
...currentPath,
|
|
10637
|
+
i
|
|
10638
|
+
]);
|
|
10410
10639
|
if (res) return res;
|
|
10411
10640
|
}
|
|
10412
10641
|
}
|
|
@@ -10499,29 +10728,73 @@ function XViewScene({
|
|
|
10499
10728
|
const [userPermissionRole, setUserPermissionRole] = (0, import_react26.useState)(null);
|
|
10500
10729
|
const [isInitialized, setIsInitialized] = (0, import_react26.useState)(false);
|
|
10501
10730
|
const [sceneVersion, setSceneVersion] = (0, import_react26.useState)(0);
|
|
10502
|
-
const [contextMenu, setContextMenu] = (0, import_react26.useState)({
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
|
|
10731
|
+
const [contextMenu, setContextMenu] = (0, import_react26.useState)({
|
|
10732
|
+
visible: false,
|
|
10733
|
+
x: 0,
|
|
10734
|
+
y: 0,
|
|
10735
|
+
nodeData: null
|
|
10736
|
+
});
|
|
10737
|
+
const [multiContextMenu, setMultiContextMenu] = (0, import_react26.useState)({
|
|
10738
|
+
visible: false,
|
|
10739
|
+
x: 0,
|
|
10740
|
+
y: 0,
|
|
10741
|
+
nodeIds: null
|
|
10742
|
+
});
|
|
10743
|
+
const [relationshipMenu, setRelationshipMenu] = (0, import_react26.useState)({
|
|
10744
|
+
visible: false,
|
|
10745
|
+
x: 0,
|
|
10746
|
+
y: 0,
|
|
10747
|
+
linkObject: null
|
|
10748
|
+
});
|
|
10749
|
+
const [creationMode, setCreationMode] = (0, import_react26.useState)({
|
|
10750
|
+
isActive: false,
|
|
10751
|
+
sourceNodeData: null
|
|
10752
|
+
});
|
|
10753
|
+
const [versionMode, setVersionMode] = (0, import_react26.useState)({
|
|
10754
|
+
isActive: false,
|
|
10755
|
+
sourceNodeData: null
|
|
10756
|
+
});
|
|
10507
10757
|
const [questMode, setQuestMode] = (0, import_react26.useState)({ isActive: false });
|
|
10508
10758
|
const [hasFocusedInitial, setHasFocusedInitial] = (0, import_react26.useState)(false);
|
|
10509
10759
|
const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = (0, import_react26.useState)(false);
|
|
10510
|
-
const [ancestryMode, setAncestryMode] = (0, import_react26.useState)({
|
|
10760
|
+
const [ancestryMode, setAncestryMode] = (0, import_react26.useState)({
|
|
10761
|
+
isActive: false,
|
|
10762
|
+
tree: null,
|
|
10763
|
+
selectedParentId: null,
|
|
10764
|
+
isEditMode: false,
|
|
10765
|
+
currentAncestryId: null,
|
|
10766
|
+
ancestryName: "",
|
|
10767
|
+
ancestryDescription: "",
|
|
10768
|
+
ancestryDescriptionSections: [],
|
|
10769
|
+
isAddingNodes: false
|
|
10770
|
+
});
|
|
10511
10771
|
const [readingMode, setReadingMode] = (0, import_react26.useState)({
|
|
10512
10772
|
isActive: false,
|
|
10513
10773
|
ancestry: null,
|
|
10514
10774
|
branchStack: [],
|
|
10515
10775
|
autoAbstraction: false
|
|
10516
10776
|
});
|
|
10517
|
-
const [formPosition, setFormPosition] = (0, import_react26.useState)({
|
|
10777
|
+
const [formPosition, setFormPosition] = (0, import_react26.useState)({
|
|
10778
|
+
left: 16,
|
|
10779
|
+
top: 16,
|
|
10780
|
+
opacity: 0
|
|
10781
|
+
});
|
|
10518
10782
|
const [detailsNode, setDetailsNode] = (0, import_react26.useState)(null);
|
|
10519
10783
|
const [detailsLink, setDetailsLink] = (0, import_react26.useState)(null);
|
|
10520
10784
|
const [ancestryLinkDetails, setAncestryLinkDetails] = (0, import_react26.useState)(null);
|
|
10521
|
-
const [imageViewer, setImageViewer] = (0, import_react26.useState)({
|
|
10522
|
-
|
|
10785
|
+
const [imageViewer, setImageViewer] = (0, import_react26.useState)({
|
|
10786
|
+
visible: false,
|
|
10787
|
+
images: [],
|
|
10788
|
+
startIndex: 0
|
|
10789
|
+
});
|
|
10790
|
+
const [editingAncestryRel, setEditingAncestryRel] = (0, import_react26.useState)({
|
|
10791
|
+
visible: false,
|
|
10792
|
+
data: null,
|
|
10793
|
+
path: null
|
|
10794
|
+
});
|
|
10523
10795
|
const [isImportModalOpen, setIsImportModalOpen] = (0, import_react26.useState)(false);
|
|
10524
10796
|
const [importSuccessMessage, setImportSuccessMessage] = (0, import_react26.useState)("");
|
|
10797
|
+
const [invalidTargetError, setInvalidTargetError] = (0, import_react26.useState)(null);
|
|
10525
10798
|
const [highlightedNodeId, setHighlightedNodeId] = (0, import_react26.useState)(null);
|
|
10526
10799
|
const [isAncestryBoardOpen, setIsAncestryBoardOpen] = (0, import_react26.useState)(false);
|
|
10527
10800
|
const [ancestryBoardData, setAncestryBoardData] = (0, import_react26.useState)([]);
|
|
@@ -10559,8 +10832,23 @@ function XViewScene({
|
|
|
10559
10832
|
ghostElements: { node: null, line: null, aura: null },
|
|
10560
10833
|
creation: { isActive: false, sourceNodeData: null },
|
|
10561
10834
|
connection: { isActive: false, sourceNodeData: null, line: null },
|
|
10562
|
-
relink: {
|
|
10563
|
-
|
|
10835
|
+
relink: {
|
|
10836
|
+
isActive: false,
|
|
10837
|
+
end: null,
|
|
10838
|
+
fixedNodeId: null,
|
|
10839
|
+
originalLine: null,
|
|
10840
|
+
line: null
|
|
10841
|
+
},
|
|
10842
|
+
ancestry: {
|
|
10843
|
+
isActive: false,
|
|
10844
|
+
tree: null,
|
|
10845
|
+
selectedParentId: null,
|
|
10846
|
+
isEditMode: false,
|
|
10847
|
+
currentAncestryId: null,
|
|
10848
|
+
ancestryName: "",
|
|
10849
|
+
ancestryDescription: "",
|
|
10850
|
+
isAddingNodes: false
|
|
10851
|
+
},
|
|
10564
10852
|
glowTexture: null,
|
|
10565
10853
|
nodeIdToParentFileMap: null,
|
|
10566
10854
|
maxAncestryRenderIndex: 0,
|
|
@@ -10569,7 +10857,11 @@ function XViewScene({
|
|
|
10569
10857
|
highlightedNodeId: null
|
|
10570
10858
|
});
|
|
10571
10859
|
const maxReadPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
|
|
10572
|
-
const {
|
|
10860
|
+
const {
|
|
10861
|
+
width: readModeWidth,
|
|
10862
|
+
isResizing: isReadModeResizing,
|
|
10863
|
+
handlePointerDown: handleReadModeResize
|
|
10864
|
+
} = useResizablePanel({
|
|
10573
10865
|
initialWidth: 700,
|
|
10574
10866
|
minWidth: 320,
|
|
10575
10867
|
maxWidth: maxReadPanelW
|
|
@@ -10586,7 +10878,9 @@ function XViewScene({
|
|
|
10586
10878
|
for (const parentFileId in allParentData) {
|
|
10587
10879
|
if (allParentData.hasOwnProperty(parentFileId)) {
|
|
10588
10880
|
const parentFile = allParentData[parentFileId];
|
|
10589
|
-
const parentDbInfo = parentDbsArray.find(
|
|
10881
|
+
const parentDbInfo = parentDbsArray.find(
|
|
10882
|
+
(db) => String(db.db_id) === String(parentFileId)
|
|
10883
|
+
);
|
|
10590
10884
|
const ownerId2 = (parentDbInfo == null ? void 0 : parentDbInfo.owner_id) || null;
|
|
10591
10885
|
const datasetName = parentFile.dataset_name || `Dataset #${parentFileId.substring(0, 6)}`;
|
|
10592
10886
|
if (parentFile.nodes && ownerId2) {
|
|
@@ -10622,7 +10916,10 @@ function XViewScene({
|
|
|
10622
10916
|
if (files.length > 0 && get_single_parent_file && save_view_data) {
|
|
10623
10917
|
for (const file of files) {
|
|
10624
10918
|
try {
|
|
10625
|
-
const parentFileData = await get_single_parent_file(
|
|
10919
|
+
const parentFileData = await get_single_parent_file(
|
|
10920
|
+
file.id,
|
|
10921
|
+
session
|
|
10922
|
+
);
|
|
10626
10923
|
if (parentFileData.success && parentFileData.data) {
|
|
10627
10924
|
parentDataRef.current = {
|
|
10628
10925
|
...parentDataRef.current,
|
|
@@ -10633,7 +10930,11 @@ function XViewScene({
|
|
|
10633
10930
|
owner_id: session.user.id
|
|
10634
10931
|
};
|
|
10635
10932
|
updatedParentDbs.push(newParentDbObject);
|
|
10636
|
-
await add_new_parent_file_to_scene_at_firebase_action(
|
|
10933
|
+
await add_new_parent_file_to_scene_at_firebase_action(
|
|
10934
|
+
sceneConfigId,
|
|
10935
|
+
session,
|
|
10936
|
+
file.id
|
|
10937
|
+
);
|
|
10637
10938
|
importedIds.push(file.id);
|
|
10638
10939
|
successCount++;
|
|
10639
10940
|
}
|
|
@@ -10645,7 +10946,10 @@ function XViewScene({
|
|
|
10645
10946
|
if (viewToImport && get_ancestry_file) {
|
|
10646
10947
|
try {
|
|
10647
10948
|
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(
|
|
10949
|
+
const ancestryResponse = await get_ancestry_file(
|
|
10950
|
+
viewToImport.id,
|
|
10951
|
+
targetViewOwnerId
|
|
10952
|
+
);
|
|
10649
10953
|
if (ancestryResponse.success && Array.isArray(ancestryResponse.data)) {
|
|
10650
10954
|
const viewSpecificAncestries = ancestryResponse.data.filter(
|
|
10651
10955
|
(anc) => anc._source_file_id === viewToImport.id && !anc.is_private
|
|
@@ -10656,14 +10960,21 @@ function XViewScene({
|
|
|
10656
10960
|
_imported_from_view_owner_id: targetViewOwnerId,
|
|
10657
10961
|
_source_file_id: viewToImport.id,
|
|
10658
10962
|
_source_owner_id: targetViewOwnerId,
|
|
10659
|
-
_origin_db_ids: (viewToImport.selected_databases || []).map(
|
|
10963
|
+
_origin_db_ids: (viewToImport.selected_databases || []).map(
|
|
10964
|
+
(db) => db.db_id
|
|
10965
|
+
)
|
|
10660
10966
|
}));
|
|
10661
10967
|
const currentAncestries = ancestryDataRef.current || [];
|
|
10662
10968
|
const newAncestries = processedAncestries.filter(
|
|
10663
|
-
(newAnc) => !currentAncestries.some(
|
|
10969
|
+
(newAnc) => !currentAncestries.some(
|
|
10970
|
+
(curr) => String(curr.ancestry_id) === String(newAnc.ancestry_id)
|
|
10971
|
+
)
|
|
10664
10972
|
);
|
|
10665
10973
|
if (newAncestries.length > 0) {
|
|
10666
|
-
ancestryDataRef.current = [
|
|
10974
|
+
ancestryDataRef.current = [
|
|
10975
|
+
...currentAncestries,
|
|
10976
|
+
...newAncestries
|
|
10977
|
+
];
|
|
10667
10978
|
ancestriesWereImported = true;
|
|
10668
10979
|
}
|
|
10669
10980
|
}
|
|
@@ -10683,7 +10994,9 @@ function XViewScene({
|
|
|
10683
10994
|
if (ancestry_save_url) {
|
|
10684
10995
|
await save_view_data(ancestry_save_url, ancestryDataRef.current);
|
|
10685
10996
|
} else {
|
|
10686
|
-
console.error(
|
|
10997
|
+
console.error(
|
|
10998
|
+
"Erro: URL de salvamento de ancestralidade n\xE3o definida."
|
|
10999
|
+
);
|
|
10687
11000
|
}
|
|
10688
11001
|
}
|
|
10689
11002
|
setSceneVersion((v) => v + 1);
|
|
@@ -10691,114 +11004,137 @@ function XViewScene({
|
|
|
10691
11004
|
setImportSuccessMessage("Importa\xE7\xE3o conclu\xEDda com sucesso.");
|
|
10692
11005
|
setTimeout(() => setImportSuccessMessage(""), 5e3);
|
|
10693
11006
|
} else if (viewToImport && !ancestriesWereImported) {
|
|
10694
|
-
console.warn(
|
|
11007
|
+
console.warn(
|
|
11008
|
+
"Importa\xE7\xE3o finalizada, mas nenhum dado novo foi adicionado."
|
|
11009
|
+
);
|
|
10695
11010
|
}
|
|
10696
11011
|
},
|
|
10697
|
-
[
|
|
11012
|
+
[
|
|
11013
|
+
get_single_parent_file,
|
|
11014
|
+
get_ancestry_file,
|
|
11015
|
+
save_view_data,
|
|
11016
|
+
session,
|
|
11017
|
+
sceneSaveUrl,
|
|
11018
|
+
ancestry_save_url,
|
|
11019
|
+
sceneConfigId,
|
|
11020
|
+
add_new_parent_file_to_scene_at_firebase_action
|
|
11021
|
+
]
|
|
10698
11022
|
);
|
|
10699
11023
|
const handleOpenImageViewer = (images, startIndex) => {
|
|
10700
11024
|
setImageViewer({ visible: true, images, startIndex });
|
|
10701
11025
|
};
|
|
10702
|
-
const tweenToTarget = (0, import_react26.useCallback)(
|
|
10703
|
-
|
|
10704
|
-
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
|
|
10708
|
-
|
|
10709
|
-
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
|
|
10713
|
-
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
11026
|
+
const tweenToTarget = (0, import_react26.useCallback)(
|
|
11027
|
+
(target, zoomFactor = 1, forcedDirection = null) => {
|
|
11028
|
+
const { camera, controls, tweenGroup } = stateRef.current;
|
|
11029
|
+
if (!camera || !controls || !tweenGroup) return;
|
|
11030
|
+
const targetPos = target instanceof THREE3.Mesh ? target.getWorldPosition(new THREE3.Vector3()) : target;
|
|
11031
|
+
const controlsTween = new import_tween2.Tween(controls.target).to(targetPos, 1500).easing(import_tween2.Easing.Cubic.Out);
|
|
11032
|
+
tweenGroup.add(controlsTween);
|
|
11033
|
+
controlsTween.start();
|
|
11034
|
+
let offset;
|
|
11035
|
+
if (forcedDirection) {
|
|
11036
|
+
offset = forcedDirection.clone().normalize().multiplyScalar(x_view_config.CAMERA_ZOOM_DISTANCE / zoomFactor);
|
|
11037
|
+
} else {
|
|
11038
|
+
offset = camera.position.clone().sub(controls.target).normalize().multiplyScalar(x_view_config.CAMERA_ZOOM_DISTANCE / zoomFactor);
|
|
11039
|
+
}
|
|
11040
|
+
const targetCameraPos = targetPos.clone().add(offset);
|
|
11041
|
+
const cameraTween = new import_tween2.Tween(camera.position).to(targetCameraPos, 1500).easing(import_tween2.Easing.Cubic.Out);
|
|
11042
|
+
tweenGroup.add(cameraTween);
|
|
11043
|
+
cameraTween.start();
|
|
11044
|
+
},
|
|
11045
|
+
[]
|
|
11046
|
+
);
|
|
10720
11047
|
const isFromUiOverlay = (event) => {
|
|
10721
11048
|
const t = event == null ? void 0 : event.target;
|
|
10722
11049
|
if (!t || typeof t.closest !== "function") return false;
|
|
10723
11050
|
return !!t.closest(".ui-overlay");
|
|
10724
11051
|
};
|
|
10725
|
-
const buildFullAncestryTree = (0, import_react26.useCallback)(
|
|
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
|
-
}
|
|
11052
|
+
const buildFullAncestryTree = (0, import_react26.useCallback)(
|
|
11053
|
+
(idTree, nodes, ancestries = []) => {
|
|
11054
|
+
if (!idTree) return null;
|
|
11055
|
+
const nodeMap = new Map(nodes.map((n) => [String(n.id), n]));
|
|
11056
|
+
const ancestryMap = new Map(
|
|
11057
|
+
ancestries.map((a) => [String(a.ancestry_id), a])
|
|
11058
|
+
);
|
|
11059
|
+
const recursiveBuild = (treeItem) => {
|
|
11060
|
+
if (!treeItem) return null;
|
|
11061
|
+
if (treeItem.is_section) {
|
|
10758
11062
|
return {
|
|
10759
|
-
...
|
|
10760
|
-
|
|
11063
|
+
...treeItem,
|
|
11064
|
+
children: (treeItem.children || []).map(recursiveBuild).filter(Boolean)
|
|
10761
11065
|
};
|
|
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
|
-
};
|
|
11066
|
+
}
|
|
11067
|
+
let nodeId = treeItem.node_id;
|
|
11068
|
+
if (!nodeId && treeItem.node) nodeId = treeItem.node.id;
|
|
11069
|
+
const fullNode = nodeMap.get(String(nodeId));
|
|
11070
|
+
const effectiveNode = fullNode || treeItem.node;
|
|
11071
|
+
let processedBranches = [];
|
|
11072
|
+
if (treeItem.parallel_branches && Array.isArray(treeItem.parallel_branches)) {
|
|
11073
|
+
processedBranches = treeItem.parallel_branches.map((branch) => {
|
|
11074
|
+
if (branch.linked_ancestry_id) {
|
|
11075
|
+
const linkedAncestry = ancestryMap.get(
|
|
11076
|
+
String(branch.linked_ancestry_id)
|
|
11077
|
+
);
|
|
11078
|
+
if (linkedAncestry && linkedAncestry.tree) {
|
|
11079
|
+
const graftedTree = recursiveBuild(linkedAncestry.tree);
|
|
11080
|
+
return {
|
|
11081
|
+
...branch,
|
|
11082
|
+
name: linkedAncestry.name,
|
|
11083
|
+
description: linkedAncestry.description,
|
|
11084
|
+
description_sections: linkedAncestry.description_sections,
|
|
11085
|
+
tree: graftedTree,
|
|
11086
|
+
isLinked: true
|
|
11087
|
+
};
|
|
11088
|
+
}
|
|
10793
11089
|
}
|
|
10794
|
-
|
|
10795
|
-
|
|
10796
|
-
|
|
11090
|
+
return {
|
|
11091
|
+
...branch,
|
|
11092
|
+
tree: recursiveBuild(branch.tree)
|
|
11093
|
+
};
|
|
11094
|
+
});
|
|
11095
|
+
}
|
|
11096
|
+
return {
|
|
11097
|
+
...effectiveNode ? { node: effectiveNode } : { node: { id: nodeId, name: "Unknown" } },
|
|
11098
|
+
relationship: treeItem.relationship || {},
|
|
11099
|
+
children: (treeItem.children || []).map(recursiveBuild).filter(Boolean),
|
|
11100
|
+
parallel_branches: processedBranches
|
|
11101
|
+
};
|
|
10797
11102
|
};
|
|
10798
|
-
|
|
10799
|
-
|
|
10800
|
-
|
|
10801
|
-
|
|
11103
|
+
let rootId = idTree.node_id;
|
|
11104
|
+
if (!rootId && idTree.node) rootId = idTree.node.id;
|
|
11105
|
+
if (rootId) {
|
|
11106
|
+
const rootNode = nodeMap.get(String(rootId));
|
|
11107
|
+
const effectiveRoot = rootNode || idTree.node;
|
|
11108
|
+
if (!effectiveRoot) return null;
|
|
11109
|
+
return {
|
|
11110
|
+
node: effectiveRoot,
|
|
11111
|
+
relationship: idTree.relationship || {},
|
|
11112
|
+
children: (idTree.children || []).map(recursiveBuild).filter(Boolean),
|
|
11113
|
+
parallel_branches: (idTree.parallel_branches || []).map((branch) => {
|
|
11114
|
+
if (branch.linked_ancestry_id) {
|
|
11115
|
+
const linkedAncestry = ancestryMap.get(
|
|
11116
|
+
String(branch.linked_ancestry_id)
|
|
11117
|
+
);
|
|
11118
|
+
if (linkedAncestry && linkedAncestry.tree) {
|
|
11119
|
+
return {
|
|
11120
|
+
...branch,
|
|
11121
|
+
name: linkedAncestry.name,
|
|
11122
|
+
description: linkedAncestry.description,
|
|
11123
|
+
description_sections: linkedAncestry.description_sections,
|
|
11124
|
+
tree: recursiveBuild(linkedAncestry.tree),
|
|
11125
|
+
isLinked: true
|
|
11126
|
+
};
|
|
11127
|
+
}
|
|
11128
|
+
}
|
|
11129
|
+
return { ...branch, tree: recursiveBuild(branch.tree) };
|
|
11130
|
+
})
|
|
11131
|
+
};
|
|
11132
|
+
}
|
|
11133
|
+
return recursiveBuild(idTree);
|
|
11134
|
+
},
|
|
11135
|
+
[]
|
|
11136
|
+
);
|
|
11137
|
+
const handleActivateTimeline = (0, import_react26.useCallback)(() => {
|
|
10802
11138
|
const { nodeObjects, tweenGroup, timelineIntervalsGroup } = stateRef.current;
|
|
10803
11139
|
if (!nodeObjects || !tweenGroup || !timelineIntervalsGroup) return;
|
|
10804
11140
|
while (timelineIntervalsGroup.children.length > 0) {
|
|
@@ -10878,10 +11214,12 @@ function XViewScene({
|
|
|
10878
11214
|
if (timelineNodes.length === 0) return;
|
|
10879
11215
|
const sortedTimePoints = Array.from(allTimePoints).sort((a, b) => a - b);
|
|
10880
11216
|
const maxTimeIndex = sortedTimePoints.length - 1;
|
|
10881
|
-
const timeToYMap = new Map(
|
|
10882
|
-
time,
|
|
10883
|
-
|
|
10884
|
-
|
|
11217
|
+
const timeToYMap = new Map(
|
|
11218
|
+
sortedTimePoints.map((time, index) => [
|
|
11219
|
+
time,
|
|
11220
|
+
(index - maxTimeIndex) * TIMELINE_LAYER_SPACING_Y
|
|
11221
|
+
])
|
|
11222
|
+
);
|
|
10885
11223
|
timelineNodes.sort((a, b) => {
|
|
10886
11224
|
if (a.isUndated && b.isUndated) return 0;
|
|
10887
11225
|
if (a.isUndated) return 1;
|
|
@@ -10924,7 +11262,12 @@ function XViewScene({
|
|
|
10924
11262
|
if (type === "interval") {
|
|
10925
11263
|
const endY = timeToYMap.get(endDate.getTime());
|
|
10926
11264
|
if (endY > y) {
|
|
10927
|
-
const barGeometry = new THREE3.CylinderGeometry(
|
|
11265
|
+
const barGeometry = new THREE3.CylinderGeometry(
|
|
11266
|
+
TIMELINE_INTERVAL_BAR_RADIUS,
|
|
11267
|
+
TIMELINE_INTERVAL_BAR_RADIUS,
|
|
11268
|
+
1,
|
|
11269
|
+
16
|
|
11270
|
+
);
|
|
10928
11271
|
const barMaterial = new THREE3.MeshStandardMaterial({
|
|
10929
11272
|
color: TIMELINE_GOLD_COLOR,
|
|
10930
11273
|
emissive: 12092939,
|
|
@@ -10953,7 +11296,8 @@ function XViewScene({
|
|
|
10953
11296
|
}, []);
|
|
10954
11297
|
const handleVersionTimeline = (0, import_react26.useCallback)((sourceMesh, versionMeshes) => {
|
|
10955
11298
|
const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
|
|
10956
|
-
if (!tweenGroup || !timelineIntervalsGroup || versionMeshes.length === 0)
|
|
11299
|
+
if (!tweenGroup || !timelineIntervalsGroup || versionMeshes.length === 0)
|
|
11300
|
+
return;
|
|
10957
11301
|
versionMeshes.forEach((mesh) => {
|
|
10958
11302
|
const oldLabel = mesh.getObjectByName("timelineLabel");
|
|
10959
11303
|
if (oldLabel) {
|
|
@@ -10969,7 +11313,8 @@ function XViewScene({
|
|
|
10969
11313
|
}
|
|
10970
11314
|
if (mesh.userData.timelineEndLabel) {
|
|
10971
11315
|
timelineIntervalsGroup.remove(mesh.userData.timelineEndLabel);
|
|
10972
|
-
if (mesh.userData.timelineEndLabel.material.map)
|
|
11316
|
+
if (mesh.userData.timelineEndLabel.material.map)
|
|
11317
|
+
mesh.userData.timelineEndLabel.material.map.dispose();
|
|
10973
11318
|
mesh.userData.timelineEndLabel.material.dispose();
|
|
10974
11319
|
delete mesh.userData.timelineEndLabel;
|
|
10975
11320
|
}
|
|
@@ -11029,8 +11374,15 @@ function XViewScene({
|
|
|
11029
11374
|
});
|
|
11030
11375
|
if (timelineNodes.length === 0) return;
|
|
11031
11376
|
const sortedTimePoints = Array.from(allTimePoints).sort((a, b) => a - b);
|
|
11032
|
-
const timeToYMap = new Map(
|
|
11033
|
-
|
|
11377
|
+
const timeToYMap = new Map(
|
|
11378
|
+
sortedTimePoints.map((time, index) => [
|
|
11379
|
+
time,
|
|
11380
|
+
index * TIMELINE_LAYER_SPACING_Y
|
|
11381
|
+
])
|
|
11382
|
+
);
|
|
11383
|
+
timelineNodes.sort(
|
|
11384
|
+
(a, b) => a.startDate - b.startDate || a.endDate - b.endDate
|
|
11385
|
+
);
|
|
11034
11386
|
const baseX = sourceMesh.position.x + TIMELINE_NODE_SPACING_X;
|
|
11035
11387
|
const baseY = sourceMesh.position.y;
|
|
11036
11388
|
const maxNodeIndex = timelineNodes.length - 1;
|
|
@@ -11048,7 +11400,12 @@ function XViewScene({
|
|
|
11048
11400
|
if (type === "interval") {
|
|
11049
11401
|
const relativeEndY = timeToYMap.get(endDate.getTime()) - timeToYMap.get(startDate.getTime());
|
|
11050
11402
|
if (relativeEndY > 0) {
|
|
11051
|
-
const barGeometry = new THREE3.CylinderGeometry(
|
|
11403
|
+
const barGeometry = new THREE3.CylinderGeometry(
|
|
11404
|
+
TIMELINE_INTERVAL_BAR_RADIUS,
|
|
11405
|
+
TIMELINE_INTERVAL_BAR_RADIUS,
|
|
11406
|
+
1,
|
|
11407
|
+
16
|
|
11408
|
+
);
|
|
11052
11409
|
const barMaterial = new THREE3.MeshStandardMaterial({
|
|
11053
11410
|
color: TIMELINE_GOLD_COLOR,
|
|
11054
11411
|
emissive: 12092939,
|
|
@@ -11085,15 +11442,31 @@ function XViewScene({
|
|
|
11085
11442
|
try {
|
|
11086
11443
|
const typeStr = (viewParams == null ? void 0 : viewParams.type) || "";
|
|
11087
11444
|
const sceneType = typeStr.toLowerCase().includes("database") ? "database" : "view";
|
|
11088
|
-
const scenePromise = get_scene_view_data(
|
|
11445
|
+
const scenePromise = get_scene_view_data(
|
|
11446
|
+
configPath,
|
|
11447
|
+
ownerId2,
|
|
11448
|
+
typeStr,
|
|
11449
|
+
session,
|
|
11450
|
+
focusNodeId,
|
|
11451
|
+
focusAncestryId
|
|
11452
|
+
);
|
|
11089
11453
|
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([
|
|
11454
|
+
const [sceneResponse, boardResponse] = await Promise.all([
|
|
11455
|
+
scenePromise,
|
|
11456
|
+
boardPromise
|
|
11457
|
+
]);
|
|
11091
11458
|
if ((sceneResponse == null ? void 0 : sceneResponse.success) && ((_a2 = sceneResponse.data) == null ? void 0 : _a2.scene) && ((_b2 = sceneResponse.data) == null ? void 0 : _b2.parent)) {
|
|
11092
11459
|
if (focusNodeId) {
|
|
11093
|
-
let targetNode = sceneResponse.data.scene.nodes.find(
|
|
11460
|
+
let targetNode = sceneResponse.data.scene.nodes.find(
|
|
11461
|
+
(n) => String(n.id) === String(focusNodeId)
|
|
11462
|
+
);
|
|
11094
11463
|
if (!targetNode) {
|
|
11095
|
-
const allParentNodes = Object.values(
|
|
11096
|
-
|
|
11464
|
+
const allParentNodes = Object.values(
|
|
11465
|
+
sceneResponse.data.parent
|
|
11466
|
+
).flatMap((f) => f.nodes || []);
|
|
11467
|
+
targetNode = allParentNodes.find(
|
|
11468
|
+
(n) => String(n.id) === String(focusNodeId)
|
|
11469
|
+
);
|
|
11097
11470
|
}
|
|
11098
11471
|
if (targetNode) {
|
|
11099
11472
|
sceneResponse.data.scene.nodes = [targetNode];
|
|
@@ -11106,12 +11479,24 @@ function XViewScene({
|
|
|
11106
11479
|
sceneDataRef.current = sceneResponse.data.scene;
|
|
11107
11480
|
parentDataRef.current = sceneResponse.data.parent;
|
|
11108
11481
|
ancestryDataRef.current = sceneResponse.data.ancestry;
|
|
11109
|
-
console.log(
|
|
11110
|
-
|
|
11111
|
-
|
|
11482
|
+
console.log(
|
|
11483
|
+
"Console de sceneResponse.data.scene:",
|
|
11484
|
+
sceneResponse.data.scene
|
|
11485
|
+
);
|
|
11486
|
+
console.log(
|
|
11487
|
+
"Console de sceneResponse.data.parent:",
|
|
11488
|
+
sceneResponse.data.parent
|
|
11489
|
+
);
|
|
11490
|
+
console.log(
|
|
11491
|
+
"Console de sceneResponse.data.ancestry:",
|
|
11492
|
+
sceneResponse.data.ancestry
|
|
11493
|
+
);
|
|
11112
11494
|
setIsInitialized(true);
|
|
11113
11495
|
} else {
|
|
11114
|
-
console.error(
|
|
11496
|
+
console.error(
|
|
11497
|
+
"Falha ao buscar dados da cena:",
|
|
11498
|
+
(sceneResponse == null ? void 0 : sceneResponse.error) || "Resposta inv\xE1lida."
|
|
11499
|
+
);
|
|
11115
11500
|
}
|
|
11116
11501
|
if (boardResponse == null ? void 0 : boardResponse.success) {
|
|
11117
11502
|
setAncestryBoardData(boardResponse.data);
|
|
@@ -11130,7 +11515,9 @@ function XViewScene({
|
|
|
11130
11515
|
console.error("Usu\xE1rio n\xE3o autenticado. Acesso negado.");
|
|
11131
11516
|
setIsLoading(false);
|
|
11132
11517
|
} else if (!sceneConfigId && status !== "loading") {
|
|
11133
|
-
console.warn(
|
|
11518
|
+
console.warn(
|
|
11519
|
+
"Nenhum par\xE2metro de cena encontrado na URL ou falha na decripta\xE7\xE3o."
|
|
11520
|
+
);
|
|
11134
11521
|
setIsLoading(false);
|
|
11135
11522
|
}
|
|
11136
11523
|
}, [
|
|
@@ -11151,33 +11538,46 @@ function XViewScene({
|
|
|
11151
11538
|
const objs = stateRef.current.nodeObjects || {};
|
|
11152
11539
|
return !!objs[key];
|
|
11153
11540
|
}, []);
|
|
11154
|
-
const addOrUpdateNodeMesh = (0, import_react26.useCallback)(
|
|
11155
|
-
|
|
11156
|
-
|
|
11157
|
-
|
|
11158
|
-
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
tweenGroup
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
|
|
11170
|
-
|
|
11541
|
+
const addOrUpdateNodeMesh = (0, import_react26.useCallback)(
|
|
11542
|
+
(nodeData, position, suppressVersionUpdate = false) => {
|
|
11543
|
+
const {
|
|
11544
|
+
graphGroup,
|
|
11545
|
+
nodeObjects,
|
|
11546
|
+
clickableNodes,
|
|
11547
|
+
glowTexture,
|
|
11548
|
+
tweenGroup
|
|
11549
|
+
} = stateRef.current;
|
|
11550
|
+
const nodeId = String(nodeData.id);
|
|
11551
|
+
if (nodeObjects[nodeId]) {
|
|
11552
|
+
const existingMesh = nodeObjects[nodeId];
|
|
11553
|
+
if (position) {
|
|
11554
|
+
const updateTween = new import_tween2.Tween(existingMesh.position).to(position, 800).easing(import_tween2.Easing.Cubic.Out);
|
|
11555
|
+
tweenGroup.add(updateTween);
|
|
11556
|
+
updateTween.start();
|
|
11557
|
+
}
|
|
11558
|
+
return existingMesh;
|
|
11559
|
+
}
|
|
11560
|
+
const mesh = createNodeMesh(
|
|
11561
|
+
nodeData,
|
|
11562
|
+
position || new THREE3.Vector3(),
|
|
11563
|
+
glowTexture
|
|
11564
|
+
);
|
|
11565
|
+
graphGroup.add(mesh);
|
|
11566
|
+
if (mesh.userData.labelObject) {
|
|
11567
|
+
if (mesh.userData.labelOffset) {
|
|
11568
|
+
mesh.userData.labelObject.position.copy(mesh.position).add(mesh.userData.labelOffset);
|
|
11569
|
+
}
|
|
11570
|
+
graphGroup.add(mesh.userData.labelObject);
|
|
11171
11571
|
}
|
|
11172
|
-
|
|
11173
|
-
|
|
11174
|
-
|
|
11175
|
-
|
|
11176
|
-
|
|
11177
|
-
|
|
11178
|
-
}
|
|
11179
|
-
|
|
11180
|
-
|
|
11572
|
+
nodeObjects[nodeId] = mesh;
|
|
11573
|
+
clickableNodes.push(mesh);
|
|
11574
|
+
if (!suppressVersionUpdate) {
|
|
11575
|
+
setSceneVersion((v) => v + 1);
|
|
11576
|
+
}
|
|
11577
|
+
return mesh;
|
|
11578
|
+
},
|
|
11579
|
+
[]
|
|
11580
|
+
);
|
|
11181
11581
|
(0, import_react26.useEffect)(() => {
|
|
11182
11582
|
if (!isInitialized || !sceneDataRef.current) return;
|
|
11183
11583
|
const currentMount = mountRef.current;
|
|
@@ -11192,7 +11592,12 @@ function XViewScene({
|
|
|
11192
11592
|
const scene = new THREE3.Scene();
|
|
11193
11593
|
scene.background = new THREE3.Color(0);
|
|
11194
11594
|
stateRef.current.scene = scene;
|
|
11195
|
-
const camera = new THREE3.PerspectiveCamera(
|
|
11595
|
+
const camera = new THREE3.PerspectiveCamera(
|
|
11596
|
+
75,
|
|
11597
|
+
currentMount.clientWidth / currentMount.clientHeight,
|
|
11598
|
+
0.1,
|
|
11599
|
+
2e3
|
|
11600
|
+
);
|
|
11196
11601
|
camera.position.set(0, 60, 120);
|
|
11197
11602
|
stateRef.current.camera = camera;
|
|
11198
11603
|
const renderer = new THREE3.WebGLRenderer({ antialias: true });
|
|
@@ -11213,7 +11618,12 @@ function XViewScene({
|
|
|
11213
11618
|
directionalLight.position.set(50, 50, 50);
|
|
11214
11619
|
scene.add(directionalLight);
|
|
11215
11620
|
const renderScene = new import_RenderPass.RenderPass(scene, camera);
|
|
11216
|
-
const bloomPass = new import_UnrealBloomPass.UnrealBloomPass(
|
|
11621
|
+
const bloomPass = new import_UnrealBloomPass.UnrealBloomPass(
|
|
11622
|
+
new THREE3.Vector2(currentMount.clientWidth, currentMount.clientHeight),
|
|
11623
|
+
x_view_config.BLOOM_EFFECT.strength,
|
|
11624
|
+
x_view_config.BLOOM_EFFECT.radius,
|
|
11625
|
+
x_view_config.BLOOM_EFFECT.threshold
|
|
11626
|
+
);
|
|
11217
11627
|
const composer = new import_EffectComposer.EffectComposer(renderer);
|
|
11218
11628
|
composer.addPass(renderScene);
|
|
11219
11629
|
composer.addPass(bloomPass);
|
|
@@ -11256,8 +11666,16 @@ function XViewScene({
|
|
|
11256
11666
|
const sourceNode = nodeObjects[String(linksArray[0].source)];
|
|
11257
11667
|
const targetNode = nodeObjects[String(linksArray[0].target)];
|
|
11258
11668
|
if (sourceNode && targetNode) {
|
|
11259
|
-
const resolution = new THREE3.Vector2(
|
|
11260
|
-
|
|
11669
|
+
const resolution = new THREE3.Vector2(
|
|
11670
|
+
currentMount.clientWidth,
|
|
11671
|
+
currentMount.clientHeight
|
|
11672
|
+
);
|
|
11673
|
+
const newLinks = createMultipleLinkLines(
|
|
11674
|
+
linksArray,
|
|
11675
|
+
sourceNode,
|
|
11676
|
+
targetNode,
|
|
11677
|
+
resolution
|
|
11678
|
+
);
|
|
11261
11679
|
newLinks.forEach((line, idx) => {
|
|
11262
11680
|
const meta = linksArray[idx];
|
|
11263
11681
|
if (meta) {
|
|
@@ -11298,7 +11716,10 @@ function XViewScene({
|
|
|
11298
11716
|
function tryPickNode() {
|
|
11299
11717
|
raycaster.setFromCamera(mouse, camera);
|
|
11300
11718
|
raycaster.layers.enable(GHOST_BLOOM_LAYER);
|
|
11301
|
-
const intersects = raycaster.intersectObjects(
|
|
11719
|
+
const intersects = raycaster.intersectObjects(
|
|
11720
|
+
stateRef.current.clickableNodes,
|
|
11721
|
+
false
|
|
11722
|
+
);
|
|
11302
11723
|
return intersects.length > 0 ? intersects[0].object : null;
|
|
11303
11724
|
}
|
|
11304
11725
|
function findClosestLinkToRay() {
|
|
@@ -11310,7 +11731,10 @@ function XViewScene({
|
|
|
11310
11731
|
x: (mouse.x * 0.5 + 0.5) * clientWidth,
|
|
11311
11732
|
y: (-mouse.y * 0.5 + 0.5) * clientHeight
|
|
11312
11733
|
};
|
|
11313
|
-
const allVisibleLinks = [
|
|
11734
|
+
const allVisibleLinks = [
|
|
11735
|
+
...stateRef.current.allLinks,
|
|
11736
|
+
...stateRef.current.ancestryLinks
|
|
11737
|
+
];
|
|
11314
11738
|
const THRESH = x_view_config.LINE_HOVER_THRESHOLD_PX + 4;
|
|
11315
11739
|
const tmpP1 = new THREE3.Vector3();
|
|
11316
11740
|
const tmpP2 = new THREE3.Vector3();
|
|
@@ -11325,19 +11749,35 @@ function XViewScene({
|
|
|
11325
11749
|
const up = new THREE3.Vector3(0, 1, 0);
|
|
11326
11750
|
const normal = new THREE3.Vector3().crossVectors(dir, up).normalize();
|
|
11327
11751
|
const controlPoint = mid.add(normal.multiplyScalar(curveOffset || 0));
|
|
11328
|
-
const curve = new THREE3.QuadraticBezierCurve3(
|
|
11752
|
+
const curve = new THREE3.QuadraticBezierCurve3(
|
|
11753
|
+
start,
|
|
11754
|
+
controlPoint,
|
|
11755
|
+
end
|
|
11756
|
+
);
|
|
11329
11757
|
const points = curve.getPoints(x_view_config.CURVE_SEGMENTS || 32);
|
|
11330
11758
|
for (let i = 0; i < points.length - 1; i++) {
|
|
11331
11759
|
tmpP1.copy(points[i]).project(camera);
|
|
11332
11760
|
tmpP2.copy(points[i + 1]).project(camera);
|
|
11333
|
-
const p1_px = {
|
|
11334
|
-
|
|
11761
|
+
const p1_px = {
|
|
11762
|
+
x: (tmpP1.x * 0.5 + 0.5) * clientWidth,
|
|
11763
|
+
y: (-tmpP1.y * 0.5 + 0.5) * clientHeight
|
|
11764
|
+
};
|
|
11765
|
+
const p2_px = {
|
|
11766
|
+
x: (tmpP2.x * 0.5 + 0.5) * clientWidth,
|
|
11767
|
+
y: (-tmpP2.y * 0.5 + 0.5) * clientHeight
|
|
11768
|
+
};
|
|
11335
11769
|
const L2 = (p2_px.x - p1_px.x) ** 2 + (p2_px.y - p1_px.y) ** 2;
|
|
11336
11770
|
if (L2 === 0) continue;
|
|
11337
11771
|
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
11772
|
t = Math.max(0, Math.min(1, t));
|
|
11339
|
-
const closestPoint = {
|
|
11340
|
-
|
|
11773
|
+
const closestPoint = {
|
|
11774
|
+
x: p1_px.x + t * (p2_px.x - p1_px.x),
|
|
11775
|
+
y: p1_px.y + t * (p2_px.y - p1_px.y)
|
|
11776
|
+
};
|
|
11777
|
+
const dist = Math.hypot(
|
|
11778
|
+
mousePixels.x - closestPoint.x,
|
|
11779
|
+
mousePixels.y - closestPoint.y
|
|
11780
|
+
);
|
|
11341
11781
|
if (dist < THRESH && dist < minDistance) {
|
|
11342
11782
|
minDistance = dist;
|
|
11343
11783
|
closestLink = link;
|
|
@@ -11346,14 +11786,26 @@ function XViewScene({
|
|
|
11346
11786
|
} else {
|
|
11347
11787
|
const p1 = new THREE3.Vector3().copy(sourceNode.position).project(camera);
|
|
11348
11788
|
const p2 = new THREE3.Vector3().copy(targetNode.position).project(camera);
|
|
11349
|
-
const p1_px = {
|
|
11350
|
-
|
|
11789
|
+
const p1_px = {
|
|
11790
|
+
x: (p1.x * 0.5 + 0.5) * clientWidth,
|
|
11791
|
+
y: (-p1.y * 0.5 + 0.5) * clientHeight
|
|
11792
|
+
};
|
|
11793
|
+
const p2_px = {
|
|
11794
|
+
x: (p2.x * 0.5 + 0.5) * clientWidth,
|
|
11795
|
+
y: (-p2.y * 0.5 + 0.5) * clientHeight
|
|
11796
|
+
};
|
|
11351
11797
|
const L2 = (p2_px.x - p1_px.x) ** 2 + (p2_px.y - p1_px.y) ** 2;
|
|
11352
11798
|
if (L2 === 0) return;
|
|
11353
11799
|
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
11800
|
t = Math.max(0, Math.min(1, t));
|
|
11355
|
-
const closestPoint = {
|
|
11356
|
-
|
|
11801
|
+
const closestPoint = {
|
|
11802
|
+
x: p1_px.x + t * (p2_px.x - p1_px.x),
|
|
11803
|
+
y: p1_px.y + t * (p2_px.y - p1_px.y)
|
|
11804
|
+
};
|
|
11805
|
+
const dist = Math.hypot(
|
|
11806
|
+
mousePixels.x - closestPoint.x,
|
|
11807
|
+
mousePixels.y - closestPoint.y
|
|
11808
|
+
);
|
|
11357
11809
|
if (dist < THRESH && dist < minDistance) {
|
|
11358
11810
|
minDistance = dist;
|
|
11359
11811
|
closestLink = link;
|
|
@@ -11388,7 +11840,11 @@ function XViewScene({
|
|
|
11388
11840
|
if (picked && !stateRef.current.ancestry.isActive) {
|
|
11389
11841
|
stateRef.current.controls.enabled = false;
|
|
11390
11842
|
}
|
|
11391
|
-
stateRef.current.pointerDown = {
|
|
11843
|
+
stateRef.current.pointerDown = {
|
|
11844
|
+
isDown: true,
|
|
11845
|
+
x: event.clientX,
|
|
11846
|
+
y: event.clientY
|
|
11847
|
+
};
|
|
11392
11848
|
stateRef.current.dragCandidate = picked;
|
|
11393
11849
|
}
|
|
11394
11850
|
function onPointerMove(event) {
|
|
@@ -11399,7 +11855,10 @@ function XViewScene({
|
|
|
11399
11855
|
const raycaster2 = new THREE3.Raycaster();
|
|
11400
11856
|
raycaster2.setFromCamera(mouse, camera);
|
|
11401
11857
|
camera.getWorldDirection(plane.normal);
|
|
11402
|
-
plane.setFromNormalAndCoplanarPoint(
|
|
11858
|
+
plane.setFromNormalAndCoplanarPoint(
|
|
11859
|
+
plane.normal,
|
|
11860
|
+
stateRef.current.draggedNode.position
|
|
11861
|
+
);
|
|
11403
11862
|
if (raycaster2.ray.intersectPlane(plane, intersectionPoint)) {
|
|
11404
11863
|
const draggedNode = stateRef.current.draggedNode;
|
|
11405
11864
|
draggedNode.position.copy(intersectionPoint);
|
|
@@ -11408,7 +11867,8 @@ function XViewScene({
|
|
|
11408
11867
|
}
|
|
11409
11868
|
if (stateRef.current.connection.isActive || stateRef.current.relink.isActive || stateRef.current.ancestry.isActive) {
|
|
11410
11869
|
const newHoveredNode2 = tryPickNode();
|
|
11411
|
-
if (stateRef.current.hoveredNode !== newHoveredNode2)
|
|
11870
|
+
if (stateRef.current.hoveredNode !== newHoveredNode2)
|
|
11871
|
+
stateRef.current.hoveredNode = newHoveredNode2;
|
|
11412
11872
|
if (currentMount) {
|
|
11413
11873
|
let fixedId = null;
|
|
11414
11874
|
if (stateRef.current.connection.isActive) {
|
|
@@ -11439,14 +11899,20 @@ function XViewScene({
|
|
|
11439
11899
|
stateRef.current.controls.enabled = false;
|
|
11440
11900
|
if (currentMount) currentMount.style.cursor = "grabbing";
|
|
11441
11901
|
camera.getWorldDirection(plane.normal);
|
|
11442
|
-
plane.setFromNormalAndCoplanarPoint(
|
|
11902
|
+
plane.setFromNormalAndCoplanarPoint(
|
|
11903
|
+
plane.normal,
|
|
11904
|
+
stateRef.current.draggedNode.position
|
|
11905
|
+
);
|
|
11443
11906
|
}
|
|
11444
11907
|
}
|
|
11445
11908
|
const newHoveredNode = tryPickNode();
|
|
11446
11909
|
const newHoveredLink = !newHoveredNode ? findClosestLinkToRay() : null;
|
|
11447
|
-
if (stateRef.current.hoveredNode !== newHoveredNode)
|
|
11448
|
-
|
|
11449
|
-
if (
|
|
11910
|
+
if (stateRef.current.hoveredNode !== newHoveredNode)
|
|
11911
|
+
stateRef.current.hoveredNode = newHoveredNode;
|
|
11912
|
+
if (stateRef.current.hoveredLink !== newHoveredLink)
|
|
11913
|
+
stateRef.current.hoveredLink = newHoveredLink;
|
|
11914
|
+
if (currentMount)
|
|
11915
|
+
currentMount.style.cursor = newHoveredNode || newHoveredLink ? "pointer" : "grab";
|
|
11450
11916
|
}
|
|
11451
11917
|
const isNodeInTree = (tree, nodeId) => {
|
|
11452
11918
|
if (!tree) return false;
|
|
@@ -11461,7 +11927,10 @@ function XViewScene({
|
|
|
11461
11927
|
const context = actionHandlerContext;
|
|
11462
11928
|
if (connection.isActive) {
|
|
11463
11929
|
if (hoveredNode && String(hoveredNode.userData.id) !== String(connection.sourceNodeData.id)) {
|
|
11464
|
-
await userActionHandlers.handleCompleteConnection(
|
|
11930
|
+
await userActionHandlers.handleCompleteConnection(
|
|
11931
|
+
context,
|
|
11932
|
+
hoveredNode.userData
|
|
11933
|
+
);
|
|
11465
11934
|
} else {
|
|
11466
11935
|
userActionHandlers.handleCancelConnection(context);
|
|
11467
11936
|
}
|
|
@@ -11469,7 +11938,10 @@ function XViewScene({
|
|
|
11469
11938
|
}
|
|
11470
11939
|
if (relink.isActive) {
|
|
11471
11940
|
if (hoveredNode && String(hoveredNode.userData.id) !== String(relink.fixedNodeId)) {
|
|
11472
|
-
await userActionHandlers.handleCompleteRelink(
|
|
11941
|
+
await userActionHandlers.handleCompleteRelink(
|
|
11942
|
+
context,
|
|
11943
|
+
hoveredNode.userData
|
|
11944
|
+
);
|
|
11473
11945
|
} else {
|
|
11474
11946
|
userActionHandlers.handleCancelRelink(context);
|
|
11475
11947
|
}
|
|
@@ -11487,7 +11959,9 @@ function XViewScene({
|
|
|
11487
11959
|
const clickedNodeId = String(clickedNode.userData.id);
|
|
11488
11960
|
const parentId = String(currentSelectedParent);
|
|
11489
11961
|
if (clickedNodeId === parentId) {
|
|
11490
|
-
alert(
|
|
11962
|
+
alert(
|
|
11963
|
+
"Erro: N\xE3o \xE9 poss\xEDvel adicionar um Node como filho dele mesmo."
|
|
11964
|
+
);
|
|
11491
11965
|
return;
|
|
11492
11966
|
}
|
|
11493
11967
|
const parentInfo = stateRef.current.nodeIdToParentFileMap.get(clickedNodeId);
|
|
@@ -11497,16 +11971,33 @@ function XViewScene({
|
|
|
11497
11971
|
const addChildToNode = (current, targetParentId, childNode) => {
|
|
11498
11972
|
const currentId = current.is_section ? current.id || current.section_id : String(current.node.id);
|
|
11499
11973
|
if (String(currentId) === String(targetParentId)) {
|
|
11500
|
-
const alreadyExists = current.children.some(
|
|
11974
|
+
const alreadyExists = current.children.some(
|
|
11975
|
+
(child) => !child.is_section && String(child.node.id) === String(childNode.id)
|
|
11976
|
+
);
|
|
11501
11977
|
if (alreadyExists) return current;
|
|
11502
|
-
return {
|
|
11978
|
+
return {
|
|
11979
|
+
...current,
|
|
11980
|
+
children: [
|
|
11981
|
+
...current.children,
|
|
11982
|
+
{ node: childNode, children: [], relationship: {} }
|
|
11983
|
+
]
|
|
11984
|
+
};
|
|
11503
11985
|
}
|
|
11504
|
-
return {
|
|
11986
|
+
return {
|
|
11987
|
+
...current,
|
|
11988
|
+
children: current.children.map(
|
|
11989
|
+
(c) => addChildToNode(c, targetParentId, childNode)
|
|
11990
|
+
)
|
|
11991
|
+
};
|
|
11505
11992
|
};
|
|
11506
11993
|
setAncestryMode((prev) => {
|
|
11507
11994
|
const treeKey = isAbstraction ? "abstraction_tree" : "tree";
|
|
11508
11995
|
if (!prev[treeKey]) return prev;
|
|
11509
|
-
const newTree = addChildToNode(
|
|
11996
|
+
const newTree = addChildToNode(
|
|
11997
|
+
prev[treeKey],
|
|
11998
|
+
parentId,
|
|
11999
|
+
fullNodeData
|
|
12000
|
+
);
|
|
11510
12001
|
return { ...prev, [treeKey]: newTree };
|
|
11511
12002
|
});
|
|
11512
12003
|
}
|
|
@@ -11520,7 +12011,8 @@ function XViewScene({
|
|
|
11520
12011
|
stateRef.current.dragCandidate = null;
|
|
11521
12012
|
stateRef.current.pointerDown.isDown = false;
|
|
11522
12013
|
stateRef.current.controls.enabled = true;
|
|
11523
|
-
if (currentMount)
|
|
12014
|
+
if (currentMount)
|
|
12015
|
+
currentMount.style.cursor = stateRef.current.hoveredNode || stateRef.current.hoveredLink ? "pointer" : "grab";
|
|
11524
12016
|
return;
|
|
11525
12017
|
}
|
|
11526
12018
|
const dragDistance = Math.hypot(
|
|
@@ -11568,16 +12060,21 @@ function XViewScene({
|
|
|
11568
12060
|
}
|
|
11569
12061
|
function handleDoubleClick(event) {
|
|
11570
12062
|
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
|
-
|
|
12063
|
+
if (isFromUiOverlay(event) || stateRef.current.isDragging || stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive)
|
|
12064
|
+
return;
|
|
12065
|
+
if (stateRef.current.hoveredNode)
|
|
12066
|
+
tweenToTarget(stateRef.current.hoveredNode);
|
|
11573
12067
|
}
|
|
11574
12068
|
function handleContextMenu(event) {
|
|
11575
12069
|
if (stateRef.current.camera) stateRef.current.camera.layers.enableAll();
|
|
11576
12070
|
if (isFromUiOverlay(event)) return;
|
|
11577
12071
|
event.preventDefault();
|
|
11578
|
-
if (stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive)
|
|
12072
|
+
if (stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive)
|
|
12073
|
+
return;
|
|
11579
12074
|
setMouseFromEvent(event);
|
|
11580
|
-
setContextMenu(
|
|
12075
|
+
setContextMenu(
|
|
12076
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
12077
|
+
);
|
|
11581
12078
|
setMultiContextMenu((prev) => ({ ...prev, visible: false }));
|
|
11582
12079
|
setRelationshipMenu((prev) => ({ ...prev, visible: false }));
|
|
11583
12080
|
const pickedNode = tryPickNode();
|
|
@@ -11608,7 +12105,12 @@ function XViewScene({
|
|
|
11608
12105
|
return;
|
|
11609
12106
|
}
|
|
11610
12107
|
stateRef.current.selectedNodes.clear();
|
|
11611
|
-
setRelationshipMenu({
|
|
12108
|
+
setRelationshipMenu({
|
|
12109
|
+
visible: true,
|
|
12110
|
+
x: event.clientX,
|
|
12111
|
+
y: event.clientY,
|
|
12112
|
+
linkObject: pickedLink
|
|
12113
|
+
});
|
|
11612
12114
|
return;
|
|
11613
12115
|
}
|
|
11614
12116
|
stateRef.current.selectedNodes.clear();
|
|
@@ -11640,7 +12142,10 @@ function XViewScene({
|
|
|
11640
12142
|
}
|
|
11641
12143
|
});
|
|
11642
12144
|
}
|
|
11643
|
-
const allRenderedLinks = [
|
|
12145
|
+
const allRenderedLinks = [
|
|
12146
|
+
...stateRef.current.allLinks,
|
|
12147
|
+
...stateRef.current.ancestryLinks
|
|
12148
|
+
];
|
|
11644
12149
|
allRenderedLinks.forEach((line) => {
|
|
11645
12150
|
const { sourceNode, targetNode, isCurved, curveOffset } = line.userData;
|
|
11646
12151
|
if (sourceNode && targetNode) {
|
|
@@ -11652,13 +12157,20 @@ function XViewScene({
|
|
|
11652
12157
|
const up = new THREE3.Vector3(0, 1, 0);
|
|
11653
12158
|
const normal = new THREE3.Vector3().crossVectors(dir, up).normalize();
|
|
11654
12159
|
const controlPoint = mid.add(normal.multiplyScalar(curveOffset));
|
|
11655
|
-
const curve = new THREE3.QuadraticBezierCurve3(
|
|
12160
|
+
const curve = new THREE3.QuadraticBezierCurve3(
|
|
12161
|
+
start,
|
|
12162
|
+
controlPoint,
|
|
12163
|
+
end
|
|
12164
|
+
);
|
|
11656
12165
|
const points = curve.getPoints(x_view_config.CURVE_SEGMENTS);
|
|
11657
12166
|
const positions = [];
|
|
11658
12167
|
points.forEach((p) => positions.push(p.x, p.y, p.z));
|
|
11659
12168
|
line.geometry.setPositions(positions);
|
|
11660
12169
|
} else {
|
|
11661
|
-
line.geometry.setPositions([
|
|
12170
|
+
line.geometry.setPositions([
|
|
12171
|
+
...sourceNode.position.toArray(),
|
|
12172
|
+
...targetNode.position.toArray()
|
|
12173
|
+
]);
|
|
11662
12174
|
}
|
|
11663
12175
|
}
|
|
11664
12176
|
});
|
|
@@ -11671,7 +12183,11 @@ function XViewScene({
|
|
|
11671
12183
|
const startPos = node.position;
|
|
11672
12184
|
const distance = startPos.distanceTo(endPos);
|
|
11673
12185
|
bar.scale.y = distance;
|
|
11674
|
-
const midpoint = new THREE3.Vector3().lerpVectors(
|
|
12186
|
+
const midpoint = new THREE3.Vector3().lerpVectors(
|
|
12187
|
+
startPos,
|
|
12188
|
+
endPos,
|
|
12189
|
+
0.5
|
|
12190
|
+
);
|
|
11675
12191
|
bar.position.copy(midpoint);
|
|
11676
12192
|
const direction = new THREE3.Vector3().subVectors(endPos, startPos).normalize();
|
|
11677
12193
|
const upVector = new THREE3.Vector3(0, 1, 0);
|
|
@@ -11682,7 +12198,11 @@ function XViewScene({
|
|
|
11682
12198
|
const { ghostElements, creation, connection, relink } = stateRef.current;
|
|
11683
12199
|
if (creation.isActive && ghostElements.node && ghostElements.line) {
|
|
11684
12200
|
const srcMesh = stateRef.current.nodeObjects[String(creation.sourceNodeData.id)];
|
|
11685
|
-
if (srcMesh)
|
|
12201
|
+
if (srcMesh)
|
|
12202
|
+
ghostElements.line.geometry.setPositions([
|
|
12203
|
+
...srcMesh.position.toArray(),
|
|
12204
|
+
...ghostElements.node.position.toArray()
|
|
12205
|
+
]);
|
|
11686
12206
|
}
|
|
11687
12207
|
if (connection.isActive && connection.line) {
|
|
11688
12208
|
const srcMesh = stateRef.current.nodeObjects[String(connection.sourceNodeData.id)];
|
|
@@ -11691,7 +12211,11 @@ function XViewScene({
|
|
|
11691
12211
|
raycaster2.setFromCamera(mouse, camera);
|
|
11692
12212
|
camera.getWorldDirection(plane.normal);
|
|
11693
12213
|
plane.setFromNormalAndCoplanarPoint(plane.normal, srcMesh.position);
|
|
11694
|
-
if (raycaster2.ray.intersectPlane(plane, intersectionPoint))
|
|
12214
|
+
if (raycaster2.ray.intersectPlane(plane, intersectionPoint))
|
|
12215
|
+
connection.line.geometry.setPositions([
|
|
12216
|
+
...srcMesh.position.toArray(),
|
|
12217
|
+
...intersectionPoint.toArray()
|
|
12218
|
+
]);
|
|
11695
12219
|
}
|
|
11696
12220
|
}
|
|
11697
12221
|
if (relink.isActive && relink.line) {
|
|
@@ -11701,7 +12225,11 @@ function XViewScene({
|
|
|
11701
12225
|
raycaster2.setFromCamera(mouse, camera);
|
|
11702
12226
|
camera.getWorldDirection(plane.normal);
|
|
11703
12227
|
plane.setFromNormalAndCoplanarPoint(plane.normal, fixedMesh.position);
|
|
11704
|
-
if (raycaster2.ray.intersectPlane(plane, intersectionPoint))
|
|
12228
|
+
if (raycaster2.ray.intersectPlane(plane, intersectionPoint))
|
|
12229
|
+
relink.line.geometry.setPositions([
|
|
12230
|
+
...fixedMesh.position.toArray(),
|
|
12231
|
+
...intersectionPoint.toArray()
|
|
12232
|
+
]);
|
|
11705
12233
|
}
|
|
11706
12234
|
}
|
|
11707
12235
|
Object.values(stateRef.current.nodeObjects).forEach((node) => {
|
|
@@ -11745,11 +12273,18 @@ function XViewScene({
|
|
|
11745
12273
|
renderer.setSize(clientWidth, clientHeight);
|
|
11746
12274
|
composer.setSize(clientWidth, clientHeight);
|
|
11747
12275
|
const resVec = new THREE3.Vector2(clientWidth, clientHeight);
|
|
11748
|
-
stateRef.current.allLinks.forEach(
|
|
11749
|
-
|
|
11750
|
-
|
|
11751
|
-
|
|
11752
|
-
|
|
12276
|
+
stateRef.current.allLinks.forEach(
|
|
12277
|
+
(line) => line.material.resolution.copy(resVec)
|
|
12278
|
+
);
|
|
12279
|
+
stateRef.current.ancestryLinks.forEach(
|
|
12280
|
+
(line) => line.material.resolution.copy(resVec)
|
|
12281
|
+
);
|
|
12282
|
+
if (stateRef.current.ghostElements.line)
|
|
12283
|
+
stateRef.current.ghostElements.line.material.resolution.copy(resVec);
|
|
12284
|
+
if (stateRef.current.connection.line)
|
|
12285
|
+
stateRef.current.connection.line.material.resolution.copy(resVec);
|
|
12286
|
+
if (stateRef.current.relink.line)
|
|
12287
|
+
stateRef.current.relink.line.material.resolution.copy(resVec);
|
|
11753
12288
|
}
|
|
11754
12289
|
window.addEventListener("resize", handleResize);
|
|
11755
12290
|
return () => {
|
|
@@ -11769,14 +12304,16 @@ function XViewScene({
|
|
|
11769
12304
|
ancestryGroup.traverse((obj) => {
|
|
11770
12305
|
if (obj.geometry) obj.geometry.dispose();
|
|
11771
12306
|
if (obj.material) {
|
|
11772
|
-
if (Array.isArray(obj.material))
|
|
12307
|
+
if (Array.isArray(obj.material))
|
|
12308
|
+
obj.material.forEach((m) => m.dispose());
|
|
11773
12309
|
else obj.material.dispose();
|
|
11774
12310
|
}
|
|
11775
12311
|
});
|
|
11776
12312
|
graphGroup.traverse((obj) => {
|
|
11777
12313
|
if (obj.geometry) obj.geometry.dispose();
|
|
11778
12314
|
if (obj.material) {
|
|
11779
|
-
if (Array.isArray(obj.material))
|
|
12315
|
+
if (Array.isArray(obj.material))
|
|
12316
|
+
obj.material.forEach((m) => m.dispose());
|
|
11780
12317
|
else obj.material.dispose();
|
|
11781
12318
|
}
|
|
11782
12319
|
});
|
|
@@ -11787,9 +12324,22 @@ function XViewScene({
|
|
|
11787
12324
|
currentMount.removeChild(renderer.domElement);
|
|
11788
12325
|
}
|
|
11789
12326
|
};
|
|
11790
|
-
}, [
|
|
12327
|
+
}, [
|
|
12328
|
+
isInitialized,
|
|
12329
|
+
tweenToTarget,
|
|
12330
|
+
dbSaveUrl,
|
|
12331
|
+
isNodeInView,
|
|
12332
|
+
addOrUpdateNodeMesh,
|
|
12333
|
+
handleActivateTimeline,
|
|
12334
|
+
get_scene_view_data,
|
|
12335
|
+
save_view_data
|
|
12336
|
+
]);
|
|
11791
12337
|
const handleGhostNodeImageChange = (0, import_react26.useCallback)((useImage, imageUrl) => {
|
|
11792
|
-
const {
|
|
12338
|
+
const {
|
|
12339
|
+
node: ghostNode,
|
|
12340
|
+
line: ghostLine,
|
|
12341
|
+
aura: ghostAura
|
|
12342
|
+
} = stateRef.current.ghostElements;
|
|
11793
12343
|
const { graphGroup, glowTexture } = stateRef.current;
|
|
11794
12344
|
if (!ghostNode || !graphGroup) return;
|
|
11795
12345
|
const currentData = {
|
|
@@ -11800,13 +12350,15 @@ function XViewScene({
|
|
|
11800
12350
|
const position = ghostNode.position.clone();
|
|
11801
12351
|
if (ghostNode.userData.labelObject) {
|
|
11802
12352
|
graphGroup.remove(ghostNode.userData.labelObject);
|
|
11803
|
-
if (ghostNode.userData.labelObject.material.map)
|
|
12353
|
+
if (ghostNode.userData.labelObject.material.map)
|
|
12354
|
+
ghostNode.userData.labelObject.material.map.dispose();
|
|
11804
12355
|
ghostNode.userData.labelObject.material.dispose();
|
|
11805
12356
|
}
|
|
11806
12357
|
graphGroup.remove(ghostNode);
|
|
11807
12358
|
if (ghostNode.geometry) ghostNode.geometry.dispose();
|
|
11808
12359
|
if (ghostNode.material) {
|
|
11809
|
-
if (Array.isArray(ghostNode.material))
|
|
12360
|
+
if (Array.isArray(ghostNode.material))
|
|
12361
|
+
ghostNode.material.forEach((m) => m.dispose());
|
|
11810
12362
|
else ghostNode.material.dispose();
|
|
11811
12363
|
}
|
|
11812
12364
|
const newGhostNode = createNodeMesh(currentData, position, glowTexture);
|
|
@@ -11851,28 +12403,31 @@ function XViewScene({
|
|
|
11851
12403
|
ghostAura.material.opacity = Math.min(0.8, newIntensity * 0.15);
|
|
11852
12404
|
}
|
|
11853
12405
|
}, []);
|
|
11854
|
-
const handleDetailNodeIntensityChange = (0, import_react26.useCallback)(
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
borderMesh.material
|
|
12406
|
+
const handleDetailNodeIntensityChange = (0, import_react26.useCallback)(
|
|
12407
|
+
(nodeId, newIntensity) => {
|
|
12408
|
+
const mesh = stateRef.current.nodeObjects[String(nodeId)];
|
|
12409
|
+
if (!mesh) return;
|
|
12410
|
+
const adjustedIntensity = newIntensity + MIN_VISIBILITY_INTENSITY;
|
|
12411
|
+
mesh.userData.intensity = newIntensity;
|
|
12412
|
+
mesh.userData._baseEmissiveIntensity = adjustedIntensity;
|
|
12413
|
+
const isImageNode = mesh.userData.useImageAsTexture === true;
|
|
12414
|
+
if (isImageNode) {
|
|
12415
|
+
const borderMesh = mesh.getObjectByName("borderRing");
|
|
12416
|
+
if (borderMesh && borderMesh.material) {
|
|
12417
|
+
borderMesh.material.emissiveIntensity = adjustedIntensity;
|
|
12418
|
+
}
|
|
12419
|
+
} else {
|
|
12420
|
+
if (mesh.material) {
|
|
12421
|
+
mesh.material.emissiveIntensity = adjustedIntensity;
|
|
12422
|
+
}
|
|
11865
12423
|
}
|
|
11866
|
-
|
|
11867
|
-
if (
|
|
11868
|
-
|
|
12424
|
+
const aura = mesh.getObjectByName("aura");
|
|
12425
|
+
if (aura && aura.material) {
|
|
12426
|
+
aura.material.opacity = Math.min(0.8, newIntensity * 0.15);
|
|
11869
12427
|
}
|
|
11870
|
-
}
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
aura.material.opacity = Math.min(0.8, newIntensity * 0.15);
|
|
11874
|
-
}
|
|
11875
|
-
}, []);
|
|
12428
|
+
},
|
|
12429
|
+
[]
|
|
12430
|
+
);
|
|
11876
12431
|
const handleGhostNodeColorChange = (newColor) => {
|
|
11877
12432
|
const { node: ghostNode, aura: ghostAura } = stateRef.current.ghostElements;
|
|
11878
12433
|
if (!ghostNode) return;
|
|
@@ -11983,7 +12538,9 @@ function XViewScene({
|
|
|
11983
12538
|
mesh.userData.size = newSize;
|
|
11984
12539
|
};
|
|
11985
12540
|
const handleStartAncestryCreation = (nodeData) => {
|
|
11986
|
-
setContextMenu(
|
|
12541
|
+
setContextMenu(
|
|
12542
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
12543
|
+
);
|
|
11987
12544
|
stateRef.current.maxAncestryRenderIndex = 0;
|
|
11988
12545
|
setAncestryMode({
|
|
11989
12546
|
isActive: true,
|
|
@@ -12008,9 +12565,12 @@ function XViewScene({
|
|
|
12008
12565
|
const newTreeStr = JSON.stringify(newTree);
|
|
12009
12566
|
let metaChanged = false;
|
|
12010
12567
|
if (extraData) {
|
|
12011
|
-
if (extraData.ancestryName !== void 0 && extraData.ancestryName !== prev.ancestryName)
|
|
12012
|
-
|
|
12013
|
-
if (extraData.
|
|
12568
|
+
if (extraData.ancestryName !== void 0 && extraData.ancestryName !== prev.ancestryName)
|
|
12569
|
+
metaChanged = true;
|
|
12570
|
+
if (extraData.ancestryDescription !== void 0 && extraData.ancestryDescription !== prev.ancestryDescription)
|
|
12571
|
+
metaChanged = true;
|
|
12572
|
+
if (extraData.ancestryDescriptionSections !== void 0 && JSON.stringify(extraData.ancestryDescriptionSections) !== JSON.stringify(prev.ancestryDescriptionSections))
|
|
12573
|
+
metaChanged = true;
|
|
12014
12574
|
}
|
|
12015
12575
|
if (prevTreeStr === newTreeStr && !metaChanged) {
|
|
12016
12576
|
return prev;
|
|
@@ -12088,13 +12648,15 @@ function XViewScene({
|
|
|
12088
12648
|
if (ghostElements.node && graphGroup) {
|
|
12089
12649
|
if (ghostElements.node.userData.labelObject) {
|
|
12090
12650
|
graphGroup.remove(ghostElements.node.userData.labelObject);
|
|
12091
|
-
if (ghostElements.node.userData.labelObject.material.map)
|
|
12651
|
+
if (ghostElements.node.userData.labelObject.material.map)
|
|
12652
|
+
ghostElements.node.userData.labelObject.material.map.dispose();
|
|
12092
12653
|
ghostElements.node.userData.labelObject.material.dispose();
|
|
12093
12654
|
}
|
|
12094
12655
|
graphGroup.remove(ghostElements.node);
|
|
12095
12656
|
ghostElements.node.traverse((child) => {
|
|
12096
12657
|
if (child.material) {
|
|
12097
|
-
if (Array.isArray(child.material))
|
|
12658
|
+
if (Array.isArray(child.material))
|
|
12659
|
+
child.material.forEach((m) => m.dispose());
|
|
12098
12660
|
else child.material.dispose();
|
|
12099
12661
|
}
|
|
12100
12662
|
if (child.geometry) child.geometry.dispose();
|
|
@@ -12104,7 +12666,17 @@ function XViewScene({
|
|
|
12104
12666
|
setQuestMode({ isActive: false });
|
|
12105
12667
|
}, []);
|
|
12106
12668
|
const handleSaveQuestNode = async (context, newQuestData) => {
|
|
12107
|
-
const {
|
|
12669
|
+
const {
|
|
12670
|
+
graphDataRef,
|
|
12671
|
+
sceneDataRef: sceneDataRef2,
|
|
12672
|
+
stateRef: stateRef2,
|
|
12673
|
+
setters,
|
|
12674
|
+
actions,
|
|
12675
|
+
sceneSaveUrl: sceneSaveUrl2,
|
|
12676
|
+
viewType,
|
|
12677
|
+
sceneConfigId: sceneConfigId2,
|
|
12678
|
+
ownerId: ownerId2
|
|
12679
|
+
} = context;
|
|
12108
12680
|
if (!graphDataRef.current || (viewType == null ? void 0 : viewType.toLowerCase()) !== "view") return;
|
|
12109
12681
|
const currentCounter = sceneDataRef2.current.quest_counter || 1;
|
|
12110
12682
|
const newNode = {
|
|
@@ -12137,7 +12709,8 @@ function XViewScene({
|
|
|
12137
12709
|
const finalPosition = stateRef2.current.ghostElements.node ? stateRef2.current.ghostElements.node.position.clone() : stateRef2.current.controls.target.clone();
|
|
12138
12710
|
const { graphGroup, ghostElements } = stateRef2.current;
|
|
12139
12711
|
if (ghostElements.node && graphGroup) {
|
|
12140
|
-
if (ghostElements.node.userData.labelObject)
|
|
12712
|
+
if (ghostElements.node.userData.labelObject)
|
|
12713
|
+
graphGroup.remove(ghostElements.node.userData.labelObject);
|
|
12141
12714
|
graphGroup.remove(ghostElements.node);
|
|
12142
12715
|
}
|
|
12143
12716
|
stateRef2.current.ghostElements = { node: null, line: null, aura: null };
|
|
@@ -12151,14 +12724,33 @@ function XViewScene({
|
|
|
12151
12724
|
}
|
|
12152
12725
|
};
|
|
12153
12726
|
userActionHandlers.handleCompleteConnection = async (context, targetNodeData) => {
|
|
12154
|
-
const {
|
|
12727
|
+
const {
|
|
12728
|
+
stateRef: stateRef2,
|
|
12729
|
+
graphDataRef,
|
|
12730
|
+
sceneDataRef: sceneDataRef2,
|
|
12731
|
+
sceneConfigId: sceneConfigId2,
|
|
12732
|
+
sceneSaveUrl: sceneSaveUrl2,
|
|
12733
|
+
ownerId: ownerId2
|
|
12734
|
+
} = context;
|
|
12155
12735
|
const { sourceNodeData } = stateRef2.current.connection;
|
|
12156
12736
|
if (!graphDataRef.current || !sceneDataRef2.current || !sourceNodeData || !targetNodeData) {
|
|
12157
12737
|
userActionHandlers.handleCancelConnection(context);
|
|
12158
12738
|
return;
|
|
12159
12739
|
}
|
|
12160
|
-
const sourceParentInfo = getParentFileInfoForNode(
|
|
12161
|
-
|
|
12740
|
+
const sourceParentInfo = getParentFileInfoForNode(
|
|
12741
|
+
graphDataRef.current,
|
|
12742
|
+
sceneDataRef2.current,
|
|
12743
|
+
sourceNodeData.id,
|
|
12744
|
+
sceneConfigId2,
|
|
12745
|
+
ownerId2
|
|
12746
|
+
);
|
|
12747
|
+
const targetParentInfo = getParentFileInfoForNode(
|
|
12748
|
+
graphDataRef.current,
|
|
12749
|
+
sceneDataRef2.current,
|
|
12750
|
+
targetNodeData.id,
|
|
12751
|
+
sceneConfigId2,
|
|
12752
|
+
ownerId2
|
|
12753
|
+
);
|
|
12162
12754
|
let parentInfoToSave = sourceParentInfo;
|
|
12163
12755
|
const isSourceQuest = sourceParentInfo.parentFileId === sceneConfigId2;
|
|
12164
12756
|
const isTargetQuest = targetParentInfo.parentFileId === sceneConfigId2;
|
|
@@ -12184,10 +12776,15 @@ function XViewScene({
|
|
|
12184
12776
|
};
|
|
12185
12777
|
await context.actions.save_view_data(sceneSaveUrl2, viewFilePayload);
|
|
12186
12778
|
} else {
|
|
12187
|
-
const specificParentData = JSON.parse(
|
|
12779
|
+
const specificParentData = JSON.parse(
|
|
12780
|
+
JSON.stringify(graphDataRef.current[parentFileIdToSave])
|
|
12781
|
+
);
|
|
12188
12782
|
specificParentData.links.push(newLink);
|
|
12189
12783
|
const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
|
|
12190
|
-
await context.actions.save_view_data(
|
|
12784
|
+
await context.actions.save_view_data(
|
|
12785
|
+
filenameForSpecificParent,
|
|
12786
|
+
specificParentData
|
|
12787
|
+
);
|
|
12191
12788
|
graphDataRef.current[parentFileIdToSave] = specificParentData;
|
|
12192
12789
|
}
|
|
12193
12790
|
addNewLinkToScene(stateRef2.current, newLink);
|
|
@@ -12199,7 +12796,9 @@ function XViewScene({
|
|
|
12199
12796
|
};
|
|
12200
12797
|
const handleClearAncestryVisuals = (0, import_react26.useCallback)((ancestryId) => {
|
|
12201
12798
|
const { renderedAncestries, ancestryGroup } = stateRef.current;
|
|
12202
|
-
const renderIndex = renderedAncestries.findIndex(
|
|
12799
|
+
const renderIndex = renderedAncestries.findIndex(
|
|
12800
|
+
(a) => String(a.id) === String(ancestryId)
|
|
12801
|
+
);
|
|
12203
12802
|
if (renderIndex !== -1) {
|
|
12204
12803
|
const toRemove = renderedAncestries[renderIndex];
|
|
12205
12804
|
toRemove.lines.forEach((line) => {
|
|
@@ -12208,12 +12807,16 @@ function XViewScene({
|
|
|
12208
12807
|
if (line.material) line.material.dispose();
|
|
12209
12808
|
});
|
|
12210
12809
|
renderedAncestries.splice(renderIndex, 1);
|
|
12211
|
-
stateRef.current.ancestryLinks = renderedAncestries.flatMap(
|
|
12810
|
+
stateRef.current.ancestryLinks = renderedAncestries.flatMap(
|
|
12811
|
+
(a) => a.lines
|
|
12812
|
+
);
|
|
12212
12813
|
}
|
|
12213
12814
|
}, []);
|
|
12214
12815
|
const handleRenderAncestry = (0, import_react26.useCallback)(
|
|
12215
12816
|
async (ancestryObject, allowedSectionIds = null, activeSectionIdForFocus = null, baseRotation = 0, forceReprocess = true) => {
|
|
12216
|
-
setContextMenu(
|
|
12817
|
+
setContextMenu(
|
|
12818
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
12819
|
+
);
|
|
12217
12820
|
if (!ancestryObject || !ancestryObject.tree) {
|
|
12218
12821
|
return;
|
|
12219
12822
|
}
|
|
@@ -12240,14 +12843,16 @@ function XViewScene({
|
|
|
12240
12843
|
if (numId !== void 0 && normalizedAllowedIds.has(numId)) return true;
|
|
12241
12844
|
if (strId && normalizedAllowedIds.has(strId)) return true;
|
|
12242
12845
|
if (strSecId && normalizedAllowedIds.has(strSecId)) return true;
|
|
12243
|
-
if (numId !== void 0 && normalizedAllowedIds.has(String(numId)))
|
|
12846
|
+
if (numId !== void 0 && normalizedAllowedIds.has(String(numId)))
|
|
12847
|
+
return true;
|
|
12244
12848
|
return false;
|
|
12245
12849
|
};
|
|
12246
12850
|
const checkIsActive = (section, targetId) => {
|
|
12247
12851
|
if (targetId === null || targetId === void 0) return false;
|
|
12248
12852
|
const sTarget = String(targetId);
|
|
12249
12853
|
if (section.id && String(section.id) === sTarget) return true;
|
|
12250
|
-
if (section.section_id && String(section.section_id) === sTarget)
|
|
12854
|
+
if (section.section_id && String(section.section_id) === sTarget)
|
|
12855
|
+
return true;
|
|
12251
12856
|
if (section.section_numeric_id !== void 0) {
|
|
12252
12857
|
if (String(section.section_numeric_id) === sTarget) return true;
|
|
12253
12858
|
}
|
|
@@ -12263,7 +12868,9 @@ function XViewScene({
|
|
|
12263
12868
|
traverse(sectionTree);
|
|
12264
12869
|
return ids;
|
|
12265
12870
|
};
|
|
12266
|
-
const existingIndex = renderedAncestries.findIndex(
|
|
12871
|
+
const existingIndex = renderedAncestries.findIndex(
|
|
12872
|
+
(a) => String(a.id) === String(ancestryObject.ancestry_id)
|
|
12873
|
+
);
|
|
12267
12874
|
let skipGeneration = false;
|
|
12268
12875
|
let ancestryEntry = null;
|
|
12269
12876
|
if (existingIndex !== -1) {
|
|
@@ -12318,9 +12925,15 @@ function XViewScene({
|
|
|
12318
12925
|
if (line.material) line.material.dispose();
|
|
12319
12926
|
});
|
|
12320
12927
|
}
|
|
12321
|
-
const allParentNodes = Object.values(parentDataRef.current).flatMap(
|
|
12928
|
+
const allParentNodes = Object.values(parentDataRef.current).flatMap(
|
|
12929
|
+
(fileData) => fileData.nodes
|
|
12930
|
+
);
|
|
12322
12931
|
const allAncestries = ancestryDataRef.current || [];
|
|
12323
|
-
const fullTree = buildFullAncestryTree(
|
|
12932
|
+
const fullTree = buildFullAncestryTree(
|
|
12933
|
+
ancestryObject.tree,
|
|
12934
|
+
allParentNodes,
|
|
12935
|
+
allAncestries
|
|
12936
|
+
);
|
|
12324
12937
|
if (!fullTree) return;
|
|
12325
12938
|
const rootNodeId = String(ancestryObject.ancestral_node);
|
|
12326
12939
|
let rootNodeMesh = nodeObjects[rootNodeId];
|
|
@@ -12363,7 +12976,10 @@ function XViewScene({
|
|
|
12363
12976
|
const colorIndex = stateRef.current.ancestryRenderCounter % 3;
|
|
12364
12977
|
colorHex = ANCESTRY_COLORS[colorIndex];
|
|
12365
12978
|
}
|
|
12366
|
-
const resolution = new THREE3.Vector2(
|
|
12979
|
+
const resolution = new THREE3.Vector2(
|
|
12980
|
+
renderer.domElement.clientWidth,
|
|
12981
|
+
renderer.domElement.clientHeight
|
|
12982
|
+
);
|
|
12367
12983
|
const cleanupLinesForNode = (nodeId) => {
|
|
12368
12984
|
if (!ancestryEntry || !ancestryEntry.lines) return;
|
|
12369
12985
|
const linesKeep = [];
|
|
@@ -12403,7 +13019,13 @@ function XViewScene({
|
|
|
12403
13019
|
}
|
|
12404
13020
|
if (originMesh && rootNodeMesh) {
|
|
12405
13021
|
cleanupLinesForNode(rootNodeId);
|
|
12406
|
-
const branchLine = createAncestryLinkLine(
|
|
13022
|
+
const branchLine = createAncestryLinkLine(
|
|
13023
|
+
originMesh,
|
|
13024
|
+
rootNodeMesh,
|
|
13025
|
+
resolution,
|
|
13026
|
+
{},
|
|
13027
|
+
colorHex
|
|
13028
|
+
);
|
|
12407
13029
|
ancestryGroup.add(branchLine);
|
|
12408
13030
|
ancestryEntry.lines.push(branchLine);
|
|
12409
13031
|
}
|
|
@@ -12414,8 +13036,14 @@ function XViewScene({
|
|
|
12414
13036
|
if (!children || children.length === 0) return null;
|
|
12415
13037
|
let lastRenderedMesh = null;
|
|
12416
13038
|
const numChildren = children.length;
|
|
12417
|
-
const forwardVec = new THREE3.Vector3(0, 0, 1).applyAxisAngle(
|
|
12418
|
-
|
|
13039
|
+
const forwardVec = new THREE3.Vector3(0, 0, 1).applyAxisAngle(
|
|
13040
|
+
new THREE3.Vector3(0, 1, 0),
|
|
13041
|
+
currentAngle
|
|
13042
|
+
);
|
|
13043
|
+
const rightVec = new THREE3.Vector3(1, 0, 0).applyAxisAngle(
|
|
13044
|
+
new THREE3.Vector3(0, 1, 0),
|
|
13045
|
+
currentAngle
|
|
13046
|
+
);
|
|
12419
13047
|
const angleRange = numChildren > 3 ? Math.PI : Math.PI / 1.5;
|
|
12420
13048
|
children.forEach((childItem, index) => {
|
|
12421
13049
|
if (childItem.is_section) return;
|
|
@@ -12436,14 +13064,30 @@ function XViewScene({
|
|
|
12436
13064
|
targetPositionsCache.set(sNodeId, childPosition.clone());
|
|
12437
13065
|
cleanupLinesForNode(sNodeId);
|
|
12438
13066
|
}
|
|
12439
|
-
const childMesh = addOrUpdateNodeMesh(
|
|
13067
|
+
const childMesh = addOrUpdateNodeMesh(
|
|
13068
|
+
childItem.node,
|
|
13069
|
+
childPosition,
|
|
13070
|
+
true
|
|
13071
|
+
);
|
|
12440
13072
|
allRenderedNodePositions.push(childPosition);
|
|
12441
|
-
const line = createAncestryLinkLine(
|
|
13073
|
+
const line = createAncestryLinkLine(
|
|
13074
|
+
parentMesh,
|
|
13075
|
+
childMesh,
|
|
13076
|
+
resolution,
|
|
13077
|
+
childItem.relationship,
|
|
13078
|
+
colorHex
|
|
13079
|
+
);
|
|
12442
13080
|
ancestryGroup.add(line);
|
|
12443
13081
|
ancestryEntry.lines.push(line);
|
|
12444
13082
|
lastRenderedMesh = childMesh;
|
|
12445
13083
|
if (childItem.children && childItem.children.length > 0) {
|
|
12446
|
-
const lastDescendant = renderCluster(
|
|
13084
|
+
const lastDescendant = renderCluster(
|
|
13085
|
+
childItem.children,
|
|
13086
|
+
childMesh,
|
|
13087
|
+
childPosition,
|
|
13088
|
+
level + 1,
|
|
13089
|
+
currentAngle
|
|
13090
|
+
);
|
|
12447
13091
|
if (lastDescendant) lastRenderedMesh = lastDescendant;
|
|
12448
13092
|
}
|
|
12449
13093
|
});
|
|
@@ -12457,9 +13101,13 @@ function XViewScene({
|
|
|
12457
13101
|
else looseNodes.push(child);
|
|
12458
13102
|
});
|
|
12459
13103
|
}
|
|
12460
|
-
sections.sort(
|
|
13104
|
+
sections.sort(
|
|
13105
|
+
(a, b) => (a.section_numeric_id || 0) - (b.section_numeric_id || 0)
|
|
13106
|
+
);
|
|
12461
13107
|
let combinedStartNodes = [...looseNodes];
|
|
12462
|
-
let session0Index = sections.findIndex(
|
|
13108
|
+
let session0Index = sections.findIndex(
|
|
13109
|
+
(s) => s.section_numeric_id === 0 || s.name === "Sess\xE3o 0"
|
|
13110
|
+
);
|
|
12463
13111
|
if (session0Index !== -1) {
|
|
12464
13112
|
const session0 = sections[session0Index];
|
|
12465
13113
|
if (checkShouldRender(session0) && session0.children) {
|
|
@@ -12468,7 +13116,13 @@ function XViewScene({
|
|
|
12468
13116
|
sections.splice(session0Index, 1);
|
|
12469
13117
|
}
|
|
12470
13118
|
const rootTargetPos2 = targetPositionsCache.get(rootNodeId) || rootNodeMesh.position;
|
|
12471
|
-
const lastStartMesh = renderCluster(
|
|
13119
|
+
const lastStartMesh = renderCluster(
|
|
13120
|
+
combinedStartNodes,
|
|
13121
|
+
rootNodeMesh,
|
|
13122
|
+
rootTargetPos2,
|
|
13123
|
+
1,
|
|
13124
|
+
baseRotation
|
|
13125
|
+
);
|
|
12472
13126
|
if (lastStartMesh) {
|
|
12473
13127
|
currentAnchorMesh = lastStartMesh;
|
|
12474
13128
|
}
|
|
@@ -12479,10 +13133,18 @@ function XViewScene({
|
|
|
12479
13133
|
const sectionNodes = section.children || [];
|
|
12480
13134
|
if (sectionNodes.length > 0) {
|
|
12481
13135
|
const parentAngle = nodeRotationMap.get(String(currentAnchorMesh.userData.id)) ?? baseRotation;
|
|
12482
|
-
const lastMeshOfThisSection = renderCluster(
|
|
13136
|
+
const lastMeshOfThisSection = renderCluster(
|
|
13137
|
+
sectionNodes,
|
|
13138
|
+
currentAnchorMesh,
|
|
13139
|
+
currentAnchorPosition,
|
|
13140
|
+
1,
|
|
13141
|
+
parentAngle
|
|
13142
|
+
);
|
|
12483
13143
|
if (lastMeshOfThisSection) {
|
|
12484
13144
|
currentAnchorMesh = lastMeshOfThisSection;
|
|
12485
|
-
currentAnchorPosition = targetPositionsCache.get(
|
|
13145
|
+
currentAnchorPosition = targetPositionsCache.get(
|
|
13146
|
+
String(lastMeshOfThisSection.userData.id)
|
|
13147
|
+
) || lastMeshOfThisSection.position;
|
|
12486
13148
|
}
|
|
12487
13149
|
}
|
|
12488
13150
|
}
|
|
@@ -12511,7 +13173,10 @@ function XViewScene({
|
|
|
12511
13173
|
} else {
|
|
12512
13174
|
const directChildren = fullTree.children ? fullTree.children.filter((c) => !c.is_section) : [];
|
|
12513
13175
|
if (directChildren.length > 0) {
|
|
12514
|
-
targetSectionForZone = {
|
|
13176
|
+
targetSectionForZone = {
|
|
13177
|
+
name: "In\xEDcio",
|
|
13178
|
+
children: directChildren
|
|
13179
|
+
};
|
|
12515
13180
|
}
|
|
12516
13181
|
}
|
|
12517
13182
|
} else {
|
|
@@ -12585,7 +13250,10 @@ function XViewScene({
|
|
|
12585
13250
|
stateRef.current.ancestryLinks = stateRef.current.renderedAncestries.flatMap((a) => a.lines);
|
|
12586
13251
|
focusTargetPosition = center;
|
|
12587
13252
|
focusTargetRotation = baseRotation;
|
|
12588
|
-
const desiredDistance = Math.max(
|
|
13253
|
+
const desiredDistance = Math.max(
|
|
13254
|
+
x_view_config.CAMERA_ZOOM_DISTANCE,
|
|
13255
|
+
radius * 2.8
|
|
13256
|
+
);
|
|
12589
13257
|
focusZoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
|
|
12590
13258
|
} else {
|
|
12591
13259
|
const anchorId = String(currentAnchorMesh.userData.id);
|
|
@@ -12600,13 +13268,18 @@ function XViewScene({
|
|
|
12600
13268
|
}
|
|
12601
13269
|
if (!focusTargetPosition && !allowedSectionIds) {
|
|
12602
13270
|
if (allRenderedNodePositions.length > 0) {
|
|
12603
|
-
const boundingBox = new THREE3.Box3().setFromPoints(
|
|
13271
|
+
const boundingBox = new THREE3.Box3().setFromPoints(
|
|
13272
|
+
allRenderedNodePositions
|
|
13273
|
+
);
|
|
12604
13274
|
const center = new THREE3.Vector3();
|
|
12605
13275
|
boundingBox.getCenter(center);
|
|
12606
13276
|
const size = new THREE3.Vector3();
|
|
12607
13277
|
boundingBox.getSize(size);
|
|
12608
13278
|
const maxDim = Math.max(size.x, size.y, size.z);
|
|
12609
|
-
const fitZoom = Math.min(
|
|
13279
|
+
const fitZoom = Math.min(
|
|
13280
|
+
1,
|
|
13281
|
+
x_view_config.CAMERA_ZOOM_DISTANCE / (maxDim * 1.5)
|
|
13282
|
+
);
|
|
12610
13283
|
const defaultBase = new THREE3.Vector3(-50, 40, 20);
|
|
12611
13284
|
const rotatedCinematicAngle = defaultBase.clone().applyAxisAngle(new THREE3.Vector3(0, 1, 0), baseRotation);
|
|
12612
13285
|
tweenToTarget(center, fitZoom, rotatedCinematicAngle);
|
|
@@ -12625,210 +13298,280 @@ function XViewScene({
|
|
|
12625
13298
|
tweenToTarget(targetPos, focusZoomFactor, rotatedCinematicAngle);
|
|
12626
13299
|
}
|
|
12627
13300
|
},
|
|
12628
|
-
[
|
|
13301
|
+
[
|
|
13302
|
+
addOrUpdateNodeMesh,
|
|
13303
|
+
tweenToTarget,
|
|
13304
|
+
buildFullAncestryTree,
|
|
13305
|
+
readingMode.isActive,
|
|
13306
|
+
ancestryMode.isActive
|
|
13307
|
+
]
|
|
12629
13308
|
);
|
|
12630
|
-
const handleRenderAbstractionTree = (0, import_react26.useCallback)(
|
|
12631
|
-
|
|
12632
|
-
|
|
12633
|
-
|
|
12634
|
-
|
|
12635
|
-
|
|
12636
|
-
|
|
12637
|
-
|
|
12638
|
-
|
|
12639
|
-
|
|
12640
|
-
|
|
12641
|
-
|
|
12642
|
-
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
13309
|
+
const handleRenderAbstractionTree = (0, import_react26.useCallback)(
|
|
13310
|
+
(ancestryObject, targetNodeId = null) => {
|
|
13311
|
+
setContextMenu(
|
|
13312
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
13313
|
+
);
|
|
13314
|
+
if (!ancestryObject || !ancestryObject.abstraction_tree) return;
|
|
13315
|
+
const { ancestryGroup, nodeObjects, renderer, renderedAncestries } = stateRef.current;
|
|
13316
|
+
const allParentNodes = Object.values(parentDataRef.current).flatMap(
|
|
13317
|
+
(f) => f.nodes
|
|
13318
|
+
);
|
|
13319
|
+
let fullTree = buildFullAncestryTree(
|
|
13320
|
+
ancestryObject.abstraction_tree,
|
|
13321
|
+
allParentNodes,
|
|
13322
|
+
ancestryDataRef.current
|
|
13323
|
+
);
|
|
13324
|
+
if (!fullTree || !fullTree.node) return;
|
|
13325
|
+
if (targetNodeId) {
|
|
13326
|
+
const pruneTreeToPath = (treeNode, targetId) => {
|
|
13327
|
+
var _a2;
|
|
13328
|
+
if (!treeNode) return null;
|
|
13329
|
+
const currentId = treeNode.is_section ? treeNode.section_id : String((_a2 = treeNode.node) == null ? void 0 : _a2.id);
|
|
13330
|
+
if (String(currentId) === String(targetId)) {
|
|
13331
|
+
return { ...treeNode, children: [] };
|
|
12651
13332
|
}
|
|
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 = (0, import_react26.useCallback)((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));
|
|
13333
|
+
if (treeNode.children && treeNode.children.length > 0) {
|
|
13334
|
+
for (let child of treeNode.children) {
|
|
13335
|
+
const prunedChild = pruneTreeToPath(child, targetId);
|
|
13336
|
+
if (prunedChild) {
|
|
13337
|
+
return { ...treeNode, children: [prunedChild] };
|
|
12739
13338
|
}
|
|
12740
13339
|
}
|
|
12741
13340
|
}
|
|
12742
|
-
|
|
12743
|
-
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
|
|
13341
|
+
return null;
|
|
13342
|
+
};
|
|
13343
|
+
const pruned = pruneTreeToPath(fullTree, targetNodeId);
|
|
13344
|
+
if (pruned) fullTree = pruned;
|
|
13345
|
+
}
|
|
13346
|
+
const absId = ancestryObject.ancestry_id + "_abs";
|
|
13347
|
+
handleClearAncestryVisuals(absId);
|
|
13348
|
+
const colorHex = 9133302;
|
|
13349
|
+
const resolution = new THREE3.Vector2(
|
|
13350
|
+
renderer.domElement.clientWidth,
|
|
13351
|
+
renderer.domElement.clientHeight
|
|
13352
|
+
);
|
|
13353
|
+
const ancestryEntry = { id: absId, lines: [], isFullRender: true };
|
|
13354
|
+
renderedAncestries.push(ancestryEntry);
|
|
13355
|
+
const rootNodeId = String(fullTree.node.id);
|
|
13356
|
+
let rootNodeMesh = nodeObjects[rootNodeId];
|
|
13357
|
+
let rootTargetPos = rootNodeMesh ? rootNodeMesh.position.clone() : new THREE3.Vector3(0, 0, 0);
|
|
13358
|
+
if (!rootNodeMesh) {
|
|
13359
|
+
rootNodeMesh = addOrUpdateNodeMesh(fullTree.node, rootTargetPos, true);
|
|
12754
13360
|
}
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
const
|
|
12758
|
-
|
|
12759
|
-
|
|
12760
|
-
|
|
12761
|
-
|
|
12762
|
-
|
|
12763
|
-
|
|
12764
|
-
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
|
|
12769
|
-
|
|
12770
|
-
|
|
13361
|
+
const SPACING_Y = -40;
|
|
13362
|
+
const SPACING_X = 55;
|
|
13363
|
+
const renderVertical = (treeNode, parentMesh, parentPos, level) => {
|
|
13364
|
+
if (!treeNode.children || treeNode.children.length === 0) return;
|
|
13365
|
+
const totalSiblings = treeNode.children.length;
|
|
13366
|
+
treeNode.children.forEach((childItem, i) => {
|
|
13367
|
+
if (!childItem.node) return;
|
|
13368
|
+
const childX = parentPos.x + (i - (totalSiblings - 1) / 2) * (SPACING_X / Math.max(1, level * 0.4));
|
|
13369
|
+
const childY = parentPos.y + SPACING_Y;
|
|
13370
|
+
const childPos = new THREE3.Vector3(childX, childY, parentPos.z);
|
|
13371
|
+
const childMesh = addOrUpdateNodeMesh(childItem.node, childPos, true);
|
|
13372
|
+
const line = createAncestryLinkLine(
|
|
13373
|
+
parentMesh,
|
|
13374
|
+
childMesh,
|
|
13375
|
+
resolution,
|
|
13376
|
+
{},
|
|
13377
|
+
colorHex
|
|
13378
|
+
);
|
|
13379
|
+
ancestryGroup.add(line);
|
|
13380
|
+
ancestryEntry.lines.push(line);
|
|
13381
|
+
renderVertical(childItem, childMesh, childPos, level + 1);
|
|
13382
|
+
});
|
|
13383
|
+
};
|
|
13384
|
+
renderVertical(fullTree, rootNodeMesh, rootTargetPos, 1);
|
|
13385
|
+
stateRef.current.ancestryLinks = renderedAncestries.flatMap(
|
|
13386
|
+
(a) => a.lines
|
|
13387
|
+
);
|
|
13388
|
+
tweenToTarget(rootTargetPos, 0.7);
|
|
13389
|
+
},
|
|
13390
|
+
[
|
|
13391
|
+
addOrUpdateNodeMesh,
|
|
13392
|
+
tweenToTarget,
|
|
13393
|
+
buildFullAncestryTree,
|
|
13394
|
+
handleClearAncestryVisuals
|
|
13395
|
+
]
|
|
13396
|
+
);
|
|
13397
|
+
const handleReadModeBranchNav = (0, import_react26.useCallback)(
|
|
13398
|
+
(nodeId, action, direction = "right") => {
|
|
13399
|
+
const { ancestry, branchStack } = readingMode;
|
|
13400
|
+
if (!ancestry || !ancestry.tree) return;
|
|
13401
|
+
const allAncestries = ancestryDataRef.current || [];
|
|
13402
|
+
const fullTree = buildFullAncestryTree(
|
|
13403
|
+
ancestry.tree,
|
|
13404
|
+
Object.values(parentDataRef.current).flatMap((f) => f.nodes),
|
|
13405
|
+
allAncestries
|
|
13406
|
+
);
|
|
13407
|
+
if (action === "open") {
|
|
13408
|
+
let currentPtr = fullTree;
|
|
13409
|
+
for (const step of branchStack) {
|
|
13410
|
+
const found = findNodePath3(currentPtr, step.nodeId);
|
|
12771
13411
|
if (found && found.node.parallel_branches) {
|
|
12772
|
-
const branch = found.node.parallel_branches.find(
|
|
12773
|
-
|
|
12774
|
-
|
|
12775
|
-
|
|
13412
|
+
const branch = found.node.parallel_branches.find(
|
|
13413
|
+
(b) => b.id === step.branchId
|
|
13414
|
+
);
|
|
13415
|
+
if (branch) currentPtr = branch.tree;
|
|
13416
|
+
}
|
|
13417
|
+
}
|
|
13418
|
+
const foundTarget = findNodePath3(currentPtr, nodeId);
|
|
13419
|
+
if (foundTarget && foundTarget.node && foundTarget.node.parallel_branches && foundTarget.node.parallel_branches.length > 0) {
|
|
13420
|
+
const branchToOpen = foundTarget.node.parallel_branches.find(
|
|
13421
|
+
(b) => (b.direction || "right") === direction
|
|
13422
|
+
);
|
|
13423
|
+
if (!branchToOpen) return;
|
|
13424
|
+
if (branchToOpen && branchToOpen.tree) {
|
|
13425
|
+
const parentIndexToSave = stateRef.current.readMode.currentMaxIndex;
|
|
13426
|
+
const savedBranchProgress = 0;
|
|
13427
|
+
stateRef.current.readMode.currentMaxIndex = savedBranchProgress;
|
|
13428
|
+
stateRef.current.maxAncestryRenderIndex = savedBranchProgress;
|
|
13429
|
+
const newStack = [
|
|
13430
|
+
...branchStack,
|
|
13431
|
+
{
|
|
13432
|
+
nodeId,
|
|
13433
|
+
branchId: branchToOpen.id,
|
|
13434
|
+
savedMaxIndex: parentIndexToSave,
|
|
13435
|
+
entryDirection: direction
|
|
13436
|
+
}
|
|
13437
|
+
];
|
|
13438
|
+
setReadingMode((prev) => ({
|
|
13439
|
+
...prev,
|
|
13440
|
+
branchStack: newStack,
|
|
13441
|
+
initialSectionId: null
|
|
13442
|
+
}));
|
|
13443
|
+
const branchAncestryObj = {
|
|
13444
|
+
ancestry_id: branchToOpen.id,
|
|
13445
|
+
ancestral_node: branchToOpen.tree.node.id,
|
|
13446
|
+
name: branchToOpen.name,
|
|
13447
|
+
description: branchToOpen.description,
|
|
13448
|
+
description_sections: branchToOpen.description_sections,
|
|
13449
|
+
tree: branchToOpen.tree,
|
|
13450
|
+
_originNodeId: nodeId,
|
|
13451
|
+
_branchDirection: direction
|
|
13452
|
+
};
|
|
13453
|
+
const allowedIds = /* @__PURE__ */ new Set(["preamble", 0, "0"]);
|
|
13454
|
+
const branchSections = parseDescriptionSections(
|
|
13455
|
+
branchToOpen.description || "",
|
|
13456
|
+
branchToOpen.description_sections || []
|
|
13457
|
+
);
|
|
13458
|
+
for (let i = 0; i <= savedBranchProgress; i++) {
|
|
13459
|
+
if (branchSections[i]) {
|
|
13460
|
+
if (branchSections[i].id)
|
|
13461
|
+
allowedIds.add(String(branchSections[i].id));
|
|
13462
|
+
if (branchSections[i].numericId !== void 0 && branchSections[i].numericId !== null) {
|
|
13463
|
+
allowedIds.add(branchSections[i].numericId);
|
|
13464
|
+
allowedIds.add(String(branchSections[i].numericId));
|
|
13465
|
+
}
|
|
13466
|
+
}
|
|
12776
13467
|
}
|
|
13468
|
+
const rotation = newStack.reduce((acc, step) => {
|
|
13469
|
+
return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
|
|
13470
|
+
}, 0);
|
|
13471
|
+
const initialFocusId = "preamble";
|
|
13472
|
+
handleRenderAncestry(
|
|
13473
|
+
branchAncestryObj,
|
|
13474
|
+
allowedIds,
|
|
13475
|
+
initialFocusId,
|
|
13476
|
+
rotation,
|
|
13477
|
+
false
|
|
13478
|
+
);
|
|
12777
13479
|
}
|
|
12778
13480
|
}
|
|
12779
|
-
|
|
12780
|
-
|
|
12781
|
-
|
|
12782
|
-
|
|
12783
|
-
|
|
12784
|
-
|
|
12785
|
-
|
|
12786
|
-
|
|
12787
|
-
|
|
12788
|
-
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
|
|
12792
|
-
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
13481
|
+
} else if (action === "back") {
|
|
13482
|
+
if (branchStack.length === 0) return;
|
|
13483
|
+
const newStack = [...branchStack];
|
|
13484
|
+
const popped = newStack.pop();
|
|
13485
|
+
if (popped && popped.branchId) {
|
|
13486
|
+
stateRef.current.readMode.progressMap[popped.branchId] = stateRef.current.readMode.currentMaxIndex;
|
|
13487
|
+
}
|
|
13488
|
+
const parentSavedIndex = popped.savedMaxIndex !== void 0 ? popped.savedMaxIndex : 0;
|
|
13489
|
+
stateRef.current.readMode.currentMaxIndex = parentSavedIndex;
|
|
13490
|
+
stateRef.current.maxAncestryRenderIndex = parentSavedIndex;
|
|
13491
|
+
let targetTreeToRender = fullTree;
|
|
13492
|
+
let targetAncestryInfo = ancestry;
|
|
13493
|
+
if (newStack.length > 0) {
|
|
13494
|
+
let ptr = fullTree;
|
|
13495
|
+
for (const step of newStack) {
|
|
13496
|
+
const found = findNodePath3(ptr, step.nodeId);
|
|
13497
|
+
if (found && found.node.parallel_branches) {
|
|
13498
|
+
const branch = found.node.parallel_branches.find(
|
|
13499
|
+
(b) => b.id === step.branchId
|
|
13500
|
+
);
|
|
13501
|
+
if (branch) {
|
|
13502
|
+
ptr = branch.tree;
|
|
13503
|
+
targetAncestryInfo = {
|
|
13504
|
+
...branch,
|
|
13505
|
+
ancestry_id: branch.id,
|
|
13506
|
+
ancestral_node: branch.tree.node.id
|
|
13507
|
+
};
|
|
13508
|
+
}
|
|
13509
|
+
}
|
|
13510
|
+
}
|
|
13511
|
+
targetTreeToRender = ptr;
|
|
13512
|
+
}
|
|
13513
|
+
const activeParentStackItem = newStack.length > 0 ? newStack[newStack.length - 1] : null;
|
|
13514
|
+
const parentAncestryObj = {
|
|
13515
|
+
...targetAncestryInfo,
|
|
13516
|
+
tree: targetTreeToRender,
|
|
13517
|
+
_originNodeId: activeParentStackItem ? activeParentStackItem.nodeId : null,
|
|
13518
|
+
_branchDirection: activeParentStackItem ? activeParentStackItem.entryDirection : null
|
|
13519
|
+
};
|
|
13520
|
+
const descriptionText = targetAncestryInfo.description || "";
|
|
13521
|
+
const savedSections = targetAncestryInfo.description_sections || [];
|
|
13522
|
+
const sections = parseDescriptionSections(
|
|
13523
|
+
descriptionText,
|
|
13524
|
+
savedSections
|
|
13525
|
+
);
|
|
13526
|
+
let focusTargetId = null;
|
|
13527
|
+
if (popped && popped.nodeId) {
|
|
13528
|
+
const mentionTag = `[[MENTION:node:${popped.nodeId}]]`;
|
|
13529
|
+
for (let i = 0; i < sections.length; i++) {
|
|
13530
|
+
if (sections[i].content && sections[i].content.includes(mentionTag)) {
|
|
13531
|
+
const section = sections[i];
|
|
13532
|
+
focusTargetId = section.numericId !== void 0 ? section.numericId : section.id;
|
|
13533
|
+
break;
|
|
13534
|
+
}
|
|
12799
13535
|
}
|
|
12800
13536
|
}
|
|
12801
|
-
|
|
12802
|
-
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
|
|
12806
|
-
|
|
12807
|
-
|
|
12808
|
-
|
|
12809
|
-
|
|
12810
|
-
|
|
12811
|
-
|
|
13537
|
+
const allowedIds = /* @__PURE__ */ new Set();
|
|
13538
|
+
allowedIds.add("preamble");
|
|
13539
|
+
allowedIds.add(0);
|
|
13540
|
+
allowedIds.add("0");
|
|
13541
|
+
for (let i = 0; i <= parentSavedIndex; i++) {
|
|
13542
|
+
if (sections[i]) {
|
|
13543
|
+
if (sections[i].id) allowedIds.add(String(sections[i].id));
|
|
13544
|
+
if (sections[i].numericId !== void 0 && sections[i].numericId !== null) {
|
|
13545
|
+
allowedIds.add(sections[i].numericId);
|
|
13546
|
+
allowedIds.add(String(sections[i].numericId));
|
|
13547
|
+
}
|
|
12812
13548
|
}
|
|
12813
13549
|
}
|
|
12814
|
-
|
|
12815
|
-
|
|
12816
|
-
|
|
12817
|
-
|
|
12818
|
-
|
|
12819
|
-
|
|
12820
|
-
|
|
12821
|
-
|
|
12822
|
-
|
|
13550
|
+
const rotation = newStack.reduce((acc, step) => {
|
|
13551
|
+
return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
|
|
13552
|
+
}, 0);
|
|
13553
|
+
handleRenderAncestry(
|
|
13554
|
+
parentAncestryObj,
|
|
13555
|
+
allowedIds,
|
|
13556
|
+
focusTargetId,
|
|
13557
|
+
rotation,
|
|
13558
|
+
false
|
|
13559
|
+
);
|
|
13560
|
+
if (popped && popped.nodeId) {
|
|
13561
|
+
const nodeMesh = stateRef.current.nodeObjects[String(popped.nodeId)];
|
|
13562
|
+
if (nodeMesh) {
|
|
13563
|
+
tweenToTarget(nodeMesh);
|
|
13564
|
+
}
|
|
12823
13565
|
}
|
|
13566
|
+
setReadingMode((prev) => ({
|
|
13567
|
+
...prev,
|
|
13568
|
+
branchStack: newStack,
|
|
13569
|
+
initialSectionId: focusTargetId
|
|
13570
|
+
}));
|
|
12824
13571
|
}
|
|
12825
|
-
|
|
12826
|
-
|
|
12827
|
-
|
|
12828
|
-
initialSectionId: focusTargetId
|
|
12829
|
-
}));
|
|
12830
|
-
}
|
|
12831
|
-
}, [readingMode, handleRenderAncestry, buildFullAncestryTree, tweenToTarget]);
|
|
13572
|
+
},
|
|
13573
|
+
[readingMode, handleRenderAncestry, buildFullAncestryTree, tweenToTarget]
|
|
13574
|
+
);
|
|
12832
13575
|
const handleReadModeHighlight = (0, import_react26.useCallback)((nodeId) => {
|
|
12833
13576
|
if (stateRef.current.highlightedNodeId !== nodeId) {
|
|
12834
13577
|
stateRef.current.highlightedNodeId = nodeId;
|
|
@@ -12836,7 +13579,8 @@ function XViewScene({
|
|
|
12836
13579
|
setHighlightedNodeId(nodeId);
|
|
12837
13580
|
}, []);
|
|
12838
13581
|
const activeNodeBranches = (0, import_react26.useMemo)(() => {
|
|
12839
|
-
if (!highlightedNodeId || !readingMode.ancestry || !readingMode.ancestry.tree)
|
|
13582
|
+
if (!highlightedNodeId || !readingMode.ancestry || !readingMode.ancestry.tree)
|
|
13583
|
+
return null;
|
|
12840
13584
|
const fullTree = buildFullAncestryTree(
|
|
12841
13585
|
readingMode.ancestry.tree,
|
|
12842
13586
|
Object.values(parentDataRef.current).flatMap((f) => f.nodes),
|
|
@@ -12871,7 +13615,13 @@ function XViewScene({
|
|
|
12871
13615
|
};
|
|
12872
13616
|
}
|
|
12873
13617
|
return null;
|
|
12874
|
-
}, [
|
|
13618
|
+
}, [
|
|
13619
|
+
highlightedNodeId,
|
|
13620
|
+
readingMode.ancestry,
|
|
13621
|
+
buildFullAncestryTree,
|
|
13622
|
+
readingMode.branchStack,
|
|
13623
|
+
ancestryDataRef.current
|
|
13624
|
+
]);
|
|
12875
13625
|
const backNavigationInfo = (0, import_react26.useMemo)(() => {
|
|
12876
13626
|
const { branchStack } = readingMode;
|
|
12877
13627
|
if (!branchStack || branchStack.length === 0) return null;
|
|
@@ -12907,7 +13657,9 @@ function XViewScene({
|
|
|
12907
13657
|
for (const step of branchStack) {
|
|
12908
13658
|
const found = findNodePath3(currentPtr, step.nodeId);
|
|
12909
13659
|
if (found && found.node.parallel_branches) {
|
|
12910
|
-
const branch = found.node.parallel_branches.find(
|
|
13660
|
+
const branch = found.node.parallel_branches.find(
|
|
13661
|
+
(b) => b.id === step.branchId
|
|
13662
|
+
);
|
|
12911
13663
|
if (branch) {
|
|
12912
13664
|
currentPtr = branch.tree;
|
|
12913
13665
|
currentMeta = branch;
|
|
@@ -12928,17 +13680,26 @@ function XViewScene({
|
|
|
12928
13680
|
if (!readingMode.isActive || !readingMode.ancestry || !readingMode.ancestry.abstraction_tree) {
|
|
12929
13681
|
return null;
|
|
12930
13682
|
}
|
|
12931
|
-
const allNodes = Object.values(parentDataRef.current || {}).flatMap(
|
|
13683
|
+
const allNodes = Object.values(parentDataRef.current || {}).flatMap(
|
|
13684
|
+
(f) => f.nodes || []
|
|
13685
|
+
);
|
|
12932
13686
|
const allAncestries = ancestryDataRef.current || [];
|
|
12933
13687
|
return buildFullAncestryTree(
|
|
12934
13688
|
readingMode.ancestry.abstraction_tree,
|
|
12935
13689
|
allNodes,
|
|
12936
13690
|
allAncestries
|
|
12937
13691
|
);
|
|
12938
|
-
}, [
|
|
13692
|
+
}, [
|
|
13693
|
+
readingMode.isActive,
|
|
13694
|
+
readingMode.ancestry,
|
|
13695
|
+
buildFullAncestryTree,
|
|
13696
|
+
sceneVersion
|
|
13697
|
+
]);
|
|
12939
13698
|
const handleStartReadingAncestry = (0, import_react26.useCallback)(
|
|
12940
|
-
async (ancestryObject) => {
|
|
12941
|
-
setContextMenu(
|
|
13699
|
+
async (ancestryObject, renderMode = "full") => {
|
|
13700
|
+
setContextMenu(
|
|
13701
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
13702
|
+
);
|
|
12942
13703
|
if (!ancestryObject || !ancestryObject.tree) {
|
|
12943
13704
|
console.warn("Ancestralidade inv\xE1lida para leitura.");
|
|
12944
13705
|
return;
|
|
@@ -12951,14 +13712,20 @@ function XViewScene({
|
|
|
12951
13712
|
const hasDescription = ancestryObject.description && ancestryObject.description.trim() !== "";
|
|
12952
13713
|
const hasMainTreeNodes = ancestryObject.tree.children && ancestryObject.tree.children.length > 0;
|
|
12953
13714
|
const hasAbstractionNodes = ancestryObject.abstraction_tree && ancestryObject.abstraction_tree.children && ancestryObject.abstraction_tree.children.length > 0;
|
|
12954
|
-
const
|
|
13715
|
+
const isFull = renderMode === "full";
|
|
13716
|
+
const isAncestryOnly = renderMode === "ancestry_only";
|
|
13717
|
+
const isAbstractionOnly = renderMode === "abstraction_only";
|
|
13718
|
+
let shouldRenderAbstraction = isAbstractionOnly;
|
|
13719
|
+
if (isFull) {
|
|
13720
|
+
shouldRenderAbstraction = !hasDescription && !hasMainTreeNodes && hasAbstractionNodes;
|
|
13721
|
+
}
|
|
12955
13722
|
setReadingMode({
|
|
12956
|
-
isActive:
|
|
13723
|
+
isActive: isFull ? hasDescription : false,
|
|
12957
13724
|
ancestry: ancestryObject,
|
|
12958
13725
|
branchStack: [],
|
|
12959
|
-
autoAbstraction:
|
|
13726
|
+
autoAbstraction: shouldRenderAbstraction
|
|
12960
13727
|
});
|
|
12961
|
-
if (
|
|
13728
|
+
if (shouldRenderAbstraction) {
|
|
12962
13729
|
handleRenderAbstractionTree(ancestryObject, null);
|
|
12963
13730
|
} else {
|
|
12964
13731
|
const initialSections = /* @__PURE__ */ new Set(["preamble", 0, "0"]);
|
|
@@ -12971,148 +13738,190 @@ function XViewScene({
|
|
|
12971
13738
|
},
|
|
12972
13739
|
[handleRenderAncestry, handleRenderAbstractionTree]
|
|
12973
13740
|
);
|
|
12974
|
-
const handleReadModeSectionChange = (0, import_react26.useCallback)(
|
|
12975
|
-
|
|
12976
|
-
|
|
12977
|
-
|
|
12978
|
-
|
|
12979
|
-
|
|
12980
|
-
|
|
12981
|
-
|
|
12982
|
-
|
|
12983
|
-
|
|
12984
|
-
|
|
12985
|
-
|
|
12986
|
-
|
|
12987
|
-
|
|
12988
|
-
|
|
12989
|
-
|
|
12990
|
-
|
|
12991
|
-
|
|
12992
|
-
|
|
12993
|
-
|
|
12994
|
-
|
|
13741
|
+
const handleReadModeSectionChange = (0, import_react26.useCallback)(
|
|
13742
|
+
(activeSectionId) => {
|
|
13743
|
+
const { ancestry, branchStack } = readingMode;
|
|
13744
|
+
if (!ancestry || !readingMode.isActive) return;
|
|
13745
|
+
let targetObj = ancestry;
|
|
13746
|
+
let targetTree = ancestry.tree;
|
|
13747
|
+
if (branchStack.length > 0) {
|
|
13748
|
+
const allNodes = Object.values(parentDataRef.current).flatMap(
|
|
13749
|
+
(f) => f.nodes
|
|
13750
|
+
);
|
|
13751
|
+
const fullTree = buildFullAncestryTree(
|
|
13752
|
+
ancestry.tree,
|
|
13753
|
+
allNodes,
|
|
13754
|
+
ancestryDataRef.current
|
|
13755
|
+
);
|
|
13756
|
+
let currentPtr = fullTree;
|
|
13757
|
+
for (const step of branchStack) {
|
|
13758
|
+
const found = findNodePath3(currentPtr, step.nodeId);
|
|
13759
|
+
if (found && found.node && found.node.parallel_branches) {
|
|
13760
|
+
const branch = found.node.parallel_branches.find(
|
|
13761
|
+
(b) => b.id === step.branchId
|
|
13762
|
+
);
|
|
13763
|
+
if (branch) {
|
|
13764
|
+
targetObj = branch;
|
|
13765
|
+
targetTree = branch.tree;
|
|
13766
|
+
currentPtr = branch.tree;
|
|
13767
|
+
}
|
|
12995
13768
|
}
|
|
12996
13769
|
}
|
|
12997
13770
|
}
|
|
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].
|
|
13771
|
+
const descriptionText = targetObj.description || "";
|
|
13772
|
+
const savedSections = targetObj.description_sections || [];
|
|
13773
|
+
const sections = parseDescriptionSections(descriptionText, savedSections);
|
|
13774
|
+
let activeIndex = sections.findIndex(
|
|
13775
|
+
(s) => String(s.id) === String(activeSectionId)
|
|
13776
|
+
);
|
|
13777
|
+
if (activeIndex === -1 && !isNaN(parseInt(activeSectionId))) {
|
|
13778
|
+
activeIndex = sections.findIndex(
|
|
13779
|
+
(s) => s.numericId === parseInt(activeSectionId)
|
|
13780
|
+
);
|
|
13781
|
+
}
|
|
13782
|
+
if (activeIndex === -1) activeIndex = 0;
|
|
13783
|
+
stateRef.current.readMode.currentMaxIndex = activeIndex;
|
|
13784
|
+
stateRef.current.maxAncestryRenderIndex = activeIndex;
|
|
13785
|
+
const renderLimitIndex = stateRef.current.maxAncestryRenderIndex;
|
|
13786
|
+
const allowedIds = /* @__PURE__ */ new Set();
|
|
13787
|
+
allowedIds.add("preamble");
|
|
13788
|
+
allowedIds.add(0);
|
|
13789
|
+
allowedIds.add("0");
|
|
13790
|
+
for (let i = 0; i <= renderLimitIndex; i++) {
|
|
13791
|
+
if (sections[i]) {
|
|
13792
|
+
if (sections[i].id) allowedIds.add(String(sections[i].id));
|
|
13793
|
+
if (sections[i].numericId !== void 0 && sections[i].numericId !== null) {
|
|
13794
|
+
allowedIds.add(sections[i].numericId);
|
|
13795
|
+
allowedIds.add(String(sections[i].numericId));
|
|
13796
|
+
}
|
|
13020
13797
|
}
|
|
13021
13798
|
}
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
13025
|
-
|
|
13026
|
-
|
|
13027
|
-
|
|
13028
|
-
|
|
13029
|
-
|
|
13030
|
-
|
|
13031
|
-
|
|
13032
|
-
|
|
13033
|
-
|
|
13034
|
-
|
|
13035
|
-
|
|
13036
|
-
|
|
13037
|
-
|
|
13038
|
-
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13799
|
+
const activeSection = sections[activeIndex];
|
|
13800
|
+
let focusTargetId = activeSectionId;
|
|
13801
|
+
if (activeSection && activeSection.numericId !== void 0 && activeSection.numericId !== null) {
|
|
13802
|
+
focusTargetId = activeSection.numericId;
|
|
13803
|
+
}
|
|
13804
|
+
const currentStackItem = branchStack.length > 0 ? branchStack[branchStack.length - 1] : null;
|
|
13805
|
+
const renderPayload = {
|
|
13806
|
+
...targetObj,
|
|
13807
|
+
ancestry_id: targetObj.ancestry_id || targetObj.id,
|
|
13808
|
+
ancestral_node: targetTree.node ? targetTree.node.id : targetTree.node_id,
|
|
13809
|
+
tree: targetTree,
|
|
13810
|
+
_originNodeId: currentStackItem ? currentStackItem.nodeId : null,
|
|
13811
|
+
_branchDirection: currentStackItem ? currentStackItem.entryDirection : null,
|
|
13812
|
+
_forceUpdate: true
|
|
13813
|
+
};
|
|
13814
|
+
const rotation = branchStack.reduce((acc, step) => {
|
|
13815
|
+
return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
|
|
13816
|
+
}, 0);
|
|
13817
|
+
handleRenderAncestry(renderPayload, allowedIds, focusTargetId, rotation);
|
|
13818
|
+
},
|
|
13819
|
+
[
|
|
13820
|
+
readingMode,
|
|
13821
|
+
handleRenderAncestry,
|
|
13822
|
+
buildFullAncestryTree,
|
|
13823
|
+
ancestryDataRef.current
|
|
13824
|
+
]
|
|
13825
|
+
);
|
|
13043
13826
|
const handleCloseReadMode = (0, import_react26.useCallback)(() => {
|
|
13044
13827
|
setReadingMode({ isActive: false, ancestry: null, branchStack: [] });
|
|
13045
13828
|
}, []);
|
|
13046
|
-
const handleAncestrySectionChange = (0, import_react26.useCallback)(
|
|
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
|
-
|
|
13829
|
+
const handleAncestrySectionChange = (0, import_react26.useCallback)(
|
|
13830
|
+
(activeSectionId, ancestryOverride = null, rotation = 0) => {
|
|
13831
|
+
var _a2, _b2;
|
|
13832
|
+
const currentMode = stateRef.current.ancestry;
|
|
13833
|
+
let targetObj = ancestryOverride;
|
|
13834
|
+
if (!targetObj) {
|
|
13835
|
+
const currentAncestryId = currentMode.currentAncestryId;
|
|
13836
|
+
const ancestryObj = (ancestryDataRef.current || []).find(
|
|
13837
|
+
(a) => String(a.ancestry_id) === String(currentAncestryId)
|
|
13838
|
+
);
|
|
13839
|
+
targetObj = ancestryObj || (currentMode.isActive ? {
|
|
13840
|
+
...currentMode,
|
|
13841
|
+
ancestry_id: "temp_creating",
|
|
13842
|
+
ancestral_node: (_b2 = (_a2 = currentMode.tree) == null ? void 0 : _a2.node) == null ? void 0 : _b2.id
|
|
13843
|
+
} : null);
|
|
13844
|
+
}
|
|
13845
|
+
if (!targetObj) return;
|
|
13846
|
+
const targetId = targetObj.ancestry_id || targetObj.id;
|
|
13847
|
+
if (stateRef.current.lastRenderedAncestryId !== targetId) {
|
|
13848
|
+
stateRef.current.maxAncestryRenderIndex = 0;
|
|
13849
|
+
stateRef.current.lastRenderedAncestryId = targetId;
|
|
13850
|
+
}
|
|
13851
|
+
const descriptionText = (ancestryOverride ? targetObj.description : currentMode.ancestryDescription) || targetObj.description || "";
|
|
13852
|
+
const savedSections = (ancestryOverride ? targetObj.description_sections : currentMode.ancestryDescriptionSections) || targetObj.description_sections || [];
|
|
13853
|
+
const sections = parseDescriptionSections(descriptionText, savedSections);
|
|
13854
|
+
let activeIndex = sections.findIndex(
|
|
13855
|
+
(s) => String(s.id) === String(activeSectionId)
|
|
13856
|
+
);
|
|
13857
|
+
if (activeIndex === -1 && !isNaN(parseInt(activeSectionId))) {
|
|
13858
|
+
activeIndex = sections.findIndex(
|
|
13859
|
+
(s) => s.numericId === parseInt(activeSectionId)
|
|
13860
|
+
);
|
|
13861
|
+
}
|
|
13862
|
+
if (activeIndex === -1) activeIndex = 0;
|
|
13863
|
+
if (stateRef.current.maxAncestryRenderIndex === void 0)
|
|
13864
|
+
stateRef.current.maxAncestryRenderIndex = 0;
|
|
13865
|
+
stateRef.current.maxAncestryRenderIndex = activeIndex;
|
|
13866
|
+
const renderLimitIndex = stateRef.current.maxAncestryRenderIndex;
|
|
13867
|
+
const allowedIds = /* @__PURE__ */ new Set();
|
|
13868
|
+
allowedIds.add("preamble");
|
|
13869
|
+
allowedIds.add(0);
|
|
13870
|
+
allowedIds.add("0");
|
|
13871
|
+
for (let i = 0; i <= renderLimitIndex; i++) {
|
|
13872
|
+
if (sections[i]) {
|
|
13873
|
+
if (sections[i].id) allowedIds.add(String(sections[i].id));
|
|
13874
|
+
if (sections[i].numericId !== void 0 && sections[i].numericId !== null) {
|
|
13875
|
+
allowedIds.add(sections[i].numericId);
|
|
13876
|
+
allowedIds.add(String(sections[i].numericId));
|
|
13877
|
+
}
|
|
13086
13878
|
}
|
|
13087
13879
|
}
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
|
|
13880
|
+
const activeSection = sections[activeIndex];
|
|
13881
|
+
let focusTargetId = activeSectionId;
|
|
13882
|
+
if (activeSection && activeSection.numericId !== void 0 && activeSection.numericId !== null) {
|
|
13883
|
+
focusTargetId = activeSection.numericId;
|
|
13884
|
+
}
|
|
13885
|
+
const treeToRender = ancestryOverride ? ancestryOverride.tree : currentMode.isActive && currentMode.tree ? currentMode.tree : targetObj.tree;
|
|
13886
|
+
const renderPayload = { ...targetObj, tree: treeToRender };
|
|
13887
|
+
handleRenderAncestry(renderPayload, allowedIds, focusTargetId, rotation);
|
|
13888
|
+
},
|
|
13889
|
+
[handleRenderAncestry]
|
|
13890
|
+
);
|
|
13098
13891
|
const handleEditAncestry = (0, import_react26.useCallback)(
|
|
13099
13892
|
async (ancestryObject) => {
|
|
13100
|
-
setContextMenu(
|
|
13893
|
+
setContextMenu(
|
|
13894
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
13895
|
+
);
|
|
13101
13896
|
if (!ancestryObject || !ancestryObject.tree) {
|
|
13102
|
-
alert(
|
|
13897
|
+
alert(
|
|
13898
|
+
"N\xE3o foi poss\xEDvel carregar os dados desta ancestralidade para edi\xE7\xE3o."
|
|
13899
|
+
);
|
|
13103
13900
|
return;
|
|
13104
13901
|
}
|
|
13105
13902
|
stateRef.current.maxAncestryRenderIndex = 0;
|
|
13106
13903
|
const initialSections = /* @__PURE__ */ new Set(["preamble", 0]);
|
|
13107
13904
|
await handleRenderAncestry(ancestryObject, initialSections);
|
|
13108
|
-
const allParentNodes = Object.values(parentDataRef.current).flatMap(
|
|
13905
|
+
const allParentNodes = Object.values(parentDataRef.current).flatMap(
|
|
13906
|
+
(fileData) => fileData.nodes
|
|
13907
|
+
);
|
|
13109
13908
|
const allAncestries = ancestryDataRef.current || [];
|
|
13110
|
-
const fullTree = buildFullAncestryTree(
|
|
13909
|
+
const fullTree = buildFullAncestryTree(
|
|
13910
|
+
ancestryObject.tree,
|
|
13911
|
+
allParentNodes,
|
|
13912
|
+
allAncestries
|
|
13913
|
+
);
|
|
13111
13914
|
if (!fullTree) {
|
|
13112
|
-
alert(
|
|
13915
|
+
alert(
|
|
13916
|
+
"Falha ao reconstruir a \xE1rvore de ancestralidade. Alguns Nodes podem estar faltando."
|
|
13917
|
+
);
|
|
13113
13918
|
return;
|
|
13114
13919
|
}
|
|
13115
|
-
const fullAbstractionTree = ancestryObject.abstraction_tree ? buildFullAncestryTree(
|
|
13920
|
+
const fullAbstractionTree = ancestryObject.abstraction_tree ? buildFullAncestryTree(
|
|
13921
|
+
ancestryObject.abstraction_tree,
|
|
13922
|
+
allParentNodes,
|
|
13923
|
+
allAncestries
|
|
13924
|
+
) : { node: fullTree.node, children: [] };
|
|
13116
13925
|
setAncestryMode({
|
|
13117
13926
|
isActive: true,
|
|
13118
13927
|
...ancestryObject,
|
|
@@ -13164,7 +13973,9 @@ function XViewScene({
|
|
|
13164
13973
|
const treeToUse = treeOverride || ancestryMode.tree;
|
|
13165
13974
|
const { isEditMode, currentAncestryId } = ancestryMode;
|
|
13166
13975
|
if (!treeToUse || !treeToUse.node) {
|
|
13167
|
-
alert(
|
|
13976
|
+
alert(
|
|
13977
|
+
"Erro: A estrutura da ancestralidade \xE9 inv\xE1lida (Node raiz ausente)."
|
|
13978
|
+
);
|
|
13168
13979
|
return;
|
|
13169
13980
|
}
|
|
13170
13981
|
if (!save_view_data || !stateRef.current.nodeIdToParentFileMap) return;
|
|
@@ -13197,14 +14008,18 @@ function XViewScene({
|
|
|
13197
14008
|
};
|
|
13198
14009
|
};
|
|
13199
14010
|
const treeWithIds = convertTreeToIds(treeToUse);
|
|
13200
|
-
const abstractionTreeWithIds = convertTreeToIds(
|
|
14011
|
+
const abstractionTreeWithIds = convertTreeToIds(
|
|
14012
|
+
ancestryMode.abstraction_tree
|
|
14013
|
+
);
|
|
13201
14014
|
if (!treeWithIds) {
|
|
13202
14015
|
alert("Erro ao processar a \xE1rvore da ancestralidade.");
|
|
13203
14016
|
return;
|
|
13204
14017
|
}
|
|
13205
14018
|
let originalAncestryObj = null;
|
|
13206
14019
|
if (isEditMode && currentAncestryId) {
|
|
13207
|
-
originalAncestryObj = (ancestryDataRef.current || []).find(
|
|
14020
|
+
originalAncestryObj = (ancestryDataRef.current || []).find(
|
|
14021
|
+
(anc) => String(anc.ancestry_id) === String(currentAncestryId)
|
|
14022
|
+
);
|
|
13208
14023
|
}
|
|
13209
14024
|
const ancestryObjectToSave = {
|
|
13210
14025
|
ancestry_id: isEditMode && currentAncestryId ? currentAncestryId : `${import_short_uuid2.default.generate()}`,
|
|
@@ -13287,14 +14102,20 @@ function XViewScene({
|
|
|
13287
14102
|
try {
|
|
13288
14103
|
if (isExternalSave) {
|
|
13289
14104
|
try {
|
|
13290
|
-
const remoteResponse = await get_ancestry_file(
|
|
14105
|
+
const remoteResponse = await get_ancestry_file(
|
|
14106
|
+
targetFileIdForCache,
|
|
14107
|
+
targetOwnerIdForCache
|
|
14108
|
+
);
|
|
13291
14109
|
if (remoteResponse.success && Array.isArray(remoteResponse.data)) {
|
|
13292
14110
|
masterAncestryList = remoteResponse.data;
|
|
13293
14111
|
} else {
|
|
13294
14112
|
masterAncestryList = [];
|
|
13295
14113
|
}
|
|
13296
14114
|
} catch (fetchErr) {
|
|
13297
|
-
console.warn(
|
|
14115
|
+
console.warn(
|
|
14116
|
+
"Arquivo de destino n\xE3o existe ou erro ao buscar, criando novo:",
|
|
14117
|
+
fetchErr
|
|
14118
|
+
);
|
|
13298
14119
|
masterAncestryList = [];
|
|
13299
14120
|
}
|
|
13300
14121
|
} else {
|
|
@@ -13314,7 +14135,9 @@ function XViewScene({
|
|
|
13314
14135
|
const result = await save_view_data(finalSaveUrl, masterAncestryList);
|
|
13315
14136
|
if (result.success) {
|
|
13316
14137
|
const localList = [...ancestryDataRef.current || []];
|
|
13317
|
-
const localIndex = localList.findIndex(
|
|
14138
|
+
const localIndex = localList.findIndex(
|
|
14139
|
+
(a) => String(a.ancestry_id) === String(ancestryObjectToSave.ancestry_id)
|
|
14140
|
+
);
|
|
13318
14141
|
if (localIndex !== -1) {
|
|
13319
14142
|
localList[localIndex] = ancestryObjectToSave;
|
|
13320
14143
|
} else {
|
|
@@ -13342,11 +14165,29 @@ function XViewScene({
|
|
|
13342
14165
|
return;
|
|
13343
14166
|
}
|
|
13344
14167
|
if (!keepOpen) {
|
|
13345
|
-
setAncestryMode({
|
|
14168
|
+
setAncestryMode({
|
|
14169
|
+
isActive: false,
|
|
14170
|
+
tree: null,
|
|
14171
|
+
selectedParentId: null,
|
|
14172
|
+
isEditMode: false,
|
|
14173
|
+
currentAncestryId: null,
|
|
14174
|
+
ancestryName: "",
|
|
14175
|
+
ancestryDescription: "",
|
|
14176
|
+
ancestryDescriptionSections: [],
|
|
14177
|
+
isAddingNodes: false
|
|
14178
|
+
});
|
|
13346
14179
|
if (mountRef.current) mountRef.current.style.cursor = "grab";
|
|
13347
14180
|
}
|
|
13348
14181
|
},
|
|
13349
|
-
[
|
|
14182
|
+
[
|
|
14183
|
+
ancestryMode,
|
|
14184
|
+
ancestry_save_url,
|
|
14185
|
+
handleRenderAncestry,
|
|
14186
|
+
save_view_data,
|
|
14187
|
+
sceneConfigId,
|
|
14188
|
+
ownerId,
|
|
14189
|
+
get_ancestry_file
|
|
14190
|
+
]
|
|
13350
14191
|
);
|
|
13351
14192
|
const handleOpenAncestryRelEditor = (path, currentData) => {
|
|
13352
14193
|
setEditingAncestryRel({ visible: true, data: currentData, path });
|
|
@@ -13376,7 +14217,9 @@ function XViewScene({
|
|
|
13376
14217
|
if (ancestryToDelete && delete_file_action) {
|
|
13377
14218
|
const urls = extractFileUrlsFromProperties(ancestryToDelete);
|
|
13378
14219
|
if (urls.length > 0) {
|
|
13379
|
-
Promise.all(urls.map((url) => delete_file_action(url))).catch(
|
|
14220
|
+
Promise.all(urls.map((url) => delete_file_action(url))).catch(
|
|
14221
|
+
(err) => console.error("Erro ao deletar arquivos da ancestralidade:", err)
|
|
14222
|
+
);
|
|
13380
14223
|
}
|
|
13381
14224
|
}
|
|
13382
14225
|
if (!ancestryToDelete) {
|
|
@@ -13386,7 +14229,9 @@ function XViewScene({
|
|
|
13386
14229
|
const sourceFileId = ancestryToDelete._source_file_id;
|
|
13387
14230
|
const sourceOwnerId = ancestryToDelete._source_owner_id;
|
|
13388
14231
|
if (!sourceFileId || !sourceOwnerId) {
|
|
13389
|
-
alert(
|
|
14232
|
+
alert(
|
|
14233
|
+
"N\xE3o foi poss\xEDvel identificar o arquivo de origem desta ancestralidade."
|
|
14234
|
+
);
|
|
13390
14235
|
return;
|
|
13391
14236
|
}
|
|
13392
14237
|
const finalSaveUrl = `x_view_ancestry/${sourceOwnerId}/${sourceFileId}`;
|
|
@@ -13394,13 +14239,18 @@ function XViewScene({
|
|
|
13394
14239
|
(anc) => anc._source_file_id === sourceFileId && String(anc.ancestry_id) !== String(ancestryIdToDelete)
|
|
13395
14240
|
);
|
|
13396
14241
|
try {
|
|
13397
|
-
const result = await save_view_data(
|
|
14242
|
+
const result = await save_view_data(
|
|
14243
|
+
finalSaveUrl,
|
|
14244
|
+
updatedAncestriesForFile
|
|
14245
|
+
);
|
|
13398
14246
|
if (result.success) {
|
|
13399
14247
|
ancestryDataRef.current = (ancestryDataRef.current || []).filter(
|
|
13400
14248
|
(ancestry) => String(ancestry.ancestry_id) !== String(ancestryIdToDelete)
|
|
13401
14249
|
);
|
|
13402
14250
|
const { renderedAncestries, ancestryGroup } = stateRef.current;
|
|
13403
|
-
const renderIndex = renderedAncestries.findIndex(
|
|
14251
|
+
const renderIndex = renderedAncestries.findIndex(
|
|
14252
|
+
(a) => String(a.id) === String(ancestryIdToDelete)
|
|
14253
|
+
);
|
|
13404
14254
|
if (renderIndex !== -1) {
|
|
13405
14255
|
const toRemove = renderedAncestries[renderIndex];
|
|
13406
14256
|
toRemove.lines.forEach((line) => {
|
|
@@ -13409,18 +14259,29 @@ function XViewScene({
|
|
|
13409
14259
|
if (line.material) line.material.dispose();
|
|
13410
14260
|
});
|
|
13411
14261
|
renderedAncestries.splice(renderIndex, 1);
|
|
13412
|
-
stateRef.current.ancestryLinks = renderedAncestries.flatMap(
|
|
14262
|
+
stateRef.current.ancestryLinks = renderedAncestries.flatMap(
|
|
14263
|
+
(a) => a.lines
|
|
14264
|
+
);
|
|
13413
14265
|
}
|
|
13414
14266
|
setSceneVersion((v) => v + 1);
|
|
13415
14267
|
} else {
|
|
13416
|
-
throw new Error(
|
|
14268
|
+
throw new Error(
|
|
14269
|
+
result.error || "Ocorreu um erro desconhecido ao excluir."
|
|
14270
|
+
);
|
|
13417
14271
|
}
|
|
13418
14272
|
} catch (error) {
|
|
13419
14273
|
console.error("Falha ao excluir a ancestralidade:", error);
|
|
13420
14274
|
alert(`Erro ao excluir a ancestralidade: ${error.message}`);
|
|
13421
14275
|
return;
|
|
13422
14276
|
}
|
|
13423
|
-
setAncestryMode({
|
|
14277
|
+
setAncestryMode({
|
|
14278
|
+
isActive: false,
|
|
14279
|
+
tree: null,
|
|
14280
|
+
selectedParentId: null,
|
|
14281
|
+
isEditMode: false,
|
|
14282
|
+
currentAncestryId: null,
|
|
14283
|
+
ancestryName: ""
|
|
14284
|
+
});
|
|
13424
14285
|
if (mountRef.current) mountRef.current.style.cursor = "grab";
|
|
13425
14286
|
},
|
|
13426
14287
|
[save_view_data, delete_file_action]
|
|
@@ -13428,24 +14289,38 @@ function XViewScene({
|
|
|
13428
14289
|
const handleOpenAncestryBoard = (0, import_react26.useCallback)(() => {
|
|
13429
14290
|
setIsAncestryBoardOpen(true);
|
|
13430
14291
|
}, []);
|
|
13431
|
-
const handleSelectAncestryFromBoard = (0, import_react26.useCallback)(
|
|
13432
|
-
|
|
13433
|
-
|
|
13434
|
-
|
|
13435
|
-
|
|
13436
|
-
|
|
13437
|
-
|
|
13438
|
-
|
|
13439
|
-
|
|
13440
|
-
|
|
14292
|
+
const handleSelectAncestryFromBoard = (0, import_react26.useCallback)(
|
|
14293
|
+
(ancestry) => {
|
|
14294
|
+
setIsAncestryBoardOpen(false);
|
|
14295
|
+
setIsSidebarOpen(false);
|
|
14296
|
+
handleStartReadingAncestry(ancestry);
|
|
14297
|
+
},
|
|
14298
|
+
[handleStartReadingAncestry]
|
|
14299
|
+
);
|
|
14300
|
+
const handleSaveAncestryBoard = (0, import_react26.useCallback)(
|
|
14301
|
+
async (groups) => {
|
|
14302
|
+
if (!sceneConfigId || !viewParams || !session) return;
|
|
14303
|
+
const sceneType = (viewParams.type || "").toLowerCase().includes("database") ? "database" : "view";
|
|
14304
|
+
await save_ancestry_board_action(
|
|
14305
|
+
sceneConfigId,
|
|
14306
|
+
sceneType,
|
|
14307
|
+
groups,
|
|
14308
|
+
session,
|
|
14309
|
+
ownerId
|
|
14310
|
+
);
|
|
14311
|
+
},
|
|
14312
|
+
[sceneConfigId, viewParams, session, save_ancestry_board_action, ownerId]
|
|
14313
|
+
);
|
|
13441
14314
|
const existingNodeTypes = (0, import_react26.useMemo)(() => {
|
|
13442
14315
|
if (!parentDataRef.current) {
|
|
13443
14316
|
return [];
|
|
13444
14317
|
}
|
|
13445
|
-
const allTypes = Object.values(parentDataRef.current).flatMap(
|
|
13446
|
-
|
|
13447
|
-
|
|
13448
|
-
|
|
14318
|
+
const allTypes = Object.values(parentDataRef.current).flatMap(
|
|
14319
|
+
(fileData) => fileData.nodes.flatMap((node) => {
|
|
14320
|
+
if (Array.isArray(node.type)) return node.type;
|
|
14321
|
+
return [node.type];
|
|
14322
|
+
})
|
|
14323
|
+
).filter((t) => Boolean(t) && String(t).toLowerCase() !== "quest");
|
|
13449
14324
|
return [...new Set(allTypes)];
|
|
13450
14325
|
}, [parentDataRef.current, sceneVersion]);
|
|
13451
14326
|
const searchableDbNodes = (0, import_react26.useMemo)(() => {
|
|
@@ -13459,7 +14334,10 @@ function XViewScene({
|
|
|
13459
14334
|
}, [parentDataRef.current, sceneVersion]);
|
|
13460
14335
|
const handleAddExistingNode = (0, import_react26.useCallback)(
|
|
13461
14336
|
(nodeId) => {
|
|
13462
|
-
return userActionHandlers.handleAddExistingNodeById(
|
|
14337
|
+
return userActionHandlers.handleAddExistingNodeById(
|
|
14338
|
+
actionHandlerContext,
|
|
14339
|
+
nodeId
|
|
14340
|
+
);
|
|
13463
14341
|
},
|
|
13464
14342
|
[actionHandlerContext]
|
|
13465
14343
|
);
|
|
@@ -13467,7 +14345,9 @@ function XViewScene({
|
|
|
13467
14345
|
var _a2, _b2, _c2;
|
|
13468
14346
|
const { nodeObjects, allLinks } = stateRef.current;
|
|
13469
14347
|
if (!nodeObjects || !allLinks || !sceneSaveUrl || !parentDataRef.current) {
|
|
13470
|
-
console.warn(
|
|
14348
|
+
console.warn(
|
|
14349
|
+
"N\xE3o \xE9 poss\xEDvel salvar a cena: estado n\xE3o inicializado ou URL de salvamento ausente."
|
|
14350
|
+
);
|
|
13471
14351
|
return;
|
|
13472
14352
|
}
|
|
13473
14353
|
if (!save_view_data) return;
|
|
@@ -13504,48 +14384,68 @@ function XViewScene({
|
|
|
13504
14384
|
}, [sceneSaveUrl, save_view_data, sceneConfigId, viewParams == null ? void 0 : viewParams.type]);
|
|
13505
14385
|
const allAvailableNodes = (0, import_react26.useMemo)(() => {
|
|
13506
14386
|
if (!parentDataRef.current) return [];
|
|
13507
|
-
return Object.values(parentDataRef.current).flatMap(
|
|
14387
|
+
return Object.values(parentDataRef.current).flatMap(
|
|
14388
|
+
(fileData) => fileData.nodes || []
|
|
14389
|
+
);
|
|
13508
14390
|
}, [sceneVersion, isInitialized]);
|
|
13509
14391
|
const allAvailableAncestries = (0, import_react26.useMemo)(() => {
|
|
13510
14392
|
return ancestryDataRef.current || [];
|
|
13511
14393
|
}, [sceneVersion, isInitialized]);
|
|
13512
|
-
const handleOpenReference = (0, import_react26.useCallback)(
|
|
13513
|
-
|
|
13514
|
-
|
|
13515
|
-
|
|
13516
|
-
|
|
13517
|
-
|
|
13518
|
-
|
|
13519
|
-
|
|
13520
|
-
|
|
13521
|
-
|
|
13522
|
-
|
|
14394
|
+
const handleOpenReference = (0, import_react26.useCallback)(
|
|
14395
|
+
(referenceData) => {
|
|
14396
|
+
const { type, id } = referenceData;
|
|
14397
|
+
if (type === "node") {
|
|
14398
|
+
const targetNode = allAvailableNodes.find(
|
|
14399
|
+
(n) => String(n.id) === String(id)
|
|
14400
|
+
);
|
|
14401
|
+
if (targetNode) {
|
|
14402
|
+
setAncestryLinkDetails(null);
|
|
14403
|
+
setDetailsLink(null);
|
|
14404
|
+
setDetailsNode(targetNode);
|
|
14405
|
+
const sceneMesh = stateRef.current.nodeObjects[String(id)];
|
|
14406
|
+
if (sceneMesh) {
|
|
14407
|
+
tweenToTarget(sceneMesh);
|
|
14408
|
+
}
|
|
14409
|
+
} else {
|
|
14410
|
+
alert("Node original n\xE3o encontrado neste contexto.");
|
|
14411
|
+
}
|
|
14412
|
+
} else if (type === "ancestry") {
|
|
14413
|
+
const targetAncestry = allAvailableAncestries.find(
|
|
14414
|
+
(a) => String(a.ancestry_id) === String(id)
|
|
14415
|
+
);
|
|
14416
|
+
if (targetAncestry) {
|
|
14417
|
+
setDetailsNode(null);
|
|
14418
|
+
setDetailsLink(null);
|
|
14419
|
+
setAncestryLinkDetails(null);
|
|
14420
|
+
handleEditAncestry(targetAncestry);
|
|
14421
|
+
} else {
|
|
14422
|
+
alert("Ancestralidade original n\xE3o encontrada neste contexto.");
|
|
13523
14423
|
}
|
|
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
14424
|
}
|
|
13537
|
-
}
|
|
13538
|
-
|
|
14425
|
+
},
|
|
14426
|
+
[
|
|
14427
|
+
allAvailableNodes,
|
|
14428
|
+
allAvailableAncestries,
|
|
14429
|
+
handleEditAncestry,
|
|
14430
|
+
tweenToTarget
|
|
14431
|
+
]
|
|
14432
|
+
);
|
|
13539
14433
|
const handleToggleAncestryAddMode = (0, import_react26.useCallback)(() => {
|
|
13540
|
-
setAncestryMode((prev) => ({
|
|
14434
|
+
setAncestryMode((prev) => ({
|
|
14435
|
+
...prev,
|
|
14436
|
+
isAddingNodes: !prev.isAddingNodes
|
|
14437
|
+
}));
|
|
13541
14438
|
}, []);
|
|
13542
|
-
const handleFocusNode = (0, import_react26.useCallback)(
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
|
|
13546
|
-
|
|
13547
|
-
|
|
13548
|
-
|
|
14439
|
+
const handleFocusNode = (0, import_react26.useCallback)(
|
|
14440
|
+
(nodeData) => {
|
|
14441
|
+
if (!nodeData) return;
|
|
14442
|
+
const nodeMesh = stateRef.current.nodeObjects[String(nodeData.id)];
|
|
14443
|
+
if (nodeMesh) {
|
|
14444
|
+
tweenToTarget(nodeMesh, 1.2);
|
|
14445
|
+
}
|
|
14446
|
+
},
|
|
14447
|
+
[tweenToTarget]
|
|
14448
|
+
);
|
|
13549
14449
|
const availableDatasets = (0, import_react26.useMemo)(() => {
|
|
13550
14450
|
if (!sceneDataRef.current || !parentDataRef.current) return [];
|
|
13551
14451
|
return sceneDataRef.current.parent_dbs.map((db) => {
|
|
@@ -13556,7 +14456,9 @@ function XViewScene({
|
|
|
13556
14456
|
};
|
|
13557
14457
|
});
|
|
13558
14458
|
}, [sceneVersion, isInitialized]);
|
|
13559
|
-
const sourceNodeDatasetId = creationMode.sourceNodeData ? (_b = stateRef.current.nodeIdToParentFileMap.get(
|
|
14459
|
+
const sourceNodeDatasetId = creationMode.sourceNodeData ? (_b = stateRef.current.nodeIdToParentFileMap.get(
|
|
14460
|
+
String(creationMode.sourceNodeData.id)
|
|
14461
|
+
)) == null ? void 0 : _b.parentFileId : null;
|
|
13560
14462
|
const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
|
|
13561
14463
|
(0, import_react26.useEffect)(() => {
|
|
13562
14464
|
if (isInitialized && focusNodeId && !hasFocusedInitial) {
|
|
@@ -13569,13 +14471,24 @@ function XViewScene({
|
|
|
13569
14471
|
}, 300);
|
|
13570
14472
|
} else {
|
|
13571
14473
|
setHasFocusedInitial(true);
|
|
14474
|
+
setInvalidTargetError(
|
|
14475
|
+
"O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
|
|
14476
|
+
);
|
|
13572
14477
|
}
|
|
13573
14478
|
}
|
|
13574
|
-
}, [
|
|
14479
|
+
}, [
|
|
14480
|
+
isInitialized,
|
|
14481
|
+
sceneVersion,
|
|
14482
|
+
focusNodeId,
|
|
14483
|
+
hasFocusedInitial,
|
|
14484
|
+
tweenToTarget
|
|
14485
|
+
]);
|
|
13575
14486
|
(0, import_react26.useEffect)(() => {
|
|
13576
14487
|
if (isInitialized && focusAncestryId && !hasOpenedInitialAncestry) {
|
|
13577
14488
|
const ancestries = ancestryDataRef.current || [];
|
|
13578
|
-
const targetAncestry = ancestries.find(
|
|
14489
|
+
const targetAncestry = ancestries.find(
|
|
14490
|
+
(a) => String(a.ancestry_id) === String(focusAncestryId)
|
|
14491
|
+
);
|
|
13579
14492
|
if (targetAncestry) {
|
|
13580
14493
|
setTimeout(() => {
|
|
13581
14494
|
handleStartReadingAncestry(targetAncestry);
|
|
@@ -13583,20 +14496,43 @@ function XViewScene({
|
|
|
13583
14496
|
}, 300);
|
|
13584
14497
|
} else {
|
|
13585
14498
|
setHasOpenedInitialAncestry(true);
|
|
14499
|
+
setInvalidTargetError(
|
|
14500
|
+
"O link aponta para uma ancestralidade que n\xE3o foi encontrada ou foi exclu\xEDda."
|
|
14501
|
+
);
|
|
13586
14502
|
}
|
|
13587
14503
|
}
|
|
13588
|
-
}, [
|
|
14504
|
+
}, [
|
|
14505
|
+
isInitialized,
|
|
14506
|
+
sceneVersion,
|
|
14507
|
+
focusAncestryId,
|
|
14508
|
+
hasOpenedInitialAncestry,
|
|
14509
|
+
handleStartReadingAncestry
|
|
14510
|
+
]);
|
|
13589
14511
|
(0, import_react26.useEffect)(() => {
|
|
13590
14512
|
function handleKeyDown(event) {
|
|
13591
14513
|
var _a2, _b2, _c2;
|
|
13592
14514
|
const context = actionHandlerContext;
|
|
13593
14515
|
if (event.key === "Escape") {
|
|
13594
|
-
if (stateRef.current.connection.isActive)
|
|
13595
|
-
|
|
13596
|
-
if (stateRef.current.
|
|
13597
|
-
|
|
14516
|
+
if (stateRef.current.connection.isActive)
|
|
14517
|
+
userActionHandlers.handleCancelConnection(context);
|
|
14518
|
+
if (stateRef.current.relink.isActive)
|
|
14519
|
+
userActionHandlers.handleCancelRelink(context);
|
|
14520
|
+
if (stateRef.current.creation.isActive)
|
|
14521
|
+
userActionHandlers.handleCancelCreation(context);
|
|
14522
|
+
if ((_a2 = stateRef.current.versionMode) == null ? void 0 : _a2.isActive)
|
|
14523
|
+
userActionHandlers.handleCancelVersioning(context);
|
|
13598
14524
|
if (stateRef.current.ancestry.isActive) {
|
|
13599
|
-
setAncestryMode({
|
|
14525
|
+
setAncestryMode({
|
|
14526
|
+
isActive: false,
|
|
14527
|
+
tree: null,
|
|
14528
|
+
selectedParentId: null,
|
|
14529
|
+
isEditMode: false,
|
|
14530
|
+
currentAncestryId: null,
|
|
14531
|
+
ancestryName: "",
|
|
14532
|
+
ancestryDescription: "",
|
|
14533
|
+
ancestryDescriptionSections: [],
|
|
14534
|
+
isAddingNodes: false
|
|
14535
|
+
});
|
|
13600
14536
|
if (mountRef.current) mountRef.current.style.cursor = "grab";
|
|
13601
14537
|
}
|
|
13602
14538
|
if (questMode.isActive) {
|
|
@@ -13605,7 +14541,9 @@ function XViewScene({
|
|
|
13605
14541
|
if (stateRef.current.selectedNodes.size > 0) {
|
|
13606
14542
|
stateRef.current.selectedNodes.clear();
|
|
13607
14543
|
}
|
|
13608
|
-
setContextMenu(
|
|
14544
|
+
setContextMenu(
|
|
14545
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
14546
|
+
);
|
|
13609
14547
|
setMultiContextMenu((prev) => ({ ...prev, visible: false }));
|
|
13610
14548
|
setRelationshipMenu((prev) => ({ ...prev, visible: false }));
|
|
13611
14549
|
}
|
|
@@ -13624,7 +14562,9 @@ function XViewScene({
|
|
|
13624
14562
|
let attempts = 0;
|
|
13625
14563
|
const MIN_CLEARANCE = 15;
|
|
13626
14564
|
while (isOccupied && attempts < 30) {
|
|
13627
|
-
isOccupied = existingNodes.some(
|
|
14565
|
+
isOccupied = existingNodes.some(
|
|
14566
|
+
(mesh) => mesh.position.distanceTo(ghostPosition) < MIN_CLEARANCE
|
|
14567
|
+
);
|
|
13628
14568
|
if (isOccupied) {
|
|
13629
14569
|
ghostPosition.x = controls.target.x + Math.cos(angle) * radius;
|
|
13630
14570
|
ghostPosition.y = controls.target.y + (Math.random() - 0.5) * 8;
|
|
@@ -13643,7 +14583,11 @@ function XViewScene({
|
|
|
13643
14583
|
intensity: 0,
|
|
13644
14584
|
type: ["quest"]
|
|
13645
14585
|
};
|
|
13646
|
-
const ghostNode = createNodeMesh(
|
|
14586
|
+
const ghostNode = createNodeMesh(
|
|
14587
|
+
ghostData,
|
|
14588
|
+
ghostPosition,
|
|
14589
|
+
glowTexture
|
|
14590
|
+
);
|
|
13647
14591
|
ghostNode.traverse((child) => {
|
|
13648
14592
|
if (child.isMesh) {
|
|
13649
14593
|
child.material.transparent = true;
|
|
@@ -13690,13 +14634,49 @@ function XViewScene({
|
|
|
13690
14634
|
return /* @__PURE__ */ import_react26.default.createElement(LoadingScreen, null);
|
|
13691
14635
|
}
|
|
13692
14636
|
if (permissionStatus === "denied") {
|
|
13693
|
-
return /* @__PURE__ */ import_react26.default.createElement("div", { className: "flex flex-col items-center justify-center min-h-screen w-full bg-slate-950 text-white" }, /* @__PURE__ */ import_react26.default.createElement("div", { className: "bg-slate-900/50 p-8 rounded-2xl border border-slate-800 shadow-2xl text-center max-w-md" }, /* @__PURE__ */ import_react26.default.createElement("div", { className: "mb-4 text-red-500" }, /* @__PURE__ */ import_react26.default.createElement(
|
|
14637
|
+
return /* @__PURE__ */ import_react26.default.createElement("div", { className: "flex flex-col items-center justify-center min-h-screen w-full bg-slate-950 text-white" }, /* @__PURE__ */ import_react26.default.createElement("div", { className: "bg-slate-900/50 p-8 rounded-2xl border border-slate-800 shadow-2xl text-center max-w-md" }, /* @__PURE__ */ import_react26.default.createElement("div", { className: "mb-4 text-red-500" }, /* @__PURE__ */ import_react26.default.createElement(
|
|
14638
|
+
"svg",
|
|
14639
|
+
{
|
|
14640
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
14641
|
+
fill: "none",
|
|
14642
|
+
viewBox: "0 0 24 24",
|
|
14643
|
+
strokeWidth: 1.5,
|
|
14644
|
+
stroke: "currentColor",
|
|
14645
|
+
className: "w-16 h-16 mx-auto"
|
|
14646
|
+
},
|
|
14647
|
+
/* @__PURE__ */ import_react26.default.createElement(
|
|
14648
|
+
"path",
|
|
14649
|
+
{
|
|
14650
|
+
strokeLinecap: "round",
|
|
14651
|
+
strokeLinejoin: "round",
|
|
14652
|
+
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"
|
|
14653
|
+
}
|
|
14654
|
+
)
|
|
14655
|
+
)), /* @__PURE__ */ import_react26.default.createElement("h2", { className: "text-2xl font-bold mb-2" }, "Acesso Negado"), /* @__PURE__ */ import_react26.default.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__ */ import_react26.default.createElement(
|
|
13694
14656
|
"button",
|
|
13695
14657
|
{
|
|
13696
14658
|
onClick: () => router.push("/dashboard/scenes"),
|
|
13697
14659
|
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
14660
|
},
|
|
13699
|
-
/* @__PURE__ */ import_react26.default.createElement(
|
|
14661
|
+
/* @__PURE__ */ import_react26.default.createElement(
|
|
14662
|
+
"svg",
|
|
14663
|
+
{
|
|
14664
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
14665
|
+
fill: "none",
|
|
14666
|
+
viewBox: "0 0 24 24",
|
|
14667
|
+
strokeWidth: 2,
|
|
14668
|
+
stroke: "currentColor",
|
|
14669
|
+
className: "w-5 h-5"
|
|
14670
|
+
},
|
|
14671
|
+
/* @__PURE__ */ import_react26.default.createElement(
|
|
14672
|
+
"path",
|
|
14673
|
+
{
|
|
14674
|
+
strokeLinecap: "round",
|
|
14675
|
+
strokeLinejoin: "round",
|
|
14676
|
+
d: "M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"
|
|
14677
|
+
}
|
|
14678
|
+
)
|
|
14679
|
+
),
|
|
13700
14680
|
"Voltar para Scenes"
|
|
13701
14681
|
)));
|
|
13702
14682
|
}
|
|
@@ -13743,7 +14723,14 @@ function XViewScene({
|
|
|
13743
14723
|
onImageChange: handleGhostNodeImageChange,
|
|
13744
14724
|
onOpenImageViewer: handleOpenImageViewer,
|
|
13745
14725
|
onMentionClick: handleAddExistingNode,
|
|
13746
|
-
style: {
|
|
14726
|
+
style: {
|
|
14727
|
+
position: "absolute",
|
|
14728
|
+
left: `${formPosition.left}px`,
|
|
14729
|
+
top: `${formPosition.top}px`,
|
|
14730
|
+
opacity: formPosition.opacity,
|
|
14731
|
+
zIndex: 20,
|
|
14732
|
+
transition: "opacity 200ms ease-out"
|
|
14733
|
+
},
|
|
13747
14734
|
refEl: formRef,
|
|
13748
14735
|
existingTypes: existingNodeTypes,
|
|
13749
14736
|
initialColor: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.color,
|
|
@@ -13767,7 +14754,14 @@ function XViewScene({
|
|
|
13767
14754
|
onImageChange: handleGhostNodeImageChange,
|
|
13768
14755
|
onOpenImageViewer: handleOpenImageViewer,
|
|
13769
14756
|
onMentionClick: handleAddExistingNode,
|
|
13770
|
-
style: {
|
|
14757
|
+
style: {
|
|
14758
|
+
position: "absolute",
|
|
14759
|
+
left: `${formPosition.left}px`,
|
|
14760
|
+
top: `${formPosition.top}px`,
|
|
14761
|
+
opacity: formPosition.opacity,
|
|
14762
|
+
zIndex: 20,
|
|
14763
|
+
transition: "opacity 200ms ease-out"
|
|
14764
|
+
},
|
|
13771
14765
|
refEl: formRef,
|
|
13772
14766
|
fixedType: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.type,
|
|
13773
14767
|
fixedColor: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.color,
|
|
@@ -13784,7 +14778,13 @@ function XViewScene({
|
|
|
13784
14778
|
onNameChange: handleGhostNodeNameChange,
|
|
13785
14779
|
onColorChange: handleGhostNodeColorChange,
|
|
13786
14780
|
onSizeChange: handleGhostNodeSizeChange,
|
|
13787
|
-
style: {
|
|
14781
|
+
style: {
|
|
14782
|
+
position: "absolute",
|
|
14783
|
+
left: `16px`,
|
|
14784
|
+
top: `16px`,
|
|
14785
|
+
zIndex: 20,
|
|
14786
|
+
transition: "opacity 200ms ease-out"
|
|
14787
|
+
},
|
|
13788
14788
|
refEl: formRef,
|
|
13789
14789
|
onOpenImageViewer: handleOpenImageViewer,
|
|
13790
14790
|
onMentionClick: handleAddExistingNode,
|
|
@@ -13799,7 +14799,14 @@ function XViewScene({
|
|
|
13799
14799
|
"div",
|
|
13800
14800
|
{
|
|
13801
14801
|
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: {
|
|
14802
|
+
style: {
|
|
14803
|
+
top: 16,
|
|
14804
|
+
right: 16,
|
|
14805
|
+
zIndex: 1100,
|
|
14806
|
+
maxHeight: "calc(100vh - 32px)",
|
|
14807
|
+
width: `${readModeWidth}px`,
|
|
14808
|
+
maxWidth: "92vw"
|
|
14809
|
+
}
|
|
13803
14810
|
},
|
|
13804
14811
|
/* @__PURE__ */ import_react26.default.createElement(
|
|
13805
14812
|
"div",
|
|
@@ -13857,7 +14864,16 @@ function XViewScene({
|
|
|
13857
14864
|
onSave: handleSaveAncestry,
|
|
13858
14865
|
onEditRelationship: handleOpenAncestryRelEditor,
|
|
13859
14866
|
onClose: () => {
|
|
13860
|
-
setAncestryMode({
|
|
14867
|
+
setAncestryMode({
|
|
14868
|
+
isActive: false,
|
|
14869
|
+
tree: null,
|
|
14870
|
+
selectedParentId: null,
|
|
14871
|
+
isEditMode: false,
|
|
14872
|
+
currentAncestryId: null,
|
|
14873
|
+
ancestryName: "",
|
|
14874
|
+
ancestryDescription: "",
|
|
14875
|
+
isAddingNodes: false
|
|
14876
|
+
});
|
|
13861
14877
|
if (mountRef.current) mountRef.current.style.cursor = "grab";
|
|
13862
14878
|
},
|
|
13863
14879
|
availableNodes: allAvailableNodes,
|
|
@@ -13922,7 +14938,12 @@ function XViewScene({
|
|
|
13922
14938
|
onOpenImageViewer: handleOpenImageViewer,
|
|
13923
14939
|
existingTypes: existingNodeTypes,
|
|
13924
14940
|
onImageChange: (useImage, url, currentColorOverride) => {
|
|
13925
|
-
const updatedNode = {
|
|
14941
|
+
const updatedNode = {
|
|
14942
|
+
...detailsNode,
|
|
14943
|
+
useImageAsTexture: useImage,
|
|
14944
|
+
textureImageUrl: url,
|
|
14945
|
+
color: currentColorOverride || detailsNode.color
|
|
14946
|
+
};
|
|
13926
14947
|
updateExistingNodeVisuals(stateRef.current, updatedNode);
|
|
13927
14948
|
setDetailsNode(updatedNode);
|
|
13928
14949
|
},
|
|
@@ -13996,7 +15017,9 @@ function XViewScene({
|
|
|
13996
15017
|
parentData: parentDataRef.current,
|
|
13997
15018
|
sceneData: sceneDataRef.current,
|
|
13998
15019
|
ancestryData: ancestryDataRef.current,
|
|
13999
|
-
onClose: () => setContextMenu(
|
|
15020
|
+
onClose: () => setContextMenu(
|
|
15021
|
+
(prev) => prev.visible ? { ...prev, visible: false } : prev
|
|
15022
|
+
),
|
|
14000
15023
|
onStartCreation: (data) => userActionHandlers.handleStartCreation(actionHandlerContext, data),
|
|
14001
15024
|
onStartConnection: (data) => userActionHandlers.handleStartConnection(actionHandlerContext, data),
|
|
14002
15025
|
onStartVersioning: handleStartVersioning,
|
|
@@ -14004,7 +15027,11 @@ function XViewScene({
|
|
|
14004
15027
|
onDeleteNode: (data) => userActionHandlers.handleDeleteNode(actionHandlerContext, data),
|
|
14005
15028
|
onDismissNode: (data) => userActionHandlers.handleDismissNode(actionHandlerContext, data),
|
|
14006
15029
|
onDismissOtherNodes: (data) => userActionHandlers.handleDismissOtherNodes(actionHandlerContext, data),
|
|
14007
|
-
onExpandConnections: (sourceNode, links) => userActionHandlers.handleExpandConnections(
|
|
15030
|
+
onExpandConnections: (sourceNode, links) => userActionHandlers.handleExpandConnections(
|
|
15031
|
+
actionHandlerContext,
|
|
15032
|
+
sourceNode,
|
|
15033
|
+
links
|
|
15034
|
+
),
|
|
14008
15035
|
onRenderAncestry: handleStartReadingAncestry,
|
|
14009
15036
|
onEditAncestry: handleEditAncestry,
|
|
14010
15037
|
onDeleteAncestry: (ancestryId) => handleDeleteAncestry(ancestryId),
|
|
@@ -14017,9 +15044,18 @@ function XViewScene({
|
|
|
14017
15044
|
data: multiContextMenu,
|
|
14018
15045
|
userRole: userPermissionRole,
|
|
14019
15046
|
onClose: () => setMultiContextMenu((prev) => ({ ...prev, visible: false })),
|
|
14020
|
-
onDismissNodes: (ids) => userActionHandlers.handleDismissMultipleNodes(
|
|
14021
|
-
|
|
14022
|
-
|
|
15047
|
+
onDismissNodes: (ids) => userActionHandlers.handleDismissMultipleNodes(
|
|
15048
|
+
actionHandlerContext,
|
|
15049
|
+
ids
|
|
15050
|
+
),
|
|
15051
|
+
onDismissOtherNodes: (ids) => userActionHandlers.handleDismissOtherMultipleNodes(
|
|
15052
|
+
actionHandlerContext,
|
|
15053
|
+
ids
|
|
15054
|
+
),
|
|
15055
|
+
onDeleteNodes: (ids) => userActionHandlers.handleDeleteMultipleNodes(
|
|
15056
|
+
actionHandlerContext,
|
|
15057
|
+
ids
|
|
15058
|
+
)
|
|
14023
15059
|
}
|
|
14024
15060
|
),
|
|
14025
15061
|
/* @__PURE__ */ import_react26.default.createElement(
|
|
@@ -14040,7 +15076,13 @@ function XViewScene({
|
|
|
14040
15076
|
onDelete: (data) => userActionHandlers.handleDeleteLink(actionHandlerContext, data)
|
|
14041
15077
|
}
|
|
14042
15078
|
),
|
|
14043
|
-
/* @__PURE__ */ import_react26.default.createElement(
|
|
15079
|
+
/* @__PURE__ */ import_react26.default.createElement(
|
|
15080
|
+
ImageViewer,
|
|
15081
|
+
{
|
|
15082
|
+
data: imageViewer,
|
|
15083
|
+
onClose: () => setImageViewer({ ...imageViewer, visible: false })
|
|
15084
|
+
}
|
|
15085
|
+
),
|
|
14044
15086
|
/* @__PURE__ */ import_react26.default.createElement(
|
|
14045
15087
|
AncestryBoard,
|
|
14046
15088
|
{
|
|
@@ -14066,6 +15108,83 @@ function XViewScene({
|
|
|
14066
15108
|
currentViewName: viewParams == null ? void 0 : viewParams.name,
|
|
14067
15109
|
currentAncestries: ancestryDataRef.current || []
|
|
14068
15110
|
}
|
|
15111
|
+
),
|
|
15112
|
+
invalidTargetError && /* @__PURE__ */ import_react26.default.createElement(
|
|
15113
|
+
"div",
|
|
15114
|
+
{
|
|
15115
|
+
className: "ui-overlay",
|
|
15116
|
+
style: {
|
|
15117
|
+
position: "fixed",
|
|
15118
|
+
top: "24px",
|
|
15119
|
+
left: "50%",
|
|
15120
|
+
transform: "translateX(-50%)",
|
|
15121
|
+
zIndex: 1e4,
|
|
15122
|
+
padding: "16px 24px",
|
|
15123
|
+
background: "rgba(30, 20, 20, 0.85)",
|
|
15124
|
+
backdropFilter: "blur(12px)",
|
|
15125
|
+
WebkitBackdropFilter: "blur(12px)",
|
|
15126
|
+
border: "1px solid rgba(255, 70, 70, 0.35)",
|
|
15127
|
+
borderRadius: "16px",
|
|
15128
|
+
boxShadow: "0 12px 40px rgba(0,0,0,0.5), 0 0 30px rgba(255, 50, 50, 0.1)",
|
|
15129
|
+
color: "#ffa0a0",
|
|
15130
|
+
display: "flex",
|
|
15131
|
+
alignItems: "center",
|
|
15132
|
+
gap: "16px",
|
|
15133
|
+
fontFamily: "Inter, sans-serif",
|
|
15134
|
+
animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
|
|
15135
|
+
}
|
|
15136
|
+
},
|
|
15137
|
+
/* @__PURE__ */ import_react26.default.createElement("style", null, `
|
|
15138
|
+
@keyframes fadeInDown {
|
|
15139
|
+
from { opacity: 0; transform: translate(-50%, -20px); }
|
|
15140
|
+
to { opacity: 1; transform: translate(-50%, 0); }
|
|
15141
|
+
}
|
|
15142
|
+
`),
|
|
15143
|
+
/* @__PURE__ */ import_react26.default.createElement(
|
|
15144
|
+
"svg",
|
|
15145
|
+
{
|
|
15146
|
+
width: "20",
|
|
15147
|
+
height: "20",
|
|
15148
|
+
viewBox: "0 0 24 24",
|
|
15149
|
+
fill: "none",
|
|
15150
|
+
stroke: "currentColor",
|
|
15151
|
+
strokeWidth: "2",
|
|
15152
|
+
strokeLinecap: "round",
|
|
15153
|
+
strokeLinejoin: "round"
|
|
15154
|
+
},
|
|
15155
|
+
/* @__PURE__ */ import_react26.default.createElement("circle", { cx: "12", cy: "12", r: "10" }),
|
|
15156
|
+
/* @__PURE__ */ import_react26.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
|
|
15157
|
+
/* @__PURE__ */ import_react26.default.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
|
|
15158
|
+
),
|
|
15159
|
+
/* @__PURE__ */ import_react26.default.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
|
|
15160
|
+
/* @__PURE__ */ import_react26.default.createElement(
|
|
15161
|
+
"button",
|
|
15162
|
+
{
|
|
15163
|
+
onClick: () => setInvalidTargetError(null),
|
|
15164
|
+
style: {
|
|
15165
|
+
background: "rgba(255, 255, 255, 0.1)",
|
|
15166
|
+
border: "none",
|
|
15167
|
+
color: "white",
|
|
15168
|
+
padding: "4px 10px",
|
|
15169
|
+
borderRadius: "8px",
|
|
15170
|
+
cursor: "pointer",
|
|
15171
|
+
fontSize: "12px",
|
|
15172
|
+
fontWeight: 600,
|
|
15173
|
+
transition: "all 0.2s",
|
|
15174
|
+
marginLeft: "8px",
|
|
15175
|
+
border: "1px solid rgba(255,255,255,0.1)"
|
|
15176
|
+
},
|
|
15177
|
+
onMouseOver: (e) => {
|
|
15178
|
+
e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
|
|
15179
|
+
e.currentTarget.style.transform = "translateY(-1px)";
|
|
15180
|
+
},
|
|
15181
|
+
onMouseOut: (e) => {
|
|
15182
|
+
e.currentTarget.style.background = "rgba(255, 255, 255, 0.1)";
|
|
15183
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
15184
|
+
}
|
|
15185
|
+
},
|
|
15186
|
+
"Fechar"
|
|
15187
|
+
)
|
|
14069
15188
|
)
|
|
14070
15189
|
);
|
|
14071
15190
|
}
|