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

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 +481 -176
  2. package/dist/index.mjs +488 -183
  3. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1181,8 +1181,6 @@ var createMultipleLinkLines = (linksArray, sourceNodeMesh, targetNodeMesh, resol
1181
1181
  targetNodeMesh,
1182
1182
  resolution,
1183
1183
  isCurved,
1184
- isCurved,
1185
- isCurved,
1186
1184
  curveOffset
1187
1185
  );
1188
1186
  line.userData = {
@@ -1515,7 +1513,7 @@ var addStandaloneNodeToScene = (state, nodeData, position) => {
1515
1513
  scaleTween.start();
1516
1514
  }
1517
1515
  };
1518
- var getParentFileInfoForNode = (allParentData, sceneData, nodeId) => {
1516
+ var getParentFileInfoForNode = (allParentData, sceneData, nodeId, sceneConfigId = null, sceneOwnerId = null) => {
1519
1517
  const parentDbsArray = (sceneData == null ? void 0 : sceneData.parent_dbs) || [];
1520
1518
  for (const parentFileId in allParentData) {
1521
1519
  if (allParentData.hasOwnProperty(parentFileId)) {
@@ -1524,6 +1522,8 @@ var getParentFileInfoForNode = (allParentData, sceneData, nodeId) => {
1524
1522
  const parentDbInfo = parentDbsArray.find((db) => String(db.db_id) === String(parentFileId));
1525
1523
  if (parentDbInfo) {
1526
1524
  return { parentFileId, ownerId: parentDbInfo.owner_id };
1525
+ } else if (sceneConfigId && String(parentFileId) === String(sceneConfigId)) {
1526
+ return { parentFileId, ownerId: sceneOwnerId };
1527
1527
  } else {
1528
1528
  console.warn(`Owner ID n\xE3o encontrado em sceneData.parent_dbs para o parentFileId: ${parentFileId}`);
1529
1529
  return { parentFileId, ownerId: null };
@@ -1793,6 +1793,7 @@ var userActionHandlers = {
1793
1793
  setters.setFormPosition((p) => ({ ...p, opacity: 0 }));
1794
1794
  },
1795
1795
  handleSaveVersionNode: async (context, newNodeData) => {
1796
+ var _a;
1796
1797
  const { graphDataRef, sceneDataRef, stateRef, versionMode, setters } = context;
1797
1798
  if (!graphDataRef.current || !sceneDataRef.current) return;
1798
1799
  const { sourceNodeData } = versionMode;
@@ -1802,7 +1803,7 @@ var userActionHandlers = {
1802
1803
  version_node: { is_version: true, parent_node: sourceNodeData.id }
1803
1804
  };
1804
1805
  const newLink = { id: `link_${short.generate()}`, source: sourceNodeData.id, target: newNode.id };
1805
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id);
1806
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id, context.sceneConfigId, context.ownerId);
1806
1807
  if (!parentInfo || !parentInfo.ownerId) {
1807
1808
  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
1809
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio.");
@@ -1812,9 +1813,21 @@ var userActionHandlers = {
1812
1813
  const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileId]));
1813
1814
  specificParentData.nodes.push(newNode);
1814
1815
  specificParentData.links.push(newLink);
1815
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
1816
1816
  try {
1817
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
1817
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
1818
+ if (isView && parentFileId === context.sceneConfigId) {
1819
+ const viewFilePayload = {
1820
+ parent_dbs: sceneDataRef.current.parent_dbs,
1821
+ nodes: sceneDataRef.current.nodes,
1822
+ links: sceneDataRef.current.links,
1823
+ quest_nodes: specificParentData.nodes,
1824
+ quest_links: specificParentData.links
1825
+ };
1826
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
1827
+ } else {
1828
+ const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
1829
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
1830
+ }
1818
1831
  graphDataRef.current[parentFileId] = specificParentData;
1819
1832
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1820
1833
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
@@ -1838,10 +1851,6 @@ var userActionHandlers = {
1838
1851
  var _a;
1839
1852
  const isSource = String(link.source) === String(sourceNode.id);
1840
1853
  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
1854
  if (!nodeObjects[String(targetNodeId)]) {
1846
1855
  const allParentNodes = Object.values(graphDataRef.current).flatMap((fileData) => fileData.nodes);
1847
1856
  const nodeData = allParentNodes.find((n) => String(n.id) === String(targetNodeId));
@@ -1849,9 +1858,6 @@ var userActionHandlers = {
1849
1858
  console.warn(`Dados do Node com ID ${targetNodeId} n\xE3o encontrados no cache.`);
1850
1859
  return;
1851
1860
  }
1852
- if (!sceneDataRef.current.nodes.some((n) => String(n.id) === String(nodeData.id))) {
1853
- sceneDataRef.current.nodes.push(nodeData);
1854
- }
1855
1861
  const startPosition = sourceNodeMesh.position.clone();
1856
1862
  const endPosition = startPosition.clone().add(
1857
1863
  new THREE.Vector3((Math.random() - 0.5) * 60, (Math.random() - 0.5) * 15, (Math.random() - 0.5) * 60)
@@ -1971,6 +1977,7 @@ var userActionHandlers = {
1971
1977
  if (mountRef.current) mountRef.current.style.cursor = "grab";
1972
1978
  },
1973
1979
  handleCompleteConnection: async (context, targetNodeData) => {
1980
+ var _a;
1974
1981
  const { stateRef, graphDataRef, sceneDataRef } = context;
1975
1982
  const { sourceNodeData } = stateRef.current.connection;
1976
1983
  if (!graphDataRef.current || !sceneDataRef.current || !sourceNodeData || !targetNodeData) {
@@ -1978,7 +1985,7 @@ var userActionHandlers = {
1978
1985
  userActionHandlers.handleCancelConnection(context);
1979
1986
  return;
1980
1987
  }
1981
- const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id);
1988
+ const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id, context.sceneConfigId, context.ownerId);
1982
1989
  if (!sourceParentInfo || !sourceParentInfo.ownerId) {
1983
1990
  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
1991
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio.");
@@ -1993,12 +2000,26 @@ var userActionHandlers = {
1993
2000
  source: sourceNodeData.id,
1994
2001
  target: targetNodeData.id
1995
2002
  };
1996
- const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
1997
- specificParentData.links.push(newLink);
1998
- const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
1999
2003
  try {
2000
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2001
- graphDataRef.current[parentFileIdToSave] = specificParentData;
2004
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2005
+ if (isView && parentFileIdToSave === context.sceneConfigId) {
2006
+ const specificParentData = graphDataRef.current[context.sceneConfigId];
2007
+ specificParentData.links.push(newLink);
2008
+ const viewFilePayload = {
2009
+ parent_dbs: sceneDataRef.current.parent_dbs,
2010
+ nodes: sceneDataRef.current.nodes,
2011
+ links: sceneDataRef.current.links,
2012
+ quest_nodes: specificParentData.nodes,
2013
+ quest_links: specificParentData.links
2014
+ };
2015
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2016
+ } else {
2017
+ const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
2018
+ specificParentData.links.push(newLink);
2019
+ const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
2020
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2021
+ graphDataRef.current[parentFileIdToSave] = specificParentData;
2022
+ }
2002
2023
  addNewLinkToScene(stateRef.current, newLink);
2003
2024
  } catch (error) {
2004
2025
  console.error("Falha ao salvar a nova conex\xE3o:", error);
@@ -2067,14 +2088,28 @@ var userActionHandlers = {
2067
2088
  } else {
2068
2089
  newTargetId = newEndNodeData.id;
2069
2090
  }
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.");
2091
+ const oldSourceInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, oldSourceId, context.sceneConfigId, context.ownerId);
2092
+ const oldTargetInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, oldTargetId, context.sceneConfigId, context.ownerId);
2093
+ const newSourceInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, newSourceId, context.sceneConfigId, context.ownerId);
2094
+ const newTargetInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, newTargetId, context.sceneConfigId, context.ownerId);
2095
+ if (!oldSourceInfo || !oldTargetInfo || !newSourceInfo || !newTargetInfo) {
2096
+ console.error("Informa\xE7\xF5es dos arquivos pai incompletas para o relink.");
2074
2097
  alert("Ocorreu um erro ao identificar os arquivos pai para salvar a altera\xE7\xE3o.");
2075
2098
  userActionHandlers.handleCancelRelink(context);
2076
2099
  return;
2077
2100
  }
2101
+ let oldGoverningFileId = oldSourceInfo.parentFileId;
2102
+ let oldGoverningOwnerId = oldSourceInfo.ownerId;
2103
+ if (oldSourceInfo.parentFileId === context.sceneConfigId || oldTargetInfo.parentFileId === context.sceneConfigId) {
2104
+ oldGoverningFileId = context.sceneConfigId;
2105
+ oldGoverningOwnerId = context.ownerId;
2106
+ }
2107
+ let newGoverningFileId = newSourceInfo.parentFileId;
2108
+ let newGoverningOwnerId = newSourceInfo.ownerId;
2109
+ if (newSourceInfo.parentFileId === context.sceneConfigId || newTargetInfo.parentFileId === context.sceneConfigId) {
2110
+ newGoverningFileId = context.sceneConfigId;
2111
+ newGoverningOwnerId = context.ownerId;
2112
+ }
2078
2113
  const { sourceNode, targetNode, ...dataToKeep } = originalLinkData;
2079
2114
  const newLinkData = {
2080
2115
  ...dataToKeep,
@@ -2082,34 +2117,44 @@ var userActionHandlers = {
2082
2117
  target: newTargetId,
2083
2118
  id: linkId
2084
2119
  };
2120
+ const saveParentData = async (fileId, fileOwnerId, data) => {
2121
+ var _a;
2122
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2123
+ if (isView && fileId === context.sceneConfigId) {
2124
+ const viewFilePayload = {
2125
+ parent_dbs: sceneDataRef.current.parent_dbs,
2126
+ nodes: sceneDataRef.current.nodes,
2127
+ links: sceneDataRef.current.links,
2128
+ quest_nodes: data.nodes,
2129
+ quest_links: data.links
2130
+ };
2131
+ return context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2132
+ } else {
2133
+ return context.actions.save_view_data(`x_view_dbs/${fileOwnerId}/${fileId}`, data);
2134
+ }
2135
+ };
2085
2136
  const savePromises = [];
2086
2137
  const filesToUpdate = {};
2087
- if (originalParentInfo.parentFileId !== newParentInfo.parentFileId) {
2088
- const updatedOriginalParentData = JSON.parse(JSON.stringify(graphDataRef.current[originalParentInfo.parentFileId]));
2138
+ if (oldGoverningFileId !== newGoverningFileId) {
2139
+ const updatedOriginalParentData = JSON.parse(JSON.stringify(graphDataRef.current[oldGoverningFileId]));
2089
2140
  updatedOriginalParentData.links = updatedOriginalParentData.links.filter(
2090
2141
  (l) => String(l.id) !== String(linkId)
2091
2142
  );
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]));
2143
+ filesToUpdate[oldGoverningFileId] = updatedOriginalParentData;
2144
+ savePromises.push(saveParentData(oldGoverningFileId, oldGoverningOwnerId, updatedOriginalParentData));
2145
+ const updatedNewParentData = JSON.parse(JSON.stringify(graphDataRef.current[newGoverningFileId]));
2097
2146
  if (!updatedNewParentData.links) updatedNewParentData.links = [];
2098
2147
  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
- );
2148
+ filesToUpdate[newGoverningFileId] = updatedNewParentData;
2149
+ savePromises.push(saveParentData(newGoverningFileId, newGoverningOwnerId, updatedNewParentData));
2103
2150
  } else {
2104
- const updatedParentData = JSON.parse(JSON.stringify(graphDataRef.current[originalParentInfo.parentFileId]));
2151
+ const updatedParentData = JSON.parse(JSON.stringify(graphDataRef.current[oldGoverningFileId]));
2105
2152
  updatedParentData.links = updatedParentData.links.filter(
2106
2153
  (l) => String(l.id) !== String(linkId)
2107
2154
  );
2108
2155
  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
- );
2156
+ filesToUpdate[oldGoverningFileId] = updatedParentData;
2157
+ savePromises.push(saveParentData(oldGoverningFileId, oldGoverningOwnerId, updatedParentData));
2113
2158
  }
2114
2159
  try {
2115
2160
  await Promise.all(savePromises);
@@ -2127,7 +2172,7 @@ var userActionHandlers = {
2127
2172
  }
2128
2173
  },
2129
2174
  handleDeleteLink: async (context, linkObject) => {
2130
- var _a, _b, _c, _d, _e, _f;
2175
+ var _a, _b, _c, _d, _e, _f, _g;
2131
2176
  const { stateRef, graphDataRef, sceneDataRef, setters } = context;
2132
2177
  setters.setRelationshipMenu({ visible: false });
2133
2178
  if (!(linkObject == null ? void 0 : linkObject.userData) || !graphDataRef.current || !sceneDataRef.current) return;
@@ -2136,7 +2181,7 @@ var userActionHandlers = {
2136
2181
  console.error("Tentativa de deletar um link sem ID.", linkObject.userData);
2137
2182
  return;
2138
2183
  }
2139
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkObject.userData.source);
2184
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkObject.userData.source, context.sceneConfigId, context.ownerId);
2140
2185
  if (!parentInfo || !parentInfo.ownerId) {
2141
2186
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o link:", linkIdToDelete);
2142
2187
  alert("Ocorreu um erro ao identificar o arquivo pai da rela\xE7\xE3o para exclus\xE3o.");
@@ -2148,9 +2193,25 @@ var userActionHandlers = {
2148
2193
  const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileId]));
2149
2194
  const newLinks = (specificParentData.links || []).filter((l) => String(l.id) !== String(linkIdToDelete));
2150
2195
  specificParentData.links = newLinks;
2151
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2196
+ let filenameToSave;
2197
+ let payloadToSave;
2198
+ const isView = ((_g = context.viewType) == null ? void 0 : _g.toLowerCase()) === "view";
2199
+ if (isView && parentFileId === context.sceneConfigId) {
2200
+ filenameToSave = context.sceneSaveUrl;
2201
+ sceneDataRef.current.links = sceneDataRef.current.links.filter((l) => String(l.id) !== String(linkIdToDelete));
2202
+ payloadToSave = {
2203
+ parent_dbs: sceneDataRef.current.parent_dbs,
2204
+ nodes: sceneDataRef.current.nodes,
2205
+ links: sceneDataRef.current.links,
2206
+ quest_nodes: specificParentData.nodes,
2207
+ quest_links: specificParentData.links
2208
+ };
2209
+ } else {
2210
+ filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2211
+ payloadToSave = specificParentData;
2212
+ }
2152
2213
  try {
2153
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2214
+ await context.actions.save_view_data(filenameToSave, payloadToSave);
2154
2215
  graphDataRef.current[parentFileId] = specificParentData;
2155
2216
  setters.setDetailsLink((prev) => String(prev == null ? void 0 : prev.id) === String(linkIdToDelete) ? null : prev);
2156
2217
  if (stateRef.current.hoveredLink === linkObject) {
@@ -2168,12 +2229,6 @@ var userActionHandlers = {
2168
2229
  if (!nodeData || !sceneDataRef.current) return;
2169
2230
  const nodeIdToDismiss = nodeData.id;
2170
2231
  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
2232
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2178
2233
  if (ancestryGroup && ancestryLinks) {
2179
2234
  const remainingAncestryLinks = [];
@@ -2215,22 +2270,12 @@ var userActionHandlers = {
2215
2270
  removeNodeFromScene(stateRef.current, nodeId);
2216
2271
  setters.setDetailsNode((prev) => String(prev == null ? void 0 : prev.id) === String(nodeId) ? null : prev);
2217
2272
  });
2218
- sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter(
2219
- (n) => String(n.id) === strNodeIdToKeep
2220
- );
2221
- sceneDataRef.current.links = [];
2222
2273
  },
2223
2274
  handleDismissMultipleNodes: (context, nodeIds) => {
2224
2275
  const { stateRef, sceneDataRef, setters } = context;
2225
2276
  setters.setMultiContextMenu({ visible: false });
2226
2277
  if (!nodeIds || nodeIds.size === 0 || !sceneDataRef.current) return;
2227
2278
  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
2279
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2235
2280
  if (ancestryGroup && ancestryLinks) {
2236
2281
  const remainingAncestryLinks = [];
@@ -2286,15 +2331,10 @@ var userActionHandlers = {
2286
2331
  removeNodeFromScene(stateRef.current, nodeId);
2287
2332
  setters.setDetailsNode((prev) => String(prev == null ? void 0 : prev.id) === String(nodeId) ? null : prev);
2288
2333
  });
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
2334
  stateRef.current.selectedNodes.clear();
2296
2335
  },
2297
2336
  handleDeleteMultipleNodes: async (context, nodeIds) => {
2337
+ var _a;
2298
2338
  const { stateRef, graphDataRef, sceneDataRef, setters, actions } = context;
2299
2339
  setters.setMultiContextMenu({ visible: false });
2300
2340
  if (!nodeIds || nodeIds.size === 0 || !graphDataRef.current || !sceneDataRef.current) return;
@@ -2313,7 +2353,7 @@ var userActionHandlers = {
2313
2353
  }
2314
2354
  const changesByParentFile = {};
2315
2355
  for (const nodeId of strNodeIdsToDelete) {
2316
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeId);
2356
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeId, context.sceneConfigId, context.ownerId);
2317
2357
  if (!parentInfo || !parentInfo.ownerId) {
2318
2358
  console.warn(`Node com ID ${nodeId} n\xE3o encontrado ou sem ownerId. Ignorando.`);
2319
2359
  continue;
@@ -2347,8 +2387,27 @@ var userActionHandlers = {
2347
2387
  originalData.links = (originalData.links || []).filter(
2348
2388
  (l) => !linksToDelete.has(String(l.id))
2349
2389
  );
2350
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2351
- savePromises.push(context.actions.save_view_data(filenameForSpecificParent, originalData));
2390
+ let filenameToSave;
2391
+ let payloadToSave;
2392
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2393
+ if (isView && parentFileId === context.sceneConfigId) {
2394
+ filenameToSave = context.sceneSaveUrl;
2395
+ const strNodesToDelete = Array.from(nodesToDelete).map(String);
2396
+ const strLinksToDelete = Array.from(linksToDelete).map(String);
2397
+ sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter((n) => !strNodesToDelete.includes(String(n.id)));
2398
+ sceneDataRef.current.links = sceneDataRef.current.links.filter((l) => !strLinksToDelete.includes(String(l.id)));
2399
+ payloadToSave = {
2400
+ parent_dbs: sceneDataRef.current.parent_dbs,
2401
+ nodes: sceneDataRef.current.nodes,
2402
+ links: sceneDataRef.current.links,
2403
+ quest_nodes: originalData.nodes,
2404
+ quest_links: originalData.links
2405
+ };
2406
+ } else {
2407
+ filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2408
+ payloadToSave = originalData;
2409
+ }
2410
+ savePromises.push(context.actions.save_view_data(filenameToSave, payloadToSave));
2352
2411
  updatedParentDataCache[parentFileId] = originalData;
2353
2412
  }
2354
2413
  }
@@ -2369,6 +2428,7 @@ var userActionHandlers = {
2369
2428
  }
2370
2429
  },
2371
2430
  handleDeleteNode: async (context, nodeData) => {
2431
+ var _a;
2372
2432
  const { stateRef, graphDataRef, sceneDataRef, setters, actions } = context;
2373
2433
  if (actions.delete_file && nodeData) {
2374
2434
  const urls = extractFileUrlsFromProperties(nodeData);
@@ -2380,7 +2440,7 @@ var userActionHandlers = {
2380
2440
  if (!nodeData || !graphDataRef.current || !sceneDataRef.current) return;
2381
2441
  const nodeIdToDelete = nodeData.id;
2382
2442
  const strNodeId = String(nodeIdToDelete);
2383
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeIdToDelete);
2443
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeIdToDelete, context.sceneConfigId, context.ownerId);
2384
2444
  if (!parentInfo || !parentInfo.ownerId) {
2385
2445
  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
2446
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio para exclus\xE3o.");
@@ -2392,9 +2452,28 @@ var userActionHandlers = {
2392
2452
  const newLinks = (specificParentData.links || []).filter((l) => String(l.source) !== strNodeId && String(l.target) !== strNodeId);
2393
2453
  specificParentData.nodes = newNodes;
2394
2454
  specificParentData.links = newLinks;
2395
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2455
+ let filenameToSave;
2456
+ let payloadToSave;
2457
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2458
+ if (isView && parentFileId === context.sceneConfigId) {
2459
+ filenameToSave = context.sceneSaveUrl;
2460
+ const newVisualNodes = sceneDataRef.current.nodes.filter((n) => String(n.id) !== strNodeId);
2461
+ const newVisualLinks = sceneDataRef.current.links.filter((l) => String(l.source) !== strNodeId && String(l.target) !== strNodeId);
2462
+ sceneDataRef.current.nodes = newVisualNodes;
2463
+ sceneDataRef.current.links = newVisualLinks;
2464
+ payloadToSave = {
2465
+ parent_dbs: sceneDataRef.current.parent_dbs,
2466
+ nodes: newVisualNodes,
2467
+ links: newVisualLinks,
2468
+ quest_nodes: specificParentData.nodes,
2469
+ quest_links: specificParentData.links
2470
+ };
2471
+ } else {
2472
+ filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2473
+ payloadToSave = specificParentData;
2474
+ }
2396
2475
  try {
2397
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2476
+ await context.actions.save_view_data(filenameToSave, payloadToSave);
2398
2477
  graphDataRef.current[parentFileId] = specificParentData;
2399
2478
  setters.setDetailsNode((prev) => String(prev == null ? void 0 : prev.id) === String(nodeIdToDelete) ? null : prev);
2400
2479
  removeNodeFromScene(stateRef.current, nodeIdToDelete);
@@ -2405,10 +2484,11 @@ var userActionHandlers = {
2405
2484
  }
2406
2485
  },
2407
2486
  handleSaveNodeDetails: async (context, updatedNode, keepOpen = false) => {
2487
+ var _a;
2408
2488
  const { graphDataRef, sceneDataRef, stateRef, setters } = context;
2409
2489
  if (!graphDataRef.current || !sceneDataRef.current) return;
2410
2490
  const { _baseEmissiveIntensity: ignored, ...nodeToSave } = updatedNode;
2411
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeToSave.id);
2491
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeToSave.id, context.sceneConfigId, context.ownerId);
2412
2492
  if (!parentInfo || !parentInfo.ownerId) {
2413
2493
  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
2494
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio para atualiza\xE7\xE3o.");
@@ -2424,9 +2504,21 @@ var userActionHandlers = {
2424
2504
  alert("Erro interno: Node n\xE3o encontrado para salvar.");
2425
2505
  return;
2426
2506
  }
2427
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2428
2507
  try {
2429
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2508
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2509
+ if (isView && parentFileId === context.sceneConfigId) {
2510
+ const viewFilePayload = {
2511
+ parent_dbs: sceneDataRef.current.parent_dbs,
2512
+ nodes: sceneDataRef.current.nodes,
2513
+ links: sceneDataRef.current.links,
2514
+ quest_nodes: specificParentData.nodes,
2515
+ quest_links: specificParentData.links
2516
+ };
2517
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2518
+ } else {
2519
+ const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2520
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2521
+ }
2430
2522
  graphDataRef.current[parentFileId] = specificParentData;
2431
2523
  updateExistingNodeVisuals(stateRef.current, nodeToSave);
2432
2524
  setters.setSceneVersion((v) => v + 1);
@@ -2439,10 +2531,11 @@ var userActionHandlers = {
2439
2531
  }
2440
2532
  },
2441
2533
  handleSaveLinkDetails: async (context, updatedLink, keepOpen = false) => {
2534
+ var _a;
2442
2535
  const { graphDataRef, sceneDataRef, stateRef, setters } = context;
2443
2536
  if (!graphDataRef.current || !sceneDataRef.current) return;
2444
2537
  const { sourceNode, targetNode, ...linkToSave } = updatedLink;
2445
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkToSave.source);
2538
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkToSave.source, context.sceneConfigId, context.ownerId);
2446
2539
  if (!parentInfo || !parentInfo.ownerId) {
2447
2540
  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
2541
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio para atualiza\xE7\xE3o.");
@@ -2458,9 +2551,21 @@ var userActionHandlers = {
2458
2551
  alert("Erro interno: link n\xE3o encontrado para salvar.");
2459
2552
  return;
2460
2553
  }
2461
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2462
2554
  try {
2463
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2555
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2556
+ if (isView && parentFileId === context.sceneConfigId) {
2557
+ const viewFilePayload = {
2558
+ parent_dbs: sceneDataRef.current.parent_dbs,
2559
+ nodes: sceneDataRef.current.nodes,
2560
+ links: sceneDataRef.current.links,
2561
+ quest_nodes: specificParentData.nodes,
2562
+ quest_links: specificParentData.links
2563
+ };
2564
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2565
+ } else {
2566
+ const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2567
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2568
+ }
2464
2569
  graphDataRef.current[parentFileId] = specificParentData;
2465
2570
  const lineObject = stateRef.current.allLinks.find(
2466
2571
  (l) => String(l.userData.id) === String(linkToSave.id)
@@ -2483,7 +2588,7 @@ var userActionHandlers = {
2483
2588
  }
2484
2589
  },
2485
2590
  handleAddExistingNodeById: (context, nodeId) => {
2486
- var _a, _b;
2591
+ var _a;
2487
2592
  const { stateRef, sceneDataRef, graphDataRef, tweenToTarget, setters } = context;
2488
2593
  const state = stateRef.current;
2489
2594
  const graphFull = graphDataRef.current;
@@ -2499,16 +2604,12 @@ var userActionHandlers = {
2499
2604
  tweenToTarget(state.nodeObjects[strNodeId]);
2500
2605
  return;
2501
2606
  }
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
2607
  const base = state.controls ? state.controls.target.clone() : new THREE.Vector3(0, 0, 0);
2507
2608
  const offset = new THREE.Vector3((Math.random() - 0.5) * 20, (Math.random() - 0.5) * 6, (Math.random() - 0.5) * 20);
2508
2609
  const position = base.add(offset);
2509
2610
  addStandaloneNodeToScene(state, nodeData, position);
2510
2611
  tweenToTarget(position, 1.3);
2511
- (_b = setters == null ? void 0 : setters.setSceneVersion) == null ? void 0 : _b.call(setters, (v) => v + 1);
2612
+ (_a = setters == null ? void 0 : setters.setSceneVersion) == null ? void 0 : _a.call(setters, (v) => v + 1);
2512
2613
  }
2513
2614
  };
2514
2615
 
@@ -3097,7 +3198,7 @@ function useResizablePanel({ initialWidth, minWidth, maxWidth }) {
3097
3198
 
3098
3199
  // src/components/CustomPropertyDisplay.jsx
3099
3200
  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";
3201
+ import { FiCheck, FiX, FiEdit3, FiTrash2, FiExternalLink, FiFileText, FiChevronDown, FiUpload, FiLoader } from "react-icons/fi";
3101
3202
  function CustomPropertyDisplay({
3102
3203
  prop,
3103
3204
  onUpdate,
@@ -3388,7 +3489,7 @@ function CustomPropertyDisplay({
3388
3489
  default:
3389
3490
  return /* @__PURE__ */ React3.createElement("input", { type: "text", placeholder: "Valor", value: tempProp.value, onChange: (e) => handlePropChange("value", e.target.value), className: baseInput });
3390
3491
  }
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" }))));
3492
+ })()), /* @__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
3493
  };
3393
3494
  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
3495
  switch (prop.type) {
@@ -7024,7 +7125,7 @@ function CreateAncestryPanel({
7024
7125
 
7025
7126
  // src/components/ImageViewer.jsx
7026
7127
  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";
7128
+ import { FiX as FiX2, FiChevronLeft as FiChevronLeft3, FiChevronRight as FiChevronRight5 } from "react-icons/fi";
7028
7129
  function ImageViewer({ data, onClose }) {
7029
7130
  var _a;
7030
7131
  const { images = [], startIndex = 0, visible } = data;
@@ -7109,7 +7210,7 @@ function ImageViewer({ data, onClose }) {
7109
7210
  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
7211
  "aria-label": "Fechar"
7111
7212
  },
7112
- /* @__PURE__ */ React11.createElement(FiX3, null)
7213
+ /* @__PURE__ */ React11.createElement(FiX2, null)
7113
7214
  ),
7114
7215
  /* @__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
7216
  "img",
@@ -7245,7 +7346,7 @@ function ColorPicker({ color, onChange, disabled }) {
7245
7346
  }
7246
7347
 
7247
7348
  // 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";
7349
+ import { FiPlus as FiPlus3, FiMaximize2, FiX as FiX3, FiCheck as FiCheck7, FiEdit2 as FiEdit24, FiSun, FiChevronDown as FiChevronDown4 } from "react-icons/fi";
7249
7350
  function InSceneCreationForm({
7250
7351
  onSave,
7251
7352
  onCancel,
@@ -7463,7 +7564,7 @@ function InSceneCreationForm({
7463
7564
  onClick: () => handleRemoveType(index),
7464
7565
  className: "hover:text-white transition-colors"
7465
7566
  },
7466
- /* @__PURE__ */ React13.createElement(FiX4, { size: 12 })
7567
+ /* @__PURE__ */ React13.createElement(FiX3, { size: 12 })
7467
7568
  ))), /* @__PURE__ */ React13.createElement(
7468
7569
  "input",
7469
7570
  {
@@ -7837,16 +7938,12 @@ function InSceneVersionForm({
7837
7938
 
7838
7939
  // src/components/InSceneQuestForm.jsx
7839
7940
  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";
7941
+ import { FiPlus as FiPlus5, FiCheck as FiCheck9, FiEdit2 as FiEdit26, FiTarget, FiX as FiX4, FiChevronDown as FiChevronDown5 } from "react-icons/fi";
7841
7942
  var QUEST_STATUS_COLORS = {
7842
7943
  "Backlog": "#64748b",
7843
- // Slate (Cinza azulado)
7844
7944
  "In Progress": "#eab308",
7845
- // Yellow (Amarelo)
7846
7945
  "Review": "#a855f7",
7847
- // Purple (Roxo)
7848
7946
  "Done": "#22c55e"
7849
- // Green (Verde)
7850
7947
  };
7851
7948
  function InSceneQuestForm({
7852
7949
  onSave,
@@ -7857,7 +7954,11 @@ function InSceneQuestForm({
7857
7954
  availableNodes = [],
7858
7955
  availableAncestries = [],
7859
7956
  onMentionClick,
7860
- onUploadFile
7957
+ onUploadFile,
7958
+ // NOVAS PROPS PARA O GHOST NODE
7959
+ onNameChange,
7960
+ onColorChange,
7961
+ onSizeChange
7861
7962
  }) {
7862
7963
  const [name, setName] = useState16("");
7863
7964
  const [types, setTypes] = useState16(["quest"]);
@@ -7866,6 +7967,7 @@ function InSceneQuestForm({
7866
7967
  const [size, setSize] = useState16("medium");
7867
7968
  const [intensity, setIntensity] = useState16(0);
7868
7969
  const [description, setDescription] = useState16("");
7970
+ const [isStatusDropdownOpen, setIsStatusDropdownOpen] = useState16(false);
7869
7971
  const [customProps, setCustomProps] = useState16([]);
7870
7972
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState16(false);
7871
7973
  const propsEndRef = useRef12(null);
@@ -7884,7 +7986,7 @@ function InSceneQuestForm({
7884
7986
  setCustomProps(newProps);
7885
7987
  };
7886
7988
  const handleAddType = (newType) => {
7887
- const trimmed = newType.trim().toLowerCase();
7989
+ const trimmed = newType.trim();
7888
7990
  if (trimmed && !types.includes(trimmed)) {
7889
7991
  setTypes([...types, trimmed]);
7890
7992
  setTypeInput("");
@@ -7916,7 +8018,6 @@ function InSceneQuestForm({
7916
8018
  name: name.trim(),
7917
8019
  type: types,
7918
8020
  color: QUEST_STATUS_COLORS[status],
7919
- // Cor atrelada ao status
7920
8021
  status,
7921
8022
  size,
7922
8023
  intensity,
@@ -7943,20 +8044,52 @@ function InSceneQuestForm({
7943
8044
  onDoubleClick: swallow
7944
8045
  },
7945
8046
  /* @__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]}` }
8047
+ /* @__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(
8048
+ "button",
8049
+ {
8050
+ type: "button",
8051
+ onClick: onCancel,
8052
+ 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",
8053
+ title: "Fechar"
7954
8054
  },
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(
8055
+ "\xD7"
8056
+ )),
8057
+ /* @__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(
8058
+ "button",
8059
+ {
8060
+ type: "button",
8061
+ onClick: () => setIsStatusDropdownOpen(!isStatusDropdownOpen),
8062
+ 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"
8063
+ },
8064
+ /* @__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)),
8065
+ /* @__PURE__ */ React15.createElement(FiChevronDown5, { className: `text-slate-400 transition-transform duration-200 ${isStatusDropdownOpen ? "rotate-180" : ""}` })
8066
+ ), 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(
8067
+ "li",
8068
+ {
8069
+ key: s,
8070
+ onClick: () => {
8071
+ setStatus(s);
8072
+ setIsStatusDropdownOpen(false);
8073
+ onColorChange == null ? void 0 : onColorChange(QUEST_STATUS_COLORS[s]);
8074
+ },
8075
+ 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"}`
8076
+ },
8077
+ /* @__PURE__ */ React15.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS[s] } }),
8078
+ s
8079
+ )))))), /* @__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(
8080
+ "input",
8081
+ {
8082
+ required: true,
8083
+ type: "text",
8084
+ placeholder: "Ex.: Refatorar M\xF3dulo X",
8085
+ value: name,
8086
+ onChange: (e) => {
8087
+ setName(e.target.value);
8088
+ onNameChange == null ? void 0 : onNameChange(e.target.value);
8089
+ },
8090
+ 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"
8091
+ }
8092
+ )), /* @__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
8093
  "input",
7961
8094
  {
7962
8095
  type: "text",
@@ -7967,7 +8100,6 @@ function InSceneQuestForm({
7967
8100
  if (typeInput.trim()) handleAddType(typeInput);
7968
8101
  },
7969
8102
  className: "flex-1 bg-transparent text-sm min-w-[80px] focus:outline-none text-slate-200",
7970
- placeholder: "Ex.: Bugfix",
7971
8103
  autoComplete: "off"
7972
8104
  }
7973
8105
  ))), /* @__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 +8112,20 @@ function InSceneQuestForm({
7980
8112
  onMentionClick,
7981
8113
  onSaveDescription: (newDesc) => setDescription(newDesc)
7982
8114
  }
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(
8115
+ ), /* @__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(
8116
+ "button",
8117
+ {
8118
+ key: s,
8119
+ type: "button",
8120
+ onClick: () => {
8121
+ setSize(s);
8122
+ onSizeChange == null ? void 0 : onSizeChange(s);
8123
+ },
8124
+ className: "flex items-center gap-2 group cursor-pointer focus:outline-none"
8125
+ },
8126
+ /* @__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" })),
8127
+ /* @__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)
8128
+ )))), /* @__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
8129
  CustomPropertyDisplay,
7985
8130
  {
7986
8131
  key: prop.id,
@@ -10173,7 +10318,7 @@ function XViewScene({
10173
10318
  });
10174
10319
  }
10175
10320
  return {
10176
- node: effectiveNode || { id: nodeId, name: "Unknown" },
10321
+ ...effectiveNode ? { node: effectiveNode } : { node: { id: nodeId, name: "Unknown" } },
10177
10322
  relationship: treeItem.relationship || {},
10178
10323
  children: (treeItem.children || []).map(recursiveBuild).filter(Boolean),
10179
10324
  parallel_branches: processedBranches
@@ -10872,20 +11017,6 @@ function XViewScene({
10872
11017
  const context = actionHandlerContext;
10873
11018
  if (connection.isActive) {
10874
11019
  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
11020
  await userActionHandlers.handleCompleteConnection(context, hoveredNode.userData);
10890
11021
  } else {
10891
11022
  userActionHandlers.handleCancelConnection(context);
@@ -10991,39 +11122,6 @@ function XViewScene({
10991
11122
  }
10992
11123
  }
10993
11124
  }
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
11125
  function handleDoubleClick(event) {
11028
11126
  if (stateRef.current.camera) stateRef.current.camera.layers.enableAll();
11029
11127
  if (isFromUiOverlay(event) || stateRef.current.isDragging || stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive) return;
@@ -11076,7 +11174,6 @@ function XViewScene({
11076
11174
  currentMount.addEventListener("dblclick", handleDoubleClick);
11077
11175
  currentMount.addEventListener("pointermove", onPointerMove);
11078
11176
  currentMount.addEventListener("contextmenu", handleContextMenu);
11079
- window.addEventListener("keydown", handleKeyDown);
11080
11177
  const originalBackground = scene.background;
11081
11178
  const clock = new THREE3.Clock();
11082
11179
  let animationFrameId = 0;
@@ -11214,7 +11311,6 @@ function XViewScene({
11214
11311
  return () => {
11215
11312
  cancelAnimationFrame(animationFrameId);
11216
11313
  window.removeEventListener("resize", handleResize);
11217
- window.removeEventListener("keydown", handleKeyDown);
11218
11314
  currentMount.removeEventListener("pointerdown", onPointerDown);
11219
11315
  currentMount.removeEventListener("pointerup", onPointerUp);
11220
11316
  currentMount.removeEventListener("dblclick", handleDoubleClick);
@@ -11488,11 +11584,10 @@ function XViewScene({
11488
11584
  creationMode,
11489
11585
  versionMode,
11490
11586
  questMode,
11491
- // <-- Adicionado
11492
11587
  sceneSaveUrl,
11493
- // <-- Adicionado
11588
+ sceneConfigId,
11589
+ ownerId,
11494
11590
  viewType: viewParams == null ? void 0 : viewParams.type,
11495
- // <-- Adicionado
11496
11591
  userId: (_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id,
11497
11592
  setters: {
11498
11593
  setContextMenu,
@@ -11506,7 +11601,6 @@ function XViewScene({
11506
11601
  setSceneVersion,
11507
11602
  setAncestryMode,
11508
11603
  setQuestMode
11509
- // <-- Adicionado
11510
11604
  },
11511
11605
  tweenToTarget,
11512
11606
  handleVersionTimeline,
@@ -11524,9 +11618,11 @@ function XViewScene({
11524
11618
  versionMode,
11525
11619
  questMode,
11526
11620
  sceneSaveUrl,
11621
+ sceneConfigId,
11622
+ ownerId,
11527
11623
  viewParams == null ? void 0 : viewParams.type,
11528
- tweenToTarget,
11529
11624
  (_a = session == null ? void 0 : session.user) == null ? void 0 : _a.id,
11625
+ tweenToTarget,
11530
11626
  handleVersionTimeline,
11531
11627
  save_view_data,
11532
11628
  get_single_parent_file,
@@ -11538,33 +11634,116 @@ function XViewScene({
11538
11634
  const handleStartVersioning = (nodeData) => {
11539
11635
  userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
11540
11636
  };
11637
+ const handleCancelQuest = useCallback4(() => {
11638
+ const { graphGroup, ghostElements } = stateRef.current;
11639
+ if (ghostElements.node && graphGroup) {
11640
+ if (ghostElements.node.userData.labelObject) {
11641
+ graphGroup.remove(ghostElements.node.userData.labelObject);
11642
+ if (ghostElements.node.userData.labelObject.material.map) ghostElements.node.userData.labelObject.material.map.dispose();
11643
+ ghostElements.node.userData.labelObject.material.dispose();
11644
+ }
11645
+ graphGroup.remove(ghostElements.node);
11646
+ ghostElements.node.traverse((child) => {
11647
+ if (child.material) {
11648
+ if (Array.isArray(child.material)) child.material.forEach((m) => m.dispose());
11649
+ else child.material.dispose();
11650
+ }
11651
+ if (child.geometry) child.geometry.dispose();
11652
+ });
11653
+ }
11654
+ stateRef.current.ghostElements = { node: null, line: null, aura: null };
11655
+ setQuestMode({ isActive: false });
11656
+ }, []);
11541
11657
  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;
11658
+ const { graphDataRef, sceneDataRef: sceneDataRef2, stateRef: stateRef2, setters, actions, sceneSaveUrl: sceneSaveUrl2, viewType, sceneConfigId: sceneConfigId2, ownerId: ownerId2 } = context;
11659
+ if (!graphDataRef.current || (viewType == null ? void 0 : viewType.toLowerCase()) !== "view") return;
11544
11660
  const newNode = {
11545
11661
  id: short2.generate(),
11546
11662
  ...newQuestData,
11663
+ is_quest: true,
11547
11664
  type: ["quest", ...newQuestData.type.filter((t) => t !== "quest")]
11548
11665
  };
11549
- const updatedSceneData = {
11550
- ...sceneDataRef2.current,
11551
- nodes: [...sceneDataRef2.current.nodes, newNode]
11666
+ if (!graphDataRef.current[sceneConfigId2]) {
11667
+ graphDataRef.current[sceneConfigId2] = { nodes: [], links: [] };
11668
+ }
11669
+ graphDataRef.current[sceneConfigId2].nodes.push(newNode);
11670
+ const sceneFileData = {
11671
+ parent_dbs: sceneDataRef2.current.parent_dbs,
11672
+ nodes: sceneDataRef2.current.nodes,
11673
+ links: sceneDataRef2.current.links,
11674
+ quest_nodes: graphDataRef.current[sceneConfigId2].nodes,
11675
+ quest_links: graphDataRef.current[sceneConfigId2].links
11552
11676
  };
11553
11677
  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);
11678
+ await actions.save_view_data(sceneSaveUrl2, sceneFileData);
11679
+ stateRef2.current.nodeIdToParentFileMap.set(String(newNode.id), {
11680
+ parentFileId: sceneConfigId2,
11681
+ ownerId: ownerId2,
11682
+ datasetName: "Quests Internas (View)"
11683
+ });
11684
+ const finalPosition = stateRef2.current.ghostElements.node ? stateRef2.current.ghostElements.node.position.clone() : stateRef2.current.controls.target.clone();
11685
+ const { graphGroup, ghostElements } = stateRef2.current;
11686
+ if (ghostElements.node && graphGroup) {
11687
+ if (ghostElements.node.userData.labelObject) graphGroup.remove(ghostElements.node.userData.labelObject);
11688
+ graphGroup.remove(ghostElements.node);
11689
+ }
11690
+ stateRef2.current.ghostElements = { node: null, line: null, aura: null };
11691
+ addStandaloneNodeToScene(stateRef2.current, newNode, finalPosition);
11560
11692
  context.tweenToTarget(finalPosition, 1.2);
11561
11693
  setters.setQuestMode({ isActive: false });
11562
11694
  setters.setSceneVersion((v) => v + 1);
11563
11695
  } catch (error) {
11564
- console.error("Falha ao salvar a Quest na View:", error);
11696
+ console.error("Falha ao salvar Quest na View:", error);
11565
11697
  alert("Ocorreu um erro ao criar a Quest.");
11566
11698
  }
11567
11699
  };
11700
+ userActionHandlers.handleCompleteConnection = async (context, targetNodeData) => {
11701
+ const { stateRef: stateRef2, graphDataRef, sceneDataRef: sceneDataRef2, sceneConfigId: sceneConfigId2, sceneSaveUrl: sceneSaveUrl2, ownerId: ownerId2 } = context;
11702
+ const { sourceNodeData } = stateRef2.current.connection;
11703
+ if (!graphDataRef.current || !sceneDataRef2.current || !sourceNodeData || !targetNodeData) {
11704
+ userActionHandlers.handleCancelConnection(context);
11705
+ return;
11706
+ }
11707
+ const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef2.current, sourceNodeData.id, sceneConfigId2, ownerId2);
11708
+ const targetParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef2.current, targetNodeData.id, sceneConfigId2, ownerId2);
11709
+ let parentInfoToSave = sourceParentInfo;
11710
+ const isSourceQuest = sourceParentInfo.parentFileId === sceneConfigId2;
11711
+ const isTargetQuest = targetParentInfo.parentFileId === sceneConfigId2;
11712
+ if (isSourceQuest || isTargetQuest) {
11713
+ parentInfoToSave = { parentFileId: sceneConfigId2, ownerId: ownerId2 };
11714
+ }
11715
+ const { parentFileId: parentFileIdToSave, ownerId: ownerIdToSave } = parentInfoToSave;
11716
+ const newLink = {
11717
+ id: `link_${short2.generate()}`,
11718
+ source: sourceNodeData.id,
11719
+ target: targetNodeData.id
11720
+ };
11721
+ try {
11722
+ if (parentFileIdToSave === sceneConfigId2) {
11723
+ const specificParentData = graphDataRef.current[sceneConfigId2];
11724
+ specificParentData.links.push(newLink);
11725
+ const viewFilePayload = {
11726
+ parent_dbs: sceneDataRef2.current.parent_dbs,
11727
+ nodes: sceneDataRef2.current.nodes,
11728
+ links: sceneDataRef2.current.links,
11729
+ quest_nodes: specificParentData.nodes,
11730
+ quest_links: specificParentData.links
11731
+ };
11732
+ await context.actions.save_view_data(sceneSaveUrl2, viewFilePayload);
11733
+ } else {
11734
+ const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
11735
+ specificParentData.links.push(newLink);
11736
+ const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
11737
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
11738
+ graphDataRef.current[parentFileIdToSave] = specificParentData;
11739
+ }
11740
+ addNewLinkToScene(stateRef2.current, newLink);
11741
+ } catch (error) {
11742
+ console.error("Falha ao salvar a nova conex\xE3o:", error);
11743
+ alert("Ocorreu um erro ao salvar a nova conex\xE3o.");
11744
+ }
11745
+ userActionHandlers.handleCancelConnection(context);
11746
+ };
11568
11747
  const handleClearAncestryVisuals = useCallback4((ancestryId) => {
11569
11748
  const { renderedAncestries, ancestryGroup } = stateRef.current;
11570
11749
  const renderIndex = renderedAncestries.findIndex((a) => String(a.id) === String(ancestryId));
@@ -12832,6 +13011,7 @@ function XViewScene({
12832
13011
  [actionHandlerContext]
12833
13012
  );
12834
13013
  const handleSaveCurrentView = useCallback4(async () => {
13014
+ var _a2, _b2, _c2;
12835
13015
  const { nodeObjects, allLinks } = stateRef.current;
12836
13016
  if (!nodeObjects || !allLinks || !sceneSaveUrl || !parentDataRef.current) {
12837
13017
  console.warn("N\xE3o \xE9 poss\xEDvel salvar a cena: estado n\xE3o inicializado ou URL de salvamento ausente.");
@@ -12853,17 +13033,22 @@ function XViewScene({
12853
13033
  const { sourceNode, targetNode, ...serializableLinkData } = line.userData;
12854
13034
  return serializableLinkData;
12855
13035
  });
13036
+ sceneDataRef.current.nodes = currentNodes;
13037
+ sceneDataRef.current.links = currentLinks;
13038
+ const isView = ((_a2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _a2.toLowerCase()) === "view";
12856
13039
  const sceneFileData = {
12857
13040
  parent_dbs: sceneDataRef.current.parent_dbs,
12858
13041
  nodes: currentNodes,
12859
- links: currentLinks
13042
+ links: currentLinks,
13043
+ quest_nodes: isView ? ((_b2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _b2.nodes) || [] : sceneDataRef.current.quest_nodes || [],
13044
+ quest_links: isView ? ((_c2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _c2.links) || [] : sceneDataRef.current.quest_links || []
12860
13045
  };
12861
13046
  try {
12862
13047
  await save_view_data(sceneSaveUrl, sceneFileData);
12863
13048
  } catch (error) {
12864
13049
  console.error("Erro na chamada de save_view_data:", error);
12865
13050
  }
12866
- }, [sceneSaveUrl, save_view_data]);
13051
+ }, [sceneSaveUrl, save_view_data, sceneConfigId, viewParams == null ? void 0 : viewParams.type]);
12867
13052
  const allAvailableNodes = useMemo12(() => {
12868
13053
  if (!parentDataRef.current) return [];
12869
13054
  return Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes || []);
@@ -12948,6 +13133,106 @@ function XViewScene({
12948
13133
  }
12949
13134
  }
12950
13135
  }, [isInitialized, sceneVersion, focusAncestryId, hasOpenedInitialAncestry, handleStartReadingAncestry]);
13136
+ useEffect21(() => {
13137
+ function handleKeyDown(event) {
13138
+ var _a2, _b2, _c2;
13139
+ const context = actionHandlerContext;
13140
+ if (event.key === "Escape") {
13141
+ if (stateRef.current.connection.isActive) userActionHandlers.handleCancelConnection(context);
13142
+ if (stateRef.current.relink.isActive) userActionHandlers.handleCancelRelink(context);
13143
+ if (stateRef.current.creation.isActive) userActionHandlers.handleCancelCreation(context);
13144
+ if ((_a2 = stateRef.current.versionMode) == null ? void 0 : _a2.isActive) userActionHandlers.handleCancelVersioning(context);
13145
+ if (stateRef.current.ancestry.isActive) {
13146
+ setAncestryMode({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", ancestryDescriptionSections: [], isAddingNodes: false });
13147
+ if (mountRef.current) mountRef.current.style.cursor = "grab";
13148
+ }
13149
+ if (questMode.isActive) {
13150
+ handleCancelQuest();
13151
+ }
13152
+ if (stateRef.current.selectedNodes.size > 0) {
13153
+ stateRef.current.selectedNodes.clear();
13154
+ }
13155
+ setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
13156
+ setMultiContextMenu((prev) => ({ ...prev, visible: false }));
13157
+ setRelationshipMenu((prev) => ({ ...prev, visible: false }));
13158
+ }
13159
+ if (event.key.toLowerCase() === "q") {
13160
+ 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;
13161
+ if (isUiClear) {
13162
+ const isView = ((_c2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _c2.toLowerCase()) === "view";
13163
+ if (!isView) return;
13164
+ const { graphGroup, glowTexture, controls, nodeObjects } = stateRef.current;
13165
+ if (graphGroup) {
13166
+ let ghostPosition = controls.target.clone();
13167
+ const existingNodes = Object.values(nodeObjects);
13168
+ let isOccupied = true;
13169
+ let radius = 18;
13170
+ let angle = 0;
13171
+ let attempts = 0;
13172
+ const MIN_CLEARANCE = 15;
13173
+ while (isOccupied && attempts < 30) {
13174
+ isOccupied = existingNodes.some((mesh) => mesh.position.distanceTo(ghostPosition) < MIN_CLEARANCE);
13175
+ if (isOccupied) {
13176
+ ghostPosition.x = controls.target.x + Math.cos(angle) * radius;
13177
+ ghostPosition.y = controls.target.y + (Math.random() - 0.5) * 8;
13178
+ ghostPosition.z = controls.target.z + Math.sin(angle) * radius;
13179
+ angle += Math.PI / 3;
13180
+ radius += 2.5;
13181
+ attempts++;
13182
+ }
13183
+ }
13184
+ const ghostData = {
13185
+ id: "ghost_quest",
13186
+ name: "Nova Quest",
13187
+ color: "#64748b",
13188
+ // Cor padrão de "Backlog"
13189
+ size: "medium",
13190
+ intensity: 0,
13191
+ type: ["quest"]
13192
+ };
13193
+ const ghostNode = createNodeMesh(ghostData, ghostPosition, glowTexture);
13194
+ ghostNode.traverse((child) => {
13195
+ if (child.isMesh) {
13196
+ child.material.transparent = true;
13197
+ child.material.opacity = 0.75;
13198
+ }
13199
+ });
13200
+ graphGroup.add(ghostNode);
13201
+ if (ghostNode.userData.labelObject) {
13202
+ graphGroup.add(ghostNode.userData.labelObject);
13203
+ }
13204
+ stateRef.current.ghostElements = {
13205
+ node: ghostNode,
13206
+ line: null,
13207
+ aura: ghostNode.getObjectByName("aura")
13208
+ };
13209
+ context.tweenToTarget(ghostPosition, 1.6);
13210
+ }
13211
+ setQuestMode({ isActive: true });
13212
+ }
13213
+ }
13214
+ }
13215
+ window.addEventListener("keydown", handleKeyDown);
13216
+ return () => window.removeEventListener("keydown", handleKeyDown);
13217
+ }, [
13218
+ contextMenu.visible,
13219
+ multiContextMenu.visible,
13220
+ relationshipMenu.visible,
13221
+ readingMode.isActive,
13222
+ isImportModalOpen,
13223
+ isAncestryBoardOpen,
13224
+ isSidebarOpen,
13225
+ detailsNode,
13226
+ detailsLink,
13227
+ ancestryLinkDetails,
13228
+ imageViewer.visible,
13229
+ editingAncestryRel.visible,
13230
+ questMode.isActive,
13231
+ viewParams,
13232
+ actionHandlerContext,
13233
+ handleCancelQuest
13234
+ // <-- handleCancelQuest adicionado aqui
13235
+ ]);
12951
13236
  if (isLoading || status === "loading" || permissionStatus === "loading") {
12952
13237
  return /* @__PURE__ */ React24.createElement(LoadingScreen, null);
12953
13238
  }
@@ -13042,7 +13327,10 @@ function XViewScene({
13042
13327
  InSceneQuestForm,
13043
13328
  {
13044
13329
  onSave: (data) => handleSaveQuestNode(actionHandlerContext, data),
13045
- onCancel: () => setQuestMode({ isActive: false }),
13330
+ onCancel: handleCancelQuest,
13331
+ onNameChange: handleGhostNodeNameChange,
13332
+ onColorChange: handleGhostNodeColorChange,
13333
+ onSizeChange: handleGhostNodeSizeChange,
13046
13334
  style: { position: "absolute", left: `16px`, top: `16px`, zIndex: 20, transition: "opacity 200ms ease-out" },
13047
13335
  refEl: formRef,
13048
13336
  onOpenImageViewer: handleOpenImageViewer,
@@ -13334,6 +13622,12 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
13334
13622
  }
13335
13623
  const sceneData = sceneResponse.data;
13336
13624
  const parentDbObjects = sceneData.parent_dbs || [];
13625
+ if (type && type.toLowerCase().includes("database")) {
13626
+ const selfExists = parentDbObjects.some((db) => String(db.db_id) === String(scene_config));
13627
+ if (!selfExists) {
13628
+ parentDbObjects.push({ db_id: scene_config, owner_id });
13629
+ }
13630
+ }
13337
13631
  const parentResponsesPromises = parentDbObjects.map(
13338
13632
  (db_info) => db_services.get_file(`x_view_dbs/${db_info.owner_id}/${db_info.db_id}`)
13339
13633
  );
@@ -13351,6 +13645,13 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
13351
13645
  );
13352
13646
  }
13353
13647
  }
13648
+ if (type && type.toLowerCase() === "view") {
13649
+ parentData[scene_config] = {
13650
+ dataset_name: "Quests Internas (View)",
13651
+ nodes: sceneData.quest_nodes || [],
13652
+ links: sceneData.quest_links || []
13653
+ };
13654
+ }
13354
13655
  const allNodes = Object.values(parentData).flatMap((db) => db.nodes || []);
13355
13656
  const allLinks = Object.values(parentData).flatMap((db) => db.links || []);
13356
13657
  const parentNodeMap = new Map(allNodes.map((node) => [String(node.id), node]));
@@ -13360,7 +13661,11 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
13360
13661
  if (nodeTypes.includes("quest")) {
13361
13662
  return sceneNode;
13362
13663
  }
13363
- return parentNodeMap.get(String(sceneNode.id));
13664
+ const dbNode = parentNodeMap.get(String(sceneNode.id));
13665
+ if (dbNode) {
13666
+ return { ...sceneNode, ...dbNode };
13667
+ }
13668
+ return null;
13364
13669
  }).filter(Boolean);
13365
13670
  const validNodeIdsInScene = new Set(validatedNodes.map((node) => String(node.id)));
13366
13671
  const validatedLinks = (sceneData.links || []).filter((sceneLink) => {