@lv-x-software-house/x_view 1.2.5-dev.1 → 1.2.5-dev.11

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.
Files changed (3) hide show
  1. package/dist/index.js +321 -57
  2. package/dist/index.mjs +321 -57
  3. package/package.json +1 -1
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 groupedConnections = commonConnections.reduce((acc, conn) => {
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
- const totalItems = availableAncestries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
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
- ))), finalRenderableConnections.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, availableAncestries.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
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 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: "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")), ability.can("update", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
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-full w-[min(92vw,320px)] z-40",
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-[28rem] overflow-y-auto rounded-lg bg-slate-900/95 border border-white/10 shadow-xl" }, filteredAndSorted.length > 0 ? filteredAndSorted.map((n) => {
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
- currentId = `link_${hoveredLink.userData.id}`;
3155
- if (hoveredLink.userData.isCurved) {
3156
- const positions = hoveredLink.geometry.attributes.position.array;
3157
- const midIndex = Math.floor(positions.length / 2 / 3) * 3;
3158
- positionTarget = new THREE2.Vector3(positions[midIndex], positions[midIndex + 1], positions[midIndex + 2]);
3159
- } else {
3160
- positionTarget = new THREE2.Vector3().addVectors(hoveredLink.userData.sourceNode.position, hoveredLink.userData.targetNode.position).multiplyScalar(0.5);
3161
- }
3162
- isLink = true;
3163
- if (tooltipEl.dataset.currentId !== currentId) {
3164
- content = hoveredLink.userData.isAncestryLink ? generateLinkTooltipHtml(hoveredLink.userData.relationship || {}, parentData, ancestryData) : generateLinkTooltipHtml(hoveredLink.userData, parentData, ancestryData);
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 newValue = [...p.value];
3474
- newValue[j] = { ...newValue[j], [f]: v };
3475
- return { ...p, value: newValue };
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 newValue = { type: newDateType };
3591
+ let newValue2 = { type: newDateType };
3530
3592
  if (newDateType === "Date Interval") {
3531
- newValue.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
3532
- newValue.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
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
- newValue.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
3596
+ newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
3535
3597
  }
3536
- handlePropChange("value", newValue);
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", null, /* @__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 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(
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 = applyDescriptionToTree(
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) => setAncestryName(e.target.value),
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 newValue = !useImageAsTexture;
7596
- setUseImageAsTexture(newValue);
7597
- if (newValue) {
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 newValue = !useImageAsTexture;
7896
- setUseImageAsTexture(newValue);
7897
- if (newValue) {
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", null, /* @__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(
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", null, /* @__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 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(
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", null, /* @__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 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(
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) => setName(e.target.value),
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.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(
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", null, /* @__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 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(
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),
@@ -10621,6 +10794,7 @@ function XViewScene({
10621
10794
  });
10622
10795
  const [isImportModalOpen, setIsImportModalOpen] = (0, import_react26.useState)(false);
10623
10796
  const [importSuccessMessage, setImportSuccessMessage] = (0, import_react26.useState)("");
10797
+ const [invalidTargetError, setInvalidTargetError] = (0, import_react26.useState)(null);
10624
10798
  const [highlightedNodeId, setHighlightedNodeId] = (0, import_react26.useState)(null);
10625
10799
  const [isAncestryBoardOpen, setIsAncestryBoardOpen] = (0, import_react26.useState)(false);
10626
10800
  const [ancestryBoardData, setAncestryBoardData] = (0, import_react26.useState)([]);
@@ -11282,6 +11456,7 @@ function XViewScene({
11282
11456
  boardPromise
11283
11457
  ]);
11284
11458
  if ((sceneResponse == null ? void 0 : sceneResponse.success) && ((_a2 = sceneResponse.data) == null ? void 0 : _a2.scene) && ((_b2 = sceneResponse.data) == null ? void 0 : _b2.parent)) {
11459
+ console.log("XViewScene - View Members:", sceneResponse.data.scene.members);
11285
11460
  if (focusNodeId) {
11286
11461
  let targetNode = sceneResponse.data.scene.nodes.find(
11287
11462
  (n) => String(n.id) === String(focusNodeId)
@@ -13522,7 +13697,7 @@ function XViewScene({
13522
13697
  sceneVersion
13523
13698
  ]);
13524
13699
  const handleStartReadingAncestry = (0, import_react26.useCallback)(
13525
- async (ancestryObject) => {
13700
+ async (ancestryObject, renderMode = "full") => {
13526
13701
  setContextMenu(
13527
13702
  (prev) => prev.visible ? { ...prev, visible: false } : prev
13528
13703
  );
@@ -13538,14 +13713,20 @@ function XViewScene({
13538
13713
  const hasDescription = ancestryObject.description && ancestryObject.description.trim() !== "";
13539
13714
  const hasMainTreeNodes = ancestryObject.tree.children && ancestryObject.tree.children.length > 0;
13540
13715
  const hasAbstractionNodes = ancestryObject.abstraction_tree && ancestryObject.abstraction_tree.children && ancestryObject.abstraction_tree.children.length > 0;
13541
- const shouldAutoRenderAbstraction = !hasDescription && !hasMainTreeNodes && hasAbstractionNodes;
13716
+ const isFull = renderMode === "full";
13717
+ const isAncestryOnly = renderMode === "ancestry_only";
13718
+ const isAbstractionOnly = renderMode === "abstraction_only";
13719
+ let shouldRenderAbstraction = isAbstractionOnly;
13720
+ if (isFull) {
13721
+ shouldRenderAbstraction = !hasDescription && !hasMainTreeNodes && hasAbstractionNodes;
13722
+ }
13542
13723
  setReadingMode({
13543
- isActive: hasDescription,
13724
+ isActive: isFull ? hasDescription : false,
13544
13725
  ancestry: ancestryObject,
13545
13726
  branchStack: [],
13546
- autoAbstraction: shouldAutoRenderAbstraction
13727
+ autoAbstraction: shouldRenderAbstraction
13547
13728
  });
13548
- if (shouldAutoRenderAbstraction) {
13729
+ if (shouldRenderAbstraction) {
13549
13730
  handleRenderAbstractionTree(ancestryObject, null);
13550
13731
  } else {
13551
13732
  const initialSections = /* @__PURE__ */ new Set(["preamble", 0, "0"]);
@@ -14291,6 +14472,9 @@ function XViewScene({
14291
14472
  }, 300);
14292
14473
  } else {
14293
14474
  setHasFocusedInitial(true);
14475
+ setInvalidTargetError(
14476
+ "O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
14477
+ );
14294
14478
  }
14295
14479
  }
14296
14480
  }, [
@@ -14313,6 +14497,9 @@ function XViewScene({
14313
14497
  }, 300);
14314
14498
  } else {
14315
14499
  setHasOpenedInitialAncestry(true);
14500
+ setInvalidTargetError(
14501
+ "O link aponta para uma ancestralidade que n\xE3o foi encontrada ou foi exclu\xEDda."
14502
+ );
14316
14503
  }
14317
14504
  }
14318
14505
  }, [
@@ -14922,6 +15109,83 @@ function XViewScene({
14922
15109
  currentViewName: viewParams == null ? void 0 : viewParams.name,
14923
15110
  currentAncestries: ancestryDataRef.current || []
14924
15111
  }
15112
+ ),
15113
+ invalidTargetError && /* @__PURE__ */ import_react26.default.createElement(
15114
+ "div",
15115
+ {
15116
+ className: "ui-overlay",
15117
+ style: {
15118
+ position: "fixed",
15119
+ top: "24px",
15120
+ left: "50%",
15121
+ transform: "translateX(-50%)",
15122
+ zIndex: 1e4,
15123
+ padding: "16px 24px",
15124
+ background: "rgba(30, 20, 20, 0.85)",
15125
+ backdropFilter: "blur(12px)",
15126
+ WebkitBackdropFilter: "blur(12px)",
15127
+ border: "1px solid rgba(255, 70, 70, 0.35)",
15128
+ borderRadius: "16px",
15129
+ boxShadow: "0 12px 40px rgba(0,0,0,0.5), 0 0 30px rgba(255, 50, 50, 0.1)",
15130
+ color: "#ffa0a0",
15131
+ display: "flex",
15132
+ alignItems: "center",
15133
+ gap: "16px",
15134
+ fontFamily: "Inter, sans-serif",
15135
+ animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
15136
+ }
15137
+ },
15138
+ /* @__PURE__ */ import_react26.default.createElement("style", null, `
15139
+ @keyframes fadeInDown {
15140
+ from { opacity: 0; transform: translate(-50%, -20px); }
15141
+ to { opacity: 1; transform: translate(-50%, 0); }
15142
+ }
15143
+ `),
15144
+ /* @__PURE__ */ import_react26.default.createElement(
15145
+ "svg",
15146
+ {
15147
+ width: "20",
15148
+ height: "20",
15149
+ viewBox: "0 0 24 24",
15150
+ fill: "none",
15151
+ stroke: "currentColor",
15152
+ strokeWidth: "2",
15153
+ strokeLinecap: "round",
15154
+ strokeLinejoin: "round"
15155
+ },
15156
+ /* @__PURE__ */ import_react26.default.createElement("circle", { cx: "12", cy: "12", r: "10" }),
15157
+ /* @__PURE__ */ import_react26.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
15158
+ /* @__PURE__ */ import_react26.default.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
15159
+ ),
15160
+ /* @__PURE__ */ import_react26.default.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
15161
+ /* @__PURE__ */ import_react26.default.createElement(
15162
+ "button",
15163
+ {
15164
+ onClick: () => setInvalidTargetError(null),
15165
+ style: {
15166
+ background: "rgba(255, 255, 255, 0.1)",
15167
+ border: "none",
15168
+ color: "white",
15169
+ padding: "4px 10px",
15170
+ borderRadius: "8px",
15171
+ cursor: "pointer",
15172
+ fontSize: "12px",
15173
+ fontWeight: 600,
15174
+ transition: "all 0.2s",
15175
+ marginLeft: "8px",
15176
+ border: "1px solid rgba(255,255,255,0.1)"
15177
+ },
15178
+ onMouseOver: (e) => {
15179
+ e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
15180
+ e.currentTarget.style.transform = "translateY(-1px)";
15181
+ },
15182
+ onMouseOut: (e) => {
15183
+ e.currentTarget.style.background = "rgba(255, 255, 255, 0.1)";
15184
+ e.currentTarget.style.transform = "translateY(0)";
15185
+ }
15186
+ },
15187
+ "Fechar"
15188
+ )
14925
15189
  )
14926
15190
  );
14927
15191
  }