@lv-x-software-house/x_view 1.2.1-dev.1 → 1.2.1-dev.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +142 -15
  2. package/dist/index.mjs +208 -78
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -131,6 +131,7 @@ function ContextMenu({
131
131
  const [menuView, setMenuView] = (0, import_react.useState)("main");
132
132
  const [selectedAncestry, setSelectedAncestry] = (0, import_react.useState)(null);
133
133
  const [versionSubMenu, setVersionSubMenu] = (0, import_react.useState)(null);
134
+ const [isLinkCopied, setIsLinkCopied] = (0, import_react.useState)(false);
134
135
  const ability = (0, import_react.useMemo)(() => defineAbilityFor(userRole), [userRole]);
135
136
  (0, import_react.useEffect)(() => {
136
137
  if (data.visible) {
@@ -267,6 +268,20 @@ function ContextMenu({
267
268
  return ((_a2 = node.version_node) == null ? void 0 : _a2.is_version) && String(node.version_node.parent_node) === String(data.nodeData.id);
268
269
  }
269
270
  ).sort((a, b) => getYearFromDate(b.Date) - getYearFromDate(a.Date));
271
+ const handleCopyLink = (e, nodeData) => {
272
+ e.stopPropagation();
273
+ const baseUrl = window.location.origin + window.location.pathname;
274
+ const fullUrl = `${baseUrl}?focus=${nodeData.id}`;
275
+ navigator.clipboard.writeText(fullUrl).then(() => {
276
+ setIsLinkCopied(true);
277
+ setTimeout(() => {
278
+ setIsLinkCopied(false);
279
+ onClose();
280
+ }, 1500);
281
+ }).catch((err) => {
282
+ console.error("Erro ao copiar link:", err);
283
+ });
284
+ };
270
285
  const renderMainView = () => {
271
286
  const hasVersions = computedVersions.length > 0;
272
287
  const canCreateVersion = ability.can("create", "Versioning");
@@ -275,7 +290,7 @@ function ContextMenu({
275
290
  return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_12px_1px_rgba(99,102,241,0.5)]" }), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
276
291
  onFocusNode == null ? void 0 : onFocusNode(data.nodeData);
277
292
  onClose();
278
- }, className: baseButtonClass, title: "Focar na c\xE2mera" }, /* @__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: "10" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Focar neste Node")), ability.can("create", "Connection") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__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: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conectar")), ability.can("create", "Node") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__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: "10" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar 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: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2.8 2 .8s1.5-.3 2-.8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__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: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ import_react.default.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__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 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), ability.can("dismiss", "Node") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDismissNode == null ? void 0 : onDismissNode(data.nodeData), className: baseButtonClass, title: "Remover da visualiza\xE7\xE3o" }, /* @__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: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "2", y1: "2", x2: "22", y2: "22" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Dismiss")), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDismissOtherNodes == null ? void 0 : onDismissOtherNodes(data.nodeData), className: baseButtonClass, title: "Remover outros da visualiza\xE7\xE3o" }, /* @__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: "M3 7V5a2 2 0 0 1 2-2h2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Dismiss other nodes"))), ability.can("delete", "Node") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDeleteNode == null ? void 0 : onDeleteNode(data.nodeData), className: deleteButtonClass, title: "Excluir Node" }, /* @__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("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Excluir Node"))));
293
+ }, className: baseButtonClass, title: "Focar na c\xE2mera" }, /* @__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: "10" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Focar neste Node")), ability.can("create", "Connection") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__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: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conectar")), ability.can("create", "Node") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__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: "10" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar 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: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2-.8 2 .8s1.5-.3 2-.8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__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: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ import_react.default.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__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 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: (e) => handleCopyLink(e, data.nodeData), className: baseButtonClass, title: "Copiar Link para Compartilhar" }, isLinkCopied ? /* @__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: "#4ade80", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("polyline", { points: "20 6 9 17 4 12" })) : /* @__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: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ import_react.default.createElement("span", { className: isLinkCopied ? "text-green-400" : "" }, isLinkCopied ? "Copiado!" : "Copiar Link")), ability.can("dismiss", "Node") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDismissNode == null ? void 0 : onDismissNode(data.nodeData), className: baseButtonClass, title: "Remover da visualiza\xE7\xE3o" }, /* @__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: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "2", y1: "2", x2: "22", y2: "22" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Dismiss")), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDismissOtherNodes == null ? void 0 : onDismissOtherNodes(data.nodeData), className: baseButtonClass, title: "Remover outros da visualiza\xE7\xE3o" }, /* @__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: "M3 7V5a2 2 0 0 1 2-2h2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Dismiss other nodes"))), ability.can("delete", "Node") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDeleteNode == null ? void 0 : onDeleteNode(data.nodeData), className: deleteButtonClass, title: "Excluir Node" }, /* @__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("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Excluir Node"))));
279
294
  };
280
295
  const renderVersionSubMenuView = () => {
281
296
  const group = versionSubMenu;
@@ -4533,6 +4548,8 @@ var ReadOnlyNodeItem = ({ nodeData, onViewSelect, highlightedPathIds = [], targe
4533
4548
  function DescriptionReadModePanel({
4534
4549
  title,
4535
4550
  description,
4551
+ ancestryId,
4552
+ // <-- NOVO: Prop recebida do XViewScene
4536
4553
  savedSections,
4537
4554
  onBack,
4538
4555
  onEdit,
@@ -4555,12 +4572,24 @@ function DescriptionReadModePanel({
4555
4572
  abstractionTree = null,
4556
4573
  onRenderAbstractionTree = null,
4557
4574
  initialShowAbstraction = false
4558
- // <--- NOVA PROP RECEBIDA
4559
4575
  }) {
4560
4576
  const [showProperties, setShowProperties] = (0, import_react7.useState)(false);
4561
4577
  const [showAbstraction, setShowAbstraction] = (0, import_react7.useState)(false);
4562
4578
  const [targetRenderNodeId, setTargetRenderNodeId] = (0, import_react7.useState)(null);
4563
- import_react7.default.useEffect(() => {
4579
+ const [isLinkCopied, setIsLinkCopied] = (0, import_react7.useState)(false);
4580
+ const handleCopyLink = (e) => {
4581
+ e.stopPropagation();
4582
+ if (!ancestryId) return;
4583
+ const baseUrl = window.location.origin + window.location.pathname;
4584
+ const fullUrl = `${baseUrl}?ancestry=${ancestryId}`;
4585
+ navigator.clipboard.writeText(fullUrl).then(() => {
4586
+ setIsLinkCopied(true);
4587
+ setTimeout(() => setIsLinkCopied(false), 2e3);
4588
+ }).catch((err) => {
4589
+ console.error("Erro ao copiar link:", err);
4590
+ });
4591
+ };
4592
+ (0, import_react7.useEffect)(() => {
4564
4593
  setShowAbstraction(initialShowAbstraction);
4565
4594
  if (initialShowAbstraction) {
4566
4595
  setShowProperties(false);
@@ -4649,7 +4678,15 @@ function DescriptionReadModePanel({
4649
4678
  title: "Voltar"
4650
4679
  },
4651
4680
  /* @__PURE__ */ import_react7.default.createElement(import_fi6.FiArrowLeft, { size: 16 })
4652
- ), /* @__PURE__ */ import_react7.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react7.default.createElement("p", { className: "text-xs/relaxed text-slate-400 uppercase tracking-wider font-semibold" }, showAbstraction ? "Tree de Abstra\xE7\xE3o" : showProperties ? "Propriedades Adicionais" : "Modo de Leitura"), /* @__PURE__ */ import_react7.default.createElement("h2", { className: "text-lg sm:text-xl font-semibold tracking-tight text-white truncate", title }, title || "Sem T\xEDtulo"))), /* @__PURE__ */ import_react7.default.createElement("div", { className: "flex items-center gap-2 flex-shrink-0" }, hasCustomProps && /* @__PURE__ */ import_react7.default.createElement(
4681
+ ), /* @__PURE__ */ import_react7.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react7.default.createElement("p", { className: "text-xs/relaxed text-slate-400 uppercase tracking-wider font-semibold" }, showAbstraction ? "Tree de Abstra\xE7\xE3o" : showProperties ? "Propriedades Adicionais" : "Modo de Leitura"), ancestryId && /* @__PURE__ */ import_react7.default.createElement(
4682
+ "button",
4683
+ {
4684
+ onClick: handleCopyLink,
4685
+ className: `p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-indigo-400"}`,
4686
+ title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Ancestralidade"
4687
+ },
4688
+ isLinkCopied ? /* @__PURE__ */ import_react7.default.createElement(import_fi6.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react7.default.createElement(import_fi6.FiLink, { size: 12 })
4689
+ )), /* @__PURE__ */ import_react7.default.createElement("h2", { className: "text-lg sm:text-xl font-semibold tracking-tight text-white truncate", title }, title || "Sem T\xEDtulo"))), /* @__PURE__ */ import_react7.default.createElement("div", { className: "flex items-center gap-2 flex-shrink-0" }, hasCustomProps && /* @__PURE__ */ import_react7.default.createElement(
4653
4690
  "button",
4654
4691
  {
4655
4692
  onClick: handleToggleProperties,
@@ -5176,6 +5213,20 @@ function CreateAncestryPanel({
5176
5213
  currentAncestryId
5177
5214
  } = ancestryMode;
5178
5215
  const [isSaving, setIsSaving] = (0, import_react10.useState)(false);
5216
+ const [isLinkCopied, setIsLinkCopied] = (0, import_react10.useState)(false);
5217
+ const handleCopyLink = (e) => {
5218
+ e.stopPropagation();
5219
+ if (!currentAncestryId || currentAncestryId === "temp_root" || currentAncestryId === "temp_creating") {
5220
+ alert("Salve a ancestralidade primeiro para gerar um link.");
5221
+ return;
5222
+ }
5223
+ const baseUrl = window.location.origin + window.location.pathname;
5224
+ const fullUrl = `${baseUrl}?ancestry=${currentAncestryId}`;
5225
+ navigator.clipboard.writeText(fullUrl).then(() => {
5226
+ setIsLinkCopied(true);
5227
+ setTimeout(() => setIsLinkCopied(false), 2e3);
5228
+ }).catch((err) => console.error("Erro ao copiar link:", err));
5229
+ };
5179
5230
  const [isPickerOpen, setIsPickerOpen] = (0, import_react10.useState)(false);
5180
5231
  const [customProps, setCustomProps] = (0, import_react10.useState)([]);
5181
5232
  const propsEndRef = (0, import_react10.useRef)(null);
@@ -6205,7 +6256,15 @@ function CreateAncestryPanel({
6205
6256
  onHighlightNode,
6206
6257
  onImageClick: handleImageClickFromText
6207
6258
  }
6208
- ) : /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, /* @__PURE__ */ import_react10.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0 flex-shrink-0" }), /* @__PURE__ */ import_react10.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4 flex-shrink-0" }, /* @__PURE__ */ import_react10.default.createElement("div", { className: "w-full" }, /* @__PURE__ */ import_react10.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react10.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_react10.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, branchStack.length > 0 ? `Ramifica\xE7\xE3o (N\xEDvel ${branchStack.length})` : isEditMode ? "Editar Ancestralidade" : "Criar Ancestralidade")), /* @__PURE__ */ import_react10.default.createElement(
6259
+ ) : /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, /* @__PURE__ */ import_react10.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0 flex-shrink-0" }), /* @__PURE__ */ import_react10.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4 flex-shrink-0" }, /* @__PURE__ */ import_react10.default.createElement("div", { className: "w-full" }, /* @__PURE__ */ import_react10.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react10.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_react10.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, branchStack.length > 0 ? `Ramifica\xE7\xE3o (N\xEDvel ${branchStack.length})` : isEditMode ? "Editar Ancestralidade" : "Criar Ancestralidade"), currentAncestryId && currentAncestryId !== "temp_creating" && currentAncestryId !== "temp_root" && /* @__PURE__ */ import_react10.default.createElement(
6260
+ "button",
6261
+ {
6262
+ onClick: handleCopyLink,
6263
+ className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-indigo-400"}`,
6264
+ title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Ancestralidade"
6265
+ },
6266
+ isLinkCopied ? /* @__PURE__ */ import_react10.default.createElement(import_fi9.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react10.default.createElement(import_fi9.FiLink, { size: 12 })
6267
+ )), /* @__PURE__ */ import_react10.default.createElement(
6209
6268
  "input",
6210
6269
  {
6211
6270
  type: "text",
@@ -7259,7 +7318,6 @@ function NodeDetailsPanel({
7259
7318
  onIntensityChange,
7260
7319
  onUploadFile,
7261
7320
  userRole
7262
- // <--- ADIÇÃO: Recebendo a prop userRole
7263
7321
  }) {
7264
7322
  const [name, setName] = (0, import_react15.useState)((node == null ? void 0 : node.name) ?? "");
7265
7323
  const [types, setTypes] = (0, import_react15.useState)([]);
@@ -7275,6 +7333,7 @@ function NodeDetailsPanel({
7275
7333
  const [isReadMode, setIsReadMode] = (0, import_react15.useState)(false);
7276
7334
  const [existingSections, setExistingSections] = (0, import_react15.useState)((node == null ? void 0 : node.description_sections) || []);
7277
7335
  const [isSaving, setIsSaving] = (0, import_react15.useState)(false);
7336
+ const [isLinkCopied, setIsLinkCopied] = (0, import_react15.useState)(false);
7278
7337
  const [useImageAsTexture, setUseImageAsTexture] = (0, import_react15.useState)(() => {
7279
7338
  if ((node == null ? void 0 : node.useImageAsTexture) === "true") return true;
7280
7339
  if ((node == null ? void 0 : node.useImageAsTexture) === "false") return false;
@@ -7336,6 +7395,17 @@ function NodeDetailsPanel({
7336
7395
  onIntensityChange == null ? void 0 : onIntensityChange(node.id, val);
7337
7396
  onDataUpdate == null ? void 0 : onDataUpdate({ ...node, intensity: val });
7338
7397
  };
7398
+ const handleCopyLink = () => {
7399
+ if (!(node == null ? void 0 : node.id)) return;
7400
+ const baseUrl = window.location.origin + window.location.pathname;
7401
+ const fullUrl = `${baseUrl}?focus=${node.id}`;
7402
+ navigator.clipboard.writeText(fullUrl).then(() => {
7403
+ setIsLinkCopied(true);
7404
+ setTimeout(() => setIsLinkCopied(false), 2e3);
7405
+ }).catch((err) => {
7406
+ console.error("Erro ao copiar link:", err);
7407
+ });
7408
+ };
7339
7409
  const swallow = (e) => e.stopPropagation();
7340
7410
  const handleNameChange = (e) => {
7341
7411
  const v = e.target.value;
@@ -7509,7 +7579,15 @@ function NodeDetailsPanel({
7509
7579
  onMentionClick,
7510
7580
  onImageClick: handleImageClickFromText
7511
7581
  }
7512
- ) : /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ import_react15.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react15.default.createElement("div", null, /* @__PURE__ */ import_react15.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react15.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_react15.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node")), /* @__PURE__ */ import_react15.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react15.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_react15.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react15.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react15.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ import_react15.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_react15.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_react15.default.createElement(
7582
+ ) : /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ import_react15.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react15.default.createElement("div", null, /* @__PURE__ */ import_react15.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react15.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_react15.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ import_react15.default.createElement(
7583
+ "button",
7584
+ {
7585
+ onClick: handleCopyLink,
7586
+ className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-indigo-400"}`,
7587
+ title: isLinkCopied ? "Link Copiado!" : "Copiar link para este Node"
7588
+ },
7589
+ isLinkCopied ? /* @__PURE__ */ import_react15.default.createElement(import_fi14.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react15.default.createElement(import_fi14.FiLink, { size: 12 })
7590
+ )), /* @__PURE__ */ import_react15.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react15.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_react15.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react15.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react15.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ import_react15.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_react15.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_react15.default.createElement(
7513
7591
  "button",
7514
7592
  {
7515
7593
  type: "button",
@@ -7662,8 +7740,7 @@ function NodeDetailsPanel({
7662
7740
  isSaving && /* @__PURE__ */ import_react15.default.createElement(import_fi14.FiLoader, { className: "animate-spin" }),
7663
7741
  isSaving ? "Salvando..." : "Salvar"
7664
7742
  )))
7665
- ), isDescriptionModalOpen && canEdit && // MODIFICAÇÃO: Modal de edição só abre se canEdit
7666
- /* @__PURE__ */ import_react15.default.createElement(
7743
+ ), isDescriptionModalOpen && canEdit && /* @__PURE__ */ import_react15.default.createElement(
7667
7744
  DescriptionEditModal,
7668
7745
  {
7669
7746
  isOpen: isDescriptionModalOpen,
@@ -8975,6 +9052,9 @@ function XViewScene({
8975
9052
  var _a, _b, _c, _d, _e, _f;
8976
9053
  const { data: session, status } = (0, import_react24.useSession)();
8977
9054
  const router = (0, import_navigation.useRouter)();
9055
+ const searchParams = (0, import_navigation.useSearchParams)();
9056
+ const focusNodeId = searchParams == null ? void 0 : searchParams.get("focus");
9057
+ const focusAncestryId = searchParams == null ? void 0 : searchParams.get("ancestry");
8978
9058
  const viewParams = (0, import_react23.useMemo)(() => {
8979
9059
  if (encryptedConfig) {
8980
9060
  const data = decryptData(encryptedConfig);
@@ -9006,6 +9086,9 @@ function XViewScene({
9006
9086
  }
9007
9087
  if (viewParams && session && status === "authenticated") {
9008
9088
  verifyPermission();
9089
+ } else if (status === "unauthenticated") {
9090
+ setPermissionStatus("denied");
9091
+ setIsLoading(false);
9009
9092
  }
9010
9093
  }, [viewParams, session, status, check_user_permission]);
9011
9094
  const sceneConfigId = (0, import_react23.useMemo)(() => (viewParams == null ? void 0 : viewParams.id) || null, [viewParams]);
@@ -9041,6 +9124,8 @@ function XViewScene({
9041
9124
  const [relationshipMenu, setRelationshipMenu] = (0, import_react23.useState)({ visible: false, x: 0, y: 0, linkObject: null });
9042
9125
  const [creationMode, setCreationMode] = (0, import_react23.useState)({ isActive: false, sourceNodeData: null });
9043
9126
  const [versionMode, setVersionMode] = (0, import_react23.useState)({ isActive: false, sourceNodeData: null });
9127
+ const [hasFocusedInitial, setHasFocusedInitial] = (0, import_react23.useState)(false);
9128
+ const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = (0, import_react23.useState)(false);
9044
9129
  const [ancestryMode, setAncestryMode] = (0, import_react23.useState)({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", ancestryDescriptionSections: [], isAddingNodes: false });
9045
9130
  const [readingMode, setReadingMode] = (0, import_react23.useState)({
9046
9131
  isActive: false,
@@ -9621,12 +9706,23 @@ function XViewScene({
9621
9706
  const boardPromise = get_ancestry_board_action && session ? get_ancestry_board_action(configPath, sceneType, session, ownerId2) : Promise.resolve({ success: false, data: [] });
9622
9707
  const [sceneResponse, boardResponse] = await Promise.all([scenePromise, boardPromise]);
9623
9708
  if ((sceneResponse == null ? void 0 : sceneResponse.success) && ((_a2 = sceneResponse.data) == null ? void 0 : _a2.scene) && ((_b2 = sceneResponse.data) == null ? void 0 : _b2.parent)) {
9709
+ if (focusNodeId) {
9710
+ let targetNode = sceneResponse.data.scene.nodes.find((n) => String(n.id) === String(focusNodeId));
9711
+ if (!targetNode) {
9712
+ const allParentNodes = Object.values(sceneResponse.data.parent).flatMap((f) => f.nodes || []);
9713
+ targetNode = allParentNodes.find((n) => String(n.id) === String(focusNodeId));
9714
+ }
9715
+ if (targetNode) {
9716
+ sceneResponse.data.scene.nodes = [targetNode];
9717
+ sceneResponse.data.scene.links = [];
9718
+ }
9719
+ } else if (focusAncestryId) {
9720
+ sceneResponse.data.scene.nodes = [];
9721
+ sceneResponse.data.scene.links = [];
9722
+ }
9624
9723
  sceneDataRef.current = sceneResponse.data.scene;
9625
9724
  parentDataRef.current = sceneResponse.data.parent;
9626
9725
  ancestryDataRef.current = sceneResponse.data.ancestry;
9627
- console.log("Console de sceneDataRef.current no useEffect inicial:", sceneDataRef.current);
9628
- console.log("Console de parentDataRef.current no useEffect inicial:", parentDataRef.current);
9629
- console.log("Console de ancestryDataRef.current no useEffect inicial:", ancestryDataRef.current);
9630
9726
  setIsInitialized(true);
9631
9727
  } else {
9632
9728
  console.error("Falha ao buscar dados da cena:", (sceneResponse == null ? void 0 : sceneResponse.error) || "Resposta inv\xE1lida.");
@@ -9634,7 +9730,6 @@ function XViewScene({
9634
9730
  if (boardResponse == null ? void 0 : boardResponse.success) {
9635
9731
  setAncestryBoardData(boardResponse.data);
9636
9732
  } else {
9637
- console.warn("Ancestry Board n\xE3o carregado ou vazio:", boardResponse == null ? void 0 : boardResponse.error);
9638
9733
  setAncestryBoardData([]);
9639
9734
  }
9640
9735
  } catch (error) {
@@ -9658,11 +9753,14 @@ function XViewScene({
9658
9753
  status,
9659
9754
  ownerId,
9660
9755
  viewParams,
9661
- // Importante incluir viewParams nas dependências
9662
9756
  get_scene_view_data,
9663
9757
  get_ancestry_board_action,
9664
9758
  isInitialized,
9665
- permissionStatus
9759
+ permissionStatus,
9760
+ focusNodeId,
9761
+ // <-- MANTIDO
9762
+ focusAncestryId
9763
+ // <-- ADICIONADO O focusAncestryId NAS DEPENDÊNCIAS
9666
9764
  ]);
9667
9765
  const isNodeInView = (0, import_react23.useCallback)((id) => {
9668
9766
  const key = String(id);
@@ -11946,6 +12044,34 @@ function XViewScene({
11946
12044
  tweenToTarget(nodeMesh, 1.2);
11947
12045
  }
11948
12046
  }, [tweenToTarget]);
12047
+ (0, import_react23.useEffect)(() => {
12048
+ if (isInitialized && focusNodeId && !hasFocusedInitial) {
12049
+ const nodeObjects = stateRef.current.nodeObjects || {};
12050
+ const targetMesh = nodeObjects[String(focusNodeId)];
12051
+ if (targetMesh) {
12052
+ setTimeout(() => {
12053
+ tweenToTarget(targetMesh, 1.2);
12054
+ setHasFocusedInitial(true);
12055
+ }, 300);
12056
+ } else {
12057
+ setHasFocusedInitial(true);
12058
+ }
12059
+ }
12060
+ }, [isInitialized, sceneVersion, focusNodeId, hasFocusedInitial, tweenToTarget]);
12061
+ (0, import_react23.useEffect)(() => {
12062
+ if (isInitialized && focusAncestryId && !hasOpenedInitialAncestry) {
12063
+ const ancestries = ancestryDataRef.current || [];
12064
+ const targetAncestry = ancestries.find((a) => String(a.ancestry_id) === String(focusAncestryId));
12065
+ if (targetAncestry) {
12066
+ setTimeout(() => {
12067
+ handleStartReadingAncestry(targetAncestry);
12068
+ setHasOpenedInitialAncestry(true);
12069
+ }, 300);
12070
+ } else {
12071
+ setHasOpenedInitialAncestry(true);
12072
+ }
12073
+ }
12074
+ }, [isInitialized, sceneVersion, focusAncestryId, hasOpenedInitialAncestry, handleStartReadingAncestry]);
11949
12075
  if (isLoading || status === "loading" || permissionStatus === "loading") {
11950
12076
  return /* @__PURE__ */ import_react23.default.createElement(LoadingScreen, null);
11951
12077
  }
@@ -12040,6 +12166,7 @@ function XViewScene({
12040
12166
  DescriptionReadModePanel,
12041
12167
  {
12042
12168
  key: readingMode.branchStack.length > 0 ? readingMode.branchStack[readingMode.branchStack.length - 1].branchId : readingMode.ancestry.ancestry_id,
12169
+ ancestryId: readingMode.ancestry.ancestry_id,
12043
12170
  title: (getReadModeDisplayContext == null ? void 0 : getReadModeDisplayContext.name) || readingMode.ancestry.name || "Ancestralidade",
12044
12171
  initialSectionId: readingMode.initialSectionId,
12045
12172
  description: getReadModeDisplayContext == null ? void 0 : getReadModeDisplayContext.description,
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/XViewScene.jsx
2
- import React23, { useCallback as useCallback3, useEffect as useEffect20, useRef as useRef17, useState as useState22, useMemo as useMemo12 } from "react";
3
- import { useRouter } from "next/navigation";
2
+ import React23, { useCallback as useCallback3, useEffect as useEffect21, useRef as useRef17, useState as useState22, useMemo as useMemo12 } from "react";
3
+ import { useRouter, useSearchParams } from "next/navigation";
4
4
  import { useSession } from "next-auth/react";
5
5
  import CryptoJS from "crypto-js";
6
6
  import * as THREE3 from "three";
@@ -87,6 +87,7 @@ function ContextMenu({
87
87
  const [menuView, setMenuView] = useState("main");
88
88
  const [selectedAncestry, setSelectedAncestry] = useState(null);
89
89
  const [versionSubMenu, setVersionSubMenu] = useState(null);
90
+ const [isLinkCopied, setIsLinkCopied] = useState(false);
90
91
  const ability = useMemo(() => defineAbilityFor(userRole), [userRole]);
91
92
  useEffect(() => {
92
93
  if (data.visible) {
@@ -223,6 +224,20 @@ function ContextMenu({
223
224
  return ((_a2 = node.version_node) == null ? void 0 : _a2.is_version) && String(node.version_node.parent_node) === String(data.nodeData.id);
224
225
  }
225
226
  ).sort((a, b) => getYearFromDate(b.Date) - getYearFromDate(a.Date));
227
+ const handleCopyLink = (e, nodeData) => {
228
+ e.stopPropagation();
229
+ const baseUrl = window.location.origin + window.location.pathname;
230
+ const fullUrl = `${baseUrl}?focus=${nodeData.id}`;
231
+ navigator.clipboard.writeText(fullUrl).then(() => {
232
+ setIsLinkCopied(true);
233
+ setTimeout(() => {
234
+ setIsLinkCopied(false);
235
+ onClose();
236
+ }, 1500);
237
+ }).catch((err) => {
238
+ console.error("Erro ao copiar link:", err);
239
+ });
240
+ };
226
241
  const renderMainView = () => {
227
242
  const hasVersions = computedVersions.length > 0;
228
243
  const canCreateVersion = ability.can("create", "Versioning");
@@ -231,7 +246,7 @@ function ContextMenu({
231
246
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_12px_1px_rgba(99,102,241,0.5)]" }), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ React.createElement("button", { onClick: () => {
232
247
  onFocusNode == null ? void 0 : onFocusNode(data.nodeData);
233
248
  onClose();
234
- }, className: baseButtonClass, title: "Focar na c\xE2mera" }, /* @__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: "10" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ React.createElement("span", null, "Focar neste Node")), ability.can("create", "Connection") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__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: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ React.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ React.createElement("span", null, "Conectar")), ability.can("create", "Node") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__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: "10" }), /* @__PURE__ */ React.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ React.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ React.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar 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: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2.8 2 .8s1.5-.3 2-.8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ React.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ React.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__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: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ React.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ React.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ React.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__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 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ React.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ React.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ React.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), ability.can("dismiss", "Node") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("button", { onClick: () => onDismissNode == null ? void 0 : onDismissNode(data.nodeData), className: baseButtonClass, title: "Remover da visualiza\xE7\xE3o" }, /* @__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: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ React.createElement("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), /* @__PURE__ */ React.createElement("path", { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), /* @__PURE__ */ React.createElement("line", { x1: "2", y1: "2", x2: "22", y2: "22" })), /* @__PURE__ */ React.createElement("span", null, "Dismiss")), /* @__PURE__ */ React.createElement("button", { onClick: () => onDismissOtherNodes == null ? void 0 : onDismissOtherNodes(data.nodeData), className: baseButtonClass, title: "Remover outros da visualiza\xE7\xE3o" }, /* @__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: "M3 7V5a2 2 0 0 1 2-2h2" }), /* @__PURE__ */ React.createElement("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ React.createElement("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }), /* @__PURE__ */ React.createElement("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" })), /* @__PURE__ */ React.createElement("span", null, "Dismiss other nodes"))), ability.can("delete", "Node") && /* @__PURE__ */ React.createElement("button", { onClick: () => onDeleteNode == null ? void 0 : onDeleteNode(data.nodeData), className: deleteButtonClass, title: "Excluir Node" }, /* @__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("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ React.createElement("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ React.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ React.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" })), /* @__PURE__ */ React.createElement("span", null, "Excluir Node"))));
249
+ }, className: baseButtonClass, title: "Focar na c\xE2mera" }, /* @__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: "10" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ React.createElement("span", null, "Focar neste Node")), ability.can("create", "Connection") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__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: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ React.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ React.createElement("span", null, "Conectar")), ability.can("create", "Node") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__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: "10" }), /* @__PURE__ */ React.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ React.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ React.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar 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: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2-.8 2 .8s1.5-.3 2-.8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ React.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ React.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__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: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ React.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ React.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ React.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__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 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ React.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ React.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ React.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: (e) => handleCopyLink(e, data.nodeData), className: baseButtonClass, title: "Copiar Link para Compartilhar" }, isLinkCopied ? /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#4ade80", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "20 6 9 17 4 12" })) : /* @__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: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ React.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ React.createElement("span", { className: isLinkCopied ? "text-green-400" : "" }, isLinkCopied ? "Copiado!" : "Copiar Link")), ability.can("dismiss", "Node") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("button", { onClick: () => onDismissNode == null ? void 0 : onDismissNode(data.nodeData), className: baseButtonClass, title: "Remover da visualiza\xE7\xE3o" }, /* @__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: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ React.createElement("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), /* @__PURE__ */ React.createElement("path", { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), /* @__PURE__ */ React.createElement("line", { x1: "2", y1: "2", x2: "22", y2: "22" })), /* @__PURE__ */ React.createElement("span", null, "Dismiss")), /* @__PURE__ */ React.createElement("button", { onClick: () => onDismissOtherNodes == null ? void 0 : onDismissOtherNodes(data.nodeData), className: baseButtonClass, title: "Remover outros da visualiza\xE7\xE3o" }, /* @__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: "M3 7V5a2 2 0 0 1 2-2h2" }), /* @__PURE__ */ React.createElement("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ React.createElement("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }), /* @__PURE__ */ React.createElement("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" })), /* @__PURE__ */ React.createElement("span", null, "Dismiss other nodes"))), ability.can("delete", "Node") && /* @__PURE__ */ React.createElement("button", { onClick: () => onDeleteNode == null ? void 0 : onDeleteNode(data.nodeData), className: deleteButtonClass, title: "Excluir Node" }, /* @__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("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ React.createElement("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ React.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ React.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" })), /* @__PURE__ */ React.createElement("span", null, "Excluir Node"))));
235
250
  };
236
251
  const renderVersionSubMenuView = () => {
237
252
  const group = versionSubMenu;
@@ -720,7 +735,7 @@ function XViewSidebar({
720
735
  }
721
736
 
722
737
  // src/components/AncestryRelationshipPanel.jsx
723
- import React8, { useState as useState8, useEffect as useEffect7, useRef as useRef7 } from "react";
738
+ import React8, { useState as useState8, useEffect as useEffect8, useRef as useRef7 } from "react";
724
739
 
725
740
  // node_modules/uuid/dist/esm-node/rng.js
726
741
  import crypto from "crypto";
@@ -4425,7 +4440,7 @@ function DescriptionDisplay({
4425
4440
  }
4426
4441
 
4427
4442
  // src/components/DescriptionReadModePanel.jsx
4428
- import React7, { useState as useState7, useMemo as useMemo6 } from "react";
4443
+ import React7, { useState as useState7, useMemo as useMemo6, useEffect as useEffect7 } from "react";
4429
4444
  import {
4430
4445
  FiArrowLeft,
4431
4446
  FiEdit2,
@@ -4437,7 +4452,9 @@ import {
4437
4452
  FiList as FiList2,
4438
4453
  FiAlignLeft,
4439
4454
  FiGitBranch,
4440
- FiFolder
4455
+ FiFolder,
4456
+ FiLink as FiLink3,
4457
+ FiCheck as FiCheck4
4441
4458
  } from "react-icons/fi";
4442
4459
  var findNodePath = (tree, targetNodeId, currentPath = []) => {
4443
4460
  var _a;
@@ -4501,6 +4518,8 @@ var ReadOnlyNodeItem = ({ nodeData, onViewSelect, highlightedPathIds = [], targe
4501
4518
  function DescriptionReadModePanel({
4502
4519
  title,
4503
4520
  description,
4521
+ ancestryId,
4522
+ // <-- NOVO: Prop recebida do XViewScene
4504
4523
  savedSections,
4505
4524
  onBack,
4506
4525
  onEdit,
@@ -4523,12 +4542,24 @@ function DescriptionReadModePanel({
4523
4542
  abstractionTree = null,
4524
4543
  onRenderAbstractionTree = null,
4525
4544
  initialShowAbstraction = false
4526
- // <--- NOVA PROP RECEBIDA
4527
4545
  }) {
4528
4546
  const [showProperties, setShowProperties] = useState7(false);
4529
4547
  const [showAbstraction, setShowAbstraction] = useState7(false);
4530
4548
  const [targetRenderNodeId, setTargetRenderNodeId] = useState7(null);
4531
- React7.useEffect(() => {
4549
+ const [isLinkCopied, setIsLinkCopied] = useState7(false);
4550
+ const handleCopyLink = (e) => {
4551
+ e.stopPropagation();
4552
+ if (!ancestryId) return;
4553
+ const baseUrl = window.location.origin + window.location.pathname;
4554
+ const fullUrl = `${baseUrl}?ancestry=${ancestryId}`;
4555
+ navigator.clipboard.writeText(fullUrl).then(() => {
4556
+ setIsLinkCopied(true);
4557
+ setTimeout(() => setIsLinkCopied(false), 2e3);
4558
+ }).catch((err) => {
4559
+ console.error("Erro ao copiar link:", err);
4560
+ });
4561
+ };
4562
+ useEffect7(() => {
4532
4563
  setShowAbstraction(initialShowAbstraction);
4533
4564
  if (initialShowAbstraction) {
4534
4565
  setShowProperties(false);
@@ -4617,7 +4648,15 @@ function DescriptionReadModePanel({
4617
4648
  title: "Voltar"
4618
4649
  },
4619
4650
  /* @__PURE__ */ React7.createElement(FiArrowLeft, { size: 16 })
4620
- ), /* @__PURE__ */ React7.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React7.createElement("p", { className: "text-xs/relaxed text-slate-400 uppercase tracking-wider font-semibold" }, showAbstraction ? "Tree de Abstra\xE7\xE3o" : showProperties ? "Propriedades Adicionais" : "Modo de Leitura"), /* @__PURE__ */ React7.createElement("h2", { className: "text-lg sm:text-xl font-semibold tracking-tight text-white truncate", title }, title || "Sem T\xEDtulo"))), /* @__PURE__ */ React7.createElement("div", { className: "flex items-center gap-2 flex-shrink-0" }, hasCustomProps && /* @__PURE__ */ React7.createElement(
4651
+ ), /* @__PURE__ */ React7.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React7.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React7.createElement("p", { className: "text-xs/relaxed text-slate-400 uppercase tracking-wider font-semibold" }, showAbstraction ? "Tree de Abstra\xE7\xE3o" : showProperties ? "Propriedades Adicionais" : "Modo de Leitura"), ancestryId && /* @__PURE__ */ React7.createElement(
4652
+ "button",
4653
+ {
4654
+ onClick: handleCopyLink,
4655
+ className: `p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-indigo-400"}`,
4656
+ title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Ancestralidade"
4657
+ },
4658
+ isLinkCopied ? /* @__PURE__ */ React7.createElement(FiCheck4, { size: 12 }) : /* @__PURE__ */ React7.createElement(FiLink3, { size: 12 })
4659
+ )), /* @__PURE__ */ React7.createElement("h2", { className: "text-lg sm:text-xl font-semibold tracking-tight text-white truncate", title }, title || "Sem T\xEDtulo"))), /* @__PURE__ */ React7.createElement("div", { className: "flex items-center gap-2 flex-shrink-0" }, hasCustomProps && /* @__PURE__ */ React7.createElement(
4621
4660
  "button",
4622
4661
  {
4623
4662
  onClick: handleToggleProperties,
@@ -4768,7 +4807,7 @@ function AncestryRelationshipPanel({
4768
4807
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState8(false);
4769
4808
  const [isReadMode, setIsReadMode] = useState8(false);
4770
4809
  const propsEndRef = useRef7(null);
4771
- useEffect7(() => {
4810
+ useEffect8(() => {
4772
4811
  setDescription((data == null ? void 0 : data.description) ?? "");
4773
4812
  setExistingSections((data == null ? void 0 : data.description_sections) || []);
4774
4813
  setCustomProps(extractCustomPropsFromNode(data || {}));
@@ -4908,13 +4947,13 @@ function AncestryRelationshipPanel({
4908
4947
  }
4909
4948
 
4910
4949
  // src/components/CreateAncestryPanel.jsx
4911
- import React10, { useState as useState10, useEffect as useEffect9, useMemo as useMemo8, useRef as useRef8, useCallback } from "react";
4950
+ import React10, { useState as useState10, useEffect as useEffect10, useMemo as useMemo8, useRef as useRef8, useCallback } from "react";
4912
4951
  import {
4913
4952
  FiEdit2 as FiEdit23,
4914
4953
  FiBookOpen as FiBookOpen2,
4915
4954
  FiFolder as FiFolder2,
4916
4955
  FiMousePointer,
4917
- FiCheck as FiCheck4,
4956
+ FiCheck as FiCheck5,
4918
4957
  FiLayers as FiLayers5,
4919
4958
  FiArrowLeft as FiArrowLeft2,
4920
4959
  FiChevronLeft as FiChevronLeft2,
@@ -4923,11 +4962,12 @@ import {
4923
4962
  FiCornerUpRight as FiCornerUpRight3,
4924
4963
  FiGitBranch as FiGitBranch2,
4925
4964
  FiPlus as FiPlus2,
4926
- FiLock
4965
+ FiLock,
4966
+ FiLink as FiLink4
4927
4967
  } from "react-icons/fi";
4928
4968
 
4929
4969
  // src/components/AncestryPickerModal.jsx
4930
- import React9, { useState as useState9, useMemo as useMemo7, useEffect as useEffect8 } from "react";
4970
+ import React9, { useState as useState9, useMemo as useMemo7, useEffect as useEffect9 } from "react";
4931
4971
  import { FiSearch as FiSearch3, FiLayers as FiLayers4, FiCornerUpRight as FiCornerUpRight2 } from "react-icons/fi";
4932
4972
  function AncestryPickerModal({
4933
4973
  isOpen,
@@ -4938,7 +4978,7 @@ function AncestryPickerModal({
4938
4978
  currentAncestryId
4939
4979
  }) {
4940
4980
  const [searchTerm, setSearchTerm] = useState9("");
4941
- useEffect8(() => {
4981
+ useEffect9(() => {
4942
4982
  if (!isOpen) return;
4943
4983
  const handleKeyDown = (e) => {
4944
4984
  if (e.key === "Escape") {
@@ -5159,6 +5199,20 @@ function CreateAncestryPanel({
5159
5199
  currentAncestryId
5160
5200
  } = ancestryMode;
5161
5201
  const [isSaving, setIsSaving] = useState10(false);
5202
+ const [isLinkCopied, setIsLinkCopied] = useState10(false);
5203
+ const handleCopyLink = (e) => {
5204
+ e.stopPropagation();
5205
+ if (!currentAncestryId || currentAncestryId === "temp_root" || currentAncestryId === "temp_creating") {
5206
+ alert("Salve a ancestralidade primeiro para gerar um link.");
5207
+ return;
5208
+ }
5209
+ const baseUrl = window.location.origin + window.location.pathname;
5210
+ const fullUrl = `${baseUrl}?ancestry=${currentAncestryId}`;
5211
+ navigator.clipboard.writeText(fullUrl).then(() => {
5212
+ setIsLinkCopied(true);
5213
+ setTimeout(() => setIsLinkCopied(false), 2e3);
5214
+ }).catch((err) => console.error("Erro ao copiar link:", err));
5215
+ };
5162
5216
  const [isPickerOpen, setIsPickerOpen] = useState10(false);
5163
5217
  const [customProps, setCustomProps] = useState10([]);
5164
5218
  const propsEndRef = useRef8(null);
@@ -5366,7 +5420,7 @@ function CreateAncestryPanel({
5366
5420
  setCustomProps(newProps);
5367
5421
  };
5368
5422
  const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
5369
- useEffect9(() => {
5423
+ useEffect10(() => {
5370
5424
  if (!isContextLinked) return;
5371
5425
  const handleKeyDown = (e) => {
5372
5426
  if (branchStack.length === 0) return;
@@ -5384,7 +5438,7 @@ function CreateAncestryPanel({
5384
5438
  window.addEventListener("keydown", handleKeyDown);
5385
5439
  return () => window.removeEventListener("keydown", handleKeyDown);
5386
5440
  }, [isContextLinked, branchStack]);
5387
- useEffect9(() => {
5441
+ useEffect10(() => {
5388
5442
  const ctx = getCurrentContext();
5389
5443
  let sourceObject = {};
5390
5444
  if (ctx) {
@@ -6057,7 +6111,7 @@ function CreateAncestryPanel({
6057
6111
  }
6058
6112
  }
6059
6113
  };
6060
- useEffect9(() => {
6114
+ useEffect10(() => {
6061
6115
  if (!description && existingSections.length === 0) return;
6062
6116
  const newRootTree = applyDescriptionToTree(ancestryMode.tree, description, existingSections);
6063
6117
  const currentTreeString = JSON.stringify(ancestryMode.tree);
@@ -6188,7 +6242,15 @@ function CreateAncestryPanel({
6188
6242
  onHighlightNode,
6189
6243
  onImageClick: handleImageClickFromText
6190
6244
  }
6191
- ) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0 flex-shrink-0" }), /* @__PURE__ */ React10.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4 flex-shrink-0" }, /* @__PURE__ */ React10.createElement("div", { className: "w-full" }, /* @__PURE__ */ React10.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React10.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__ */ React10.createElement("p", { className: "text-xs/relaxed text-slate-300" }, branchStack.length > 0 ? `Ramifica\xE7\xE3o (N\xEDvel ${branchStack.length})` : isEditMode ? "Editar Ancestralidade" : "Criar Ancestralidade")), /* @__PURE__ */ React10.createElement(
6245
+ ) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0 flex-shrink-0" }), /* @__PURE__ */ React10.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4 flex-shrink-0" }, /* @__PURE__ */ React10.createElement("div", { className: "w-full" }, /* @__PURE__ */ React10.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React10.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__ */ React10.createElement("p", { className: "text-xs/relaxed text-slate-300" }, branchStack.length > 0 ? `Ramifica\xE7\xE3o (N\xEDvel ${branchStack.length})` : isEditMode ? "Editar Ancestralidade" : "Criar Ancestralidade"), currentAncestryId && currentAncestryId !== "temp_creating" && currentAncestryId !== "temp_root" && /* @__PURE__ */ React10.createElement(
6246
+ "button",
6247
+ {
6248
+ onClick: handleCopyLink,
6249
+ className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-indigo-400"}`,
6250
+ title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Ancestralidade"
6251
+ },
6252
+ isLinkCopied ? /* @__PURE__ */ React10.createElement(FiCheck5, { size: 12 }) : /* @__PURE__ */ React10.createElement(FiLink4, { size: 12 })
6253
+ )), /* @__PURE__ */ React10.createElement(
6192
6254
  "input",
6193
6255
  {
6194
6256
  type: "text",
@@ -6323,7 +6385,7 @@ function CreateAncestryPanel({
6323
6385
  className: `p-1.5 rounded-md transition-colors ${isAddingNodes ? "bg-cyan-500 text-white shadow-lg shadow-cyan-500/30" : "bg-slate-700 text-slate-400 hover:text-white hover:bg-slate-600"}`,
6324
6386
  title: isAddingNodes ? "Concluir edi\xE7\xE3o da estrutura" : "Editar estrutura e adicionar nodes"
6325
6387
  },
6326
- isAddingNodes ? /* @__PURE__ */ React10.createElement(FiCheck4, { size: 14 }) : /* @__PURE__ */ React10.createElement(FiEdit23, { size: 14 })
6388
+ isAddingNodes ? /* @__PURE__ */ React10.createElement(FiCheck5, { size: 14 }) : /* @__PURE__ */ React10.createElement(FiEdit23, { size: 14 })
6327
6389
  ))), /* @__PURE__ */ React10.createElement("div", { className: "p-4 space-y-2" }, isAddingNodes && /* @__PURE__ */ React10.createElement("div", { className: "mb-3 p-2 rounded bg-cyan-900/20 border border-cyan-500/20 text-xs text-cyan-200 flex items-start gap-2" }, /* @__PURE__ */ React10.createElement(FiMousePointer, { className: "mt-0.5 flex-shrink-0" }), /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ React10.createElement("span", null, "Clique nos nodes do cen\xE1rio para adicion\xE1-los como filhos de: ", /* @__PURE__ */ React10.createElement("strong", { className: "text-white underline decoration-dashed" }, getSelectedParentName())), /* @__PURE__ */ React10.createElement("span", { className: "opacity-75" }, "Arraste e solte itens na lista para reorganizar a hierarquia."))), activeTree && /* @__PURE__ */ React10.createElement(
6328
6390
  NodeItem,
6329
6391
  {
@@ -6388,7 +6450,7 @@ function CreateAncestryPanel({
6388
6450
  className: `p-1.5 rounded-md transition-colors ${ancestryMode.isAddingAbstractionNodes ? "bg-purple-500 text-white shadow-lg shadow-purple-500/30" : "bg-slate-700 text-slate-400 hover:text-white hover:bg-slate-600"}`,
6389
6451
  title: "Editar estrutura da abstra\xE7\xE3o"
6390
6452
  },
6391
- ancestryMode.isAddingAbstractionNodes ? /* @__PURE__ */ React10.createElement(FiCheck4, { size: 14 }) : /* @__PURE__ */ React10.createElement(FiEdit23, { size: 14 })
6453
+ ancestryMode.isAddingAbstractionNodes ? /* @__PURE__ */ React10.createElement(FiCheck5, { size: 14 }) : /* @__PURE__ */ React10.createElement(FiEdit23, { size: 14 })
6392
6454
  ))),
6393
6455
  /* @__PURE__ */ React10.createElement("div", { className: "p-4 space-y-2" }, ancestryMode.isAddingAbstractionNodes && /* @__PURE__ */ React10.createElement("div", { className: "mb-3 p-2 rounded bg-purple-900/20 border border-purple-500/20 text-xs text-purple-200 flex items-start gap-2" }, /* @__PURE__ */ React10.createElement(FiMousePointer, { className: "mt-0.5 flex-shrink-0" }), /* @__PURE__ */ React10.createElement("span", null, "Clique nos nodes do cen\xE1rio para adicion\xE1-los. Arraste e solte para organizar a hierarquia.")), /* @__PURE__ */ React10.createElement(
6394
6456
  NodeItem,
@@ -6408,7 +6470,7 @@ function CreateAncestryPanel({
6408
6470
  isEditable: ancestryMode.isAddingAbstractionNodes
6409
6471
  }
6410
6472
  ))
6411
- ), branchStack.length === 0 && /* @__PURE__ */ React10.createElement("div", { className: "mt-3 flex items-center justify-end px-1" }, /* @__PURE__ */ React10.createElement("label", { className: "flex items-center gap-2 cursor-pointer group select-none" }, /* @__PURE__ */ React10.createElement("div", { className: `w-4 h-4 rounded border flex items-center justify-center transition-colors ${isPrivate ? "bg-indigo-500 border-indigo-500" : "border-slate-500 bg-transparent"}` }, isPrivate && /* @__PURE__ */ React10.createElement(FiCheck4, { size: 12, className: "text-white" })), /* @__PURE__ */ React10.createElement(
6473
+ ), branchStack.length === 0 && /* @__PURE__ */ React10.createElement("div", { className: "mt-3 flex items-center justify-end px-1" }, /* @__PURE__ */ React10.createElement("label", { className: "flex items-center gap-2 cursor-pointer group select-none" }, /* @__PURE__ */ React10.createElement("div", { className: `w-4 h-4 rounded border flex items-center justify-center transition-colors ${isPrivate ? "bg-indigo-500 border-indigo-500" : "border-slate-500 bg-transparent"}` }, isPrivate && /* @__PURE__ */ React10.createElement(FiCheck5, { size: 12, className: "text-white" })), /* @__PURE__ */ React10.createElement(
6412
6474
  "input",
6413
6475
  {
6414
6476
  type: "checkbox",
@@ -6456,7 +6518,7 @@ function CreateAncestryPanel({
6456
6518
  }
6457
6519
 
6458
6520
  // src/components/ImageViewer.jsx
6459
- import React11, { useState as useState11, useEffect as useEffect10, useLayoutEffect as useLayoutEffect2, useCallback as useCallback2 } from "react";
6521
+ import React11, { useState as useState11, useEffect as useEffect11, useLayoutEffect as useLayoutEffect2, useCallback as useCallback2 } from "react";
6460
6522
  import { FiX as FiX2, FiChevronLeft as FiChevronLeft3, FiChevronRight as FiChevronRight5 } from "react-icons/fi";
6461
6523
  function ImageViewer({ data, onClose }) {
6462
6524
  var _a;
@@ -6479,7 +6541,7 @@ function ImageViewer({ data, onClose }) {
6479
6541
  setCurrentIndex((prev) => (prev - 1 + images.length) % images.length);
6480
6542
  }
6481
6543
  }, [images.length]);
6482
- useEffect10(() => {
6544
+ useEffect11(() => {
6483
6545
  if (!visible) return;
6484
6546
  const handleKeyDown = (e) => {
6485
6547
  if (e.key === "Escape") {
@@ -6492,7 +6554,7 @@ function ImageViewer({ data, onClose }) {
6492
6554
  window.addEventListener("keydown", handleKeyDown, { capture: true });
6493
6555
  return () => window.removeEventListener("keydown", handleKeyDown, { capture: true });
6494
6556
  }, [visible, onClose, handleNext, handlePrev]);
6495
- useEffect10(() => {
6557
+ useEffect11(() => {
6496
6558
  if (!visible || images.length === 0) return;
6497
6559
  const curr = images[currentIndex];
6498
6560
  const src = curr == null ? void 0 : curr.value;
@@ -6579,12 +6641,12 @@ function ImageViewer({ data, onClose }) {
6579
6641
  }
6580
6642
 
6581
6643
  // src/components/InSceneCreationForm.jsx
6582
- import React13, { useState as useState13, useEffect as useEffect12, useRef as useRef10 } from "react";
6644
+ import React13, { useState as useState13, useEffect as useEffect13, useRef as useRef10 } from "react";
6583
6645
 
6584
6646
  // src/components/ColorPicker.jsx
6585
- import React12, { useState as useState12, useEffect as useEffect11, useRef as useRef9 } from "react";
6647
+ import React12, { useState as useState12, useEffect as useEffect12, useRef as useRef9 } from "react";
6586
6648
  import { HexColorPicker } from "react-colorful";
6587
- import { FiHash, FiCheck as FiCheck5 } from "react-icons/fi";
6649
+ import { FiHash, FiCheck as FiCheck6 } from "react-icons/fi";
6588
6650
  var PRESET_COLORS = [
6589
6651
  "#ef4444",
6590
6652
  "#f97316",
@@ -6605,7 +6667,7 @@ var PRESET_COLORS = [
6605
6667
  function ColorPicker({ color, onChange, disabled }) {
6606
6668
  const [isOpen, setIsOpen] = useState12(false);
6607
6669
  const popoverRef = useRef9(null);
6608
- useEffect11(() => {
6670
+ useEffect12(() => {
6609
6671
  const handleClickOutside = (event) => {
6610
6672
  if (popoverRef.current && !popoverRef.current.contains(event.target)) {
6611
6673
  setIsOpen(false);
@@ -6653,7 +6715,7 @@ function ColorPicker({ color, onChange, disabled }) {
6653
6715
  style: { backgroundColor: preset },
6654
6716
  title: preset
6655
6717
  },
6656
- color.toLowerCase() === preset.toLowerCase() && /* @__PURE__ */ React12.createElement(FiCheck5, { className: `drop-shadow-md ${["#ffffff", "#4df5cb", "#84cc16", "#f59e0b"].includes(preset) ? "text-black" : "text-white"}`, size: 12 })
6718
+ color.toLowerCase() === preset.toLowerCase() && /* @__PURE__ */ React12.createElement(FiCheck6, { className: `drop-shadow-md ${["#ffffff", "#4df5cb", "#84cc16", "#f59e0b"].includes(preset) ? "text-black" : "text-white"}`, size: 12 })
6657
6719
  )))), /* @__PURE__ */ React12.createElement("style", null, `
6658
6720
  .custom-react-colorful .react-colorful {
6659
6721
  width: 100%;
@@ -6678,7 +6740,7 @@ function ColorPicker({ color, onChange, disabled }) {
6678
6740
  }
6679
6741
 
6680
6742
  // src/components/InSceneCreationForm.jsx
6681
- import { FiPlus as FiPlus3, FiMaximize2, FiX as FiX3, FiCheck as FiCheck6, FiEdit2 as FiEdit24, FiSun } from "react-icons/fi";
6743
+ import { FiPlus as FiPlus3, FiMaximize2, FiX as FiX3, FiCheck as FiCheck7, FiEdit2 as FiEdit24, FiSun } from "react-icons/fi";
6682
6744
  function InSceneCreationForm({
6683
6745
  onSave,
6684
6746
  onCancel,
@@ -6713,14 +6775,14 @@ function InSceneCreationForm({
6713
6775
  const [selectedImageUrl, setSelectedImageUrl] = useState13(null);
6714
6776
  const propsEndRef = useRef10(null);
6715
6777
  const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
6716
- useEffect12(() => {
6778
+ useEffect13(() => {
6717
6779
  if (!hasImages && useImageAsTexture) {
6718
6780
  setUseImageAsTexture(false);
6719
6781
  setSelectedImageUrl(null);
6720
6782
  onImageChange == null ? void 0 : onImageChange(false, null);
6721
6783
  }
6722
6784
  }, [hasImages, useImageAsTexture, onImageChange]);
6723
- useEffect12(() => {
6785
+ useEffect13(() => {
6724
6786
  let result = [];
6725
6787
  if (typeInput.trim() === "") {
6726
6788
  result = existingTypes.filter((t) => !types.includes(t));
@@ -6742,7 +6804,7 @@ function InSceneCreationForm({
6742
6804
  }
6743
6805
  setFilteredTypes(result);
6744
6806
  }, [typeInput, existingTypes, types, sourceTypes]);
6745
- useEffect12(() => {
6807
+ useEffect13(() => {
6746
6808
  if (initialColor) {
6747
6809
  onColorChange == null ? void 0 : onColorChange(initialColor);
6748
6810
  }
@@ -6941,10 +7003,10 @@ function InSceneCreationForm({
6941
7003
  onClick: () => handleSizeChange(s),
6942
7004
  className: "flex items-center gap-2 group cursor-pointer focus:outline-none"
6943
7005
  },
6944
- /* @__PURE__ */ React13.createElement("div", { className: `w-4 h-4 rounded-[4px] border flex items-center justify-center transition-all duration-200 ${isSelected ? "bg-indigo-500 border-indigo-500 shadow-[0_0_10px_rgba(99,102,241,0.4)]" : "border-slate-600 bg-transparent group-hover:border-slate-500"}` }, isSelected && /* @__PURE__ */ React13.createElement(FiCheck6, { size: 12, className: "text-white" })),
7006
+ /* @__PURE__ */ React13.createElement("div", { className: `w-4 h-4 rounded-[4px] border flex items-center justify-center transition-all duration-200 ${isSelected ? "bg-indigo-500 border-indigo-500 shadow-[0_0_10px_rgba(99,102,241,0.4)]" : "border-slate-600 bg-transparent group-hover:border-slate-500"}` }, isSelected && /* @__PURE__ */ React13.createElement(FiCheck7, { size: 12, className: "text-white" })),
6945
7007
  /* @__PURE__ */ React13.createElement("span", { className: `text-sm capitalize transition-colors ${isSelected ? "text-white font-medium" : "text-slate-400 group-hover:text-slate-300"}` }, s)
6946
7008
  );
6947
- }))), /* @__PURE__ */ React13.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React13.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React13.createElement("label", { className: "text-xs text-slate-300" }, "Cor e Brilho"), hasImages && /* @__PURE__ */ React13.createElement("label", { className: "flex items-center gap-2 cursor-pointer group" }, /* @__PURE__ */ React13.createElement("div", { className: `w-4 h-4 rounded border flex items-center justify-center transition-colors ${useImageAsTexture ? "bg-indigo-500 border-indigo-500" : "border-slate-500 bg-transparent"}` }, useImageAsTexture && /* @__PURE__ */ React13.createElement(FiCheck6, { size: 12, className: "text-white" })), /* @__PURE__ */ React13.createElement(
7009
+ }))), /* @__PURE__ */ React13.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React13.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React13.createElement("label", { className: "text-xs text-slate-300" }, "Cor e Brilho"), hasImages && /* @__PURE__ */ React13.createElement("label", { className: "flex items-center gap-2 cursor-pointer group" }, /* @__PURE__ */ React13.createElement("div", { className: `w-4 h-4 rounded border flex items-center justify-center transition-colors ${useImageAsTexture ? "bg-indigo-500 border-indigo-500" : "border-slate-500 bg-transparent"}` }, useImageAsTexture && /* @__PURE__ */ React13.createElement(FiCheck7, { size: 12, className: "text-white" })), /* @__PURE__ */ React13.createElement(
6948
7010
  "input",
6949
7011
  {
6950
7012
  type: "checkbox",
@@ -7004,8 +7066,8 @@ function InSceneCreationForm({
7004
7066
  }
7005
7067
 
7006
7068
  // src/components/InSceneVersionForm.jsx
7007
- import React14, { useState as useState14, useEffect as useEffect13, useRef as useRef11 } from "react";
7008
- import { FiPlus as FiPlus4, FiMaximize2 as FiMaximize22, FiCheck as FiCheck7, FiEdit2 as FiEdit25 } from "react-icons/fi";
7069
+ import React14, { useState as useState14, useEffect as useEffect14, useRef as useRef11 } from "react";
7070
+ import { FiPlus as FiPlus4, FiMaximize2 as FiMaximize22, FiCheck as FiCheck8, FiEdit2 as FiEdit25 } from "react-icons/fi";
7009
7071
  function InSceneVersionForm({
7010
7072
  onSave,
7011
7073
  onCancel,
@@ -7031,14 +7093,14 @@ function InSceneVersionForm({
7031
7093
  const [useImageAsTexture, setUseImageAsTexture] = useState14(false);
7032
7094
  const [selectedImageUrl, setSelectedImageUrl] = useState14(null);
7033
7095
  const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
7034
- useEffect13(() => {
7096
+ useEffect14(() => {
7035
7097
  if (!hasImages && useImageAsTexture) {
7036
7098
  setUseImageAsTexture(false);
7037
7099
  setSelectedImageUrl(null);
7038
7100
  onImageChange == null ? void 0 : onImageChange(false, null);
7039
7101
  }
7040
7102
  }, [hasImages, useImageAsTexture, onImageChange]);
7041
- useEffect13(() => {
7103
+ useEffect14(() => {
7042
7104
  if (fixedColor) {
7043
7105
  }
7044
7106
  }, [fixedColor]);
@@ -7178,10 +7240,10 @@ function InSceneVersionForm({
7178
7240
  onClick: () => handleSizeChange(s),
7179
7241
  className: "flex items-center gap-2 group cursor-pointer focus:outline-none"
7180
7242
  },
7181
- /* @__PURE__ */ React14.createElement("div", { className: `w-4 h-4 rounded-[4px] border flex items-center justify-center transition-all duration-200 ${isSelected ? "bg-indigo-500 border-indigo-500 shadow-[0_0_10px_rgba(99,102,241,0.4)]" : "border-slate-600 bg-transparent group-hover:border-slate-500"}` }, isSelected && /* @__PURE__ */ React14.createElement(FiCheck7, { size: 12, className: "text-white" })),
7243
+ /* @__PURE__ */ React14.createElement("div", { className: `w-4 h-4 rounded-[4px] border flex items-center justify-center transition-all duration-200 ${isSelected ? "bg-indigo-500 border-indigo-500 shadow-[0_0_10px_rgba(99,102,241,0.4)]" : "border-slate-600 bg-transparent group-hover:border-slate-500"}` }, isSelected && /* @__PURE__ */ React14.createElement(FiCheck8, { size: 12, className: "text-white" })),
7182
7244
  /* @__PURE__ */ React14.createElement("span", { className: `text-sm capitalize transition-colors ${isSelected ? "text-white font-medium" : "text-slate-400 group-hover:text-slate-300"}` }, s)
7183
7245
  );
7184
- }))), /* @__PURE__ */ React14.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React14.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React14.createElement("label", { className: "text-xs text-slate-300" }, "Cor do Node (Fixa)"), hasImages && /* @__PURE__ */ React14.createElement("label", { className: "flex items-center gap-2 cursor-pointer group" }, /* @__PURE__ */ React14.createElement("div", { className: `w-4 h-4 rounded border flex items-center justify-center transition-colors ${useImageAsTexture ? "bg-indigo-500 border-indigo-500" : "border-slate-500 bg-transparent"}` }, useImageAsTexture && /* @__PURE__ */ React14.createElement(FiCheck7, { size: 12, className: "text-white" })), /* @__PURE__ */ React14.createElement(
7246
+ }))), /* @__PURE__ */ React14.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React14.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React14.createElement("label", { className: "text-xs text-slate-300" }, "Cor do Node (Fixa)"), hasImages && /* @__PURE__ */ React14.createElement("label", { className: "flex items-center gap-2 cursor-pointer group" }, /* @__PURE__ */ React14.createElement("div", { className: `w-4 h-4 rounded border flex items-center justify-center transition-colors ${useImageAsTexture ? "bg-indigo-500 border-indigo-500" : "border-slate-500 bg-transparent"}` }, useImageAsTexture && /* @__PURE__ */ React14.createElement(FiCheck8, { size: 12, className: "text-white" })), /* @__PURE__ */ React14.createElement(
7185
7247
  "input",
7186
7248
  {
7187
7249
  type: "checkbox",
@@ -7222,8 +7284,8 @@ function InSceneVersionForm({
7222
7284
  }
7223
7285
 
7224
7286
  // src/components/NodeDetailsPanel.jsx
7225
- import React15, { useState as useState15, useEffect as useEffect14, useRef as useRef12 } from "react";
7226
- import { FiPlus as FiPlus5, FiMaximize2 as FiMaximize23, FiX as FiX4, FiCheck as FiCheck8, FiImage as FiImage3, FiEdit2 as FiEdit26, FiLoader as FiLoader2, FiBookOpen as FiBookOpen3, FiSun as FiSun2 } from "react-icons/fi";
7287
+ import React15, { useState as useState15, useEffect as useEffect15, useRef as useRef12 } from "react";
7288
+ import { FiPlus as FiPlus5, FiMaximize2 as FiMaximize23, FiX as FiX4, FiCheck as FiCheck9, FiImage as FiImage3, FiEdit2 as FiEdit26, FiLoader as FiLoader2, FiBookOpen as FiBookOpen3, FiSun as FiSun2, FiLink as FiLink5 } from "react-icons/fi";
7227
7289
  function NodeDetailsPanel({
7228
7290
  node,
7229
7291
  onClose,
@@ -7242,7 +7304,6 @@ function NodeDetailsPanel({
7242
7304
  onIntensityChange,
7243
7305
  onUploadFile,
7244
7306
  userRole
7245
- // <--- ADIÇÃO: Recebendo a prop userRole
7246
7307
  }) {
7247
7308
  const [name, setName] = useState15((node == null ? void 0 : node.name) ?? "");
7248
7309
  const [types, setTypes] = useState15([]);
@@ -7258,6 +7319,7 @@ function NodeDetailsPanel({
7258
7319
  const [isReadMode, setIsReadMode] = useState15(false);
7259
7320
  const [existingSections, setExistingSections] = useState15((node == null ? void 0 : node.description_sections) || []);
7260
7321
  const [isSaving, setIsSaving] = useState15(false);
7322
+ const [isLinkCopied, setIsLinkCopied] = useState15(false);
7261
7323
  const [useImageAsTexture, setUseImageAsTexture] = useState15(() => {
7262
7324
  if ((node == null ? void 0 : node.useImageAsTexture) === "true") return true;
7263
7325
  if ((node == null ? void 0 : node.useImageAsTexture) === "false") return false;
@@ -7273,7 +7335,7 @@ function NodeDetailsPanel({
7273
7335
  onOpenImageViewer([{ name: name2 || "Imagem", value: url }], 0);
7274
7336
  }
7275
7337
  };
7276
- useEffect14(() => {
7338
+ useEffect15(() => {
7277
7339
  if ((node == null ? void 0 : node.id) !== prevNodeIdRef.current) {
7278
7340
  prevNodeIdRef.current = node == null ? void 0 : node.id;
7279
7341
  setName((node == null ? void 0 : node.name) ?? "");
@@ -7295,13 +7357,13 @@ function NodeDetailsPanel({
7295
7357
  }
7296
7358
  }, [node]);
7297
7359
  const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
7298
- useEffect14(() => {
7360
+ useEffect15(() => {
7299
7361
  if (!hasImages && useImageAsTexture) {
7300
7362
  setUseImageAsTexture(false);
7301
7363
  setSelectedImageUrl(null);
7302
7364
  }
7303
7365
  }, [hasImages, useImageAsTexture]);
7304
- useEffect14(() => {
7366
+ useEffect15(() => {
7305
7367
  if (typeInput.trim() === "") {
7306
7368
  setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
7307
7369
  } else {
@@ -7319,6 +7381,17 @@ function NodeDetailsPanel({
7319
7381
  onIntensityChange == null ? void 0 : onIntensityChange(node.id, val);
7320
7382
  onDataUpdate == null ? void 0 : onDataUpdate({ ...node, intensity: val });
7321
7383
  };
7384
+ const handleCopyLink = () => {
7385
+ if (!(node == null ? void 0 : node.id)) return;
7386
+ const baseUrl = window.location.origin + window.location.pathname;
7387
+ const fullUrl = `${baseUrl}?focus=${node.id}`;
7388
+ navigator.clipboard.writeText(fullUrl).then(() => {
7389
+ setIsLinkCopied(true);
7390
+ setTimeout(() => setIsLinkCopied(false), 2e3);
7391
+ }).catch((err) => {
7392
+ console.error("Erro ao copiar link:", err);
7393
+ });
7394
+ };
7322
7395
  const swallow = (e) => e.stopPropagation();
7323
7396
  const handleNameChange = (e) => {
7324
7397
  const v = e.target.value;
@@ -7492,7 +7565,15 @@ function NodeDetailsPanel({
7492
7565
  onMentionClick,
7493
7566
  onImageClick: handleImageClickFromText
7494
7567
  }
7495
- ) : /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ React15.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React15.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__ */ React15.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node")), /* @__PURE__ */ React15.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ React15.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__ */ React15.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ React15.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__ */ React15.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__ */ React15.createElement(
7568
+ ) : /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ React15.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React15.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__ */ React15.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ React15.createElement(
7569
+ "button",
7570
+ {
7571
+ onClick: handleCopyLink,
7572
+ className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-indigo-400"}`,
7573
+ title: isLinkCopied ? "Link Copiado!" : "Copiar link para este Node"
7574
+ },
7575
+ isLinkCopied ? /* @__PURE__ */ React15.createElement(FiCheck9, { size: 12 }) : /* @__PURE__ */ React15.createElement(FiLink5, { size: 12 })
7576
+ )), /* @__PURE__ */ React15.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ React15.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__ */ React15.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ React15.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__ */ React15.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__ */ React15.createElement(
7496
7577
  "button",
7497
7578
  {
7498
7579
  type: "button",
@@ -7587,10 +7668,10 @@ function NodeDetailsPanel({
7587
7668
  onClick: () => canEdit && handleSizeChange(s),
7588
7669
  className: `flex items-center gap-2 group focus:outline-none ${canEdit ? "cursor-pointer" : "cursor-default opacity-80"}`
7589
7670
  },
7590
- /* @__PURE__ */ React15.createElement("div", { className: `w-4 h-4 rounded-[4px] border flex items-center justify-center transition-all duration-200 ${isSelected ? "bg-indigo-500 border-indigo-500 shadow-[0_0_10px_rgba(99,102,241,0.4)]" : "border-slate-600 bg-transparent " + (canEdit ? "group-hover:border-slate-500" : "")}` }, isSelected && /* @__PURE__ */ React15.createElement(FiCheck8, { size: 12, className: "text-white" })),
7671
+ /* @__PURE__ */ React15.createElement("div", { className: `w-4 h-4 rounded-[4px] border flex items-center justify-center transition-all duration-200 ${isSelected ? "bg-indigo-500 border-indigo-500 shadow-[0_0_10px_rgba(99,102,241,0.4)]" : "border-slate-600 bg-transparent " + (canEdit ? "group-hover:border-slate-500" : "")}` }, isSelected && /* @__PURE__ */ React15.createElement(FiCheck9, { size: 12, className: "text-white" })),
7591
7672
  /* @__PURE__ */ React15.createElement("span", { className: `text-sm capitalize transition-colors ${isSelected ? "text-white font-medium" : "text-slate-400 " + (canEdit ? "group-hover:text-slate-300" : "")}` }, s)
7592
7673
  );
7593
- }))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Cor e Brilho"), canEdit && hasImages && /* @__PURE__ */ React15.createElement("label", { className: "flex items-center gap-2 cursor-pointer group" }, /* @__PURE__ */ React15.createElement("div", { className: `w-4 h-4 rounded border flex items-center justify-center transition-colors ${useImageAsTexture ? "bg-indigo-500 border-indigo-500" : "border-slate-500 bg-transparent"}` }, useImageAsTexture && /* @__PURE__ */ React15.createElement(FiCheck8, { size: 12, className: "text-white" })), /* @__PURE__ */ React15.createElement(
7674
+ }))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Cor e Brilho"), canEdit && hasImages && /* @__PURE__ */ React15.createElement("label", { className: "flex items-center gap-2 cursor-pointer group" }, /* @__PURE__ */ React15.createElement("div", { className: `w-4 h-4 rounded border flex items-center justify-center transition-colors ${useImageAsTexture ? "bg-indigo-500 border-indigo-500" : "border-slate-500 bg-transparent"}` }, useImageAsTexture && /* @__PURE__ */ React15.createElement(FiCheck9, { size: 12, className: "text-white" })), /* @__PURE__ */ React15.createElement(
7594
7675
  "input",
7595
7676
  {
7596
7677
  type: "checkbox",
@@ -7645,8 +7726,7 @@ function NodeDetailsPanel({
7645
7726
  isSaving && /* @__PURE__ */ React15.createElement(FiLoader2, { className: "animate-spin" }),
7646
7727
  isSaving ? "Salvando..." : "Salvar"
7647
7728
  )))
7648
- ), isDescriptionModalOpen && canEdit && // MODIFICAÇÃO: Modal de edição só abre se canEdit
7649
- /* @__PURE__ */ React15.createElement(
7729
+ ), isDescriptionModalOpen && canEdit && /* @__PURE__ */ React15.createElement(
7650
7730
  DescriptionEditModal,
7651
7731
  {
7652
7732
  isOpen: isDescriptionModalOpen,
@@ -7666,7 +7746,7 @@ function NodeDetailsPanel({
7666
7746
  }
7667
7747
 
7668
7748
  // src/components/MultiNodeContextMenu.jsx
7669
- import React16, { useLayoutEffect as useLayoutEffect3, useRef as useRef13, useState as useState16, useEffect as useEffect15 } from "react";
7749
+ import React16, { useLayoutEffect as useLayoutEffect3, useRef as useRef13, useState as useState16, useEffect as useEffect16 } from "react";
7670
7750
  function MultiNodeContextMenu({
7671
7751
  data,
7672
7752
  userRole,
@@ -7693,7 +7773,7 @@ function MultiNodeContextMenu({
7693
7773
  if (top + h + 8 > vh) top = Math.max(8, vh - h - 8);
7694
7774
  setMenuPos({ left, top });
7695
7775
  }, [data]);
7696
- useEffect15(() => {
7776
+ useEffect16(() => {
7697
7777
  if (!data.visible) return;
7698
7778
  const handleClickOutside = (e) => {
7699
7779
  if (menuRef.current && !menuRef.current.contains(e.target)) onClose();
@@ -7726,7 +7806,7 @@ function MultiNodeContextMenu({
7726
7806
  }
7727
7807
 
7728
7808
  // src/components/RelationshipDetailsPanel.jsx
7729
- import React17, { useState as useState17, useEffect as useEffect16, useRef as useRef14, useMemo as useMemo9 } from "react";
7809
+ import React17, { useState as useState17, useEffect as useEffect17, useRef as useRef14, useMemo as useMemo9 } from "react";
7730
7810
  import { FiPlus as FiPlus6, FiEdit2 as FiEdit27, FiLoader as FiLoader3, FiBookOpen as FiBookOpen4 } from "react-icons/fi";
7731
7811
  function RelationshipDetailsPanel({
7732
7812
  link,
@@ -7754,7 +7834,7 @@ function RelationshipDetailsPanel({
7754
7834
  const ability = defineAbilityFor(userRole);
7755
7835
  return ability.can("update", "Connection");
7756
7836
  }, [userRole]);
7757
- useEffect16(() => {
7837
+ useEffect17(() => {
7758
7838
  setName((link == null ? void 0 : link.name) ?? "");
7759
7839
  setDescription((link == null ? void 0 : link.description) ?? "");
7760
7840
  setExistingSections((link == null ? void 0 : link.description_sections) || []);
@@ -7951,7 +8031,7 @@ function RelationshipDetailsPanel({
7951
8031
  }
7952
8032
 
7953
8033
  // src/components/RelationshipContextMenu.jsx
7954
- import React18, { useLayoutEffect as useLayoutEffect4, useRef as useRef15, useState as useState18, useEffect as useEffect17, useMemo as useMemo10 } from "react";
8034
+ import React18, { useLayoutEffect as useLayoutEffect4, useRef as useRef15, useState as useState18, useEffect as useEffect18, useMemo as useMemo10 } from "react";
7955
8035
  function RelationshipContextMenu({
7956
8036
  data,
7957
8037
  userRole,
@@ -7992,7 +8072,7 @@ function RelationshipContextMenu({
7992
8072
  if (top + h + 8 > vh) top = Math.max(8, vh - h - 8);
7993
8073
  setMenuPos({ left, top });
7994
8074
  }, [data]);
7995
- useEffect17(() => {
8075
+ useEffect18(() => {
7996
8076
  if (!data.visible) return;
7997
8077
  const handleClickOutside = (e) => {
7998
8078
  if (menuRef.current && !menuRef.current.contains(e.target)) onClose();
@@ -8165,7 +8245,7 @@ function LoadingScreen() {
8165
8245
  }
8166
8246
 
8167
8247
  // src/components/ImportParentFileModal.jsx
8168
- import React20, { useEffect as useEffect18, useState as useState19 } from "react";
8248
+ import React20, { useEffect as useEffect19, useState as useState19 } from "react";
8169
8249
  function ImportParentFileModal({
8170
8250
  isOpen,
8171
8251
  onClose,
@@ -8181,7 +8261,7 @@ function ImportParentFileModal({
8181
8261
  const [availableViews, setAvailableViews] = useState19([]);
8182
8262
  const [selectedItem, setSelectedItem] = useState19(null);
8183
8263
  const [isLoading, setIsLoading] = useState19(false);
8184
- useEffect18(() => {
8264
+ useEffect19(() => {
8185
8265
  if (isOpen && session && onFetchAvailableFiles) {
8186
8266
  const fetchData = async () => {
8187
8267
  setIsLoading(true);
@@ -8217,7 +8297,7 @@ function ImportParentFileModal({
8217
8297
  fetchData();
8218
8298
  }
8219
8299
  }, [isOpen, session, parentDbs, onFetchAvailableFiles, currentViewName]);
8220
- useEffect18(() => {
8300
+ useEffect19(() => {
8221
8301
  setSelectedItem(null);
8222
8302
  }, [activeTab]);
8223
8303
  if (!isOpen) {
@@ -8402,7 +8482,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
8402
8482
  }
8403
8483
 
8404
8484
  // src/components/AncestryBoard.jsx
8405
- import React22, { useState as useState21, useMemo as useMemo11, useEffect as useEffect19, useRef as useRef16 } from "react";
8485
+ import React22, { useState as useState21, useMemo as useMemo11, useEffect as useEffect20, useRef as useRef16 } from "react";
8406
8486
  import {
8407
8487
  FiSearch as FiSearch4,
8408
8488
  FiLayers as FiLayers6,
@@ -8444,7 +8524,7 @@ var GroupItem = ({
8444
8524
  textarea.style.height = `${textarea.scrollHeight}px`;
8445
8525
  }
8446
8526
  };
8447
- useEffect19(() => {
8527
+ useEffect20(() => {
8448
8528
  adjustHeight();
8449
8529
  }, [group.text]);
8450
8530
  return /* @__PURE__ */ React22.createElement("div", { className: "flex flex-col gap-2 mb-3 pl-3 border-l border-white/10 relative group/item animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React22.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ React22.createElement("div", { className: `
@@ -8587,7 +8667,7 @@ function AncestryBoard({
8587
8667
  const canEdit = useMemo11(() => {
8588
8668
  return userRole !== "viewer";
8589
8669
  }, [userRole]);
8590
- useEffect19(() => {
8670
+ useEffect20(() => {
8591
8671
  if (initialGroups && !isLoaded) {
8592
8672
  setGroups(initialGroups);
8593
8673
  setIsLoaded(true);
@@ -8618,7 +8698,7 @@ function AncestryBoard({
8618
8698
  children: sanitizeGroups(g.children || [])
8619
8699
  }));
8620
8700
  };
8621
- useEffect19(() => {
8701
+ useEffect20(() => {
8622
8702
  if (!isLoaded || !onSave) return;
8623
8703
  const timeoutId = setTimeout(async () => {
8624
8704
  setSaveStatus("saving");
@@ -8636,7 +8716,7 @@ function AncestryBoard({
8636
8716
  }, 3e3);
8637
8717
  return () => clearTimeout(timeoutId);
8638
8718
  }, [groups, isLoaded, onSave]);
8639
- useEffect19(() => {
8719
+ useEffect20(() => {
8640
8720
  if (!isOpen) return;
8641
8721
  const handleKeyDown = (e) => {
8642
8722
  if (e.key === "Escape") {
@@ -8971,6 +9051,9 @@ function XViewScene({
8971
9051
  var _a, _b, _c, _d, _e, _f;
8972
9052
  const { data: session, status } = useSession();
8973
9053
  const router = useRouter();
9054
+ const searchParams = useSearchParams();
9055
+ const focusNodeId = searchParams == null ? void 0 : searchParams.get("focus");
9056
+ const focusAncestryId = searchParams == null ? void 0 : searchParams.get("ancestry");
8974
9057
  const viewParams = useMemo12(() => {
8975
9058
  if (encryptedConfig) {
8976
9059
  const data = decryptData(encryptedConfig);
@@ -8980,7 +9063,7 @@ function XViewScene({
8980
9063
  }
8981
9064
  return null;
8982
9065
  }, [encryptedConfig, session]);
8983
- useEffect20(() => {
9066
+ useEffect21(() => {
8984
9067
  async function verifyPermission() {
8985
9068
  if (!viewParams || !session || !check_user_permission) return;
8986
9069
  const { id, type, owner_id } = viewParams;
@@ -9002,6 +9085,9 @@ function XViewScene({
9002
9085
  }
9003
9086
  if (viewParams && session && status === "authenticated") {
9004
9087
  verifyPermission();
9088
+ } else if (status === "unauthenticated") {
9089
+ setPermissionStatus("denied");
9090
+ setIsLoading(false);
9005
9091
  }
9006
9092
  }, [viewParams, session, status, check_user_permission]);
9007
9093
  const sceneConfigId = useMemo12(() => (viewParams == null ? void 0 : viewParams.id) || null, [viewParams]);
@@ -9037,6 +9123,8 @@ function XViewScene({
9037
9123
  const [relationshipMenu, setRelationshipMenu] = useState22({ visible: false, x: 0, y: 0, linkObject: null });
9038
9124
  const [creationMode, setCreationMode] = useState22({ isActive: false, sourceNodeData: null });
9039
9125
  const [versionMode, setVersionMode] = useState22({ isActive: false, sourceNodeData: null });
9126
+ const [hasFocusedInitial, setHasFocusedInitial] = useState22(false);
9127
+ const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = useState22(false);
9040
9128
  const [ancestryMode, setAncestryMode] = useState22({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", ancestryDescriptionSections: [], isAddingNodes: false });
9041
9129
  const [readingMode, setReadingMode] = useState22({
9042
9130
  isActive: false,
@@ -9098,10 +9186,10 @@ function XViewScene({
9098
9186
  lastDescriptionLength: 0,
9099
9187
  highlightedNodeId: null
9100
9188
  });
9101
- useEffect20(() => {
9189
+ useEffect21(() => {
9102
9190
  stateRef.current.ancestry = ancestryMode;
9103
9191
  }, [ancestryMode]);
9104
- useEffect20(() => {
9192
+ useEffect21(() => {
9105
9193
  var _a2;
9106
9194
  if (!isInitialized) return;
9107
9195
  const map = /* @__PURE__ */ new Map();
@@ -9602,7 +9690,7 @@ function XViewScene({
9602
9690
  }
9603
9691
  });
9604
9692
  }, []);
9605
- useEffect20(() => {
9693
+ useEffect21(() => {
9606
9694
  async function fetchAllData(configPath, ownerId2) {
9607
9695
  var _a2, _b2;
9608
9696
  if (!get_scene_view_data) {
@@ -9617,12 +9705,23 @@ function XViewScene({
9617
9705
  const boardPromise = get_ancestry_board_action && session ? get_ancestry_board_action(configPath, sceneType, session, ownerId2) : Promise.resolve({ success: false, data: [] });
9618
9706
  const [sceneResponse, boardResponse] = await Promise.all([scenePromise, boardPromise]);
9619
9707
  if ((sceneResponse == null ? void 0 : sceneResponse.success) && ((_a2 = sceneResponse.data) == null ? void 0 : _a2.scene) && ((_b2 = sceneResponse.data) == null ? void 0 : _b2.parent)) {
9708
+ if (focusNodeId) {
9709
+ let targetNode = sceneResponse.data.scene.nodes.find((n) => String(n.id) === String(focusNodeId));
9710
+ if (!targetNode) {
9711
+ const allParentNodes = Object.values(sceneResponse.data.parent).flatMap((f) => f.nodes || []);
9712
+ targetNode = allParentNodes.find((n) => String(n.id) === String(focusNodeId));
9713
+ }
9714
+ if (targetNode) {
9715
+ sceneResponse.data.scene.nodes = [targetNode];
9716
+ sceneResponse.data.scene.links = [];
9717
+ }
9718
+ } else if (focusAncestryId) {
9719
+ sceneResponse.data.scene.nodes = [];
9720
+ sceneResponse.data.scene.links = [];
9721
+ }
9620
9722
  sceneDataRef.current = sceneResponse.data.scene;
9621
9723
  parentDataRef.current = sceneResponse.data.parent;
9622
9724
  ancestryDataRef.current = sceneResponse.data.ancestry;
9623
- console.log("Console de sceneDataRef.current no useEffect inicial:", sceneDataRef.current);
9624
- console.log("Console de parentDataRef.current no useEffect inicial:", parentDataRef.current);
9625
- console.log("Console de ancestryDataRef.current no useEffect inicial:", ancestryDataRef.current);
9626
9725
  setIsInitialized(true);
9627
9726
  } else {
9628
9727
  console.error("Falha ao buscar dados da cena:", (sceneResponse == null ? void 0 : sceneResponse.error) || "Resposta inv\xE1lida.");
@@ -9630,7 +9729,6 @@ function XViewScene({
9630
9729
  if (boardResponse == null ? void 0 : boardResponse.success) {
9631
9730
  setAncestryBoardData(boardResponse.data);
9632
9731
  } else {
9633
- console.warn("Ancestry Board n\xE3o carregado ou vazio:", boardResponse == null ? void 0 : boardResponse.error);
9634
9732
  setAncestryBoardData([]);
9635
9733
  }
9636
9734
  } catch (error) {
@@ -9654,11 +9752,14 @@ function XViewScene({
9654
9752
  status,
9655
9753
  ownerId,
9656
9754
  viewParams,
9657
- // Importante incluir viewParams nas dependências
9658
9755
  get_scene_view_data,
9659
9756
  get_ancestry_board_action,
9660
9757
  isInitialized,
9661
- permissionStatus
9758
+ permissionStatus,
9759
+ focusNodeId,
9760
+ // <-- MANTIDO
9761
+ focusAncestryId
9762
+ // <-- ADICIONADO O focusAncestryId NAS DEPENDÊNCIAS
9662
9763
  ]);
9663
9764
  const isNodeInView = useCallback3((id) => {
9664
9765
  const key = String(id);
@@ -9692,7 +9793,7 @@ function XViewScene({
9692
9793
  }
9693
9794
  return mesh;
9694
9795
  }, []);
9695
- useEffect20(() => {
9796
+ useEffect21(() => {
9696
9797
  if (!isInitialized || !sceneDataRef.current) return;
9697
9798
  const currentMount = mountRef.current;
9698
9799
  if (!currentMount) return;
@@ -11942,6 +12043,34 @@ function XViewScene({
11942
12043
  tweenToTarget(nodeMesh, 1.2);
11943
12044
  }
11944
12045
  }, [tweenToTarget]);
12046
+ useEffect21(() => {
12047
+ if (isInitialized && focusNodeId && !hasFocusedInitial) {
12048
+ const nodeObjects = stateRef.current.nodeObjects || {};
12049
+ const targetMesh = nodeObjects[String(focusNodeId)];
12050
+ if (targetMesh) {
12051
+ setTimeout(() => {
12052
+ tweenToTarget(targetMesh, 1.2);
12053
+ setHasFocusedInitial(true);
12054
+ }, 300);
12055
+ } else {
12056
+ setHasFocusedInitial(true);
12057
+ }
12058
+ }
12059
+ }, [isInitialized, sceneVersion, focusNodeId, hasFocusedInitial, tweenToTarget]);
12060
+ useEffect21(() => {
12061
+ if (isInitialized && focusAncestryId && !hasOpenedInitialAncestry) {
12062
+ const ancestries = ancestryDataRef.current || [];
12063
+ const targetAncestry = ancestries.find((a) => String(a.ancestry_id) === String(focusAncestryId));
12064
+ if (targetAncestry) {
12065
+ setTimeout(() => {
12066
+ handleStartReadingAncestry(targetAncestry);
12067
+ setHasOpenedInitialAncestry(true);
12068
+ }, 300);
12069
+ } else {
12070
+ setHasOpenedInitialAncestry(true);
12071
+ }
12072
+ }
12073
+ }, [isInitialized, sceneVersion, focusAncestryId, hasOpenedInitialAncestry, handleStartReadingAncestry]);
11945
12074
  if (isLoading || status === "loading" || permissionStatus === "loading") {
11946
12075
  return /* @__PURE__ */ React23.createElement(LoadingScreen, null);
11947
12076
  }
@@ -12036,6 +12165,7 @@ function XViewScene({
12036
12165
  DescriptionReadModePanel,
12037
12166
  {
12038
12167
  key: readingMode.branchStack.length > 0 ? readingMode.branchStack[readingMode.branchStack.length - 1].branchId : readingMode.ancestry.ancestry_id,
12168
+ ancestryId: readingMode.ancestry.ancestry_id,
12039
12169
  title: (getReadModeDisplayContext == null ? void 0 : getReadModeDisplayContext.name) || readingMode.ancestry.name || "Ancestralidade",
12040
12170
  initialSectionId: readingMode.initialSectionId,
12041
12171
  description: getReadModeDisplayContext == null ? void 0 : getReadModeDisplayContext.description,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lv-x-software-house/x_view",
3
- "version": "1.2.1-dev.1",
3
+ "version": "1.2.1-dev.10",
4
4
  "description": "Pacote privado contendo os componentes e lógica de renderização 3D do X View.",
5
5
  "author": "iv.x - Engenharia de Software - ivxsoftwarehouse@gmail.com",
6
6
  "license": "UNLICENSED",