@lv-x-software-house/x_view 1.3.4 → 1.3.6

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.
package/dist/index.js CHANGED
@@ -836,6 +836,55 @@ var removeLinkFromScene = (state, linkObject) => {
836
836
  else linkObject.material.dispose();
837
837
  }
838
838
  };
839
+ var removeAllTimelineElements = (state) => {
840
+ if (!state) return;
841
+ const { timelineIntervalsGroup, nodeObjects } = state;
842
+ if (timelineIntervalsGroup) {
843
+ while (timelineIntervalsGroup.children.length > 0) {
844
+ const obj = timelineIntervalsGroup.children[0];
845
+ timelineIntervalsGroup.remove(obj);
846
+ if (obj.traverse) {
847
+ obj.traverse((child) => {
848
+ if (child.geometry) child.geometry.dispose();
849
+ if (child.material) {
850
+ if (child.material.map) child.material.map.dispose();
851
+ child.material.dispose();
852
+ }
853
+ });
854
+ } else {
855
+ if (obj.geometry) obj.geometry.dispose();
856
+ if (obj.material) {
857
+ if (obj.material.map) obj.material.map.dispose();
858
+ obj.material.dispose();
859
+ }
860
+ }
861
+ }
862
+ }
863
+ if (nodeObjects) {
864
+ Object.values(nodeObjects).forEach((mesh) => {
865
+ var _a, _b, _c, _d;
866
+ if (!mesh) return;
867
+ const oldTimelineLabel = mesh.getObjectByName("timelineLabel");
868
+ if (oldTimelineLabel) {
869
+ mesh.remove(oldTimelineLabel);
870
+ if ((_a = oldTimelineLabel.material) == null ? void 0 : _a.map) {
871
+ oldTimelineLabel.material.map.dispose();
872
+ }
873
+ (_b = oldTimelineLabel.material) == null ? void 0 : _b.dispose();
874
+ }
875
+ const oldQuestDateLabel = mesh.getObjectByName("questDateLabel");
876
+ if (oldQuestDateLabel) {
877
+ mesh.remove(oldQuestDateLabel);
878
+ if ((_c = oldQuestDateLabel.material) == null ? void 0 : _c.map) {
879
+ oldQuestDateLabel.material.map.dispose();
880
+ }
881
+ (_d = oldQuestDateLabel.material) == null ? void 0 : _d.dispose();
882
+ }
883
+ delete mesh.userData.timelineIntervalBar;
884
+ delete mesh.userData.timelineEndLabel;
885
+ });
886
+ }
887
+ };
839
888
  var removeNodeFromScene = (state, nodeId) => {
840
889
  const {
841
890
  graphGroup,
@@ -864,8 +913,15 @@ var removeNodeFromScene = (state, nodeId) => {
864
913
  const { timelineIntervalBar, timelineEndLabel } = nodeToRemove.userData;
865
914
  if (timelineIntervalBar) {
866
915
  timelineIntervalsGroup.remove(timelineIntervalBar);
867
- if (timelineIntervalBar.geometry) timelineIntervalBar.geometry.dispose();
868
- if (timelineIntervalBar.material) timelineIntervalBar.material.dispose();
916
+ if (timelineIntervalBar.traverse) {
917
+ timelineIntervalBar.traverse((child) => {
918
+ if (child.geometry) child.geometry.dispose();
919
+ if (child.material) child.material.dispose();
920
+ });
921
+ } else {
922
+ if (timelineIntervalBar.geometry) timelineIntervalBar.geometry.dispose();
923
+ if (timelineIntervalBar.material) timelineIntervalBar.material.dispose();
924
+ }
869
925
  }
870
926
  if (timelineEndLabel) {
871
927
  timelineIntervalsGroup.remove(timelineEndLabel);
@@ -936,7 +992,7 @@ var getParentFileInfoForNode = (allParentData, sceneData, nodeId, sceneConfigId
936
992
  };
937
993
  async function instantiateSubProcess(context, templateProcess, versionedProcess, startCounter) {
938
994
  var _a, _b, _c;
939
- const { graphDataRef, sceneDataRef, actions: actions2 } = context;
995
+ const { graphDataRef, sceneDataRef, actions } = context;
940
996
  const ownerId = context.ownerId;
941
997
  const parentFileId = context.sceneConfigId;
942
998
  const viewName = context.viewName || "View";
@@ -991,7 +1047,7 @@ async function instantiateSubProcess(context, templateProcess, versionedProcess,
991
1047
  }
992
1048
  ] : originalChildNode.timeline || []
993
1049
  };
994
- if (actions2.copy_google_doc_action && versionedChildNode.Links) {
1050
+ if (actions.copy_google_doc_action && versionedChildNode.Links) {
995
1051
  const docIdx = versionedChildNode.Links.findIndex((l) => l.name === "Google docs");
996
1052
  if (docIdx !== -1) {
997
1053
  const url = versionedChildNode.Links[docIdx].value;
@@ -999,7 +1055,7 @@ async function instantiateSubProcess(context, templateProcess, versionedProcess,
999
1055
  const match = url.match(/\/d\/([a-zA-Z0-9-_]+)/);
1000
1056
  if (match == null ? void 0 : match[1]) {
1001
1057
  try {
1002
- const res = await actions2.copy_google_doc_action(
1058
+ const res = await actions.copy_google_doc_action(
1003
1059
  match[1],
1004
1060
  nextName,
1005
1061
  (_c = sceneDataRef.current) == null ? void 0 : _c.view_google_folder_id
@@ -1038,14 +1094,14 @@ async function instantiateSubProcess(context, templateProcess, versionedProcess,
1038
1094
  childRef.node.version_node = versionedChildNode.version_node;
1039
1095
  }
1040
1096
  childRef.node_id = versionedChildId;
1041
- if (versionedChildNode.is_quest && actions2.upsert_quest_firestore) {
1042
- savePromises.push(actions2.upsert_quest_firestore(ownerId, parentFileId, versionedChildNode));
1043
- if (actions2.upsert_quest_link_firestore)
1044
- savePromises.push(actions2.upsert_quest_link_firestore(ownerId, parentFileId, childVersionLink));
1045
- } else if (versionedChildNode.is_process && actions2.upsert_process_firestore) {
1046
- savePromises.push(actions2.upsert_process_firestore(ownerId, parentFileId, versionedChildNode));
1047
- if (actions2.upsert_process_link_firestore)
1048
- savePromises.push(actions2.upsert_process_link_firestore(ownerId, parentFileId, childVersionLink));
1097
+ if (versionedChildNode.is_quest && actions.upsert_quest_firestore) {
1098
+ savePromises.push(actions.upsert_quest_firestore(ownerId, parentFileId, versionedChildNode));
1099
+ if (actions.upsert_quest_link_firestore)
1100
+ savePromises.push(actions.upsert_quest_link_firestore(ownerId, parentFileId, childVersionLink));
1101
+ } else if (versionedChildNode.is_process && actions.upsert_process_firestore) {
1102
+ savePromises.push(actions.upsert_process_firestore(ownerId, parentFileId, versionedChildNode));
1103
+ if (actions.upsert_process_link_firestore)
1104
+ savePromises.push(actions.upsert_process_link_firestore(ownerId, parentFileId, childVersionLink));
1049
1105
  }
1050
1106
  }
1051
1107
  if (versionedProcess.process_abstraction_tree) {
@@ -1177,7 +1233,7 @@ var userActionHandlers = {
1177
1233
  stateRef,
1178
1234
  creationMode,
1179
1235
  setters,
1180
- actions: actions2,
1236
+ actions,
1181
1237
  checkLimit
1182
1238
  } = context;
1183
1239
  if (!graphDataRef.current || !sceneDataRef.current) return;
@@ -1211,20 +1267,20 @@ var userActionHandlers = {
1211
1267
  const savePromises = [];
1212
1268
  if (isCrossDataset) {
1213
1269
  savePromises.push(
1214
- actions2.save_view_data(
1270
+ actions.save_view_data(
1215
1271
  `x_view_dbs/${sourceParentInfo.ownerId}/${sourceParentInfo.parentFileId}`,
1216
1272
  sourceDataToUpdate
1217
1273
  )
1218
1274
  );
1219
1275
  savePromises.push(
1220
- actions2.save_view_data(
1276
+ actions.save_view_data(
1221
1277
  `x_view_dbs/${targetParentInfo.owner_id}/${finalTargetDatasetId}`,
1222
1278
  targetDataToUpdate
1223
1279
  )
1224
1280
  );
1225
1281
  } else {
1226
1282
  savePromises.push(
1227
- actions2.save_view_data(
1283
+ actions.save_view_data(
1228
1284
  `x_view_dbs/${sourceParentInfo.ownerId}/${sourceParentInfo.parentFileId}`,
1229
1285
  sourceDataToUpdate
1230
1286
  )
@@ -1247,8 +1303,8 @@ var userActionHandlers = {
1247
1303
  ownerId: targetParentInfo.owner_id,
1248
1304
  datasetName: targetDataToUpdate.dataset_name || "Dataset Desconhecido"
1249
1305
  });
1250
- if (actions2.update_node_counter) {
1251
- await actions2.update_node_counter(targetParentInfo.owner_id, 1);
1306
+ if (actions.update_node_counter) {
1307
+ await actions.update_node_counter(targetParentInfo.owner_id, 1);
1252
1308
  }
1253
1309
  setters.setSceneVersion((v) => v + 1);
1254
1310
  } catch (error) {
@@ -1382,7 +1438,7 @@ var userActionHandlers = {
1382
1438
  },
1383
1439
  handleSaveVersionNode: async (context, newNodeData) => {
1384
1440
  var _a;
1385
- const { graphDataRef, sceneDataRef, stateRef, versionMode, setters, checkLimit } = context;
1441
+ const { graphDataRef, sceneDataRef, stateRef, versionMode, setters, checkLimit, actions } = context;
1386
1442
  if (!graphDataRef.current || !sceneDataRef.current) return;
1387
1443
  if (checkLimit && checkLimit()) return;
1388
1444
  const { sourceNodeData } = versionMode;
@@ -1493,8 +1549,8 @@ var userActionHandlers = {
1493
1549
  );
1494
1550
  const targetFinalPositions = [];
1495
1551
  const newVersionMeshes = [];
1552
+ const newNodesToCreate = [];
1496
1553
  linksToExpand.forEach((link) => {
1497
- var _a;
1498
1554
  const isSource = String(link.source) === String(sourceNode.id);
1499
1555
  const targetNodeId = isSource ? link.target : link.source;
1500
1556
  if (!nodeObjects[String(targetNodeId)]) {
@@ -1504,45 +1560,94 @@ var userActionHandlers = {
1504
1560
  const nodeData = allParentNodes.find(
1505
1561
  (n) => String(n.id) === String(targetNodeId)
1506
1562
  );
1507
- if (!nodeData) {
1508
- console.warn(
1509
- `Dados do Node com ID ${targetNodeId} n\xE3o encontrados no cache.`
1510
- );
1511
- return;
1563
+ if (nodeData) {
1564
+ newNodesToCreate.push({ link, nodeData, targetNodeId });
1512
1565
  }
1513
- const startPosition = sourceNodeMesh.position.clone();
1514
- const endPosition = startPosition.clone().add(
1515
- new THREE.Vector3(
1516
- (Math.random() - 0.5) * 60,
1517
- (Math.random() - 0.5) * 15,
1518
- (Math.random() - 0.5) * 60
1519
- )
1520
- );
1521
- targetFinalPositions.push(endPosition);
1522
- const targetMesh = createNodeMesh(
1523
- nodeData,
1524
- startPosition,
1525
- glowTexture,
1526
- !!nodeData.is_quest
1527
- );
1528
- targetMesh.scale.set(0.01, 0.01, 0.01);
1529
- graphGroup.add(targetMesh);
1530
- if (targetMesh.userData.labelObject) {
1531
- graphGroup.add(targetMesh.userData.labelObject);
1532
- }
1533
- nodeObjects[String(targetNodeId)] = targetMesh;
1534
- clickableNodes.push(targetMesh);
1535
- const posTween = new import_tween.Tween(targetMesh.position).to(endPosition, 1200).easing(import_tween.Easing.Quadratic.Out);
1536
- tweenGroup.add(posTween);
1537
- posTween.start();
1538
- const targetScale = NODE_SCALE_FACTORS[nodeData.size || "medium"] || 1;
1539
- const scaleTween = new import_tween.Tween(targetMesh.scale).to({ x: targetScale, y: targetScale, z: targetScale }, 1e3).easing(import_tween.Easing.Elastic.Out);
1540
- tweenGroup.add(scaleTween);
1541
- scaleTween.start();
1542
- if (((_a = nodeData.version_node) == null ? void 0 : _a.is_version) && String(nodeData.version_node.parent_node) === String(sourceNode.id)) {
1543
- newVersionMeshes.push(targetMesh);
1566
+ }
1567
+ });
1568
+ const N = newNodesToCreate.length;
1569
+ let referenceDir = new THREE.Vector3(0, 0, 1);
1570
+ if (N > 0) {
1571
+ const incomingVectors = [];
1572
+ allLinks.forEach((l) => {
1573
+ const srcId = String(l.userData.source);
1574
+ const tgtId = String(l.userData.target);
1575
+ if (srcId === String(sourceNode.id) || tgtId === String(sourceNode.id)) {
1576
+ const otherId = srcId === String(sourceNode.id) ? tgtId : srcId;
1577
+ const otherMesh = nodeObjects[otherId];
1578
+ if (otherMesh) {
1579
+ const dir = new THREE.Vector3().subVectors(
1580
+ sourceNodeMesh.position,
1581
+ otherMesh.position
1582
+ );
1583
+ if (dir.lengthSq() > 1e-3) {
1584
+ dir.normalize();
1585
+ incomingVectors.push(dir);
1586
+ }
1587
+ }
1588
+ }
1589
+ });
1590
+ if (incomingVectors.length > 0) {
1591
+ referenceDir.set(0, 0, 0);
1592
+ incomingVectors.forEach((v) => referenceDir.add(v));
1593
+ if (referenceDir.lengthSq() > 1e-3) {
1594
+ referenceDir.normalize();
1595
+ } else {
1596
+ referenceDir.set(0, 0, 1);
1597
+ }
1598
+ } else {
1599
+ const toSource = sourceNodeMesh.position.clone();
1600
+ if (toSource.lengthSq() > 1e-3) {
1601
+ referenceDir.copy(toSource).normalize();
1544
1602
  }
1545
1603
  }
1604
+ }
1605
+ const up = new THREE.Vector3(0, 1, 0);
1606
+ if (Math.abs(referenceDir.y) > 0.95) {
1607
+ up.set(1, 0, 0);
1608
+ }
1609
+ const right = new THREE.Vector3().crossVectors(up, referenceDir).normalize();
1610
+ const upActual = new THREE.Vector3().crossVectors(referenceDir, right).normalize();
1611
+ const maxSpread = N <= 1 ? 0 : Math.min(Math.PI * 0.4 + (N - 2) * 0.15, Math.PI * 0.8);
1612
+ const baseDistance = 40;
1613
+ newNodesToCreate.forEach((item, i) => {
1614
+ var _a;
1615
+ const { link, nodeData, targetNodeId } = item;
1616
+ let phi = 0;
1617
+ if (N > 1) {
1618
+ phi = -maxSpread / 2 + i / (N - 1) * maxSpread;
1619
+ }
1620
+ const angleY = N > 1 ? Math.sin(i * Math.PI) * 0.12 : 0;
1621
+ const nodeDir = referenceDir.clone().multiplyScalar(Math.cos(phi)).add(right.clone().multiplyScalar(Math.sin(phi))).add(upActual.clone().multiplyScalar(angleY)).normalize();
1622
+ const distFactor = N > 1 ? 1 + (i % 2 === 0 ? 0 : 0.15) : 1;
1623
+ const distance = baseDistance * distFactor;
1624
+ const startPosition = sourceNodeMesh.position.clone();
1625
+ const endPosition = startPosition.clone().add(nodeDir.multiplyScalar(distance));
1626
+ targetFinalPositions.push(endPosition);
1627
+ const targetMesh = createNodeMesh(
1628
+ nodeData,
1629
+ startPosition,
1630
+ glowTexture,
1631
+ !!nodeData.is_quest
1632
+ );
1633
+ targetMesh.scale.set(0.01, 0.01, 0.01);
1634
+ graphGroup.add(targetMesh);
1635
+ if (targetMesh.userData.labelObject) {
1636
+ graphGroup.add(targetMesh.userData.labelObject);
1637
+ }
1638
+ nodeObjects[String(targetNodeId)] = targetMesh;
1639
+ clickableNodes.push(targetMesh);
1640
+ const delay = i * 60;
1641
+ const posTween = new import_tween.Tween(targetMesh.position).to(endPosition, 1200).delay(delay).easing(import_tween.Easing.Quadratic.Out);
1642
+ tweenGroup.add(posTween);
1643
+ posTween.start();
1644
+ const targetScale = NODE_SCALE_FACTORS[nodeData.size || "medium"] || 1;
1645
+ const scaleTween = new import_tween.Tween(targetMesh.scale).to({ x: targetScale, y: targetScale, z: targetScale }, 1e3).delay(delay).easing(import_tween.Easing.Elastic.Out);
1646
+ tweenGroup.add(scaleTween);
1647
+ scaleTween.start();
1648
+ if (((_a = nodeData.version_node) == null ? void 0 : _a.is_version) && String(nodeData.version_node.parent_node) === String(sourceNode.id)) {
1649
+ newVersionMeshes.push(targetMesh);
1650
+ }
1546
1651
  });
1547
1652
  linksToExpand.forEach((link) => {
1548
1653
  const linkAlreadyInSceneObjects = allLinks.some(
@@ -1574,7 +1679,12 @@ var userActionHandlers = {
1574
1679
  newLine.material.opacity = 0;
1575
1680
  graphGroup.add(newLine);
1576
1681
  allLinks.push(newLine);
1577
- const opacityTween = new import_tween.Tween(newLine.material).to({ opacity: 0.3 }, 800).delay(500);
1682
+ const targetId = String(newLine.userData.target);
1683
+ const newNodeIndex = newNodesToCreate.findIndex(
1684
+ (item) => String(item.targetNodeId) === targetId
1685
+ );
1686
+ const delay = newNodeIndex !== -1 ? newNodeIndex * 60 : 0;
1687
+ const opacityTween = new import_tween.Tween(newLine.material).to({ opacity: 0.3 }, 800).delay(delay);
1578
1688
  tweenGroup.add(opacityTween);
1579
1689
  opacityTween.start();
1580
1690
  });
@@ -2071,7 +2181,7 @@ var userActionHandlers = {
2071
2181
  },
2072
2182
  syncQuestTemplateFlags: async (context) => {
2073
2183
  var _a, _b;
2074
- const { sceneDataRef, graphDataRef, actions: actions2, setters, sceneConfigId, ownerId, stateRef } = context;
2184
+ const { sceneDataRef, graphDataRef, actions, setters, sceneConfigId, ownerId, stateRef } = context;
2075
2185
  if (!((_a = sceneDataRef.current) == null ? void 0 : _a.process_nodes)) return;
2076
2186
  const allTemplateQuestIds = /* @__PURE__ */ new Set();
2077
2187
  sceneDataRef.current.process_nodes.forEach((proc) => {
@@ -2106,8 +2216,8 @@ var userActionHandlers = {
2106
2216
  const pIdx = graphDataRef.current[sceneConfigId].nodes.findIndex((n) => String(n.id) === String(quest.id));
2107
2217
  if (pIdx !== -1) graphDataRef.current[sceneConfigId].nodes[pIdx] = quest;
2108
2218
  }
2109
- if (actions2.upsert_quest_firestore) {
2110
- await actions2.upsert_quest_firestore(ownerId, sceneConfigId, quest);
2219
+ if (actions.upsert_quest_firestore) {
2220
+ await actions.upsert_quest_firestore(ownerId, sceneConfigId, quest);
2111
2221
  }
2112
2222
  const mesh = (_b = stateRef.current.nodeObjects) == null ? void 0 : _b[String(quest.id)];
2113
2223
  if (mesh) {
@@ -2123,6 +2233,7 @@ var userActionHandlers = {
2123
2233
  if (!nodeData || !sceneDataRef.current) return;
2124
2234
  const nodeIdToDismiss = nodeData.id;
2125
2235
  const strNodeId = String(nodeIdToDismiss);
2236
+ removeAllTimelineElements(stateRef.current);
2126
2237
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2127
2238
  if (ancestryGroup && ancestryLinks) {
2128
2239
  const remainingAncestryLinks = [];
@@ -2154,6 +2265,7 @@ var userActionHandlers = {
2154
2265
  (id) => id !== strNodeIdToKeep
2155
2266
  );
2156
2267
  if (nodeIdsToDismiss.length === 0) return;
2268
+ removeAllTimelineElements(stateRef.current);
2157
2269
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2158
2270
  if (ancestryGroup && ancestryLinks) {
2159
2271
  ancestryLinks.forEach((link) => {
@@ -2176,6 +2288,7 @@ var userActionHandlers = {
2176
2288
  setters.setMultiContextMenu({ visible: false });
2177
2289
  if (!nodeIds || nodeIds.size === 0 || !sceneDataRef.current) return;
2178
2290
  const strNodeIds = Array.from(nodeIds).map(String);
2291
+ removeAllTimelineElements(stateRef.current);
2179
2292
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2180
2293
  if (ancestryGroup && ancestryLinks) {
2181
2294
  const remainingAncestryLinks = [];
@@ -2215,6 +2328,7 @@ var userActionHandlers = {
2215
2328
  stateRef.current.selectedNodes.clear();
2216
2329
  return;
2217
2330
  }
2331
+ removeAllTimelineElements(stateRef.current);
2218
2332
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2219
2333
  if (ancestryGroup && ancestryLinks) {
2220
2334
  const remainingAncestryLinks = [];
@@ -2242,19 +2356,19 @@ var userActionHandlers = {
2242
2356
  },
2243
2357
  handleDeleteMultipleNodes: async (context, nodeIds) => {
2244
2358
  var _a;
2245
- const { stateRef, graphDataRef, sceneDataRef, setters, actions: actions2 } = context;
2359
+ const { stateRef, graphDataRef, sceneDataRef, setters, actions } = context;
2246
2360
  setters.setMultiContextMenu({ visible: false });
2247
2361
  if (!nodeIds || nodeIds.size === 0 || !graphDataRef.current || !sceneDataRef.current)
2248
2362
  return;
2249
2363
  const strNodeIdsToDelete = Array.from(nodeIds).map(String);
2250
- if (actions2.delete_file) {
2364
+ if (actions.delete_file) {
2251
2365
  strNodeIdsToDelete.forEach((id) => {
2252
2366
  const nodeMesh = stateRef.current.nodeObjects[id];
2253
2367
  const nodeData = nodeMesh ? nodeMesh.userData : null;
2254
2368
  if (nodeData) {
2255
2369
  const urls = extractFileUrlsFromProperties(nodeData);
2256
2370
  if (urls.length > 0) {
2257
- Promise.all(urls.map((url) => actions2.delete_file(url))).catch(
2371
+ Promise.all(urls.map((url) => actions.delete_file(url))).catch(
2258
2372
  (err) => console.error(`Erro ao deletar arquivos do node ${id}:`, err)
2259
2373
  );
2260
2374
  }
@@ -2385,9 +2499,9 @@ var userActionHandlers = {
2385
2499
  } else {
2386
2500
  filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2387
2501
  payloadToSave = originalData;
2388
- if (actions2.update_node_counter) {
2502
+ if (actions.update_node_counter) {
2389
2503
  savePromises.push(
2390
- actions2.update_node_counter(ownerId, -nodesToDelete.size)
2504
+ actions.update_node_counter(ownerId, -nodesToDelete.size)
2391
2505
  );
2392
2506
  }
2393
2507
  }
@@ -2432,11 +2546,11 @@ var userActionHandlers = {
2432
2546
  },
2433
2547
  handleDeleteNode: async (context, nodeData) => {
2434
2548
  var _a;
2435
- const { stateRef, graphDataRef, sceneDataRef, setters, actions: actions2 } = context;
2436
- if (actions2.delete_file && nodeData) {
2549
+ const { stateRef, graphDataRef, sceneDataRef, setters, actions } = context;
2550
+ if (actions.delete_file && nodeData) {
2437
2551
  const urls = extractFileUrlsFromProperties(nodeData);
2438
2552
  if (urls.length > 0) {
2439
- Promise.all(urls.map((url) => actions2.delete_file(url))).then(() => console.log("Arquivos do node deletados.")).catch(
2553
+ Promise.all(urls.map((url) => actions.delete_file(url))).then(() => console.log("Arquivos do node deletados.")).catch(
2440
2554
  (err) => console.error("Erro ao deletar arquivos do node:", err)
2441
2555
  );
2442
2556
  }
@@ -2540,8 +2654,8 @@ var userActionHandlers = {
2540
2654
  setters.setSceneVersion((v) => v + 1);
2541
2655
  if (nodeData.is_process) {
2542
2656
  await userActionHandlers.syncQuestTemplateFlags(context);
2543
- } else if (!nodeData.is_quest && actions2.update_node_counter) {
2544
- await actions2.update_node_counter(ownerId, -1);
2657
+ } else if (!nodeData.is_quest && actions.update_node_counter) {
2658
+ await actions.update_node_counter(ownerId, -1);
2545
2659
  }
2546
2660
  } catch (e) {
2547
2661
  console.error("Falha ao salvar remo\xE7\xE3o do Node:", e);
@@ -2559,8 +2673,8 @@ var userActionHandlers = {
2559
2673
  (prev) => String(prev == null ? void 0 : prev.id) === String(nodeIdToDelete) ? null : prev
2560
2674
  );
2561
2675
  removeNodeFromScene(stateRef.current, nodeIdToDelete);
2562
- if (!isQuestOrProcess && actions2.update_node_counter) {
2563
- await actions2.update_node_counter(ownerId, -1);
2676
+ if (!isQuestOrProcess && actions.update_node_counter) {
2677
+ await actions.update_node_counter(ownerId, -1);
2564
2678
  } else if (isQuestOrProcess && setters.setNodeCounter) {
2565
2679
  setters.setNodeCounter((prev) => prev - 1);
2566
2680
  }
@@ -3210,16 +3324,26 @@ var userActionHandlers = {
3210
3324
  links: specificParentData.links,
3211
3325
  milestones: sceneDataRef.current.milestones || []
3212
3326
  };
3213
- await context.actions.save_view_data(
3214
- context.sceneSaveUrl,
3215
- viewFilePayload
3216
- );
3217
- if (context.actions.upsert_quest_firestore) {
3327
+ const isQuestNode = nodeToSave.is_quest || (Array.isArray(nodeToSave.type) ? nodeToSave.type.includes("quest") : nodeToSave.type === "quest");
3328
+ const isProcessNode = nodeToSave.is_process || (Array.isArray(nodeToSave.type) ? nodeToSave.type.includes("process") : nodeToSave.type === "process");
3329
+ if (!isQuestNode && !isProcessNode) {
3330
+ await context.actions.save_view_data(
3331
+ context.sceneSaveUrl,
3332
+ viewFilePayload
3333
+ );
3334
+ }
3335
+ if (isQuestNode && context.actions.upsert_quest_firestore) {
3218
3336
  await context.actions.upsert_quest_firestore(
3219
3337
  context.ownerId,
3220
3338
  context.sceneConfigId,
3221
3339
  nodeToSave
3222
3340
  );
3341
+ } else if (isProcessNode && context.actions.upsert_process_firestore) {
3342
+ await context.actions.upsert_process_firestore(
3343
+ context.ownerId,
3344
+ context.sceneConfigId,
3345
+ nodeToSave
3346
+ );
3223
3347
  }
3224
3348
  } else {
3225
3349
  const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
@@ -3252,7 +3376,7 @@ var userActionHandlers = {
3252
3376
  },
3253
3377
  handleTriggerProcessBranch: async (context, parentProcess, selectedTemplateChild, triggeringQuestId) => {
3254
3378
  var _a, _b, _c;
3255
- const { graphDataRef, sceneDataRef, stateRef, setters, actions: actions2 } = context;
3379
+ const { graphDataRef, sceneDataRef, stateRef, setters, actions } = context;
3256
3380
  if (!graphDataRef.current || !sceneDataRef.current) return;
3257
3381
  try {
3258
3382
  setters.setIsProcessing(true);
@@ -3314,7 +3438,7 @@ var userActionHandlers = {
3314
3438
  }
3315
3439
  ] : originalChildNode.timeline || []
3316
3440
  };
3317
- if (actions2.copy_google_doc_action && versionedChildNode.Links) {
3441
+ if (actions.copy_google_doc_action && versionedChildNode.Links) {
3318
3442
  const docLinkIndex = versionedChildNode.Links.findIndex((l) => l.name === "Google docs");
3319
3443
  if (docLinkIndex !== -1) {
3320
3444
  const originalUrl = versionedChildNode.Links[docLinkIndex].value;
@@ -3322,7 +3446,7 @@ var userActionHandlers = {
3322
3446
  const docIdMatch = originalUrl.match(/\/d\/([a-zA-Z0-9-_]+)/);
3323
3447
  if (docIdMatch && docIdMatch[1]) {
3324
3448
  try {
3325
- const copyRes = await actions2.copy_google_doc_action(
3449
+ const copyRes = await actions.copy_google_doc_action(
3326
3450
  docIdMatch[1],
3327
3451
  nextName,
3328
3452
  (_c = sceneDataRef.current) == null ? void 0 : _c.view_google_folder_id
@@ -3385,15 +3509,15 @@ var userActionHandlers = {
3385
3509
  }
3386
3510
  }
3387
3511
  if (versionedChildNode.is_quest) {
3388
- savePromises.push(actions2.upsert_quest_firestore(ownerId, parentFileId, versionedChildNode));
3389
- savePromises.push(actions2.upsert_quest_link_firestore(ownerId, parentFileId, childVersionLink));
3512
+ savePromises.push(actions.upsert_quest_firestore(ownerId, parentFileId, versionedChildNode));
3513
+ savePromises.push(actions.upsert_quest_link_firestore(ownerId, parentFileId, childVersionLink));
3390
3514
  } else {
3391
- savePromises.push(actions2.upsert_process_firestore(ownerId, parentFileId, versionedChildNode));
3392
- savePromises.push(actions2.upsert_process_link_firestore(ownerId, parentFileId, childVersionLink));
3515
+ savePromises.push(actions.upsert_process_firestore(ownerId, parentFileId, versionedChildNode));
3516
+ savePromises.push(actions.upsert_process_link_firestore(ownerId, parentFileId, childVersionLink));
3393
3517
  }
3394
3518
  sceneDataRef.current.quest_counter = currentCounter;
3395
- savePromises.push(actions2.update_view_counter(ownerId, parentFileId, currentCounter));
3396
- savePromises.push(actions2.upsert_process_firestore(ownerId, parentFileId, parentProcess));
3519
+ savePromises.push(actions.update_view_counter(ownerId, parentFileId, currentCounter));
3520
+ savePromises.push(actions.upsert_process_firestore(ownerId, parentFileId, parentProcess));
3397
3521
  const specificParentData = graphDataRef.current[parentFileId];
3398
3522
  if (specificParentData) {
3399
3523
  specificParentData.nodes.push(...newNodes);
@@ -3560,7 +3684,7 @@ var userActionHandlers = {
3560
3684
  },
3561
3685
  handleInitiateProcess: async (context, processNode) => {
3562
3686
  var _a, _b, _c, _d, _e;
3563
- const { graphDataRef, sceneDataRef, stateRef, setters, actions: actions2 } = context;
3687
+ const { graphDataRef, sceneDataRef, stateRef, setters, actions } = context;
3564
3688
  if (!graphDataRef.current || !sceneDataRef.current) return;
3565
3689
  try {
3566
3690
  setters.setIsProcessing(true);
@@ -3598,9 +3722,9 @@ var userActionHandlers = {
3598
3722
  target: versionedProcessId
3599
3723
  };
3600
3724
  newLinks.push(processVersionLink);
3601
- if (actions2.upsert_process_link_firestore) {
3725
+ if (actions.upsert_process_link_firestore) {
3602
3726
  savePromises.push(
3603
- actions2.upsert_process_link_firestore(
3727
+ actions.upsert_process_link_firestore(
3604
3728
  ownerId,
3605
3729
  parentFileId,
3606
3730
  processVersionLink
@@ -3660,7 +3784,7 @@ var userActionHandlers = {
3660
3784
  }
3661
3785
  ] : originalChildNode.timeline || []
3662
3786
  };
3663
- if (actions2.copy_google_doc_action && versionedChildNode.Links) {
3787
+ if (actions.copy_google_doc_action && versionedChildNode.Links) {
3664
3788
  const docLinkIndex = versionedChildNode.Links.findIndex(
3665
3789
  (l) => l.name === "Google docs"
3666
3790
  );
@@ -3670,7 +3794,7 @@ var userActionHandlers = {
3670
3794
  const docIdMatch = originalUrl.match(/\/d\/([a-zA-Z0-9-_]+)/);
3671
3795
  if (docIdMatch && docIdMatch[1]) {
3672
3796
  try {
3673
- const copyRes = await actions2.copy_google_doc_action(
3797
+ const copyRes = await actions.copy_google_doc_action(
3674
3798
  docIdMatch[1],
3675
3799
  nextName,
3676
3800
  (_d = sceneDataRef.current) == null ? void 0 : _d.view_google_folder_id
@@ -3719,34 +3843,34 @@ var userActionHandlers = {
3719
3843
  newLinks.push(...subResult.newLinks);
3720
3844
  savePromises.push(...subResult.savePromises);
3721
3845
  }
3722
- if (versionedChildNode.is_quest && actions2.upsert_quest_firestore) {
3846
+ if (versionedChildNode.is_quest && actions.upsert_quest_firestore) {
3723
3847
  savePromises.push(
3724
- actions2.upsert_quest_firestore(
3848
+ actions.upsert_quest_firestore(
3725
3849
  ownerId,
3726
3850
  parentFileId,
3727
3851
  versionedChildNode
3728
3852
  )
3729
3853
  );
3730
- if (actions2.upsert_quest_link_firestore) {
3854
+ if (actions.upsert_quest_link_firestore) {
3731
3855
  savePromises.push(
3732
- actions2.upsert_quest_link_firestore(
3856
+ actions.upsert_quest_link_firestore(
3733
3857
  ownerId,
3734
3858
  parentFileId,
3735
3859
  childVersionLink
3736
3860
  )
3737
3861
  );
3738
3862
  }
3739
- } else if (versionedChildNode.is_process && actions2.upsert_process_firestore) {
3863
+ } else if (versionedChildNode.is_process && actions.upsert_process_firestore) {
3740
3864
  savePromises.push(
3741
- actions2.upsert_process_firestore(
3865
+ actions.upsert_process_firestore(
3742
3866
  ownerId,
3743
3867
  parentFileId,
3744
3868
  versionedChildNode
3745
3869
  )
3746
3870
  );
3747
- if (actions2.upsert_process_link_firestore) {
3871
+ if (actions.upsert_process_link_firestore) {
3748
3872
  savePromises.push(
3749
- actions2.upsert_process_link_firestore(
3873
+ actions.upsert_process_link_firestore(
3750
3874
  ownerId,
3751
3875
  parentFileId,
3752
3876
  childVersionLink
@@ -3763,14 +3887,14 @@ var userActionHandlers = {
3763
3887
  versionedProcess.process_abstraction_tree.node_id = versionedProcessId;
3764
3888
  }
3765
3889
  sceneDataRef.current.quest_counter = currentCounter;
3766
- if (actions2.update_view_counter) {
3890
+ if (actions.update_view_counter) {
3767
3891
  savePromises.push(
3768
- actions2.update_view_counter(ownerId, parentFileId, currentCounter)
3892
+ actions.update_view_counter(ownerId, parentFileId, currentCounter)
3769
3893
  );
3770
3894
  }
3771
- if (actions2.upsert_process_firestore) {
3895
+ if (actions.upsert_process_firestore) {
3772
3896
  savePromises.push(
3773
- actions2.upsert_process_firestore(
3897
+ actions.upsert_process_firestore(
3774
3898
  ownerId,
3775
3899
  parentFileId,
3776
3900
  versionedProcess
@@ -3811,7 +3935,7 @@ var userActionHandlers = {
3811
3935
  );
3812
3936
  } else {
3813
3937
  savePromises.push(
3814
- actions2.save_view_data(
3938
+ actions.save_view_data(
3815
3939
  `x_view_dbs/${ownerId}/${parentFileId}`,
3816
3940
  specificParentData
3817
3941
  )
@@ -3862,7 +3986,7 @@ var userActionHandlers = {
3862
3986
  var SECTION_SPLIT_REGEX = /\*\/\s*(\d+)(?::([a-zA-Z0-9-]+))?\s*\//;
3863
3987
  var MENTION_REGEX = /\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g;
3864
3988
  var IMAGE_MENTION_REGEX = /\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g;
3865
- var LINK_REGEX = /\[([^\]]+)\]\((https?:\/\/[^\)]+)\)/g;
3989
+ var LINK_REGEX = /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g;
3866
3990
  var UNIQUE_PROP_TYPES = ["links", "images", "date", "documents"];
3867
3991
  var QUEST_STATUS_COLORS = {
3868
3992
  Backlog: "#64748b",
@@ -3925,7 +4049,7 @@ var resolveDescriptionReference = (refString, availableNodes = [], availableAnce
3925
4049
  /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/
3926
4050
  );
3927
4051
  if (!match) return null;
3928
- const [_, type, itemId, sectionId] = match;
4052
+ const [, type, itemId, sectionId] = match;
3929
4053
  let sourceItem = null;
3930
4054
  if (type === "node") {
3931
4055
  sourceItem = availableNodes.find((n) => String(n.id) === String(itemId));
@@ -3957,7 +4081,7 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
3957
4081
  const allAncestries = ancestryData || [];
3958
4082
  text = text.replace(
3959
4083
  /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g,
3960
- (match, type, itemId, sectionId) => {
4084
+ (match) => {
3961
4085
  const resolved = resolveDescriptionReference(
3962
4086
  match,
3963
4087
  allNodes,
@@ -3986,7 +4110,7 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
3986
4110
  text = text.replace(/^#+\s*/gm, "");
3987
4111
  text = text.replace(/```[\s\S]*?```/g, "[C\xF3digo]");
3988
4112
  text = text.replace(/-\s\[[xX ]\]\s*/g, "\u2022 ");
3989
- text = text.replace(/\[([^\]]+)\]\((https?:\/\/[^\)]+)\)/g, "$1");
4113
+ text = text.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, "$1");
3990
4114
  return text.trim();
3991
4115
  }
3992
4116
  function generateTooltipHtml(data, parentData, ancestryData) {
@@ -4622,7 +4746,8 @@ function createTimelineEntry(actorId, description, type = "comment", extras = {}
4622
4746
  id: img.id || (0, import_uuid.v4)(),
4623
4747
  name: img.name || "",
4624
4748
  value: img.value || ""
4625
- }))
4749
+ })),
4750
+ metadata
4626
4751
  };
4627
4752
  }
4628
4753
  function calculateNextRecurrenceDate(config) {
@@ -4633,14 +4758,15 @@ function calculateNextRecurrenceDate(config) {
4633
4758
  case "daily":
4634
4759
  nextDate.setDate(now.getDate() + 1);
4635
4760
  break;
4636
- case "weekly":
4761
+ case "weekly": {
4637
4762
  const targetDay = parseInt(config.day_of_week, 10);
4638
4763
  const currentDay = now.getDay();
4639
4764
  let daysUntilNext = targetDay - currentDay;
4640
4765
  if (daysUntilNext <= 0) daysUntilNext += 7;
4641
4766
  nextDate.setDate(now.getDate() + daysUntilNext);
4642
4767
  break;
4643
- case "monthly":
4768
+ }
4769
+ case "monthly": {
4644
4770
  const dayOfMonth = parseInt(config.day_of_month || 1, 10);
4645
4771
  let mYear = now.getFullYear();
4646
4772
  let mMonth = now.getMonth();
@@ -4653,7 +4779,8 @@ function calculateNextRecurrenceDate(config) {
4653
4779
  }
4654
4780
  nextDate = mDate;
4655
4781
  break;
4656
- case "custom":
4782
+ }
4783
+ case "custom": {
4657
4784
  if (config.custom_date) {
4658
4785
  const parts = config.custom_date.split("/");
4659
4786
  if (parts.length === 3) {
@@ -4669,6 +4796,7 @@ function calculateNextRecurrenceDate(config) {
4669
4796
  return null;
4670
4797
  }
4671
4798
  break;
4799
+ }
4672
4800
  default:
4673
4801
  return null;
4674
4802
  }
@@ -4681,6 +4809,11 @@ function calculateNextRecurrenceDate(config) {
4681
4809
  confirmed: true
4682
4810
  };
4683
4811
  }
4812
+ var createSafeDate = (year, month = 0, day = 1) => {
4813
+ const d = /* @__PURE__ */ new Date();
4814
+ d.setFullYear(year, month, day);
4815
+ return d;
4816
+ };
4684
4817
  var parseDate = (str) => {
4685
4818
  if (!str) return null;
4686
4819
  const parts = str.split("/");
@@ -4689,7 +4822,7 @@ var parseDate = (str) => {
4689
4822
  const month = parseInt(parts[1], 10) - 1;
4690
4823
  const year = parseInt(parts[2], 10);
4691
4824
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
4692
- return new Date(year, month, day);
4825
+ return createSafeDate(year, month, day);
4693
4826
  }
4694
4827
  }
4695
4828
  return null;
@@ -4744,7 +4877,7 @@ var parseDateProp = (dateProp) => {
4744
4877
  } else if (dateProp.year) {
4745
4878
  const y = parseInt(dateProp.year, 10);
4746
4879
  if (!isNaN(y)) {
4747
- const d = new Date(y, 0, 1);
4880
+ const d = createSafeDate(y, 0, 1);
4748
4881
  info = {
4749
4882
  startDate: d,
4750
4883
  endDate: d,
@@ -4757,8 +4890,8 @@ var parseDateProp = (dateProp) => {
4757
4890
  const sY = parseInt(dateProp.date_interval.start, 10);
4758
4891
  const eY = parseInt(dateProp.date_interval.end, 10) || sY;
4759
4892
  if (!isNaN(sY)) {
4760
- const sd = new Date(sY, 0, 1);
4761
- const ed = new Date(eY, 11, 31);
4893
+ const sd = createSafeDate(sY, 0, 1);
4894
+ const ed = createSafeDate(eY, 11, 31);
4762
4895
  info = {
4763
4896
  startDate: sd,
4764
4897
  endDate: ed,
@@ -5587,6 +5720,7 @@ function XViewSidebar({
5587
5720
  const [showList, setShowList] = (0, import_react3.useState)(false);
5588
5721
  const [selectedNodeId, setSelectedNodeId] = (0, import_react3.useState)(null);
5589
5722
  const [isSaving, setIsSaving] = (0, import_react3.useState)(false);
5723
+ const [isNavigatingBack, setIsNavigatingBack] = (0, import_react3.useState)(false);
5590
5724
  const [activeFilters, setActiveFilters] = (0, import_react3.useState)([]);
5591
5725
  const [isFilterMenuOpen, setIsFilterMenuOpen] = (0, import_react3.useState)(false);
5592
5726
  const [filterCategory, setFilterCategory] = (0, import_react3.useState)("Type");
@@ -5792,12 +5926,16 @@ function XViewSidebar({
5792
5926
  };
5793
5927
  const handleAddFilter = () => {
5794
5928
  if (!filterValue.trim()) return;
5795
- setActiveFilters((prev) => [
5796
- ...prev,
5797
- { type: filterCategory, value: filterValue.trim() }
5798
- ]);
5929
+ const exists = activeFilters.some(
5930
+ (f) => f.type === filterCategory && f.value.trim().toLowerCase() === filterValue.trim().toLowerCase()
5931
+ );
5932
+ if (!exists) {
5933
+ setActiveFilters((prev) => [
5934
+ ...prev,
5935
+ { type: filterCategory, value: filterValue.trim() }
5936
+ ]);
5937
+ }
5799
5938
  setFilterValue("");
5800
- setIsFilterMenuOpen(false);
5801
5939
  };
5802
5940
  const handleRemoveFilter = (index) => {
5803
5941
  setActiveFilters((prev) => prev.filter((_, i) => i !== index));
@@ -6331,15 +6469,63 @@ function XViewSidebar({
6331
6469
  /* @__PURE__ */ import_react3.default.createElement("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" })
6332
6470
  ),
6333
6471
  "Filters"
6334
- ), isFilterMenuOpen && /* @__PURE__ */ import_react3.default.createElement("div", { className: "absolute right-0 top-full mt-2 w-56 p-3 rounded-lg bg-slate-900 border border-white/10 shadow-xl z-50 flex flex-col gap-3 animate-in fade-in zoom-in-95 duration-100" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color", "Dataset"].map((opt) => /* @__PURE__ */ import_react3.default.createElement(
6472
+ ), isFilterMenuOpen && /* @__PURE__ */ import_react3.default.createElement("div", { className: "absolute right-0 top-full mt-2 w-64 p-3 rounded-lg bg-slate-900 border border-white/10 shadow-xl z-50 flex flex-col gap-3 animate-in fade-in zoom-in-95 duration-100" }, activeFilters.length > 0 && /* @__PURE__ */ import_react3.default.createElement("div", { className: "space-y-1.5 border-b border-white/10 pb-2 mb-0.5" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Ativos (", activeFilters.length, ")"), /* @__PURE__ */ import_react3.default.createElement(
6473
+ "button",
6474
+ {
6475
+ type: "button",
6476
+ onClick: () => setActiveFilters([]),
6477
+ className: "text-[9px] text-red-400 hover:text-red-300 transition-colors uppercase font-bold tracking-wider"
6478
+ },
6479
+ "Limpar tudo"
6480
+ )), /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex flex-wrap gap-1 max-h-24 overflow-y-auto custom-scrollbar pt-1" }, activeFilters.map((filter, idx) => /* @__PURE__ */ import_react3.default.createElement(
6481
+ "span",
6482
+ {
6483
+ key: idx,
6484
+ className: "flex items-center gap-1 bg-indigo-500/20 text-indigo-200 border border-indigo-500/30 px-1.5 py-0.5 rounded text-[10px] max-w-full font-medium"
6485
+ },
6486
+ filter.type === "Color" && /* @__PURE__ */ import_react3.default.createElement(
6487
+ "span",
6488
+ {
6489
+ className: "inline-block w-2 h-2 rounded-full border border-white/20",
6490
+ style: { backgroundColor: filter.value }
6491
+ }
6492
+ ),
6493
+ /* @__PURE__ */ import_react3.default.createElement("span", { className: "truncate max-w-[100px]" }, filter.type, ": ", filter.value),
6494
+ /* @__PURE__ */ import_react3.default.createElement(
6495
+ "button",
6496
+ {
6497
+ type: "button",
6498
+ onClick: () => handleRemoveFilter(idx),
6499
+ className: "hover:text-red-400 transition-colors ml-0.5",
6500
+ title: "Remover"
6501
+ },
6502
+ /* @__PURE__ */ import_react3.default.createElement(
6503
+ "svg",
6504
+ {
6505
+ xmlns: "http://www.w3.org/2000/svg",
6506
+ width: "10",
6507
+ height: "10",
6508
+ viewBox: "0 0 24 24",
6509
+ fill: "none",
6510
+ stroke: "currentColor",
6511
+ strokeWidth: "3",
6512
+ strokeLinecap: "round",
6513
+ strokeLinejoin: "round"
6514
+ },
6515
+ /* @__PURE__ */ import_react3.default.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
6516
+ /* @__PURE__ */ import_react3.default.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
6517
+ )
6518
+ )
6519
+ )))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color", "Dataset"].map((opt) => /* @__PURE__ */ import_react3.default.createElement(
6335
6520
  "button",
6336
6521
  {
6337
6522
  key: opt,
6523
+ type: "button",
6338
6524
  onClick: () => {
6339
6525
  setFilterCategory(opt);
6340
6526
  setFilterValue("");
6341
6527
  },
6342
- className: `flex-1 text-xs py-1 rounded transition-colors ${filterCategory === opt ? "bg-indigo-600 text-white" : "text-slate-400 hover:text-slate-200"}`
6528
+ className: `flex-1 text-[11px] py-1 rounded transition-colors ${filterCategory === opt ? "bg-indigo-600 text-white font-medium" : "text-slate-400 hover:text-slate-200"}`
6343
6529
  },
6344
6530
  opt
6345
6531
  )))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Valor"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "relative", ref: typeDropdownRef }, /* @__PURE__ */ import_react3.default.createElement(
@@ -6376,11 +6562,12 @@ function XViewSidebar({
6376
6562
  )))), /* @__PURE__ */ import_react3.default.createElement(
6377
6563
  "button",
6378
6564
  {
6565
+ type: "button",
6379
6566
  onClick: handleAddFilter,
6380
6567
  disabled: !filterValue.trim(),
6381
- className: "w-full py-1.5 bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white text-xs font-medium rounded transition-colors"
6568
+ className: "w-full py-1.5 bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white text-xs font-semibold rounded transition-colors"
6382
6569
  },
6383
- "Aplicar Filtro"
6570
+ "Adicionar Filtro"
6384
6571
  ))))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "h-px bg-white/10" }), /* @__PURE__ */ import_react3.default.createElement("button", { onClick: onOpenImportModal, className: buttonBaseClass }, /* @__PURE__ */ import_react3.default.createElement(import_fi2.FiPlus, { size: 16 }), "Importar Datasets / Views"), importSuccessMessage && /* @__PURE__ */ import_react3.default.createElement("p", { className: "text-[10px] text-emerald-400 animate-pulse px-1 font-medium -mt-1" }, importSuccessMessage), /* @__PURE__ */ import_react3.default.createElement("section", { className: "space-y-2" }, /* @__PURE__ */ import_react3.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Lentes"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "relative", ref: questLensRef }, /* @__PURE__ */ import_react3.default.createElement(
6385
6572
  "button",
6386
6573
  {
@@ -6482,7 +6669,45 @@ function XViewSidebar({
6482
6669
  className: isSaving ? buttonDisabledClass : buttonBaseClass
6483
6670
  },
6484
6671
  isSaving ? "Salvando..." : "Atualizar cen\xE1rio inicial"
6485
- ), /* @__PURE__ */ import_react3.default.createElement("button", { onClick: onNavigateBack, className: buttonBaseClass }, "Voltar ao Dashboard"))),
6672
+ ), /* @__PURE__ */ import_react3.default.createElement(
6673
+ "button",
6674
+ {
6675
+ onClick: () => {
6676
+ setIsNavigatingBack(true);
6677
+ onNavigateBack();
6678
+ },
6679
+ disabled: isNavigatingBack,
6680
+ className: isNavigatingBack ? buttonDisabledClass : buttonBaseClass
6681
+ },
6682
+ isNavigatingBack ? /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement(
6683
+ "svg",
6684
+ {
6685
+ className: "animate-spin h-4 w-4",
6686
+ xmlns: "http://www.w3.org/2000/svg",
6687
+ fill: "none",
6688
+ viewBox: "0 0 24 24"
6689
+ },
6690
+ /* @__PURE__ */ import_react3.default.createElement(
6691
+ "circle",
6692
+ {
6693
+ className: "opacity-25",
6694
+ cx: "12",
6695
+ cy: "12",
6696
+ r: "10",
6697
+ stroke: "currentColor",
6698
+ strokeWidth: "4"
6699
+ }
6700
+ ),
6701
+ /* @__PURE__ */ import_react3.default.createElement(
6702
+ "path",
6703
+ {
6704
+ className: "opacity-75",
6705
+ fill: "currentColor",
6706
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
6707
+ }
6708
+ )
6709
+ ), "Voltar ao Dashboard") : "Voltar ao Dashboard"
6710
+ ))),
6486
6711
  contextLabel && /* @__PURE__ */ import_react3.default.createElement("div", { className: "px-4 py-3 border-t border-white/10 bg-slate-900/50 truncate text-sm font-medium text-slate-200" }, contextLabel),
6487
6712
  /* @__PURE__ */ import_react3.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" })
6488
6713
  ),
@@ -6551,6 +6776,11 @@ function CustomPropertyDisplay({
6551
6776
  const file = (_a = e.target.files) == null ? void 0 : _a[0];
6552
6777
  const targetIndex = currentUploadTargetRef.current;
6553
6778
  if (!file || targetIndex === null || !onUploadFile) return;
6779
+ const MAX_SIZE = 10 * 1024 * 1024;
6780
+ if (file.size > MAX_SIZE) {
6781
+ alert("O arquivo \xE9 muito grande. O limite m\xE1ximo de tamanho permitido \xE9 de 10MB.");
6782
+ return;
6783
+ }
6554
6784
  try {
6555
6785
  setUploadingItemIndex(targetIndex);
6556
6786
  const formData = new FormData();
@@ -6635,9 +6865,9 @@ function CustomPropertyDisplay({
6635
6865
  };
6636
6866
  const handleRemoveListItem = (j) => setTempProp((p) => ({ ...p, value: p.value.filter((_, k) => k !== j) }));
6637
6867
  const handleListItemChange = (j, f, v) => setTempProp((p) => {
6638
- const newValue2 = [...p.value];
6639
- newValue2[j] = { ...newValue2[j], [f]: v };
6640
- return { ...p, value: newValue2 };
6868
+ const newValue = [...p.value];
6869
+ newValue[j] = { ...newValue[j], [f]: v };
6870
+ return { ...p, value: newValue };
6641
6871
  });
6642
6872
  const baseInput = "w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60 transition-all duration-150";
6643
6873
  const renderEditView = () => {
@@ -6691,14 +6921,14 @@ function CustomPropertyDisplay({
6691
6921
  const inputClass = `${baseInput} ${noSpinnerClass}`;
6692
6922
  const handleDateTypeChange = (newDateType) => {
6693
6923
  var _a3, _b2, _c2;
6694
- let newValue2 = { type: newDateType };
6924
+ let newValue = { type: newDateType };
6695
6925
  if (newDateType === "Date Interval") {
6696
- newValue2.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
6697
- newValue2.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
6926
+ newValue.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
6927
+ newValue.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
6698
6928
  } else {
6699
- newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
6929
+ newValue.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
6700
6930
  }
6701
- handlePropChange("value", newValue2);
6931
+ handlePropChange("value", newValue);
6702
6932
  };
6703
6933
  const handleDateValueChange = (dateField, dateValue) => {
6704
6934
  handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
@@ -9495,7 +9725,7 @@ function CreateAncestryPanel({
9495
9725
  }
9496
9726
  };
9497
9727
  const sideActions = (0, import_react11.useMemo)(() => {
9498
- const actions2 = { left: null, right: null };
9728
+ const actions = { left: null, right: null };
9499
9729
  const isInBranch = branchStack.length > 0;
9500
9730
  if (internalHighlightedNodeId) {
9501
9731
  const found = findNodePath2(activeTree, internalHighlightedNodeId);
@@ -9503,13 +9733,13 @@ function CreateAncestryPanel({
9503
9733
  const branches = found.node.parallel_branches || [];
9504
9734
  const leftBranch = branches.find((b) => (b.direction || "right") === "left");
9505
9735
  if (leftBranch) {
9506
- actions2.left = {
9736
+ actions.left = {
9507
9737
  type: "open",
9508
9738
  label: "Editar Branch Esq.",
9509
9739
  onClick: () => handleBranchNav(internalHighlightedNodeId, "open", "left")
9510
9740
  };
9511
9741
  } else {
9512
- actions2.left = {
9742
+ actions.left = {
9513
9743
  type: "create",
9514
9744
  label: "Criar Branch Esq.",
9515
9745
  onClick: () => handleBranchNav(internalHighlightedNodeId, "open", "left")
@@ -9517,32 +9747,32 @@ function CreateAncestryPanel({
9517
9747
  }
9518
9748
  const rightBranch = branches.find((b) => (b.direction || "right") === "right");
9519
9749
  if (rightBranch) {
9520
- actions2.right = {
9750
+ actions.right = {
9521
9751
  type: "open",
9522
9752
  label: "Editar Branch Dir.",
9523
9753
  onClick: () => handleBranchNav(internalHighlightedNodeId, "open", "right")
9524
9754
  };
9525
9755
  } else {
9526
- actions2.right = {
9756
+ actions.right = {
9527
9757
  type: "create",
9528
9758
  label: "Criar Branch Dir.",
9529
9759
  onClick: () => handleBranchNav(internalHighlightedNodeId, "open", "right")
9530
9760
  };
9531
9761
  }
9532
9762
  }
9533
- return actions2;
9763
+ return actions;
9534
9764
  }
9535
9765
  if (isInBranch && isAtStartOfBranch) {
9536
9766
  const lastStep = branchStack[branchStack.length - 1];
9537
9767
  const entryDir = lastStep.entryDirection || "right";
9538
9768
  const backSide = entryDir === "right" ? "left" : "right";
9539
- actions2[backSide] = {
9769
+ actions[backSide] = {
9540
9770
  type: "back",
9541
9771
  label: "Voltar",
9542
9772
  onClick: () => handleBranchNav(null, "back")
9543
9773
  };
9544
9774
  }
9545
- return actions2;
9775
+ return actions;
9546
9776
  }, [internalHighlightedNodeId, branchStack, activeTree, isAtStartOfBranch]);
9547
9777
  const applyDescriptionToTree = (baseTree, descText, descSections) => {
9548
9778
  const rootTreeClone = JSON.parse(JSON.stringify(baseTree));
@@ -11580,9 +11810,9 @@ function InSceneCreationForm({
11580
11810
  };
11581
11811
  const handleToggleImageMode = () => {
11582
11812
  var _a2, _b;
11583
- const newValue2 = !useImageAsTexture;
11584
- setUseImageAsTexture(newValue2);
11585
- if (newValue2) {
11813
+ const newValue = !useImageAsTexture;
11814
+ setUseImageAsTexture(newValue);
11815
+ if (newValue) {
11586
11816
  const firstImageProp = customProps.find((p) => p.type === "images");
11587
11817
  if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
11588
11818
  const url = firstImageProp.value[0].value;
@@ -12057,9 +12287,9 @@ function InSceneVersionForm({
12057
12287
  };
12058
12288
  const handleToggleImageMode = () => {
12059
12289
  var _a, _b;
12060
- const newValue2 = !useImageAsTexture;
12061
- setUseImageAsTexture(newValue2);
12062
- if (newValue2) {
12290
+ const newValue = !useImageAsTexture;
12291
+ setUseImageAsTexture(newValue);
12292
+ if (newValue) {
12063
12293
  const firstImageProp = customProps.find((p) => p.type === "images");
12064
12294
  if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
12065
12295
  const url = firstImageProp.value[0].value;
@@ -13631,6 +13861,7 @@ function NodeDetailsPanel({
13631
13861
  }
13632
13862
  };
13633
13863
  const handleToggleImageMode = () => {
13864
+ const newValue = !useImageAsTexture;
13634
13865
  setUseImageAsTexture(newValue);
13635
13866
  setHasUnsavedChanges(true);
13636
13867
  let activeUrl = null;
@@ -19107,7 +19338,13 @@ function MilestoneManager({
19107
19338
  // src/components/UpgradeModal.jsx
19108
19339
  var import_react36 = __toESM(require("react"));
19109
19340
  var import_lucide_react = require("lucide-react");
19110
- function UpgradeModal({ isOpen, onClose, nodeCounter }) {
19341
+ function UpgradeModal({
19342
+ isOpen,
19343
+ onClose,
19344
+ nodeCounter,
19345
+ nodeLimit = 100,
19346
+ onUpgrade
19347
+ }) {
19111
19348
  if (!isOpen) return null;
19112
19349
  return /* @__PURE__ */ import_react36.default.createElement("div", { className: "fixed inset-0 z-[10000] flex items-center justify-center p-4" }, /* @__PURE__ */ import_react36.default.createElement("style", null, `
19113
19350
  @keyframes modalShow {
@@ -19133,12 +19370,16 @@ function UpgradeModal({ isOpen, onClose, nodeCounter }) {
19133
19370
  className: "absolute top-6 right-6 p-2 rounded-full hover:bg-white/5 text-white/40 hover:text-white transition-all duration-300"
19134
19371
  },
19135
19372
  /* @__PURE__ */ import_react36.default.createElement(import_lucide_react.X, { size: 20 })
19136
- ), /* @__PURE__ */ import_react36.default.createElement("div", { className: "p-12 flex flex-col items-center text-center" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-20 h-20 rounded-[2rem] bg-gradient-to-br from-sky-400 to-indigo-600 p-[1px] mb-8 shadow-lg shadow-sky-500/20" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-full h-full rounded-[2rem] bg-[#0d1117] flex items-center justify-center" }, /* @__PURE__ */ import_react36.default.createElement(import_lucide_react.Zap, { className: "text-sky-400 fill-sky-400/20", size: 32 }))), /* @__PURE__ */ import_react36.default.createElement("h2", { className: "text-3xl font-bold text-white mb-4 tracking-tight" }, "Limite de Nodes", " ", /* @__PURE__ */ import_react36.default.createElement("span", { className: "bg-clip-text text-transparent bg-gradient-to-r from-sky-400 to-indigo-400" }, "Atingido")), /* @__PURE__ */ import_react36.default.createElement("p", { className: "text-white/60 text-lg leading-relaxed mb-10 max-w-md" }, "Voc\xEA atingiu o limite de", " ", /* @__PURE__ */ import_react36.default.createElement("span", { className: "text-white font-semibold" }, "50.000 nodes"), " do seu plano Free. Seu universo est\xE1 crescendo r\xE1pido!"), /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-full grid grid-cols-2 gap-4 mb-10" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "bg-white/5 border border-white/10 rounded-3xl p-6 backdrop-blur-md" }, /* @__PURE__ */ import_react36.default.createElement("p", { className: "text-white/30 text-[10px] font-bold uppercase tracking-widest mb-1" }, "Status Atual"), /* @__PURE__ */ import_react36.default.createElement("p", { className: "text-2xl font-bold text-white" }, nodeCounter, " / 50000")), /* @__PURE__ */ import_react36.default.createElement("div", { className: "bg-white/5 border border-white/10 rounded-3xl p-6 backdrop-blur-md" }, /* @__PURE__ */ import_react36.default.createElement("p", { className: "text-white/30 text-[10px] font-bold uppercase tracking-widest mb-1" }, "Pr\xF3ximo Plano"), /* @__PURE__ */ import_react36.default.createElement("p", { className: "text-2xl font-bold text-sky-400" }, "Ilimitado"))), /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-full space-y-4 mb-10 text-left px-4" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "flex items-center gap-3 text-white/70 text-sm" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-6 h-6 rounded-full bg-sky-500/10 flex items-center justify-center shrink-0" }, /* @__PURE__ */ import_react36.default.createElement(import_lucide_react.Rocket, { size: 14, className: "text-sky-400" })), /* @__PURE__ */ import_react36.default.createElement("span", null, "Nodes, Quests e Processos ilimitados")), /* @__PURE__ */ import_react36.default.createElement("div", { className: "flex items-center gap-3 text-white/70 text-sm" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-6 h-6 rounded-full bg-sky-500/10 flex items-center justify-center shrink-0" }, /* @__PURE__ */ import_react36.default.createElement(import_lucide_react.ShieldCheck, { size: 14, className: "text-sky-400" })), /* @__PURE__ */ import_react36.default.createElement("span", null, "Suporte priorit\xE1rio e recursos exclusivos"))), /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-full flex flex-col gap-4" }, /* @__PURE__ */ import_react36.default.createElement(
19373
+ ), /* @__PURE__ */ import_react36.default.createElement("div", { className: "p-12 flex flex-col items-center text-center" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-20 h-20 rounded-[2rem] bg-gradient-to-br from-sky-400 to-indigo-600 p-[1px] mb-8 shadow-lg shadow-sky-500/20" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-full h-full rounded-[2rem] bg-[#0d1117] flex items-center justify-center" }, /* @__PURE__ */ import_react36.default.createElement(import_lucide_react.Zap, { className: "text-sky-400 fill-sky-400/20", size: 32 }))), /* @__PURE__ */ import_react36.default.createElement("h2", { className: "text-3xl font-bold text-white mb-4 tracking-tight" }, "Limite de Nodes", " ", /* @__PURE__ */ import_react36.default.createElement("span", { className: "bg-clip-text text-transparent bg-gradient-to-r from-sky-400 to-indigo-400" }, "Atingido")), /* @__PURE__ */ import_react36.default.createElement("p", { className: "text-white/60 text-lg leading-relaxed mb-10 max-w-md" }, "Voc\xEA atingiu o limite de", " ", /* @__PURE__ */ import_react36.default.createElement("span", { className: "text-white font-semibold" }, nodeLimit, " nodes"), " ", "do seu plano Free. Seu universo est\xE1 crescendo r\xE1pido!"), /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-full grid grid-cols-2 gap-4 mb-10" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "bg-white/5 border border-white/10 rounded-3xl p-6 backdrop-blur-md" }, /* @__PURE__ */ import_react36.default.createElement("p", { className: "text-white/30 text-[10px] font-bold uppercase tracking-widest mb-1" }, "Status Atual"), /* @__PURE__ */ import_react36.default.createElement("p", { className: "text-2xl font-bold text-white" }, nodeCounter, " / ", nodeLimit)), /* @__PURE__ */ import_react36.default.createElement("div", { className: "bg-white/5 border border-white/10 rounded-3xl p-6 backdrop-blur-md" }, /* @__PURE__ */ import_react36.default.createElement("p", { className: "text-white/30 text-[10px] font-bold uppercase tracking-widest mb-1" }, "Pr\xF3ximo Plano"), /* @__PURE__ */ import_react36.default.createElement("p", { className: "text-2xl font-bold text-sky-400" }, "Ilimitado"))), /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-full space-y-4 mb-10 text-left px-4" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "flex items-center gap-3 text-white/70 text-sm" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-6 h-6 rounded-full bg-sky-500/10 flex items-center justify-center shrink-0" }, /* @__PURE__ */ import_react36.default.createElement(import_lucide_react.Rocket, { size: 14, className: "text-sky-400" })), /* @__PURE__ */ import_react36.default.createElement("span", null, "Cria\xE7\xE3o de Nodes ilimitada")), /* @__PURE__ */ import_react36.default.createElement("div", { className: "flex items-center gap-3 text-white/70 text-sm" }, /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-6 h-6 rounded-full bg-sky-500/10 flex items-center justify-center shrink-0" }, /* @__PURE__ */ import_react36.default.createElement(import_lucide_react.ShieldCheck, { size: 14, className: "text-sky-400" })), /* @__PURE__ */ import_react36.default.createElement("span", null, "Acesso expandido \xE0s funcionalidades colaborativas e de comunidade"))), /* @__PURE__ */ import_react36.default.createElement("div", { className: "w-full flex flex-col gap-4" }, /* @__PURE__ */ import_react36.default.createElement(
19137
19374
  "button",
19138
19375
  {
19139
19376
  className: "w-full py-5 rounded-[1.5rem] bg-gradient-to-r from-sky-500 to-indigo-600 text-white font-bold text-sm uppercase tracking-widest hover:shadow-[0_0_30px_-5px_rgba(56,189,248,0.5)] hover:scale-[1.02] active:scale-95 transition-all duration-300 flex items-center justify-center gap-2",
19140
- onClick: () => {
19141
- alert("Redirecionando para upgrade... (Em breve)");
19377
+ onClick: async () => {
19378
+ if (onUpgrade) {
19379
+ await onUpgrade();
19380
+ } else {
19381
+ alert("Redirecionando para upgrade... (Em breve)");
19382
+ }
19142
19383
  onClose();
19143
19384
  }
19144
19385
  },
@@ -19179,7 +19420,62 @@ var TIMELINE_LAYER_SPACING_Y = 25;
19179
19420
  var TIMELINE_NODE_SPACING_X = 20;
19180
19421
  var TIMELINE_INTERVAL_BAR_RADIUS = 0.2;
19181
19422
  var TIMELINE_INTERVAL_BAR_OPACITY = 0.85;
19182
- var TIMELINE_GOLD_COLOR = 16766720;
19423
+ var TIMELINE_BLUE_COLOR = 30719;
19424
+ var TIMELINE_BRACKET_WIDTH = 4;
19425
+ var createTimelineIntervalBracket = () => {
19426
+ const group = new THREE3.Group();
19427
+ group.name = "timelineIntervalBracket";
19428
+ const barMaterial = new THREE3.MeshStandardMaterial({
19429
+ color: TIMELINE_BLUE_COLOR,
19430
+ emissive: 17578,
19431
+ emissiveIntensity: 0.5,
19432
+ transparent: true,
19433
+ opacity: TIMELINE_INTERVAL_BAR_OPACITY,
19434
+ roughness: 0.3,
19435
+ metalness: 0.8
19436
+ });
19437
+ const geoHorizontal = new THREE3.CylinderGeometry(
19438
+ TIMELINE_INTERVAL_BAR_RADIUS,
19439
+ TIMELINE_INTERVAL_BAR_RADIUS,
19440
+ 1,
19441
+ 16
19442
+ );
19443
+ const geoVertical = new THREE3.CylinderGeometry(
19444
+ TIMELINE_INTERVAL_BAR_RADIUS,
19445
+ TIMELINE_INTERVAL_BAR_RADIUS,
19446
+ 1,
19447
+ 16
19448
+ );
19449
+ const bottomArm = new THREE3.Mesh(geoHorizontal, barMaterial);
19450
+ bottomArm.rotation.z = Math.PI / 2;
19451
+ const verticalStem = new THREE3.Mesh(geoVertical, barMaterial);
19452
+ const topArm = new THREE3.Mesh(geoHorizontal.clone(), barMaterial);
19453
+ topArm.rotation.z = Math.PI / 2;
19454
+ group.add(bottomArm);
19455
+ group.add(verticalStem);
19456
+ group.add(topArm);
19457
+ group.userData.bottomArm = bottomArm;
19458
+ group.userData.verticalStem = verticalStem;
19459
+ group.userData.topArm = topArm;
19460
+ return group;
19461
+ };
19462
+ var updateTimelineIntervalBracket = (group, startPos, endPos) => {
19463
+ if (!group || !group.userData.bottomArm) return;
19464
+ const { bottomArm, verticalStem, topArm } = group.userData;
19465
+ const w = TIMELINE_BRACKET_WIDTH;
19466
+ const z = -0.5;
19467
+ const p1 = new THREE3.Vector3(startPos.x, startPos.y, z);
19468
+ const p2 = new THREE3.Vector3(startPos.x - w, startPos.y, z);
19469
+ const p3 = new THREE3.Vector3(endPos.x - w, endPos.y, z);
19470
+ const p4 = new THREE3.Vector3(endPos.x, endPos.y, z);
19471
+ bottomArm.scale.y = w;
19472
+ bottomArm.position.set((p1.x + p2.x) / 2, p1.y, z);
19473
+ const verticalLen = Math.abs(p3.y - p2.y);
19474
+ verticalStem.scale.y = Math.max(verticalLen, 1e-3);
19475
+ verticalStem.position.set(p2.x, (p2.y + p3.y) / 2, z);
19476
+ topArm.scale.y = w;
19477
+ topArm.position.set((p3.x + p4.x) / 2, p3.y, z);
19478
+ };
19183
19479
  var QUEST_LENS_COLUMN_SPACING = 80;
19184
19480
  var QUEST_LENS_ROW_SPACING = 40;
19185
19481
  var getAllNodeIdsFromTree = (treeNode) => {
@@ -19246,7 +19542,8 @@ function XViewScene({
19246
19542
  delete_process_firestore,
19247
19543
  upsert_process_link_firestore,
19248
19544
  delete_process_link_firestore,
19249
- update_node_counter
19545
+ update_node_counter,
19546
+ onUpgrade
19250
19547
  }) {
19251
19548
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
19252
19549
  const { data: session, status } = (0, import_react38.useSession)();
@@ -19263,6 +19560,11 @@ function XViewScene({
19263
19560
  }
19264
19561
  return null;
19265
19562
  }, [encryptedConfig, session]);
19563
+ (0, import_react37.useEffect)(() => {
19564
+ if (viewParams == null ? void 0 : viewParams.name) {
19565
+ document.title = `${viewParams.name} - x.plore`;
19566
+ }
19567
+ }, [viewParams == null ? void 0 : viewParams.name]);
19266
19568
  (0, import_react37.useEffect)(() => {
19267
19569
  async function verifyPermission() {
19268
19570
  if (!viewParams || !session || !check_user_permission) return;
@@ -19426,6 +19728,7 @@ function XViewScene({
19426
19728
  const [isMilestoneManagerOpen, setIsMilestoneManagerOpen] = (0, import_react37.useState)(false);
19427
19729
  const [nodeCounter, setNodeCounter] = (0, import_react37.useState)(0);
19428
19730
  const [isUpgradeModalOpen, setIsUpgradeModalOpen] = (0, import_react37.useState)(false);
19731
+ const [nodeLimit, setNodeLimit] = (0, import_react37.useState)(100);
19429
19732
  const mountRef = (0, import_react37.useRef)(null);
19430
19733
  const tooltipRef = (0, import_react37.useRef)(null);
19431
19734
  const formRef = (0, import_react37.useRef)(null);
@@ -19861,7 +20164,9 @@ function XViewScene({
19861
20164
  const month = parseInt(parts[1], 10) - 1;
19862
20165
  const year = parseInt(parts[2], 10);
19863
20166
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
19864
- return new Date(year, month, day);
20167
+ const d = /* @__PURE__ */ new Date();
20168
+ d.setFullYear(year, month, day);
20169
+ return d;
19865
20170
  }
19866
20171
  }
19867
20172
  return null;
@@ -19918,10 +20223,12 @@ function XViewScene({
19918
20223
  });
19919
20224
  const timelineWidth = (columns.length > 0 ? columns.length - 1 : 0) * TIMELINE_NODE_SPACING_X;
19920
20225
  const offsetX = -timelineWidth / 2;
20226
+ const allFinalPoints = [];
19921
20227
  timelineNodes.forEach((node) => {
19922
20228
  const { mesh, type, startDate, endDate, label, columnIndex, isUndated } = node;
19923
20229
  const x = columnIndex * TIMELINE_NODE_SPACING_X + offsetX;
19924
20230
  const y = isUndated ? 0 : timeToYMap.get(startDate.getTime());
20231
+ allFinalPoints.push(new THREE3.Vector3(x, y, 0));
19925
20232
  const timelineTween = new import_tween2.Tween(mesh.position).to({ x, y, z: 0 }, 1500).easing(import_tween2.Easing.Cubic.InOut);
19926
20233
  tweenGroup.add(timelineTween);
19927
20234
  timelineTween.start();
@@ -19934,25 +20241,16 @@ function XViewScene({
19934
20241
  if (type === "interval") {
19935
20242
  const endY = timeToYMap.get(endDate.getTime());
19936
20243
  if (endY > y) {
19937
- const barGeometry = new THREE3.CylinderGeometry(
19938
- TIMELINE_INTERVAL_BAR_RADIUS,
19939
- TIMELINE_INTERVAL_BAR_RADIUS,
19940
- 1,
19941
- 16
19942
- );
19943
- const barMaterial = new THREE3.MeshStandardMaterial({
19944
- color: TIMELINE_GOLD_COLOR,
19945
- emissive: 12092939,
19946
- emissiveIntensity: 0.4,
19947
- transparent: true,
19948
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
19949
- roughness: 0.3,
19950
- metalness: 0.8
19951
- });
19952
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20244
+ allFinalPoints.push(new THREE3.Vector3(x, endY, 0));
20245
+ const intervalBar = createTimelineIntervalBracket();
19953
20246
  intervalBar.userData.associatedNodeId = mesh.userData.id;
19954
20247
  const endPosition = new THREE3.Vector3(x, endY, -0.5);
19955
20248
  intervalBar.userData.endPosition = endPosition;
20249
+ updateTimelineIntervalBracket(
20250
+ intervalBar,
20251
+ new THREE3.Vector3(x, y, 0),
20252
+ endPosition
20253
+ );
19956
20254
  timelineIntervalsGroup.add(intervalBar);
19957
20255
  const endLabelText = endDate.getFullYear().toString();
19958
20256
  const endLabel = createTextSprite(endLabelText, 48);
@@ -19965,7 +20263,21 @@ function XViewScene({
19965
20263
  }
19966
20264
  }
19967
20265
  });
19968
- }, []);
20266
+ if (allFinalPoints.length > 0) {
20267
+ const boundingBox = new THREE3.Box3().setFromPoints(allFinalPoints);
20268
+ const center = new THREE3.Vector3();
20269
+ boundingBox.getCenter(center);
20270
+ const size = new THREE3.Vector3();
20271
+ boundingBox.getSize(size);
20272
+ const spread = Math.max(size.x, size.y, size.z, 1);
20273
+ const desiredDistance = Math.max(
20274
+ x_view_config.CAMERA_ZOOM_DISTANCE,
20275
+ spread * 0.75
20276
+ );
20277
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
20278
+ tweenToTarget(center, zoomFactor);
20279
+ }
20280
+ }, [tweenToTarget]);
19969
20281
  const handleVersionTimeline = (0, import_react37.useCallback)(
19970
20282
  (sourceMesh, versionMeshes) => {
19971
20283
  const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
@@ -20001,7 +20313,9 @@ function XViewScene({
20001
20313
  const month = parseInt(parts[1], 10) - 1;
20002
20314
  const year = parseInt(parts[2], 10);
20003
20315
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
20004
- return new Date(year, month, day);
20316
+ const d = /* @__PURE__ */ new Date();
20317
+ d.setFullYear(year, month, day);
20318
+ return d;
20005
20319
  }
20006
20320
  }
20007
20321
  return null;
@@ -20072,22 +20386,7 @@ function XViewScene({
20072
20386
  if (type === "interval") {
20073
20387
  const relativeEndY = timeToYMap.get(endDate.getTime()) - timeToYMap.get(startDate.getTime());
20074
20388
  if (relativeEndY > 0) {
20075
- const barGeometry = new THREE3.CylinderGeometry(
20076
- TIMELINE_INTERVAL_BAR_RADIUS,
20077
- TIMELINE_INTERVAL_BAR_RADIUS,
20078
- 1,
20079
- 16
20080
- );
20081
- const barMaterial = new THREE3.MeshStandardMaterial({
20082
- color: TIMELINE_GOLD_COLOR,
20083
- emissive: 12092939,
20084
- emissiveIntensity: 0.4,
20085
- transparent: true,
20086
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20087
- roughness: 0.3,
20088
- metalness: 0.8
20089
- });
20090
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20389
+ const intervalBar = createTimelineIntervalBracket();
20091
20390
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20092
20391
  const endPosition = new THREE3.Vector3(
20093
20392
  baseX,
@@ -20095,6 +20394,11 @@ function XViewScene({
20095
20394
  -0.5
20096
20395
  );
20097
20396
  intervalBar.userData.endPosition = endPosition;
20397
+ updateTimelineIntervalBracket(
20398
+ intervalBar,
20399
+ new THREE3.Vector3(x, y, 0),
20400
+ endPosition
20401
+ );
20098
20402
  timelineIntervalsGroup.add(intervalBar);
20099
20403
  const endLabelText = endDate.getFullYear().toString();
20100
20404
  const endLabel = createTextSprite(endLabelText, 48);
@@ -20187,6 +20491,9 @@ function XViewScene({
20187
20491
  ancestryDataRef.current
20188
20492
  );
20189
20493
  setNodeCounter(sceneResponse.data.node_counter || 0);
20494
+ if (sceneResponse.data.node_limit !== void 0) {
20495
+ setNodeLimit(sceneResponse.data.node_limit);
20496
+ }
20190
20497
  setIsInitialized(true);
20191
20498
  } else {
20192
20499
  console.error(
@@ -20587,25 +20894,15 @@ function XViewScene({
20587
20894
  const endY = timeToYMap.get(timeInfo.endDate.getTime());
20588
20895
  if (Math.abs(endY - targetY) > 0.1) {
20589
20896
  const barHeight = Math.abs(endY - targetY);
20590
- const barGeometry = new THREE3.CylinderGeometry(
20591
- TIMELINE_INTERVAL_BAR_RADIUS,
20592
- TIMELINE_INTERVAL_BAR_RADIUS,
20593
- 1,
20594
- 16
20595
- );
20596
- const barMaterial = new THREE3.MeshStandardMaterial({
20597
- color: TIMELINE_GOLD_COLOR,
20598
- emissive: 12092939,
20599
- emissiveIntensity: 0.4,
20600
- transparent: true,
20601
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20602
- roughness: 0.3,
20603
- metalness: 0.8
20604
- });
20605
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20897
+ const intervalBar = createTimelineIntervalBracket();
20606
20898
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20607
- intervalBar.position.set(finalX, (targetY + endY) / 2, -0.5);
20608
- intervalBar.scale.y = barHeight;
20899
+ const endPosition = new THREE3.Vector3(finalX, endY, -0.5);
20900
+ intervalBar.userData.endPosition = endPosition;
20901
+ updateTimelineIntervalBracket(
20902
+ intervalBar,
20903
+ new THREE3.Vector3(finalX, targetY, 0),
20904
+ endPosition
20905
+ );
20609
20906
  graphGroup.add(intervalBar);
20610
20907
  mesh.userData.timelineIntervalBar = intervalBar;
20611
20908
  const endLabel = createTextSprite(
@@ -21374,23 +21671,12 @@ function XViewScene({
21374
21671
  }
21375
21672
  });
21376
21673
  (_a2 = stateRef.current.timelineIntervalsGroup) == null ? void 0 : _a2.children.forEach((obj) => {
21377
- if (obj.isMesh && obj.userData.associatedNodeId && obj.userData.endPosition) {
21674
+ if (obj.userData.associatedNodeId && obj.userData.endPosition) {
21378
21675
  const bar = obj;
21379
21676
  const node = stateRef.current.nodeObjects[String(bar.userData.associatedNodeId)];
21380
21677
  const endPos = bar.userData.endPosition;
21381
21678
  if (node) {
21382
- const startPos = node.position;
21383
- const distance = startPos.distanceTo(endPos);
21384
- bar.scale.y = distance;
21385
- const midpoint = new THREE3.Vector3().lerpVectors(
21386
- startPos,
21387
- endPos,
21388
- 0.5
21389
- );
21390
- bar.position.copy(midpoint);
21391
- const direction = new THREE3.Vector3().subVectors(endPos, startPos).normalize();
21392
- const upVector = new THREE3.Vector3(0, 1, 0);
21393
- bar.quaternion.setFromUnitVectors(upVector, direction);
21679
+ updateTimelineIntervalBracket(bar, node.position, endPos);
21394
21680
  }
21395
21681
  }
21396
21682
  });
@@ -21866,7 +22152,8 @@ function XViewScene({
21866
22152
  handleRenderProcessAbstractionTree,
21867
22153
  nodeCounter,
21868
22154
  checkLimit: (countToAdd = 1) => {
21869
- if (nodeCounter + countToAdd > 5e4) {
22155
+ if (nodeLimit === -1) return false;
22156
+ if (nodeCounter + countToAdd > nodeLimit) {
21870
22157
  setIsUpgradeModalOpen(true);
21871
22158
  return true;
21872
22159
  }
@@ -22427,7 +22714,7 @@ function XViewScene({
22427
22714
  sceneDataRef: sceneDataRef2,
22428
22715
  stateRef: stateRef2,
22429
22716
  setters,
22430
- actions: actions2,
22717
+ actions,
22431
22718
  sceneSaveUrl: sceneSaveUrl2,
22432
22719
  viewType,
22433
22720
  sceneConfigId: sceneConfigId2,
@@ -22445,11 +22732,11 @@ function XViewScene({
22445
22732
  const currentCounter = sceneDataRef2.current.quest_counter || 1;
22446
22733
  const nextCounter = currentCounter + 1;
22447
22734
  try {
22448
- if (actions2.upsert_quest_firestore) {
22449
- await actions2.upsert_quest_firestore(ownerId2, sceneConfigId2, newNode);
22735
+ if (actions.upsert_quest_firestore) {
22736
+ await actions.upsert_quest_firestore(ownerId2, sceneConfigId2, newNode);
22450
22737
  }
22451
- if (actions2.update_view_counter) {
22452
- await actions2.update_view_counter(ownerId2, sceneConfigId2, nextCounter);
22738
+ if (actions.update_view_counter) {
22739
+ await actions.update_view_counter(ownerId2, sceneConfigId2, nextCounter);
22453
22740
  }
22454
22741
  sceneDataRef2.current.quest_counter = nextCounter;
22455
22742
  stateRef2.current.nodeIdToParentFileMap.set(String(newNode.id), {
@@ -22491,7 +22778,7 @@ function XViewScene({
22491
22778
  sceneDataRef: sceneDataRef2,
22492
22779
  stateRef: stateRef2,
22493
22780
  setters,
22494
- actions: actions2,
22781
+ actions,
22495
22782
  viewType,
22496
22783
  sceneConfigId: sceneConfigId2,
22497
22784
  ownerId: ownerId2,
@@ -22509,8 +22796,8 @@ function XViewScene({
22509
22796
  ]
22510
22797
  };
22511
22798
  try {
22512
- if (actions2.upsert_process_firestore) {
22513
- await actions2.upsert_process_firestore(ownerId2, sceneConfigId2, newNode);
22799
+ if (actions.upsert_process_firestore) {
22800
+ await actions.upsert_process_firestore(ownerId2, sceneConfigId2, newNode);
22514
22801
  }
22515
22802
  stateRef2.current.nodeIdToParentFileMap.set(String(newNode.id), {
22516
22803
  parentFileId: sceneConfigId2,
@@ -24371,8 +24658,9 @@ function XViewScene({
24371
24658
  )) == null ? void 0 : _b.parentFileId : null;
24372
24659
  const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
24373
24660
  (0, import_react37.useEffect)(() => {
24374
- if (isInitialized && focusNodeId && !hasFocusedInitial) {
24375
- const nodeObjects = stateRef.current.nodeObjects || {};
24661
+ if (!isInitialized || hasFocusedInitial) return;
24662
+ const nodeObjects = stateRef.current.nodeObjects || {};
24663
+ if (focusNodeId) {
24376
24664
  const targetMesh = nodeObjects[String(focusNodeId)];
24377
24665
  if (targetMesh) {
24378
24666
  setTimeout(() => {
@@ -24385,11 +24673,34 @@ function XViewScene({
24385
24673
  "O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
24386
24674
  );
24387
24675
  }
24676
+ } else if (!focusAncestryId) {
24677
+ const meshes = Object.values(nodeObjects);
24678
+ if (meshes.length > 0) {
24679
+ setTimeout(() => {
24680
+ const nodePositions = meshes.map((mesh) => mesh.position.clone());
24681
+ const boundingBox = new THREE3.Box3().setFromPoints(nodePositions);
24682
+ const center = new THREE3.Vector3();
24683
+ boundingBox.getCenter(center);
24684
+ const size = new THREE3.Vector3();
24685
+ boundingBox.getSize(size);
24686
+ const maxDim = Math.max(size.x, size.y, size.z);
24687
+ const desiredDistance = Math.max(
24688
+ x_view_config.CAMERA_ZOOM_DISTANCE,
24689
+ maxDim * 1.8
24690
+ );
24691
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
24692
+ tweenToTarget(center, zoomFactor);
24693
+ setHasFocusedInitial(true);
24694
+ }, 300);
24695
+ } else {
24696
+ setHasFocusedInitial(true);
24697
+ }
24388
24698
  }
24389
24699
  }, [
24390
24700
  isInitialized,
24391
24701
  sceneVersion,
24392
24702
  focusNodeId,
24703
+ focusAncestryId,
24393
24704
  hasFocusedInitial,
24394
24705
  tweenToTarget
24395
24706
  ]);
@@ -25383,7 +25694,20 @@ function XViewScene({
25383
25694
  {
25384
25695
  isOpen: isUpgradeModalOpen,
25385
25696
  onClose: () => setIsUpgradeModalOpen(false),
25386
- nodeCounter
25697
+ nodeCounter,
25698
+ nodeLimit,
25699
+ onUpgrade: async () => {
25700
+ if (onUpgrade) {
25701
+ const res = await onUpgrade();
25702
+ if (res.success) {
25703
+ setNodeLimit(-1);
25704
+ alert("Upgrade realizado com sucesso! Sua conta agora \xE9 Premium.");
25705
+ } else {
25706
+ alert("Erro ao realizar upgrade: " + res.error);
25707
+ }
25708
+ return res;
25709
+ }
25710
+ }
25387
25711
  }
25388
25712
  )
25389
25713
  );
@@ -25499,6 +25823,16 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
25499
25823
  }
25500
25824
  })();
25501
25825
  parentResponsesPromises.push(questFetchPromise, processFetchPromise, nodeCounterPromise);
25826
+ } else {
25827
+ const nodeCounterPromise = (async () => {
25828
+ try {
25829
+ if (!db_services.get_user_node_counter) return { success: true, node_counter: 0 };
25830
+ return await db_services.get_user_node_counter(owner_id);
25831
+ } catch (err) {
25832
+ return { success: false, error: err.message };
25833
+ }
25834
+ })();
25835
+ parentResponsesPromises.push(nodeCounterPromise);
25502
25836
  }
25503
25837
  const parentResponsesResults = await Promise.allSettled(
25504
25838
  parentResponsesPromises
@@ -25507,9 +25841,10 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
25507
25841
  let questDataObj = null;
25508
25842
  let processDataObj = null;
25509
25843
  let userNodeCounter = 0;
25844
+ let userNodeLimit = 100;
25510
25845
  for (let i = 0; i < parentResponsesResults.length; i++) {
25511
25846
  const result = parentResponsesResults[i];
25512
- if (isView && i >= parentDbObjects.length) {
25847
+ if (i >= parentDbObjects.length) {
25513
25848
  if (result.status === "fulfilled" && ((_a = result.value) == null ? void 0 : _a.success)) {
25514
25849
  if (result.value.type === "processes") {
25515
25850
  processDataObj = result.value.data;
@@ -25520,6 +25855,9 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
25520
25855
  }
25521
25856
  } else if (result.value.node_counter !== void 0) {
25522
25857
  userNodeCounter = result.value.node_counter;
25858
+ if (result.value.node_limit !== void 0) {
25859
+ userNodeLimit = result.value.node_limit;
25860
+ }
25523
25861
  }
25524
25862
  }
25525
25863
  continue;
@@ -25885,7 +26223,8 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
25885
26223
  scene: validatedSceneData,
25886
26224
  parent: parentData,
25887
26225
  ancestry: ancestryData,
25888
- node_counter: userNodeCounter
26226
+ node_counter: userNodeCounter,
26227
+ node_limit: userNodeLimit
25889
26228
  }
25890
26229
  };
25891
26230
  } catch (error) {