@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.js CHANGED
@@ -1225,8 +1225,6 @@ var createMultipleLinkLines = (linksArray, sourceNodeMesh, targetNodeMesh, resol
1225
1225
  targetNodeMesh,
1226
1226
  resolution,
1227
1227
  isCurved,
1228
- isCurved,
1229
- isCurved,
1230
1228
  curveOffset
1231
1229
  );
1232
1230
  line.userData = {
@@ -1559,7 +1557,7 @@ var addStandaloneNodeToScene = (state, nodeData, position) => {
1559
1557
  scaleTween.start();
1560
1558
  }
1561
1559
  };
1562
- var getParentFileInfoForNode = (allParentData, sceneData, nodeId) => {
1560
+ var getParentFileInfoForNode = (allParentData, sceneData, nodeId, sceneConfigId = null, sceneOwnerId = null) => {
1563
1561
  const parentDbsArray = (sceneData == null ? void 0 : sceneData.parent_dbs) || [];
1564
1562
  for (const parentFileId in allParentData) {
1565
1563
  if (allParentData.hasOwnProperty(parentFileId)) {
@@ -1568,6 +1566,8 @@ var getParentFileInfoForNode = (allParentData, sceneData, nodeId) => {
1568
1566
  const parentDbInfo = parentDbsArray.find((db) => String(db.db_id) === String(parentFileId));
1569
1567
  if (parentDbInfo) {
1570
1568
  return { parentFileId, ownerId: parentDbInfo.owner_id };
1569
+ } else if (sceneConfigId && String(parentFileId) === String(sceneConfigId)) {
1570
+ return { parentFileId, ownerId: sceneOwnerId };
1571
1571
  } else {
1572
1572
  console.warn(`Owner ID n\xE3o encontrado em sceneData.parent_dbs para o parentFileId: ${parentFileId}`);
1573
1573
  return { parentFileId, ownerId: null };
@@ -1837,6 +1837,7 @@ var userActionHandlers = {
1837
1837
  setters.setFormPosition((p) => ({ ...p, opacity: 0 }));
1838
1838
  },
1839
1839
  handleSaveVersionNode: async (context, newNodeData) => {
1840
+ var _a;
1840
1841
  const { graphDataRef, sceneDataRef, stateRef, versionMode, setters } = context;
1841
1842
  if (!graphDataRef.current || !sceneDataRef.current) return;
1842
1843
  const { sourceNodeData } = versionMode;
@@ -1846,7 +1847,7 @@ var userActionHandlers = {
1846
1847
  version_node: { is_version: true, parent_node: sourceNodeData.id }
1847
1848
  };
1848
1849
  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);
1850
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id, context.sceneConfigId, context.ownerId);
1850
1851
  if (!parentInfo || !parentInfo.ownerId) {
1851
1852
  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
1853
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio.");
@@ -1856,9 +1857,21 @@ var userActionHandlers = {
1856
1857
  const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileId]));
1857
1858
  specificParentData.nodes.push(newNode);
1858
1859
  specificParentData.links.push(newLink);
1859
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
1860
1860
  try {
1861
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
1861
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
1862
+ if (isView && parentFileId === context.sceneConfigId) {
1863
+ const viewFilePayload = {
1864
+ parent_dbs: sceneDataRef.current.parent_dbs,
1865
+ nodes: sceneDataRef.current.nodes,
1866
+ links: sceneDataRef.current.links,
1867
+ quest_nodes: specificParentData.nodes,
1868
+ quest_links: specificParentData.links
1869
+ };
1870
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
1871
+ } else {
1872
+ const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
1873
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
1874
+ }
1862
1875
  graphDataRef.current[parentFileId] = specificParentData;
1863
1876
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1864
1877
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
@@ -1882,10 +1895,6 @@ var userActionHandlers = {
1882
1895
  var _a;
1883
1896
  const isSource = String(link.source) === String(sourceNode.id);
1884
1897
  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
1898
  if (!nodeObjects[String(targetNodeId)]) {
1890
1899
  const allParentNodes = Object.values(graphDataRef.current).flatMap((fileData) => fileData.nodes);
1891
1900
  const nodeData = allParentNodes.find((n) => String(n.id) === String(targetNodeId));
@@ -1893,9 +1902,6 @@ var userActionHandlers = {
1893
1902
  console.warn(`Dados do Node com ID ${targetNodeId} n\xE3o encontrados no cache.`);
1894
1903
  return;
1895
1904
  }
1896
- if (!sceneDataRef.current.nodes.some((n) => String(n.id) === String(nodeData.id))) {
1897
- sceneDataRef.current.nodes.push(nodeData);
1898
- }
1899
1905
  const startPosition = sourceNodeMesh.position.clone();
1900
1906
  const endPosition = startPosition.clone().add(
1901
1907
  new THREE.Vector3((Math.random() - 0.5) * 60, (Math.random() - 0.5) * 15, (Math.random() - 0.5) * 60)
@@ -2015,6 +2021,7 @@ var userActionHandlers = {
2015
2021
  if (mountRef.current) mountRef.current.style.cursor = "grab";
2016
2022
  },
2017
2023
  handleCompleteConnection: async (context, targetNodeData) => {
2024
+ var _a;
2018
2025
  const { stateRef, graphDataRef, sceneDataRef } = context;
2019
2026
  const { sourceNodeData } = stateRef.current.connection;
2020
2027
  if (!graphDataRef.current || !sceneDataRef.current || !sourceNodeData || !targetNodeData) {
@@ -2022,7 +2029,7 @@ var userActionHandlers = {
2022
2029
  userActionHandlers.handleCancelConnection(context);
2023
2030
  return;
2024
2031
  }
2025
- const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id);
2032
+ const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id, context.sceneConfigId, context.ownerId);
2026
2033
  if (!sourceParentInfo || !sourceParentInfo.ownerId) {
2027
2034
  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
2035
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio.");
@@ -2037,12 +2044,26 @@ var userActionHandlers = {
2037
2044
  source: sourceNodeData.id,
2038
2045
  target: targetNodeData.id
2039
2046
  };
2040
- const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
2041
- specificParentData.links.push(newLink);
2042
- const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
2043
2047
  try {
2044
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2045
- graphDataRef.current[parentFileIdToSave] = specificParentData;
2048
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2049
+ if (isView && parentFileIdToSave === context.sceneConfigId) {
2050
+ const specificParentData = graphDataRef.current[context.sceneConfigId];
2051
+ specificParentData.links.push(newLink);
2052
+ const viewFilePayload = {
2053
+ parent_dbs: sceneDataRef.current.parent_dbs,
2054
+ nodes: sceneDataRef.current.nodes,
2055
+ links: sceneDataRef.current.links,
2056
+ quest_nodes: specificParentData.nodes,
2057
+ quest_links: specificParentData.links
2058
+ };
2059
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2060
+ } else {
2061
+ const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
2062
+ specificParentData.links.push(newLink);
2063
+ const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
2064
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2065
+ graphDataRef.current[parentFileIdToSave] = specificParentData;
2066
+ }
2046
2067
  addNewLinkToScene(stateRef.current, newLink);
2047
2068
  } catch (error) {
2048
2069
  console.error("Falha ao salvar a nova conex\xE3o:", error);
@@ -2111,14 +2132,28 @@ var userActionHandlers = {
2111
2132
  } else {
2112
2133
  newTargetId = newEndNodeData.id;
2113
2134
  }
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.");
2135
+ const oldSourceInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, oldSourceId, context.sceneConfigId, context.ownerId);
2136
+ const oldTargetInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, oldTargetId, context.sceneConfigId, context.ownerId);
2137
+ const newSourceInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, newSourceId, context.sceneConfigId, context.ownerId);
2138
+ const newTargetInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, newTargetId, context.sceneConfigId, context.ownerId);
2139
+ if (!oldSourceInfo || !oldTargetInfo || !newSourceInfo || !newTargetInfo) {
2140
+ console.error("Informa\xE7\xF5es dos arquivos pai incompletas para o relink.");
2118
2141
  alert("Ocorreu um erro ao identificar os arquivos pai para salvar a altera\xE7\xE3o.");
2119
2142
  userActionHandlers.handleCancelRelink(context);
2120
2143
  return;
2121
2144
  }
2145
+ let oldGoverningFileId = oldSourceInfo.parentFileId;
2146
+ let oldGoverningOwnerId = oldSourceInfo.ownerId;
2147
+ if (oldSourceInfo.parentFileId === context.sceneConfigId || oldTargetInfo.parentFileId === context.sceneConfigId) {
2148
+ oldGoverningFileId = context.sceneConfigId;
2149
+ oldGoverningOwnerId = context.ownerId;
2150
+ }
2151
+ let newGoverningFileId = newSourceInfo.parentFileId;
2152
+ let newGoverningOwnerId = newSourceInfo.ownerId;
2153
+ if (newSourceInfo.parentFileId === context.sceneConfigId || newTargetInfo.parentFileId === context.sceneConfigId) {
2154
+ newGoverningFileId = context.sceneConfigId;
2155
+ newGoverningOwnerId = context.ownerId;
2156
+ }
2122
2157
  const { sourceNode, targetNode, ...dataToKeep } = originalLinkData;
2123
2158
  const newLinkData = {
2124
2159
  ...dataToKeep,
@@ -2126,34 +2161,44 @@ var userActionHandlers = {
2126
2161
  target: newTargetId,
2127
2162
  id: linkId
2128
2163
  };
2164
+ const saveParentData = async (fileId, fileOwnerId, data) => {
2165
+ var _a;
2166
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2167
+ if (isView && fileId === context.sceneConfigId) {
2168
+ const viewFilePayload = {
2169
+ parent_dbs: sceneDataRef.current.parent_dbs,
2170
+ nodes: sceneDataRef.current.nodes,
2171
+ links: sceneDataRef.current.links,
2172
+ quest_nodes: data.nodes,
2173
+ quest_links: data.links
2174
+ };
2175
+ return context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2176
+ } else {
2177
+ return context.actions.save_view_data(`x_view_dbs/${fileOwnerId}/${fileId}`, data);
2178
+ }
2179
+ };
2129
2180
  const savePromises = [];
2130
2181
  const filesToUpdate = {};
2131
- if (originalParentInfo.parentFileId !== newParentInfo.parentFileId) {
2132
- const updatedOriginalParentData = JSON.parse(JSON.stringify(graphDataRef.current[originalParentInfo.parentFileId]));
2182
+ if (oldGoverningFileId !== newGoverningFileId) {
2183
+ const updatedOriginalParentData = JSON.parse(JSON.stringify(graphDataRef.current[oldGoverningFileId]));
2133
2184
  updatedOriginalParentData.links = updatedOriginalParentData.links.filter(
2134
2185
  (l) => String(l.id) !== String(linkId)
2135
2186
  );
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]));
2187
+ filesToUpdate[oldGoverningFileId] = updatedOriginalParentData;
2188
+ savePromises.push(saveParentData(oldGoverningFileId, oldGoverningOwnerId, updatedOriginalParentData));
2189
+ const updatedNewParentData = JSON.parse(JSON.stringify(graphDataRef.current[newGoverningFileId]));
2141
2190
  if (!updatedNewParentData.links) updatedNewParentData.links = [];
2142
2191
  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
- );
2192
+ filesToUpdate[newGoverningFileId] = updatedNewParentData;
2193
+ savePromises.push(saveParentData(newGoverningFileId, newGoverningOwnerId, updatedNewParentData));
2147
2194
  } else {
2148
- const updatedParentData = JSON.parse(JSON.stringify(graphDataRef.current[originalParentInfo.parentFileId]));
2195
+ const updatedParentData = JSON.parse(JSON.stringify(graphDataRef.current[oldGoverningFileId]));
2149
2196
  updatedParentData.links = updatedParentData.links.filter(
2150
2197
  (l) => String(l.id) !== String(linkId)
2151
2198
  );
2152
2199
  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
- );
2200
+ filesToUpdate[oldGoverningFileId] = updatedParentData;
2201
+ savePromises.push(saveParentData(oldGoverningFileId, oldGoverningOwnerId, updatedParentData));
2157
2202
  }
2158
2203
  try {
2159
2204
  await Promise.all(savePromises);
@@ -2171,7 +2216,7 @@ var userActionHandlers = {
2171
2216
  }
2172
2217
  },
2173
2218
  handleDeleteLink: async (context, linkObject) => {
2174
- var _a, _b, _c, _d, _e, _f;
2219
+ var _a, _b, _c, _d, _e, _f, _g;
2175
2220
  const { stateRef, graphDataRef, sceneDataRef, setters } = context;
2176
2221
  setters.setRelationshipMenu({ visible: false });
2177
2222
  if (!(linkObject == null ? void 0 : linkObject.userData) || !graphDataRef.current || !sceneDataRef.current) return;
@@ -2180,7 +2225,7 @@ var userActionHandlers = {
2180
2225
  console.error("Tentativa de deletar um link sem ID.", linkObject.userData);
2181
2226
  return;
2182
2227
  }
2183
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkObject.userData.source);
2228
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkObject.userData.source, context.sceneConfigId, context.ownerId);
2184
2229
  if (!parentInfo || !parentInfo.ownerId) {
2185
2230
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o link:", linkIdToDelete);
2186
2231
  alert("Ocorreu um erro ao identificar o arquivo pai da rela\xE7\xE3o para exclus\xE3o.");
@@ -2192,9 +2237,25 @@ var userActionHandlers = {
2192
2237
  const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileId]));
2193
2238
  const newLinks = (specificParentData.links || []).filter((l) => String(l.id) !== String(linkIdToDelete));
2194
2239
  specificParentData.links = newLinks;
2195
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2240
+ let filenameToSave;
2241
+ let payloadToSave;
2242
+ const isView = ((_g = context.viewType) == null ? void 0 : _g.toLowerCase()) === "view";
2243
+ if (isView && parentFileId === context.sceneConfigId) {
2244
+ filenameToSave = context.sceneSaveUrl;
2245
+ sceneDataRef.current.links = sceneDataRef.current.links.filter((l) => String(l.id) !== String(linkIdToDelete));
2246
+ payloadToSave = {
2247
+ parent_dbs: sceneDataRef.current.parent_dbs,
2248
+ nodes: sceneDataRef.current.nodes,
2249
+ links: sceneDataRef.current.links,
2250
+ quest_nodes: specificParentData.nodes,
2251
+ quest_links: specificParentData.links
2252
+ };
2253
+ } else {
2254
+ filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2255
+ payloadToSave = specificParentData;
2256
+ }
2196
2257
  try {
2197
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2258
+ await context.actions.save_view_data(filenameToSave, payloadToSave);
2198
2259
  graphDataRef.current[parentFileId] = specificParentData;
2199
2260
  setters.setDetailsLink((prev) => String(prev == null ? void 0 : prev.id) === String(linkIdToDelete) ? null : prev);
2200
2261
  if (stateRef.current.hoveredLink === linkObject) {
@@ -2212,12 +2273,6 @@ var userActionHandlers = {
2212
2273
  if (!nodeData || !sceneDataRef.current) return;
2213
2274
  const nodeIdToDismiss = nodeData.id;
2214
2275
  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
2276
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2222
2277
  if (ancestryGroup && ancestryLinks) {
2223
2278
  const remainingAncestryLinks = [];
@@ -2259,22 +2314,12 @@ var userActionHandlers = {
2259
2314
  removeNodeFromScene(stateRef.current, nodeId);
2260
2315
  setters.setDetailsNode((prev) => String(prev == null ? void 0 : prev.id) === String(nodeId) ? null : prev);
2261
2316
  });
2262
- sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter(
2263
- (n) => String(n.id) === strNodeIdToKeep
2264
- );
2265
- sceneDataRef.current.links = [];
2266
2317
  },
2267
2318
  handleDismissMultipleNodes: (context, nodeIds) => {
2268
2319
  const { stateRef, sceneDataRef, setters } = context;
2269
2320
  setters.setMultiContextMenu({ visible: false });
2270
2321
  if (!nodeIds || nodeIds.size === 0 || !sceneDataRef.current) return;
2271
2322
  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
2323
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2279
2324
  if (ancestryGroup && ancestryLinks) {
2280
2325
  const remainingAncestryLinks = [];
@@ -2330,15 +2375,10 @@ var userActionHandlers = {
2330
2375
  removeNodeFromScene(stateRef.current, nodeId);
2331
2376
  setters.setDetailsNode((prev) => String(prev == null ? void 0 : prev.id) === String(nodeId) ? null : prev);
2332
2377
  });
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
2378
  stateRef.current.selectedNodes.clear();
2340
2379
  },
2341
2380
  handleDeleteMultipleNodes: async (context, nodeIds) => {
2381
+ var _a;
2342
2382
  const { stateRef, graphDataRef, sceneDataRef, setters, actions } = context;
2343
2383
  setters.setMultiContextMenu({ visible: false });
2344
2384
  if (!nodeIds || nodeIds.size === 0 || !graphDataRef.current || !sceneDataRef.current) return;
@@ -2357,7 +2397,7 @@ var userActionHandlers = {
2357
2397
  }
2358
2398
  const changesByParentFile = {};
2359
2399
  for (const nodeId of strNodeIdsToDelete) {
2360
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeId);
2400
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeId, context.sceneConfigId, context.ownerId);
2361
2401
  if (!parentInfo || !parentInfo.ownerId) {
2362
2402
  console.warn(`Node com ID ${nodeId} n\xE3o encontrado ou sem ownerId. Ignorando.`);
2363
2403
  continue;
@@ -2391,8 +2431,27 @@ var userActionHandlers = {
2391
2431
  originalData.links = (originalData.links || []).filter(
2392
2432
  (l) => !linksToDelete.has(String(l.id))
2393
2433
  );
2394
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2395
- savePromises.push(context.actions.save_view_data(filenameForSpecificParent, originalData));
2434
+ let filenameToSave;
2435
+ let payloadToSave;
2436
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2437
+ if (isView && parentFileId === context.sceneConfigId) {
2438
+ filenameToSave = context.sceneSaveUrl;
2439
+ const strNodesToDelete = Array.from(nodesToDelete).map(String);
2440
+ const strLinksToDelete = Array.from(linksToDelete).map(String);
2441
+ sceneDataRef.current.nodes = sceneDataRef.current.nodes.filter((n) => !strNodesToDelete.includes(String(n.id)));
2442
+ sceneDataRef.current.links = sceneDataRef.current.links.filter((l) => !strLinksToDelete.includes(String(l.id)));
2443
+ payloadToSave = {
2444
+ parent_dbs: sceneDataRef.current.parent_dbs,
2445
+ nodes: sceneDataRef.current.nodes,
2446
+ links: sceneDataRef.current.links,
2447
+ quest_nodes: originalData.nodes,
2448
+ quest_links: originalData.links
2449
+ };
2450
+ } else {
2451
+ filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2452
+ payloadToSave = originalData;
2453
+ }
2454
+ savePromises.push(context.actions.save_view_data(filenameToSave, payloadToSave));
2396
2455
  updatedParentDataCache[parentFileId] = originalData;
2397
2456
  }
2398
2457
  }
@@ -2413,6 +2472,7 @@ var userActionHandlers = {
2413
2472
  }
2414
2473
  },
2415
2474
  handleDeleteNode: async (context, nodeData) => {
2475
+ var _a;
2416
2476
  const { stateRef, graphDataRef, sceneDataRef, setters, actions } = context;
2417
2477
  if (actions.delete_file && nodeData) {
2418
2478
  const urls = extractFileUrlsFromProperties(nodeData);
@@ -2424,7 +2484,7 @@ var userActionHandlers = {
2424
2484
  if (!nodeData || !graphDataRef.current || !sceneDataRef.current) return;
2425
2485
  const nodeIdToDelete = nodeData.id;
2426
2486
  const strNodeId = String(nodeIdToDelete);
2427
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeIdToDelete);
2487
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeIdToDelete, context.sceneConfigId, context.ownerId);
2428
2488
  if (!parentInfo || !parentInfo.ownerId) {
2429
2489
  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
2490
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio para exclus\xE3o.");
@@ -2436,9 +2496,28 @@ var userActionHandlers = {
2436
2496
  const newLinks = (specificParentData.links || []).filter((l) => String(l.source) !== strNodeId && String(l.target) !== strNodeId);
2437
2497
  specificParentData.nodes = newNodes;
2438
2498
  specificParentData.links = newLinks;
2439
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2499
+ let filenameToSave;
2500
+ let payloadToSave;
2501
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2502
+ if (isView && parentFileId === context.sceneConfigId) {
2503
+ filenameToSave = context.sceneSaveUrl;
2504
+ const newVisualNodes = sceneDataRef.current.nodes.filter((n) => String(n.id) !== strNodeId);
2505
+ const newVisualLinks = sceneDataRef.current.links.filter((l) => String(l.source) !== strNodeId && String(l.target) !== strNodeId);
2506
+ sceneDataRef.current.nodes = newVisualNodes;
2507
+ sceneDataRef.current.links = newVisualLinks;
2508
+ payloadToSave = {
2509
+ parent_dbs: sceneDataRef.current.parent_dbs,
2510
+ nodes: newVisualNodes,
2511
+ links: newVisualLinks,
2512
+ quest_nodes: specificParentData.nodes,
2513
+ quest_links: specificParentData.links
2514
+ };
2515
+ } else {
2516
+ filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2517
+ payloadToSave = specificParentData;
2518
+ }
2440
2519
  try {
2441
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2520
+ await context.actions.save_view_data(filenameToSave, payloadToSave);
2442
2521
  graphDataRef.current[parentFileId] = specificParentData;
2443
2522
  setters.setDetailsNode((prev) => String(prev == null ? void 0 : prev.id) === String(nodeIdToDelete) ? null : prev);
2444
2523
  removeNodeFromScene(stateRef.current, nodeIdToDelete);
@@ -2449,10 +2528,11 @@ var userActionHandlers = {
2449
2528
  }
2450
2529
  },
2451
2530
  handleSaveNodeDetails: async (context, updatedNode, keepOpen = false) => {
2531
+ var _a;
2452
2532
  const { graphDataRef, sceneDataRef, stateRef, setters } = context;
2453
2533
  if (!graphDataRef.current || !sceneDataRef.current) return;
2454
2534
  const { _baseEmissiveIntensity: ignored, ...nodeToSave } = updatedNode;
2455
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeToSave.id);
2535
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeToSave.id, context.sceneConfigId, context.ownerId);
2456
2536
  if (!parentInfo || !parentInfo.ownerId) {
2457
2537
  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
2538
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio para atualiza\xE7\xE3o.");
@@ -2468,9 +2548,21 @@ var userActionHandlers = {
2468
2548
  alert("Erro interno: Node n\xE3o encontrado para salvar.");
2469
2549
  return;
2470
2550
  }
2471
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2472
2551
  try {
2473
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2552
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2553
+ if (isView && parentFileId === context.sceneConfigId) {
2554
+ const viewFilePayload = {
2555
+ parent_dbs: sceneDataRef.current.parent_dbs,
2556
+ nodes: sceneDataRef.current.nodes,
2557
+ links: sceneDataRef.current.links,
2558
+ quest_nodes: specificParentData.nodes,
2559
+ quest_links: specificParentData.links
2560
+ };
2561
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2562
+ } else {
2563
+ const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2564
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2565
+ }
2474
2566
  graphDataRef.current[parentFileId] = specificParentData;
2475
2567
  updateExistingNodeVisuals(stateRef.current, nodeToSave);
2476
2568
  setters.setSceneVersion((v) => v + 1);
@@ -2483,10 +2575,11 @@ var userActionHandlers = {
2483
2575
  }
2484
2576
  },
2485
2577
  handleSaveLinkDetails: async (context, updatedLink, keepOpen = false) => {
2578
+ var _a;
2486
2579
  const { graphDataRef, sceneDataRef, stateRef, setters } = context;
2487
2580
  if (!graphDataRef.current || !sceneDataRef.current) return;
2488
2581
  const { sourceNode, targetNode, ...linkToSave } = updatedLink;
2489
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkToSave.source);
2582
+ const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, linkToSave.source, context.sceneConfigId, context.ownerId);
2490
2583
  if (!parentInfo || !parentInfo.ownerId) {
2491
2584
  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
2585
  alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio para atualiza\xE7\xE3o.");
@@ -2502,9 +2595,21 @@ var userActionHandlers = {
2502
2595
  alert("Erro interno: link n\xE3o encontrado para salvar.");
2503
2596
  return;
2504
2597
  }
2505
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2506
2598
  try {
2507
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2599
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2600
+ if (isView && parentFileId === context.sceneConfigId) {
2601
+ const viewFilePayload = {
2602
+ parent_dbs: sceneDataRef.current.parent_dbs,
2603
+ nodes: sceneDataRef.current.nodes,
2604
+ links: sceneDataRef.current.links,
2605
+ quest_nodes: specificParentData.nodes,
2606
+ quest_links: specificParentData.links
2607
+ };
2608
+ await context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
2609
+ } else {
2610
+ const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2611
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
2612
+ }
2508
2613
  graphDataRef.current[parentFileId] = specificParentData;
2509
2614
  const lineObject = stateRef.current.allLinks.find(
2510
2615
  (l) => String(l.userData.id) === String(linkToSave.id)
@@ -2527,7 +2632,7 @@ var userActionHandlers = {
2527
2632
  }
2528
2633
  },
2529
2634
  handleAddExistingNodeById: (context, nodeId) => {
2530
- var _a, _b;
2635
+ var _a;
2531
2636
  const { stateRef, sceneDataRef, graphDataRef, tweenToTarget, setters } = context;
2532
2637
  const state = stateRef.current;
2533
2638
  const graphFull = graphDataRef.current;
@@ -2543,16 +2648,12 @@ var userActionHandlers = {
2543
2648
  tweenToTarget(state.nodeObjects[strNodeId]);
2544
2649
  return;
2545
2650
  }
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
2651
  const base = state.controls ? state.controls.target.clone() : new THREE.Vector3(0, 0, 0);
2551
2652
  const offset = new THREE.Vector3((Math.random() - 0.5) * 20, (Math.random() - 0.5) * 6, (Math.random() - 0.5) * 20);
2552
2653
  const position = base.add(offset);
2553
2654
  addStandaloneNodeToScene(state, nodeData, position);
2554
2655
  tweenToTarget(position, 1.3);
2555
- (_b = setters == null ? void 0 : setters.setSceneVersion) == null ? void 0 : _b.call(setters, (v) => v + 1);
2656
+ (_a = setters == null ? void 0 : setters.setSceneVersion) == null ? void 0 : _a.call(setters, (v) => v + 1);
2556
2657
  }
2557
2658
  };
2558
2659
 
@@ -7853,13 +7954,9 @@ var import_react16 = __toESM(require("react"));
7853
7954
  var import_fi14 = require("react-icons/fi");
7854
7955
  var QUEST_STATUS_COLORS = {
7855
7956
  "Backlog": "#64748b",
7856
- // Slate (Cinza azulado)
7857
7957
  "In Progress": "#eab308",
7858
- // Yellow (Amarelo)
7859
7958
  "Review": "#a855f7",
7860
- // Purple (Roxo)
7861
7959
  "Done": "#22c55e"
7862
- // Green (Verde)
7863
7960
  };
7864
7961
  function InSceneQuestForm({
7865
7962
  onSave,
@@ -7870,7 +7967,11 @@ function InSceneQuestForm({
7870
7967
  availableNodes = [],
7871
7968
  availableAncestries = [],
7872
7969
  onMentionClick,
7873
- onUploadFile
7970
+ onUploadFile,
7971
+ // NOVAS PROPS PARA O GHOST NODE
7972
+ onNameChange,
7973
+ onColorChange,
7974
+ onSizeChange
7874
7975
  }) {
7875
7976
  const [name, setName] = (0, import_react16.useState)("");
7876
7977
  const [types, setTypes] = (0, import_react16.useState)(["quest"]);
@@ -7879,6 +7980,7 @@ function InSceneQuestForm({
7879
7980
  const [size, setSize] = (0, import_react16.useState)("medium");
7880
7981
  const [intensity, setIntensity] = (0, import_react16.useState)(0);
7881
7982
  const [description, setDescription] = (0, import_react16.useState)("");
7983
+ const [isStatusDropdownOpen, setIsStatusDropdownOpen] = (0, import_react16.useState)(false);
7882
7984
  const [customProps, setCustomProps] = (0, import_react16.useState)([]);
7883
7985
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react16.useState)(false);
7884
7986
  const propsEndRef = (0, import_react16.useRef)(null);
@@ -7897,7 +7999,7 @@ function InSceneQuestForm({
7897
7999
  setCustomProps(newProps);
7898
8000
  };
7899
8001
  const handleAddType = (newType) => {
7900
- const trimmed = newType.trim().toLowerCase();
8002
+ const trimmed = newType.trim();
7901
8003
  if (trimmed && !types.includes(trimmed)) {
7902
8004
  setTypes([...types, trimmed]);
7903
8005
  setTypeInput("");
@@ -7929,7 +8031,6 @@ function InSceneQuestForm({
7929
8031
  name: name.trim(),
7930
8032
  type: types,
7931
8033
  color: QUEST_STATUS_COLORS[status],
7932
- // Cor atrelada ao status
7933
8034
  status,
7934
8035
  size,
7935
8036
  intensity,
@@ -7956,20 +8057,52 @@ function InSceneQuestForm({
7956
8057
  onDoubleClick: swallow
7957
8058
  },
7958
8059
  /* @__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]}` }
8060
+ /* @__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(
8061
+ "button",
8062
+ {
8063
+ type: "button",
8064
+ onClick: onCancel,
8065
+ 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",
8066
+ title: "Fechar"
7967
8067
  },
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(
8068
+ "\xD7"
8069
+ )),
8070
+ /* @__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(
8071
+ "button",
8072
+ {
8073
+ type: "button",
8074
+ onClick: () => setIsStatusDropdownOpen(!isStatusDropdownOpen),
8075
+ 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"
8076
+ },
8077
+ /* @__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)),
8078
+ /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isStatusDropdownOpen ? "rotate-180" : ""}` })
8079
+ ), 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(
8080
+ "li",
8081
+ {
8082
+ key: s,
8083
+ onClick: () => {
8084
+ setStatus(s);
8085
+ setIsStatusDropdownOpen(false);
8086
+ onColorChange == null ? void 0 : onColorChange(QUEST_STATUS_COLORS[s]);
8087
+ },
8088
+ 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"}`
8089
+ },
8090
+ /* @__PURE__ */ import_react16.default.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS[s] } }),
8091
+ s
8092
+ )))))), /* @__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(
8093
+ "input",
8094
+ {
8095
+ required: true,
8096
+ type: "text",
8097
+ placeholder: "Ex.: Refatorar M\xF3dulo X",
8098
+ value: name,
8099
+ onChange: (e) => {
8100
+ setName(e.target.value);
8101
+ onNameChange == null ? void 0 : onNameChange(e.target.value);
8102
+ },
8103
+ 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"
8104
+ }
8105
+ )), /* @__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
8106
  "input",
7974
8107
  {
7975
8108
  type: "text",
@@ -7980,7 +8113,6 @@ function InSceneQuestForm({
7980
8113
  if (typeInput.trim()) handleAddType(typeInput);
7981
8114
  },
7982
8115
  className: "flex-1 bg-transparent text-sm min-w-[80px] focus:outline-none text-slate-200",
7983
- placeholder: "Ex.: Bugfix",
7984
8116
  autoComplete: "off"
7985
8117
  }
7986
8118
  ))), /* @__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 +8125,20 @@ function InSceneQuestForm({
7993
8125
  onMentionClick,
7994
8126
  onSaveDescription: (newDesc) => setDescription(newDesc)
7995
8127
  }
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(
8128
+ ), /* @__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(
8129
+ "button",
8130
+ {
8131
+ key: s,
8132
+ type: "button",
8133
+ onClick: () => {
8134
+ setSize(s);
8135
+ onSizeChange == null ? void 0 : onSizeChange(s);
8136
+ },
8137
+ className: "flex items-center gap-2 group cursor-pointer focus:outline-none"
8138
+ },
8139
+ /* @__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" })),
8140
+ /* @__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)
8141
+ )))), /* @__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
8142
  CustomPropertyDisplay,
7998
8143
  {
7999
8144
  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 = (0, import_react25.useCallback)(() => {
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: import_short_uuid2.default.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_${import_short_uuid2.default.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 = (0, import_react25.useCallback)((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 = (0, import_react25.useCallback)(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 = (0, import_react25.useMemo)(() => {
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
+ (0, import_react25.useEffect)(() => {
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__ */ import_react25.default.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) => {