@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.js CHANGED
@@ -294,11 +294,12 @@ function ContextMenu({
294
294
  });
295
295
  };
296
296
  const renderMainView = () => {
297
+ var _a2;
297
298
  const hasVersions = computedVersions.length > 0;
298
299
  const canCreateVersion = ability.can("create", "Versioning");
299
300
  const canReadVersion = ability.can("read", "Versioning");
300
301
  const shouldShowVersioningBtn = canCreateVersion || canReadVersion && hasVersions;
301
- return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_12px_1px_rgba(99,102,241,0.5)]" }), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, ability.can("create", "Connection") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conectar")), ability.can("create", "Node") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar Ancestralidade" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2-.8 2 .8s1.5-.3 2-.8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("line", { x1: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ import_react.default.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
302
+ return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_12px_1px_rgba(99,102,241,0.5)]" }), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, ability.can("create", "Connection") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conectar")), ability.can("create", "Node") && !((_a2 = data.nodeData) == null ? void 0 : _a2.is_quest) && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar Ancestralidade" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2-.8 2 .8s1.5-.3 2-.8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("line", { x1: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ import_react.default.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
302
303
  onFocusNode == null ? void 0 : onFocusNode(data.nodeData);
303
304
  onClose();
304
305
  }, className: baseButtonClass, title: "Focar na c\xE2mera" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Focar neste Node")), /* @__PURE__ */ import_react.default.createElement("button", { onClick: (e) => handleCopyLink(e, data.nodeData), className: baseButtonClass, title: "Copiar Link para Compartilhar" }, isLinkCopied ? /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#4ade80", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("polyline", { points: "20 6 9 17 4 12" })) : /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ import_react.default.createElement("span", { className: isLinkCopied ? "text-green-400" : "" }, isLinkCopied ? "Copiado!" : "Copiar Link")), ability.can("dismiss", "Node") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDismissNode == null ? void 0 : onDismissNode(data.nodeData), className: baseButtonClass, title: "Remover da visualiza\xE7\xE3o" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "2", y1: "2", x2: "22", y2: "22" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Dismiss")), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDismissOtherNodes == null ? void 0 : onDismissOtherNodes(data.nodeData), className: baseButtonClass, title: "Remover outros da visualiza\xE7\xE3o" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Dismiss other nodes"))), ability.can("delete", "Node") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("deleteConfirmation"), className: deleteButtonClass, title: "Excluir Node" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Excluir Node"))));
@@ -1225,8 +1226,6 @@ var createMultipleLinkLines = (linksArray, sourceNodeMesh, targetNodeMesh, resol
1225
1226
  targetNodeMesh,
1226
1227
  resolution,
1227
1228
  isCurved,
1228
- isCurved,
1229
- isCurved,
1230
1229
  curveOffset
1231
1230
  );
1232
1231
  line.userData = {
@@ -1559,7 +1558,7 @@ var addStandaloneNodeToScene = (state, nodeData, position) => {
1559
1558
  scaleTween.start();
1560
1559
  }
1561
1560
  };
