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

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 +484 -178
  2. package/dist/index.mjs +491 -185
  3. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -250,11 +250,12 @@ function ContextMenu({
250
250
  });
251
251
  };
252
252
  const renderMainView = () => {
253
+ var _a2;
253
254
  const hasVersions = computedVersions.length > 0;
254
255
  const canCreateVersion = ability.can("create", "Versioning");
255
256
  const canReadVersion = ability.can("read", "Versioning");
256
257
  const shouldShowVersioningBtn = canCreateVersion || canReadVersion && hasVersions;
257
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_12px_1px_rgba(99,102,241,0.5)]" }), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, ability.can("create", "Connection") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ React.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ React.createElement("span", null, "Conectar")), ability.can("create", "Node") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ React.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ React.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar Ancestralidade" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2-.8 2 .8s1.5-.3 2-.8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ React.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ React.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("line", { x1: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ React.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ React.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ React.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ React.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ React.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ React.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: () => {
258
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_12px_1px_rgba(99,102,241,0.5)]" }), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, ability.can("create", "Connection") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ React.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ React.createElement("span", null, "Conectar")), ability.can("create", "Node") && !((_a2 = data.nodeData) == null ? void 0 : _a2.is_quest) && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ React.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ React.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar Ancestralidade" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2-.8 2 .8s1.5-.3 2-.8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ React.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ React.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("line", { x1: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ React.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ React.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ React.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ React.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ React.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ React.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: () => {
258
259
  onFocusNode == null ? void 0 : onFocusNode(data.nodeData);
259
260
  onClose();
260
261
  }, className: baseButtonClass, title: "Focar na c\xE2mera" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ React.createElement("span", null, "Focar neste Node")), /* @__PURE__ */ React.createElement("button", { onClick: (e) => handleCopyLink(e, data.nodeData), className: baseButtonClass, title: "Copiar Link para Compartilhar" }, isLinkCopied ? /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#4ade80", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "20 6 9 17 4 12" })) : /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ React.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ React.createElement("span", { className: isLinkCopied ? "text-green-400" : "" }, isLinkCopied ? "Copiado!" : "Copiar Link")), ability.can("dismiss", "Node") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("button", { onClick: () => onDismissNode == null ? void 0 : onDismissNode(data.nodeData), className: baseButtonClass, title: "Remover da visualiza\xE7\xE3o" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ React.createElement("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), /* @__PURE__ */ React.createElement("path", { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), /* @__PURE__ */ React.createElement("line", { x1: "2", y1: "2", x2: "22", y2: "22" })), /* @__PURE__ */ React.createElement("span", null, "Dismiss")), /* @__PURE__ */ React.createElement("button", { onClick: () => onDismissOtherNodes == null ? void 0 : onDismissOtherNodes(data.nodeData), className: baseButtonClass, title: "Remover outros da visualiza\xE7\xE3o" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }), /* @__PURE__ */ React.createElement("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ React.createElement("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }), /* @__PURE__ */ React.createElement("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" })), /* @__PURE__ */ React.createElement("span", null, "Dismiss other nodes"))), ability.can("delete", "Node") && /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("deleteConfirmation"), className: deleteButtonClass, title: "Excluir Node" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ React.createElement("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ React.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ React.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" })), /* @__PURE__ */ React.createElement("span", null, "Excluir Node"))));
@@ -1181,8 +1182,6 @@ var createMultipleLinkLines = (linksArray, sourceNodeMesh, targetNodeMesh, resol
1181
1182
  targetNodeMesh,
1182
1183
  resolution,
1183
1184
  isCurved,
1184
- isCurved,
1185
- isCurved,
1186
1185
  curveOffset
1187
1186
  );
1188
1187
  line.userData = {
@@ -1515,7 +1514,7 @@ var addStandaloneNodeToScene = (state, nodeData, position) => {
1515
1514
  scaleTween.start();
1516
1515
  }
1517
1516
  };
1518
- var getParentFileInfoForNode = (allParentData, sceneData, nodeId) => {
1517
+ var getParentFileInfoForNode = (allParentData, sceneData, nodeId, sceneConfigId = null, sceneOwnerId = null) => {
1519
1518
  const parentDbsArray = (sceneData == null ? void 0 : sceneData.parent_dbs) || [];
1520
1519
  for (const parentFileId in allParentData) {
1521
1520
  if (allParentData.hasOwnProperty(parentFileId)) {
@@ -1524,6 +1523,8 @@ var getParentFileInfoForNode = (allParentData, sceneData, nodeId) => {
1524
1523
  const parentDbInfo = parentDbsArray.find((db) => String(db.db_id) === String(parentFileId));
1525
1524
  if (parentDbInfo) {
1526
1525
  return { parentFileId, ownerId: parentDbInfo.owner_id };
1526
+ } else if (sceneConfigId && String(parentFileId) === String(sceneConfigId)) {
1527
+ return { parentFileId, ownerId: sceneOwnerId };
1527
1528
  } else {
1528
1529
  console.warn(`Owner ID n\xE3o encontrado em sceneData.parent_dbs para o parentFileId: ${parentFileId}`);
1529
1530
  return { parentFileId, ownerId: null };
@@ -1793,6 +1794,7 @@ var userActionHandlers = {
1793
1794
  setters.setFormPosition((p) => ({ ...p, opacity: 0 }));
1794
1795
  },
1795
1796
  handleSaveVersionNode: async (context, newNodeData) => {
1797
+ var _a;
1796
1798
  const { graphDataRef, sceneDataRef, stateRef, versionMode, setters } = context;
1797
1799
  if (!graphDataRef.current || !sceneDataRef.current) return;
1798
1800
  const { sourceNodeData } = versionMode;
@@ -1802,7 +1804,7 @@ var userActionHandlers = {
1802
1804
  version_node: { is_version: true, parent_node: sourceNodeData.id }
1803
1805
  };
1804
1806
  const newLink = { id: `link_${short.generate()}`, source: sourceNodeData.id, target: newNode.id };
1805
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id);
1807
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id, context.sceneConfigId, context.ownerId);
1806
1808
  if (!parentInfo || !parentInfo.ownerId) {
1807
1809
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node de origem:", sourceNodeData.id);
1808
1810
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio.");
@@ -1812,9 +1814,21 @@ var userActionHandlers = {
1812
1814
  const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileId]));
1813
1815
  specificParentData.nodes.push(newNode);
1814
1816
  specificParentData.links.push(newLink);
1815
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
1816
1817
  try {
1817
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
1818
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
1819
+ if (isView && parentFileId === context.sceneConfigId) {
1820
+ const viewFilePayload = {
1821
+ parent_dbs: sceneDataRef.current.parent_dbs,
1822
+ nodes: sceneDataRef.current.nodes,
1823
+ links: sceneDataRef.current.links,
1824
+ quest_nodes: specificParentData.nodes,
1825
+ quest_links: specificParentData.links
1826
+ };
1827
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
1828
+ } else {
1829
+ const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
1830
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
1831
+ }
1818
1832
  graphDataRef.current[parentFileId] = specificParentData;
1819
1833
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1820
1834
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
@@ -1838,10 +1852,6 @@ var userActionHandlers = {
1838
1852
  var _a;
1839
1853
  const isSource = String(link.source) === String(sourceNode.id);
1840
1854
  const targetNodeId = isSource ? link.target : link.source;
1841
- const linkAlreadyInSceneData = sceneDataRef.current.links.some((l) => String(l.id) === String(link.id));
1842
- if (!linkAlreadyInSceneData) {
1843
- sceneDataRef.current.links.push(link);
1844
- }
1845
1855
  if (!nodeObjects[String(targetNodeId)]) {
1846
1856
  const allParentNodes = Object.values(graphDataRef.current).flatMap((fileData) => fileData.nodes);
1847
1857
  const nodeData = allParentNodes.find((n) => String(n.id) === String(targetNodeId));
@@ -1849,9 +1859,6 @@ var userActionHandlers = {
1849
1859
  console.warn(`Dados do Node com ID ${targetNodeId} n\xE3o encontrados no cache.`);
1850
1860
  return;
1851
1861
  }
1852
- if (!sceneDataRef.current.nodes.some((n) => String(n.id) === String(nodeData.id))) {
1853
- sceneDataRef.current.nodes.push(nodeData);
1854
- }
1855
1862
  const startPosition = sourceNodeMesh.position.clone();
1856
1863
  const endPosition = startPosition.clone().add(
1857
1864
  new THREE.Vector3((Math.random() - 0.5) * 60, (Math.random() - 0.5) * 15, (Math.random() - 0.5) * 60)
@@ -1971,6 +1978,7 @@ var userActionHandlers = {
1971
1978
  if (mountRef.current) mountRef.current.style.cursor = "grab";
1972
1979
  },
1973
1980
  handleCompleteConnection: async (context, targetNodeData) => {
1981
+ var _a;
1974
1982
  const { stateRef, graphDataRef, sceneDataRef } = context;
1975
1983
  const { sourceNodeData } = stateRef.current.connection;
1976
1984
  if (!graphDataRef.current || !sceneDataRef.current || !sourceNodeData || !targetNodeData) {
@@ -1978,7 +1986,7 @@ var userActionHandlers = {
1978
1986
  userActionHandlers.handleCancelConnection(context);
1979
1987
  return;
1980
1988
  }
1981
- const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id);
1989
+ const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id, context.sceneConfigId, context.ownerId);
1982
1990
  if (!sourceParentInfo || !sourceParentInfo.ownerId) {
1983
1991
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node de origem:", sourceNodeData.id);
1984
1992
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio.");
@@ -1993,12 +2001,26 @@ var userActionHandlers = {
1993
2001
  source: sourceNodeData.id,
1994
2002
  target: targetNodeData.id
1995
2003
  };
1996
- const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
1997
- specificParentData.links.push(newLink);
1998
- const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
1999
2004
  try {
2000
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2001
- graphDataRef.current[parentFileIdToSave] = specificParentData;
2005
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2006
+ if (isView && parentFileIdToSave === context.sceneConfigId) {
2007
+ const specificParentData = graphDataRef.current[context.sceneConfigId];
2008
+ specificParentData.links.push(newLink);
2009
+ const viewFilePayload = {
2010
+ parent_dbs: sceneDataRef.current.parent_dbs,
2011
+ nodes: sceneDataRef.current.nodes,
2012
+ links: sceneDataRef.current.links,
2013
+ quest_nodes: specificParentData.nodes,
2014
+ quest_links: specificParentData.links
2015
+ };
2016
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2017
+ } else {
2018
+ const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
2019
+ specificParentData.links.push(newLink);
2020
+ const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
2021
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2022
+ graphDataRef.current[parentFileIdToSave] = specificParentData;
2023
+ }
2002
2024
  addNewLinkToScene(stateRef.current, newLink);
2003
2025
  } catch (error) {
2004
2026
  console.error("Falha ao salvar a nova conex\xE3o:", error);
@@ -2067,14 +2089,28 @@ var userActionHandlers = {
2067
2089
  } else {
2068
2090
  newTargetId = newEndNodeData.id;
2069
2091
  }
2070
- const originalParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, oldSourceId);
2071
- const newParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, newSourceId);
2072
- if (!originalParentInfo || !newParentInfo || !originalParentInfo.ownerId || !newParentInfo.ownerId) {
2073
- console.error("N\xE3o foi poss\xEDvel encontrar informa\xE7\xF5es dos arquivos pai para o relink.");
2092
+ const oldSourceInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, oldSourceId, context.sceneConfigId, context.ownerId);
2093
+ const oldTargetInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, oldTargetId, context.sceneConfigId, context.ownerId);
2094
+ const newSourceInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, newSourceId, context.sceneConfigId, context.ownerId);
2095
+ const newTargetInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, newTargetId, context.sceneConfigId, context.ownerId);
2096
+ if (!oldSourceInfo || !oldTargetInfo || !newSourceInfo || !newTargetInfo) {
2097
+ console.error("Informa\xE7\xF5es dos arquivos pai incompletas para o relink.");
2074
2098
  alert("Ocorreu um erro ao identificar os arquivos pai para salvar a altera\xE7\xE3o.");
2075
2099
  userActionHandlers.handleCancelRelink(context);
2076
2100
  return;
2077
2101
  }
2102
+ let oldGoverningFileId = oldSourceInfo.parentFileId;
2103
+ let oldGoverningOwnerId = oldSourceInfo.ownerId;
2104
+ if (oldSourceInfo.parentFileId === context.sceneConfigId || oldTargetInfo.parentFileId === context.sceneConfigId) {
2105
+ oldGoverningFileId = context.sceneConfigId;
2106
+ oldGoverningOwnerId = context.ownerId;
2107
+ }
2108
+ let newGoverningFileId = newSourceInfo.parentFileId;
2109
+ let newGoverningOwnerId = newSourceInfo.ownerId;
2110
+ if (newSourceInfo.parentFileId === context.sceneConfigId || newTargetInfo.parentFileId === context.sceneConfigId) {
2111
+ newGoverningFileId = context.sceneConfigId;
2112
+ newGoverningOwnerId = context.ownerId;
2113
+ }
2078
2114
  const { sourceNode, targetNode, ...dataToKeep } = originalLinkData;
2079
2115
  const newLinkData = {
2080
2116
  ...dataToKeep,
@@ -2082,34 +2118,44 @@ var userActionHandlers = {
2082
2118
  target: newTargetId,
2083
2119
  id: linkId
2084
2120
  };
2121
+ const saveParentData = async (fileId, fileOwnerId, data) => {
2122
+ var _a;
2123
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2124
+ if (isView && fileId === context.sceneConfigId) {
2125
+ const viewFilePayload = {
2126
+ parent_dbs: sceneDataRef.current.parent_dbs,
2127
+ nodes: sceneDataRef.current.nodes,
2128
+ links: sceneDataRef.current.links,
2129
+ quest_nodes: data.nodes,
2130
+ quest_links: data.links
2131
+ };
2132
+ return context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2133
+ } else {
2134
+ return context.actions.save_view_data(`x_view_dbs/${fileOwnerId}/${fileId}`, data);
2135
+ }
2136
+ };
2085
2137
  const savePromises = [];
2086
2138
  const filesToUpdate = {};
2087
- if (originalParentInfo.parentFileId !== newParentInfo.parentFileId) {
2088
- const updatedOriginalParentData = JSON.parse(JSON.stringify(graphDataRef.current[originalParentInfo.parentFileId]));
2139
+ if (oldGoverningFileId !== newGoverningFileId) {
2140
+ const updatedOriginalParentData = JSON.parse(JSON.stringify(graphDataRef.current[oldGoverningFileId]));
2089
2141
  updatedOriginalParentData.links = updatedOriginalParentData.links.filter(
2090
2142
  (l) => String(l.id) !== String(linkId)
2091
2143
  );
2092
- filesToUpdate[originalParentInfo.parentFileId] = updatedOriginalParentData;
2093
- savePromises.push(
2094
- context.actions.save_view_data(`x_view_dbs/${originalParentInfo.ownerId}/${originalParentInfo.parentFileId}`, updatedOriginalParentData)
2095
- );
2096
- const updatedNewParentData = JSON.parse(JSON.stringify(graphDataRef.current[newParentInfo.parentFileId]));
2144
+ filesToUpdate[oldGoverningFileId] = updatedOriginalParentData;
2145
+ savePromises.push(saveParentData(oldGoverningFileId, oldGoverningOwnerId, updatedOriginalParentData));
2146
+ const updatedNewParentData = JSON.parse(JSON.stringify(graphDataRef.current[newGoverningFileId]));
2097
2147
  if (!updatedNewParentData.links) updatedNewParentData.links = [];
2098
2148
  updatedNewParentData.links.push(newLinkData);
2099
- filesToUpdate[newParentInfo.parentFileId] = updatedNewParentData;
2100
- savePromises.push(
2101
- context.actions.save_view_data(`x_view_dbs/${newParentInfo.ownerId}/${newParentInfo.parentFileId}`, updatedNewParentData)
2102
- );
2149
+ filesToUpdate[newGoverningFileId] = updatedNewParentData;
2150
+ savePromises.push(saveParentData(newGoverningFileId, newGoverningOwnerId, updatedNewParentData));
2103
2151
  } else {
2104
- const updatedParentData = JSON.parse(JSON.stringify(graphDataRef.current[originalParentInfo.parentFileId]));
2152
+ const updatedParentData = JSON.parse(JSON.stringify(graphDataRef.current[oldGoverningFileId]));
2105
2153
  updatedParentData.links = updatedParentData.links.filter(
2106
2154
  (l) => String(l.id) !== String(linkId)
2107
2155
  );
2108
2156
  updatedParentData.links.push(newLinkData);
2109
- filesToUpdate[originalParentInfo.parentFileId] = updatedParentData;
2110
- savePromises.push(
2111
- context.actions.save_view_data(`x_view_dbs/${originalParentInfo.ownerId}/${originalParentInfo.parentFileId}`, updatedParentData)
2112
- );
2157
+ filesToUpdate[oldGoverningFileId] = updatedParentData;
2158
+ savePromises.push(saveParentData(oldGoverningFileId, oldGoverningOwnerId, updatedParentData));
2113
2159
  }
2114
2160
  try {
2115
2161
  await Promise.all(savePromises);
@@ -2127,7 +2173,7 @@ var userActionHandlers = {
2127
2173
  }
2128
2174
  },
2129
2175
  handleDeleteLink: async (context, linkObject) => {
2130
- var _a, _b, _c, _d, _e, _f;
2176
+ var _a, _b, _c, _d, _e, _f, _g;
2131
2177
  const { stateRef, graphDataRef, sceneDataRef, setters } = context;
2132
2178
  setters.setRelationshipMenu({ visible: false });
2133
2179
  if (!(linkObject == null ? void 0 : linkObject.userData) || !graphDataRef.current || !sceneDataRef.current) return;
@@ -2136,7 +2182,7 @@ var userActionHandlers = {
2136
2182
  console.error("Tentativa de deletar um link sem ID.", linkObject.userData);
2137
2183
  return;
2138
2184
  }
2139
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkObject.userData.source);
2185
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkObject.userData.source, context.sceneConfigId, context.ownerId);
2140
2186
  if (!parentInfo || !parentInfo.ownerId) {
2141
2187
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o link:", linkIdToDelete);
2142
2188
  alert("Ocorreu um erro ao identificar o arquivo pai da rela\xE7\xE3o para exclus\xE3o.");
@@ -2148,9 +2194,25 @@ var userActionHandlers = {
2148
2194
  const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileId]));
2149
2195
  const newLinks = (specificParentData.links || []).filter((l) => String(l.id) !== String(linkIdToDelete));
2150
2196
  specificParentData.links = newLinks;
2151
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2197
+ let filenameToSave;
2198
+ let payloadToSave;
2199
+ const isView = ((_g = context.viewType) == null ? void 0 : _g.toLowerCase()) === "view";
2200
+ if (isView && parentFileId === context.sceneConfigId) {
2201
+ filenameToSave = context.sceneSaveUrl;
2202
+ sceneDataRef.current.links = sceneDataRef.current.links.filter((l) => String(l.id) !== String(linkIdToDelete));
2203
+ payloadToSave = {
2204
+ parent_dbs: sceneDataRef.current.parent_dbs,
2205
+ nodes: sceneDataRef.current.nodes,
2206
+ links: sceneDataRef.current.links,
2207
+ quest_nodes: specificParentData.nodes,
2208
+ quest_links: specificParentData.links
2209
+ };
2210
+ } else {
2211
+ filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2212
+ payloadToSave = specificParentData;
2213
+ }
2152
2214
  try {
2153
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2215
+ await context.actions.save_view_data(filenameToSave, payloadToSave);
2154
2216
  graphDataRef.current[parentFileId] = specificParentData;
2155
2217
  setters.setDetailsLink((prev) => String(prev == null ? void 0 : prev.id) === String(linkIdToDelete) ? null : prev);
2156
2218
  if (stateRef.current.hoveredLink === linkObject) {
@@ -2168,12 +2230,6 @@ var userActionHandlers = {
2168
2230
  if (!nodeData || !sceneDataRef.current) return;
2169
2231
  const nodeIdToDismiss = nodeData.id;
2170
2232
  const strNodeId = String(nodeIdToDismiss);
2171
- sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter(
2172
- (n) => String(n.id) !== strNodeId
2173
- );
2174
- sceneDataRef.current.links = sceneDataRef.current.links.filter(
2175
- (l) => String(l.source) !== strNodeId && String(l.target) !== strNodeId
2176
- );
2177
2233
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2178
2234
  if (ancestryGroup && ancestryLinks) {
2179
2235
  const remainingAncestryLinks = [];
@@ -2215,22 +2271,12 @@ var userActionHandlers = {
2215
2271
  removeNodeFromScene(stateRef.current, nodeId);
2216
2272
  setters.setDetailsNode((prev) => String(prev == null ? void 0 : prev.id) === String(nodeId) ? null : prev);
2217
2273
  });
2218
- sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter(
2219
- (n) => String(n.id) === strNodeIdToKeep
2220
- );
2221
- sceneDataRef.current.links = [];
2222
2274
  },
2223
2275
  handleDismissMultipleNodes: (context, nodeIds) => {
2224
2276
  const { stateRef, sceneDataRef, setters } = context;
2225
2277
  setters.setMultiContextMenu({ visible: false });
2226
2278
  if (!nodeIds || nodeIds.size === 0 || !sceneDataRef.current) return;
2227
2279
  const strNodeIds = Array.from(nodeIds).map(String);
2228
- sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter(
2229
- (n) => !strNodeIds.includes(String(n.id))
2230
- );
2231
- sceneDataRef.current.links = sceneDataRef.current.links.filter(
2232
- (l) => !strNodeIds.includes(String(l.source)) && !strNodeIds.includes(String(l.target))
2233
- );
2234
2280
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2235
2281
  if (ancestryGroup && ancestryLinks) {
2236
2282
  const remainingAncestryLinks = [];
@@ -2286,15 +2332,10 @@ var userActionHandlers = {
2286
2332
  removeNodeFromScene(stateRef.current, nodeId);
2287
2333
  setters.setDetailsNode((prev) => String(prev == null ? void 0 : prev.id) === String(nodeId) ? null : prev);
2288
2334
  });
2289
- sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter(
2290
- (n) => strNodeIdsToKeep.includes(String(n.id))
2291
- );
2292
- sceneDataRef.current.links = sceneDataRef.current.links.filter(
2293
- (l) => strNodeIdsToKeep.includes(String(l.source)) && strNodeIdsToKeep.includes(String(l.target))
2294
- );
2295
2335
  stateRef.current.selectedNodes.clear();
2296
2336
  },
2297
2337
  handleDeleteMultipleNodes: async (context, nodeIds) => {
2338
+ var _a;
2298
2339
  const { stateRef, graphDataRef, sceneDataRef, setters, actions } = context;
2299
2340
  setters.setMultiContextMenu({ visible: false });
2300
2341
  if (!nodeIds || nodeIds.size === 0 || !graphDataRef.current || !sceneDataRef.current) return;
@@ -2313,7 +2354,7 @@ var userActionHandlers = {
2313
2354
  }
2314
2355
  const changesByParentFile = {};
2315
2356
  for (const nodeId of strNodeIdsToDelete) {
2316
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeId);
2357
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeId, context.sceneConfigId, context.ownerId);
2317
2358
  if (!parentInfo || !parentInfo.ownerId) {
2318
2359
  console.warn(`Node com ID ${nodeId} n\xE3o encontrado ou sem ownerId. Ignorando.`);
2319
2360
  continue;
@@ -2347,8 +2388,27 @@ var userActionHandlers = {
2347
2388
  originalData.links = (originalData.links || []).filter(
2348
2389
  (l) => !linksToDelete.has(String(l.id))
2349
2390
  );
2350
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2351
- savePromises.push(context.actions.save_view_data(filenameForSpecificParent, originalData));
2391
+ let filenameToSave;
2392
+ let payloadToSave;
2393
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2394
+ if (isView && parentFileId === context.sceneConfigId) {
2395
+ filenameToSave = context.sceneSaveUrl;
2396
+ const strNodesToDelete = Array.from(nodesToDelete).map(String);
2397
+ const strLinksToDelete = Array.from(linksToDelete).map(String);
2398
+ sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter((n) => !strNodesToDelete.includes(String(n.id)));
2399
+ sceneDataRef.current.links = sceneDataRef.current.links.filter((l) => !strLinksToDelete.includes(String(l.id)));
2400
+ payloadToSave = {
2401
+ parent_dbs: sceneDataRef.current.parent_dbs,
2402
+ nodes: sceneDataRef.current.nodes,
2403
+ links: sceneDataRef.current.links,
2404
+ quest_nodes: originalData.nodes,
2405
+ quest_links: originalData.links
2406
+ };
2407
+ } else {
2408
+ filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2409
+ payloadToSave = originalData;
2410
+ }
2411
+ savePromises.push(context.actions.save_view_data(filenameToSave, payloadToSave));
2352
2412
  updatedParentDataCache[parentFileId] = originalData;
2353
2413
  }
2354
2414
  }
@@ -2369,6 +2429,7 @@ var userActionHandlers = {
2369
2429
  }
2370
2430
  },
2371
2431
  handleDeleteNode: async (context, nodeData) => {
2432
+ var _a;
2372
2433
  const { stateRef, graphDataRef, sceneDataRef, setters, actions } = context;
2373
2434
  if (actions.delete_file && nodeData) {
2374
2435
  const urls = extractFileUrlsFromProperties(nodeData);
@@ -2380,7 +2441,7 @@ var userActionHandlers = {
2380
2441
  if (!nodeData || !graphDataRef.current || !sceneDataRef.current) return;
2381
2442
  const nodeIdToDelete = nodeData.id;
2382
2443
  const strNodeId = String(nodeIdToDelete);
2383
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeIdToDelete);
2444
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeIdToDelete, context.sceneConfigId, context.ownerId);
2384
2445
  if (!parentInfo || !parentInfo.ownerId) {
2385
2446
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node a ser exclu\xEDdo:", nodeIdToDelete);
2386
2447
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio para exclus\xE3o.");
@@ -2392,9 +2453,28 @@ var userActionHandlers = {
2392
2453
  const newLinks = (specificParentData.links || []).filter((l) => String(l.source) !== strNodeId && String(l.target) !== strNodeId);
2393
2454
  specificParentData.nodes = newNodes;
2394
2455
  specificParentData.links = newLinks;
2395
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2456
+ let filenameToSave;
2457
+ let payloadToSave;
2458
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2459
+ if (isView && parentFileId === context.sceneConfigId) {
2460
+ filenameToSave = context.sceneSaveUrl;
2461
+ const newVisualNodes = sceneDataRef.current.nodes.filter((n) => String(n.id) !== strNodeId);
2462
+ const newVisualLinks = sceneDataRef.current.links.filter((l) => String(l.source) !== strNodeId && String(l.target) !== strNodeId);
2463
+ sceneDataRef.current.nodes = newVisualNodes;
2464
+ sceneDataRef.current.links = newVisualLinks;
2465
+ payloadToSave = {
2466
+ parent_dbs: sceneDataRef.current.parent_dbs,
2467
+ nodes: newVisualNodes,
2468
+ links: newVisualLinks,
2469
+ quest_nodes: specificParentData.nodes,
2470
+ quest_links: specificParentData.links
2471
+ };
2472
+ } else {
2473
+ filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2474
+ payloadToSave = specificParentData;
2475
+ }
2396
2476
  try {
2397
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2477
+ await context.actions.save_view_data(filenameToSave, payloadToSave);
2398
2478
  graphDataRef.current[parentFileId] = specificParentData;
2399
2479
  setters.setDetailsNode((prev) => String(prev == null ? void 0 : prev.id) === String(nodeIdToDelete) ? null : prev);
2400
2480
  removeNodeFromScene(stateRef.current, nodeIdToDelete);
@@ -2405,10 +2485,11 @@ var userActionHandlers = {
2405
2485
  }
2406
2486
  },
2407
2487
  handleSaveNodeDetails: async (context, updatedNode, keepOpen = false) => {
2488
+ var _a;
2408
2489
  const { graphDataRef, sceneDataRef, stateRef, setters } = context;
2409
2490
  if (!graphDataRef.current || !sceneDataRef.current) return;
2410
2491
  const { _baseEmissiveIntensity: ignored, ...nodeToSave } = updatedNode;
2411
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeToSave.id);
2492
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeToSave.id, context.sceneConfigId, context.ownerId);
2412
2493
  if (!parentInfo || !parentInfo.ownerId) {
2413
2494
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node a ser atualizado:", nodeToSave.id);
2414
2495
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio para atualiza\xE7\xE3o.");
@@ -2424,9 +2505,21 @@ var userActionHandlers = {
2424
2505
  alert("Erro interno: Node n\xE3o encontrado para salvar.");
2425
2506
  return;
2426
2507
  }
2427
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2428
2508
  try {
2429
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2509
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2510
+ if (isView && parentFileId === context.sceneConfigId) {
2511
+ const viewFilePayload = {
2512
+ parent_dbs: sceneDataRef.current.parent_dbs,
2513
+ nodes: sceneDataRef.current.nodes,
2514
+ links: sceneDataRef.current.links,
2515
+ quest_nodes: specificParentData.nodes,
2516
+ quest_links: specificParentData.links
2517
+ };
2518
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2519
+ } else {
2520
+ const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2521
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2522
+ }
2430
2523
  graphDataRef.current[parentFileId] = specificParentData;
2431
2524
  updateExistingNodeVisuals(stateRef.current, nodeToSave);
2432
2525
  setters.setSceneVersion((v) => v + 1);
@@ -2439,10 +2532,11 @@ var userActionHandlers = {
2439
2532
  }
2440
2533
  },
2441
2534
  handleSaveLinkDetails: async (context, updatedLink, keepOpen = false) => {
2535
+ var _a;
2442
2536
  const { graphDataRef, sceneDataRef, stateRef, setters } = context;
2443
2537
  if (!graphDataRef.current || !sceneDataRef.current) return;
2444
2538
  const { sourceNode, targetNode, ...linkToSave } = updatedLink;
2445
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkToSave.source);
2539
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkToSave.source, context.sceneConfigId, context.ownerId);
2446
2540
  if (!parentInfo || !parentInfo.ownerId) {
2447
2541
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o link a ser atualizado:", linkToSave.id);
2448
2542
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio para atualiza\xE7\xE3o.");
@@ -2458,9 +2552,21 @@ var userActionHandlers = {
2458
2552
  alert("Erro interno: link n\xE3o encontrado para salvar.");
2459
2553
  return;
2460
2554
  }
2461
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2462
2555
  try {
2463
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2556
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2557
+ if (isView && parentFileId === context.sceneConfigId) {
2558
+ const viewFilePayload = {
2559
+ parent_dbs: sceneDataRef.current.parent_dbs,
2560
+ nodes: sceneDataRef.current.nodes,
2561
+ links: sceneDataRef.current.links,
2562
+ quest_nodes: specificParentData.nodes,
2563
+ quest_links: specificParentData.links
2564
+ };
2565
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2566
+ } else {
2567
+ const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2568
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2569
+ }
2464
2570
  graphDataRef.current[parentFileId] = specificParentData;
2465
2571
  const lineObject = stateRef.current.allLinks.find(
2466
2572
  (l) => String(l.userData.id) === String(linkToSave.id)
@@ -2483,7 +2589,7 @@ var userActionHandlers = {
2483
2589
  }
2484
2590
  },
2485
2591
  handleAddExistingNodeById: (context, nodeId) => {
2486
- var _a, _b;
2592
+ var _a;
2487
2593
  const { stateRef, sceneDataRef, graphDataRef, tweenToTarget, setters } = context;
2488
2594
  const state = stateRef.current;
2489
2595
  const graphFull = graphDataRef.current;
@@ -2499,16 +2605,12 @@ var userActionHandlers = {
2499
2605
  tweenToTarget(state.nodeObjects[strNodeId]);
2500
2606
  return;
2501
2607
  }
2502
- const alreadyInSceneData = (((_a = sceneDataRef.current) == null ? void 0 : _a.nodes) || []).some((n) => String(n.id) === String(strNodeId));
2503
- if (!alreadyInSceneData) {
2504
- sceneDataRef.current.nodes.push(nodeData);
2505
- }
2506
2608
  const base = state.controls ? state.controls.target.clone() : new THREE.Vector3(0, 0, 0);
2507
2609
  const offset = new THREE.Vector3((Math.random() - 0.5) * 20, (Math.random() - 0.5) * 6, (Math.random() - 0.5) * 20);
2508
2610
  const position = base.add(offset);
2509
2611
  addStandaloneNodeToScene(state, nodeData, position);
2510
2612
  tweenToTarget(position, 1.3);
2511
- (_b = setters == null ? void 0 : setters.setSceneVersion) == null ? void 0 : _b.call(setters, (v) => v + 1);
2613
+ (_a = setters == null ? void 0 : setters.setSceneVersion) == null ? void 0 : _a.call(setters, (v) => v + 1);
2512
2614
  }
2513
2615
  };
2514
2616
 
@@ -3097,7 +3199,7 @@ function useResizablePanel({ initialWidth, minWidth, maxWidth }) {
3097
3199
 
3098
3200
  // src/components/CustomPropertyDisplay.jsx
3099
3201
  import React3, { useState as useState4, useRef as useRef3, useEffect as useEffect3 } from "react";
3100
- import { FiCheck, FiX as FiX2, FiEdit3, FiTrash2, FiExternalLink, FiFileText, FiChevronDown, FiUpload, FiLoader } from "react-icons/fi";
3202
+ import { FiCheck, FiX, FiEdit3, FiTrash2, FiExternalLink, FiFileText, FiChevronDown, FiUpload, FiLoader } from "react-icons/fi";
3101
3203
  function CustomPropertyDisplay({
3102
3204
  prop,
3103
3205
  onUpdate,
@@ -3388,7 +3490,7 @@ function CustomPropertyDisplay({
3388
3490
  default:
3389
3491
  return /* @__PURE__ */ React3.createElement("input", { type: "text", placeholder: "Valor", value: tempProp.value, onChange: (e) => handlePropChange("value", e.target.value), className: baseInput });
3390
3492
  }
3391
- })()), /* @__PURE__ */ React3.createElement("div", { className: "flex justify-end items-center gap-2 pt-1" }, /* @__PURE__ */ React3.createElement("button", { onClick: handleCancel, type: "button", className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors" }, /* @__PURE__ */ React3.createElement(FiX2, { className: "text-red-400" })), /* @__PURE__ */ React3.createElement("button", { onClick: handleSave, type: "button", className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors" }, /* @__PURE__ */ React3.createElement(FiCheck, { className: "text-green-400" }))));
3493
+ })()), /* @__PURE__ */ React3.createElement("div", { className: "flex justify-end items-center gap-2 pt-1" }, /* @__PURE__ */ React3.createElement("button", { onClick: handleCancel, type: "button", className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors" }, /* @__PURE__ */ React3.createElement(FiX, { className: "text-red-400" })), /* @__PURE__ */ React3.createElement("button", { onClick: handleSave, type: "button", className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors" }, /* @__PURE__ */ React3.createElement(FiCheck, { className: "text-green-400" }))));
3392
3494
  };
3393
3495
  const renderDisplayView = () => /* @__PURE__ */ React3.createElement("div", { className: "w-full space-y-2 text-sm" }, /* @__PURE__ */ React3.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React3.createElement("p", { className: "font-semibold text-slate-200" }, prop.key), /* @__PURE__ */ React3.createElement("span", { className: "text-xs capitalize bg-slate-700/50 px-2 py-0.5 rounded text-slate-400" }, prop.type)), /* @__PURE__ */ React3.createElement("div", { className: "text-slate-300 pl-1" }, (() => {
3394
3496
  switch (prop.type) {
@@ -7024,7 +7126,7 @@ function CreateAncestryPanel({
7024
7126
 
7025
7127
  // src/components/ImageViewer.jsx
7026
7128
  import React11, { useState as useState12, useEffect as useEffect11, useLayoutEffect as useLayoutEffect2, useCallback as useCallback3 } from "react";
7027
- import { FiX as FiX3, FiChevronLeft as FiChevronLeft3, FiChevronRight as FiChevronRight5 } from "react-icons/fi";
7129
+ import { FiX as FiX2, FiChevronLeft as FiChevronLeft3, FiChevronRight as FiChevronRight5 } from "react-icons/fi";
7028
7130
  function ImageViewer({ data, onClose }) {
7029
7131
  var _a;
7030
7132
  const { images = [], startIndex = 0, visible } = data;
@@ -7109,7 +7211,7 @@ function ImageViewer({ data, onClose }) {
7109
7211
  className: "absolute top-4 right-4 z-10 w-10 h-10 flex items-center justify-center bg-white/10 hover:bg-white/20 rounded-full text-white text-2xl transition-colors",
7110
7212
  "aria-label": "Fechar"
7111
7213
  },
7112
- /* @__PURE__ */ React11.createElement(FiX3, null)
7214
+ /* @__PURE__ */ React11.createElement(FiX2, null)
7113
7215
  ),
7114
7216
  /* @__PURE__ */ React11.createElement("div", { className: "relative max-w-full max-h-full flex items-center justify-center" }, isLoading && /* @__PURE__ */ React11.createElement("div", { className: "absolute inset-0 flex items-center justify-center" }, /* @__PURE__ */ React11.createElement("div", { className: "h-10 w-10 border-2 border-white/40 border-t-white rounded-full animate-spin" })), loadedSrc && /* @__PURE__ */ React11.createElement(
7115
7217
  "img",
@@ -7245,7 +7347,7 @@ function ColorPicker({ color, onChange, disabled }) {
7245
7347
  }
7246
7348
 
7247
7349
  // src/components/InSceneCreationForm.jsx
7248
- import { FiPlus as FiPlus3, FiMaximize2, FiX as FiX4, FiCheck as FiCheck7, FiEdit2 as FiEdit24, FiSun, FiChevronDown as FiChevronDown4 } from "react-icons/fi";
7350
+ import { FiPlus as FiPlus3, FiMaximize2, FiX as FiX3, FiCheck as FiCheck7, FiEdit2 as FiEdit24, FiSun, FiChevronDown as FiChevronDown4 } from "react-icons/fi";
7249
7351
  function InSceneCreationForm({
7250
7352
  onSave,
7251
7353
  onCancel,
@@ -7463,7 +7565,7 @@ function InSceneCreationForm({
7463
7565
  onClick: () => handleRemoveType(index),
7464
7566
  className: "hover:text-white transition-colors"
7465
7567
  },
7466
- /* @__PURE__ */ React13.createElement(FiX4, { size: 12 })
7568
+ /* @__PURE__ */ React13.createElement(FiX3, { size: 12 })
7467
7569
  ))), /* @__PURE__ */ React13.createElement(
7468
7570
  "input",
7469
7571
  {
@@ -7837,16 +7939,12 @@ function InSceneVersionForm({
7837
7939
 
7838
7940
  // src/components/InSceneQuestForm.jsx
7839
7941
  import React15, { useState as useState16, useRef as useRef12 } from "react";
7840
- import { FiPlus as FiPlus5, FiCheck as FiCheck9, FiEdit2 as FiEdit26, FiTarget } from "react-icons/fi";
7942
+ import { FiPlus as FiPlus5, FiCheck as FiCheck9, FiEdit2 as FiEdit26, FiTarget, FiX as FiX4, FiChevronDown as FiChevronDown5 } from "react-icons/fi";
7841
7943
  var QUEST_STATUS_COLORS = {
7842
7944
  "Backlog": "#64748b",
7843
- // Slate (Cinza azulado)
7844
7945
  "In Progress": "#eab308",
7845
- // Yellow (Amarelo)
7846
7946
  "Review": "#a855f7",
7847
- // Purple (Roxo)
7848
7947
  "Done": "#22c55e"
7849
- // Green (Verde)
7850
7948
  };
7851
7949
  function InSceneQuestForm({
7852
7950
  onSave,
@@ -7857,7 +7955,11 @@ function InSceneQuestForm({
7857
7955
  availableNodes = [],
7858
7956
  availableAncestries = [],
7859
7957
  onMentionClick,
7860
- onUploadFile
7958
+ onUploadFile,
7959
+ // NOVAS PROPS PARA O GHOST NODE
7960
+ onNameChange,
7961
+ onColorChange,
7962
+ onSizeChange
7861
7963
  }) {
7862
7964
  const [name, setName] = useState16("");
7863
7965
  const [types, setTypes] = useState16(["quest"]);
@@ -7866,6 +7968,7 @@ function InSceneQuestForm({
7866
7968
  const [size, setSize] = useState16("medium");
7867
7969
  const [intensity, setIntensity] = useState16(0);
7868
7970
  const [description, setDescription] = useState16("");
7971
+ const [isStatusDropdownOpen, setIsStatusDropdownOpen] = useState16(false);
7869
7972
  const [customProps, setCustomProps] = useState16([]);
7870
7973
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState16(false);
7871
7974
  const propsEndRef = useRef12(null);
@@ -7884,7 +7987,7 @@ function InSceneQuestForm({
7884
7987
  setCustomProps(newProps);
7885
7988
  };
7886
7989
  const handleAddType = (newType) => {
7887
- const trimmed = newType.trim().toLowerCase();
7990
+ const trimmed = newType.trim();
7888
7991
  if (trimmed && !types.includes(trimmed)) {
7889
7992
  setTypes([...types, trimmed]);
7890
7993
  setTypeInput("");
@@ -7916,7 +8019,6 @@ function InSceneQuestForm({
7916
8019
  name: name.trim(),
7917
8020
  type: types,
7918
8021
  color: QUEST_STATUS_COLORS[status],
7919
- // Cor atrelada ao status
7920
8022
  status,
7921
8023
  size,
7922
8024
  intensity,
@@ -7943,20 +8045,52 @@ function InSceneQuestForm({
7943
8045
  onDoubleClick: swallow
7944
8046
  },
7945
8047
  /* @__PURE__ */ React15.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS[status]}, transparent)` } }),
7946
- /* @__PURE__ */ React15.createElement("div", { className: "px-6 pt-5 pb-3" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React15.createElement(FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ React15.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Nova Tarefa / Objetivo")), /* @__PURE__ */ React15.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Criar Quest")),
7947
- /* @__PURE__ */ React15.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col max-h-[68vh]" }, /* @__PURE__ */ React15.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ React15.createElement(
7948
- "select",
7949
- {
7950
- value: status,
7951
- onChange: (e) => setStatus(e.target.value),
7952
- className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60 appearance-none cursor-pointer",
7953
- style: { borderLeft: `4px solid ${QUEST_STATUS_COLORS[status]}` }
8048
+ /* @__PURE__ */ React15.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React15.createElement(FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ React15.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Nova quest")), /* @__PURE__ */ React15.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Criar Quest")), /* @__PURE__ */ React15.createElement(
8049
+ "button",
8050
+ {
8051
+ type: "button",
8052
+ onClick: onCancel,
8053
+ className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl",
8054
+ title: "Fechar"
8055
+ },
8056
+ "\xD7"
8057
+ )),
8058
+ /* @__PURE__ */ React15.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col max-h-[68vh]" }, /* @__PURE__ */ React15.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
8059
+ "button",
8060
+ {
8061
+ type: "button",
8062
+ onClick: () => setIsStatusDropdownOpen(!isStatusDropdownOpen),
8063
+ className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 hover:border-white/20 focus:outline-none focus:ring-2 focus:ring-indigo-400/60 transition-colors flex items-center justify-between"
8064
+ },
8065
+ /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement("span", { className: "w-3 h-3 rounded-full shadow-[0_0_8px_rgba(0,0,0,0.5)]", style: { backgroundColor: QUEST_STATUS_COLORS[status] } }), /* @__PURE__ */ React15.createElement("span", { className: "text-slate-200 font-medium" }, status)),
8066
+ /* @__PURE__ */ React15.createElement(FiChevronDown5, { className: `text-slate-400 transition-transform duration-200 ${isStatusDropdownOpen ? "rotate-180" : ""}` })
8067
+ ), isStatusDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsStatusDropdownOpen(false) }), /* @__PURE__ */ React15.createElement("ul", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-800 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden" }, Object.keys(QUEST_STATUS_COLORS).map((s) => /* @__PURE__ */ React15.createElement(
8068
+ "li",
8069
+ {
8070
+ key: s,
8071
+ onClick: () => {
8072
+ setStatus(s);
8073
+ setIsStatusDropdownOpen(false);
8074
+ onColorChange == null ? void 0 : onColorChange(QUEST_STATUS_COLORS[s]);
8075
+ },
8076
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${status === s ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
7954
8077
  },
7955
- /* @__PURE__ */ React15.createElement("option", { value: "Backlog" }, "Backlog"),
7956
- /* @__PURE__ */ React15.createElement("option", { value: "In Progress" }, "In Progress"),
7957
- /* @__PURE__ */ React15.createElement("option", { value: "Review" }, "Review"),
7958
- /* @__PURE__ */ React15.createElement("option", { value: "Done" }, "Done")
7959
- )), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Quest"), /* @__PURE__ */ React15.createElement("input", { required: true, type: "text", placeholder: "Ex.: Refatorar M\xF3dulo X", value: name, onChange: (e) => setName(e.target.value), className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60" })), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ React15.createElement("div", { className: "relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 focus-within:ring-2 focus-within:ring-indigo-400/60 transition-all" }, types.map((t, index) => /* @__PURE__ */ React15.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, t !== "quest" && /* @__PURE__ */ React15.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ React15.createElement(FiX, { size: 12 })))), /* @__PURE__ */ React15.createElement(
8078
+ /* @__PURE__ */ React15.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS[s] } }),
8079
+ s
8080
+ )))))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Quest"), /* @__PURE__ */ React15.createElement(
8081
+ "input",
8082
+ {
8083
+ required: true,
8084
+ type: "text",
8085
+ placeholder: "Ex.: Refatorar M\xF3dulo X",
8086
+ value: name,
8087
+ onChange: (e) => {
8088
+ setName(e.target.value);
8089
+ onNameChange == null ? void 0 : onNameChange(e.target.value);
8090
+ },
8091
+ className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60"
8092
+ }
8093
+ )), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ React15.createElement("div", { className: "relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 focus-within:ring-2 focus-within:ring-indigo-400/60 transition-all" }, types.map((t, index) => /* @__PURE__ */ React15.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, t !== "quest" && /* @__PURE__ */ React15.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ React15.createElement(FiX4, { size: 12 })))), /* @__PURE__ */ React15.createElement(
7960
8094
  "input",
7961
8095
  {
7962
8096
  type: "text",
@@ -7967,7 +8101,6 @@ function InSceneQuestForm({
7967
8101
  if (typeInput.trim()) handleAddType(typeInput);
7968
8102
  },
7969
8103
  className: "flex-1 bg-transparent text-sm min-w-[80px] focus:outline-none text-slate-200",
7970
- placeholder: "Ex.: Bugfix",
7971
8104
  autoComplete: "off"
7972
8105
  }
7973
8106
  ))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o (Opcional)"), /* @__PURE__ */ React15.createElement("div", { className: "relative group min-h-[80px] bg-slate-800/70 p-2.5 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React15.createElement(
@@ -7980,7 +8113,20 @@ function InSceneQuestForm({
7980
8113
  onMentionClick,
7981
8114
  onSaveDescription: (newDesc) => setDescription(newDesc)
7982
8115
  }
7983
- ), /* @__PURE__ */ React15.createElement("div", { className: "absolute top-0 right-0 flex bg-slate-900/50 rounded-bl-lg backdrop-blur-sm opacity-0 group-hover:opacity-100 transition-opacity overflow-hidden border-b border-l border-white/5" }, /* @__PURE__ */ React15.createElement("button", { type: "button", onClick: () => setIsDescriptionModalOpen(true), className: "p-2 text-slate-400 hover:text-white hover:bg-white/10 transition-colors" }, /* @__PURE__ */ React15.createElement(FiEdit26, { size: 14 }))), !description && /* @__PURE__ */ React15.createElement("div", { onClick: () => setIsDescriptionModalOpen(true), className: "absolute inset-0 flex items-center justify-center text-xs text-slate-500 cursor-text" }, "Adicionar descri\xE7\xE3o..."))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Tamanho no Cen\xE1rio (Size)"), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-5" }, ["small", "medium", "large"].map((s) => /* @__PURE__ */ React15.createElement("button", { key: s, type: "button", onClick: () => setSize(s), className: "flex items-center gap-2 group cursor-pointer focus:outline-none" }, /* @__PURE__ */ React15.createElement("div", { className: `w-4 h-4 rounded-[4px] border flex items-center justify-center transition-all duration-200 ${size === s ? "bg-indigo-500 border-indigo-500" : "border-slate-600 bg-transparent group-hover:border-slate-500"}` }, size === s && /* @__PURE__ */ React15.createElement(FiCheck9, { size: 12, className: "text-white" })), /* @__PURE__ */ React15.createElement("span", { className: `text-sm capitalize transition-colors ${size === s ? "text-white font-medium" : "text-slate-400 group-hover:text-slate-300"}` }, s))))), /* @__PURE__ */ React15.createElement("div", { className: "pt-2" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between mb-2" }, /* @__PURE__ */ React15.createElement("h3", { className: "text-sm font-medium" }, "Propriedades Adicionais"), /* @__PURE__ */ React15.createElement("button", { type: "button", onClick: handleAddProp, className: "flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-md bg-slate-800/70 hover:bg-slate-700/70 border border-white/10 transition-colors" }, /* @__PURE__ */ React15.createElement(FiPlus5, { size: 14 }), " Adicionar")), /* @__PURE__ */ React15.createElement("div", { className: "flex flex-col gap-3" }, customProps.map((prop, index) => /* @__PURE__ */ React15.createElement(
8116
+ ), /* @__PURE__ */ React15.createElement("div", { className: "absolute top-0 right-0 flex bg-slate-900/50 rounded-bl-lg backdrop-blur-sm opacity-0 group-hover:opacity-100 transition-opacity overflow-hidden border-b border-l border-white/5" }, /* @__PURE__ */ React15.createElement("button", { type: "button", onClick: () => setIsDescriptionModalOpen(true), className: "p-2 text-slate-400 hover:text-white hover:bg-white/10 transition-colors" }, /* @__PURE__ */ React15.createElement(FiEdit26, { size: 14 }))), !description && /* @__PURE__ */ React15.createElement("div", { onClick: () => setIsDescriptionModalOpen(true), className: "absolute inset-0 flex items-center justify-center text-xs text-slate-500 cursor-text" }, "Adicionar descri\xE7\xE3o..."))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Tamanho no Node (Size)"), /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-5" }, ["small", "medium", "large"].map((s) => /* @__PURE__ */ React15.createElement(
8117
+ "button",
8118
+ {
8119
+ key: s,
8120
+ type: "button",
8121
+ onClick: () => {
8122
+ setSize(s);
8123
+ onSizeChange == null ? void 0 : onSizeChange(s);
8124
+ },
8125
+ className: "flex items-center gap-2 group cursor-pointer focus:outline-none"
8126
+ },
8127
+ /* @__PURE__ */ React15.createElement("div", { className: `w-4 h-4 rounded-[4px] border flex items-center justify-center transition-all duration-200 ${size === s ? "bg-indigo-500 border-indigo-500" : "border-slate-600 bg-transparent group-hover:border-slate-500"}` }, size === s && /* @__PURE__ */ React15.createElement(FiCheck9, { size: 12, className: "text-white" })),
8128
+ /* @__PURE__ */ React15.createElement("span", { className: `text-sm capitalize transition-colors ${size === s ? "text-white font-medium" : "text-slate-400 group-hover:text-slate-300"}` }, s)
8129
+ )))), /* @__PURE__ */ React15.createElement("div", { className: "pt-2" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center justify-between mb-2" }, /* @__PURE__ */ React15.createElement("h3", { className: "text-sm font-medium" }, "Propriedades Adicionais"), /* @__PURE__ */ React15.createElement("button", { type: "button", onClick: handleAddProp, className: "flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-md bg-slate-800/70 hover:bg-slate-700/70 border border-white/10 transition-colors" }, /* @__PURE__ */ React15.createElement(FiPlus5, { size: 14 }), " Adicionar")), /* @__PURE__ */ React15.createElement("div", { className: "flex flex-col gap-3" }, customProps.map((prop, index) => /* @__PURE__ */ React15.createElement(
7984
8130
  CustomPropertyDisplay,
7985
8131
  {
7986
8132
  key: prop.id,
@@ -10173,7 +10319,7 @@ function XViewScene({
10173
10319
  });
10174
10320
  }
10175
10321
  return {
10176
- node: effectiveNode || { id: nodeId, name: "Unknown" },
10322
+ ...effectiveNode ? { node: effectiveNode } : { node: { id: nodeId, name: "Unknown" } },
10177
10323
  relationship: treeItem.relationship || {},
10178
10324
  children: (treeItem.children || []).map(recursiveBuild).filter(Boolean),
10179
10325
  parallel_branches: processedBranches
@@ -10872,20 +11018,6 @@ function XViewScene({
10872
11018
  const context = actionHandlerContext;
10873
11019
  if (connection.isActive) {
10874
11020
  if (hoveredNode && String(hoveredNode.userData.id) !== String(connection.sourceNodeData.id)) {
10875
- const sourceId = String(connection.sourceNodeData.id);
10876
- const targetId = String(hoveredNode.userData.id);
10877
- let parentInfo = stateRef.current.nodeIdToParentFileMap.get(sourceId);
10878
- if (!parentInfo || !parentInfo.ownerId) {
10879
- parentInfo = stateRef.current.nodeIdToParentFileMap.get(targetId);
10880
- if (parentInfo && parentInfo.ownerId) {
10881
- stateRef.current.nodeIdToParentFileMap.set(sourceId, parentInfo);
10882
- } else {
10883
- console.error("Nenhum dos Nodes possui um Dataset pai v\xE1lido para salvar a conex\xE3o.");
10884
- alert("N\xE3o \xE9 poss\xEDvel conectar dois Nodes de Quest diretamente sem um Dataset pai, ou ocorreu um erro.");
10885
- userActionHandlers.handleCancelConnection(context);
10886
- return;
10887
- }
10888
- }
10889
11021
  await userActionHandlers.handleCompleteConnection(context, hoveredNode.userData);
10890
11022
  } else {
10891
11023
  userActionHandlers.handleCancelConnection(context);
@@ -10991,39 +11123,6 @@ function XViewScene({
10991
11123
  }
10992
11124
  }
10993
11125
  }
10994
- function handleCancelAncestryCreation() {
10995
- setAncestryMode({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", ancestryDescriptionSections: [], isAddingNodes: false });
10996
- if (mountRef.current) mountRef.current.style.cursor = "grab";
10997
- }
10998
- function handleKeyDown(event) {
10999
- var _a2, _b2, _c2, _d2;
11000
- const context = actionHandlerContext;
11001
- if (event.key === "Escape") {
11002
- if (stateRef.current.connection.isActive) userActionHandlers.handleCancelConnection(context);
11003
- if (stateRef.current.relink.isActive) userActionHandlers.handleCancelRelink(context);
11004
- if (stateRef.current.creation.isActive) userActionHandlers.handleCancelCreation(context);
11005
- if ((_a2 = stateRef.current.versionMode) == null ? void 0 : _a2.isActive) userActionHandlers.handleCancelVersioning(context);
11006
- if (stateRef.current.ancestry.isActive) handleCancelAncestryCreation();
11007
- if ((_b2 = context.questMode) == null ? void 0 : _b2.isActive) context.setters.setQuestMode({ isActive: false });
11008
- if (stateRef.current.selectedNodes.size > 0) {
11009
- stateRef.current.selectedNodes.clear();
11010
- }
11011
- setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
11012
- setMultiContextMenu((prev) => ({ ...prev, visible: false }));
11013
- setRelationshipMenu((prev) => ({ ...prev, visible: false }));
11014
- }
11015
- if (event.key.toLowerCase() === "q") {
11016
- const isUiClear = !stateRef.current.creation.isActive && !stateRef.current.connection.isActive && !stateRef.current.relink.isActive && !stateRef.current.ancestry.isActive && !((_c2 = context.versionMode) == null ? void 0 : _c2.isActive) && !contextMenu.visible && !multiContextMenu.visible && !relationshipMenu.visible && !readingMode.isActive && !isImportModalOpen && !isAncestryBoardOpen;
11017
- if (isUiClear) {
11018
- const isView = ((_d2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _d2.toLowerCase()) === "view";
11019
- if (!isView) {
11020
- alert("Nodes de Quest s\xF3 podem ser criados dentro de uma View.");
11021
- return;
11022
- }
11023
- setQuestMode({ isActive: true });
11024
- }
11025
- }
11026
- }
11027
11126
  function handleDoubleClick(event) {
11028
11127
  if (stateRef.current.camera) stateRef.current.camera.layers.enableAll();
11029
11128
  if (isFromUiOverlay(event) || stateRef.current.isDragging || stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive) return;
@@ -11076,7 +11175,6 @@ function XViewScene({
11076
11175
  currentMount.addEventListener("dblclick", handleDoubleClick);
11077
11176
  currentMount.addEventListener("pointermove", onPointerMove);
11078
11177
  currentMount.addEventListener("contextmenu", handleContextMenu);
11079
- window.addEventListener("keydown", handleKeyDown);
11080
11178
  const originalBackground = scene.background;
11081
11179
  const clock = new THREE3.Clock();
11082
11180
  let animationFrameId = 0;
@@ -11214,7 +11312,6 @@ function XViewScene({
11214
11312
  return () => {
11215
11313
  cancelAnimationFrame(animationFrameId);
11216
11314
  window.removeEventListener("resize", handleResize);
11217
- window.removeEventListener("keydown", handleKeyDown);
11218
11315
  currentMount.removeEventListener("pointerdown", onPointerDown);
11219
11316
  currentMount.removeEventListener("pointerup", onPointerUp);
11220
11317
  currentMount.removeEventListener("dblclick", handleDoubleClick);
@@ -11488,11 +11585,10 @@ function XViewScene({
11488
11585
  creationMode,
11489
11586
  versionMode,
11490
11587
  questMode,
11491
- // <-- Adicionado
11492
11588
  sceneSaveUrl,
11493
- // <-- Adicionado
11589
+ sceneConfigId,
11590
+ ownerId,
11494
11591
  viewType: viewParams == null ? void 0 : viewParams.type,
11495
- // <-- Adicionado
11496
11592
  userId: (_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id,
11497
11593
  setters: {
11498
11594
  setContextMenu,
@@ -11506,7 +11602,6 @@ function XViewScene({
11506
11602
  setSceneVersion,
11507
11603
  setAncestryMode,
11508
11604
  setQuestMode
11509
- // <-- Adicionado
11510
11605
  },
11511
11606
  tweenToTarget,
11512
11607
  handleVersionTimeline,
@@ -11524,9 +11619,11 @@ function XViewScene({
11524
11619
  versionMode,
11525
11620
  questMode,
11526
11621
  sceneSaveUrl,
11622
+ sceneConfigId,
11623
+ ownerId,
11527
11624
  viewParams == null ? void 0 : viewParams.type,
11528
- tweenToTarget,
11529
11625
  (_a = session == null ? void 0 : session.user) == null ? void 0 : _a.id,
11626
+ tweenToTarget,
11530
11627
  handleVersionTimeline,
11531
11628
  save_view_data,
11532
11629
  get_single_parent_file,
@@ -11538,33 +11635,116 @@ function XViewScene({
11538
11635
  const handleStartVersioning = (nodeData) => {
11539
11636
  userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
11540
11637
  };
11638
+ const handleCancelQuest = useCallback4(() => {
11639
+ const { graphGroup, ghostElements } = stateRef.current;
11640
+ if (ghostElements.node && graphGroup) {
11641
+ if (ghostElements.node.userData.labelObject) {
11642
+ graphGroup.remove(ghostElements.node.userData.labelObject);
11643
+ if (ghostElements.node.userData.labelObject.material.map) ghostElements.node.userData.labelObject.material.map.dispose();
11644
+ ghostElements.node.userData.labelObject.material.dispose();
11645
+ }
11646
+ graphGroup.remove(ghostElements.node);
11647
+ ghostElements.node.traverse((child) => {
11648
+ if (child.material) {
11649
+ if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());
11650
+ else child.material.dispose();
11651
+ }
11652
+ if (child.geometry) child.geometry.dispose();
11653
+ });
11654
+ }
11655
+ stateRef.current.ghostElements = { node: null, line: null, aura: null };
11656
+ setQuestMode({ isActive: false });
11657
+ }, []);
11541
11658
  const handleSaveQuestNode = async (context, newQuestData) => {
11542
- const { sceneDataRef: sceneDataRef2, stateRef: stateRef2, setters, actions, sceneSaveUrl: sceneSaveUrl2, viewType } = context;
11543
- if (!sceneDataRef2.current || (viewType == null ? void 0 : viewType.toLowerCase()) !== "view") return;
11659
+ const { graphDataRef, sceneDataRef: sceneDataRef2, stateRef: stateRef2, setters, actions, sceneSaveUrl: sceneSaveUrl2, viewType, sceneConfigId: sceneConfigId2, ownerId: ownerId2 } = context;
11660
+ if (!graphDataRef.current || (viewType == null ? void 0 : viewType.toLowerCase()) !== "view") return;
11544
11661
  const newNode = {
11545
11662
  id: short2.generate(),
11546
11663
  ...newQuestData,
11664
+ is_quest: true,
11547
11665
  type: ["quest", ...newQuestData.type.filter((t) => t !== "quest")]
11548
11666
  };
11549
- const updatedSceneData = {
11550
- ...sceneDataRef2.current,
11551
- nodes: [...sceneDataRef2.current.nodes, newNode]
11667
+ if (!graphDataRef.current[sceneConfigId2]) {
11668
+ graphDataRef.current[sceneConfigId2] = { nodes: [], links: [] };
11669
+ }
11670
+ graphDataRef.current[sceneConfigId2].nodes.push(newNode);
11671
+ const sceneFileData = {
11672
+ parent_dbs: sceneDataRef2.current.parent_dbs,
11673
+ nodes: sceneDataRef2.current.nodes,
11674
+ links: sceneDataRef2.current.links,
11675
+ quest_nodes: graphDataRef.current[sceneConfigId2].nodes,
11676
+ quest_links: graphDataRef.current[sceneConfigId2].links
11552
11677
  };
11553
11678
  try {
11554
- await actions.save_view_data(sceneSaveUrl2, updatedSceneData);
11555
- sceneDataRef2.current.nodes.push(newNode);
11556
- const basePosition = stateRef2.current.controls.target.clone();
11557
- const offset = new THREE3.Vector3((Math.random() - 0.5) * 10, (Math.random() - 0.5) * 5, (Math.random() - 0.5) * 10);
11558
- const finalPosition = basePosition.add(offset);
11559
- addOrUpdateNodeMesh(newNode, finalPosition);
11679
+ await actions.save_view_data(sceneSaveUrl2, sceneFileData);
11680
+ stateRef2.current.nodeIdToParentFileMap.set(String(newNode.id), {
11681
+ parentFileId: sceneConfigId2,
11682
+ ownerId: ownerId2,
11683
+ datasetName: "Quests Internas (View)"
11684
+ });
11685
+ const finalPosition = stateRef2.current.ghostElements.node ? stateRef2.current.ghostElements.node.position.clone() : stateRef2.current.controls.target.clone();
11686
+ const { graphGroup, ghostElements } = stateRef2.current;
11687
+ if (ghostElements.node && graphGroup) {
11688
+ if (ghostElements.node.userData.labelObject) graphGroup.remove(ghostElements.node.userData.labelObject);
11689
+ graphGroup.remove(ghostElements.node);
11690
+ }
11691
+ stateRef2.current.ghostElements = { node: null, line: null, aura: null };
11692
+ addStandaloneNodeToScene(stateRef2.current, newNode, finalPosition);
11560
11693
  context.tweenToTarget(finalPosition, 1.2);
11561
11694
  setters.setQuestMode({ isActive: false });
11562
11695
  setters.setSceneVersion((v) => v + 1);
11563
11696
  } catch (error) {
11564
- console.error("Falha ao salvar a Quest na View:", error);
11697
+ console.error("Falha ao salvar Quest na View:", error);
11565
11698
  alert("Ocorreu um erro ao criar a Quest.");
11566
11699
  }
11567
11700
  };
11701
+ userActionHandlers.handleCompleteConnection = async (context, targetNodeData) => {
11702
+ const { stateRef: stateRef2, graphDataRef, sceneDataRef: sceneDataRef2, sceneConfigId: sceneConfigId2, sceneSaveUrl: sceneSaveUrl2, ownerId: ownerId2 } = context;
11703
+ const { sourceNodeData } = stateRef2.current.connection;
11704
+ if (!graphDataRef.current || !sceneDataRef2.current || !sourceNodeData || !targetNodeData) {
11705
+ userActionHandlers.handleCancelConnection(context);
11706
+ return;
11707
+ }
11708
+ const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef2.current, sourceNodeData.id, sceneConfigId2, ownerId2);
11709
+ const targetParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef2.current, targetNodeData.id, sceneConfigId2, ownerId2);
11710
+ let parentInfoToSave = sourceParentInfo;
11711
+ const isSourceQuest = sourceParentInfo.parentFileId === sceneConfigId2;
11712
+ const isTargetQuest = targetParentInfo.parentFileId === sceneConfigId2;
11713
+ if (isSourceQuest || isTargetQuest) {
11714
+ parentInfoToSave = { parentFileId: sceneConfigId2, ownerId: ownerId2 };
11715
+ }
11716
+ const { parentFileId: parentFileIdToSave, ownerId: ownerIdToSave } = parentInfoToSave;
11717
+ const newLink = {
11718
+ id: `link_${short2.generate()}`,
11719
+ source: sourceNodeData.id,
11720
+ target: targetNodeData.id
11721
+ };
11722
+ try {
11723
+ if (parentFileIdToSave === sceneConfigId2) {
11724
+ const specificParentData = graphDataRef.current[sceneConfigId2];
11725
+ specificParentData.links.push(newLink);
11726
+ const viewFilePayload = {
11727
+ parent_dbs: sceneDataRef2.current.parent_dbs,
11728
+ nodes: sceneDataRef2.current.nodes,
11729
+ links: sceneDataRef2.current.links,
11730
+ quest_nodes: specificParentData.nodes,
11731
+ quest_links: specificParentData.links
11732
+ };
11733
+ await context.actions.save_view_data(sceneSaveUrl2, viewFilePayload);
11734
+ } else {
11735
+ const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
11736
+ specificParentData.links.push(newLink);
11737
+ const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
11738
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
11739
+ graphDataRef.current[parentFileIdToSave] = specificParentData;
11740
+ }
11741
+ addNewLinkToScene(stateRef2.current, newLink);
11742
+ } catch (error) {
11743
+ console.error("Falha ao salvar a nova conex\xE3o:", error);
11744
+ alert("Ocorreu um erro ao salvar a nova conex\xE3o.");
11745
+ }
11746
+ userActionHandlers.handleCancelConnection(context);
11747
+ };
11568
11748
  const handleClearAncestryVisuals = useCallback4((ancestryId) => {
11569
11749
  const { renderedAncestries, ancestryGroup } = stateRef.current;
11570
11750
  const renderIndex = renderedAncestries.findIndex((a) => String(a.id) === String(ancestryId));
@@ -12813,7 +12993,7 @@ function XViewScene({
12813
12993
  const allTypes = Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes.flatMap((node) => {
12814
12994
  if (Array.isArray(node.type)) return node.type;
12815
12995
  return [node.type];
12816
- })).filter(Boolean);
12996
+ })).filter((t) => Boolean(t) && String(t).toLowerCase() !== "quest");
12817
12997
  return [...new Set(allTypes)];
12818
12998
  }, [parentDataRef.current, sceneVersion]);
12819
12999
  const searchableDbNodes = useMemo12(() => {
@@ -12832,6 +13012,7 @@ function XViewScene({
12832
13012
  [actionHandlerContext]
12833
13013
  );
12834
13014
  const handleSaveCurrentView = useCallback4(async () => {
13015
+ var _a2, _b2, _c2;
12835
13016
  const { nodeObjects, allLinks } = stateRef.current;
12836
13017
  if (!nodeObjects || !allLinks || !sceneSaveUrl || !parentDataRef.current) {
12837
13018
  console.warn("N\xE3o \xE9 poss\xEDvel salvar a cena: estado n\xE3o inicializado ou URL de salvamento ausente.");
@@ -12853,17 +13034,22 @@ function XViewScene({
12853
13034
  const { sourceNode, targetNode, ...serializableLinkData } = line.userData;
12854
13035
  return serializableLinkData;
12855
13036
  });
13037
+ sceneDataRef.current.nodes = currentNodes;
13038
+ sceneDataRef.current.links = currentLinks;
13039
+ const isView = ((_a2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _a2.toLowerCase()) === "view";
12856
13040
  const sceneFileData = {
12857
13041
  parent_dbs: sceneDataRef.current.parent_dbs,
12858
13042
  nodes: currentNodes,
12859
- links: currentLinks
13043
+ links: currentLinks,
13044
+ quest_nodes: isView ? ((_b2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _b2.nodes) || [] : sceneDataRef.current.quest_nodes || [],
13045
+ quest_links: isView ? ((_c2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _c2.links) || [] : sceneDataRef.current.quest_links || []
12860
13046
  };
12861
13047
  try {
12862
13048
  await save_view_data(sceneSaveUrl, sceneFileData);
12863
13049
  } catch (error) {
12864
13050
  console.error("Erro na chamada de save_view_data:", error);
12865
13051
  }
12866
- }, [sceneSaveUrl, save_view_data]);
13052
+ }, [sceneSaveUrl, save_view_data, sceneConfigId, viewParams == null ? void 0 : viewParams.type]);
12867
13053
  const allAvailableNodes = useMemo12(() => {
12868
13054
  if (!parentDataRef.current) return [];
12869
13055
  return Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes || []);
@@ -12948,6 +13134,106 @@ function XViewScene({
12948
13134
  }
12949
13135
  }
12950
13136
  }, [isInitialized, sceneVersion, focusAncestryId, hasOpenedInitialAncestry, handleStartReadingAncestry]);
13137
+ useEffect21(() => {
13138
+ function handleKeyDown(event) {
13139
+ var _a2, _b2, _c2;
13140
+ const context = actionHandlerContext;
13141
+ if (event.key === "Escape") {
13142
+ if (stateRef.current.connection.isActive) userActionHandlers.handleCancelConnection(context);
13143
+ if (stateRef.current.relink.isActive) userActionHandlers.handleCancelRelink(context);
13144
+ if (stateRef.current.creation.isActive) userActionHandlers.handleCancelCreation(context);
13145
+ if ((_a2 = stateRef.current.versionMode) == null ? void 0 : _a2.isActive) userActionHandlers.handleCancelVersioning(context);
13146
+ if (stateRef.current.ancestry.isActive) {
13147
+ setAncestryMode({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", ancestryDescriptionSections: [], isAddingNodes: false });
13148
+ if (mountRef.current) mountRef.current.style.cursor = "grab";
13149
+ }
13150
+ if (questMode.isActive) {
13151
+ handleCancelQuest();
13152
+ }
13153
+ if (stateRef.current.selectedNodes.size > 0) {
13154
+ stateRef.current.selectedNodes.clear();
13155
+ }
13156
+ setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
13157
+ setMultiContextMenu((prev) => ({ ...prev, visible: false }));
13158
+ setRelationshipMenu((prev) => ({ ...prev, visible: false }));
13159
+ }
13160
+ if (event.key.toLowerCase() === "q") {
13161
+ const isUiClear = !stateRef.current.creation.isActive && !stateRef.current.connection.isActive && !stateRef.current.relink.isActive && !stateRef.current.ancestry.isActive && !((_b2 = context.versionMode) == null ? void 0 : _b2.isActive) && !contextMenu.visible && !multiContextMenu.visible && !relationshipMenu.visible && !readingMode.isActive && !isImportModalOpen && !isAncestryBoardOpen && !isSidebarOpen && !detailsNode && !detailsLink && !ancestryLinkDetails && !imageViewer.visible && !editingAncestryRel.visible && !questMode.isActive;
13162
+ if (isUiClear) {
13163
+ const isView = ((_c2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _c2.toLowerCase()) === "view";
13164
+ if (!isView) return;
13165
+ const { graphGroup, glowTexture, controls, nodeObjects } = stateRef.current;
13166
+ if (graphGroup) {
13167
+ let ghostPosition = controls.target.clone();
13168
+ const existingNodes = Object.values(nodeObjects);
13169
+ let isOccupied = true;
13170
+ let radius = 18;
13171
+ let angle = 0;
13172
+ let attempts = 0;
13173
+ const MIN_CLEARANCE = 15;
13174
+ while (isOccupied && attempts < 30) {
13175
+ isOccupied = existingNodes.some((mesh) => mesh.position.distanceTo(ghostPosition) < MIN_CLEARANCE);
13176
+ if (isOccupied) {
13177
+ ghostPosition.x = controls.target.x + Math.cos(angle) * radius;
13178
+ ghostPosition.y = controls.target.y + (Math.random() - 0.5) * 8;
13179
+ ghostPosition.z = controls.target.z + Math.sin(angle) * radius;
13180
+ angle += Math.PI / 3;
13181
+ radius += 2.5;
13182
+ attempts++;
13183
+ }
13184
+ }
13185
+ const ghostData = {
13186
+ id: "ghost_quest",
13187
+ name: "Nova Quest",
13188
+ color: "#64748b",
13189
+ // Cor padrão de "Backlog"
13190
+ size: "medium",
13191
+ intensity: 0,
13192
+ type: ["quest"]
13193
+ };
13194
+ const ghostNode = createNodeMesh(ghostData, ghostPosition, glowTexture);
13195
+ ghostNode.traverse((child) => {
13196
+ if (child.isMesh) {
13197
+ child.material.transparent = true;
13198
+ child.material.opacity = 0.75;
13199
+ }
13200
+ });
13201
+ graphGroup.add(ghostNode);
13202
+ if (ghostNode.userData.labelObject) {
13203
+ graphGroup.add(ghostNode.userData.labelObject);
13204
+ }
13205
+ stateRef.current.ghostElements = {
13206
+ node: ghostNode,
13207
+ line: null,
13208
+ aura: ghostNode.getObjectByName("aura")
13209
+ };
13210
+ context.tweenToTarget(ghostPosition, 1.6);
13211
+ }
13212
+ setQuestMode({ isActive: true });
13213
+ }
13214
+ }
13215
+ }
13216
+ window.addEventListener("keydown", handleKeyDown);
13217
+ return () => window.removeEventListener("keydown", handleKeyDown);
13218
+ }, [
13219
+ contextMenu.visible,
13220
+ multiContextMenu.visible,
13221
+ relationshipMenu.visible,
13222
+ readingMode.isActive,
13223
+ isImportModalOpen,
13224
+ isAncestryBoardOpen,
13225
+ isSidebarOpen,
13226
+ detailsNode,
13227
+ detailsLink,
13228
+ ancestryLinkDetails,
13229
+ imageViewer.visible,
13230
+ editingAncestryRel.visible,
13231
+ questMode.isActive,
13232
+ viewParams,
13233
+ actionHandlerContext,
13234
+ handleCancelQuest
13235
+ // <-- handleCancelQuest adicionado aqui
13236
+ ]);
12951
13237
  if (isLoading || status === "loading" || permissionStatus === "loading") {
12952
13238
  return /* @__PURE__ */ React24.createElement(LoadingScreen, null);
12953
13239
  }
@@ -13042,7 +13328,10 @@ function XViewScene({
13042
13328
  InSceneQuestForm,
13043
13329
  {
13044
13330
  onSave: (data) => handleSaveQuestNode(actionHandlerContext, data),
13045
- onCancel: () => setQuestMode({ isActive: false }),
13331
+ onCancel: handleCancelQuest,
13332
+ onNameChange: handleGhostNodeNameChange,
13333
+ onColorChange: handleGhostNodeColorChange,
13334
+ onSizeChange: handleGhostNodeSizeChange,
13046
13335
  style: { position: "absolute", left: `16px`, top: `16px`, zIndex: 20, transition: "opacity 200ms ease-out" },
13047
13336
  refEl: formRef,
13048
13337
  onOpenImageViewer: handleOpenImageViewer,
@@ -13334,6 +13623,12 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
13334
13623
  }
13335
13624
  const sceneData = sceneResponse.data;
13336
13625
  const parentDbObjects = sceneData.parent_dbs || [];
13626
+ if (type && type.toLowerCase().includes("database")) {
13627
+ const selfExists = parentDbObjects.some((db) => String(db.db_id) === String(scene_config));
13628
+ if (!selfExists) {
13629
+ parentDbObjects.push({ db_id: scene_config, owner_id });
13630
+ }
13631
+ }
13337
13632
  const parentResponsesPromises = parentDbObjects.map(
13338
13633
  (db_info) => db_services.get_file(`x_view_dbs/${db_info.owner_id}/${db_info.db_id}`)
13339
13634
  );
@@ -13351,6 +13646,13 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
13351
13646
  );
13352
13647
  }
13353
13648
  }
13649
+ if (type && type.toLowerCase() === "view") {
13650
+ parentData[scene_config] = {
13651
+ dataset_name: "Quests Internas (View)",
13652
+ nodes: sceneData.quest_nodes || [],
13653
+ links: sceneData.quest_links || []
13654
+ };
13655
+ }
13354
13656
  const allNodes = Object.values(parentData).flatMap((db) => db.nodes || []);
13355
13657
  const allLinks = Object.values(parentData).flatMap((db) => db.links || []);
13356
13658
  const parentNodeMap = new Map(allNodes.map((node) => [String(node.id), node]));
@@ -13360,7 +13662,11 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
13360
13662
  if (nodeTypes.includes("quest")) {
13361
13663
  return sceneNode;
13362
13664
  }
13363
- return parentNodeMap.get(String(sceneNode.id));
13665
+ const dbNode = parentNodeMap.get(String(sceneNode.id));
13666
+ if (dbNode) {
13667
+ return { ...sceneNode, ...dbNode };
13668
+ }
13669
+ return null;
13364
13670
  }).filter(Boolean);
13365
13671
  const validNodeIdsInScene = new Set(validatedNodes.map((node) => String(node.id)));
13366
13672
  const validatedLinks = (sceneData.links || []).filter((sceneLink) => {