@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.mjs CHANGED
@@ -111,6 +111,7 @@ function ContextMenu({
111
111
  const [menuView, setMenuView] = useState("main");
112
112
  const [selectedAncestry, setSelectedAncestry] = useState(null);
113
113
  const [versionSubMenu, setVersionSubMenu] = useState(null);
114
+ const [labelSubMenu, setLabelSubMenu] = useState(null);
114
115
  const [isLinkCopied, setIsLinkCopied] = useState(false);
115
116
  const [selectedQuestStatus, setSelectedQuestStatus] = useState(null);
116
117
  const ability = useMemo(() => defineAbilityFor(userRole), [userRole]);
@@ -119,6 +120,7 @@ function ContextMenu({
119
120
  setMenuView("main");
120
121
  setSelectedAncestry(null);
121
122
  setVersionSubMenu(null);
123
+ setLabelSubMenu(null);
122
124
  setSelectedQuestStatus(null);
123
125
  }
124
126
  }, [data.visible, (_a = data.nodeData) == null ? void 0 : _a.id]);
@@ -134,7 +136,7 @@ function ContextMenu({
134
136
  if (left + w + 8 > vw) left = Math.max(8, vw - w - 8);
135
137
  if (top + h + 8 > vh) top = Math.max(8, vh - h - 8);
136
138
  setMenuPos({ left, top });
137
- }, [data, menuView, versionSubMenu, selectedQuestStatus]);
139
+ }, [data, menuView, versionSubMenu, labelSubMenu, selectedQuestStatus]);
138
140
  useEffect(() => {
139
141
  if (!data.visible) return;
140
142
  const handleClickOutside = (e) => {
@@ -186,7 +188,21 @@ function ContextMenu({
186
188
  var _a2;
187
189
  return (_a2 = c.targetNode) == null ? void 0 : _a2.is_quest;
188
190
  });
189
- const groupedConnections = commonConnections.reduce((acc, conn) => {
191
+ const getLabelForConnection = (conn) => {
192
+ if (conn.direction === "outgoing") return conn.link.source_label || null;
193
+ if (conn.direction === "incoming") return conn.link.target_label || null;
194
+ return null;
195
+ };
196
+ const commonWithLabel = commonConnections.filter((c) => getLabelForConnection(c));
197
+ const commonWithoutLabel = commonConnections.filter((c) => !getLabelForConnection(c));
198
+ const labelGroups = commonWithLabel.reduce((acc, conn) => {
199
+ const label = getLabelForConnection(conn);
200
+ if (!acc[label]) acc[label] = [];
201
+ acc[label].push(conn);
202
+ return acc;
203
+ }, {});
204
+ const labelGroupEntries = Object.entries(labelGroups).sort(([a], [b]) => a.localeCompare(b));
205
+ const groupedConnections = commonWithoutLabel.reduce((acc, conn) => {
190
206
  var _a2;
191
207
  const { targetNode } = conn;
192
208
  const groupingKey = ((_a2 = targetNode.version_node) == null ? void 0 : _a2.is_version) ? targetNode.version_node.parent_node : targetNode.id;
@@ -314,11 +330,32 @@ function ContextMenu({
314
330
  /* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
315
331
  ))));
316
332
  };
333
+ const renderLabelSubMenuView = () => {
334
+ const group = labelSubMenu;
335
+ const isScrollable = group.connections.length > 10;
336
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React.createElement("button", { onClick: () => {
337
+ setLabelSubMenu(null);
338
+ setMenuView("connections");
339
+ }, className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ React.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-violet-300/80 truncate", title: group.label }, group.label))), group.connections.length > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => handleExpandAndClose(group.connections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, group.connections.map((conn) => /* @__PURE__ */ React.createElement(
340
+ "button",
341
+ {
342
+ key: conn.targetNode.id,
343
+ onClick: () => handleExpandAndClose([conn.link]),
344
+ className: baseButtonClass,
345
+ title: `Expandir conex\xE3o com ${conn.targetNode.name}`
346
+ },
347
+ /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }), /* @__PURE__ */ React.createElement("polyline", { points: "12 5 19 12 12 19" })),
348
+ /* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
349
+ ))));
350
+ };
317
351
  const renderConnectionsView = () => {
318
352
  if (versionSubMenu) {
319
353
  return renderVersionSubMenuView();
320
354
  }
321
- const totalItems = availableAncestries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
355
+ if (labelSubMenu) {
356
+ return renderLabelSubMenuView();
357
+ }
358
+ const totalItems = availableAncestries.length + labelGroupEntries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
322
359
  const isScrollable = totalItems > 10;
323
360
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("main"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Conex\xF5es")), commonConnections.length > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => handleExpandAndClose(commonConnections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ React.createElement("div", { className: `flex flex-col ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, /* @__PURE__ */ React.createElement("div", { className: "px-2 py-1 text-[11px] uppercase tracking-wider text-indigo-400/90" }, "Ancestralidades Salvas"), availableAncestries.map((anc) => /* @__PURE__ */ React.createElement(
324
361
  "button",
@@ -330,7 +367,20 @@ function ContextMenu({
330
367
  },
331
368
  /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 3v4a2 2 0 0 0 2 2h4" }), /* @__PURE__ */ React.createElement("path", { d: "M18 3v4a2 2 0 0 1-2 2h-4" }), /* @__PURE__ */ React.createElement("circle", { cx: "6", cy: "3", r: "2" }), /* @__PURE__ */ React.createElement("circle", { cx: "18", cy: "3", r: "2" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "11", r: "2" }), /* @__PURE__ */ React.createElement("path", { d: "M12 13v6" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "21", r: "2" })),
332
369
  /* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, anc.name || `Ancestralidade #${anc.ancestry_id.substring(0, 8)}`)
333
- ))), finalRenderableConnections.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, availableAncestries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
370
+ ))), labelGroupEntries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${availableAncestries.length > 0 ? "mt-2" : ""} ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), labelGroupEntries.map(([label, conns]) => /* @__PURE__ */ React.createElement(
371
+ "button",
372
+ {
373
+ key: label,
374
+ onClick: () => {
375
+ setLabelSubMenu({ label, connections: conns });
376
+ },
377
+ className: baseButtonClass,
378
+ title: `Ver grupo: ${label}`
379
+ },
380
+ /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400" }, /* @__PURE__ */ React.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })),
381
+ /* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, label),
382
+ /* @__PURE__ */ React.createElement("span", { className: "text-xs px-2 py-0.5 bg-violet-500/20 text-violet-300 rounded-full" }, conns.length)
383
+ ))), finalRenderableConnections.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, (availableAncestries.length > 0 || labelGroupEntries.length > 0) && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
334
384
  if (group.isVersionGroup) {
335
385
  return /* @__PURE__ */ React.createElement(
336
386
  "button",
@@ -408,11 +458,18 @@ function ContextMenu({
408
458
  ))));
409
459
  };
410
460
  const renderAncestryActionsView = () => {
461
+ var _a2, _b2;
411
462
  const ancestryTitle = (selectedAncestry == null ? void 0 : selectedAncestry.name) || `Ancestralidade #${selectedAncestry == null ? void 0 : selectedAncestry.ancestry_id.substring(0, 8)}`;
412
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400 truncate", title: ancestryTitle }, ancestryTitle))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, ability.can("read", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => {
413
- onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry);
463
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400 truncate", title: ancestryTitle }, ancestryTitle))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, ability.can("read", "Ancestry") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("button", { onClick: () => {
464
+ onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry, "full");
465
+ onClose();
466
+ }, className: baseButtonClass, title: "Renderizar Ancestralidade Completa" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ React.createElement("span", null, "Renderizar Ancestralidade")), /* @__PURE__ */ React.createElement("button", { onClick: () => {
467
+ onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry, "ancestry_only");
468
+ onClose();
469
+ }, className: baseButtonClass, title: "Renderizar apenas a \xC1rvore de Ancestralidade (Radial)" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M12 2v3m0 14v3m10-10h-3m-14 0H2m15.66-6.34-2.12 2.12m-9.08 9.08-2.12 2.12m13.32 0-2.12-2.12m-9.08-9.08-2.12-2.12" })), /* @__PURE__ */ React.createElement("span", null, "\xC1rvore de Ancestralidade")), ((_b2 = (_a2 = selectedAncestry == null ? void 0 : selectedAncestry.abstraction_tree) == null ? void 0 : _a2.children) == null ? void 0 : _b2.length) > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => {
470
+ onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry, "abstraction_only");
414
471
  onClose();
415
- }, className: baseButtonClass, title: "Renderizar Ancestralidade" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ React.createElement("span", null, "Renderizar Ancestralidade")), ability.can("update", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => {
472
+ }, className: baseButtonClass, title: "Renderizar apenas a \xC1rvore de Abstra\xE7\xE3o (Vertical)" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ React.createElement("rect", { x: "14", y: "3", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ React.createElement("rect", { x: "14", y: "14", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ React.createElement("rect", { x: "3", y: "14", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ React.createElement("path", { d: "M10 6.5h4" }), /* @__PURE__ */ React.createElement("path", { d: "M10 17.5h4" }), /* @__PURE__ */ React.createElement("path", { d: "M6.5 10v4" }), /* @__PURE__ */ React.createElement("path", { d: "M17.5 10v4" })), /* @__PURE__ */ React.createElement("span", null, "\xC1rvore de Abstra\xE7\xE3o"))), ability.can("update", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => {
416
473
  onEditAncestry == null ? void 0 : onEditAncestry(selectedAncestry);
417
474
  onClose();
418
475
  }, className: baseButtonClass, title: "Editar Ancestralidade" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M12 20h9" }), /* @__PURE__ */ React.createElement("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" })), /* @__PURE__ */ React.createElement("span", null, "Editar Ancestralidade")), (ability.can("update", "Ancestry") || ability.can("delete", "Ancestry")) && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), ability.can("delete", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => {
@@ -659,7 +716,7 @@ function XViewSidebar({
659
716
  "div",
660
717
  {
661
718
  ref: containerRef,
662
- className: "ui-overlay fixed left-0 top-0 h-full w-[min(92vw,320px)] z-40",
719
+ className: "ui-overlay fixed left-0 top-0 h-[100dvh] w-[min(92vw,320px)] z-40 overflow-hidden",
663
720
  onPointerDown: swallow,
664
721
  onPointerMove: swallow,
665
722
  onPointerUp: swallow,
@@ -668,7 +725,7 @@ function XViewSidebar({
668
725
  onContextMenu: swallow,
669
726
  onDoubleClick: swallow
670
727
  },
671
- /* @__PURE__ */ React2.createElement("div", { className: "h-full flex flex-col bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ React2.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React2.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ React2.createElement(
728
+ /* @__PURE__ */ React2.createElement("div", { className: "h-full flex flex-col overflow-hidden bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ React2.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React2.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ React2.createElement(
672
729
  "button",
673
730
  {
674
731
  className: "ml-auto p-2 rounded-md text-slate-400 hover:text-white hover:bg-white/10 transition-colors",
@@ -725,7 +782,7 @@ function XViewSidebar({
725
782
  autoComplete: "off"
726
783
  }
727
784
  )
728
- ), showList && /* @__PURE__ */ React2.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) => {
785
+ ), showList && /* @__PURE__ */ React2.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[min(448px,50dvh)] overflow-y-auto rounded-lg bg-slate-900/95 border border-white/10 shadow-xl" }, filteredAndSorted.length > 0 ? filteredAndSorted.map((n) => {
729
786
  const inView = isNodeInView(n.id);
730
787
  const active = selectedNodeId === n.id;
731
788
  const typeLabel = Array.isArray(n.type) ? n.type.join(", ") : n.type;
@@ -3101,6 +3158,7 @@ function calculateNodePositions(nodes) {
3101
3158
  return positions;
3102
3159
  }
3103
3160
  function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, isSceneBusy, parentData, ancestryData }) {
3161
+ var _a, _b;
3104
3162
  if (!tooltipEl || !camera || !mountEl) return;
3105
3163
  let content = "";
3106
3164
  let positionTarget = null;
@@ -3113,17 +3171,21 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3113
3171
  content = generateTooltipHtml(hoveredNode.userData, parentData, ancestryData);
3114
3172
  }
3115
3173
  } else if (hoveredLink && !isSceneBusy) {
3116
- currentId = `link_${hoveredLink.userData.id}`;
3117
- if (hoveredLink.userData.isCurved) {
3118
- const positions = hoveredLink.geometry.attributes.position.array;
3119
- const midIndex = Math.floor(positions.length / 2 / 3) * 3;
3120
- positionTarget = new THREE2.Vector3(positions[midIndex], positions[midIndex + 1], positions[midIndex + 2]);
3121
- } else {
3122
- positionTarget = new THREE2.Vector3().addVectors(hoveredLink.userData.sourceNode.position, hoveredLink.userData.targetNode.position).multiplyScalar(0.5);
3123
- }
3124
- isLink = true;
3125
- if (tooltipEl.dataset.currentId !== currentId) {
3126
- content = hoveredLink.userData.isAncestryLink ? generateLinkTooltipHtml(hoveredLink.userData.relationship || {}, parentData, ancestryData) : generateLinkTooltipHtml(hoveredLink.userData, parentData, ancestryData);
3174
+ const linkData = hoveredLink.userData.isAncestryLink ? hoveredLink.userData.relationship || {} : hoveredLink.userData;
3175
+ const hasContent = ((_a = linkData.name) == null ? void 0 : _a.trim()) || ((_b = linkData.description) == null ? void 0 : _b.trim());
3176
+ if (hasContent) {
3177
+ currentId = `link_${hoveredLink.userData.id}`;
3178
+ if (hoveredLink.userData.isCurved) {
3179
+ const positions = hoveredLink.geometry.attributes.position.array;
3180
+ const midIndex = Math.floor(positions.length / 2 / 3) * 3;
3181
+ positionTarget = new THREE2.Vector3(positions[midIndex], positions[midIndex + 1], positions[midIndex + 2]);
3182
+ } else {
3183
+ positionTarget = new THREE2.Vector3().addVectors(hoveredLink.userData.sourceNode.position, hoveredLink.userData.targetNode.position).multiplyScalar(0.5);
3184
+ }
3185
+ isLink = true;
3186
+ if (tooltipEl.dataset.currentId !== currentId) {
3187
+ content = generateLinkTooltipHtml(linkData, parentData, ancestryData);
3188
+ }
3127
3189
  }
3128
3190
  }
3129
3191
  if (positionTarget) {
@@ -3432,9 +3494,9 @@ function CustomPropertyDisplay({
3432
3494
  };
3433
3495
  const handleRemoveListItem = (j) => setTempProp((p) => ({ ...p, value: p.value.filter((_, k) => k !== j) }));
3434
3496
  const handleListItemChange = (j, f, v) => setTempProp((p) => {
3435
- const newValue = [...p.value];
3436
- newValue[j] = { ...newValue[j], [f]: v };
3437
- return { ...p, value: newValue };
3497
+ const newValue2 = [...p.value];
3498
+ newValue2[j] = { ...newValue2[j], [f]: v };
3499
+ return { ...p, value: newValue2 };
3438
3500
  });
3439
3501
  const baseInput = "w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60 transition-all duration-150";
3440
3502
  const renderEditView = () => {
@@ -3488,14 +3550,14 @@ function CustomPropertyDisplay({
3488
3550
  const inputClass = `${baseInput} ${noSpinnerClass}`;
3489
3551
  const handleDateTypeChange = (newDateType) => {
3490
3552
  var _a3, _b2, _c2;
3491
- let newValue = { type: newDateType };
3553
+ let newValue2 = { type: newDateType };
3492
3554
  if (newDateType === "Date Interval") {
3493
- newValue.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
3494
- newValue.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
3555
+ newValue2.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
3556
+ newValue2.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
3495
3557
  } else {
3496
- newValue.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
3558
+ newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
3497
3559
  }
3498
- handlePropChange("value", newValue);
3560
+ handlePropChange("value", newValue2);
3499
3561
  };
3500
3562
  const handleDateValueChange = (dateField, dateValue) => {
3501
3563
  handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
@@ -4343,7 +4405,7 @@ ${space}${bullet} `);
4343
4405
  }
4344
4406
  ),
4345
4407
  /* @__PURE__ */ React5.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0 shrink-0" }),
4346
- /* @__PURE__ */ React5.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React5.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80" }), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-slate-200" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ React5.createElement("button", { onClick: handleClose, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")),
4408
+ /* @__PURE__ */ React5.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React5.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shrink-0" }), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-slate-200 truncate" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ React5.createElement("button", { onClick: handleClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")),
4347
4409
  /* @__PURE__ */ React5.createElement("div", { className: "px-6 py-3 flex flex-col gap-3 border-b border-white/5 bg-white/5 shrink-0" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 flex-wrap" }, /* @__PURE__ */ React5.createElement(
4348
4410
  "button",
4349
4411
  {
@@ -5472,7 +5534,7 @@ function AncestryRelationshipPanel({
5472
5534
  onImageClick: handleImageClickFromText,
5473
5535
  onSaveDescription: handleSaveDescriptionInline
5474
5536
  }
5475
- ) : /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React8.createElement("div", null, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-cyan-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ React8.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o de Ancestralidade")), /* @__PURE__ */ React8.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Editar Rela\xE7\xE3o")), /* @__PURE__ */ React8.createElement("button", { onClick: onClose, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pb-4 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ React8.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React8.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o da Rela\xE7\xE3o"), /* @__PURE__ */ React8.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React8.createElement(
5537
+ ) : /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React8.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-cyan-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ React8.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o de Ancestralidade")), /* @__PURE__ */ React8.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Editar Rela\xE7\xE3o")), /* @__PURE__ */ React8.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pb-4 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ React8.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React8.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o da Rela\xE7\xE3o"), /* @__PURE__ */ React8.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React8.createElement(
5476
5538
  DescriptionDisplay,
5477
5539
  {
5478
5540
  description,
@@ -5794,6 +5856,7 @@ function CreateAncestryPanel({
5794
5856
  } = ancestryMode;
5795
5857
  const [isSaving, setIsSaving] = useState11(false);
5796
5858
  const [isLinkCopied, setIsLinkCopied] = useState11(false);
5859
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState11(false);
5797
5860
  const [showDeleteBranchConfirm, setShowDeleteBranchConfirm] = useState11(false);
5798
5861
  const handleCopyLink = (e) => {
5799
5862
  e.stopPropagation();
@@ -5861,12 +5924,14 @@ function CreateAncestryPanel({
5861
5924
  };
5862
5925
  const handleSelectAncestryParent = (nodeId, isAbstraction = false) => {
5863
5926
  setAncestryMode((prev) => isAbstraction ? { ...prev, selectedAbstractionParentId: nodeId } : { ...prev, selectedParentId: nodeId });
5927
+ setHasUnsavedChanges(true);
5864
5928
  };
5865
5929
  const handleToggleAddMode = (isAbstraction = false) => {
5866
5930
  if (isAbstraction && !ancestryMode.isAddingAbstractionNodes) {
5867
5931
  setTargetRenderNodeId(null);
5868
5932
  }
5869
5933
  setAncestryMode((prev) => isAbstraction ? { ...prev, isAddingAbstractionNodes: !prev.isAddingAbstractionNodes } : { ...prev, isAddingNodes: !prev.isAddingNodes });
5934
+ setHasUnsavedChanges(true);
5870
5935
  };
5871
5936
  const handleRemoveNode = useCallback2((pathToRemove, isAbstraction = false) => {
5872
5937
  if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) return;
@@ -5884,6 +5949,7 @@ function CreateAncestryPanel({
5884
5949
  const indexToRemove = pathToRemove[pathToRemove.length - 1];
5885
5950
  if (currentParent.children && currentParent.children.length > indexToRemove) {
5886
5951
  currentParent.children.splice(indexToRemove, 1);
5952
+ setHasUnsavedChanges(true);
5887
5953
  }
5888
5954
  return { ...prev, [treeKey]: newTree };
5889
5955
  });
@@ -5942,6 +6008,7 @@ function CreateAncestryPanel({
5942
6008
  updateGlobalTree(rootTreeClone);
5943
6009
  }
5944
6010
  setAncestryMode((prev) => ({ ...prev, [treeKey]: rootTreeClone }));
6011
+ setHasUnsavedChanges(true);
5945
6012
  } else {
5946
6013
  alert("N\xE3o \xE9 poss\xEDvel mover um node para dentro de seus pr\xF3prios descendentes.");
5947
6014
  }
@@ -6014,6 +6081,7 @@ function CreateAncestryPanel({
6014
6081
  const handleAddProp = () => {
6015
6082
  const newProp = createNewCustomProperty(customProps);
6016
6083
  setCustomProps((p) => [...p, newProp]);
6084
+ setHasUnsavedChanges(true);
6017
6085
  setTimeout(() => {
6018
6086
  var _a;
6019
6087
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -6022,11 +6090,13 @@ function CreateAncestryPanel({
6022
6090
  const handleRemoveProp = (i) => {
6023
6091
  const newProps = customProps.filter((_, idx) => idx !== i);
6024
6092
  setCustomProps(newProps);
6093
+ setHasUnsavedChanges(true);
6025
6094
  };
6026
6095
  const handleUpdateProp = (index, updatedProp) => {
6027
6096
  const newProps = [...customProps];
6028
6097
  newProps[index] = updatedProp;
6029
6098
  setCustomProps(newProps);
6099
+ setHasUnsavedChanges(true);
6030
6100
  };
6031
6101
  const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
6032
6102
  useEffect10(() => {
@@ -6136,6 +6206,7 @@ function CreateAncestryPanel({
6136
6206
  updateGlobalTree(rootTreeClone);
6137
6207
  setBranchStack([...branchStack]);
6138
6208
  setIsPickerOpen(false);
6209
+ setHasUnsavedChanges(true);
6139
6210
  try {
6140
6211
  setIsSaving(true);
6141
6212
  const rootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6149,6 +6220,7 @@ function CreateAncestryPanel({
6149
6220
  rootExtras
6150
6221
  );
6151
6222
  setLastSavedSnapshot(takeSnapshot(rootTreeClone, ancestryName, description, processedSections, [], isPrivate, ancestryMode.abstraction_tree));
6223
+ setHasUnsavedChanges(false);
6152
6224
  if (onRenderFullAncestry) {
6153
6225
  const fullTreePayload = {
6154
6226
  ancestry_id: ancestryMode.currentAncestryId || "temp_root",
@@ -6191,6 +6263,7 @@ function CreateAncestryPanel({
6191
6263
  if (branchIndex !== -1) {
6192
6264
  foundParentPath.node.parallel_branches.splice(branchIndex, 1);
6193
6265
  updateGlobalTree(rootTreeClone);
6266
+ setHasUnsavedChanges(true);
6194
6267
  try {
6195
6268
  setIsSaving(true);
6196
6269
  const currentRootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6212,6 +6285,7 @@ function CreateAncestryPanel({
6212
6285
  isPrivate,
6213
6286
  ancestryMode.abstraction_tree
6214
6287
  ));
6288
+ setHasUnsavedChanges(false);
6215
6289
  if (onClearAncestryVisuals) {
6216
6290
  onClearAncestryVisuals(currentStep.branchId);
6217
6291
  }
@@ -6244,6 +6318,7 @@ function CreateAncestryPanel({
6244
6318
  if (branchIndex !== -1) {
6245
6319
  foundParentPath.node.parallel_branches.splice(branchIndex, 1);
6246
6320
  updateGlobalTree(rootTreeClone);
6321
+ setHasUnsavedChanges(true);
6247
6322
  try {
6248
6323
  setIsSaving(true);
6249
6324
  const currentRootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6265,6 +6340,7 @@ function CreateAncestryPanel({
6265
6340
  isPrivate,
6266
6341
  ancestryMode.abstraction_tree
6267
6342
  ));
6343
+ setHasUnsavedChanges(false);
6268
6344
  if (onClearAncestryVisuals) {
6269
6345
  onClearAncestryVisuals(currentStep.branchId);
6270
6346
  }
@@ -6526,6 +6602,7 @@ function CreateAncestryPanel({
6526
6602
  }
6527
6603
  setBranchStack(parentStack);
6528
6604
  setTargetScrollSectionId(targetFocusId);
6605
+ setHasUnsavedChanges(true);
6529
6606
  if (onRenderFullAncestry) {
6530
6607
  const parentStack2 = currentStack;
6531
6608
  const rotation = parentStack2.reduce((acc, step) => {
@@ -6587,7 +6664,6 @@ function CreateAncestryPanel({
6587
6664
  direction,
6588
6665
  tree: {
6589
6666
  node: nodeData,
6590
- node_id: nodeId,
6591
6667
  children: [],
6592
6668
  relationship: {}
6593
6669
  }
@@ -6609,6 +6685,7 @@ function CreateAncestryPanel({
6609
6685
  savedMaxIndex: parentIndexToSave,
6610
6686
  entryDirection: direction
6611
6687
  }]);
6688
+ setHasUnsavedChanges(true);
6612
6689
  if (branch && branch.tree && onRenderFullAncestry) {
6613
6690
  const branchAncestryObj = {
6614
6691
  ancestry_id: branch.id,
@@ -6659,6 +6736,10 @@ function CreateAncestryPanel({
6659
6736
  const currentInputName = overrides.ancestryName !== void 0 ? overrides.ancestryName : ancestryName;
6660
6737
  const currentInputDesc = overrides.description !== void 0 ? overrides.description : description;
6661
6738
  const currentInputSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
6739
+ if (!keepOpen && !hasUnsavedChanges) {
6740
+ onClose();
6741
+ return;
6742
+ }
6662
6743
  if (!currentInputName.trim()) {
6663
6744
  alert("O nome n\xE3o pode estar vazio.");
6664
6745
  return;
@@ -6666,11 +6747,7 @@ function CreateAncestryPanel({
6666
6747
  setIsSaving(true);
6667
6748
  const processedSections = processDescriptionForSave(currentInputDesc, currentInputSections);
6668
6749
  setExistingSections(processedSections);
6669
- const updatedRootTree = applyDescriptionToTree(
6670
- ancestryMode.tree,
6671
- currentInputDesc,
6672
- processedSections
6673
- );
6750
+ const updatedRootTree = JSON.parse(JSON.stringify(ancestryMode.tree));
6674
6751
  const extrasObj = {
6675
6752
  ...toObjectFromCustomProps(customProps.filter((p) => !p.isEditing)),
6676
6753
  is_private: isPrivate
@@ -6712,6 +6789,7 @@ function CreateAncestryPanel({
6712
6789
  isPrivate,
6713
6790
  ancestryMode.abstraction_tree
6714
6791
  ));
6792
+ setHasUnsavedChanges(false);
6715
6793
  if (onRenderFullAncestry) {
6716
6794
  const rotation = branchStack.reduce((acc, step) => {
6717
6795
  return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
@@ -6763,6 +6841,7 @@ function CreateAncestryPanel({
6763
6841
  updatedRootTree,
6764
6842
  extrasObj
6765
6843
  );
6844
+ setHasUnsavedChanges(false);
6766
6845
  setLastSavedSnapshot(takeSnapshot(
6767
6846
  updatedRootTree,
6768
6847
  currentInputName,
@@ -6785,6 +6864,7 @@ function CreateAncestryPanel({
6785
6864
  const newTreeString = JSON.stringify(newRootTree);
6786
6865
  if (currentTreeString !== newTreeString) {
6787
6866
  updateGlobalTree(newRootTree);
6867
+ setHasUnsavedChanges(true);
6788
6868
  }
6789
6869
  }, [description, existingSections]);
6790
6870
  const handleTriggerFullRender = () => {
@@ -6807,6 +6887,7 @@ function CreateAncestryPanel({
6807
6887
  };
6808
6888
  const handleSaveDescriptionInline = (newDesc) => {
6809
6889
  setDescription(newDesc);
6890
+ setHasUnsavedChanges(true);
6810
6891
  handleLocalSave(true, { description: newDesc });
6811
6892
  };
6812
6893
  const swallow = (e) => e.stopPropagation();
@@ -6936,7 +7017,11 @@ function CreateAncestryPanel({
6936
7017
  {
6937
7018
  type: "text",
6938
7019
  value: ancestryName,
6939
- onChange: (e) => setAncestryName(e.target.value),
7020
+ onChange: (e) => {
7021
+ setAncestryName(e.target.value);
7022
+ setHasUnsavedChanges(true);
7023
+ },
7024
+ readOnly: isContextLinked,
6940
7025
  placeholder: "Nome da Ancestralidade",
6941
7026
  className: "text-xl sm:text-2xl font-semibold tracking-tight bg-transparent border-none p-0 focus:ring-2 focus:ring-indigo-500 rounded-md -ml-1.5 px-1.5 w-full outline-none transition-all focus:bg-slate-800/70"
6942
7027
  }
@@ -7585,9 +7670,9 @@ function InSceneCreationForm({
7585
7670
  };
7586
7671
  const handleToggleImageMode = () => {
7587
7672
  var _a2, _b;
7588
- const newValue = !useImageAsTexture;
7589
- setUseImageAsTexture(newValue);
7590
- if (newValue) {
7673
+ const newValue2 = !useImageAsTexture;
7674
+ setUseImageAsTexture(newValue2);
7675
+ if (newValue2) {
7591
7676
  const firstImageProp = customProps.find((p) => p.type === "images");
7592
7677
  if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
7593
7678
  const url = firstImageProp.value[0].value;
@@ -7885,9 +7970,9 @@ function InSceneVersionForm({
7885
7970
  };
7886
7971
  const handleToggleImageMode = () => {
7887
7972
  var _a, _b;
7888
- const newValue = !useImageAsTexture;
7889
- setUseImageAsTexture(newValue);
7890
- if (newValue) {
7973
+ const newValue2 = !useImageAsTexture;
7974
+ setUseImageAsTexture(newValue2);
7975
+ if (newValue2) {
7891
7976
  const firstImageProp = customProps.find((p) => p.type === "images");
7892
7977
  if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
7893
7978
  const url = firstImageProp.value[0].value;
@@ -8307,6 +8392,7 @@ function NodeDetailsPanel({
8307
8392
  return !!(node == null ? void 0 : node.useImageAsTexture);
8308
8393
  });
8309
8394
  const [selectedImageUrl, setSelectedImageUrl] = useState17((node == null ? void 0 : node.textureImageUrl) ?? null);
8395
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState17(false);
8310
8396
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
8311
8397
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
8312
8398
  initialWidth: isReadMode ? 700 : 440,
@@ -8344,6 +8430,7 @@ function NodeDetailsPanel({
8344
8430
  else if ((node == null ? void 0 : node.useImageAsTexture) === "false") setUseImageAsTexture(false);
8345
8431
  else setUseImageAsTexture(!!(node == null ? void 0 : node.useImageAsTexture));
8346
8432
  setSelectedImageUrl((node == null ? void 0 : node.textureImageUrl) ?? null);
8433
+ setHasUnsavedChanges(false);
8347
8434
  }
8348
8435
  }, [node]);
8349
8436
  const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
@@ -8370,6 +8457,7 @@ function NodeDetailsPanel({
8370
8457
  setIntensity(val);
8371
8458
  onIntensityChange == null ? void 0 : onIntensityChange(node.id, val);
8372
8459
  onDataUpdate == null ? void 0 : onDataUpdate({ ...node, intensity: val });
8460
+ setHasUnsavedChanges(true);
8373
8461
  };
8374
8462
  const handleCopyLink = () => {
8375
8463
  if (!(node == null ? void 0 : node.id)) return;
@@ -8387,14 +8475,17 @@ function NodeDetailsPanel({
8387
8475
  const v = e.target.value;
8388
8476
  setName(v);
8389
8477
  onNameChange == null ? void 0 : onNameChange(node.id, v);
8478
+ setHasUnsavedChanges(true);
8390
8479
  };
8391
8480
  const handleColorChange = (val) => {
8392
8481
  setColor(val);
8393
8482
  onColorChange == null ? void 0 : onColorChange(node.id, val);
8483
+ setHasUnsavedChanges(true);
8394
8484
  };
8395
8485
  const handleSizeChange = (newSize) => {
8396
8486
  setSize(newSize);
8397
8487
  onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
8488
+ setHasUnsavedChanges(true);
8398
8489
  };
8399
8490
  const handleAddType = (newType) => {
8400
8491
  const trimmed = newType.trim();
@@ -8402,10 +8493,12 @@ function NodeDetailsPanel({
8402
8493
  setTypes([...types, trimmed]);
8403
8494
  setTypeInput("");
8404
8495
  setShowTypeSuggestions(false);
8496
+ setHasUnsavedChanges(true);
8405
8497
  }
8406
8498
  };
8407
8499
  const handleRemoveType = (indexToRemove) => {
8408
8500
  setTypes(types.filter((_, index) => index !== indexToRemove));
8501
+ setHasUnsavedChanges(true);
8409
8502
  };
8410
8503
  const handleTypeInputKeyDown = (e) => {
8411
8504
  if (e.key === "Enter") {
@@ -8418,6 +8511,7 @@ function NodeDetailsPanel({
8418
8511
  const handleAddProp = () => {
8419
8512
  const newProp = createNewCustomProperty(customProps);
8420
8513
  setCustomProps((p) => [...p, newProp]);
8514
+ setHasUnsavedChanges(true);
8421
8515
  setTimeout(() => {
8422
8516
  var _a;
8423
8517
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -8426,19 +8520,21 @@ function NodeDetailsPanel({
8426
8520
  const handleRemoveProp = (i) => {
8427
8521
  const newProps = customProps.filter((_, idx) => idx !== i);
8428
8522
  setCustomProps(newProps);
8523
+ setHasUnsavedChanges(true);
8429
8524
  triggerAutoSave({ customProps: newProps });
8430
8525
  };
8431
8526
  const handleUpdateProp = (index, updatedProp) => {
8432
8527
  const newProps = [...customProps];
8433
8528
  newProps[index] = updatedProp;
8434
8529
  setCustomProps(newProps);
8530
+ setHasUnsavedChanges(true);
8435
8531
  if (!updatedProp.isEditing) {
8436
8532
  triggerAutoSave({ customProps: newProps });
8437
8533
  }
8438
8534
  };
8439
8535
  const handleToggleImageMode = () => {
8440
- const newValue = !useImageAsTexture;
8441
8536
  setUseImageAsTexture(newValue);
8537
+ setHasUnsavedChanges(true);
8442
8538
  let activeUrl = null;
8443
8539
  if (newValue) {
8444
8540
  const firstImageProp = customProps.find((p) => p.type === "images");
@@ -8460,6 +8556,7 @@ function NodeDetailsPanel({
8460
8556
  };
8461
8557
  const handleSelectTexture = (url) => {
8462
8558
  setSelectedImageUrl(url);
8559
+ setHasUnsavedChanges(true);
8463
8560
  onImageChange == null ? void 0 : onImageChange(true, url, color);
8464
8561
  onDataUpdate == null ? void 0 : onDataUpdate({
8465
8562
  ...node,
@@ -8470,6 +8567,7 @@ function NodeDetailsPanel({
8470
8567
  };
8471
8568
  const handleSaveDescriptionInline = (newDescription) => {
8472
8569
  setDescription(newDescription);
8570
+ setHasUnsavedChanges(true);
8473
8571
  onDataUpdate({ ...node, description: newDescription });
8474
8572
  triggerAutoSave({ description: newDescription });
8475
8573
  };
@@ -8480,6 +8578,10 @@ function NodeDetailsPanel({
8480
8578
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
8481
8579
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
8482
8580
  const currentIntensity = overrides.intensity !== void 0 ? overrides.intensity : intensity;
8581
+ if (!keepOpen && !hasUnsavedChanges) {
8582
+ onClose();
8583
+ return;
8584
+ }
8483
8585
  if (!currentName.trim() || currentTypes.length === 0) {
8484
8586
  alert("O campo 'Nome' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8485
8587
  return;
@@ -8504,6 +8606,7 @@ function NodeDetailsPanel({
8504
8606
  };
8505
8607
  await onSave(dataToSave, keepOpen);
8506
8608
  onDataUpdate(dataToSave);
8609
+ setHasUnsavedChanges(false);
8507
8610
  if (!keepOpen) {
8508
8611
  onClose();
8509
8612
  }
@@ -8570,7 +8673,7 @@ function NodeDetailsPanel({
8570
8673
  onImageClick: handleImageClickFromText,
8571
8674
  onSaveDescription: handleSaveDescriptionInline
8572
8675
  }
8573
- ) : /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React16.createElement("div", null, /* @__PURE__ */ React16.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React16.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shadow-[0_0_18px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React16.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ React16.createElement(
8676
+ ) : /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React16.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React16.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React16.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shadow-[0_0_18px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React16.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ React16.createElement(
8574
8677
  "button",
8575
8678
  {
8576
8679
  onClick: handleCopyLink,
@@ -8578,7 +8681,7 @@ function NodeDetailsPanel({
8578
8681
  title: isLinkCopied ? "Link Copiado!" : "Copiar link para este Node"
8579
8682
  },
8580
8683
  isLinkCopied ? /* @__PURE__ */ React16.createElement(FiCheck10, { size: 12 }) : /* @__PURE__ */ React16.createElement(FiLink5, { size: 12 })
8581
- )), /* @__PURE__ */ React16.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ React16.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React16.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React16.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ React16.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React16.createElement("span", { key: index, className: "flex items-center gap-1 bg-indigo-500/30 text-indigo-100 px-1.5 py-0.5 rounded-md text-xs font-medium border border-indigo-500/20" }, t, canEdit && /* @__PURE__ */ React16.createElement(
8684
+ )), /* @__PURE__ */ React16.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ React16.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React16.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React16.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ React16.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React16.createElement("span", { key: index, className: "flex items-center gap-1 bg-indigo-500/30 text-indigo-100 px-1.5 py-0.5 rounded-md text-xs font-medium border border-indigo-500/20" }, t, canEdit && /* @__PURE__ */ React16.createElement(
8582
8685
  "button",
8583
8686
  {
8584
8687
  type: "button",
@@ -8740,6 +8843,7 @@ function NodeDetailsPanel({
8740
8843
  initialValue: description,
8741
8844
  onSave: (newDescription) => {
8742
8845
  setDescription(newDescription);
8846
+ setHasUnsavedChanges(true);
8743
8847
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
8744
8848
  triggerAutoSave({ description: newDescription });
8745
8849
  },
@@ -8799,6 +8903,7 @@ function QuestDetailsPanel({
8799
8903
  const [existingSections, setExistingSections] = useState18((node == null ? void 0 : node.description_sections) || []);
8800
8904
  const [isSaving, setIsSaving] = useState18(false);
8801
8905
  const [isLinkCopied, setIsLinkCopied] = useState18(false);
8906
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState18(false);
8802
8907
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
8803
8908
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
8804
8909
  initialWidth: isReadMode ? 700 : 440,
@@ -8830,6 +8935,7 @@ function QuestDetailsPanel({
8830
8935
  setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
8831
8936
  setExistingSections((node == null ? void 0 : node.description_sections) || []);
8832
8937
  setCustomProps(extractCustomPropsFromNode(node || {}));
8938
+ setHasUnsavedChanges(false);
8833
8939
  }
8834
8940
  }, [node]);
8835
8941
  useEffect16(() => {
@@ -8861,16 +8967,19 @@ function QuestDetailsPanel({
8861
8967
  setRawTitle(val);
8862
8968
  const newStandardName = questPrefix ? `${questPrefix} - \xBB ${val || "Sem t\xEDtulo"}` : val;
8863
8969
  onNameChange == null ? void 0 : onNameChange(node.id, newStandardName, val);
8970
+ setHasUnsavedChanges(true);
8864
8971
  };
8865
8972
  const handleSizeChange = (newSize) => {
8866
8973
  setSize(newSize);
8867
8974
  onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
8975
+ setHasUnsavedChanges(true);
8868
8976
  };
8869
8977
  const handleStatusChange = (newStatus) => {
8870
8978
  setStatus(newStatus);
8871
8979
  const newColor = QUEST_STATUS_COLORS3[newStatus];
8872
8980
  onColorChange == null ? void 0 : onColorChange(node.id, newColor);
8873
8981
  onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor });
8982
+ setHasUnsavedChanges(true);
8874
8983
  };
8875
8984
  const handleAddType = (newType) => {
8876
8985
  const trimmed = newType.trim();
@@ -8878,11 +8987,13 @@ function QuestDetailsPanel({
8878
8987
  setTypes([...types, trimmed]);
8879
8988
  setTypeInput("");
8880
8989
  setShowTypeSuggestions(false);
8990
+ setHasUnsavedChanges(true);
8881
8991
  }
8882
8992
  };
8883
8993
  const handleRemoveType = (indexToRemove) => {
8884
8994
  if (types[indexToRemove] === "quest") return;
8885
8995
  setTypes(types.filter((_, index) => index !== indexToRemove));
8996
+ setHasUnsavedChanges(true);
8886
8997
  };
8887
8998
  const handleTypeInputKeyDown = (e) => {
8888
8999
  if (e.key === "Enter") {
@@ -8895,6 +9006,7 @@ function QuestDetailsPanel({
8895
9006
  const handleAddProp = () => {
8896
9007
  const newProp = createNewCustomProperty(customProps);
8897
9008
  setCustomProps((p) => [...p, newProp]);
9009
+ setHasUnsavedChanges(true);
8898
9010
  setTimeout(() => {
8899
9011
  var _a2;
8900
9012
  (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -8903,18 +9015,21 @@ function QuestDetailsPanel({
8903
9015
  const handleRemoveProp = (i) => {
8904
9016
  const newProps = customProps.filter((_, idx) => idx !== i);
8905
9017
  setCustomProps(newProps);
9018
+ setHasUnsavedChanges(true);
8906
9019
  triggerAutoSave({ customProps: newProps });
8907
9020
  };
8908
9021
  const handleUpdateProp = (index, updatedProp) => {
8909
9022
  const newProps = [...customProps];
8910
9023
  newProps[index] = updatedProp;
8911
9024
  setCustomProps(newProps);
9025
+ setHasUnsavedChanges(true);
8912
9026
  if (!updatedProp.isEditing) {
8913
9027
  triggerAutoSave({ customProps: newProps });
8914
9028
  }
8915
9029
  };
8916
9030
  const handleSaveDescriptionInline = (newDescription) => {
8917
9031
  setDescription(newDescription);
9032
+ setHasUnsavedChanges(true);
8918
9033
  onDataUpdate({ ...node, description: newDescription });
8919
9034
  triggerAutoSave({ description: newDescription });
8920
9035
  };
@@ -8926,6 +9041,10 @@ function QuestDetailsPanel({
8926
9041
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
8927
9042
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
8928
9043
  const currentStatus = overrides.status !== void 0 ? overrides.status : status;
9044
+ if (!keepOpen && !hasUnsavedChanges) {
9045
+ onClose();
9046
+ return;
9047
+ }
8929
9048
  if (!currentRawTitle.trim() || currentTypes.length === 0) {
8930
9049
  alert("O campo 'T\xEDtulo' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8931
9050
  return;
@@ -8955,6 +9074,7 @@ function QuestDetailsPanel({
8955
9074
  };
8956
9075
  await onSave(dataToSave, keepOpen);
8957
9076
  onDataUpdate(dataToSave);
9077
+ setHasUnsavedChanges(false);
8958
9078
  if (!keepOpen) {
8959
9079
  onClose();
8960
9080
  }
@@ -9012,7 +9132,7 @@ function QuestDetailsPanel({
9012
9132
  onImageClick: handleImageClickFromText,
9013
9133
  onSaveDescription: handleSaveDescriptionInline
9014
9134
  }
9015
- ) : /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React17.createElement(FiTarget2, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ React17.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ React17.createElement("button", { onClick: handleCopyLink, className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-sky-400"}`, title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Quest" }, isLinkCopied ? /* @__PURE__ */ React17.createElement(FiCheck11, { size: 12 }) : /* @__PURE__ */ React17.createElement(FiLink6, { size: 12 }))), /* @__PURE__ */ React17.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, standardizedName || (node == null ? void 0 : node.name))), /* @__PURE__ */ React17.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "T\xEDtulo da Quest"), /* @__PURE__ */ React17.createElement(
9135
+ ) : /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React17.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React17.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React17.createElement(FiTarget2, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ React17.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ React17.createElement("button", { onClick: handleCopyLink, className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-sky-400"}`, title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Quest" }, isLinkCopied ? /* @__PURE__ */ React17.createElement(FiCheck11, { size: 12 }) : /* @__PURE__ */ React17.createElement(FiLink6, { size: 12 }))), /* @__PURE__ */ React17.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, standardizedName || (node == null ? void 0 : node.name))), /* @__PURE__ */ React17.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "T\xEDtulo da Quest"), /* @__PURE__ */ React17.createElement(
9016
9136
  "input",
9017
9137
  {
9018
9138
  type: "text",
@@ -9186,9 +9306,12 @@ function RelationshipDetailsPanel({
9186
9306
  const [description, setDescription] = useState20((link == null ? void 0 : link.description) ?? "");
9187
9307
  const [customProps, setCustomProps] = useState20(() => extractCustomPropsFromNode(link || {}));
9188
9308
  const [existingSections, setExistingSections] = useState20((link == null ? void 0 : link.description_sections) || []);
9309
+ const [sourceLabel, setSourceLabel] = useState20((link == null ? void 0 : link.source_label) ?? "");
9310
+ const [targetLabel, setTargetLabel] = useState20((link == null ? void 0 : link.target_label) ?? "");
9189
9311
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState20(false);
9190
9312
  const [isSaving, setIsSaving] = useState20(false);
9191
9313
  const [isReadMode, setIsReadMode] = useState20(false);
9314
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState20(false);
9192
9315
  const propsEndRef = useRef16(null);
9193
9316
  const canEdit = useMemo9(() => {
9194
9317
  const ability = defineAbilityFor(userRole);
@@ -9199,12 +9322,16 @@ function RelationshipDetailsPanel({
9199
9322
  setDescription((link == null ? void 0 : link.description) ?? "");
9200
9323
  setExistingSections((link == null ? void 0 : link.description_sections) || []);
9201
9324
  setCustomProps(extractCustomPropsFromNode(link || {}));
9325
+ setSourceLabel((link == null ? void 0 : link.source_label) ?? "");
9326
+ setTargetLabel((link == null ? void 0 : link.target_label) ?? "");
9327
+ setHasUnsavedChanges(false);
9202
9328
  }, [link]);
9203
9329
  const swallow = (e) => e.stopPropagation();
9204
9330
  const handleAddProp = () => {
9205
9331
  if (!canEdit) return;
9206
9332
  const newProp = createNewCustomProperty(customProps);
9207
9333
  setCustomProps((p) => [...p, newProp]);
9334
+ setHasUnsavedChanges(true);
9208
9335
  setTimeout(() => {
9209
9336
  var _a;
9210
9337
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -9216,6 +9343,12 @@ function RelationshipDetailsPanel({
9216
9343
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
9217
9344
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
9218
9345
  const currentName = overrides.name !== void 0 ? overrides.name : name;
9346
+ const currentSourceLabel = overrides.sourceLabel !== void 0 ? overrides.sourceLabel : sourceLabel;
9347
+ const currentTargetLabel = overrides.targetLabel !== void 0 ? overrides.targetLabel : targetLabel;
9348
+ if (!keepOpen && !hasUnsavedChanges) {
9349
+ onClose();
9350
+ return;
9351
+ }
9219
9352
  setIsSaving(true);
9220
9353
  try {
9221
9354
  const extrasObj = toObjectFromCustomProps(currentCustomProps.filter((p) => !p.isEditing));
@@ -9231,8 +9364,11 @@ function RelationshipDetailsPanel({
9231
9364
  isCurved: link.isCurved,
9232
9365
  curveOffset: link.curveOffset
9233
9366
  };
9367
+ if (currentSourceLabel.trim()) dataToSave.source_label = currentSourceLabel.trim();
9368
+ if (currentTargetLabel.trim()) dataToSave.target_label = currentTargetLabel.trim();
9234
9369
  await onSave(dataToSave, keepOpen);
9235
9370
  onDataUpdate(dataToSave);
9371
+ setHasUnsavedChanges(false);
9236
9372
  if (!keepOpen) {
9237
9373
  onClose();
9238
9374
  }
@@ -9246,18 +9382,21 @@ function RelationshipDetailsPanel({
9246
9382
  const handleSaveDescriptionInline = (newDescription) => {
9247
9383
  if (!canEdit) return;
9248
9384
  setDescription(newDescription);
9385
+ setHasUnsavedChanges(true);
9249
9386
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
9250
9387
  triggerAutoSave({ description: newDescription });
9251
9388
  };
9252
9389
  const handleRemoveProp = (i) => {
9253
9390
  const newProps = customProps.filter((_, idx) => idx !== i);
9254
9391
  setCustomProps(newProps);
9392
+ setHasUnsavedChanges(true);
9255
9393
  triggerAutoSave({ customProps: newProps });
9256
9394
  };
9257
9395
  const handleUpdateProp = (index, updatedProp) => {
9258
9396
  const newProps = [...customProps];
9259
9397
  newProps[index] = updatedProp;
9260
9398
  setCustomProps(newProps);
9399
+ setHasUnsavedChanges(true);
9261
9400
  if (!updatedProp.isEditing) {
9262
9401
  triggerAutoSave({ customProps: newProps });
9263
9402
  }
@@ -9305,19 +9444,52 @@ function RelationshipDetailsPanel({
9305
9444
  onImageClick: handleImageClickFromText,
9306
9445
  onSaveDescription: handleSaveDescriptionInline
9307
9446
  }
9308
- ) : /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React19.createElement("div", null, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React19.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-teal-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ React19.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o")), /* @__PURE__ */ React19.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || "Rela\xE7\xE3o")), /* @__PURE__ */ React19.createElement("button", { onClick: onClose, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Rela\xE7\xE3o (Opcional)"), /* @__PURE__ */ React19.createElement(
9447
+ ) : /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React19.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React19.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-teal-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ React19.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o")), /* @__PURE__ */ React19.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || "Rela\xE7\xE3o")), /* @__PURE__ */ React19.createElement("button", { onClick: onClose, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Rela\xE7\xE3o (Opcional)"), /* @__PURE__ */ React19.createElement(
9309
9448
  "input",
9310
9449
  {
9311
9450
  type: "text",
9312
9451
  value: name,
9313
- onChange: (e) => setName(e.target.value),
9452
+ onChange: (e) => {
9453
+ setName(e.target.value);
9454
+ setHasUnsavedChanges(true);
9455
+ },
9314
9456
  placeholder: "Ex: Controla, Pertence a, Fornece...",
9315
9457
  disabled: !canEdit,
9316
9458
  className: `w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60
9317
9459
  ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9318
9460
  `
9319
9461
  }
9320
- )), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React19.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React19.createElement(
9462
+ )), /* @__PURE__ */ React19.createElement("div", { className: "rounded-xl border border-white/8 bg-slate-800/30 p-3 space-y-3" }, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React19.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ React19.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React19.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ React19.createElement("p", { className: "text-xs font-medium text-violet-300/80 uppercase tracking-wider" }, "Labels de Agrupamento")), /* @__PURE__ */ React19.createElement("p", { className: "text-[11px] text-slate-400 leading-relaxed" }, "Labels agrupam conex\xF5es no menu de Conex\xF5es. Cada lado da conex\xE3o pode ter sua pr\xF3pria label."), /* @__PURE__ */ React19.createElement("div", { className: "grid grid-cols-2 gap-2" }, /* @__PURE__ */ React19.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Source"), /* @__PURE__ */ React19.createElement(
9463
+ "input",
9464
+ {
9465
+ type: "text",
9466
+ value: sourceLabel,
9467
+ onChange: (e) => {
9468
+ setSourceLabel(e.target.value);
9469
+ setHasUnsavedChanges(true);
9470
+ },
9471
+ placeholder: "Ex: Conceitos",
9472
+ disabled: !canEdit,
9473
+ className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
9474
+ ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9475
+ `
9476
+ }
9477
+ )), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Target"), /* @__PURE__ */ React19.createElement(
9478
+ "input",
9479
+ {
9480
+ type: "text",
9481
+ value: targetLabel,
9482
+ onChange: (e) => {
9483
+ setTargetLabel(e.target.value);
9484
+ setHasUnsavedChanges(true);
9485
+ },
9486
+ placeholder: "Ex: Refer\xEAncias",
9487
+ disabled: !canEdit,
9488
+ className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
9489
+ ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9490
+ `
9491
+ }
9492
+ )))), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React19.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React19.createElement(
9321
9493
  DescriptionDisplay,
9322
9494
  {
9323
9495
  description,
@@ -9387,6 +9559,7 @@ function RelationshipDetailsPanel({
9387
9559
  onSave: (newDescription) => {
9388
9560
  if (!canEdit) return;
9389
9561
  setDescription(newDescription);
9562
+ setHasUnsavedChanges(true);
9390
9563
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
9391
9564
  triggerAutoSave({ description: newDescription });
9392
9565
  },
@@ -9834,7 +10007,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
9834
10007
  onMentionClick,
9835
10008
  onImageClick: handleImageClickFromText
9836
10009
  }
9837
- ) : /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React23.createElement("div", null, /* @__PURE__ */ React23.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React23.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ React23.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ React23.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ React23.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ React23.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ React23.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ React23.createElement("button", { onClick: onClose, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ React23.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React23.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React23.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React23.createElement(
10010
+ ) : /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React23.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React23.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React23.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ React23.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ React23.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ React23.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ React23.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ React23.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ React23.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ React23.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React23.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React23.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React23.createElement(
9838
10011
  "button",
9839
10012
  {
9840
10013
  onClick: () => setIsReadMode(true),
@@ -10627,6 +10800,7 @@ function XViewScene({
10627
10800
  });
10628
10801
  const [isImportModalOpen, setIsImportModalOpen] = useState25(false);
10629
10802
  const [importSuccessMessage, setImportSuccessMessage] = useState25("");
10803
+ const [invalidTargetError, setInvalidTargetError] = useState25(null);
10630
10804
  const [highlightedNodeId, setHighlightedNodeId] = useState25(null);
10631
10805
  const [isAncestryBoardOpen, setIsAncestryBoardOpen] = useState25(false);
10632
10806
  const [ancestryBoardData, setAncestryBoardData] = useState25([]);
@@ -11288,6 +11462,7 @@ function XViewScene({
11288
11462
  boardPromise
11289
11463
  ]);
11290
11464
  if ((sceneResponse == null ? void 0 : sceneResponse.success) && ((_a2 = sceneResponse.data) == null ? void 0 : _a2.scene) && ((_b2 = sceneResponse.data) == null ? void 0 : _b2.parent)) {
11465
+ console.log("XViewScene - View Members:", sceneResponse.data.scene.members);
11291
11466
  if (focusNodeId) {
11292
11467
  let targetNode = sceneResponse.data.scene.nodes.find(
11293
11468
  (n) => String(n.id) === String(focusNodeId)
@@ -13528,7 +13703,7 @@ function XViewScene({
13528
13703
  sceneVersion
13529
13704
  ]);
13530
13705
  const handleStartReadingAncestry = useCallback4(
13531
- async (ancestryObject) => {
13706
+ async (ancestryObject, renderMode = "full") => {
13532
13707
  setContextMenu(
13533
13708
  (prev) => prev.visible ? { ...prev, visible: false } : prev
13534
13709
  );
@@ -13544,14 +13719,20 @@ function XViewScene({
13544
13719
  const hasDescription = ancestryObject.description && ancestryObject.description.trim() !== "";
13545
13720
  const hasMainTreeNodes = ancestryObject.tree.children && ancestryObject.tree.children.length > 0;
13546
13721
  const hasAbstractionNodes = ancestryObject.abstraction_tree && ancestryObject.abstraction_tree.children && ancestryObject.abstraction_tree.children.length > 0;
13547
- const shouldAutoRenderAbstraction = !hasDescription && !hasMainTreeNodes && hasAbstractionNodes;
13722
+ const isFull = renderMode === "full";
13723
+ const isAncestryOnly = renderMode === "ancestry_only";
13724
+ const isAbstractionOnly = renderMode === "abstraction_only";
13725
+ let shouldRenderAbstraction = isAbstractionOnly;
13726
+ if (isFull) {
13727
+ shouldRenderAbstraction = !hasDescription && !hasMainTreeNodes && hasAbstractionNodes;
13728
+ }
13548
13729
  setReadingMode({
13549
- isActive: hasDescription,
13730
+ isActive: isFull ? hasDescription : false,
13550
13731
  ancestry: ancestryObject,
13551
13732
  branchStack: [],
13552
- autoAbstraction: shouldAutoRenderAbstraction
13733
+ autoAbstraction: shouldRenderAbstraction
13553
13734
  });
13554
- if (shouldAutoRenderAbstraction) {
13735
+ if (shouldRenderAbstraction) {
13555
13736
  handleRenderAbstractionTree(ancestryObject, null);
13556
13737
  } else {
13557
13738
  const initialSections = /* @__PURE__ */ new Set(["preamble", 0, "0"]);
@@ -14297,6 +14478,9 @@ function XViewScene({
14297
14478
  }, 300);
14298
14479
  } else {
14299
14480
  setHasFocusedInitial(true);
14481
+ setInvalidTargetError(
14482
+ "O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
14483
+ );
14300
14484
  }
14301
14485
  }
14302
14486
  }, [
@@ -14319,6 +14503,9 @@ function XViewScene({
14319
14503
  }, 300);
14320
14504
  } else {
14321
14505
  setHasOpenedInitialAncestry(true);
14506
+ setInvalidTargetError(
14507
+ "O link aponta para uma ancestralidade que n\xE3o foi encontrada ou foi exclu\xEDda."
14508
+ );
14322
14509
  }
14323
14510
  }
14324
14511
  }, [
@@ -14928,6 +15115,83 @@ function XViewScene({
14928
15115
  currentViewName: viewParams == null ? void 0 : viewParams.name,
14929
15116
  currentAncestries: ancestryDataRef.current || []
14930
15117
  }
15118
+ ),
15119
+ invalidTargetError && /* @__PURE__ */ React25.createElement(
15120
+ "div",
15121
+ {
15122
+ className: "ui-overlay",
15123
+ style: {
15124
+ position: "fixed",
15125
+ top: "24px",
15126
+ left: "50%",
15127
+ transform: "translateX(-50%)",
15128
+ zIndex: 1e4,
15129
+ padding: "16px 24px",
15130
+ background: "rgba(30, 20, 20, 0.85)",
15131
+ backdropFilter: "blur(12px)",
15132
+ WebkitBackdropFilter: "blur(12px)",
15133
+ border: "1px solid rgba(255, 70, 70, 0.35)",
15134
+ borderRadius: "16px",
15135
+ boxShadow: "0 12px 40px rgba(0,0,0,0.5), 0 0 30px rgba(255, 50, 50, 0.1)",
15136
+ color: "#ffa0a0",
15137
+ display: "flex",
15138
+ alignItems: "center",
15139
+ gap: "16px",
15140
+ fontFamily: "Inter, sans-serif",
15141
+ animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
15142
+ }
15143
+ },
15144
+ /* @__PURE__ */ React25.createElement("style", null, `
15145
+ @keyframes fadeInDown {
15146
+ from { opacity: 0; transform: translate(-50%, -20px); }
15147
+ to { opacity: 1; transform: translate(-50%, 0); }
15148
+ }
15149
+ `),
15150
+ /* @__PURE__ */ React25.createElement(
15151
+ "svg",
15152
+ {
15153
+ width: "20",
15154
+ height: "20",
15155
+ viewBox: "0 0 24 24",
15156
+ fill: "none",
15157
+ stroke: "currentColor",
15158
+ strokeWidth: "2",
15159
+ strokeLinecap: "round",
15160
+ strokeLinejoin: "round"
15161
+ },
15162
+ /* @__PURE__ */ React25.createElement("circle", { cx: "12", cy: "12", r: "10" }),
15163
+ /* @__PURE__ */ React25.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
15164
+ /* @__PURE__ */ React25.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
15165
+ ),
15166
+ /* @__PURE__ */ React25.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
15167
+ /* @__PURE__ */ React25.createElement(
15168
+ "button",
15169
+ {
15170
+ onClick: () => setInvalidTargetError(null),
15171
+ style: {
15172
+ background: "rgba(255, 255, 255, 0.1)",
15173
+ border: "none",
15174
+ color: "white",
15175
+ padding: "4px 10px",
15176
+ borderRadius: "8px",
15177
+ cursor: "pointer",
15178
+ fontSize: "12px",
15179
+ fontWeight: 600,
15180
+ transition: "all 0.2s",
15181
+ marginLeft: "8px",
15182
+ border: "1px solid rgba(255,255,255,0.1)"
15183
+ },
15184
+ onMouseOver: (e) => {
15185
+ e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
15186
+ e.currentTarget.style.transform = "translateY(-1px)";
15187
+ },
15188
+ onMouseOut: (e) => {
15189
+ e.currentTarget.style.background = "rgba(255, 255, 255, 0.1)";
15190
+ e.currentTarget.style.transform = "translateY(0)";
15191
+ }
15192
+ },
15193
+ "Fechar"
15194
+ )
14931
15195
  )
14932
15196
  );
14933
15197
  }