1562
- var getParentFileInfoForNode = (allParentData, sceneData, nodeId) => {
1561
+ var getParentFileInfoForNode = (allParentData, sceneData, nodeId, sceneConfigId = null, sceneOwnerId = null) => {
1563
1562
  const parentDbsArray = (sceneData == null ? void 0 : sceneData.parent_dbs) || [];
1564
1563
  for (const parentFileId in allParentData) {
1565
1564
  if (allParentData.hasOwnProperty(parentFileId)) {
@@ -1568,6 +1567,8 @@ var getParentFileInfoForNode = (allParentData, sceneData, nodeId) => {
1568
1567
  const parentDbInfo = parentDbsArray.find((db) => String(db.db_id) === String(parentFileId));
1569
1568
  if (parentDbInfo) {
1570
1569
  return { parentFileId, ownerId: parentDbInfo.owner_id };
1570
+ } else if (sceneConfigId && String(parentFileId) === String(sceneConfigId)) {
1571
+ return { parentFileId, ownerId: sceneOwnerId };
1571
1572
  } else {
1572
1573
  console.warn(`Owner ID n\xE3o encontrado em sceneData.parent_dbs para o parentFileId: ${parentFileId}`);
1573
1574
  return { parentFileId, ownerId: null };
@@ -1837,6 +1838,7 @@ var userActionHandlers = {
1837
1838
  setters.setFormPosition((p) => ({ ...p, opacity: 0 }));
1838
1839
  },
1839
1840
  handleSaveVersionNode: async (context, newNodeData) => {
1841
+ var _a;
1840
1842
  const { graphDataRef, sceneDataRef, stateRef, versionMode, setters } = context;
1841
1843
  if (!graphDataRef.current || !sceneDataRef.current) return;
1842
1844
  const { sourceNodeData } = versionMode;
@@ -1846,7 +1848,7 @@ var userActionHandlers = {
1846
1848
  version_node: { is_version: true, parent_node: sourceNodeData.id }
1847
1849
  };
1848
1850
  const newLink = { id: `link_${import_short_uuid.default.generate()}`, source: sourceNodeData.id, target: newNode.id };
1849
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id);
1851
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id, context.sceneConfigId, context.ownerId);
1850
1852
  if (!parentInfo || !parentInfo.ownerId) {
1851
1853
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node de origem:", sourceNodeData.id);
1852
1854
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio.");
@@ -1856,9 +1858,21 @@ var userActionHandlers = {
1856
1858
  const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileId]));
1857
1859
  specificParentData.nodes.push(newNode);
1858
1860
  specificParentData.links.push(newLink);
1859
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
1860
1861
  try {
1861
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
1862
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
1863
+ if (isView && parentFileId === context.sceneConfigId) {
1864
+ const viewFilePayload = {
1865
+ parent_dbs: sceneDataRef.current.parent_dbs,
1866
+ nodes: sceneDataRef.current.nodes,
1867
+ links: sceneDataRef.current.links,
1868
+ quest_nodes: specificParentData.nodes,
1869
+ quest_links: specificParentData.links
1870
+ };
1871
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
1872
+ } else {
1873
+ const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
1874
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
1875
+ }
1862
1876
  graphDataRef.current[parentFileId] = specificParentData;
1863
1877
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1864
1878
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
@@ -1882,10 +1896,6 @@ var userActionHandlers = {
1882
1896
  var _a;
1883
1897
  const isSource = String(link.source) === String(sourceNode.id);
1884
1898
  const targetNodeId = isSource ? link.target : link.source;
1885
- const linkAlreadyInSceneData = sceneDataRef.current.links.some((l) => String(l.id) === String(link.id));
1886
- if (!linkAlreadyInSceneData) {
1887
- sceneDataRef.current.links.push(link);
1888
- }
1889
1899
  if (!nodeObjects[String(targetNodeId)]) {
1890
1900
  const allParentNodes = Object.values(graphDataRef.current).flatMap((fileData) => fileData.nodes);
1891
1901
  const nodeData = allParentNodes.find((n) => String(n.id) === String(targetNodeId));
@@ -1893,9 +1903,6 @@ var userActionHandlers = {
1893
1903
  console.warn(`Dados do Node com ID ${targetNodeId} n\xE3o encontrados no cache.`);
1894
1904
  return;
1895
1905
  }
1896
- if (!sceneDataRef.current.nodes.some((n) => String(n.id) === String(nodeData.id))) {
1897
- sceneDataRef.current.nodes.push(nodeData);
1898
- }
1899
1906
  const startPosition = sourceNodeMesh.position.clone();
1900
1907
  const endPosition = startPosition.clone().add(
1901
1908
  new THREE.Vector3((Math.random() - 0.5) * 60, (Math.random() - 0.5) * 15, (Math.random() - 0.5) * 60)
@@ -2015,6 +2022,7 @@ var userActionHandlers = {
2015
2022
  if (mountRef.current) mountRef.current.style.cursor = "grab";
2016
2023
  },
2017
2024
  handleCompleteConnection: async (context, targetNodeData) => {
2025
+ var _a;
2018
2026
  const { stateRef, graphDataRef, sceneDataRef } = context;
2019
2027
  const { sourceNodeData } = stateRef.current.connection;
2020
2028
  if (!graphDataRef.current || !sceneDataRef.current || !sourceNodeData || !targetNodeData) {
@@ -2022,7 +2030,7 @@ var userActionHandlers = {
2022
2030
  userActionHandlers.handleCancelConnection(context);
2023
2031
  return;
2024
2032
  }
2025
- const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id);
2033
+ const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id, context.sceneConfigId, context.ownerId);
2026
2034
  if (!sourceParentInfo || !sourceParentInfo.ownerId) {
2027
2035
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node de origem:", sourceNodeData.id);
2028
2036
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio.");
@@ -2037,12 +2045,26 @@ var userActionHandlers = {
2037
2045
  source: sourceNodeData.id,
2038
2046
  target: targetNodeData.id
2039
2047
  };
2040
- const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
2041
- specificParentData.links.push(newLink);
2042
- const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
2043
2048
  try {
2044
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2045
- graphDataRef.current[parentFileIdToSave] = specificParentData;
2049
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2050
+ if (isView && parentFileIdToSave === context.sceneConfigId) {
2051
+ const specificParentData = graphDataRef.current[context.sceneConfigId];
2052
+ specificParentData.links.push(newLink);
2053
+ const viewFilePayload = {
2054
+ parent_dbs: sceneDataRef.current.parent_dbs,
2055
+ nodes: sceneDataRef.current.nodes,
2056
+ links: sceneDataRef.current.links,
2057
+ quest_nodes: specificParentData.nodes,
2058
+ quest_links: specificParentData.links
2059
+ };
2060
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2061
+ } else {
2062
+ const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
2063
+ specificParentData.links.push(newLink);
2064
+ const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
2065
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2066
+ graphDataRef.current[parentFileIdToSave] = specificParentData;
2067
+ }
2046
2068
  addNewLinkToScene(stateRef.current, newLink);
2047
2069
  } catch (error) {
2048
2070
  console.error("Falha ao salvar a nova conex\xE3o:", error);
@@ -2111,14 +2133,28 @@ var userActionHandlers = {
2111
2133
  } else {
2112
2134
  newTargetId = newEndNodeData.id;
2113
2135
  }
2114
- const originalParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, oldSourceId);
2115
- const newParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, newSourceId);
2116
- if (!originalParentInfo || !newParentInfo || !originalParentInfo.ownerId || !newParentInfo.ownerId) {
2117
- console.error("N\xE3o foi poss\xEDvel encontrar informa\xE7\xF5es dos arquivos pai para o relink.");
2136
+ const oldSourceInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, oldSourceId, context.sceneConfigId, context.ownerId);
2137
+ const oldTargetInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, oldTargetId, context.sceneConfigId, context.ownerId);
2138
+ const newSourceInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, newSourceId, context.sceneConfigId, context.ownerId);
2139
+ const newTargetInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, newTargetId, context.sceneConfigId, context.ownerId);
2140
+ if (!oldSourceInfo || !oldTargetInfo || !newSourceInfo || !newTargetInfo) {
2141
+ console.error("Informa\xE7\xF5es dos arquivos pai incompletas para o relink.");
2118
2142
  alert("Ocorreu um erro ao identificar os arquivos pai para salvar a altera\xE7\xE3o.");
2119
2143
  userActionHandlers.handleCancelRelink(context);
2120
2144
  return;
2121
2145
  }
2146
+ let oldGoverningFileId = oldSourceInfo.parentFileId;
2147
+ let oldGoverningOwnerId = oldSourceInfo.ownerId;
2148
+ if (oldSourceInfo.parentFileId === context.sceneConfigId || oldTargetInfo.parentFileId === context.sceneConfigId) {
2149
+ oldGoverningFileId = context.sceneConfigId;
2150
+ oldGoverningOwnerId = context.ownerId;
2151
+ }
2152
+ let newGoverningFileId = newSourceInfo.parentFileId;
2153
+ let newGoverningOwnerId = newSourceInfo.ownerId;
2154
+ if (newSourceInfo.parentFileId === context.sceneConfigId || newTargetInfo.parentFileId === context.sceneConfigId) {
2155
+ newGoverningFileId = context.sceneConfigId;
2156
+ newGoverningOwnerId = context.ownerId;
2157
+ }
2122
2158
  const { sourceNode, targetNode, ...dataToKeep } = originalLinkData;
2123
2159
  const newLinkData = {
2124
2160
  ...dataToKeep,
@@ -2126,34 +2162,44 @@ var userActionHandlers = {
2126
2162
  target: newTargetId,
2127
2163
  id: linkId
2128
2164
  };
2165
+ const saveParentData = async (fileId, fileOwnerId, data) => {
2166
+ var _a;
2167
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2168
+ if (isView && fileId === context.sceneConfigId) {
2169
+ const viewFilePayload = {
2170
+ parent_dbs: sceneDataRef.current.parent_dbs,
2171
+ nodes: sceneDataRef.current.nodes,
2172
+ links: sceneDataRef.current.links,
2173
+ quest_nodes: data.nodes,
2174
+ quest_links: data.links
2175
+ };
2176
+ return context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2177
+ } else {
2178
+ return context.actions.save_view_data(`x_view_dbs/${fileOwnerId}/${fileId}`, data);
2179
+ }
2180
+ };
2129
2181
  const savePromises = [];
2130
2182
  const filesToUpdate = {};
2131
- if (originalParentInfo.parentFileId !== newParentInfo.parentFileId) {
2132
- const updatedOriginalParentData = JSON.parse(JSON.stringify(graphDataRef.current[originalParentInfo.parentFileId]));
2183
+ if (oldGoverningFileId !== newGoverningFileId) {
2184
+ const updatedOriginalParentData = JSON.parse(JSON.stringify(graphDataRef.current[oldGoverningFileId]));
2133
2185
  updatedOriginalParentData.links = updatedOriginalParentData.links.filter(
2134
2186
  (l) => String(l.id) !== String(linkId)
2135
2187
  );
2136
- filesToUpdate[originalParentInfo.parentFileId] = updatedOriginalParentData;
2137
- savePromises.push(
2138
- context.actions.save_view_data(`x_view_dbs/${originalParentInfo.ownerId}/${originalParentInfo.parentFileId}`, updatedOriginalParentData)
2139
- );
2140
- const updatedNewParentData = JSON.parse(JSON.stringify(graphDataRef.current[newParentInfo.parentFileId]));
2188
+ filesToUpdate[oldGoverningFileId] = updatedOriginalParentData;
2189
+ savePromises.push(saveParentData(oldGoverningFileId, oldGoverningOwnerId, updatedOriginalParentData));
2190
+ const updatedNewParentData = JSON.parse(JSON.stringify(graphDataRef.current[newGoverningFileId]));
2141
2191
  if (!updatedNewParentData.links) updatedNewParentData.links = [];
2142
2192
  updatedNewParentData.links.push(newLinkData);
2143
- filesToUpdate[newParentInfo.parentFileId] = updatedNewParentData;
2144
- savePromises.push(
2145
- context.actions.save_view_data(`x_view_dbs/${newParentInfo.ownerId}/${newParentInfo.parentFileId}`, updatedNewParentData)
2146
- );
2193
+ filesToUpdate[newGoverningFileId] = updatedNewParentData;
2194
+ savePromises.push(saveParentData(newGoverningFileId, newGoverningOwnerId, updatedNewParentData));
2147
2195
  } else {
2148
- const updatedParentData = JSON.parse(JSON.stringify(graphDataRef.current[originalParentInfo.parentFileId]));
2196
+ const updatedParentData = JSON.parse(JSON.stringify(graphDataRef.current[oldGoverningFileId]));
2149
2197
  updatedParentData.links = updatedParentData.links.filter(
2150
2198
  (l) => String(l.id) !== String(linkId)
2151
2199
  );
2152
2200
  updatedParentData.links.push(newLinkData);
2153
- filesToUpdate[originalParentInfo.parentFileId] = updatedParentData;
2154
- savePromises.push(
2155
- context.actions.save_view_data(`x_view_dbs/${originalParentInfo.ownerId}/${originalParentInfo.parentFileId}`, updatedParentData)
2156
- );
2201
+ filesToUpdate[oldGoverningFileId] = updatedParentData;
2202
+ savePromises.push(saveParentData(oldGoverningFileId, oldGoverningOwnerId, updatedParentData));
2157
2203
  }
2158
2204
  try {
2159
2205
  await Promise.all(savePromises);
@@ -2171,7 +2217,7 @@ var userActionHandlers = {
2171
2217
  }
2172
2218
  },
2173
2219
  handleDeleteLink: async (context, linkObject) => {
2174
- var _a, _b, _c, _d, _e, _f;
2220
+ var _a, _b, _c, _d, _e, _f, _g;
2175
2221
  const { stateRef, graphDataRef, sceneDataRef, setters } = context;
2176
2222
  setters.setRelationshipMenu({ visible: false });
2177
2223
  if (!(linkObject == null ? void 0 : linkObject.userData) || !graphDataRef.current || !sceneDataRef.current) return;
@@ -2180,7 +2226,7 @@ var userActionHandlers = {
2180
2226
  console.error("Tentativa de deletar um link sem ID.", linkObject.userData);
2181
2227
  return;
2182
2228
  }
2183
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkObject.userData.source);
2229
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkObject.userData.source, context.sceneConfigId, context.ownerId);
2184
2230
  if (!parentInfo || !parentInfo.ownerId) {
2185
2231
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o link:", linkIdToDelete);
2186
2232
  alert("Ocorreu um erro ao identificar o arquivo pai da rela\xE7\xE3o para exclus\xE3o.");
@@ -2192,9 +2238,25 @@ var userActionHandlers = {
2192
2238
  const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileId]));
2193
2239
  const newLinks = (specificParentData.links || []).filter((l) => String(l.id) !== String(linkIdToDelete));
2194
2240
  specificParentData.links = newLinks;
2195
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2241
+ let filenameToSave;
2242
+ let payloadToSave;
2243
+ const isView = ((_g = context.viewType) == null ? void 0 : _g.toLowerCase()) === "view";
2244
+ if (isView && parentFileId === context.sceneConfigId) {
2245
+ filenameToSave = context.sceneSaveUrl;
2246
+ sceneDataRef.current.links = sceneDataRef.current.links.filter((l) => String(l.id) !== String(linkIdToDelete));
2247
+ payloadToSave = {
2248
+ parent_dbs: sceneDataRef.current.parent_dbs,
2249
+ nodes: sceneDataRef.current.nodes,
2250
+ links: sceneDataRef.current.links,
2251
+ quest_nodes: specificParentData.nodes,
2252
+ quest_links: specificParentData.links
2253
+ };
2254
+ } else {
2255
+ filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2256
+ payloadToSave = specificParentData;
2257
+ }
2196
2258
  try {
2197
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2259
+ await context.actions.save_view_data(filenameToSave, payloadToSave);
2198
2260
  graphDataRef.current[parentFileId] = specificParentData;
2199
2261
  setters.setDetailsLink((prev) => String(prev == null ? void 0 : prev.id) === String(linkIdToDelete) ? null : prev);
2200
2262
  if (stateRef.current.hoveredLink === linkObject) {
@@ -2212,12 +2274,6 @@ var userActionHandlers = {
2212
2274
  if (!nodeData || !sceneDataRef.current) return;
2213
2275
  const nodeIdToDismiss = nodeData.id;
2214
2276
  const strNodeId = String(nodeIdToDismiss);
2215
- sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter(
2216
- (n) => String(n.id) !== strNodeId
2217
- );
2218
- sceneDataRef.current.links = sceneDataRef.current.links.filter(
2219
- (l) => String(l.source) !== strNodeId && String(l.target) !== strNodeId
2220
- );
2221
2277
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2222
2278
  if (ancestryGroup && ancestryLinks) {
2223
2279
  const remainingAncestryLinks = [];
@@ -2259,22 +2315,12 @@ var userActionHandlers = {
2259
2315
  removeNodeFromScene(stateRef.current, nodeId);
2260
2316
  setters.setDetailsNode((prev) => String(prev == null ? void 0 : prev.id) === String(nodeId) ? null : prev);
2261
2317
  });
2262
- sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter(
2263
- (n) => String(n.id) === strNodeIdToKeep
2264
- );
2265
- sceneDataRef.current.links = [];
2266
2318
  },
2267
2319
  handleDismissMultipleNodes: (context, nodeIds) => {
2268
2320
  const { stateRef, sceneDataRef, setters } = context;
2269
2321
  setters.setMultiContextMenu({ visible: false });
2270
2322
  if (!nodeIds || nodeIds.size === 0 || !sceneDataRef.current) return;
2271
2323
  const strNodeIds = Array.from(nodeIds).map(String);
2272
- sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter(
2273
- (n) => !strNodeIds.includes(String(n.id))
2274
- );
2275
- sceneDataRef.current.links = sceneDataRef.current.links.filter(
2276
- (l) => !strNodeIds.includes(String(l.source)) && !strNodeIds.includes(String(l.target))
2277
- );
2278
2324
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2279
2325
  if (ancestryGroup && ancestryLinks) {
2280
2326
  const remainingAncestryLinks = [];
@@ -2330,15 +2376,10 @@ var userActionHandlers = {
2330
2376
  removeNodeFromScene(stateRef.current, nodeId);
2331
2377
  setters.setDetailsNode((prev) => String(prev == null ? void 0 : prev.id) === String(nodeId) ? null : prev);
2332
2378
  });
2333
- sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter(
2334
- (n) => strNodeIdsToKeep.includes(String(n.id))
2335
- );
2336
- sceneDataRef.current.links = sceneDataRef.current.links.filter(
2337
- (l) => strNodeIdsToKeep.includes(String(l.source)) && strNodeIdsToKeep.includes(String(l.target))
2338
- );
2339
2379
  stateRef.current.selectedNodes.clear();
2340
2380
  },
2341
2381
  handleDeleteMultipleNodes: async (context, nodeIds) => {
2382
+ var _a;
2342
2383
  const { stateRef, graphDataRef, sceneDataRef, setters, actions } = context;
2343
2384
  setters.setMultiContextMenu({ visible: false });
2344
2385
  if (!nodeIds || nodeIds.size === 0 || !graphDataRef.current || !sceneDataRef.current) return;
@@ -2357,7 +2398,7 @@ var userActionHandlers = {
2357
2398
  }
2358
2399
  const changesByParentFile = {};
2359
2400
  for (const nodeId of strNodeIdsToDelete) {
2360
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeId);
2401
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeId, context.sceneConfigId, context.ownerId);
2361
2402
  if (!parentInfo || !parentInfo.ownerId) {
2362
2403
  console.warn(`Node com ID ${nodeId} n\xE3o encontrado ou sem ownerId. Ignorando.`);
2363
2404
  continue;
@@ -2391,8 +2432,27 @@ var userActionHandlers = {
2391
2432
  originalData.links = (originalData.links || []).filter(
2392
2433
  (l) => !linksToDelete.has(String(l.id))
2393
2434
  );
2394
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2395
- savePromises.push(context.actions.save_view_data(filenameForSpecificParent, originalData));
2435
+ let filenameToSave;
2436
+ let payloadToSave;
2437
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2438
+ if (isView && parentFileId === context.sceneConfigId) {
2439
+ filenameToSave = context.sceneSaveUrl;
2440
+ const strNodesToDelete = Array.from(nodesToDelete).map(String);
2441
+ const strLinksToDelete = Array.from(linksToDelete).map(String);
2442
+ sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter((n) => !strNodesToDelete.includes(String(n.id)));
2443
+ sceneDataRef.current.links = sceneDataRef.current.links.filter((l) => !strLinksToDelete.includes(String(l.id)));
2444
+ payloadToSave = {
2445
+ parent_dbs: sceneDataRef.current.parent_dbs,
2446
+ nodes: sceneDataRef.current.nodes,
2447
+ links: sceneDataRef.current.links,
2448
+ quest_nodes: originalData.nodes,
2449
+ quest_links: originalData.links
2450
+ };
2451
+ } else {
2452
+ filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2453
+ payloadToSave = originalData;
2454
+ }
2455
+ savePromises.push(context.actions.save_view_data(filenameToSave, payloadToSave));
2396
2456
  updatedParentDataCache[parentFileId] = originalData;
2397
2457
  }
2398
2458
  }
@@ -2413,6 +2473,7 @@ var userActionHandlers = {
2413
2473
  }
2414
2474
  },
2415
2475
  handleDeleteNode: async (context, nodeData) => {
2476
+ var _a;
2416
2477
  const { stateRef, graphDataRef, sceneDataRef, setters, actions } = context;
2417
2478
  if (actions.delete_file && nodeData) {
2418
2479
  const urls = extractFileUrlsFromProperties(nodeData);
@@ -2424,7 +2485,7 @@ var userActionHandlers = {
2424
2485
  if (!nodeData || !graphDataRef.current || !sceneDataRef.current) return;
2425
2486
  const nodeIdToDelete = nodeData.id;
2426
2487
  const strNodeId = String(nodeIdToDelete);
2427
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeIdToDelete);
2488
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeIdToDelete, context.sceneConfigId, context.ownerId);
2428
2489
  if (!parentInfo || !parentInfo.ownerId) {
2429
2490
  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);
2430
2491
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio para exclus\xE3o.");
@@ -2436,9 +2497,28 @@ var userActionHandlers = {
2436
2497
  const newLinks = (specificParentData.links || []).filter((l) => String(l.source) !== strNodeId && String(l.target) !== strNodeId);
2437
2498
  specificParentData.nodes = newNodes;
2438
2499
  specificParentData.links = newLinks;
2439
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2500
+ let filenameToSave;
2501
+ let payloadToSave;
2502
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2503
+ if (isView && parentFileId === context.sceneConfigId) {
2504
+ filenameToSave = context.sceneSaveUrl;
2505
+ const newVisualNodes = sceneDataRef.current.nodes.filter((n) => String(n.id) !== strNodeId);
2506
+ const newVisualLinks = sceneDataRef.current.links.filter((l) => String(l.source) !== strNodeId && String(l.target) !== strNodeId);
2507
+ sceneDataRef.current.nodes = newVisualNodes;
2508
+ sceneDataRef.current.links = newVisualLinks;
2509
+ payloadToSave = {
2510
+ parent_dbs: sceneDataRef.current.parent_dbs,
2511
+ nodes: newVisualNodes,
2512
+ links: newVisualLinks,
2513
+ quest_nodes: specificParentData.nodes,
2514
+ quest_links: specificParentData.links
2515
+ };
2516
+ } else {
2517
+ filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2518
+ payloadToSave = specificParentData;
2519
+ }
2440
2520
  try {
2441
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2521
+ await context.actions.save_view_data(filenameToSave, payloadToSave);
2442
2522
  graphDataRef.current[parentFileId] = specificParentData;
2443
2523
  setters.setDetailsNode((prev) => String(prev == null ? void 0 : prev.id) === String(nodeIdToDelete) ? null : prev);
2444
2524
  removeNodeFromScene(stateRef.current, nodeIdToDelete);
@@ -2449,10 +2529,11 @@ var userActionHandlers = {
2449
2529
  }
2450
2530
  },
2451
2531
  handleSaveNodeDetails: async (context, updatedNode, keepOpen = false) => {
2532
+ var _a;
2452
2533
  const { graphDataRef, sceneDataRef, stateRef, setters } = context;
2453
2534
  if (!graphDataRef.current || !sceneDataRef.current) return;
2454
2535
  const { _baseEmissiveIntensity: ignored, ...nodeToSave } = updatedNode;
2455
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeToSave.id);
2536
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeToSave.id, context.sceneConfigId, context.ownerId);
2456
2537
  if (!parentInfo || !parentInfo.ownerId) {
2457
2538
  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);
2458
2539
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio para atualiza\xE7\xE3o.");
@@ -2468,9 +2549,21 @@ var userActionHandlers = {
2468
2549
  alert("Erro interno: Node n\xE3o encontrado para salvar.");
2469
2550
  return;
2470
2551
  }
2471
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2472
2552
  try {
2473
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2553
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2554
+ if (isView && parentFileId === context.sceneConfigId) {
2555
+ const viewFilePayload = {
2556
+ parent_dbs: sceneDataRef.current.parent_dbs,
2557
+ nodes: sceneDataRef.current.nodes,
2558
+ links: sceneDataRef.current.links,
2559
+ quest_nodes: specificParentData.nodes,
2560
+ quest_links: specificParentData.links
2561
+ };
2562
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2563
+ } else {
2564
+ const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2565
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2566
+ }
2474
2567
  graphDataRef.current[parentFileId] = specificParentData;
2475
2568
  updateExistingNodeVisuals(stateRef.current, nodeToSave);
2476
2569
  setters.setSceneVersion((v) => v + 1);
@@ -2483,10 +2576,11 @@ var userActionHandlers = {
2483
2576
  }
2484
2577
  },
2485
2578
  handleSaveLinkDetails: async (context, updatedLink, keepOpen = false) => {
2579
+ var _a;
2486
2580
  const { graphDataRef, sceneDataRef, stateRef, setters } = context;
2487
2581
  if (!graphDataRef.current || !sceneDataRef.current) return;
2488
2582
  const { sourceNode, targetNode, ...linkToSave } = updatedLink;
2489
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkToSave.source);
2583
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkToSave.source, context.sceneConfigId, context.ownerId);
2490
2584
  if (!parentInfo || !parentInfo.ownerId) {
2491
2585
  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);
2492
2586
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio para atualiza\xE7\xE3o.");
@@ -2502,9 +2596,21 @@ var userActionHandlers = {
2502
2596
  alert("Erro interno: link n\xE3o encontrado para salvar.");
2503
2597
  return;
2504
2598
  }
2505
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2506
2599
  try {
2507
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2600
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2601
+ if (isView && parentFileId === context.sceneConfigId) {
2602
+ const viewFilePayload = {
2603
+ parent_dbs: sceneDataRef.current.parent_dbs,
2604
+ nodes: sceneDataRef.current.nodes,
2605
+ links: sceneDataRef.current.links,
2606
+ quest_nodes: specificParentData.nodes,
2607
+ quest_links: specificParentData.links
2608
+ };
2609
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2610
+ } else {
2611
+ const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2612
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2613
+ }
2508
2614
  graphDataRef.current[parentFileId] = specificParentData;
2509
2615
  const lineObject = stateRef.current.allLinks.find(
2510
2616
  (l) => String(l.userData.id) === String(linkToSave.id)
@@ -2527,7 +2633,7 @@ var userActionHandlers = {
2527
2633
  }
2528
2634
  },
2529
2635
  handleAddExistingNodeById: (context, nodeId) => {
2530
- var _a, _b;
2636
+ var _a;
2531
2637
  const { stateRef, sceneDataRef, graphDataRef, tweenToTarget, setters } = context;
2532
2638
  const state = stateRef.current;
2533
2639
  const graphFull = graphDataRef.current;
@@ -2543,16 +2649,12 @@ var userActionHandlers = {
2543
2649
  tweenToTarget(state.nodeObjects[strNodeId]);
2544
2650
  return;
2545
2651
  }
2546
- const alreadyInSceneData = (((_a = sceneDataRef.current) == null ? void 0 : _a.nodes) || []).some((n) => String(n.id) === String(strNodeId));
2547
- if (!alreadyInSceneData) {
2548
- sceneDataRef.current.nodes.push(nodeData);
2549
- }
2550
2652
  const base = state.controls ? state.controls.target.clone() : new THREE.Vector3(0, 0, 0);
2551
2653
  const offset = new THREE.Vector3((Math.random() - 0.5) * 20, (Math.random() - 0.5) * 6, (Math.random() - 0.5) * 20);
2552
2654
  const position = base.add(offset);
2553
2655
  addStandaloneNodeToScene(state, nodeData, position);
2554
2656
  tweenToTarget(position, 1.3);
2555
- (_b = setters == null ? void 0 : setters.setSceneVersion) == null ? void 0 : _b.call(setters, (v) => v + 1);
2657
+ (_a = setters == null ? void 0 : setters.setSceneVersion) == null ? void 0 : _a.call(setters, (v) => v + 1);
2556
2658
  }
2557
2659
  };
2558
2660
 
@@ -7853,13 +7955,9 @@ var import_react16 = __toESM(require("react"));
7853
7955
  var import_fi14 = require("react-icons/fi");
7854
7956
  var QUEST_STATUS_COLORS = {
7855
7957
  "Backlog": "#64748b",
7856
- // Slate (Cinza azulado)
7857
7958
  "In Progress": "#eab308",
7858
- // Yellow (Amarelo)
7859
7959
  "Review": "#a855f7",
7860
- // Purple (Roxo)
7861
7960
  "Done": "#22c55e"
7862
- // Green (Verde)
7863
7961
  };
7864
7962
  function InSceneQuestForm({
7865
7963
  onSave,
@@ -7870,7 +7968,11 @@ function InSceneQuestForm({
7870
7968
  availableNodes = [],
7871
7969
  availableAncestries = [],
7872
7970
  onMentionClick,
7873
- onUploadFile
7971
+ onUploadFile,
7972
+ // NOVAS PROPS PARA O GHOST NODE
7973
+ onNameChange,
7974
+ onColorChange,
7975
+ onSizeChange
7874
7976
  }) {
7875
7977
  const [name, setName] = (0, import_react16.useState)("");
7876
7978
  const [types, setTypes] = (0, import_react16.useState)(["quest"]);
@@ -7879,6 +7981,7 @@ function InSceneQuestForm({
7879
7981
  const [size, setSize] = (0, import_react16.useState)("medium");
7880
7982
  const [intensity, setIntensity] = (0, import_react16.useState)(0);
7881
7983
  const [description, setDescription] = (0, import_react16.useState)("");
7984
+ const [isStatusDropdownOpen, setIsStatusDropdownOpen] = (0, import_react16.useState)(false);
7882
7985
  const [customProps, setCustomProps] = (0, import_react16.useState)([]);
7883
7986
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react16.useState)(false);
7884
7987
  const propsEndRef = (0, import_react16.useRef)(null);
@@ -7897,7 +8000,7 @@ function InSceneQuestForm({
7897
8000
  setCustomProps(newProps);
7898
8001
  };
7899
8002
  const handleAddType = (newType) => {
7900
- const trimmed = newType.trim().toLowerCase();
8003
+ const trimmed = newType.trim();
7901
8004
  if (trimmed && !types.includes(trimmed)) {
7902
8005
  setTypes([...types, trimmed]);
7903
8006
  setTypeInput("");
@@ -7929,7 +8032,6 @@ function InSceneQuestForm({
7929
8032
  name: name.trim(),
7930
8033
  type: types,
7931
8034
  color: QUEST_STATUS_COLORS[status],
7932
- // Cor atrelada ao status
7933
8035
  status,
7934
8036
  size,
7935
8037
  intensity,
@@ -7956,20 +8058,52 @@ function InSceneQuestForm({
7956
8058
  onDoubleClick: swallow
7957
8059
  },
7958
8060
  /* @__PURE__ */ import_react16.default.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS[status]}, transparent)` } }),
7959
- /* @__PURE__ */ import_react16.default.createElement("div", { className: "px-6 pt-5 pb-3" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ import_react16.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Nova Tarefa / Objetivo")), /* @__PURE__ */ import_react16.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Criar Quest")),
7960
- /* @__PURE__ */ import_react16.default.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col max-h-[68vh]" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ import_react16.default.createElement(
7961
- "select",
7962
- {
7963
- value: status,
7964
- onChange: (e) => setStatus(e.target.value),
7965
- 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",
7966
- style: { borderLeft: `4px solid ${QUEST_STATUS_COLORS[status]}` }
8061
+ /* @__PURE__ */ import_react16.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react16.default.createElement("div", null, /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ import_react16.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Nova quest")), /* @__PURE__ */ import_react16.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Criar Quest")), /* @__PURE__ */ import_react16.default.createElement(
8062
+ "button",
8063
+ {
8064
+ type: "button",
8065
+ onClick: onCancel,
8066
+ 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",
8067
+ title: "Fechar"
8068
+ },
8069
+ "\xD7"
8070
+ )),
8071
+ /* @__PURE__ */ import_react16.default.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col max-h-[68vh]" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react16.default.createElement(
8072
+ "button",
8073
+ {
8074
+ type: "button",
8075
+ onClick: () => setIsStatusDropdownOpen(!isStatusDropdownOpen),
8076
+ 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"
8077
+ },
8078
+ /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement("span", { className: "text-slate-200 font-medium" }, status)),
8079
+ /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isStatusDropdownOpen ? "rotate-180" : ""}` })
8080
+ ), isStatusDropdownOpen && /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsStatusDropdownOpen(false) }), /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement(
8081
+ "li",
8082
+ {
8083
+ key: s,
8084
+ onClick: () => {
8085
+ setStatus(s);
8086
+ setIsStatusDropdownOpen(false);
8087
+ onColorChange == null ? void 0 : onColorChange(QUEST_STATUS_COLORS[s]);
8088
+ },
8089
+ 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"}`
7967
8090
  },
7968
- /* @__PURE__ */ import_react16.default.createElement("option", { value: "Backlog" }, "Backlog"),
7969
- /* @__PURE__ */ import_react16.default.createElement("option", { value: "In Progress" }, "In Progress"),
7970
- /* @__PURE__ */ import_react16.default.createElement("option", { value: "Review" }, "Review"),
7971
- /* @__PURE__ */ import_react16.default.createElement("option", { value: "Done" }, "Done")
7972
- )), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Quest"), /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 focus-within:ring-2 focus-within:ring-indigo-400/60 transition-all" }, types.map((t, index) => /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ import_react16.default.createElement(FiX, { size: 12 })))), /* @__PURE__ */ import_react16.default.createElement(
8091
+ /* @__PURE__ */ import_react16.default.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS[s] } }),
8092
+ s
8093
+ )))))), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Quest"), /* @__PURE__ */ import_react16.default.createElement(
8094
+ "input",
8095
+ {
8096
+ required: true,
8097
+ type: "text",
8098
+ placeholder: "Ex.: Refatorar M\xF3dulo X",
8099
+ value: name,
8100
+ onChange: (e) => {
8101
+ setName(e.target.value);
8102
+ onNameChange == null ? void 0 : onNameChange(e.target.value);
8103
+ },
8104
+ 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"
8105
+ }
8106
+ )), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 focus-within:ring-2 focus-within:ring-indigo-400/60 transition-all" }, types.map((t, index) => /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiX, { size: 12 })))), /* @__PURE__ */ import_react16.default.createElement(
7973
8107
  "input",
