@lv-x-software-house/x_view 1.2.4 → 1.2.5-dev.2

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 +1684 -815
  2. package/dist/index.mjs +1691 -816
  3. package/package.json +52 -43
package/dist/index.mjs CHANGED
@@ -1,5 +1,11 @@
1
1
  // src/XViewScene.jsx
2
- import React25, { useCallback as useCallback4, useEffect as useEffect22, useRef as useRef19, useState as useState25, useMemo as useMemo12 } from "react";
2
+ import React25, {
3
+ useCallback as useCallback4,
4
+ useEffect as useEffect22,
5
+ useRef as useRef19,
6
+ useState as useState25,
7
+ useMemo as useMemo12
8
+ } from "react";
3
9
  import { useRouter, useSearchParams } from "next/navigation";
4
10
  import { useSession } from "next-auth/react";
5
11
  import CryptoJS from "crypto-js";
@@ -402,11 +408,18 @@ function ContextMenu({
402
408
  ))));
403
409
  };
404
410
  const renderAncestryActionsView = () => {
411
+ var _a2, _b2;
405
412
  const ancestryTitle = (selectedAncestry == null ? void 0 : selectedAncestry.name) || `Ancestralidade #${selectedAncestry == null ? void 0 : selectedAncestry.ancestry_id.substring(0, 8)}`;
406
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400 truncate", title: ancestryTitle }, ancestryTitle))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, ability.can("read", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => {
407
- onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry);
413
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400 truncate", title: ancestryTitle }, ancestryTitle))), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, ability.can("read", "Ancestry") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("button", { onClick: () => {
414
+ onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry, "full");
415
+ onClose();
416
+ }, className: baseButtonClass, title: "Renderizar Ancestralidade Completa" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ React.createElement("span", null, "Renderizar Ancestralidade")), /* @__PURE__ */ React.createElement("button", { onClick: () => {
417
+ onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry, "ancestry_only");
408
418
  onClose();
409
- }, className: baseButtonClass, title: "Renderizar Ancestralidade" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ React.createElement("span", null, "Renderizar Ancestralidade")), ability.can("update", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => {
419
+ }, className: baseButtonClass, title: "Renderizar apenas a \xC1rvore de Ancestralidade (Radial)" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M12 2v3m0 14v3m10-10h-3m-14 0H2m15.66-6.34-2.12 2.12m-9.08 9.08-2.12 2.12m13.32 0-2.12-2.12m-9.08-9.08-2.12-2.12" })), /* @__PURE__ */ React.createElement("span", null, "\xC1rvore de Ancestralidade")), ((_b2 = (_a2 = selectedAncestry == null ? void 0 : selectedAncestry.abstraction_tree) == null ? void 0 : _a2.children) == null ? void 0 : _b2.length) > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => {
420
+ onRenderAncestry == null ? void 0 : onRenderAncestry(selectedAncestry, "abstraction_only");
421
+ onClose();
422
+ }, className: baseButtonClass, title: "Renderizar apenas a \xC1rvore de Abstra\xE7\xE3o (Vertical)" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ React.createElement("rect", { x: "14", y: "3", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ React.createElement("rect", { x: "14", y: "14", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ React.createElement("rect", { x: "3", y: "14", width: "7", height: "7", rx: "1" }), /* @__PURE__ */ React.createElement("path", { d: "M10 6.5h4" }), /* @__PURE__ */ React.createElement("path", { d: "M10 17.5h4" }), /* @__PURE__ */ React.createElement("path", { d: "M6.5 10v4" }), /* @__PURE__ */ React.createElement("path", { d: "M17.5 10v4" })), /* @__PURE__ */ React.createElement("span", null, "\xC1rvore de Abstra\xE7\xE3o"))), ability.can("update", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => {
410
423
  onEditAncestry == null ? void 0 : onEditAncestry(selectedAncestry);
411
424
  onClose();
412
425
  }, className: baseButtonClass, title: "Editar Ancestralidade" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M12 20h9" }), /* @__PURE__ */ React.createElement("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" })), /* @__PURE__ */ React.createElement("span", null, "Editar Ancestralidade")), (ability.can("update", "Ancestry") || ability.can("delete", "Ancestry")) && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), ability.can("delete", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => {
@@ -9890,7 +9903,6 @@ var GroupItem = ({
9890
9903
  onPlayAncestry,
9891
9904
  availableIds,
9892
9905
  canEdit
9893
- // [NOVO] Recebe permissão de edição
9894
9906
  }) => {
9895
9907
  const canIndent = index > 0;
9896
9908
  const isPickingForThisGroup = pickingGroupId === group.id;
@@ -9905,107 +9917,126 @@ var GroupItem = ({
9905
9917
  useEffect21(() => {
9906
9918
  adjustHeight();
9907
9919
  }, [group.text]);
9908
- return /* @__PURE__ */ React24.createElement("div", { className: "flex flex-col gap-2 mb-3 pl-3 border-l border-white/10 relative group/item animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React24.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ React24.createElement("div", { className: `
9920
+ return /* @__PURE__ */ React24.createElement("div", { className: "flex flex-col gap-2 mb-3 pl-3 border-l border-white/10 relative group/item animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React24.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ React24.createElement(
9921
+ "div",
9922
+ {
9923
+ className: `
9909
9924
  flex flex-col gap-2 py-2 px-3 transition-all duration-200
9910
9925
  ${isPickingForThisGroup ? "bg-indigo-500/10 border-l-2 border-indigo-500" : "hover:bg-white/5 border-l-2 border-transparent hover:border-white/20"}
9911
- ` }, /* @__PURE__ */ React24.createElement(
9912
- "textarea",
9913
- {
9914
- ref: textareaRef,
9915
- className: `w-full bg-transparent text-sm text-slate-200 placeholder-slate-600 resize-none focus:outline-none focus:placeholder-slate-500 overflow-y-auto ${!canEdit ? "cursor-default" : ""}`,
9916
- rows: 1,
9917
- style: {
9918
- minHeight: "1.5rem",
9919
- maxHeight: "250px"
9920
- },
9921
- placeholder: canEdit ? "Escreva sobre este grupo..." : "",
9922
- value: group.text,
9923
- readOnly: !canEdit,
9924
- onChange: (e) => {
9925
- if (canEdit) onUpdate(group.id, { ...group, text: e.target.value });
9926
- }
9927
- }
9928
- ), group.ancestries && group.ancestries.length > 0 && /* @__PURE__ */ React24.createElement("div", { className: "flex flex-wrap gap-2 mt-1" }, group.ancestries.map((anc) => {
9929
- const isValid = availableIds.has(String(anc.ancestry_id));
9930
- return /* @__PURE__ */ React24.createElement(
9931
- "div",
9926
+ `
9927
+ },
9928
+ /* @__PURE__ */ React24.createElement(
9929
+ "textarea",
9932
9930
  {
9933
- key: anc.ancestry_id,
9934
- className: `
9931
+ ref: textareaRef,
9932
+ className: `w-full bg-transparent text-sm text-slate-200 placeholder-slate-600 resize-none focus:outline-none focus:placeholder-slate-500 overflow-y-auto ${!canEdit ? "cursor-default" : ""}`,
9933
+ rows: 1,
9934
+ style: {
9935
+ minHeight: "1.5rem",
9936
+ maxHeight: "250px"
9937
+ },
9938
+ placeholder: canEdit ? "Escreva sobre este grupo..." : "",
9939
+ value: group.text,
9940
+ readOnly: !canEdit,
9941
+ onChange: (e) => {
9942
+ if (canEdit) onUpdate(group.id, { ...group, text: e.target.value });
9943
+ }
9944
+ }
9945
+ ),
9946
+ group.ancestries && group.ancestries.length > 0 && /* @__PURE__ */ React24.createElement("div", { className: "flex flex-wrap gap-2 mt-1" }, group.ancestries.map((anc) => {
9947
+ const isValid = availableIds.has(String(anc.ancestry_id));
9948
+ return /* @__PURE__ */ React24.createElement(
9949
+ "div",
9950
+ {
9951
+ key: anc.ancestry_id,
9952
+ className: `
9935
9953
  flex items-center gap-2 rounded-md px-3 py-1.5 text-xs transition-all group/card border
9936
9954
  ${isValid ? "bg-slate-800/60 border-white/10 hover:border-indigo-500/30 text-slate-200" : "bg-red-900/10 border-red-500/30 text-red-300/70 hover:bg-red-900/20"}
9937
9955
  `,
9938
- title: isValid ? "" : "Esta ancestralidade foi removida ou n\xE3o existe mais."
9939
- },
9940
- isValid ? (
9941
- // [MANTIDO] Botão Play visível para todos
9956
+ title: isValid ? "" : "Esta ancestralidade foi removida ou n\xE3o existe mais."
9957
+ },
9958
+ isValid ? (
9959
+ // [MANTIDO] Botão Play visível para todos
9960
+ /* @__PURE__ */ React24.createElement(
9961
+ "button",
9962
+ {
9963
+ onClick: () => onPlayAncestry(anc.ancestry_id),
9964
+ className: "text-indigo-400 hover:text-white hover:bg-indigo-500 p-1 rounded-full transition-colors",
9965
+ title: "Renderizar no cen\xE1rio"
9966
+ },
9967
+ /* @__PURE__ */ React24.createElement(FiPlay, { size: 10, className: "ml-0.5 fill-current" })
9968
+ )
9969
+ ) : /* @__PURE__ */ React24.createElement("div", { className: "p-1 text-red-500 cursor-not-allowed" }, /* @__PURE__ */ React24.createElement(FiAlertTriangle, { size: 10 })),
9942
9970
  /* @__PURE__ */ React24.createElement(
9971
+ "span",
9972
+ {
9973
+ className: `font-medium truncate max-w-[150px] ${!isValid && "line-through decoration-red-500/50"}`
9974
+ },
9975
+ anc.name
9976
+ ),
9977
+ canEdit && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement(
9978
+ "div",
9979
+ {
9980
+ className: `w-px h-3 mx-0.5 ${isValid ? "bg-white/10" : "bg-red-500/20"}`
9981
+ }
9982
+ ), /* @__PURE__ */ React24.createElement(
9943
9983
  "button",
9944
9984
  {
9945
- onClick: () => onPlayAncestry(anc.ancestry_id),
9946
- className: "text-indigo-400 hover:text-white hover:bg-indigo-500 p-1 rounded-full transition-colors",
9947
- title: "Renderizar no cen\xE1rio"
9985
+ onClick: () => onRemoveAncestry(group.id, anc.ancestry_id),
9986
+ className: `${isValid ? "text-slate-500 hover:text-red-400" : "text-red-400 hover:text-red-200"} p-0.5 rounded transition-colors`,
9987
+ title: "Remover men\xE7\xE3o"
9948
9988
  },
9949
- /* @__PURE__ */ React24.createElement(FiPlay, { size: 10, className: "ml-0.5 fill-current" })
9950
- )
9951
- ) : /* @__PURE__ */ React24.createElement("div", { className: "p-1 text-red-500 cursor-not-allowed" }, /* @__PURE__ */ React24.createElement(FiAlertTriangle, { size: 10 })),
9952
- /* @__PURE__ */ React24.createElement("span", { className: `font-medium truncate max-w-[150px] ${!isValid && "line-through decoration-red-500/50"}` }, anc.name),
9953
- canEdit && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement("div", { className: `w-px h-3 mx-0.5 ${isValid ? "bg-white/10" : "bg-red-500/20"}` }), /* @__PURE__ */ React24.createElement(
9954
- "button",
9955
- {
9956
- onClick: () => onRemoveAncestry(group.id, anc.ancestry_id),
9957
- className: `${isValid ? "text-slate-500 hover:text-red-400" : "text-red-400 hover:text-red-200"} p-0.5 rounded transition-colors`,
9958
- title: "Remover men\xE7\xE3o"
9959
- },
9960
- /* @__PURE__ */ React24.createElement(FiX7, { size: 12 })
9961
- ))
9962
- );
9963
- })), canEdit && /* @__PURE__ */ React24.createElement("div", { className: "flex items-center justify-between pt-2 mt-1 border-t border-white/5 opacity-40 group-hover/item:opacity-100 transition-opacity" }, /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React24.createElement(
9964
- "button",
9965
- {
9966
- onClick: () => onRequestPickAncestry(group.id),
9967
- className: `
9989
+ /* @__PURE__ */ React24.createElement(FiX7, { size: 12 })
9990
+ ))
9991
+ );
9992
+ })),
9993
+ canEdit && /* @__PURE__ */ React24.createElement("div", { className: "flex items-center justify-between pt-2 mt-1 border-t border-white/5 opacity-40 group-hover/item:opacity-100 transition-opacity" }, /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React24.createElement(
9994
+ "button",
9995
+ {
9996
+ onClick: () => onRequestPickAncestry(group.id),
9997
+ className: `
9968
9998
  flex items-center gap-1.5 px-2 py-1 rounded text-xs font-medium transition-colors
9969
9999
  ${isPickingForThisGroup ? "text-indigo-300 animate-pulse" : "text-slate-500 hover:text-indigo-300 hover:bg-indigo-500/10"}
9970
10000
  `,
9971
- title: "Adicionar Ancestralidade a este grupo"
9972
- },
9973
- isPickingForThisGroup ? /* @__PURE__ */ React24.createElement(FiCheckCircle, { size: 12 }) : /* @__PURE__ */ React24.createElement(FiSearch4, { size: 12 }),
9974
- isPickingForThisGroup ? "Selecionando..." : "Adicionar"
9975
- ), /* @__PURE__ */ React24.createElement(
9976
- "button",
9977
- {
9978
- onClick: () => onAddSubgroup(group.id),
9979
- className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
9980
- title: "Criar Subgrupo"
9981
- },
9982
- /* @__PURE__ */ React24.createElement(FiPlus9, { size: 14 })
9983
- )), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React24.createElement(
9984
- "button",
9985
- {
9986
- onClick: () => onIndent(group.id),
9987
- disabled: !canIndent,
9988
- className: `p-1.5 rounded transition-colors ${!canIndent ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`,
9989
- title: "Aninhar no grupo acima"
9990
- },
9991
- /* @__PURE__ */ React24.createElement(FiArrowRight, { size: 14 })
9992
- ), /* @__PURE__ */ React24.createElement(
9993
- "button",
9994
- {
9995
- onClick: () => onOutdent(group.id),
9996
- className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
9997
- title: "Desaninhar"
9998
- },
9999
- /* @__PURE__ */ React24.createElement(FiArrowLeft3, { size: 14 })
10000
- ), /* @__PURE__ */ React24.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), /* @__PURE__ */ React24.createElement(
10001
- "button",
10002
- {
10003
- onClick: () => onDelete(group.id),
10004
- className: "p-1.5 text-slate-600 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors",
10005
- title: "Remover Grupo"
10006
- },
10007
- /* @__PURE__ */ React24.createElement(FiTrash23, { size: 14 })
10008
- )))), group.children && group.children.length > 0 && /* @__PURE__ */ React24.createElement("div", { className: "ml-2" }, group.children.map((childGroup, idx) => /* @__PURE__ */ React24.createElement(
10001
+ title: "Adicionar Ancestralidade a este grupo"
10002
+ },
10003
+ isPickingForThisGroup ? /* @__PURE__ */ React24.createElement(FiCheckCircle, { size: 12 }) : /* @__PURE__ */ React24.createElement(FiSearch4, { size: 12 }),
10004
+ isPickingForThisGroup ? "Selecionando..." : "Adicionar"
10005
+ ), /* @__PURE__ */ React24.createElement(
10006
+ "button",
10007
+ {
10008
+ onClick: () => onAddSubgroup(group.id),
10009
+ className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
10010
+ title: "Criar Subgrupo"
10011
+ },
10012
+ /* @__PURE__ */ React24.createElement(FiPlus9, { size: 14 })
10013
+ )), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React24.createElement(
10014
+ "button",
10015
+ {
10016
+ onClick: () => onIndent(group.id),
10017
+ disabled: !canIndent,
10018
+ className: `p-1.5 rounded transition-colors ${!canIndent ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`,
10019
+ title: "Aninhar no grupo acima"
10020
+ },
10021
+ /* @__PURE__ */ React24.createElement(FiArrowRight, { size: 14 })
10022
+ ), /* @__PURE__ */ React24.createElement(
10023
+ "button",
10024
+ {
10025
+ onClick: () => onOutdent(group.id),
10026
+ className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
10027
+ title: "Desaninhar"
10028
+ },
10029
+ /* @__PURE__ */ React24.createElement(FiArrowLeft3, { size: 14 })
10030
+ ), /* @__PURE__ */ React24.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), /* @__PURE__ */ React24.createElement(
10031
+ "button",
10032
+ {
10033
+ onClick: () => onDelete(group.id),
10034
+ className: "p-1.5 text-slate-600 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors",
10035
+ title: "Remover Grupo"
10036
+ },
10037
+ /* @__PURE__ */ React24.createElement(FiTrash23, { size: 14 })
10038
+ )))
10039
+ ), group.children && group.children.length > 0 && /* @__PURE__ */ React24.createElement("div", { className: "ml-2" }, group.children.map((childGroup, idx) => /* @__PURE__ */ React24.createElement(
10009
10040
  GroupItem,
10010
10041
  {
10011
10042
  key: childGroup.id,
@@ -10214,7 +10245,9 @@ function AncestryBoard({
10214
10245
  const addRecursive = (list) => {
10215
10246
  return list.map((g) => {
10216
10247
  if (g.id === pickingGroupId) {
10217
- const exists = g.ancestries.some((a) => a.ancestry_id === ancestry.ancestry_id);
10248
+ const exists = g.ancestries.some(
10249
+ (a) => a.ancestry_id === ancestry.ancestry_id
10250
+ );
10218
10251
  if (exists) return g;
10219
10252
  return { ...g, ancestries: [...g.ancestries, ancestry] };
10220
10253
  }
@@ -10225,12 +10258,16 @@ function AncestryBoard({
10225
10258
  });
10226
10259
  setPickingGroupId(null);
10227
10260
  } else {
10228
- const fullAncestry = availableAncestries.find((a) => a.ancestry_id === ancestry.ancestry_id) || ancestry;
10261
+ const fullAncestry = availableAncestries.find(
10262
+ (a) => a.ancestry_id === ancestry.ancestry_id
10263
+ ) || ancestry;
10229
10264
  onSelect(fullAncestry);
10230
10265
  }
10231
10266
  };
10232
10267
  const handlePlayFromGroup = (ancestryId) => {
10233
- const fullAncestry = availableAncestries.find((a) => a.ancestry_id === ancestryId);
10268
+ const fullAncestry = availableAncestries.find(
10269
+ (a) => a.ancestry_id === ancestryId
10270
+ );
10234
10271
  if (fullAncestry && onSelect) {
10235
10272
  onSelect(fullAncestry);
10236
10273
  }
@@ -10240,7 +10277,12 @@ function AncestryBoard({
10240
10277
  const removeRecursive = (list) => {
10241
10278
  return list.map((g) => {
10242
10279
  if (g.id === groupId) {
10243
- return { ...g, ancestries: g.ancestries.filter((a) => a.ancestry_id !== ancestryId) };
10280
+ return {
10281
+ ...g,
10282
+ ancestries: g.ancestries.filter(
10283
+ (a) => a.ancestry_id !== ancestryId
10284
+ )
10285
+ };
10244
10286
  }
10245
10287
  return { ...g, children: removeRecursive(g.children) };
10246
10288
  });
@@ -10261,7 +10303,13 @@ function AncestryBoard({
10261
10303
  className: "bg-slate-950 border border-white/10 rounded-xl w-[98vw] h-[97vh] flex flex-col shadow-2xl overflow-hidden animate-in fade-in zoom-in-95 duration-200",
10262
10304
  onClick: (e) => e.stopPropagation()
10263
10305
  },
10264
- /* @__PURE__ */ React24.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React24.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ React24.createElement(FiLayers6, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React24.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-slate-900/50 border border-white/5" }, saveStatus === "saving" && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement(FiLoader5, { className: "animate-spin text-indigo-400", size: 12 }), /* @__PURE__ */ React24.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement(FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ React24.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ React24.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-3" }, pickingGroupId && /* @__PURE__ */ React24.createElement("span", { className: "text-xs text-indigo-300 font-medium animate-pulse hidden sm:inline-block mr-2" }, "Selecione na lateral..."), canEdit && /* @__PURE__ */ React24.createElement(
10306
+ /* @__PURE__ */ React24.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React24.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ React24.createElement(FiLayers6, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React24.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-slate-900/50 border border-white/5" }, saveStatus === "saving" && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement(
10307
+ FiLoader5,
10308
+ {
10309
+ className: "animate-spin text-indigo-400",
10310
+ size: 12
10311
+ }
10312
+ ), /* @__PURE__ */ React24.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement(FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ React24.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ React24.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-3" }, pickingGroupId && /* @__PURE__ */ React24.createElement("span", { className: "text-xs text-indigo-300 font-medium animate-pulse hidden sm:inline-block mr-2" }, "Selecione na lateral..."), canEdit && /* @__PURE__ */ React24.createElement(
10265
10313
  "button",
10266
10314
  {
10267
10315
  onClick: handleAddRootGroup,
@@ -10277,57 +10325,75 @@ function AncestryBoard({
10277
10325
  },
10278
10326
  "\xD7"
10279
10327
  ))),
10280
- /* @__PURE__ */ React24.createElement("div", { className: "flex flex-1 overflow-hidden" }, /* @__PURE__ */ React24.createElement("div", { className: `
10328
+ /* @__PURE__ */ React24.createElement("div", { className: "flex flex-1 overflow-hidden" }, /* @__PURE__ */ React24.createElement(
10329
+ "div",
10330
+ {
10331
+ className: `
10281
10332
  flex flex-col border-r border-white/10 transition-all duration-300 flex-none
10282
10333
  ${pickingGroupId ? "w-[25%] border-indigo-500/30" : "w-[20%]"}
10283
10334
  min-w-[280px] max-w-[500px] bg-slate-900
10284
- ` }, /* @__PURE__ */ React24.createElement("div", { className: "p-3 border-b border-white/5 bg-slate-900/50" }, /* @__PURE__ */ React24.createElement("div", { className: "relative group" }, /* @__PURE__ */ React24.createElement(FiSearch4, { className: `absolute left-3 top-1/2 -translate-y-1/2 transition-colors ${pickingGroupId ? "text-indigo-400" : "text-slate-500 group-focus-within:text-indigo-400"}` }), /* @__PURE__ */ React24.createElement(
10285
- "input",
10286
- {
10287
- type: "text",
10288
- placeholder: pickingGroupId ? "Pesquise para adicionar..." : "Pesquisar ancestralidade...",
10289
- className: `
10335
+ `
10336
+ },
10337
+ /* @__PURE__ */ React24.createElement("div", { className: "p-3 border-b border-white/5 bg-slate-900/50" }, /* @__PURE__ */ React24.createElement("div", { className: "relative group" }, /* @__PURE__ */ React24.createElement(
10338
+ FiSearch4,
10339
+ {
10340
+ className: `absolute left-3 top-1/2 -translate-y-1/2 transition-colors ${pickingGroupId ? "text-indigo-400" : "text-slate-500 group-focus-within:text-indigo-400"}`
10341
+ }
10342
+ ), /* @__PURE__ */ React24.createElement(
10343
+ "input",
10344
+ {
10345
+ type: "text",
10346
+ placeholder: pickingGroupId ? "Pesquise para adicionar..." : "Pesquisar ancestralidade...",
10347
+ className: `
10290
10348
  w-full rounded-md pl-9 pr-4 py-2 text-sm transition-all focus:outline-none focus:ring-1 focus:ring-indigo-500 border
10291
10349
  ${pickingGroupId ? "bg-indigo-950/30 border-indigo-500/30 text-white placeholder-indigo-300/50" : "bg-slate-950 border-white/10 text-slate-200 placeholder-slate-600"}
10292
10350
  `,
10293
- value: searchTerm,
10294
- onChange: (e) => setSearchTerm(e.target.value),
10295
- autoFocus: !pickingGroupId
10296
- }
10297
- ))), /* @__PURE__ */ React24.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-3 space-y-2" }, filtered.map((anc) => {
10298
- const parentNodeName = nodeNamesMap.get(String(anc.ancestral_node)) || "Node Desconhecido";
10299
- const isPicking = !!pickingGroupId;
10300
- return /* @__PURE__ */ React24.createElement(
10301
- "div",
10302
- {
10303
- key: anc.ancestry_id,
10304
- onClick: () => {
10305
- if (isPicking) handleSelectAncestry(anc);
10306
- },
10307
- className: `
10351
+ value: searchTerm,
10352
+ onChange: (e) => setSearchTerm(e.target.value),
10353
+ autoFocus: !pickingGroupId
10354
+ }
10355
+ ))),
10356
+ /* @__PURE__ */ React24.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-3 space-y-2" }, filtered.map((anc) => {
10357
+ const parentNodeName = nodeNamesMap.get(String(anc.ancestral_node)) || "Node Desconhecido";
10358
+ const isPicking = !!pickingGroupId;
10359
+ return /* @__PURE__ */ React24.createElement(
10360
+ "div",
10361
+ {
10362
+ key: anc.ancestry_id,
10363
+ onClick: () => {
10364
+ if (isPicking) handleSelectAncestry(anc);
10365
+ },
10366
+ className: `
10308
10367
  group relative flex items-start gap-3 p-3 text-left rounded-lg border transition-all duration-200
10309
10368
  ${isPicking ? "border-indigo-500/30 bg-indigo-500/5 hover:bg-indigo-500/20 hover:border-indigo-400 cursor-pointer" : "border-white/5 bg-slate-800/40 hover:bg-indigo-600/10 hover:border-indigo-500/30 cursor-default"}
10310
10369
  `
10311
- },
10312
- /* @__PURE__ */ React24.createElement("div", { className: `
10370
+ },
10371
+ /* @__PURE__ */ React24.createElement(
10372
+ "div",
10373
+ {
10374
+ className: `
10313
10375
  mt-0.5 w-8 h-8 rounded-md grid place-content-center shrink-0 border transition-all shadow-lg
10314
10376
  ${isPicking ? "bg-indigo-500 text-white border-indigo-400" : "bg-slate-800 text-indigo-400 border-white/5 group-hover:bg-indigo-500 group-hover:text-white"}
10315
- ` }, isPicking ? /* @__PURE__ */ React24.createElement(FiPlus9, { size: 16 }) : /* @__PURE__ */ React24.createElement(FiLayers6, { size: 14 })),
10316
- /* @__PURE__ */ React24.createElement("div", { className: "flex-1 min-w-0 pb-2" }, /* @__PURE__ */ React24.createElement("div", { className: "flex items-center justify-between gap-2" }, /* @__PURE__ */ React24.createElement("h4", { className: "text-sm font-medium text-slate-200 group-hover:text-white truncate transition-colors" }, anc.name || "Sem Nome"), anc.is_private && /* @__PURE__ */ React24.createElement("span", { className: "text-[9px] px-1 py-0.5 rounded bg-amber-500/10 text-amber-300 border border-amber-500/20" }, "Priv")), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1.5 mt-0.5 text-[11px] text-slate-500 group-hover:text-indigo-200/70 transition-colors" }, /* @__PURE__ */ React24.createElement(FiCornerUpRight4, { size: 10 }), /* @__PURE__ */ React24.createElement("span", { className: "truncate max-w-[120px]" }, parentNodeName)), anc.description && /* @__PURE__ */ React24.createElement("p", { className: "mt-1.5 text-[11px] text-slate-400 line-clamp-2 leading-relaxed opacity-80" }, anc.description)),
10317
- !isPicking && /* @__PURE__ */ React24.createElement(
10318
- "button",
10319
- {
10320
- onClick: (e) => {
10321
- e.stopPropagation();
10322
- handleSelectAncestry(anc);
10377
+ `
10323
10378
  },
10324
- className: "absolute right-2 bottom-2 opacity-0 group-hover:opacity-100 transition-all duration-300 transform translate-y-2 group-hover:translate-y-0 z-10",
10325
- title: "Renderizar Ancestralidade"
10326
- },
10327
- /* @__PURE__ */ React24.createElement("div", { className: "bg-indigo-500 text-white p-2 rounded-full shadow-lg hover:bg-indigo-400 hover:scale-110 transition-all" }, /* @__PURE__ */ React24.createElement(FiPlay, { size: 14, className: "ml-0.5" }))
10328
- )
10329
- );
10330
- }))), /* @__PURE__ */ React24.createElement("div", { className: "flex flex-col flex-1 bg-slate-950/30" }, /* @__PURE__ */ React24.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-6 space-y-4" }, groups.length === 0 ? /* @__PURE__ */ React24.createElement("div", { className: "flex flex-col items-center justify-center h-full text-slate-500 gap-3 border-2 border-dashed border-white/5 rounded-xl m-4 bg-slate-900/20" }, /* @__PURE__ */ React24.createElement(FiLayers6, { size: 24, className: "opacity-20" }), /* @__PURE__ */ React24.createElement("p", { className: "text-xs text-center px-4" }, canEdit ? /* @__PURE__ */ React24.createElement(React24.Fragment, null, "Nenhum grupo criado.", /* @__PURE__ */ React24.createElement("br", null), 'Use o bot\xE3o "Novo Grupo" acima.') : /* @__PURE__ */ React24.createElement(React24.Fragment, null, "Nenhum grupo dispon\xEDvel para visualiza\xE7\xE3o."))) : groups.map((group, index) => /* @__PURE__ */ React24.createElement(
10379
+ isPicking ? /* @__PURE__ */ React24.createElement(FiPlus9, { size: 16 }) : /* @__PURE__ */ React24.createElement(FiLayers6, { size: 14 })
10380
+ ),
10381
+ /* @__PURE__ */ React24.createElement("div", { className: "flex-1 min-w-0 pb-2" }, /* @__PURE__ */ React24.createElement("div", { className: "flex items-center justify-between gap-2" }, /* @__PURE__ */ React24.createElement("h4", { className: "text-sm font-medium text-slate-200 group-hover:text-white truncate transition-colors" }, anc.name || "Sem Nome"), anc.is_private && /* @__PURE__ */ React24.createElement("span", { className: "text-[9px] px-1 py-0.5 rounded bg-amber-500/10 text-amber-300 border border-amber-500/20" }, "Priv")), /* @__PURE__ */ React24.createElement("div", { className: "flex items-center gap-1.5 mt-0.5 text-[11px] text-slate-500 group-hover:text-indigo-200/70 transition-colors" }, /* @__PURE__ */ React24.createElement(FiCornerUpRight4, { size: 10 }), /* @__PURE__ */ React24.createElement("span", { className: "truncate max-w-[120px]" }, parentNodeName)), anc.description && /* @__PURE__ */ React24.createElement("p", { className: "mt-1.5 text-[11px] text-slate-400 line-clamp-2 leading-relaxed opacity-80" }, anc.description)),
10382
+ !isPicking && /* @__PURE__ */ React24.createElement(
10383
+ "button",
10384
+ {
10385
+ onClick: (e) => {
10386
+ e.stopPropagation();
10387
+ handleSelectAncestry(anc);
10388
+ },
10389
+ className: "absolute right-2 bottom-2 opacity-0 group-hover:opacity-100 transition-all duration-300 transform translate-y-2 group-hover:translate-y-0 z-10",
10390
+ title: "Renderizar Ancestralidade"
10391
+ },
10392
+ /* @__PURE__ */ React24.createElement("div", { className: "bg-indigo-500 text-white p-2 rounded-full shadow-lg hover:bg-indigo-400 hover:scale-110 transition-all" }, /* @__PURE__ */ React24.createElement(FiPlay, { size: 14, className: "ml-0.5" }))
10393
+ )
10394
+ );
10395
+ }))
10396
+ ), /* @__PURE__ */ React24.createElement("div", { className: "flex flex-col flex-1 bg-slate-950/30" }, /* @__PURE__ */ React24.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-6 space-y-4" }, groups.length === 0 ? /* @__PURE__ */ React24.createElement("div", { className: "flex flex-col items-center justify-center h-full text-slate-500 gap-3 border-2 border-dashed border-white/5 rounded-xl m-4 bg-slate-900/20" }, /* @__PURE__ */ React24.createElement(FiLayers6, { size: 24, className: "opacity-20" }), /* @__PURE__ */ React24.createElement("p", { className: "text-xs text-center px-4" }, canEdit ? /* @__PURE__ */ React24.createElement(React24.Fragment, null, "Nenhum grupo criado.", /* @__PURE__ */ React24.createElement("br", null), 'Use o bot\xE3o "Novo Grupo" acima.') : /* @__PURE__ */ React24.createElement(React24.Fragment, null, "Nenhum grupo dispon\xEDvel para visualiza\xE7\xE3o."))) : groups.map((group, index) => /* @__PURE__ */ React24.createElement(
10331
10397
  GroupItem,
10332
10398
  {
10333
10399
  key: group.id,
@@ -10406,7 +10472,10 @@ var findNodePath3 = (tree, targetNodeId, currentPath = []) => {
10406
10472
  }
10407
10473
  if (tree.children) {
10408
10474
  for (let i = 0; i < tree.children.length; i++) {
10409
- const res = findNodePath3(tree.children[i], targetNodeId, [...currentPath, i]);
10475
+ const res = findNodePath3(tree.children[i], targetNodeId, [
10476
+ ...currentPath,
10477
+ i
10478
+ ]);
10410
10479
  if (res) return res;
10411
10480
  }
10412
10481
  }
@@ -10499,27 +10568,70 @@ function XViewScene({
10499
10568
  const [userPermissionRole, setUserPermissionRole] = useState25(null);
10500
10569
  const [isInitialized, setIsInitialized] = useState25(false);
10501
10570
  const [sceneVersion, setSceneVersion] = useState25(0);
10502
- const [contextMenu, setContextMenu] = useState25({ visible: false, x: 0, y: 0, nodeData: null });
10503
- const [multiContextMenu, setMultiContextMenu] = useState25({ visible: false, x: 0, y: 0, nodeIds: null });
10504
- const [relationshipMenu, setRelationshipMenu] = useState25({ visible: false, x: 0, y: 0, linkObject: null });
10505
- const [creationMode, setCreationMode] = useState25({ isActive: false, sourceNodeData: null });
10506
- const [versionMode, setVersionMode] = useState25({ isActive: false, sourceNodeData: null });
10571
+ const [contextMenu, setContextMenu] = useState25({
10572
+ visible: false,
10573
+ x: 0,
10574
+ y: 0,
10575
+ nodeData: null
10576
+ });
10577
+ const [multiContextMenu, setMultiContextMenu] = useState25({
10578
+ visible: false,
10579
+ x: 0,
10580
+ y: 0,
10581
+ nodeIds: null
10582
+ });
10583
+ const [relationshipMenu, setRelationshipMenu] = useState25({
10584
+ visible: false,
10585
+ x: 0,
10586
+ y: 0,
10587
+ linkObject: null
10588
+ });
10589
+ const [creationMode, setCreationMode] = useState25({
10590
+ isActive: false,
10591
+ sourceNodeData: null
10592
+ });
10593
+ const [versionMode, setVersionMode] = useState25({
10594
+ isActive: false,
10595
+ sourceNodeData: null
10596
+ });
10507
10597
  const [questMode, setQuestMode] = useState25({ isActive: false });
10508
10598
  const [hasFocusedInitial, setHasFocusedInitial] = useState25(false);
10509
10599
  const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = useState25(false);
10510
- const [ancestryMode, setAncestryMode] = useState25({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", ancestryDescriptionSections: [], isAddingNodes: false });
10600
+ const [ancestryMode, setAncestryMode] = useState25({
10601
+ isActive: false,
10602
+ tree: null,
10603
+ selectedParentId: null,
10604
+ isEditMode: false,
10605
+ currentAncestryId: null,
10606
+ ancestryName: "",
10607
+ ancestryDescription: "",
10608
+ ancestryDescriptionSections: [],
10609
+ isAddingNodes: false
10610
+ });
10511
10611
  const [readingMode, setReadingMode] = useState25({
10512
10612
  isActive: false,
10513
10613
  ancestry: null,
10514
10614
  branchStack: [],
10515
10615
  autoAbstraction: false
10516
10616
  });
10517
- const [formPosition, setFormPosition] = useState25({ left: 16, top: 16, opacity: 0 });
10617
+ const [formPosition, setFormPosition] = useState25({
10618
+ left: 16,
10619
+ top: 16,
10620
+ opacity: 0
10621
+ });
10518
10622
  const [detailsNode, setDetailsNode] = useState25(null);
10519
10623
  const [detailsLink, setDetailsLink] = useState25(null);
10520
10624
  const [ancestryLinkDetails, setAncestryLinkDetails] = useState25(null);
10521
- const [imageViewer, setImageViewer] = useState25({ visible: false, images: [], startIndex: 0 });
10522
- const [editingAncestryRel, setEditingAncestryRel] = useState25({ visible: false, data: null, path: null });
10625
+ const [imageViewer, setImageViewer] = useState25({
10626
+ visible: false,
10627
+ images: [],
10628
+ startIndex: 0
10629
+ });
10630
+ const [editingAncestryRel, setEditingAncestryRel] = useState25({
10631
+ visible: false,
10632
+ data: null,
10633
+ path: null
10634
+ });
10523
10635
  const [isImportModalOpen, setIsImportModalOpen] = useState25(false);
10524
10636
  const [importSuccessMessage, setImportSuccessMessage] = useState25("");
10525
10637
  const [highlightedNodeId, setHighlightedNodeId] = useState25(null);
@@ -10559,8 +10671,23 @@ function XViewScene({
10559
10671
  ghostElements: { node: null, line: null, aura: null },
10560
10672
  creation: { isActive: false, sourceNodeData: null },
10561
10673
  connection: { isActive: false, sourceNodeData: null, line: null },
10562
- relink: { isActive: false, end: null, fixedNodeId: null, originalLine: null, line: null },
10563
- ancestry: { isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", isAddingNodes: false },
10674
+ relink: {
10675
+ isActive: false,
10676
+ end: null,
10677
+ fixedNodeId: null,
10678
+ originalLine: null,
10679
+ line: null
10680
+ },
10681
+ ancestry: {
10682
+ isActive: false,
10683
+ tree: null,
10684
+ selectedParentId: null,
10685
+ isEditMode: false,
10686
+ currentAncestryId: null,
10687
+ ancestryName: "",
10688
+ ancestryDescription: "",
10689
+ isAddingNodes: false
10690
+ },
10564
10691
  glowTexture: null,
10565
10692
  nodeIdToParentFileMap: null,
10566
10693
  maxAncestryRenderIndex: 0,
@@ -10569,7 +10696,11 @@ function XViewScene({
10569
10696
  highlightedNodeId: null
10570
10697
  });
10571
10698
  const maxReadPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
10572
- const { width: readModeWidth, isResizing: isReadModeResizing, handlePointerDown: handleReadModeResize } = useResizablePanel({
10699
+ const {
10700
+ width: readModeWidth,
10701
+ isResizing: isReadModeResizing,
10702
+ handlePointerDown: handleReadModeResize
10703
+ } = useResizablePanel({
10573
10704
  initialWidth: 700,
10574
10705
  minWidth: 320,
10575
10706
  maxWidth: maxReadPanelW
@@ -10586,7 +10717,9 @@ function XViewScene({
10586
10717
  for (const parentFileId in allParentData) {
10587
10718
  if (allParentData.hasOwnProperty(parentFileId)) {
10588
10719
  const parentFile = allParentData[parentFileId];
10589
- const parentDbInfo = parentDbsArray.find((db) => String(db.db_id) === String(parentFileId));
10720
+ const parentDbInfo = parentDbsArray.find(
10721
+ (db) => String(db.db_id) === String(parentFileId)
10722
+ );
10590
10723
  const ownerId2 = (parentDbInfo == null ? void 0 : parentDbInfo.owner_id) || null;
10591
10724
  const datasetName = parentFile.dataset_name || `Dataset #${parentFileId.substring(0, 6)}`;
10592
10725
  if (parentFile.nodes && ownerId2) {
@@ -10622,7 +10755,10 @@ function XViewScene({
10622
10755
  if (files.length > 0 && get_single_parent_file && save_view_data) {
10623
10756
  for (const file of files) {
10624
10757
  try {
10625
- const parentFileData = await get_single_parent_file(file.id, session);
10758
+ const parentFileData = await get_single_parent_file(
10759
+ file.id,
10760
+ session
10761
+ );
10626
10762
  if (parentFileData.success && parentFileData.data) {
10627
10763
  parentDataRef.current = {
10628
10764
  ...parentDataRef.current,
@@ -10633,7 +10769,11 @@ function XViewScene({
10633
10769
  owner_id: session.user.id
10634
10770
  };
10635
10771
  updatedParentDbs.push(newParentDbObject);
10636
- await add_new_parent_file_to_scene_at_firebase_action(sceneConfigId, session, file.id);
10772
+ await add_new_parent_file_to_scene_at_firebase_action(
10773
+ sceneConfigId,
10774
+ session,
10775
+ file.id
10776
+ );
10637
10777
  importedIds.push(file.id);
10638
10778
  successCount++;
10639
10779
  }
@@ -10645,7 +10785,10 @@ function XViewScene({
10645
10785
  if (viewToImport && get_ancestry_file) {
10646
10786
  try {
10647
10787
  const targetViewOwnerId = ((_b2 = (_a2 = viewToImport.members) == null ? void 0 : _a2.find((m) => m.permission === "owner")) == null ? void 0 : _b2.id) || session.user.id;
10648
- const ancestryResponse = await get_ancestry_file(viewToImport.id, targetViewOwnerId);
10788
+ const ancestryResponse = await get_ancestry_file(
10789
+ viewToImport.id,
10790
+ targetViewOwnerId
10791
+ );
10649
10792
  if (ancestryResponse.success && Array.isArray(ancestryResponse.data)) {
10650
10793
  const viewSpecificAncestries = ancestryResponse.data.filter(
10651
10794
  (anc) => anc._source_file_id === viewToImport.id && !anc.is_private
@@ -10656,14 +10799,21 @@ function XViewScene({
10656
10799
  _imported_from_view_owner_id: targetViewOwnerId,
10657
10800
  _source_file_id: viewToImport.id,
10658
10801
  _source_owner_id: targetViewOwnerId,
10659
- _origin_db_ids: (viewToImport.selected_databases || []).map((db) => db.db_id)
10802
+ _origin_db_ids: (viewToImport.selected_databases || []).map(
10803
+ (db) => db.db_id
10804
+ )
10660
10805
  }));
10661
10806
  const currentAncestries = ancestryDataRef.current || [];
10662
10807
  const newAncestries = processedAncestries.filter(
10663
- (newAnc) => !currentAncestries.some((curr) => String(curr.ancestry_id) === String(newAnc.ancestry_id))
10808
+ (newAnc) => !currentAncestries.some(
10809
+ (curr) => String(curr.ancestry_id) === String(newAnc.ancestry_id)
10810
+ )
10664
10811
  );
10665
10812
  if (newAncestries.length > 0) {
10666
- ancestryDataRef.current = [...currentAncestries, ...newAncestries];
10813
+ ancestryDataRef.current = [
10814
+ ...currentAncestries,
10815
+ ...newAncestries
10816
+ ];
10667
10817
  ancestriesWereImported = true;
10668
10818
  }
10669
10819
  }
@@ -10683,7 +10833,9 @@ function XViewScene({
10683
10833
  if (ancestry_save_url) {
10684
10834
  await save_view_data(ancestry_save_url, ancestryDataRef.current);
10685
10835
  } else {
10686
- console.error("Erro: URL de salvamento de ancestralidade n\xE3o definida.");
10836
+ console.error(
10837
+ "Erro: URL de salvamento de ancestralidade n\xE3o definida."
10838
+ );
10687
10839
  }
10688
10840
  }
10689
10841
  setSceneVersion((v) => v + 1);
@@ -10691,113 +10843,136 @@ function XViewScene({
10691
10843
  setImportSuccessMessage("Importa\xE7\xE3o conclu\xEDda com sucesso.");
10692
10844
  setTimeout(() => setImportSuccessMessage(""), 5e3);
10693
10845
  } else if (viewToImport && !ancestriesWereImported) {
10694
- console.warn("Importa\xE7\xE3o finalizada, mas nenhum dado novo foi adicionado.");
10846
+ console.warn(
10847
+ "Importa\xE7\xE3o finalizada, mas nenhum dado novo foi adicionado."
10848
+ );
10695
10849
  }
10696
10850
  },
10697
- [get_single_parent_file, get_ancestry_file, save_view_data, session, sceneSaveUrl, ancestry_save_url, sceneConfigId, add_new_parent_file_to_scene_at_firebase_action]
10851
+ [
10852
+ get_single_parent_file,
10853
+ get_ancestry_file,
10854
+ save_view_data,
10855
+ session,
10856
+ sceneSaveUrl,
10857
+ ancestry_save_url,
10858
+ sceneConfigId,
10859
+ add_new_parent_file_to_scene_at_firebase_action
10860
+ ]
10698
10861
  );
10699
10862
  const handleOpenImageViewer = (images, startIndex) => {
10700
10863
  setImageViewer({ visible: true, images, startIndex });
10701
10864
  };
10702
- const tweenToTarget = useCallback4((target, zoomFactor = 1, forcedDirection = null) => {
10703
- const { camera, controls, tweenGroup } = stateRef.current;
10704
- if (!camera || !controls || !tweenGroup) return;
10705
- const targetPos = target instanceof THREE3.Mesh ? target.getWorldPosition(new THREE3.Vector3()) : target;
10706
- const controlsTween = new Tween2(controls.target).to(targetPos, 1500).easing(Easing2.Cubic.Out);
10707
- tweenGroup.add(controlsTween);
10708
- controlsTween.start();
10709
- let offset;
10710
- if (forcedDirection) {
10711
- offset = forcedDirection.clone().normalize().multiplyScalar(x_view_config.CAMERA_ZOOM_DISTANCE / zoomFactor);
10712
- } else {
10713
- offset = camera.position.clone().sub(controls.target).normalize().multiplyScalar(x_view_config.CAMERA_ZOOM_DISTANCE / zoomFactor);
10714
- }
10715
- const targetCameraPos = targetPos.clone().add(offset);
10716
- const cameraTween = new Tween2(camera.position).to(targetCameraPos, 1500).easing(Easing2.Cubic.Out);
10717
- tweenGroup.add(cameraTween);
10718
- cameraTween.start();
10719
- }, []);
10865
+ const tweenToTarget = useCallback4(
10866
+ (target, zoomFactor = 1, forcedDirection = null) => {
10867
+ const { camera, controls, tweenGroup } = stateRef.current;
10868
+ if (!camera || !controls || !tweenGroup) return;
10869
+ const targetPos = target instanceof THREE3.Mesh ? target.getWorldPosition(new THREE3.Vector3()) : target;
10870
+ const controlsTween = new Tween2(controls.target).to(targetPos, 1500).easing(Easing2.Cubic.Out);
10871
+ tweenGroup.add(controlsTween);
10872
+ controlsTween.start();
10873
+ let offset;
10874
+ if (forcedDirection) {
10875
+ offset = forcedDirection.clone().normalize().multiplyScalar(x_view_config.CAMERA_ZOOM_DISTANCE / zoomFactor);
10876
+ } else {
10877
+ offset = camera.position.clone().sub(controls.target).normalize().multiplyScalar(x_view_config.CAMERA_ZOOM_DISTANCE / zoomFactor);
10878
+ }
10879
+ const targetCameraPos = targetPos.clone().add(offset);
10880
+ const cameraTween = new Tween2(camera.position).to(targetCameraPos, 1500).easing(Easing2.Cubic.Out);
10881
+ tweenGroup.add(cameraTween);
10882
+ cameraTween.start();
10883
+ },
10884
+ []
10885
+ );
10720
10886
  const isFromUiOverlay = (event) => {
10721
10887
  const t = event == null ? void 0 : event.target;
10722
10888
  if (!t || typeof t.closest !== "function") return false;
10723
10889
  return !!t.closest(".ui-overlay");
10724
10890
  };
10725
- const buildFullAncestryTree = useCallback4((idTree, nodes, ancestries = []) => {
10726
- if (!idTree) return null;
10727
- const nodeMap = new Map(nodes.map((n) => [String(n.id), n]));
10728
- const ancestryMap = new Map(ancestries.map((a) => [String(a.ancestry_id), a]));
10729
- const recursiveBuild = (treeItem) => {
10730
- if (!treeItem) return null;
10731
- if (treeItem.is_section) {
10732
- return {
10733
- ...treeItem,
10734
- children: (treeItem.children || []).map(recursiveBuild).filter(Boolean)
10735
- };
10736
- }
10737
- let nodeId = treeItem.node_id;
10738
- if (!nodeId && treeItem.node) nodeId = treeItem.node.id;
10739
- const fullNode = nodeMap.get(String(nodeId));
10740
- const effectiveNode = fullNode || treeItem.node;
10741
- let processedBranches = [];
10742
- if (treeItem.parallel_branches && Array.isArray(treeItem.parallel_branches)) {
10743
- processedBranches = treeItem.parallel_branches.map((branch) => {
10744
- if (branch.linked_ancestry_id) {
10745
- const linkedAncestry = ancestryMap.get(String(branch.linked_ancestry_id));
10746
- if (linkedAncestry && linkedAncestry.tree) {
10747
- const graftedTree = recursiveBuild(linkedAncestry.tree);
10748
- return {
10749
- ...branch,
10750
- name: linkedAncestry.name,
10751
- description: linkedAncestry.description,
10752
- description_sections: linkedAncestry.description_sections,
10753
- tree: graftedTree,
10754
- isLinked: true
10755
- };
10756
- }
10757
- }
10891
+ const buildFullAncestryTree = useCallback4(
10892
+ (idTree, nodes, ancestries = []) => {
10893
+ if (!idTree) return null;
10894
+ const nodeMap = new Map(nodes.map((n) => [String(n.id), n]));
10895
+ const ancestryMap = new Map(
10896
+ ancestries.map((a) => [String(a.ancestry_id), a])
10897
+ );
10898
+ const recursiveBuild = (treeItem) => {
10899
+ if (!treeItem) return null;
10900
+ if (treeItem.is_section) {
10758
10901
  return {
10759
- ...branch,
10760
- tree: recursiveBuild(branch.tree)
10902
+ ...treeItem,
10903
+ children: (treeItem.children || []).map(recursiveBuild).filter(Boolean)
10761
10904
  };
10762
- });
10763
- }
10764
- return {
10765
- ...effectiveNode ? { node: effectiveNode } : { node: { id: nodeId, name: "Unknown" } },
10766
- relationship: treeItem.relationship || {},
10767
- children: (treeItem.children || []).map(recursiveBuild).filter(Boolean),
10768
- parallel_branches: processedBranches
10769
- };
10770
- };
10771
- let rootId = idTree.node_id;
10772
- if (!rootId && idTree.node) rootId = idTree.node.id;
10773
- if (rootId) {
10774
- const rootNode = nodeMap.get(String(rootId));
10775
- const effectiveRoot = rootNode || idTree.node;
10776
- if (!effectiveRoot) return null;
10777
- return {
10778
- node: effectiveRoot,
10779
- relationship: idTree.relationship || {},
10780
- children: (idTree.children || []).map(recursiveBuild).filter(Boolean),
10781
- parallel_branches: (idTree.parallel_branches || []).map((branch) => {
10782
- if (branch.linked_ancestry_id) {
10783
- const linkedAncestry = ancestryMap.get(String(branch.linked_ancestry_id));
10784
- if (linkedAncestry && linkedAncestry.tree) {
10785
- return {
10786
- ...branch,
10787
- name: linkedAncestry.name,
10788
- description: linkedAncestry.description,
10789
- description_sections: linkedAncestry.description_sections,
10790
- tree: recursiveBuild(linkedAncestry.tree),
10791
- isLinked: true
10792
- };
10905
+ }
10906
+ let nodeId = treeItem.node_id;
10907
+ if (!nodeId && treeItem.node) nodeId = treeItem.node.id;
10908
+ const fullNode = nodeMap.get(String(nodeId));
10909
+ const effectiveNode = fullNode || treeItem.node;
10910
+ let processedBranches = [];
10911
+ if (treeItem.parallel_branches && Array.isArray(treeItem.parallel_branches)) {
10912
+ processedBranches = treeItem.parallel_branches.map((branch) => {
10913
+ if (branch.linked_ancestry_id) {
10914
+ const linkedAncestry = ancestryMap.get(
10915
+ String(branch.linked_ancestry_id)
10916
+ );
10917
+ if (linkedAncestry && linkedAncestry.tree) {
10918
+ const graftedTree = recursiveBuild(linkedAncestry.tree);
10919
+ return {
10920
+ ...branch,
10921
+ name: linkedAncestry.name,
10922
+ description: linkedAncestry.description,
10923
+ description_sections: linkedAncestry.description_sections,
10924
+ tree: graftedTree,
10925
+ isLinked: true
10926
+ };
10927
+ }
10793
10928
  }
10794
- }
10795
- return { ...branch, tree: recursiveBuild(branch.tree) };
10796
- })
10929
+ return {
10930
+ ...branch,
10931
+ tree: recursiveBuild(branch.tree)
10932
+ };
10933
+ });
10934
+ }
10935
+ return {
10936
+ ...effectiveNode ? { node: effectiveNode } : { node: { id: nodeId, name: "Unknown" } },
10937
+ relationship: treeItem.relationship || {},
10938
+ children: (treeItem.children || []).map(recursiveBuild).filter(Boolean),
10939
+ parallel_branches: processedBranches
10940
+ };
10797
10941
  };
10798
- }
10799
- return recursiveBuild(idTree);
10800
- }, []);
10942
+ let rootId = idTree.node_id;
10943
+ if (!rootId && idTree.node) rootId = idTree.node.id;
10944
+ if (rootId) {
10945
+ const rootNode = nodeMap.get(String(rootId));
10946
+ const effectiveRoot = rootNode || idTree.node;
10947
+ if (!effectiveRoot) return null;
10948
+ return {
10949
+ node: effectiveRoot,
10950
+ relationship: idTree.relationship || {},
10951
+ children: (idTree.children || []).map(recursiveBuild).filter(Boolean),
10952
+ parallel_branches: (idTree.parallel_branches || []).map((branch) => {
10953
+ if (branch.linked_ancestry_id) {
10954
+ const linkedAncestry = ancestryMap.get(
10955
+ String(branch.linked_ancestry_id)
10956
+ );
10957
+ if (linkedAncestry && linkedAncestry.tree) {
10958
+ return {
10959
+ ...branch,
10960
+ name: linkedAncestry.name,
10961
+ description: linkedAncestry.description,
10962
+ description_sections: linkedAncestry.description_sections,
10963
+ tree: recursiveBuild(linkedAncestry.tree),
10964
+ isLinked: true
10965
+ };
10966
+ }
10967
+ }
10968
+ return { ...branch, tree: recursiveBuild(branch.tree) };
10969
+ })
10970
+ };
10971
+ }
10972
+ return recursiveBuild(idTree);
10973
+ },
10974
+ []
10975
+ );
10801
10976
  const handleActivateTimeline = useCallback4(() => {
10802
10977
  const { nodeObjects, tweenGroup, timelineIntervalsGroup } = stateRef.current;
10803
10978
  if (!nodeObjects || !tweenGroup || !timelineIntervalsGroup) return;
@@ -10878,10 +11053,12 @@ function XViewScene({
10878
11053
  if (timelineNodes.length === 0) return;
10879
11054
  const sortedTimePoints = Array.from(allTimePoints).sort((a, b) => a - b);
10880
11055
  const maxTimeIndex = sortedTimePoints.length - 1;
10881
- const timeToYMap = new Map(sortedTimePoints.map((time, index) => [
10882
- time,
10883
- (index - maxTimeIndex) * TIMELINE_LAYER_SPACING_Y
10884
- ]));
11056
+ const timeToYMap = new Map(
11057
+ sortedTimePoints.map((time, index) => [
11058
+ time,
11059
+ (index - maxTimeIndex) * TIMELINE_LAYER_SPACING_Y
11060
+ ])
11061
+ );
10885
11062
  timelineNodes.sort((a, b) => {
10886
11063
  if (a.isUndated && b.isUndated) return 0;
10887
11064
  if (a.isUndated) return 1;
@@ -10924,7 +11101,12 @@ function XViewScene({
10924
11101
  if (type === "interval") {
10925
11102
  const endY = timeToYMap.get(endDate.getTime());
10926
11103
  if (endY > y) {
10927
- const barGeometry = new THREE3.CylinderGeometry(TIMELINE_INTERVAL_BAR_RADIUS, TIMELINE_INTERVAL_BAR_RADIUS, 1, 16);
11104
+ const barGeometry = new THREE3.CylinderGeometry(
11105
+ TIMELINE_INTERVAL_BAR_RADIUS,
11106
+ TIMELINE_INTERVAL_BAR_RADIUS,
11107
+ 1,
11108
+ 16
11109
+ );
10928
11110
  const barMaterial = new THREE3.MeshStandardMaterial({
10929
11111
  color: TIMELINE_GOLD_COLOR,
10930
11112
  emissive: 12092939,
@@ -10953,7 +11135,8 @@ function XViewScene({
10953
11135
  }, []);
10954
11136
  const handleVersionTimeline = useCallback4((sourceMesh, versionMeshes) => {
10955
11137
  const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
10956
- if (!tweenGroup || !timelineIntervalsGroup || versionMeshes.length === 0) return;
11138
+ if (!tweenGroup || !timelineIntervalsGroup || versionMeshes.length === 0)
11139
+ return;
10957
11140
  versionMeshes.forEach((mesh) => {
10958
11141
  const oldLabel = mesh.getObjectByName("timelineLabel");
10959
11142
  if (oldLabel) {
@@ -10969,7 +11152,8 @@ function XViewScene({
10969
11152
  }
10970
11153
  if (mesh.userData.timelineEndLabel) {
10971
11154
  timelineIntervalsGroup.remove(mesh.userData.timelineEndLabel);
10972
- if (mesh.userData.timelineEndLabel.material.map) mesh.userData.timelineEndLabel.material.map.dispose();
11155
+ if (mesh.userData.timelineEndLabel.material.map)
11156
+ mesh.userData.timelineEndLabel.material.map.dispose();
10973
11157
  mesh.userData.timelineEndLabel.material.dispose();
10974
11158
  delete mesh.userData.timelineEndLabel;
10975
11159
  }
@@ -11029,8 +11213,15 @@ function XViewScene({
11029
11213
  });
11030
11214
  if (timelineNodes.length === 0) return;
11031
11215
  const sortedTimePoints = Array.from(allTimePoints).sort((a, b) => a - b);
11032
- const timeToYMap = new Map(sortedTimePoints.map((time, index) => [time, index * TIMELINE_LAYER_SPACING_Y]));
11033
- timelineNodes.sort((a, b) => a.startDate - b.startDate || a.endDate - b.endDate);
11216
+ const timeToYMap = new Map(
11217
+ sortedTimePoints.map((time, index) => [
11218
+ time,
11219
+ index * TIMELINE_LAYER_SPACING_Y
11220
+ ])
11221
+ );
11222
+ timelineNodes.sort(
11223
+ (a, b) => a.startDate - b.startDate || a.endDate - b.endDate
11224
+ );
11034
11225
  const baseX = sourceMesh.position.x + TIMELINE_NODE_SPACING_X;
11035
11226
  const baseY = sourceMesh.position.y;
11036
11227
  const maxNodeIndex = timelineNodes.length - 1;
@@ -11048,7 +11239,12 @@ function XViewScene({
11048
11239
  if (type === "interval") {
11049
11240
  const relativeEndY = timeToYMap.get(endDate.getTime()) - timeToYMap.get(startDate.getTime());
11050
11241
  if (relativeEndY > 0) {
11051
- const barGeometry = new THREE3.CylinderGeometry(TIMELINE_INTERVAL_BAR_RADIUS, TIMELINE_INTERVAL_BAR_RADIUS, 1, 16);
11242
+ const barGeometry = new THREE3.CylinderGeometry(
11243
+ TIMELINE_INTERVAL_BAR_RADIUS,
11244
+ TIMELINE_INTERVAL_BAR_RADIUS,
11245
+ 1,
11246
+ 16
11247
+ );
11052
11248
  const barMaterial = new THREE3.MeshStandardMaterial({
11053
11249
  color: TIMELINE_GOLD_COLOR,
11054
11250
  emissive: 12092939,
@@ -11085,15 +11281,31 @@ function XViewScene({
11085
11281
  try {
11086
11282
  const typeStr = (viewParams == null ? void 0 : viewParams.type) || "";
11087
11283
  const sceneType = typeStr.toLowerCase().includes("database") ? "database" : "view";
11088
- const scenePromise = get_scene_view_data(configPath, ownerId2, typeStr, session, focusNodeId, focusAncestryId);
11284
+ const scenePromise = get_scene_view_data(
11285
+ configPath,
11286
+ ownerId2,
11287
+ typeStr,
11288
+ session,
11289
+ focusNodeId,
11290
+ focusAncestryId
11291
+ );
11089
11292
  const boardPromise = get_ancestry_board_action && session ? get_ancestry_board_action(configPath, sceneType, session, ownerId2) : Promise.resolve({ success: false, data: [] });
11090
- const [sceneResponse, boardResponse] = await Promise.all([scenePromise, boardPromise]);
11293
+ const [sceneResponse, boardResponse] = await Promise.all([
11294
+ scenePromise,
11295
+ boardPromise
11296
+ ]);
11091
11297
  if ((sceneResponse == null ? void 0 : sceneResponse.success) && ((_a2 = sceneResponse.data) == null ? void 0 : _a2.scene) && ((_b2 = sceneResponse.data) == null ? void 0 : _b2.parent)) {
11092
11298
  if (focusNodeId) {
11093
- let targetNode = sceneResponse.data.scene.nodes.find((n) => String(n.id) === String(focusNodeId));
11299
+ let targetNode = sceneResponse.data.scene.nodes.find(
11300
+ (n) => String(n.id) === String(focusNodeId)
11301
+ );
11094
11302
  if (!targetNode) {
11095
- const allParentNodes = Object.values(sceneResponse.data.parent).flatMap((f) => f.nodes || []);
11096
- targetNode = allParentNodes.find((n) => String(n.id) === String(focusNodeId));
11303
+ const allParentNodes = Object.values(
11304
+ sceneResponse.data.parent
11305
+ ).flatMap((f) => f.nodes || []);
11306
+ targetNode = allParentNodes.find(
11307
+ (n) => String(n.id) === String(focusNodeId)
11308
+ );
11097
11309
  }
11098
11310
  if (targetNode) {
11099
11311
  sceneResponse.data.scene.nodes = [targetNode];
@@ -11106,12 +11318,24 @@ function XViewScene({
11106
11318
  sceneDataRef.current = sceneResponse.data.scene;
11107
11319
  parentDataRef.current = sceneResponse.data.parent;
11108
11320
  ancestryDataRef.current = sceneResponse.data.ancestry;
11109
- console.log("Console de sceneResponse.data.scene:", sceneResponse.data.scene);
11110
- console.log("Console de sceneResponse.data.parent:", sceneResponse.data.parent);
11111
- console.log("Console de sceneResponse.data.ancestry:", sceneResponse.data.ancestry);
11321
+ console.log(
11322
+ "Console de sceneResponse.data.scene:",
11323
+ sceneResponse.data.scene
11324
+ );
11325
+ console.log(
11326
+ "Console de sceneResponse.data.parent:",
11327
+ sceneResponse.data.parent
11328
+ );
11329
+ console.log(
11330
+ "Console de sceneResponse.data.ancestry:",
11331
+ sceneResponse.data.ancestry
11332
+ );
11112
11333
  setIsInitialized(true);
11113
11334
  } else {
11114
- console.error("Falha ao buscar dados da cena:", (sceneResponse == null ? void 0 : sceneResponse.error) || "Resposta inv\xE1lida.");
11335
+ console.error(
11336
+ "Falha ao buscar dados da cena:",
11337
+ (sceneResponse == null ? void 0 : sceneResponse.error) || "Resposta inv\xE1lida."
11338
+ );
11115
11339
  }
11116
11340
  if (boardResponse == null ? void 0 : boardResponse.success) {
11117
11341
  setAncestryBoardData(boardResponse.data);
@@ -11130,7 +11354,9 @@ function XViewScene({
11130
11354
  console.error("Usu\xE1rio n\xE3o autenticado. Acesso negado.");
11131
11355
  setIsLoading(false);
11132
11356
  } else if (!sceneConfigId && status !== "loading") {
11133
- console.warn("Nenhum par\xE2metro de cena encontrado na URL ou falha na decripta\xE7\xE3o.");
11357
+ console.warn(
11358
+ "Nenhum par\xE2metro de cena encontrado na URL ou falha na decripta\xE7\xE3o."
11359
+ );
11134
11360
  setIsLoading(false);
11135
11361
  }
11136
11362
  }, [
@@ -11151,33 +11377,46 @@ function XViewScene({
11151
11377
  const objs = stateRef.current.nodeObjects || {};
11152
11378
  return !!objs[key];
11153
11379
  }, []);
11154
- const addOrUpdateNodeMesh = useCallback4((nodeData, position, suppressVersionUpdate = false) => {
11155
- const { graphGroup, nodeObjects, clickableNodes, glowTexture, tweenGroup } = stateRef.current;
11156
- const nodeId = String(nodeData.id);
11157
- if (nodeObjects[nodeId]) {
11158
- const existingMesh = nodeObjects[nodeId];
11159
- if (position) {
11160
- const updateTween = new Tween2(existingMesh.position).to(position, 800).easing(Easing2.Cubic.Out);
11161
- tweenGroup.add(updateTween);
11162
- updateTween.start();
11163
- }
11164
- return existingMesh;
11165
- }
11166
- const mesh = createNodeMesh(nodeData, position || new THREE3.Vector3(), glowTexture);
11167
- graphGroup.add(mesh);
11168
- if (mesh.userData.labelObject) {
11169
- if (mesh.userData.labelOffset) {
11170
- mesh.userData.labelObject.position.copy(mesh.position).add(mesh.userData.labelOffset);
11380
+ const addOrUpdateNodeMesh = useCallback4(
11381
+ (nodeData, position, suppressVersionUpdate = false) => {
11382
+ const {
11383
+ graphGroup,
11384
+ nodeObjects,
11385
+ clickableNodes,
11386
+ glowTexture,
11387
+ tweenGroup
11388
+ } = stateRef.current;
11389
+ const nodeId = String(nodeData.id);
11390
+ if (nodeObjects[nodeId]) {
11391
+ const existingMesh = nodeObjects[nodeId];
11392
+ if (position) {
11393
+ const updateTween = new Tween2(existingMesh.position).to(position, 800).easing(Easing2.Cubic.Out);
11394
+ tweenGroup.add(updateTween);
11395
+ updateTween.start();
11396
+ }
11397
+ return existingMesh;
11398
+ }
11399
+ const mesh = createNodeMesh(
11400
+ nodeData,
11401
+ position || new THREE3.Vector3(),
11402
+ glowTexture
11403
+ );
11404
+ graphGroup.add(mesh);
11405
+ if (mesh.userData.labelObject) {
11406
+ if (mesh.userData.labelOffset) {
11407
+ mesh.userData.labelObject.position.copy(mesh.position).add(mesh.userData.labelOffset);
11408
+ }
11409
+ graphGroup.add(mesh.userData.labelObject);
11171
11410
  }
11172
- graphGroup.add(mesh.userData.labelObject);
11173
- }
11174
- nodeObjects[nodeId] = mesh;
11175
- clickableNodes.push(mesh);
11176
- if (!suppressVersionUpdate) {
11177
- setSceneVersion((v) => v + 1);
11178
- }
11179
- return mesh;
11180
- }, []);
11411
+ nodeObjects[nodeId] = mesh;
11412
+ clickableNodes.push(mesh);
11413
+ if (!suppressVersionUpdate) {
11414
+ setSceneVersion((v) => v + 1);
11415
+ }
11416
+ return mesh;
11417
+ },
11418
+ []
11419
+ );
11181
11420
  useEffect22(() => {
11182
11421
  if (!isInitialized || !sceneDataRef.current) return;
11183
11422
  const currentMount = mountRef.current;
@@ -11192,7 +11431,12 @@ function XViewScene({
11192
11431
  const scene = new THREE3.Scene();
11193
11432
  scene.background = new THREE3.Color(0);
11194
11433
  stateRef.current.scene = scene;
11195
- const camera = new THREE3.PerspectiveCamera(75, currentMount.clientWidth / currentMount.clientHeight, 0.1, 2e3);
11434
+ const camera = new THREE3.PerspectiveCamera(
11435
+ 75,
11436
+ currentMount.clientWidth / currentMount.clientHeight,
11437
+ 0.1,
11438
+ 2e3
11439
+ );
11196
11440
  camera.position.set(0, 60, 120);
11197
11441
  stateRef.current.camera = camera;
11198
11442
  const renderer = new THREE3.WebGLRenderer({ antialias: true });
@@ -11213,7 +11457,12 @@ function XViewScene({
11213
11457
  directionalLight.position.set(50, 50, 50);
11214
11458
  scene.add(directionalLight);
11215
11459
  const renderScene = new RenderPass(scene, camera);
11216
- const bloomPass = new UnrealBloomPass(new THREE3.Vector2(currentMount.clientWidth, currentMount.clientHeight), x_view_config.BLOOM_EFFECT.strength, x_view_config.BLOOM_EFFECT.radius, x_view_config.BLOOM_EFFECT.threshold);
11460
+ const bloomPass = new UnrealBloomPass(
11461
+ new THREE3.Vector2(currentMount.clientWidth, currentMount.clientHeight),
11462
+ x_view_config.BLOOM_EFFECT.strength,
11463
+ x_view_config.BLOOM_EFFECT.radius,
11464
+ x_view_config.BLOOM_EFFECT.threshold
11465
+ );
11217
11466
  const composer = new EffectComposer(renderer);
11218
11467
  composer.addPass(renderScene);
11219
11468
  composer.addPass(bloomPass);
@@ -11256,8 +11505,16 @@ function XViewScene({
11256
11505
  const sourceNode = nodeObjects[String(linksArray[0].source)];
11257
11506
  const targetNode = nodeObjects[String(linksArray[0].target)];
11258
11507
  if (sourceNode && targetNode) {
11259
- const resolution = new THREE3.Vector2(currentMount.clientWidth, currentMount.clientHeight);
11260
- const newLinks = createMultipleLinkLines(linksArray, sourceNode, targetNode, resolution);
11508
+ const resolution = new THREE3.Vector2(
11509
+ currentMount.clientWidth,
11510
+ currentMount.clientHeight
11511
+ );
11512
+ const newLinks = createMultipleLinkLines(
11513
+ linksArray,
11514
+ sourceNode,
11515
+ targetNode,
11516
+ resolution
11517
+ );
11261
11518
  newLinks.forEach((line, idx) => {
11262
11519
  const meta = linksArray[idx];
11263
11520
  if (meta) {
@@ -11298,7 +11555,10 @@ function XViewScene({
11298
11555
  function tryPickNode() {
11299
11556
  raycaster.setFromCamera(mouse, camera);
11300
11557
  raycaster.layers.enable(GHOST_BLOOM_LAYER);
11301
- const intersects = raycaster.intersectObjects(stateRef.current.clickableNodes, false);
11558
+ const intersects = raycaster.intersectObjects(
11559
+ stateRef.current.clickableNodes,
11560
+ false
11561
+ );
11302
11562
  return intersects.length > 0 ? intersects[0].object : null;
11303
11563
  }
11304
11564
  function findClosestLinkToRay() {
@@ -11310,7 +11570,10 @@ function XViewScene({
11310
11570
  x: (mouse.x * 0.5 + 0.5) * clientWidth,
11311
11571
  y: (-mouse.y * 0.5 + 0.5) * clientHeight
11312
11572
  };
11313
- const allVisibleLinks = [...stateRef.current.allLinks, ...stateRef.current.ancestryLinks];
11573
+ const allVisibleLinks = [
11574
+ ...stateRef.current.allLinks,
11575
+ ...stateRef.current.ancestryLinks
11576
+ ];
11314
11577
  const THRESH = x_view_config.LINE_HOVER_THRESHOLD_PX + 4;
11315
11578
  const tmpP1 = new THREE3.Vector3();
11316
11579
  const tmpP2 = new THREE3.Vector3();
@@ -11325,19 +11588,35 @@ function XViewScene({
11325
11588
  const up = new THREE3.Vector3(0, 1, 0);
11326
11589
  const normal = new THREE3.Vector3().crossVectors(dir, up).normalize();
11327
11590
  const controlPoint = mid.add(normal.multiplyScalar(curveOffset || 0));
11328
- const curve = new THREE3.QuadraticBezierCurve3(start, controlPoint, end);
11591
+ const curve = new THREE3.QuadraticBezierCurve3(
11592
+ start,
11593
+ controlPoint,
11594
+ end
11595
+ );
11329
11596
  const points = curve.getPoints(x_view_config.CURVE_SEGMENTS || 32);
11330
11597
  for (let i = 0; i < points.length - 1; i++) {
11331
11598
  tmpP1.copy(points[i]).project(camera);
11332
11599
  tmpP2.copy(points[i + 1]).project(camera);
11333
- const p1_px = { x: (tmpP1.x * 0.5 + 0.5) * clientWidth, y: (-tmpP1.y * 0.5 + 0.5) * clientHeight };
11334
- const p2_px = { x: (tmpP2.x * 0.5 + 0.5) * clientWidth, y: (-tmpP2.y * 0.5 + 0.5) * clientHeight };
11600
+ const p1_px = {
11601
+ x: (tmpP1.x * 0.5 + 0.5) * clientWidth,
11602
+ y: (-tmpP1.y * 0.5 + 0.5) * clientHeight
11603
+ };
11604
+ const p2_px = {
11605
+ x: (tmpP2.x * 0.5 + 0.5) * clientWidth,
11606
+ y: (-tmpP2.y * 0.5 + 0.5) * clientHeight
11607
+ };
11335
11608
  const L2 = (p2_px.x - p1_px.x) ** 2 + (p2_px.y - p1_px.y) ** 2;
11336
11609
  if (L2 === 0) continue;
11337
11610
  let t = ((mousePixels.x - p1_px.x) * (p2_px.x - p1_px.x) + (mousePixels.y - p1_px.y) * (p2_px.y - p1_px.y)) / L2;
11338
11611
  t = Math.max(0, Math.min(1, t));
11339
- const closestPoint = { x: p1_px.x + t * (p2_px.x - p1_px.x), y: p1_px.y + t * (p2_px.y - p1_px.y) };
11340
- const dist = Math.hypot(mousePixels.x - closestPoint.x, mousePixels.y - closestPoint.y);
11612
+ const closestPoint = {
11613
+ x: p1_px.x + t * (p2_px.x - p1_px.x),
11614
+ y: p1_px.y + t * (p2_px.y - p1_px.y)
11615
+ };
11616
+ const dist = Math.hypot(
11617
+ mousePixels.x - closestPoint.x,
11618
+ mousePixels.y - closestPoint.y
11619
+ );
11341
11620
  if (dist < THRESH && dist < minDistance) {
11342
11621
  minDistance = dist;
11343
11622
  closestLink = link;
@@ -11346,14 +11625,26 @@ function XViewScene({
11346
11625
  } else {
11347
11626
  const p1 = new THREE3.Vector3().copy(sourceNode.position).project(camera);
11348
11627
  const p2 = new THREE3.Vector3().copy(targetNode.position).project(camera);
11349
- const p1_px = { x: (p1.x * 0.5 + 0.5) * clientWidth, y: (-p1.y * 0.5 + 0.5) * clientHeight };
11350
- const p2_px = { x: (p2.x * 0.5 + 0.5) * clientWidth, y: (-p2.y * 0.5 + 0.5) * clientHeight };
11628
+ const p1_px = {
11629
+ x: (p1.x * 0.5 + 0.5) * clientWidth,
11630
+ y: (-p1.y * 0.5 + 0.5) * clientHeight
11631
+ };
11632
+ const p2_px = {
11633
+ x: (p2.x * 0.5 + 0.5) * clientWidth,
11634
+ y: (-p2.y * 0.5 + 0.5) * clientHeight
11635
+ };
11351
11636
  const L2 = (p2_px.x - p1_px.x) ** 2 + (p2_px.y - p1_px.y) ** 2;
11352
11637
  if (L2 === 0) return;
11353
11638
  let t = ((mousePixels.x - p1_px.x) * (p2_px.x - p1_px.x) + (mousePixels.y - p1_px.y) * (p2_px.y - p1_px.y)) / L2;
11354
11639
  t = Math.max(0, Math.min(1, t));
11355
- const closestPoint = { x: p1_px.x + t * (p2_px.x - p1_px.x), y: p1_px.y + t * (p2_px.y - p1_px.y) };
11356
- const dist = Math.hypot(mousePixels.x - closestPoint.x, mousePixels.y - closestPoint.y);
11640
+ const closestPoint = {
11641
+ x: p1_px.x + t * (p2_px.x - p1_px.x),
11642
+ y: p1_px.y + t * (p2_px.y - p1_px.y)
11643
+ };
11644
+ const dist = Math.hypot(
11645
+ mousePixels.x - closestPoint.x,
11646
+ mousePixels.y - closestPoint.y
11647
+ );
11357
11648
  if (dist < THRESH && dist < minDistance) {
11358
11649
  minDistance = dist;
11359
11650
  closestLink = link;
@@ -11388,7 +11679,11 @@ function XViewScene({
11388
11679
  if (picked && !stateRef.current.ancestry.isActive) {
11389
11680
  stateRef.current.controls.enabled = false;
11390
11681
  }
11391
- stateRef.current.pointerDown = { isDown: true, x: event.clientX, y: event.clientY };
11682
+ stateRef.current.pointerDown = {
11683
+ isDown: true,
11684
+ x: event.clientX,
11685
+ y: event.clientY
11686
+ };
11392
11687
  stateRef.current.dragCandidate = picked;
11393
11688
  }
11394
11689
  function onPointerMove(event) {
@@ -11399,7 +11694,10 @@ function XViewScene({
11399
11694
  const raycaster2 = new THREE3.Raycaster();
11400
11695
  raycaster2.setFromCamera(mouse, camera);
11401
11696
  camera.getWorldDirection(plane.normal);
11402
- plane.setFromNormalAndCoplanarPoint(plane.normal, stateRef.current.draggedNode.position);
11697
+ plane.setFromNormalAndCoplanarPoint(
11698
+ plane.normal,
11699
+ stateRef.current.draggedNode.position
11700
+ );
11403
11701
  if (raycaster2.ray.intersectPlane(plane, intersectionPoint)) {
11404
11702
  const draggedNode = stateRef.current.draggedNode;
11405
11703
  draggedNode.position.copy(intersectionPoint);
@@ -11408,7 +11706,8 @@ function XViewScene({
11408
11706
  }
11409
11707
  if (stateRef.current.connection.isActive || stateRef.current.relink.isActive || stateRef.current.ancestry.isActive) {
11410
11708
  const newHoveredNode2 = tryPickNode();
11411
- if (stateRef.current.hoveredNode !== newHoveredNode2) stateRef.current.hoveredNode = newHoveredNode2;
11709
+ if (stateRef.current.hoveredNode !== newHoveredNode2)
11710
+ stateRef.current.hoveredNode = newHoveredNode2;
11412
11711
  if (currentMount) {
11413
11712
  let fixedId = null;
11414
11713
  if (stateRef.current.connection.isActive) {
@@ -11439,14 +11738,20 @@ function XViewScene({
11439
11738
  stateRef.current.controls.enabled = false;
11440
11739
  if (currentMount) currentMount.style.cursor = "grabbing";
11441
11740
  camera.getWorldDirection(plane.normal);
11442
- plane.setFromNormalAndCoplanarPoint(plane.normal, stateRef.current.draggedNode.position);
11741
+ plane.setFromNormalAndCoplanarPoint(
11742
+ plane.normal,
11743
+ stateRef.current.draggedNode.position
11744
+ );
11443
11745
  }
11444
11746
  }
11445
11747
  const newHoveredNode = tryPickNode();
11446
11748
  const newHoveredLink = !newHoveredNode ? findClosestLinkToRay() : null;
11447
- if (stateRef.current.hoveredNode !== newHoveredNode) stateRef.current.hoveredNode = newHoveredNode;
11448
- if (stateRef.current.hoveredLink !== newHoveredLink) stateRef.current.hoveredLink = newHoveredLink;
11449
- if (currentMount) currentMount.style.cursor = newHoveredNode || newHoveredLink ? "pointer" : "grab";
11749
+ if (stateRef.current.hoveredNode !== newHoveredNode)
11750
+ stateRef.current.hoveredNode = newHoveredNode;
11751
+ if (stateRef.current.hoveredLink !== newHoveredLink)
11752
+ stateRef.current.hoveredLink = newHoveredLink;
11753
+ if (currentMount)
11754
+ currentMount.style.cursor = newHoveredNode || newHoveredLink ? "pointer" : "grab";
11450
11755
  }
11451
11756
  const isNodeInTree = (tree, nodeId) => {
11452
11757
  if (!tree) return false;
@@ -11461,7 +11766,10 @@ function XViewScene({
11461
11766
  const context = actionHandlerContext;
11462
11767
  if (connection.isActive) {
11463
11768
  if (hoveredNode && String(hoveredNode.userData.id) !== String(connection.sourceNodeData.id)) {
11464
- await userActionHandlers.handleCompleteConnection(context, hoveredNode.userData);
11769
+ await userActionHandlers.handleCompleteConnection(
11770
+ context,
11771
+ hoveredNode.userData
11772
+ );
11465
11773
  } else {
11466
11774
  userActionHandlers.handleCancelConnection(context);
11467
11775
  }
@@ -11469,7 +11777,10 @@ function XViewScene({
11469
11777
  }
11470
11778
  if (relink.isActive) {
11471
11779
  if (hoveredNode && String(hoveredNode.userData.id) !== String(relink.fixedNodeId)) {
11472
- await userActionHandlers.handleCompleteRelink(context, hoveredNode.userData);
11780
+ await userActionHandlers.handleCompleteRelink(
11781
+ context,
11782
+ hoveredNode.userData
11783
+ );
11473
11784
  } else {
11474
11785
  userActionHandlers.handleCancelRelink(context);
11475
11786
  }
@@ -11487,7 +11798,9 @@ function XViewScene({
11487
11798
  const clickedNodeId = String(clickedNode.userData.id);
11488
11799
  const parentId = String(currentSelectedParent);
11489
11800
  if (clickedNodeId === parentId) {
11490
- alert("Erro: N\xE3o \xE9 poss\xEDvel adicionar um Node como filho dele mesmo.");
11801
+ alert(
11802
+ "Erro: N\xE3o \xE9 poss\xEDvel adicionar um Node como filho dele mesmo."
11803
+ );
11491
11804
  return;
11492
11805
  }
11493
11806
  const parentInfo = stateRef.current.nodeIdToParentFileMap.get(clickedNodeId);
@@ -11497,16 +11810,33 @@ function XViewScene({
11497
11810
  const addChildToNode = (current, targetParentId, childNode) => {
11498
11811
  const currentId = current.is_section ? current.id || current.section_id : String(current.node.id);
11499
11812
  if (String(currentId) === String(targetParentId)) {
11500
- const alreadyExists = current.children.some((child) => !child.is_section && String(child.node.id) === String(childNode.id));
11813
+ const alreadyExists = current.children.some(
11814
+ (child) => !child.is_section && String(child.node.id) === String(childNode.id)
11815
+ );
11501
11816
  if (alreadyExists) return current;
11502
- return { ...current, children: [...current.children, { node: childNode, children: [], relationship: {} }] };
11817
+ return {
11818
+ ...current,
11819
+ children: [
11820
+ ...current.children,
11821
+ { node: childNode, children: [], relationship: {} }
11822
+ ]
11823
+ };
11503
11824
  }
11504
- return { ...current, children: current.children.map((c) => addChildToNode(c, targetParentId, childNode)) };
11825
+ return {
11826
+ ...current,
11827
+ children: current.children.map(
11828
+ (c) => addChildToNode(c, targetParentId, childNode)
11829
+ )
11830
+ };
11505
11831
  };
11506
11832
  setAncestryMode((prev) => {
11507
11833
  const treeKey = isAbstraction ? "abstraction_tree" : "tree";
11508
11834
  if (!prev[treeKey]) return prev;
11509
- const newTree = addChildToNode(prev[treeKey], parentId, fullNodeData);
11835
+ const newTree = addChildToNode(
11836
+ prev[treeKey],
11837
+ parentId,
11838
+ fullNodeData
11839
+ );
11510
11840
  return { ...prev, [treeKey]: newTree };
11511
11841
  });
11512
11842
  }
@@ -11520,7 +11850,8 @@ function XViewScene({
11520
11850
  stateRef.current.dragCandidate = null;
11521
11851
  stateRef.current.pointerDown.isDown = false;
11522
11852
  stateRef.current.controls.enabled = true;
11523
- if (currentMount) currentMount.style.cursor = stateRef.current.hoveredNode || stateRef.current.hoveredLink ? "pointer" : "grab";
11853
+ if (currentMount)
11854
+ currentMount.style.cursor = stateRef.current.hoveredNode || stateRef.current.hoveredLink ? "pointer" : "grab";
11524
11855
  return;
11525
11856
  }
11526
11857
  const dragDistance = Math.hypot(
@@ -11568,16 +11899,21 @@ function XViewScene({
11568
11899
  }
11569
11900
  function handleDoubleClick(event) {
11570
11901
  if (stateRef.current.camera) stateRef.current.camera.layers.enableAll();
11571
- if (isFromUiOverlay(event) || stateRef.current.isDragging || stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive) return;
11572
- if (stateRef.current.hoveredNode) tweenToTarget(stateRef.current.hoveredNode);
11902
+ if (isFromUiOverlay(event) || stateRef.current.isDragging || stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive)
11903
+ return;
11904
+ if (stateRef.current.hoveredNode)
11905
+ tweenToTarget(stateRef.current.hoveredNode);
11573
11906
  }
11574
11907
  function handleContextMenu(event) {
11575
11908
  if (stateRef.current.camera) stateRef.current.camera.layers.enableAll();
11576
11909
  if (isFromUiOverlay(event)) return;
11577
11910
  event.preventDefault();
11578
- if (stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive) return;
11911
+ if (stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive)
11912
+ return;
11579
11913
  setMouseFromEvent(event);
11580
- setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
11914
+ setContextMenu(
11915
+ (prev) => prev.visible ? { ...prev, visible: false } : prev
11916
+ );
11581
11917
  setMultiContextMenu((prev) => ({ ...prev, visible: false }));
11582
11918
  setRelationshipMenu((prev) => ({ ...prev, visible: false }));
11583
11919
  const pickedNode = tryPickNode();
@@ -11608,7 +11944,12 @@ function XViewScene({
11608
11944
  return;
11609
11945
  }
11610
11946
  stateRef.current.selectedNodes.clear();
11611
- setRelationshipMenu({ visible: true, x: event.clientX, y: event.clientY, linkObject: pickedLink });
11947
+ setRelationshipMenu({
11948
+ visible: true,
11949
+ x: event.clientX,
11950
+ y: event.clientY,
11951
+ linkObject: pickedLink
11952
+ });
11612
11953
  return;
11613
11954
  }
11614
11955
  stateRef.current.selectedNodes.clear();
@@ -11640,7 +11981,10 @@ function XViewScene({
11640
11981
  }
11641
11982
  });
11642
11983
  }
11643
- const allRenderedLinks = [...stateRef.current.allLinks, ...stateRef.current.ancestryLinks];
11984
+ const allRenderedLinks = [
11985
+ ...stateRef.current.allLinks,
11986
+ ...stateRef.current.ancestryLinks
11987
+ ];
11644
11988
  allRenderedLinks.forEach((line) => {
11645
11989
  const { sourceNode, targetNode, isCurved, curveOffset } = line.userData;
11646
11990
  if (sourceNode && targetNode) {
@@ -11652,13 +11996,20 @@ function XViewScene({
11652
11996
  const up = new THREE3.Vector3(0, 1, 0);
11653
11997
  const normal = new THREE3.Vector3().crossVectors(dir, up).normalize();
11654
11998
  const controlPoint = mid.add(normal.multiplyScalar(curveOffset));
11655
- const curve = new THREE3.QuadraticBezierCurve3(start, controlPoint, end);
11999
+ const curve = new THREE3.QuadraticBezierCurve3(
12000
+ start,
12001
+ controlPoint,
12002
+ end
12003
+ );
11656
12004
  const points = curve.getPoints(x_view_config.CURVE_SEGMENTS);
11657
12005
  const positions = [];
11658
12006
  points.forEach((p) => positions.push(p.x, p.y, p.z));
11659
12007
  line.geometry.setPositions(positions);
11660
12008
  } else {
11661
- line.geometry.setPositions([...sourceNode.position.toArray(), ...targetNode.position.toArray()]);
12009
+ line.geometry.setPositions([
12010
+ ...sourceNode.position.toArray(),
12011
+ ...targetNode.position.toArray()
12012
+ ]);
11662
12013
  }
11663
12014
  }
11664
12015
  });
@@ -11671,7 +12022,11 @@ function XViewScene({
11671
12022
  const startPos = node.position;
11672
12023
  const distance = startPos.distanceTo(endPos);
11673
12024
  bar.scale.y = distance;
11674
- const midpoint = new THREE3.Vector3().lerpVectors(startPos, endPos, 0.5);
12025
+ const midpoint = new THREE3.Vector3().lerpVectors(
12026
+ startPos,
12027
+ endPos,
12028
+ 0.5
12029
+ );
11675
12030
  bar.position.copy(midpoint);
11676
12031
  const direction = new THREE3.Vector3().subVectors(endPos, startPos).normalize();
11677
12032
  const upVector = new THREE3.Vector3(0, 1, 0);
@@ -11682,7 +12037,11 @@ function XViewScene({
11682
12037
  const { ghostElements, creation, connection, relink } = stateRef.current;
11683
12038
  if (creation.isActive && ghostElements.node && ghostElements.line) {
11684
12039
  const srcMesh = stateRef.current.nodeObjects[String(creation.sourceNodeData.id)];
11685
- if (srcMesh) ghostElements.line.geometry.setPositions([...srcMesh.position.toArray(), ...ghostElements.node.position.toArray()]);
12040
+ if (srcMesh)
12041
+ ghostElements.line.geometry.setPositions([
12042
+ ...srcMesh.position.toArray(),
12043
+ ...ghostElements.node.position.toArray()
12044
+ ]);
11686
12045
  }
11687
12046
  if (connection.isActive && connection.line) {
11688
12047
  const srcMesh = stateRef.current.nodeObjects[String(connection.sourceNodeData.id)];
@@ -11691,7 +12050,11 @@ function XViewScene({
11691
12050
  raycaster2.setFromCamera(mouse, camera);
11692
12051
  camera.getWorldDirection(plane.normal);
11693
12052
  plane.setFromNormalAndCoplanarPoint(plane.normal, srcMesh.position);
11694
- if (raycaster2.ray.intersectPlane(plane, intersectionPoint)) connection.line.geometry.setPositions([...srcMesh.position.toArray(), ...intersectionPoint.toArray()]);
12053
+ if (raycaster2.ray.intersectPlane(plane, intersectionPoint))
12054
+ connection.line.geometry.setPositions([
12055
+ ...srcMesh.position.toArray(),
12056
+ ...intersectionPoint.toArray()
12057
+ ]);
11695
12058
  }
11696
12059
  }
11697
12060
  if (relink.isActive && relink.line) {
@@ -11701,7 +12064,11 @@ function XViewScene({
11701
12064
  raycaster2.setFromCamera(mouse, camera);
11702
12065
  camera.getWorldDirection(plane.normal);
11703
12066
  plane.setFromNormalAndCoplanarPoint(plane.normal, fixedMesh.position);
11704
- if (raycaster2.ray.intersectPlane(plane, intersectionPoint)) relink.line.geometry.setPositions([...fixedMesh.position.toArray(), ...intersectionPoint.toArray()]);
12067
+ if (raycaster2.ray.intersectPlane(plane, intersectionPoint))
12068
+ relink.line.geometry.setPositions([
12069
+ ...fixedMesh.position.toArray(),
12070
+ ...intersectionPoint.toArray()
12071
+ ]);
11705
12072
  }
11706
12073
  }
11707
12074
  Object.values(stateRef.current.nodeObjects).forEach((node) => {
@@ -11745,11 +12112,18 @@ function XViewScene({
11745
12112
  renderer.setSize(clientWidth, clientHeight);
11746
12113
  composer.setSize(clientWidth, clientHeight);
11747
12114
  const resVec = new THREE3.Vector2(clientWidth, clientHeight);
11748
- stateRef.current.allLinks.forEach((line) => line.material.resolution.copy(resVec));
11749
- stateRef.current.ancestryLinks.forEach((line) => line.material.resolution.copy(resVec));
11750
- if (stateRef.current.ghostElements.line) stateRef.current.ghostElements.line.material.resolution.copy(resVec);
11751
- if (stateRef.current.connection.line) stateRef.current.connection.line.material.resolution.copy(resVec);
11752
- if (stateRef.current.relink.line) stateRef.current.relink.line.material.resolution.copy(resVec);
12115
+ stateRef.current.allLinks.forEach(
12116
+ (line) => line.material.resolution.copy(resVec)
12117
+ );
12118
+ stateRef.current.ancestryLinks.forEach(
12119
+ (line) => line.material.resolution.copy(resVec)
12120
+ );
12121
+ if (stateRef.current.ghostElements.line)
12122
+ stateRef.current.ghostElements.line.material.resolution.copy(resVec);
12123
+ if (stateRef.current.connection.line)
12124
+ stateRef.current.connection.line.material.resolution.copy(resVec);
12125
+ if (stateRef.current.relink.line)
12126
+ stateRef.current.relink.line.material.resolution.copy(resVec);
11753
12127
  }
11754
12128
  window.addEventListener("resize", handleResize);
11755
12129
  return () => {
@@ -11769,14 +12143,16 @@ function XViewScene({
11769
12143
  ancestryGroup.traverse((obj) => {
11770
12144
  if (obj.geometry) obj.geometry.dispose();
11771
12145
  if (obj.material) {
11772
- if (Array.isArray(obj.material)) obj.material.forEach((m) => m.dispose());
12146
+ if (Array.isArray(obj.material))
12147
+ obj.material.forEach((m) => m.dispose());
11773
12148
  else obj.material.dispose();
11774
12149
  }
11775
12150
  });
11776
12151
  graphGroup.traverse((obj) => {
11777
12152
  if (obj.geometry) obj.geometry.dispose();
11778
12153
  if (obj.material) {
11779
- if (Array.isArray(obj.material)) obj.material.forEach((m) => m.dispose());
12154
+ if (Array.isArray(obj.material))
12155
+ obj.material.forEach((m) => m.dispose());
11780
12156
  else obj.material.dispose();
11781
12157
  }
11782
12158
  });
@@ -11787,9 +12163,22 @@ function XViewScene({
11787
12163
  currentMount.removeChild(renderer.domElement);
11788
12164
  }
11789
12165
  };
11790
- }, [isInitialized, tweenToTarget, dbSaveUrl, isNodeInView, addOrUpdateNodeMesh, handleActivateTimeline, get_scene_view_data, save_view_data]);
12166
+ }, [
12167
+ isInitialized,
12168
+ tweenToTarget,
12169
+ dbSaveUrl,
12170
+ isNodeInView,
12171
+ addOrUpdateNodeMesh,
12172
+ handleActivateTimeline,
12173
+ get_scene_view_data,
12174
+ save_view_data
12175
+ ]);
11791
12176
  const handleGhostNodeImageChange = useCallback4((useImage, imageUrl) => {
11792
- const { node: ghostNode, line: ghostLine, aura: ghostAura } = stateRef.current.ghostElements;
12177
+ const {
12178
+ node: ghostNode,
12179
+ line: ghostLine,
12180
+ aura: ghostAura
12181
+ } = stateRef.current.ghostElements;
11793
12182
  const { graphGroup, glowTexture } = stateRef.current;
11794
12183
  if (!ghostNode || !graphGroup) return;
11795
12184
  const currentData = {
@@ -11800,13 +12189,15 @@ function XViewScene({
11800
12189
  const position = ghostNode.position.clone();
11801
12190
  if (ghostNode.userData.labelObject) {
11802
12191
  graphGroup.remove(ghostNode.userData.labelObject);
11803
- if (ghostNode.userData.labelObject.material.map) ghostNode.userData.labelObject.material.map.dispose();
12192
+ if (ghostNode.userData.labelObject.material.map)
12193
+ ghostNode.userData.labelObject.material.map.dispose();
11804
12194
  ghostNode.userData.labelObject.material.dispose();
11805
12195
  }
11806
12196
  graphGroup.remove(ghostNode);
11807
12197
  if (ghostNode.geometry) ghostNode.geometry.dispose();
11808
12198
  if (ghostNode.material) {
11809
- if (Array.isArray(ghostNode.material)) ghostNode.material.forEach((m) => m.dispose());
12199
+ if (Array.isArray(ghostNode.material))
12200
+ ghostNode.material.forEach((m) => m.dispose());
11810
12201
  else ghostNode.material.dispose();
11811
12202
  }
11812
12203
  const newGhostNode = createNodeMesh(currentData, position, glowTexture);
@@ -11851,28 +12242,31 @@ function XViewScene({
11851
12242
  ghostAura.material.opacity = Math.min(0.8, newIntensity * 0.15);
11852
12243
  }
11853
12244
  }, []);
11854
- const handleDetailNodeIntensityChange = useCallback4((nodeId, newIntensity) => {
11855
- const mesh = stateRef.current.nodeObjects[String(nodeId)];
11856
- if (!mesh) return;
11857
- const adjustedIntensity = newIntensity + MIN_VISIBILITY_INTENSITY;
11858
- mesh.userData.intensity = newIntensity;
11859
- mesh.userData._baseEmissiveIntensity = adjustedIntensity;
11860
- const isImageNode = mesh.userData.useImageAsTexture === true;
11861
- if (isImageNode) {
11862
- const borderMesh = mesh.getObjectByName("borderRing");
11863
- if (borderMesh && borderMesh.material) {
11864
- borderMesh.material.emissiveIntensity = adjustedIntensity;
12245
+ const handleDetailNodeIntensityChange = useCallback4(
12246
+ (nodeId, newIntensity) => {
12247
+ const mesh = stateRef.current.nodeObjects[String(nodeId)];
12248
+ if (!mesh) return;
12249
+ const adjustedIntensity = newIntensity + MIN_VISIBILITY_INTENSITY;
12250
+ mesh.userData.intensity = newIntensity;
12251
+ mesh.userData._baseEmissiveIntensity = adjustedIntensity;
12252
+ const isImageNode = mesh.userData.useImageAsTexture === true;
12253
+ if (isImageNode) {
12254
+ const borderMesh = mesh.getObjectByName("borderRing");
12255
+ if (borderMesh && borderMesh.material) {
12256
+ borderMesh.material.emissiveIntensity = adjustedIntensity;
12257
+ }
12258
+ } else {
12259
+ if (mesh.material) {
12260
+ mesh.material.emissiveIntensity = adjustedIntensity;
12261
+ }
11865
12262
  }
11866
- } else {
11867
- if (mesh.material) {
11868
- mesh.material.emissiveIntensity = adjustedIntensity;
12263
+ const aura = mesh.getObjectByName("aura");
12264
+ if (aura && aura.material) {
12265
+ aura.material.opacity = Math.min(0.8, newIntensity * 0.15);
11869
12266
  }
11870
- }
11871
- const aura = mesh.getObjectByName("aura");
11872
- if (aura && aura.material) {
11873
- aura.material.opacity = Math.min(0.8, newIntensity * 0.15);
11874
- }
11875
- }, []);
12267
+ },
12268
+ []
12269
+ );
11876
12270
  const handleGhostNodeColorChange = (newColor) => {
11877
12271
  const { node: ghostNode, aura: ghostAura } = stateRef.current.ghostElements;
11878
12272
  if (!ghostNode) return;
@@ -11983,7 +12377,9 @@ function XViewScene({
11983
12377
  mesh.userData.size = newSize;
11984
12378
  };
11985
12379
  const handleStartAncestryCreation = (nodeData) => {
11986
- setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
12380
+ setContextMenu(
12381
+ (prev) => prev.visible ? { ...prev, visible: false } : prev
12382
+ );
11987
12383
  stateRef.current.maxAncestryRenderIndex = 0;
11988
12384
  setAncestryMode({
11989
12385
  isActive: true,
@@ -12008,9 +12404,12 @@ function XViewScene({
12008
12404
  const newTreeStr = JSON.stringify(newTree);
12009
12405
  let metaChanged = false;
12010
12406
  if (extraData) {
12011
- if (extraData.ancestryName !== void 0 && extraData.ancestryName !== prev.ancestryName) metaChanged = true;
12012
- if (extraData.ancestryDescription !== void 0 && extraData.ancestryDescription !== prev.ancestryDescription) metaChanged = true;
12013
- if (extraData.ancestryDescriptionSections !== void 0 && JSON.stringify(extraData.ancestryDescriptionSections) !== JSON.stringify(prev.ancestryDescriptionSections)) metaChanged = true;
12407
+ if (extraData.ancestryName !== void 0 && extraData.ancestryName !== prev.ancestryName)
12408
+ metaChanged = true;
12409
+ if (extraData.ancestryDescription !== void 0 && extraData.ancestryDescription !== prev.ancestryDescription)
12410
+ metaChanged = true;
12411
+ if (extraData.ancestryDescriptionSections !== void 0 && JSON.stringify(extraData.ancestryDescriptionSections) !== JSON.stringify(prev.ancestryDescriptionSections))
12412
+ metaChanged = true;
12014
12413
  }
12015
12414
  if (prevTreeStr === newTreeStr && !metaChanged) {
12016
12415
  return prev;
@@ -12088,13 +12487,15 @@ function XViewScene({
12088
12487
  if (ghostElements.node && graphGroup) {
12089
12488
  if (ghostElements.node.userData.labelObject) {
12090
12489
  graphGroup.remove(ghostElements.node.userData.labelObject);
12091
- if (ghostElements.node.userData.labelObject.material.map) ghostElements.node.userData.labelObject.material.map.dispose();
12490
+ if (ghostElements.node.userData.labelObject.material.map)
12491
+ ghostElements.node.userData.labelObject.material.map.dispose();
12092
12492
  ghostElements.node.userData.labelObject.material.dispose();
12093
12493
  }
12094
12494
  graphGroup.remove(ghostElements.node);
12095
12495
  ghostElements.node.traverse((child) => {
12096
12496
  if (child.material) {
12097
- if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());
12497
+ if (Array.isArray(child.material))
12498
+ child.material.forEach((m) => m.dispose());
12098
12499
  else child.material.dispose();
12099
12500
  }
12100
12501
  if (child.geometry) child.geometry.dispose();
@@ -12104,7 +12505,17 @@ function XViewScene({
12104
12505
  setQuestMode({ isActive: false });
12105
12506
  }, []);
12106
12507
  const handleSaveQuestNode = async (context, newQuestData) => {
12107
- const { graphDataRef, sceneDataRef: sceneDataRef2, stateRef: stateRef2, setters, actions, sceneSaveUrl: sceneSaveUrl2, viewType, sceneConfigId: sceneConfigId2, ownerId: ownerId2 } = context;
12508
+ const {
12509
+ graphDataRef,
12510
+ sceneDataRef: sceneDataRef2,
12511
+ stateRef: stateRef2,
12512
+ setters,
12513
+ actions,
12514
+ sceneSaveUrl: sceneSaveUrl2,
12515
+ viewType,
12516
+ sceneConfigId: sceneConfigId2,
12517
+ ownerId: ownerId2
12518
+ } = context;
12108
12519
  if (!graphDataRef.current || (viewType == null ? void 0 : viewType.toLowerCase()) !== "view") return;
12109
12520
  const currentCounter = sceneDataRef2.current.quest_counter || 1;
12110
12521
  const newNode = {
@@ -12137,7 +12548,8 @@ function XViewScene({
12137
12548
  const finalPosition = stateRef2.current.ghostElements.node ? stateRef2.current.ghostElements.node.position.clone() : stateRef2.current.controls.target.clone();
12138
12549
  const { graphGroup, ghostElements } = stateRef2.current;
12139
12550
  if (ghostElements.node && graphGroup) {
12140
- if (ghostElements.node.userData.labelObject) graphGroup.remove(ghostElements.node.userData.labelObject);
12551
+ if (ghostElements.node.userData.labelObject)
12552
+ graphGroup.remove(ghostElements.node.userData.labelObject);
12141
12553
  graphGroup.remove(ghostElements.node);
12142
12554
  }
12143
12555
  stateRef2.current.ghostElements = { node: null, line: null, aura: null };
@@ -12151,14 +12563,33 @@ function XViewScene({
12151
12563
  }
12152
12564
  };
12153
12565
  userActionHandlers.handleCompleteConnection = async (context, targetNodeData) => {
12154
- const { stateRef: stateRef2, graphDataRef, sceneDataRef: sceneDataRef2, sceneConfigId: sceneConfigId2, sceneSaveUrl: sceneSaveUrl2, ownerId: ownerId2 } = context;
12566
+ const {
12567
+ stateRef: stateRef2,
12568
+ graphDataRef,
12569
+ sceneDataRef: sceneDataRef2,
12570
+ sceneConfigId: sceneConfigId2,
12571
+ sceneSaveUrl: sceneSaveUrl2,
12572
+ ownerId: ownerId2
12573
+ } = context;
12155
12574
  const { sourceNodeData } = stateRef2.current.connection;
12156
12575
  if (!graphDataRef.current || !sceneDataRef2.current || !sourceNodeData || !targetNodeData) {
12157
12576
  userActionHandlers.handleCancelConnection(context);
12158
12577
  return;
12159
12578
  }
12160
- const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef2.current, sourceNodeData.id, sceneConfigId2, ownerId2);
12161
- const targetParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef2.current, targetNodeData.id, sceneConfigId2, ownerId2);
12579
+ const sourceParentInfo = getParentFileInfoForNode(
12580
+ graphDataRef.current,
12581
+ sceneDataRef2.current,
12582
+ sourceNodeData.id,
12583
+ sceneConfigId2,
12584
+ ownerId2
12585
+ );
12586
+ const targetParentInfo = getParentFileInfoForNode(
12587
+ graphDataRef.current,
12588
+ sceneDataRef2.current,
12589
+ targetNodeData.id,
12590
+ sceneConfigId2,
12591
+ ownerId2
12592
+ );
12162
12593
  let parentInfoToSave = sourceParentInfo;
12163
12594
  const isSourceQuest = sourceParentInfo.parentFileId === sceneConfigId2;
12164
12595
  const isTargetQuest = targetParentInfo.parentFileId === sceneConfigId2;
@@ -12184,10 +12615,15 @@ function XViewScene({
12184
12615
  };
12185
12616
  await context.actions.save_view_data(sceneSaveUrl2, viewFilePayload);
12186
12617
  } else {
12187
- const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
12618
+ const specificParentData = JSON.parse(
12619
+ JSON.stringify(graphDataRef.current[parentFileIdToSave])
12620
+ );
12188
12621
  specificParentData.links.push(newLink);
12189
12622
  const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
12190
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
12623
+ await context.actions.save_view_data(
12624
+ filenameForSpecificParent,
12625
+ specificParentData
12626
+ );
12191
12627
  graphDataRef.current[parentFileIdToSave] = specificParentData;
12192
12628
  }
12193
12629
  addNewLinkToScene(stateRef2.current, newLink);
@@ -12199,7 +12635,9 @@ function XViewScene({
12199
12635
  };
12200
12636
  const handleClearAncestryVisuals = useCallback4((ancestryId) => {
12201
12637
  const { renderedAncestries, ancestryGroup } = stateRef.current;
12202
- const renderIndex = renderedAncestries.findIndex((a) => String(a.id) === String(ancestryId));
12638
+ const renderIndex = renderedAncestries.findIndex(
12639
+ (a) => String(a.id) === String(ancestryId)
12640
+ );
12203
12641
  if (renderIndex !== -1) {
12204
12642
  const toRemove = renderedAncestries[renderIndex];
12205
12643
  toRemove.lines.forEach((line) => {
@@ -12208,12 +12646,16 @@ function XViewScene({
12208
12646
  if (line.material) line.material.dispose();
12209
12647
  });
12210
12648
  renderedAncestries.splice(renderIndex, 1);
12211
- stateRef.current.ancestryLinks = renderedAncestries.flatMap((a) => a.lines);
12649
+ stateRef.current.ancestryLinks = renderedAncestries.flatMap(
12650
+ (a) => a.lines
12651
+ );
12212
12652
  }
12213
12653
  }, []);
12214
12654
  const handleRenderAncestry = useCallback4(
12215
12655
  async (ancestryObject, allowedSectionIds = null, activeSectionIdForFocus = null, baseRotation = 0, forceReprocess = true) => {
12216
- setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
12656
+ setContextMenu(
12657
+ (prev) => prev.visible ? { ...prev, visible: false } : prev
12658
+ );
12217
12659
  if (!ancestryObject || !ancestryObject.tree) {
12218
12660
  return;
12219
12661
  }
@@ -12240,14 +12682,16 @@ function XViewScene({
12240
12682
  if (numId !== void 0 && normalizedAllowedIds.has(numId)) return true;
12241
12683
  if (strId && normalizedAllowedIds.has(strId)) return true;
12242
12684
  if (strSecId && normalizedAllowedIds.has(strSecId)) return true;
12243
- if (numId !== void 0 && normalizedAllowedIds.has(String(numId))) return true;
12685
+ if (numId !== void 0 && normalizedAllowedIds.has(String(numId)))
12686
+ return true;
12244
12687
  return false;
12245
12688
  };
12246
12689
  const checkIsActive = (section, targetId) => {
12247
12690
  if (targetId === null || targetId === void 0) return false;
12248
12691
  const sTarget = String(targetId);
12249
12692
  if (section.id && String(section.id) === sTarget) return true;
12250
- if (section.section_id && String(section.section_id) === sTarget) return true;
12693
+ if (section.section_id && String(section.section_id) === sTarget)
12694
+ return true;
12251
12695
  if (section.section_numeric_id !== void 0) {
12252
12696
  if (String(section.section_numeric_id) === sTarget) return true;
12253
12697
  }
@@ -12263,7 +12707,9 @@ function XViewScene({
12263
12707
  traverse(sectionTree);
12264
12708
  return ids;
12265
12709
  };
12266
- const existingIndex = renderedAncestries.findIndex((a) => String(a.id) === String(ancestryObject.ancestry_id));
12710
+ const existingIndex = renderedAncestries.findIndex(
12711
+ (a) => String(a.id) === String(ancestryObject.ancestry_id)
12712
+ );
12267
12713
  let skipGeneration = false;
12268
12714
  let ancestryEntry = null;
12269
12715
  if (existingIndex !== -1) {
@@ -12318,9 +12764,15 @@ function XViewScene({
12318
12764
  if (line.material) line.material.dispose();
12319
12765
  });
12320
12766
  }
12321
- const allParentNodes = Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes);
12767
+ const allParentNodes = Object.values(parentDataRef.current).flatMap(
12768
+ (fileData) => fileData.nodes
12769
+ );
12322
12770
  const allAncestries = ancestryDataRef.current || [];
12323
- const fullTree = buildFullAncestryTree(ancestryObject.tree, allParentNodes, allAncestries);
12771
+ const fullTree = buildFullAncestryTree(
12772
+ ancestryObject.tree,
12773
+ allParentNodes,
12774
+ allAncestries
12775
+ );
12324
12776
  if (!fullTree) return;
12325
12777
  const rootNodeId = String(ancestryObject.ancestral_node);
12326
12778
  let rootNodeMesh = nodeObjects[rootNodeId];
@@ -12363,7 +12815,10 @@ function XViewScene({
12363
12815
  const colorIndex = stateRef.current.ancestryRenderCounter % 3;
12364
12816
  colorHex = ANCESTRY_COLORS[colorIndex];
12365
12817
  }
12366
- const resolution = new THREE3.Vector2(renderer.domElement.clientWidth, renderer.domElement.clientHeight);
12818
+ const resolution = new THREE3.Vector2(
12819
+ renderer.domElement.clientWidth,
12820
+ renderer.domElement.clientHeight
12821
+ );
12367
12822
  const cleanupLinesForNode = (nodeId) => {
12368
12823
  if (!ancestryEntry || !ancestryEntry.lines) return;
12369
12824
  const linesKeep = [];
@@ -12403,7 +12858,13 @@ function XViewScene({
12403
12858
  }
12404
12859
  if (originMesh && rootNodeMesh) {
12405
12860
  cleanupLinesForNode(rootNodeId);
12406
- const branchLine = createAncestryLinkLine(originMesh, rootNodeMesh, resolution, {}, colorHex);
12861
+ const branchLine = createAncestryLinkLine(
12862
+ originMesh,
12863
+ rootNodeMesh,
12864
+ resolution,
12865
+ {},
12866
+ colorHex
12867
+ );
12407
12868
  ancestryGroup.add(branchLine);
12408
12869
  ancestryEntry.lines.push(branchLine);
12409
12870
  }
@@ -12414,8 +12875,14 @@ function XViewScene({
12414
12875
  if (!children || children.length === 0) return null;
12415
12876
  let lastRenderedMesh = null;
12416
12877
  const numChildren = children.length;
12417
- const forwardVec = new THREE3.Vector3(0, 0, 1).applyAxisAngle(new THREE3.Vector3(0, 1, 0), currentAngle);
12418
- const rightVec = new THREE3.Vector3(1, 0, 0).applyAxisAngle(new THREE3.Vector3(0, 1, 0), currentAngle);
12878
+ const forwardVec = new THREE3.Vector3(0, 0, 1).applyAxisAngle(
12879
+ new THREE3.Vector3(0, 1, 0),
12880
+ currentAngle
12881
+ );
12882
+ const rightVec = new THREE3.Vector3(1, 0, 0).applyAxisAngle(
12883
+ new THREE3.Vector3(0, 1, 0),
12884
+ currentAngle
12885
+ );
12419
12886
  const angleRange = numChildren > 3 ? Math.PI : Math.PI / 1.5;
12420
12887
  children.forEach((childItem, index) => {
12421
12888
  if (childItem.is_section) return;
@@ -12436,14 +12903,30 @@ function XViewScene({
12436
12903
  targetPositionsCache.set(sNodeId, childPosition.clone());
12437
12904
  cleanupLinesForNode(sNodeId);
12438
12905
  }
12439
- const childMesh = addOrUpdateNodeMesh(childItem.node, childPosition, true);
12906
+ const childMesh = addOrUpdateNodeMesh(
12907
+ childItem.node,
12908
+ childPosition,
12909
+ true
12910
+ );
12440
12911
  allRenderedNodePositions.push(childPosition);
12441
- const line = createAncestryLinkLine(parentMesh, childMesh, resolution, childItem.relationship, colorHex);
12912
+ const line = createAncestryLinkLine(
12913
+ parentMesh,
12914
+ childMesh,
12915
+ resolution,
12916
+ childItem.relationship,
12917
+ colorHex
12918
+ );
12442
12919
  ancestryGroup.add(line);
12443
12920
  ancestryEntry.lines.push(line);
12444
12921
  lastRenderedMesh = childMesh;
12445
12922
  if (childItem.children && childItem.children.length > 0) {
12446
- const lastDescendant = renderCluster(childItem.children, childMesh, childPosition, level + 1, currentAngle);
12923
+ const lastDescendant = renderCluster(
12924
+ childItem.children,
12925
+ childMesh,
12926
+ childPosition,
12927
+ level + 1,
12928
+ currentAngle
12929
+ );
12447
12930
  if (lastDescendant) lastRenderedMesh = lastDescendant;
12448
12931
  }
12449
12932
  });
@@ -12457,9 +12940,13 @@ function XViewScene({
12457
12940
  else looseNodes.push(child);
12458
12941
  });
12459
12942
  }
12460
- sections.sort((a, b) => (a.section_numeric_id || 0) - (b.section_numeric_id || 0));
12943
+ sections.sort(
12944
+ (a, b) => (a.section_numeric_id || 0) - (b.section_numeric_id || 0)
12945
+ );
12461
12946
  let combinedStartNodes = [...looseNodes];
12462
- let session0Index = sections.findIndex((s) => s.section_numeric_id === 0 || s.name === "Sess\xE3o 0");
12947
+ let session0Index = sections.findIndex(
12948
+ (s) => s.section_numeric_id === 0 || s.name === "Sess\xE3o 0"
12949
+ );
12463
12950
  if (session0Index !== -1) {
12464
12951
  const session0 = sections[session0Index];
12465
12952
  if (checkShouldRender(session0) && session0.children) {
@@ -12468,7 +12955,13 @@ function XViewScene({
12468
12955
  sections.splice(session0Index, 1);
12469
12956
  }
12470
12957
  const rootTargetPos2 = targetPositionsCache.get(rootNodeId) || rootNodeMesh.position;
12471
- const lastStartMesh = renderCluster(combinedStartNodes, rootNodeMesh, rootTargetPos2, 1, baseRotation);
12958
+ const lastStartMesh = renderCluster(
12959
+ combinedStartNodes,
12960
+ rootNodeMesh,
12961
+ rootTargetPos2,
12962
+ 1,
12963
+ baseRotation
12964
+ );
12472
12965
  if (lastStartMesh) {
12473
12966
  currentAnchorMesh = lastStartMesh;
12474
12967
  }
@@ -12479,10 +12972,18 @@ function XViewScene({
12479
12972
  const sectionNodes = section.children || [];
12480
12973
  if (sectionNodes.length > 0) {
12481
12974
  const parentAngle = nodeRotationMap.get(String(currentAnchorMesh.userData.id)) ?? baseRotation;
12482
- const lastMeshOfThisSection = renderCluster(sectionNodes, currentAnchorMesh, currentAnchorPosition, 1, parentAngle);
12975
+ const lastMeshOfThisSection = renderCluster(
12976
+ sectionNodes,
12977
+ currentAnchorMesh,
12978
+ currentAnchorPosition,
12979
+ 1,
12980
+ parentAngle
12981
+ );
12483
12982
  if (lastMeshOfThisSection) {
12484
12983
  currentAnchorMesh = lastMeshOfThisSection;
12485
- currentAnchorPosition = targetPositionsCache.get(String(lastMeshOfThisSection.userData.id)) || lastMeshOfThisSection.position;
12984
+ currentAnchorPosition = targetPositionsCache.get(
12985
+ String(lastMeshOfThisSection.userData.id)
12986
+ ) || lastMeshOfThisSection.position;
12486
12987
  }
12487
12988
  }
12488
12989
  }
@@ -12511,7 +13012,10 @@ function XViewScene({
12511
13012
  } else {
12512
13013
  const directChildren = fullTree.children ? fullTree.children.filter((c) => !c.is_section) : [];
12513
13014
  if (directChildren.length > 0) {
12514
- targetSectionForZone = { name: "In\xEDcio", children: directChildren };
13015
+ targetSectionForZone = {
13016
+ name: "In\xEDcio",
13017
+ children: directChildren
13018
+ };
12515
13019
  }
12516
13020
  }
12517
13021
  } else {
@@ -12585,7 +13089,10 @@ function XViewScene({
12585
13089
  stateRef.current.ancestryLinks = stateRef.current.renderedAncestries.flatMap((a) => a.lines);
12586
13090
  focusTargetPosition = center;
12587
13091
  focusTargetRotation = baseRotation;
12588
- const desiredDistance = Math.max(x_view_config.CAMERA_ZOOM_DISTANCE, radius * 2.8);
13092
+ const desiredDistance = Math.max(
13093
+ x_view_config.CAMERA_ZOOM_DISTANCE,
13094
+ radius * 2.8
13095
+ );
12589
13096
  focusZoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
12590
13097
  } else {
12591
13098
  const anchorId = String(currentAnchorMesh.userData.id);
@@ -12600,13 +13107,18 @@ function XViewScene({
12600
13107
  }
12601
13108
  if (!focusTargetPosition && !allowedSectionIds) {
12602
13109
  if (allRenderedNodePositions.length > 0) {
12603
- const boundingBox = new THREE3.Box3().setFromPoints(allRenderedNodePositions);
13110
+ const boundingBox = new THREE3.Box3().setFromPoints(
13111
+ allRenderedNodePositions
13112
+ );
12604
13113
  const center = new THREE3.Vector3();
12605
13114
  boundingBox.getCenter(center);
12606
13115
  const size = new THREE3.Vector3();
12607
13116
  boundingBox.getSize(size);
12608
13117
  const maxDim = Math.max(size.x, size.y, size.z);
12609
- const fitZoom = Math.min(1, x_view_config.CAMERA_ZOOM_DISTANCE / (maxDim * 1.5));
13118
+ const fitZoom = Math.min(
13119
+ 1,
13120
+ x_view_config.CAMERA_ZOOM_DISTANCE / (maxDim * 1.5)
13121
+ );
12610
13122
  const defaultBase = new THREE3.Vector3(-50, 40, 20);
12611
13123
  const rotatedCinematicAngle = defaultBase.clone().applyAxisAngle(new THREE3.Vector3(0, 1, 0), baseRotation);
12612
13124
  tweenToTarget(center, fitZoom, rotatedCinematicAngle);
@@ -12625,210 +13137,280 @@ function XViewScene({
12625
13137
  tweenToTarget(targetPos, focusZoomFactor, rotatedCinematicAngle);
12626
13138
  }
12627
13139
  },
12628
- [addOrUpdateNodeMesh, tweenToTarget, buildFullAncestryTree, readingMode.isActive, ancestryMode.isActive]
13140
+ [
13141
+ addOrUpdateNodeMesh,
13142
+ tweenToTarget,
13143
+ buildFullAncestryTree,
13144
+ readingMode.isActive,
13145
+ ancestryMode.isActive
13146
+ ]
12629
13147
  );
12630
- const handleRenderAbstractionTree = useCallback4((ancestryObject, targetNodeId = null) => {
12631
- setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
12632
- if (!ancestryObject || !ancestryObject.abstraction_tree) return;
12633
- const { ancestryGroup, nodeObjects, renderer, renderedAncestries } = stateRef.current;
12634
- const allParentNodes = Object.values(parentDataRef.current).flatMap((f) => f.nodes);
12635
- let fullTree = buildFullAncestryTree(ancestryObject.abstraction_tree, allParentNodes, ancestryDataRef.current);
12636
- if (!fullTree || !fullTree.node) return;
12637
- if (targetNodeId) {
12638
- const pruneTreeToPath = (treeNode, targetId) => {
12639
- var _a2;
12640
- if (!treeNode) return null;
12641
- const currentId = treeNode.is_section ? treeNode.section_id : String((_a2 = treeNode.node) == null ? void 0 : _a2.id);
12642
- if (String(currentId) === String(targetId)) {
12643
- return { ...treeNode, children: [] };
12644
- }
12645
- if (treeNode.children && treeNode.children.length > 0) {
12646
- for (let child of treeNode.children) {
12647
- const prunedChild = pruneTreeToPath(child, targetId);
12648
- if (prunedChild) {
12649
- return { ...treeNode, children: [prunedChild] };
12650
- }
13148
+ const handleRenderAbstractionTree = useCallback4(
13149
+ (ancestryObject, targetNodeId = null) => {
13150
+ setContextMenu(
13151
+ (prev) => prev.visible ? { ...prev, visible: false } : prev
13152
+ );
13153
+ if (!ancestryObject || !ancestryObject.abstraction_tree) return;
13154
+ const { ancestryGroup, nodeObjects, renderer, renderedAncestries } = stateRef.current;
13155
+ const allParentNodes = Object.values(parentDataRef.current).flatMap(
13156
+ (f) => f.nodes
13157
+ );
13158
+ let fullTree = buildFullAncestryTree(
13159
+ ancestryObject.abstraction_tree,
13160
+ allParentNodes,
13161
+ ancestryDataRef.current
13162
+ );
13163
+ if (!fullTree || !fullTree.node) return;
13164
+ if (targetNodeId) {
13165
+ const pruneTreeToPath = (treeNode, targetId) => {
13166
+ var _a2;
13167
+ if (!treeNode) return null;
13168
+ const currentId = treeNode.is_section ? treeNode.section_id : String((_a2 = treeNode.node) == null ? void 0 : _a2.id);
13169
+ if (String(currentId) === String(targetId)) {
13170
+ return { ...treeNode, children: [] };
12651
13171
  }
12652
- }
12653
- return null;
12654
- };
12655
- const pruned = pruneTreeToPath(fullTree, targetNodeId);
12656
- if (pruned) fullTree = pruned;
12657
- }
12658
- const absId = ancestryObject.ancestry_id + "_abs";
12659
- handleClearAncestryVisuals(absId);
12660
- const colorHex = 9133302;
12661
- const resolution = new THREE3.Vector2(renderer.domElement.clientWidth, renderer.domElement.clientHeight);
12662
- const ancestryEntry = { id: absId, lines: [], isFullRender: true };
12663
- renderedAncestries.push(ancestryEntry);
12664
- const rootNodeId = String(fullTree.node.id);
12665
- let rootNodeMesh = nodeObjects[rootNodeId];
12666
- let rootTargetPos = rootNodeMesh ? rootNodeMesh.position.clone() : new THREE3.Vector3(0, 0, 0);
12667
- if (!rootNodeMesh) {
12668
- rootNodeMesh = addOrUpdateNodeMesh(fullTree.node, rootTargetPos, true);
12669
- }
12670
- const SPACING_Y = -40;
12671
- const SPACING_X = 55;
12672
- const renderVertical = (treeNode, parentMesh, parentPos, level) => {
12673
- if (!treeNode.children || treeNode.children.length === 0) return;
12674
- const totalSiblings = treeNode.children.length;
12675
- treeNode.children.forEach((childItem, i) => {
12676
- if (!childItem.node) return;
12677
- const childX = parentPos.x + (i - (totalSiblings - 1) / 2) * (SPACING_X / Math.max(1, level * 0.4));
12678
- const childY = parentPos.y + SPACING_Y;
12679
- const childPos = new THREE3.Vector3(childX, childY, parentPos.z);
12680
- const childMesh = addOrUpdateNodeMesh(childItem.node, childPos, true);
12681
- const line = createAncestryLinkLine(parentMesh, childMesh, resolution, {}, colorHex);
12682
- ancestryGroup.add(line);
12683
- ancestryEntry.lines.push(line);
12684
- renderVertical(childItem, childMesh, childPos, level + 1);
12685
- });
12686
- };
12687
- renderVertical(fullTree, rootNodeMesh, rootTargetPos, 1);
12688
- stateRef.current.ancestryLinks = renderedAncestries.flatMap((a) => a.lines);
12689
- tweenToTarget(rootTargetPos, 0.7);
12690
- }, [addOrUpdateNodeMesh, tweenToTarget, buildFullAncestryTree, handleClearAncestryVisuals]);
12691
- const handleReadModeBranchNav = useCallback4((nodeId, action, direction = "right") => {
12692
- const { ancestry, branchStack } = readingMode;
12693
- if (!ancestry || !ancestry.tree) return;
12694
- const allAncestries = ancestryDataRef.current || [];
12695
- const fullTree = buildFullAncestryTree(ancestry.tree, Object.values(parentDataRef.current).flatMap((f) => f.nodes), allAncestries);
12696
- if (action === "open") {
12697
- let currentPtr = fullTree;
12698
- for (const step of branchStack) {
12699
- const found = findNodePath3(currentPtr, step.nodeId);
12700
- if (found && found.node.parallel_branches) {
12701
- const branch = found.node.parallel_branches.find((b) => b.id === step.branchId);
12702
- if (branch) currentPtr = branch.tree;
12703
- }
12704
- }
12705
- const foundTarget = findNodePath3(currentPtr, nodeId);
12706
- if (foundTarget && foundTarget.node && foundTarget.node.parallel_branches && foundTarget.node.parallel_branches.length > 0) {
12707
- const branchToOpen = foundTarget.node.parallel_branches.find((b) => (b.direction || "right") === direction);
12708
- if (!branchToOpen) return;
12709
- if (branchToOpen && branchToOpen.tree) {
12710
- const parentIndexToSave = stateRef.current.readMode.currentMaxIndex;
12711
- const savedBranchProgress = 0;
12712
- stateRef.current.readMode.currentMaxIndex = savedBranchProgress;
12713
- stateRef.current.maxAncestryRenderIndex = savedBranchProgress;
12714
- const newStack = [...branchStack, {
12715
- nodeId,
12716
- branchId: branchToOpen.id,
12717
- savedMaxIndex: parentIndexToSave,
12718
- entryDirection: direction
12719
- }];
12720
- setReadingMode((prev) => ({ ...prev, branchStack: newStack, initialSectionId: null }));
12721
- const branchAncestryObj = {
12722
- ancestry_id: branchToOpen.id,
12723
- ancestral_node: branchToOpen.tree.node.id,
12724
- name: branchToOpen.name,
12725
- description: branchToOpen.description,
12726
- description_sections: branchToOpen.description_sections,
12727
- tree: branchToOpen.tree,
12728
- _originNodeId: nodeId,
12729
- _branchDirection: direction
12730
- };
12731
- const allowedIds = /* @__PURE__ */ new Set(["preamble", 0, "0"]);
12732
- const branchSections = parseDescriptionSections(branchToOpen.description || "", branchToOpen.description_sections || []);
12733
- for (let i = 0; i <= savedBranchProgress; i++) {
12734
- if (branchSections[i]) {
12735
- if (branchSections[i].id) allowedIds.add(String(branchSections[i].id));
12736
- if (branchSections[i].numericId !== void 0 && branchSections[i].numericId !== null) {
12737
- allowedIds.add(branchSections[i].numericId);
12738
- allowedIds.add(String(branchSections[i].numericId));
13172
+ if (treeNode.children && treeNode.children.length > 0) {
13173
+ for (let child of treeNode.children) {
13174
+ const prunedChild = pruneTreeToPath(child, targetId);
13175
+ if (prunedChild) {
13176
+ return { ...treeNode, children: [prunedChild] };
12739
13177
  }
12740
13178
  }
12741
13179
  }
12742
- const rotation = newStack.reduce((acc, step) => {
12743
- return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
12744
- }, 0);
12745
- const initialFocusId = "preamble";
12746
- handleRenderAncestry(
12747
- branchAncestryObj,
12748
- allowedIds,
12749
- initialFocusId,
12750
- rotation,
12751
- false
12752
- );
12753
- }
13180
+ return null;
13181
+ };
13182
+ const pruned = pruneTreeToPath(fullTree, targetNodeId);
13183
+ if (pruned) fullTree = pruned;
13184
+ }
13185
+ const absId = ancestryObject.ancestry_id + "_abs";
13186
+ handleClearAncestryVisuals(absId);
13187
+ const colorHex = 9133302;
13188
+ const resolution = new THREE3.Vector2(
13189
+ renderer.domElement.clientWidth,
13190
+ renderer.domElement.clientHeight
13191
+ );
13192
+ const ancestryEntry = { id: absId, lines: [], isFullRender: true };
13193
+ renderedAncestries.push(ancestryEntry);
13194
+ const rootNodeId = String(fullTree.node.id);
13195
+ let rootNodeMesh = nodeObjects[rootNodeId];
13196
+ let rootTargetPos = rootNodeMesh ? rootNodeMesh.position.clone() : new THREE3.Vector3(0, 0, 0);
13197
+ if (!rootNodeMesh) {
13198
+ rootNodeMesh = addOrUpdateNodeMesh(fullTree.node, rootTargetPos, true);
12754
13199
  }
12755
- } else if (action === "back") {
12756
- if (branchStack.length === 0) return;
12757
- const newStack = [...branchStack];
12758
- const popped = newStack.pop();
12759
- if (popped && popped.branchId) {
12760
- stateRef.current.readMode.progressMap[popped.branchId] = stateRef.current.readMode.currentMaxIndex;
12761
- }
12762
- const parentSavedIndex = popped.savedMaxIndex !== void 0 ? popped.savedMaxIndex : 0;
12763
- stateRef.current.readMode.currentMaxIndex = parentSavedIndex;
12764
- stateRef.current.maxAncestryRenderIndex = parentSavedIndex;
12765
- let targetTreeToRender = fullTree;
12766
- let targetAncestryInfo = ancestry;
12767
- if (newStack.length > 0) {
12768
- let ptr = fullTree;
12769
- for (const step of newStack) {
12770
- const found = findNodePath3(ptr, step.nodeId);
13200
+ const SPACING_Y = -40;
13201
+ const SPACING_X = 55;
13202
+ const renderVertical = (treeNode, parentMesh, parentPos, level) => {
13203
+ if (!treeNode.children || treeNode.children.length === 0) return;
13204
+ const totalSiblings = treeNode.children.length;
13205
+ treeNode.children.forEach((childItem, i) => {
13206
+ if (!childItem.node) return;
13207
+ const childX = parentPos.x + (i - (totalSiblings - 1) / 2) * (SPACING_X / Math.max(1, level * 0.4));
13208
+ const childY = parentPos.y + SPACING_Y;
13209
+ const childPos = new THREE3.Vector3(childX, childY, parentPos.z);
13210
+ const childMesh = addOrUpdateNodeMesh(childItem.node, childPos, true);
13211
+ const line = createAncestryLinkLine(
13212
+ parentMesh,
13213
+ childMesh,
13214
+ resolution,
13215
+ {},
13216
+ colorHex
13217
+ );
13218
+ ancestryGroup.add(line);
13219
+ ancestryEntry.lines.push(line);
13220
+ renderVertical(childItem, childMesh, childPos, level + 1);
13221
+ });
13222
+ };
13223
+ renderVertical(fullTree, rootNodeMesh, rootTargetPos, 1);
13224
+ stateRef.current.ancestryLinks = renderedAncestries.flatMap(
13225
+ (a) => a.lines
13226
+ );
13227
+ tweenToTarget(rootTargetPos, 0.7);
13228
+ },
13229
+ [
13230
+ addOrUpdateNodeMesh,
13231
+ tweenToTarget,
13232
+ buildFullAncestryTree,
13233
+ handleClearAncestryVisuals
13234
+ ]
13235
+ );
13236
+ const handleReadModeBranchNav = useCallback4(
13237
+ (nodeId, action, direction = "right") => {
13238
+ const { ancestry, branchStack } = readingMode;
13239
+ if (!ancestry || !ancestry.tree) return;
13240
+ const allAncestries = ancestryDataRef.current || [];
13241
+ const fullTree = buildFullAncestryTree(
13242
+ ancestry.tree,
13243
+ Object.values(parentDataRef.current).flatMap((f) => f.nodes),
13244
+ allAncestries
13245
+ );
13246
+ if (action === "open") {
13247
+ let currentPtr = fullTree;
13248
+ for (const step of branchStack) {
13249
+ const found = findNodePath3(currentPtr, step.nodeId);
12771
13250
  if (found && found.node.parallel_branches) {
12772
- const branch = found.node.parallel_branches.find((b) => b.id === step.branchId);
12773
- if (branch) {
12774
- ptr = branch.tree;
12775
- targetAncestryInfo = { ...branch, ancestry_id: branch.id, ancestral_node: branch.tree.node.id };
13251
+ const branch = found.node.parallel_branches.find(
13252
+ (b) => b.id === step.branchId
13253
+ );
13254
+ if (branch) currentPtr = branch.tree;
13255
+ }
13256
+ }
13257
+ const foundTarget = findNodePath3(currentPtr, nodeId);
13258
+ if (foundTarget && foundTarget.node && foundTarget.node.parallel_branches && foundTarget.node.parallel_branches.length > 0) {
13259
+ const branchToOpen = foundTarget.node.parallel_branches.find(
13260
+ (b) => (b.direction || "right") === direction
13261
+ );
13262
+ if (!branchToOpen) return;
13263
+ if (branchToOpen && branchToOpen.tree) {
13264
+ const parentIndexToSave = stateRef.current.readMode.currentMaxIndex;
13265
+ const savedBranchProgress = 0;
13266
+ stateRef.current.readMode.currentMaxIndex = savedBranchProgress;
13267
+ stateRef.current.maxAncestryRenderIndex = savedBranchProgress;
13268
+ const newStack = [
13269
+ ...branchStack,
13270
+ {
13271
+ nodeId,
13272
+ branchId: branchToOpen.id,
13273
+ savedMaxIndex: parentIndexToSave,
13274
+ entryDirection: direction
13275
+ }
13276
+ ];
13277
+ setReadingMode((prev) => ({
13278
+ ...prev,
13279
+ branchStack: newStack,
13280
+ initialSectionId: null
13281
+ }));
13282
+ const branchAncestryObj = {
13283
+ ancestry_id: branchToOpen.id,
13284
+ ancestral_node: branchToOpen.tree.node.id,
13285
+ name: branchToOpen.name,
13286
+ description: branchToOpen.description,
13287
+ description_sections: branchToOpen.description_sections,
13288
+ tree: branchToOpen.tree,
13289
+ _originNodeId: nodeId,
13290
+ _branchDirection: direction
13291
+ };
13292
+ const allowedIds = /* @__PURE__ */ new Set(["preamble", 0, "0"]);
13293
+ const branchSections = parseDescriptionSections(
13294
+ branchToOpen.description || "",
13295
+ branchToOpen.description_sections || []
13296
+ );
13297
+ for (let i = 0; i <= savedBranchProgress; i++) {
13298
+ if (branchSections[i]) {
13299
+ if (branchSections[i].id)
13300
+ allowedIds.add(String(branchSections[i].id));
13301
+ if (branchSections[i].numericId !== void 0 && branchSections[i].numericId !== null) {
13302
+ allowedIds.add(branchSections[i].numericId);
13303
+ allowedIds.add(String(branchSections[i].numericId));
13304
+ }
13305
+ }
12776
13306
  }
13307
+ const rotation = newStack.reduce((acc, step) => {
13308
+ return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
13309
+ }, 0);
13310
+ const initialFocusId = "preamble";
13311
+ handleRenderAncestry(
13312
+ branchAncestryObj,
13313
+ allowedIds,
13314
+ initialFocusId,
13315
+ rotation,
13316
+ false
13317
+ );
12777
13318
  }
12778
13319
  }
12779
- targetTreeToRender = ptr;
12780
- }
12781
- const activeParentStackItem = newStack.length > 0 ? newStack[newStack.length - 1] : null;
12782
- const parentAncestryObj = {
12783
- ...targetAncestryInfo,
12784
- tree: targetTreeToRender,
12785
- _originNodeId: activeParentStackItem ? activeParentStackItem.nodeId : null,
12786
- _branchDirection: activeParentStackItem ? activeParentStackItem.entryDirection : null
12787
- };
12788
- const descriptionText = targetAncestryInfo.description || "";
12789
- const savedSections = targetAncestryInfo.description_sections || [];
12790
- const sections = parseDescriptionSections(descriptionText, savedSections);
12791
- let focusTargetId = null;
12792
- if (popped && popped.nodeId) {
12793
- const mentionTag = `[[MENTION:node:${popped.nodeId}]]`;
12794
- for (let i = 0; i < sections.length; i++) {
12795
- if (sections[i].content && sections[i].content.includes(mentionTag)) {
12796
- const section = sections[i];
12797
- focusTargetId = section.numericId !== void 0 ? section.numericId : section.id;
12798
- break;
13320
+ } else if (action === "back") {
13321
+ if (branchStack.length === 0) return;
13322
+ const newStack = [...branchStack];
13323
+ const popped = newStack.pop();
13324
+ if (popped && popped.branchId) {
13325
+ stateRef.current.readMode.progressMap[popped.branchId] = stateRef.current.readMode.currentMaxIndex;
13326
+ }
13327
+ const parentSavedIndex = popped.savedMaxIndex !== void 0 ? popped.savedMaxIndex : 0;
13328
+ stateRef.current.readMode.currentMaxIndex = parentSavedIndex;
13329
+ stateRef.current.maxAncestryRenderIndex = parentSavedIndex;
13330
+ let targetTreeToRender = fullTree;
13331
+ let targetAncestryInfo = ancestry;
13332
+ if (newStack.length > 0) {
13333
+ let ptr = fullTree;
13334
+ for (const step of newStack) {
13335
+ const found = findNodePath3(ptr, step.nodeId);
13336
+ if (found && found.node.parallel_branches) {
13337
+ const branch = found.node.parallel_branches.find(
13338
+ (b) => b.id === step.branchId
13339
+ );
13340
+ if (branch) {
13341
+ ptr = branch.tree;
13342
+ targetAncestryInfo = {
13343
+ ...branch,
13344
+ ancestry_id: branch.id,
13345
+ ancestral_node: branch.tree.node.id
13346
+ };
13347
+ }
13348
+ }
13349
+ }
13350
+ targetTreeToRender = ptr;
13351
+ }
13352
+ const activeParentStackItem = newStack.length > 0 ? newStack[newStack.length - 1] : null;
13353
+ const parentAncestryObj = {
13354
+ ...targetAncestryInfo,
13355
+ tree: targetTreeToRender,
13356
+ _originNodeId: activeParentStackItem ? activeParentStackItem.nodeId : null,
13357
+ _branchDirection: activeParentStackItem ? activeParentStackItem.entryDirection : null
13358
+ };
13359
+ const descriptionText = targetAncestryInfo.description || "";
13360
+ const savedSections = targetAncestryInfo.description_sections || [];
13361
+ const sections = parseDescriptionSections(
13362
+ descriptionText,
13363
+ savedSections
13364
+ );
13365
+ let focusTargetId = null;
13366
+ if (popped && popped.nodeId) {
13367
+ const mentionTag = `[[MENTION:node:${popped.nodeId}]]`;
13368
+ for (let i = 0; i < sections.length; i++) {
13369
+ if (sections[i].content && sections[i].content.includes(mentionTag)) {
13370
+ const section = sections[i];
13371
+ focusTargetId = section.numericId !== void 0 ? section.numericId : section.id;
13372
+ break;
13373
+ }
12799
13374
  }
12800
13375
  }
12801
- }
12802
- const allowedIds = /* @__PURE__ */ new Set();
12803
- allowedIds.add("preamble");
12804
- allowedIds.add(0);
12805
- allowedIds.add("0");
12806
- for (let i = 0; i <= parentSavedIndex; i++) {
12807
- if (sections[i]) {
12808
- if (sections[i].id) allowedIds.add(String(sections[i].id));
12809
- if (sections[i].numericId !== void 0 && sections[i].numericId !== null) {
12810
- allowedIds.add(sections[i].numericId);
12811
- allowedIds.add(String(sections[i].numericId));
13376
+ const allowedIds = /* @__PURE__ */ new Set();
13377
+ allowedIds.add("preamble");
13378
+ allowedIds.add(0);
13379
+ allowedIds.add("0");
13380
+ for (let i = 0; i <= parentSavedIndex; i++) {
13381
+ if (sections[i]) {
13382
+ if (sections[i].id) allowedIds.add(String(sections[i].id));
13383
+ if (sections[i].numericId !== void 0 && sections[i].numericId !== null) {
13384
+ allowedIds.add(sections[i].numericId);
13385
+ allowedIds.add(String(sections[i].numericId));
13386
+ }
12812
13387
  }
12813
13388
  }
12814
- }
12815
- const rotation = newStack.reduce((acc, step) => {
12816
- return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
12817
- }, 0);
12818
- handleRenderAncestry(parentAncestryObj, allowedIds, focusTargetId, rotation, false);
12819
- if (popped && popped.nodeId) {
12820
- const nodeMesh = stateRef.current.nodeObjects[String(popped.nodeId)];
12821
- if (nodeMesh) {
12822
- tweenToTarget(nodeMesh);
13389
+ const rotation = newStack.reduce((acc, step) => {
13390
+ return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
13391
+ }, 0);
13392
+ handleRenderAncestry(
13393
+ parentAncestryObj,
13394
+ allowedIds,
13395
+ focusTargetId,
13396
+ rotation,
13397
+ false
13398
+ );
13399
+ if (popped && popped.nodeId) {
13400
+ const nodeMesh = stateRef.current.nodeObjects[String(popped.nodeId)];
13401
+ if (nodeMesh) {
13402
+ tweenToTarget(nodeMesh);
13403
+ }
12823
13404
  }
13405
+ setReadingMode((prev) => ({
13406
+ ...prev,
13407
+ branchStack: newStack,
13408
+ initialSectionId: focusTargetId
13409
+ }));
12824
13410
  }
12825
- setReadingMode((prev) => ({
12826
- ...prev,
12827
- branchStack: newStack,
12828
- initialSectionId: focusTargetId
12829
- }));
12830
- }
12831
- }, [readingMode, handleRenderAncestry, buildFullAncestryTree, tweenToTarget]);
13411
+ },
13412
+ [readingMode, handleRenderAncestry, buildFullAncestryTree, tweenToTarget]
13413
+ );
12832
13414
  const handleReadModeHighlight = useCallback4((nodeId) => {
12833
13415
  if (stateRef.current.highlightedNodeId !== nodeId) {
12834
13416
  stateRef.current.highlightedNodeId = nodeId;
@@ -12836,7 +13418,8 @@ function XViewScene({
12836
13418
  setHighlightedNodeId(nodeId);
12837
13419
  }, []);
12838
13420
  const activeNodeBranches = useMemo12(() => {
12839
- if (!highlightedNodeId || !readingMode.ancestry || !readingMode.ancestry.tree) return null;
13421
+ if (!highlightedNodeId || !readingMode.ancestry || !readingMode.ancestry.tree)
13422
+ return null;
12840
13423
  const fullTree = buildFullAncestryTree(
12841
13424
  readingMode.ancestry.tree,
12842
13425
  Object.values(parentDataRef.current).flatMap((f) => f.nodes),
@@ -12871,7 +13454,13 @@ function XViewScene({
12871
13454
  };
12872
13455
  }
12873
13456
  return null;
12874
- }, [highlightedNodeId, readingMode.ancestry, buildFullAncestryTree, readingMode.branchStack, ancestryDataRef.current]);
13457
+ }, [
13458
+ highlightedNodeId,
13459
+ readingMode.ancestry,
13460
+ buildFullAncestryTree,
13461
+ readingMode.branchStack,
13462
+ ancestryDataRef.current
13463
+ ]);
12875
13464
  const backNavigationInfo = useMemo12(() => {
12876
13465
  const { branchStack } = readingMode;
12877
13466
  if (!branchStack || branchStack.length === 0) return null;
@@ -12907,7 +13496,9 @@ function XViewScene({
12907
13496
  for (const step of branchStack) {
12908
13497
  const found = findNodePath3(currentPtr, step.nodeId);
12909
13498
  if (found && found.node.parallel_branches) {
12910
- const branch = found.node.parallel_branches.find((b) => b.id === step.branchId);
13499
+ const branch = found.node.parallel_branches.find(
13500
+ (b) => b.id === step.branchId
13501
+ );
12911
13502
  if (branch) {
12912
13503
  currentPtr = branch.tree;
12913
13504
  currentMeta = branch;
@@ -12928,17 +13519,26 @@ function XViewScene({
12928
13519
  if (!readingMode.isActive || !readingMode.ancestry || !readingMode.ancestry.abstraction_tree) {
12929
13520
  return null;
12930
13521
  }
12931
- const allNodes = Object.values(parentDataRef.current || {}).flatMap((f) => f.nodes || []);
13522
+ const allNodes = Object.values(parentDataRef.current || {}).flatMap(
13523
+ (f) => f.nodes || []
13524
+ );
12932
13525
  const allAncestries = ancestryDataRef.current || [];
12933
13526
  return buildFullAncestryTree(
12934
13527
  readingMode.ancestry.abstraction_tree,
12935
13528
  allNodes,
12936
13529
  allAncestries
12937
13530
  );
12938
- }, [readingMode.isActive, readingMode.ancestry, buildFullAncestryTree, sceneVersion]);
13531
+ }, [
13532
+ readingMode.isActive,
13533
+ readingMode.ancestry,
13534
+ buildFullAncestryTree,
13535
+ sceneVersion
13536
+ ]);
12939
13537
  const handleStartReadingAncestry = useCallback4(
12940
- async (ancestryObject) => {
12941
- setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
13538
+ async (ancestryObject, renderMode = "full") => {
13539
+ setContextMenu(
13540
+ (prev) => prev.visible ? { ...prev, visible: false } : prev
13541
+ );
12942
13542
  if (!ancestryObject || !ancestryObject.tree) {
12943
13543
  console.warn("Ancestralidade inv\xE1lida para leitura.");
12944
13544
  return;
@@ -12951,14 +13551,20 @@ function XViewScene({
12951
13551
  const hasDescription = ancestryObject.description && ancestryObject.description.trim() !== "";
12952
13552
  const hasMainTreeNodes = ancestryObject.tree.children && ancestryObject.tree.children.length > 0;
12953
13553
  const hasAbstractionNodes = ancestryObject.abstraction_tree && ancestryObject.abstraction_tree.children && ancestryObject.abstraction_tree.children.length > 0;
12954
- const shouldAutoRenderAbstraction = !hasDescription && !hasMainTreeNodes && hasAbstractionNodes;
13554
+ const isFull = renderMode === "full";
13555
+ const isAncestryOnly = renderMode === "ancestry_only";
13556
+ const isAbstractionOnly = renderMode === "abstraction_only";
13557
+ let shouldRenderAbstraction = isAbstractionOnly;
13558
+ if (isFull) {
13559
+ shouldRenderAbstraction = !hasDescription && !hasMainTreeNodes && hasAbstractionNodes;
13560
+ }
12955
13561
  setReadingMode({
12956
- isActive: true,
13562
+ isActive: isFull ? hasDescription : false,
12957
13563
  ancestry: ancestryObject,
12958
13564
  branchStack: [],
12959
- autoAbstraction: shouldAutoRenderAbstraction
13565
+ autoAbstraction: shouldRenderAbstraction
12960
13566
  });
12961
- if (shouldAutoRenderAbstraction) {
13567
+ if (shouldRenderAbstraction) {
12962
13568
  handleRenderAbstractionTree(ancestryObject, null);
12963
13569
  } else {
12964
13570
  const initialSections = /* @__PURE__ */ new Set(["preamble", 0, "0"]);
@@ -12971,148 +13577,190 @@ function XViewScene({
12971
13577
  },
12972
13578
  [handleRenderAncestry, handleRenderAbstractionTree]
12973
13579
  );
12974
- const handleReadModeSectionChange = useCallback4((activeSectionId) => {
12975
- const { ancestry, branchStack } = readingMode;
12976
- if (!ancestry || !readingMode.isActive) return;
12977
- let targetObj = ancestry;
12978
- let targetTree = ancestry.tree;
12979
- if (branchStack.length > 0) {
12980
- const allNodes = Object.values(parentDataRef.current).flatMap((f) => f.nodes);
12981
- const fullTree = buildFullAncestryTree(
12982
- ancestry.tree,
12983
- allNodes,
12984
- ancestryDataRef.current
12985
- );
12986
- let currentPtr = fullTree;
12987
- for (const step of branchStack) {
12988
- const found = findNodePath3(currentPtr, step.nodeId);
12989
- if (found && found.node && found.node.parallel_branches) {
12990
- const branch = found.node.parallel_branches.find((b) => b.id === step.branchId);
12991
- if (branch) {
12992
- targetObj = branch;
12993
- targetTree = branch.tree;
12994
- currentPtr = branch.tree;
13580
+ const handleReadModeSectionChange = useCallback4(
13581
+ (activeSectionId) => {
13582
+ const { ancestry, branchStack } = readingMode;
13583
+ if (!ancestry || !readingMode.isActive) return;
13584
+ let targetObj = ancestry;
13585
+ let targetTree = ancestry.tree;
13586
+ if (branchStack.length > 0) {
13587
+ const allNodes = Object.values(parentDataRef.current).flatMap(
13588
+ (f) => f.nodes
13589
+ );
13590
+ const fullTree = buildFullAncestryTree(
13591
+ ancestry.tree,
13592
+ allNodes,
13593
+ ancestryDataRef.current
13594
+ );
13595
+ let currentPtr = fullTree;
13596
+ for (const step of branchStack) {
13597
+ const found = findNodePath3(currentPtr, step.nodeId);
13598
+ if (found && found.node && found.node.parallel_branches) {
13599
+ const branch = found.node.parallel_branches.find(
13600
+ (b) => b.id === step.branchId
13601
+ );
13602
+ if (branch) {
13603
+ targetObj = branch;
13604
+ targetTree = branch.tree;
13605
+ currentPtr = branch.tree;
13606
+ }
12995
13607
  }
12996
13608
  }
12997
13609
  }
12998
- }
12999
- const descriptionText = targetObj.description || "";
13000
- const savedSections = targetObj.description_sections || [];
13001
- const sections = parseDescriptionSections(descriptionText, savedSections);
13002
- let activeIndex = sections.findIndex((s) => String(s.id) === String(activeSectionId));
13003
- if (activeIndex === -1 && !isNaN(parseInt(activeSectionId))) {
13004
- activeIndex = sections.findIndex((s) => s.numericId === parseInt(activeSectionId));
13005
- }
13006
- if (activeIndex === -1) activeIndex = 0;
13007
- stateRef.current.readMode.currentMaxIndex = activeIndex;
13008
- stateRef.current.maxAncestryRenderIndex = activeIndex;
13009
- const renderLimitIndex = stateRef.current.maxAncestryRenderIndex;
13010
- const allowedIds = /* @__PURE__ */ new Set();
13011
- allowedIds.add("preamble");
13012
- allowedIds.add(0);
13013
- allowedIds.add("0");
13014
- for (let i = 0; i <= renderLimitIndex; i++) {
13015
- if (sections[i]) {
13016
- if (sections[i].id) allowedIds.add(String(sections[i].id));
13017
- if (sections[i].numericId !== void 0 && sections[i].numericId !== null) {
13018
- allowedIds.add(sections[i].numericId);
13019
- allowedIds.add(String(sections[i].numericId));
13610
+ const descriptionText = targetObj.description || "";
13611
+ const savedSections = targetObj.description_sections || [];
13612
+ const sections = parseDescriptionSections(descriptionText, savedSections);
13613
+ let activeIndex = sections.findIndex(
13614
+ (s) => String(s.id) === String(activeSectionId)
13615
+ );
13616
+ if (activeIndex === -1 && !isNaN(parseInt(activeSectionId))) {
13617
+ activeIndex = sections.findIndex(
13618
+ (s) => s.numericId === parseInt(activeSectionId)
13619
+ );
13620
+ }
13621
+ if (activeIndex === -1) activeIndex = 0;
13622
+ stateRef.current.readMode.currentMaxIndex = activeIndex;
13623
+ stateRef.current.maxAncestryRenderIndex = activeIndex;
13624
+ const renderLimitIndex = stateRef.current.maxAncestryRenderIndex;
13625
+ const allowedIds = /* @__PURE__ */ new Set();
13626
+ allowedIds.add("preamble");
13627
+ allowedIds.add(0);
13628
+ allowedIds.add("0");
13629
+ for (let i = 0; i <= renderLimitIndex; i++) {
13630
+ if (sections[i]) {
13631
+ if (sections[i].id) allowedIds.add(String(sections[i].id));
13632
+ if (sections[i].numericId !== void 0 && sections[i].numericId !== null) {
13633
+ allowedIds.add(sections[i].numericId);
13634
+ allowedIds.add(String(sections[i].numericId));
13635
+ }
13020
13636
  }
13021
13637
  }
13022
- }
13023
- const activeSection = sections[activeIndex];
13024
- let focusTargetId = activeSectionId;
13025
- if (activeSection && activeSection.numericId !== void 0 && activeSection.numericId !== null) {
13026
- focusTargetId = activeSection.numericId;
13027
- }
13028
- const currentStackItem = branchStack.length > 0 ? branchStack[branchStack.length - 1] : null;
13029
- const renderPayload = {
13030
- ...targetObj,
13031
- ancestry_id: targetObj.ancestry_id || targetObj.id,
13032
- ancestral_node: targetTree.node ? targetTree.node.id : targetTree.node_id,
13033
- tree: targetTree,
13034
- _originNodeId: currentStackItem ? currentStackItem.nodeId : null,
13035
- _branchDirection: currentStackItem ? currentStackItem.entryDirection : null,
13036
- _forceUpdate: true
13037
- };
13038
- const rotation = branchStack.reduce((acc, step) => {
13039
- return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
13040
- }, 0);
13041
- handleRenderAncestry(renderPayload, allowedIds, focusTargetId, rotation);
13042
- }, [readingMode, handleRenderAncestry, buildFullAncestryTree, ancestryDataRef.current]);
13638
+ const activeSection = sections[activeIndex];
13639
+ let focusTargetId = activeSectionId;
13640
+ if (activeSection && activeSection.numericId !== void 0 && activeSection.numericId !== null) {
13641
+ focusTargetId = activeSection.numericId;
13642
+ }
13643
+ const currentStackItem = branchStack.length > 0 ? branchStack[branchStack.length - 1] : null;
13644
+ const renderPayload = {
13645
+ ...targetObj,
13646
+ ancestry_id: targetObj.ancestry_id || targetObj.id,
13647
+ ancestral_node: targetTree.node ? targetTree.node.id : targetTree.node_id,
13648
+ tree: targetTree,
13649
+ _originNodeId: currentStackItem ? currentStackItem.nodeId : null,
13650
+ _branchDirection: currentStackItem ? currentStackItem.entryDirection : null,
13651
+ _forceUpdate: true
13652
+ };
13653
+ const rotation = branchStack.reduce((acc, step) => {
13654
+ return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
13655
+ }, 0);
13656
+ handleRenderAncestry(renderPayload, allowedIds, focusTargetId, rotation);
13657
+ },
13658
+ [
13659
+ readingMode,
13660
+ handleRenderAncestry,
13661
+ buildFullAncestryTree,
13662
+ ancestryDataRef.current
13663
+ ]
13664
+ );
13043
13665
  const handleCloseReadMode = useCallback4(() => {
13044
13666
  setReadingMode({ isActive: false, ancestry: null, branchStack: [] });
13045
13667
  }, []);
13046
- const handleAncestrySectionChange = useCallback4((activeSectionId, ancestryOverride = null, rotation = 0) => {
13047
- var _a2, _b2;
13048
- const currentMode = stateRef.current.ancestry;
13049
- let targetObj = ancestryOverride;
13050
- if (!targetObj) {
13051
- const currentAncestryId = currentMode.currentAncestryId;
13052
- const ancestryObj = (ancestryDataRef.current || []).find((a) => String(a.ancestry_id) === String(currentAncestryId));
13053
- targetObj = ancestryObj || (currentMode.isActive ? {
13054
- ...currentMode,
13055
- ancestry_id: "temp_creating",
13056
- ancestral_node: (_b2 = (_a2 = currentMode.tree) == null ? void 0 : _a2.node) == null ? void 0 : _b2.id
13057
- } : null);
13058
- }
13059
- if (!targetObj) return;
13060
- const targetId = targetObj.ancestry_id || targetObj.id;
13061
- if (stateRef.current.lastRenderedAncestryId !== targetId) {
13062
- stateRef.current.maxAncestryRenderIndex = 0;
13063
- stateRef.current.lastRenderedAncestryId = targetId;
13064
- }
13065
- const descriptionText = (ancestryOverride ? targetObj.description : currentMode.ancestryDescription) || targetObj.description || "";
13066
- const savedSections = (ancestryOverride ? targetObj.description_sections : currentMode.ancestryDescriptionSections) || targetObj.description_sections || [];
13067
- const sections = parseDescriptionSections(descriptionText, savedSections);
13068
- let activeIndex = sections.findIndex((s) => String(s.id) === String(activeSectionId));
13069
- if (activeIndex === -1 && !isNaN(parseInt(activeSectionId))) {
13070
- activeIndex = sections.findIndex((s) => s.numericId === parseInt(activeSectionId));
13071
- }
13072
- if (activeIndex === -1) activeIndex = 0;
13073
- if (stateRef.current.maxAncestryRenderIndex === void 0) stateRef.current.maxAncestryRenderIndex = 0;
13074
- stateRef.current.maxAncestryRenderIndex = activeIndex;
13075
- const renderLimitIndex = stateRef.current.maxAncestryRenderIndex;
13076
- const allowedIds = /* @__PURE__ */ new Set();
13077
- allowedIds.add("preamble");
13078
- allowedIds.add(0);
13079
- allowedIds.add("0");
13080
- for (let i = 0; i <= renderLimitIndex; i++) {
13081
- if (sections[i]) {
13082
- if (sections[i].id) allowedIds.add(String(sections[i].id));
13083
- if (sections[i].numericId !== void 0 && sections[i].numericId !== null) {
13084
- allowedIds.add(sections[i].numericId);
13085
- allowedIds.add(String(sections[i].numericId));
13668
+ const handleAncestrySectionChange = useCallback4(
13669
+ (activeSectionId, ancestryOverride = null, rotation = 0) => {
13670
+ var _a2, _b2;
13671
+ const currentMode = stateRef.current.ancestry;
13672
+ let targetObj = ancestryOverride;
13673
+ if (!targetObj) {
13674
+ const currentAncestryId = currentMode.currentAncestryId;
13675
+ const ancestryObj = (ancestryDataRef.current || []).find(
13676
+ (a) => String(a.ancestry_id) === String(currentAncestryId)
13677
+ );
13678
+ targetObj = ancestryObj || (currentMode.isActive ? {
13679
+ ...currentMode,
13680
+ ancestry_id: "temp_creating",
13681
+ ancestral_node: (_b2 = (_a2 = currentMode.tree) == null ? void 0 : _a2.node) == null ? void 0 : _b2.id
13682
+ } : null);
13683
+ }
13684
+ if (!targetObj) return;
13685
+ const targetId = targetObj.ancestry_id || targetObj.id;
13686
+ if (stateRef.current.lastRenderedAncestryId !== targetId) {
13687
+ stateRef.current.maxAncestryRenderIndex = 0;
13688
+ stateRef.current.lastRenderedAncestryId = targetId;
13689
+ }
13690
+ const descriptionText = (ancestryOverride ? targetObj.description : currentMode.ancestryDescription) || targetObj.description || "";
13691
+ const savedSections = (ancestryOverride ? targetObj.description_sections : currentMode.ancestryDescriptionSections) || targetObj.description_sections || [];
13692
+ const sections = parseDescriptionSections(descriptionText, savedSections);
13693
+ let activeIndex = sections.findIndex(
13694
+ (s) => String(s.id) === String(activeSectionId)
13695
+ );
13696
+ if (activeIndex === -1 && !isNaN(parseInt(activeSectionId))) {
13697
+ activeIndex = sections.findIndex(
13698
+ (s) => s.numericId === parseInt(activeSectionId)
13699
+ );
13700
+ }
13701
+ if (activeIndex === -1) activeIndex = 0;
13702
+ if (stateRef.current.maxAncestryRenderIndex === void 0)
13703
+ stateRef.current.maxAncestryRenderIndex = 0;
13704
+ stateRef.current.maxAncestryRenderIndex = activeIndex;
13705
+ const renderLimitIndex = stateRef.current.maxAncestryRenderIndex;
13706
+ const allowedIds = /* @__PURE__ */ new Set();
13707
+ allowedIds.add("preamble");
13708
+ allowedIds.add(0);
13709
+ allowedIds.add("0");
13710
+ for (let i = 0; i <= renderLimitIndex; i++) {
13711
+ if (sections[i]) {
13712
+ if (sections[i].id) allowedIds.add(String(sections[i].id));
13713
+ if (sections[i].numericId !== void 0 && sections[i].numericId !== null) {
13714
+ allowedIds.add(sections[i].numericId);
13715
+ allowedIds.add(String(sections[i].numericId));
13716
+ }
13086
13717
  }
13087
13718
  }
13088
- }
13089
- const activeSection = sections[activeIndex];
13090
- let focusTargetId = activeSectionId;
13091
- if (activeSection && activeSection.numericId !== void 0 && activeSection.numericId !== null) {
13092
- focusTargetId = activeSection.numericId;
13093
- }
13094
- const treeToRender = ancestryOverride ? ancestryOverride.tree : currentMode.isActive && currentMode.tree ? currentMode.tree : targetObj.tree;
13095
- const renderPayload = { ...targetObj, tree: treeToRender };
13096
- handleRenderAncestry(renderPayload, allowedIds, focusTargetId, rotation);
13097
- }, [handleRenderAncestry]);
13719
+ const activeSection = sections[activeIndex];
13720
+ let focusTargetId = activeSectionId;
13721
+ if (activeSection && activeSection.numericId !== void 0 && activeSection.numericId !== null) {
13722
+ focusTargetId = activeSection.numericId;
13723
+ }
13724
+ const treeToRender = ancestryOverride ? ancestryOverride.tree : currentMode.isActive && currentMode.tree ? currentMode.tree : targetObj.tree;
13725
+ const renderPayload = { ...targetObj, tree: treeToRender };
13726
+ handleRenderAncestry(renderPayload, allowedIds, focusTargetId, rotation);
13727
+ },
13728
+ [handleRenderAncestry]
13729
+ );
13098
13730
  const handleEditAncestry = useCallback4(
13099
13731
  async (ancestryObject) => {
13100
- setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
13732
+ setContextMenu(
13733
+ (prev) => prev.visible ? { ...prev, visible: false } : prev
13734
+ );
13101
13735
  if (!ancestryObject || !ancestryObject.tree) {
13102
- alert("N\xE3o foi poss\xEDvel carregar os dados desta ancestralidade para edi\xE7\xE3o.");
13736
+ alert(
13737
+ "N\xE3o foi poss\xEDvel carregar os dados desta ancestralidade para edi\xE7\xE3o."
13738
+ );
13103
13739
  return;
13104
13740
  }
13105
13741
  stateRef.current.maxAncestryRenderIndex = 0;
13106
13742
  const initialSections = /* @__PURE__ */ new Set(["preamble", 0]);
13107
13743
  await handleRenderAncestry(ancestryObject, initialSections);
13108
- const allParentNodes = Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes);
13744
+ const allParentNodes = Object.values(parentDataRef.current).flatMap(
13745
+ (fileData) => fileData.nodes
13746
+ );
13109
13747
  const allAncestries = ancestryDataRef.current || [];
13110
- const fullTree = buildFullAncestryTree(ancestryObject.tree, allParentNodes, allAncestries);
13748
+ const fullTree = buildFullAncestryTree(
13749
+ ancestryObject.tree,
13750
+ allParentNodes,
13751
+ allAncestries
13752
+ );
13111
13753
  if (!fullTree) {
13112
- alert("Falha ao reconstruir a \xE1rvore de ancestralidade. Alguns Nodes podem estar faltando.");
13754
+ alert(
13755
+ "Falha ao reconstruir a \xE1rvore de ancestralidade. Alguns Nodes podem estar faltando."
13756
+ );
13113
13757
  return;
13114
13758
  }
13115
- const fullAbstractionTree = ancestryObject.abstraction_tree ? buildFullAncestryTree(ancestryObject.abstraction_tree, allParentNodes, allAncestries) : { node: fullTree.node, children: [] };
13759
+ const fullAbstractionTree = ancestryObject.abstraction_tree ? buildFullAncestryTree(
13760
+ ancestryObject.abstraction_tree,
13761
+ allParentNodes,
13762
+ allAncestries
13763
+ ) : { node: fullTree.node, children: [] };
13116
13764
  setAncestryMode({
13117
13765
  isActive: true,
13118
13766
  ...ancestryObject,
@@ -13164,7 +13812,9 @@ function XViewScene({
13164
13812
  const treeToUse = treeOverride || ancestryMode.tree;
13165
13813
  const { isEditMode, currentAncestryId } = ancestryMode;
13166
13814
  if (!treeToUse || !treeToUse.node) {
13167
- alert("Erro: A estrutura da ancestralidade \xE9 inv\xE1lida (Node raiz ausente).");
13815
+ alert(
13816
+ "Erro: A estrutura da ancestralidade \xE9 inv\xE1lida (Node raiz ausente)."
13817
+ );
13168
13818
  return;
13169
13819
  }
13170
13820
  if (!save_view_data || !stateRef.current.nodeIdToParentFileMap) return;
@@ -13197,14 +13847,18 @@ function XViewScene({
13197
13847
  };
13198
13848
  };
13199
13849
  const treeWithIds = convertTreeToIds(treeToUse);
13200
- const abstractionTreeWithIds = convertTreeToIds(ancestryMode.abstraction_tree);
13850
+ const abstractionTreeWithIds = convertTreeToIds(
13851
+ ancestryMode.abstraction_tree
13852
+ );
13201
13853
  if (!treeWithIds) {
13202
13854
  alert("Erro ao processar a \xE1rvore da ancestralidade.");
13203
13855
  return;
13204
13856
  }
13205
13857
  let originalAncestryObj = null;
13206
13858
  if (isEditMode && currentAncestryId) {
13207
- originalAncestryObj = (ancestryDataRef.current || []).find((anc) => String(anc.ancestry_id) === String(currentAncestryId));
13859
+ originalAncestryObj = (ancestryDataRef.current || []).find(
13860
+ (anc) => String(anc.ancestry_id) === String(currentAncestryId)
13861
+ );
13208
13862
  }
13209
13863
  const ancestryObjectToSave = {
13210
13864
  ancestry_id: isEditMode && currentAncestryId ? currentAncestryId : `${short2.generate()}`,
@@ -13287,14 +13941,20 @@ function XViewScene({
13287
13941
  try {
13288
13942
  if (isExternalSave) {
13289
13943
  try {
13290
- const remoteResponse = await get_ancestry_file(targetFileIdForCache, targetOwnerIdForCache);
13944
+ const remoteResponse = await get_ancestry_file(
13945
+ targetFileIdForCache,
13946
+ targetOwnerIdForCache
13947
+ );
13291
13948
  if (remoteResponse.success && Array.isArray(remoteResponse.data)) {
13292
13949
  masterAncestryList = remoteResponse.data;
13293
13950
  } else {
13294
13951
  masterAncestryList = [];
13295
13952
  }
13296
13953
  } catch (fetchErr) {
13297
- console.warn("Arquivo de destino n\xE3o existe ou erro ao buscar, criando novo:", fetchErr);
13954
+ console.warn(
13955
+ "Arquivo de destino n\xE3o existe ou erro ao buscar, criando novo:",
13956
+ fetchErr
13957
+ );
13298
13958
  masterAncestryList = [];
13299
13959
  }
13300
13960
  } else {
@@ -13314,7 +13974,9 @@ function XViewScene({
13314
13974
  const result = await save_view_data(finalSaveUrl, masterAncestryList);
13315
13975
  if (result.success) {
13316
13976
  const localList = [...ancestryDataRef.current || []];
13317
- const localIndex = localList.findIndex((a) => String(a.ancestry_id) === String(ancestryObjectToSave.ancestry_id));
13977
+ const localIndex = localList.findIndex(
13978
+ (a) => String(a.ancestry_id) === String(ancestryObjectToSave.ancestry_id)
13979
+ );
13318
13980
  if (localIndex !== -1) {
13319
13981
  localList[localIndex] = ancestryObjectToSave;
13320
13982
  } else {
@@ -13342,11 +14004,29 @@ function XViewScene({
13342
14004
  return;
13343
14005
  }
13344
14006
  if (!keepOpen) {
13345
- setAncestryMode({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", ancestryDescriptionSections: [], isAddingNodes: false });
14007
+ setAncestryMode({
14008
+ isActive: false,
14009
+ tree: null,
14010
+ selectedParentId: null,
14011
+ isEditMode: false,
14012
+ currentAncestryId: null,
14013
+ ancestryName: "",
14014
+ ancestryDescription: "",
14015
+ ancestryDescriptionSections: [],
14016
+ isAddingNodes: false
14017
+ });
13346
14018
  if (mountRef.current) mountRef.current.style.cursor = "grab";
13347
14019
  }
13348
14020
  },
13349
- [ancestryMode, ancestry_save_url, handleRenderAncestry, save_view_data, sceneConfigId, ownerId, get_ancestry_file]
14021
+ [
14022
+ ancestryMode,
14023
+ ancestry_save_url,
14024
+ handleRenderAncestry,
14025
+ save_view_data,
14026
+ sceneConfigId,
14027
+ ownerId,
14028
+ get_ancestry_file
14029
+ ]
13350
14030
  );
13351
14031
  const handleOpenAncestryRelEditor = (path, currentData) => {
13352
14032
  setEditingAncestryRel({ visible: true, data: currentData, path });
@@ -13376,7 +14056,9 @@ function XViewScene({
13376
14056
  if (ancestryToDelete && delete_file_action) {
13377
14057
  const urls = extractFileUrlsFromProperties(ancestryToDelete);
13378
14058
  if (urls.length > 0) {
13379
- Promise.all(urls.map((url) => delete_file_action(url))).catch((err) => console.error("Erro ao deletar arquivos da ancestralidade:", err));
14059
+ Promise.all(urls.map((url) => delete_file_action(url))).catch(
14060
+ (err) => console.error("Erro ao deletar arquivos da ancestralidade:", err)
14061
+ );
13380
14062
  }
13381
14063
  }
13382
14064
  if (!ancestryToDelete) {
@@ -13386,7 +14068,9 @@ function XViewScene({
13386
14068
  const sourceFileId = ancestryToDelete._source_file_id;
13387
14069
  const sourceOwnerId = ancestryToDelete._source_owner_id;
13388
14070
  if (!sourceFileId || !sourceOwnerId) {
13389
- alert("N\xE3o foi poss\xEDvel identificar o arquivo de origem desta ancestralidade.");
14071
+ alert(
14072
+ "N\xE3o foi poss\xEDvel identificar o arquivo de origem desta ancestralidade."
14073
+ );
13390
14074
  return;
13391
14075
  }
13392
14076
  const finalSaveUrl = `x_view_ancestry/${sourceOwnerId}/${sourceFileId}`;
@@ -13394,13 +14078,18 @@ function XViewScene({
13394
14078
  (anc) => anc._source_file_id === sourceFileId && String(anc.ancestry_id) !== String(ancestryIdToDelete)
13395
14079
  );
13396
14080
  try {
13397
- const result = await save_view_data(finalSaveUrl, updatedAncestriesForFile);
14081
+ const result = await save_view_data(
14082
+ finalSaveUrl,
14083
+ updatedAncestriesForFile
14084
+ );
13398
14085
  if (result.success) {
13399
14086
  ancestryDataRef.current = (ancestryDataRef.current || []).filter(
13400
14087
  (ancestry) => String(ancestry.ancestry_id) !== String(ancestryIdToDelete)
13401
14088
  );
13402
14089
  const { renderedAncestries, ancestryGroup } = stateRef.current;
13403
- const renderIndex = renderedAncestries.findIndex((a) => String(a.id) === String(ancestryIdToDelete));
14090
+ const renderIndex = renderedAncestries.findIndex(
14091
+ (a) => String(a.id) === String(ancestryIdToDelete)
14092
+ );
13404
14093
  if (renderIndex !== -1) {
13405
14094
  const toRemove = renderedAncestries[renderIndex];
13406
14095
  toRemove.lines.forEach((line) => {
@@ -13409,18 +14098,29 @@ function XViewScene({
13409
14098
  if (line.material) line.material.dispose();
13410
14099
  });
13411
14100
  renderedAncestries.splice(renderIndex, 1);
13412
- stateRef.current.ancestryLinks = renderedAncestries.flatMap((a) => a.lines);
14101
+ stateRef.current.ancestryLinks = renderedAncestries.flatMap(
14102
+ (a) => a.lines
14103
+ );
13413
14104
  }
13414
14105
  setSceneVersion((v) => v + 1);
13415
14106
  } else {
13416
- throw new Error(result.error || "Ocorreu um erro desconhecido ao excluir.");
14107
+ throw new Error(
14108
+ result.error || "Ocorreu um erro desconhecido ao excluir."
14109
+ );
13417
14110
  }
13418
14111
  } catch (error) {
13419
14112
  console.error("Falha ao excluir a ancestralidade:", error);
13420
14113
  alert(`Erro ao excluir a ancestralidade: ${error.message}`);
13421
14114
  return;
13422
14115
  }
13423
- setAncestryMode({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "" });
14116
+ setAncestryMode({
14117
+ isActive: false,
14118
+ tree: null,
14119
+ selectedParentId: null,
14120
+ isEditMode: false,
14121
+ currentAncestryId: null,
14122
+ ancestryName: ""
14123
+ });
13424
14124
  if (mountRef.current) mountRef.current.style.cursor = "grab";
13425
14125
  },
13426
14126
  [save_view_data, delete_file_action]
@@ -13428,24 +14128,38 @@ function XViewScene({
13428
14128
  const handleOpenAncestryBoard = useCallback4(() => {
13429
14129
  setIsAncestryBoardOpen(true);
13430
14130
  }, []);
13431
- const handleSelectAncestryFromBoard = useCallback4((ancestry) => {
13432
- setIsAncestryBoardOpen(false);
13433
- setIsSidebarOpen(false);
13434
- handleStartReadingAncestry(ancestry);
13435
- }, [handleStartReadingAncestry]);
13436
- const handleSaveAncestryBoard = useCallback4(async (groups) => {
13437
- if (!sceneConfigId || !viewParams || !session) return;
13438
- const sceneType = (viewParams.type || "").toLowerCase().includes("database") ? "database" : "view";
13439
- await save_ancestry_board_action(sceneConfigId, sceneType, groups, session, ownerId);
13440
- }, [sceneConfigId, viewParams, session, save_ancestry_board_action, ownerId]);
14131
+ const handleSelectAncestryFromBoard = useCallback4(
14132
+ (ancestry) => {
14133
+ setIsAncestryBoardOpen(false);
14134
+ setIsSidebarOpen(false);
14135
+ handleStartReadingAncestry(ancestry);
14136
+ },
14137
+ [handleStartReadingAncestry]
14138
+ );
14139
+ const handleSaveAncestryBoard = useCallback4(
14140
+ async (groups) => {
14141
+ if (!sceneConfigId || !viewParams || !session) return;
14142
+ const sceneType = (viewParams.type || "").toLowerCase().includes("database") ? "database" : "view";
14143
+ await save_ancestry_board_action(
14144
+ sceneConfigId,
14145
+ sceneType,
14146
+ groups,
14147
+ session,
14148
+ ownerId
14149
+ );
14150
+ },
14151
+ [sceneConfigId, viewParams, session, save_ancestry_board_action, ownerId]
14152
+ );
13441
14153
  const existingNodeTypes = useMemo12(() => {
13442
14154
  if (!parentDataRef.current) {
13443
14155
  return [];
13444
14156
  }
13445
- const allTypes = Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes.flatMap((node) => {
13446
- if (Array.isArray(node.type)) return node.type;
13447
- return [node.type];
13448
- })).filter((t) => Boolean(t) && String(t).toLowerCase() !== "quest");
14157
+ const allTypes = Object.values(parentDataRef.current).flatMap(
14158
+ (fileData) => fileData.nodes.flatMap((node) => {
14159
+ if (Array.isArray(node.type)) return node.type;
14160
+ return [node.type];
14161
+ })
14162
+ ).filter((t) => Boolean(t) && String(t).toLowerCase() !== "quest");
13449
14163
  return [...new Set(allTypes)];
13450
14164
  }, [parentDataRef.current, sceneVersion]);
13451
14165
  const searchableDbNodes = useMemo12(() => {
@@ -13459,7 +14173,10 @@ function XViewScene({
13459
14173
  }, [parentDataRef.current, sceneVersion]);
13460
14174
  const handleAddExistingNode = useCallback4(
13461
14175
  (nodeId) => {
13462
- return userActionHandlers.handleAddExistingNodeById(actionHandlerContext, nodeId);
14176
+ return userActionHandlers.handleAddExistingNodeById(
14177
+ actionHandlerContext,
14178
+ nodeId
14179
+ );
13463
14180
  },
13464
14181
  [actionHandlerContext]
13465
14182
  );
@@ -13467,7 +14184,9 @@ function XViewScene({
13467
14184
  var _a2, _b2, _c2;
13468
14185
  const { nodeObjects, allLinks } = stateRef.current;
13469
14186
  if (!nodeObjects || !allLinks || !sceneSaveUrl || !parentDataRef.current) {
13470
- console.warn("N\xE3o \xE9 poss\xEDvel salvar a cena: estado n\xE3o inicializado ou URL de salvamento ausente.");
14187
+ console.warn(
14188
+ "N\xE3o \xE9 poss\xEDvel salvar a cena: estado n\xE3o inicializado ou URL de salvamento ausente."
14189
+ );
13471
14190
  return;
13472
14191
  }
13473
14192
  if (!save_view_data) return;
@@ -13504,48 +14223,68 @@ function XViewScene({
13504
14223
  }, [sceneSaveUrl, save_view_data, sceneConfigId, viewParams == null ? void 0 : viewParams.type]);
13505
14224
  const allAvailableNodes = useMemo12(() => {
13506
14225
  if (!parentDataRef.current) return [];
13507
- return Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes || []);
14226
+ return Object.values(parentDataRef.current).flatMap(
14227
+ (fileData) => fileData.nodes || []
14228
+ );
13508
14229
  }, [sceneVersion, isInitialized]);
13509
14230
  const allAvailableAncestries = useMemo12(() => {
13510
14231
  return ancestryDataRef.current || [];
13511
14232
  }, [sceneVersion, isInitialized]);
13512
- const handleOpenReference = useCallback4((referenceData) => {
13513
- const { type, id } = referenceData;
13514
- if (type === "node") {
13515
- const targetNode = allAvailableNodes.find((n) => String(n.id) === String(id));
13516
- if (targetNode) {
13517
- setAncestryLinkDetails(null);
13518
- setDetailsLink(null);
13519
- setDetailsNode(targetNode);
13520
- const sceneMesh = stateRef.current.nodeObjects[String(id)];
13521
- if (sceneMesh) {
13522
- tweenToTarget(sceneMesh);
14233
+ const handleOpenReference = useCallback4(
14234
+ (referenceData) => {
14235
+ const { type, id } = referenceData;
14236
+ if (type === "node") {
14237
+ const targetNode = allAvailableNodes.find(
14238
+ (n) => String(n.id) === String(id)
14239
+ );
14240
+ if (targetNode) {
14241
+ setAncestryLinkDetails(null);
14242
+ setDetailsLink(null);
14243
+ setDetailsNode(targetNode);
14244
+ const sceneMesh = stateRef.current.nodeObjects[String(id)];
14245
+ if (sceneMesh) {
14246
+ tweenToTarget(sceneMesh);
14247
+ }
14248
+ } else {
14249
+ alert("Node original n\xE3o encontrado neste contexto.");
14250
+ }
14251
+ } else if (type === "ancestry") {
14252
+ const targetAncestry = allAvailableAncestries.find(
14253
+ (a) => String(a.ancestry_id) === String(id)
14254
+ );
14255
+ if (targetAncestry) {
14256
+ setDetailsNode(null);
14257
+ setDetailsLink(null);
14258
+ setAncestryLinkDetails(null);
14259
+ handleEditAncestry(targetAncestry);
14260
+ } else {
14261
+ alert("Ancestralidade original n\xE3o encontrada neste contexto.");
13523
14262
  }
13524
- } else {
13525
- alert("Node original n\xE3o encontrado neste contexto.");
13526
- }
13527
- } else if (type === "ancestry") {
13528
- const targetAncestry = allAvailableAncestries.find((a) => String(a.ancestry_id) === String(id));
13529
- if (targetAncestry) {
13530
- setDetailsNode(null);
13531
- setDetailsLink(null);
13532
- setAncestryLinkDetails(null);
13533
- handleEditAncestry(targetAncestry);
13534
- } else {
13535
- alert("Ancestralidade original n\xE3o encontrada neste contexto.");
13536
14263
  }
13537
- }
13538
- }, [allAvailableNodes, allAvailableAncestries, handleEditAncestry, tweenToTarget]);
14264
+ },
14265
+ [
14266
+ allAvailableNodes,
14267
+ allAvailableAncestries,
14268
+ handleEditAncestry,
14269
+ tweenToTarget
14270
+ ]
14271
+ );
13539
14272
  const handleToggleAncestryAddMode = useCallback4(() => {
13540
- setAncestryMode((prev) => ({ ...prev, isAddingNodes: !prev.isAddingNodes }));
14273
+ setAncestryMode((prev) => ({
14274
+ ...prev,
14275
+ isAddingNodes: !prev.isAddingNodes
14276
+ }));
13541
14277
  }, []);
13542
- const handleFocusNode = useCallback4((nodeData) => {
13543
- if (!nodeData) return;
13544
- const nodeMesh = stateRef.current.nodeObjects[String(nodeData.id)];
13545
- if (nodeMesh) {
13546
- tweenToTarget(nodeMesh, 1.2);
13547
- }
13548
- }, [tweenToTarget]);
14278
+ const handleFocusNode = useCallback4(
14279
+ (nodeData) => {
14280
+ if (!nodeData) return;
14281
+ const nodeMesh = stateRef.current.nodeObjects[String(nodeData.id)];
14282
+ if (nodeMesh) {
14283
+ tweenToTarget(nodeMesh, 1.2);
14284
+ }
14285
+ },
14286
+ [tweenToTarget]
14287
+ );
13549
14288
  const availableDatasets = useMemo12(() => {
13550
14289
  if (!sceneDataRef.current || !parentDataRef.current) return [];
13551
14290
  return sceneDataRef.current.parent_dbs.map((db) => {
@@ -13556,7 +14295,9 @@ function XViewScene({
13556
14295
  };
13557
14296
  });
13558
14297
  }, [sceneVersion, isInitialized]);
13559
- const sourceNodeDatasetId = creationMode.sourceNodeData ? (_b = stateRef.current.nodeIdToParentFileMap.get(String(creationMode.sourceNodeData.id))) == null ? void 0 : _b.parentFileId : null;
14298
+ const sourceNodeDatasetId = creationMode.sourceNodeData ? (_b = stateRef.current.nodeIdToParentFileMap.get(
14299
+ String(creationMode.sourceNodeData.id)
14300
+ )) == null ? void 0 : _b.parentFileId : null;
13560
14301
  const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
13561
14302
  useEffect22(() => {
13562
14303
  if (isInitialized && focusNodeId && !hasFocusedInitial) {
@@ -13571,11 +14312,19 @@ function XViewScene({
13571
14312
  setHasFocusedInitial(true);
13572
14313
  }
13573
14314
  }
13574
- }, [isInitialized, sceneVersion, focusNodeId, hasFocusedInitial, tweenToTarget]);
14315
+ }, [
14316
+ isInitialized,
14317
+ sceneVersion,
14318
+ focusNodeId,
14319
+ hasFocusedInitial,
14320
+ tweenToTarget
14321
+ ]);
13575
14322
  useEffect22(() => {
13576
14323
  if (isInitialized && focusAncestryId && !hasOpenedInitialAncestry) {
13577
14324
  const ancestries = ancestryDataRef.current || [];
13578
- const targetAncestry = ancestries.find((a) => String(a.ancestry_id) === String(focusAncestryId));
14325
+ const targetAncestry = ancestries.find(
14326
+ (a) => String(a.ancestry_id) === String(focusAncestryId)
14327
+ );
13579
14328
  if (targetAncestry) {
13580
14329
  setTimeout(() => {
13581
14330
  handleStartReadingAncestry(targetAncestry);
@@ -13585,18 +14334,38 @@ function XViewScene({
13585
14334
  setHasOpenedInitialAncestry(true);
13586
14335
  }
13587
14336
  }
13588
- }, [isInitialized, sceneVersion, focusAncestryId, hasOpenedInitialAncestry, handleStartReadingAncestry]);
14337
+ }, [
14338
+ isInitialized,
14339
+ sceneVersion,
14340
+ focusAncestryId,
14341
+ hasOpenedInitialAncestry,
14342
+ handleStartReadingAncestry
14343
+ ]);
13589
14344
  useEffect22(() => {
13590
14345
  function handleKeyDown(event) {
13591
14346
  var _a2, _b2, _c2;
13592
14347
  const context = actionHandlerContext;
13593
14348
  if (event.key === "Escape") {
13594
- if (stateRef.current.connection.isActive) userActionHandlers.handleCancelConnection(context);
13595
- if (stateRef.current.relink.isActive) userActionHandlers.handleCancelRelink(context);
13596
- if (stateRef.current.creation.isActive) userActionHandlers.handleCancelCreation(context);
13597
- if ((_a2 = stateRef.current.versionMode) == null ? void 0 : _a2.isActive) userActionHandlers.handleCancelVersioning(context);
14349
+ if (stateRef.current.connection.isActive)
14350
+ userActionHandlers.handleCancelConnection(context);
14351
+ if (stateRef.current.relink.isActive)
14352
+ userActionHandlers.handleCancelRelink(context);
14353
+ if (stateRef.current.creation.isActive)
14354
+ userActionHandlers.handleCancelCreation(context);
14355
+ if ((_a2 = stateRef.current.versionMode) == null ? void 0 : _a2.isActive)
14356
+ userActionHandlers.handleCancelVersioning(context);
13598
14357
  if (stateRef.current.ancestry.isActive) {
13599
- setAncestryMode({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", ancestryDescriptionSections: [], isAddingNodes: false });
14358
+ setAncestryMode({
14359
+ isActive: false,
14360
+ tree: null,
14361
+ selectedParentId: null,
14362
+ isEditMode: false,
14363
+ currentAncestryId: null,
14364
+ ancestryName: "",
14365
+ ancestryDescription: "",
14366
+ ancestryDescriptionSections: [],
14367
+ isAddingNodes: false
14368
+ });
13600
14369
  if (mountRef.current) mountRef.current.style.cursor = "grab";
13601
14370
  }
13602
14371
  if (questMode.isActive) {
@@ -13605,7 +14374,9 @@ function XViewScene({
13605
14374
  if (stateRef.current.selectedNodes.size > 0) {
13606
14375
  stateRef.current.selectedNodes.clear();
13607
14376
  }
13608
- setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
14377
+ setContextMenu(
14378
+ (prev) => prev.visible ? { ...prev, visible: false } : prev
14379
+ );
13609
14380
  setMultiContextMenu((prev) => ({ ...prev, visible: false }));
13610
14381
  setRelationshipMenu((prev) => ({ ...prev, visible: false }));
13611
14382
  }
@@ -13624,7 +14395,9 @@ function XViewScene({
13624
14395
  let attempts = 0;
13625
14396
  const MIN_CLEARANCE = 15;
13626
14397
  while (isOccupied && attempts < 30) {
13627
- isOccupied = existingNodes.some((mesh) => mesh.position.distanceTo(ghostPosition) < MIN_CLEARANCE);
14398
+ isOccupied = existingNodes.some(
14399
+ (mesh) => mesh.position.distanceTo(ghostPosition) < MIN_CLEARANCE
14400
+ );
13628
14401
  if (isOccupied) {
13629
14402
  ghostPosition.x = controls.target.x + Math.cos(angle) * radius;
13630
14403
  ghostPosition.y = controls.target.y + (Math.random() - 0.5) * 8;
@@ -13643,7 +14416,11 @@ function XViewScene({
13643
14416
  intensity: 0,
13644
14417
  type: ["quest"]
13645
14418
  };
13646
- const ghostNode = createNodeMesh(ghostData, ghostPosition, glowTexture);
14419
+ const ghostNode = createNodeMesh(
14420
+ ghostData,
14421
+ ghostPosition,
14422
+ glowTexture
14423
+ );
13647
14424
  ghostNode.traverse((child) => {
13648
14425
  if (child.isMesh) {
13649
14426
  child.material.transparent = true;
@@ -13690,13 +14467,49 @@ function XViewScene({
13690
14467
  return /* @__PURE__ */ React25.createElement(LoadingScreen, null);
13691
14468
  }
13692
14469
  if (permissionStatus === "denied") {
13693
- return /* @__PURE__ */ React25.createElement("div", { className: "flex flex-col items-center justify-center min-h-screen w-full bg-slate-950 text-white" }, /* @__PURE__ */ React25.createElement("div", { className: "bg-slate-900/50 p-8 rounded-2xl border border-slate-800 shadow-2xl text-center max-w-md" }, /* @__PURE__ */ React25.createElement("div", { className: "mb-4 text-red-500" }, /* @__PURE__ */ React25.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: "currentColor", className: "w-16 h-16 mx-auto" }, /* @__PURE__ */ React25.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" }))), /* @__PURE__ */ React25.createElement("h2", { className: "text-2xl font-bold mb-2" }, "Acesso Negado"), /* @__PURE__ */ React25.createElement("p", { className: "text-slate-400 mb-6" }, "Voc\xEA n\xE3o tem permiss\xE3o para acessar este conte\xFAdo. Solicite acesso ao propriet\xE1rio ou verifique se est\xE1 na conta correta."), /* @__PURE__ */ React25.createElement(
14470
+ return /* @__PURE__ */ React25.createElement("div", { className: "flex flex-col items-center justify-center min-h-screen w-full bg-slate-950 text-white" }, /* @__PURE__ */ React25.createElement("div", { className: "bg-slate-900/50 p-8 rounded-2xl border border-slate-800 shadow-2xl text-center max-w-md" }, /* @__PURE__ */ React25.createElement("div", { className: "mb-4 text-red-500" }, /* @__PURE__ */ React25.createElement(
14471
+ "svg",
14472
+ {
14473
+ xmlns: "http://www.w3.org/2000/svg",
14474
+ fill: "none",
14475
+ viewBox: "0 0 24 24",
14476
+ strokeWidth: 1.5,
14477
+ stroke: "currentColor",
14478
+ className: "w-16 h-16 mx-auto"
14479
+ },
14480
+ /* @__PURE__ */ React25.createElement(
14481
+ "path",
14482
+ {
14483
+ strokeLinecap: "round",
14484
+ strokeLinejoin: "round",
14485
+ d: "M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
14486
+ }
14487
+ )
14488
+ )), /* @__PURE__ */ React25.createElement("h2", { className: "text-2xl font-bold mb-2" }, "Acesso Negado"), /* @__PURE__ */ React25.createElement("p", { className: "text-slate-400 mb-6" }, "Voc\xEA n\xE3o tem permiss\xE3o para acessar este conte\xFAdo. Solicite acesso ao propriet\xE1rio ou verifique se est\xE1 na conta correta."), /* @__PURE__ */ React25.createElement(
13694
14489
  "button",
13695
14490
  {
13696
14491
  onClick: () => router.push("/dashboard/scenes"),
13697
14492
  className: "flex items-center justify-center gap-2 w-full py-3 px-4 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors font-medium"
13698
14493
  },
13699
- /* @__PURE__ */ React25.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", className: "w-5 h-5" }, /* @__PURE__ */ React25.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18" })),
14494
+ /* @__PURE__ */ React25.createElement(
14495
+ "svg",
14496
+ {
14497
+ xmlns: "http://www.w3.org/2000/svg",
14498
+ fill: "none",
14499
+ viewBox: "0 0 24 24",
14500
+ strokeWidth: 2,
14501
+ stroke: "currentColor",
14502
+ className: "w-5 h-5"
14503
+ },
14504
+ /* @__PURE__ */ React25.createElement(
14505
+ "path",
14506
+ {
14507
+ strokeLinecap: "round",
14508
+ strokeLinejoin: "round",
14509
+ d: "M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"
14510
+ }
14511
+ )
14512
+ ),
13700
14513
  "Voltar para Scenes"
13701
14514
  )));
13702
14515
  }
@@ -13743,7 +14556,14 @@ function XViewScene({
13743
14556
  onImageChange: handleGhostNodeImageChange,
13744
14557
  onOpenImageViewer: handleOpenImageViewer,
13745
14558
  onMentionClick: handleAddExistingNode,
13746
- style: { position: "absolute", left: `${formPosition.left}px`, top: `${formPosition.top}px`, opacity: formPosition.opacity, zIndex: 20, transition: "opacity 200ms ease-out" },
14559
+ style: {
14560
+ position: "absolute",
14561
+ left: `${formPosition.left}px`,
14562
+ top: `${formPosition.top}px`,
14563
+ opacity: formPosition.opacity,
14564
+ zIndex: 20,
14565
+ transition: "opacity 200ms ease-out"
14566
+ },
13747
14567
  refEl: formRef,
13748
14568
  existingTypes: existingNodeTypes,
13749
14569
  initialColor: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.color,
@@ -13767,7 +14587,14 @@ function XViewScene({
13767
14587
  onImageChange: handleGhostNodeImageChange,
13768
14588
  onOpenImageViewer: handleOpenImageViewer,
13769
14589
  onMentionClick: handleAddExistingNode,
13770
- style: { position: "absolute", left: `${formPosition.left}px`, top: `${formPosition.top}px`, opacity: formPosition.opacity, zIndex: 20, transition: "opacity 200ms ease-out" },
14590
+ style: {
14591
+ position: "absolute",
14592
+ left: `${formPosition.left}px`,
14593
+ top: `${formPosition.top}px`,
14594
+ opacity: formPosition.opacity,
14595
+ zIndex: 20,
14596
+ transition: "opacity 200ms ease-out"
14597
+ },
13771
14598
  refEl: formRef,
13772
14599
  fixedType: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.type,
13773
14600
  fixedColor: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.color,
@@ -13784,7 +14611,13 @@ function XViewScene({
13784
14611
  onNameChange: handleGhostNodeNameChange,
13785
14612
  onColorChange: handleGhostNodeColorChange,
13786
14613
  onSizeChange: handleGhostNodeSizeChange,
13787
- style: { position: "absolute", left: `16px`, top: `16px`, zIndex: 20, transition: "opacity 200ms ease-out" },
14614
+ style: {
14615
+ position: "absolute",
14616
+ left: `16px`,
14617
+ top: `16px`,
14618
+ zIndex: 20,
14619
+ transition: "opacity 200ms ease-out"
14620
+ },
13788
14621
  refEl: formRef,
13789
14622
  onOpenImageViewer: handleOpenImageViewer,
13790
14623
  onMentionClick: handleAddExistingNode,
@@ -13799,7 +14632,14 @@ function XViewScene({
13799
14632
  "div",
13800
14633
  {
13801
14634
  className: `ui-overlay absolute group rounded-2xl border border-white/10 bg-slate-950/70 backdrop-blur-xl shadow-[0_20px_80px_rgba(0,0,0,0.6)] ring-1 ring-white/10 text-white overflow-hidden flex flex-col ${isReadModeResizing ? "transition-none" : "transition-all duration-300 ease-out"}`,
13802
- style: { top: 16, right: 16, zIndex: 1100, maxHeight: "calc(100vh - 32px)", width: `${readModeWidth}px`, maxWidth: "92vw" }
14635
+ style: {
14636
+ top: 16,
14637
+ right: 16,
14638
+ zIndex: 1100,
14639
+ maxHeight: "calc(100vh - 32px)",
14640
+ width: `${readModeWidth}px`,
14641
+ maxWidth: "92vw"
14642
+ }
13803
14643
  },
13804
14644
  /* @__PURE__ */ React25.createElement(
13805
14645
  "div",
@@ -13857,7 +14697,16 @@ function XViewScene({
13857
14697
  onSave: handleSaveAncestry,
13858
14698
  onEditRelationship: handleOpenAncestryRelEditor,
13859
14699
  onClose: () => {
13860
- setAncestryMode({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", isAddingNodes: false });
14700
+ setAncestryMode({
14701
+ isActive: false,
14702
+ tree: null,
14703
+ selectedParentId: null,
14704
+ isEditMode: false,
14705
+ currentAncestryId: null,
14706
+ ancestryName: "",
14707
+ ancestryDescription: "",
14708
+ isAddingNodes: false
14709
+ });
13861
14710
  if (mountRef.current) mountRef.current.style.cursor = "grab";
13862
14711
  },
13863
14712
  availableNodes: allAvailableNodes,
@@ -13922,7 +14771,12 @@ function XViewScene({
13922
14771
  onOpenImageViewer: handleOpenImageViewer,
13923
14772
  existingTypes: existingNodeTypes,
13924
14773
  onImageChange: (useImage, url, currentColorOverride) => {
13925
- const updatedNode = { ...detailsNode, useImageAsTexture: useImage, textureImageUrl: url, color: currentColorOverride || detailsNode.color };
14774
+ const updatedNode = {
14775
+ ...detailsNode,
14776
+ useImageAsTexture: useImage,
14777
+ textureImageUrl: url,
14778
+ color: currentColorOverride || detailsNode.color
14779
+ };
13926
14780
  updateExistingNodeVisuals(stateRef.current, updatedNode);
13927
14781
  setDetailsNode(updatedNode);
13928
14782
  },
@@ -13996,7 +14850,9 @@ function XViewScene({
13996
14850
  parentData: parentDataRef.current,
13997
14851
  sceneData: sceneDataRef.current,
13998
14852
  ancestryData: ancestryDataRef.current,
13999
- onClose: () => setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev),
14853
+ onClose: () => setContextMenu(
14854
+ (prev) => prev.visible ? { ...prev, visible: false } : prev
14855
+ ),
14000
14856
  onStartCreation: (data) => userActionHandlers.handleStartCreation(actionHandlerContext, data),
14001
14857
  onStartConnection: (data) => userActionHandlers.handleStartConnection(actionHandlerContext, data),
14002
14858
  onStartVersioning: handleStartVersioning,
@@ -14004,7 +14860,11 @@ function XViewScene({
14004
14860
  onDeleteNode: (data) => userActionHandlers.handleDeleteNode(actionHandlerContext, data),
14005
14861
  onDismissNode: (data) => userActionHandlers.handleDismissNode(actionHandlerContext, data),
14006
14862
  onDismissOtherNodes: (data) => userActionHandlers.handleDismissOtherNodes(actionHandlerContext, data),
14007
- onExpandConnections: (sourceNode, links) => userActionHandlers.handleExpandConnections(actionHandlerContext, sourceNode, links),
14863
+ onExpandConnections: (sourceNode, links) => userActionHandlers.handleExpandConnections(
14864
+ actionHandlerContext,
14865
+ sourceNode,
14866
+ links
14867
+ ),
14008
14868
  onRenderAncestry: handleStartReadingAncestry,
14009
14869
  onEditAncestry: handleEditAncestry,
14010
14870
  onDeleteAncestry: (ancestryId) => handleDeleteAncestry(ancestryId),
@@ -14017,9 +14877,18 @@ function XViewScene({
14017
14877
  data: multiContextMenu,
14018
14878
  userRole: userPermissionRole,
14019
14879
  onClose: () => setMultiContextMenu((prev) => ({ ...prev, visible: false })),
14020
- onDismissNodes: (ids) => userActionHandlers.handleDismissMultipleNodes(actionHandlerContext, ids),
14021
- onDismissOtherNodes: (ids) => userActionHandlers.handleDismissOtherMultipleNodes(actionHandlerContext, ids),
14022
- onDeleteNodes: (ids) => userActionHandlers.handleDeleteMultipleNodes(actionHandlerContext, ids)
14880
+ onDismissNodes: (ids) => userActionHandlers.handleDismissMultipleNodes(
14881
+ actionHandlerContext,
14882
+ ids
14883
+ ),
14884
+ onDismissOtherNodes: (ids) => userActionHandlers.handleDismissOtherMultipleNodes(
14885
+ actionHandlerContext,
14886
+ ids
14887
+ ),
14888
+ onDeleteNodes: (ids) => userActionHandlers.handleDeleteMultipleNodes(
14889
+ actionHandlerContext,
14890
+ ids
14891
+ )
14023
14892
  }
14024
14893
  ),
14025
14894
  /* @__PURE__ */ React25.createElement(
@@ -14040,7 +14909,13 @@ function XViewScene({
14040
14909
  onDelete: (data) => userActionHandlers.handleDeleteLink(actionHandlerContext, data)
14041
14910
  }
14042
14911
  ),
14043
- /* @__PURE__ */ React25.createElement(ImageViewer, { data: imageViewer, onClose: () => setImageViewer({ ...imageViewer, visible: false }) }),
14912
+ /* @__PURE__ */ React25.createElement(
14913
+ ImageViewer,
14914
+ {
14915
+ data: imageViewer,
14916
+ onClose: () => setImageViewer({ ...imageViewer, visible: false })
14917
+ }
14918
+ ),
14044
14919
  /* @__PURE__ */ React25.createElement(
14045
14920
  AncestryBoard,
14046
14921
  {