7974
8108
  {
7975
8109
  type: "text",
@@ -7980,7 +8114,6 @@ function InSceneQuestForm({
7980
8114
  if (typeInput.trim()) handleAddType(typeInput);
7981
8115
  },
7982
8116
  className: "flex-1 bg-transparent text-sm min-w-[80px] focus:outline-none text-slate-200",
7983
- placeholder: "Ex.: Bugfix",
7984
8117
  autoComplete: "off"
7985
8118
  }
7986
8119
  ))), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o (Opcional)"), /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement(
@@ -7993,7 +8126,20 @@ function InSceneQuestForm({
7993
8126
  onMentionClick,
7994
8127
  onSaveDescription: (newDesc) => setDescription(newDesc)
7995
8128
  }
7996
- ), /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement("button", { type: "button", onClick: () => setIsDescriptionModalOpen(true), className: "p-2 text-slate-400 hover:text-white hover:bg-white/10 transition-colors" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiEdit2, { size: 14 }))), !description && /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Tamanho no Cen\xE1rio (Size)"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex items-center gap-5" }, ["small", "medium", "large"].map((s) => /* @__PURE__ */ import_react16.default.createElement("button", { key: s, type: "button", onClick: () => setSize(s), className: "flex items-center gap-2 group cursor-pointer focus:outline-none" }, /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement(import_fi14.FiCheck, { size: 12, className: "text-white" })), /* @__PURE__ */ import_react16.default.createElement("span", { className: `text-sm capitalize transition-colors ${size === s ? "text-white font-medium" : "text-slate-400 group-hover:text-slate-300"}` }, s))))), /* @__PURE__ */ import_react16.default.createElement("div", { className: "pt-2" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex items-center justify-between mb-2" }, /* @__PURE__ */ import_react16.default.createElement("h3", { className: "text-sm font-medium" }, "Propriedades Adicionais"), /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement(import_fi14.FiPlus, { size: 14 }), " Adicionar")), /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex flex-col gap-3" }, customProps.map((prop, index) => /* @__PURE__ */ import_react16.default.createElement(
8129
+ ), /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement("button", { type: "button", onClick: () => setIsDescriptionModalOpen(true), className: "p-2 text-slate-400 hover:text-white hover:bg-white/10 transition-colors" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiEdit2, { size: 14 }))), !description && /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Tamanho no Node (Size)"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex items-center gap-5" }, ["small", "medium", "large"].map((s) => /* @__PURE__ */ import_react16.default.createElement(
8130
+ "button",
8131
+ {
8132
+ key: s,
8133
+ type: "button",
8134
+ onClick: () => {
8135
+ setSize(s);
8136
+ onSizeChange == null ? void 0 : onSizeChange(s);
8137
+ },
8138
+ className: "flex items-center gap-2 group cursor-pointer focus:outline-none"
8139
+ },
8140
+ /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement(import_fi14.FiCheck, { size: 12, className: "text-white" })),
8141
+ /* @__PURE__ */ import_react16.default.createElement("span", { className: `text-sm capitalize transition-colors ${size === s ? "text-white font-medium" : "text-slate-400 group-hover:text-slate-300"}` }, s)
8142
+ )))), /* @__PURE__ */ import_react16.default.createElement("div", { className: "pt-2" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex items-center justify-between mb-2" }, /* @__PURE__ */ import_react16.default.createElement("h3", { className: "text-sm font-medium" }, "Propriedades Adicionais"), /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.createElement(import_fi14.FiPlus, { size: 14 }), " Adicionar")), /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex flex-col gap-3" }, customProps.map((prop, index) => /* @__PURE__ */ import_react16.default.createElement(
7997
8143
  CustomPropertyDisplay,
7998
8144
  {
7999
8145
  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 = (0, import_react25.useCallback)(() => {
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: import_short_uuid2.default.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_${import_short_uuid2.default.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 = (0, import_react25.useCallback)((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 = (0, import_react25.useMemo)(() => {
@@ -12832,6 +13012,7 @@ function XViewScene({
12832
13012
  [actionHandlerContext]
12833
13013
  );
12834
13014
  const handleSaveCurrentView = (0, import_react25.useCallback)(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 = (0, import_react25.useMemo)(() => {
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
+ (0, import_react25.useEffect)(() => {
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__ */ import_react25.default.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) => {