@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.mjs CHANGED
@@ -797,6 +797,55 @@ var removeLinkFromScene = (state, linkObject) => {
797
797
  else linkObject.material.dispose();
798
798
  }
799
799
  };
800
+ var removeAllTimelineElements = (state) => {
801
+ if (!state) return;
802
+ const { timelineIntervalsGroup, nodeObjects } = state;
803
+ if (timelineIntervalsGroup) {
804
+ while (timelineIntervalsGroup.children.length > 0) {
805
+ const obj = timelineIntervalsGroup.children[0];
806
+ timelineIntervalsGroup.remove(obj);
807
+ if (obj.traverse) {
808
+ obj.traverse((child) => {
809
+ if (child.geometry) child.geometry.dispose();
810
+ if (child.material) {
811
+ if (child.material.map) child.material.map.dispose();
812
+ child.material.dispose();
813
+ }
814
+ });
815
+ } else {
816
+ if (obj.geometry) obj.geometry.dispose();
817
+ if (obj.material) {
818
+ if (obj.material.map) obj.material.map.dispose();
819
+ obj.material.dispose();
820
+ }
821
+ }
822
+ }
823
+ }
824
+ if (nodeObjects) {
825
+ Object.values(nodeObjects).forEach((mesh) => {
826
+ var _a, _b, _c, _d;
827
+ if (!mesh) return;
828
+ const oldTimelineLabel = mesh.getObjectByName("timelineLabel");
829
+ if (oldTimelineLabel) {
830
+ mesh.remove(oldTimelineLabel);
831
+ if ((_a = oldTimelineLabel.material) == null ? void 0 : _a.map) {
832
+ oldTimelineLabel.material.map.dispose();
833
+ }
834
+ (_b = oldTimelineLabel.material) == null ? void 0 : _b.dispose();
835
+ }
836
+ const oldQuestDateLabel = mesh.getObjectByName("questDateLabel");
837
+ if (oldQuestDateLabel) {
838
+ mesh.remove(oldQuestDateLabel);
839
+ if ((_c = oldQuestDateLabel.material) == null ? void 0 : _c.map) {
840
+ oldQuestDateLabel.material.map.dispose();
841
+ }
842
+ (_d = oldQuestDateLabel.material) == null ? void 0 : _d.dispose();
843
+ }
844
+ delete mesh.userData.timelineIntervalBar;
845
+ delete mesh.userData.timelineEndLabel;
846
+ });
847
+ }
848
+ };
800
849
  var removeNodeFromScene = (state, nodeId) => {
801
850
  const {
802
851
  graphGroup,
@@ -825,8 +874,15 @@ var removeNodeFromScene = (state, nodeId) => {
825
874
  const { timelineIntervalBar, timelineEndLabel } = nodeToRemove.userData;
826
875
  if (timelineIntervalBar) {
827
876
  timelineIntervalsGroup.remove(timelineIntervalBar);
828
- if (timelineIntervalBar.geometry) timelineIntervalBar.geometry.dispose();
829
- if (timelineIntervalBar.material) timelineIntervalBar.material.dispose();
877
+ if (timelineIntervalBar.traverse) {
878
+ timelineIntervalBar.traverse((child) => {
879
+ if (child.geometry) child.geometry.dispose();
880
+ if (child.material) child.material.dispose();
881
+ });
882
+ } else {
883
+ if (timelineIntervalBar.geometry) timelineIntervalBar.geometry.dispose();
884
+ if (timelineIntervalBar.material) timelineIntervalBar.material.dispose();
885
+ }
830
886
  }
831
887
  if (timelineEndLabel) {
832
888
  timelineIntervalsGroup.remove(timelineEndLabel);
@@ -897,7 +953,7 @@ var getParentFileInfoForNode = (allParentData, sceneData, nodeId, sceneConfigId
897
953
  };
898
954
  async function instantiateSubProcess(context, templateProcess, versionedProcess, startCounter) {
899
955
  var _a, _b, _c;
900
- const { graphDataRef, sceneDataRef, actions: actions2 } = context;
956
+ const { graphDataRef, sceneDataRef, actions } = context;
901
957
  const ownerId = context.ownerId;
902
958
  const parentFileId = context.sceneConfigId;
903
959
  const viewName = context.viewName || "View";
@@ -952,7 +1008,7 @@ async function instantiateSubProcess(context, templateProcess, versionedProcess,
952
1008
  }
953
1009
  ] : originalChildNode.timeline || []
954
1010
  };
955
- if (actions2.copy_google_doc_action && versionedChildNode.Links) {
1011
+ if (actions.copy_google_doc_action && versionedChildNode.Links) {
956
1012
  const docIdx = versionedChildNode.Links.findIndex((l) => l.name === "Google docs");
957
1013
  if (docIdx !== -1) {
958
1014
  const url = versionedChildNode.Links[docIdx].value;
@@ -960,7 +1016,7 @@ async function instantiateSubProcess(context, templateProcess, versionedProcess,
960
1016
  const match = url.match(/\/d\/([a-zA-Z0-9-_]+)/);
961
1017
  if (match == null ? void 0 : match[1]) {
962
1018
  try {
963
- const res = await actions2.copy_google_doc_action(
1019
+ const res = await actions.copy_google_doc_action(
964
1020
  match[1],
965
1021
  nextName,
966
1022
  (_c = sceneDataRef.current) == null ? void 0 : _c.view_google_folder_id
@@ -999,14 +1055,14 @@ async function instantiateSubProcess(context, templateProcess, versionedProcess,
999
1055
  childRef.node.version_node = versionedChildNode.version_node;
1000
1056
  }
1001
1057
  childRef.node_id = versionedChildId;
1002
- if (versionedChildNode.is_quest && actions2.upsert_quest_firestore) {
1003
- savePromises.push(actions2.upsert_quest_firestore(ownerId, parentFileId, versionedChildNode));
1004
- if (actions2.upsert_quest_link_firestore)
1005
- savePromises.push(actions2.upsert_quest_link_firestore(ownerId, parentFileId, childVersionLink));
1006
- } else if (versionedChildNode.is_process && actions2.upsert_process_firestore) {
1007
- savePromises.push(actions2.upsert_process_firestore(ownerId, parentFileId, versionedChildNode));
1008
- if (actions2.upsert_process_link_firestore)
1009
- savePromises.push(actions2.upsert_process_link_firestore(ownerId, parentFileId, childVersionLink));
1058
+ if (versionedChildNode.is_quest && actions.upsert_quest_firestore) {
1059
+ savePromises.push(actions.upsert_quest_firestore(ownerId, parentFileId, versionedChildNode));
1060
+ if (actions.upsert_quest_link_firestore)
1061
+ savePromises.push(actions.upsert_quest_link_firestore(ownerId, parentFileId, childVersionLink));
1062
+ } else if (versionedChildNode.is_process && actions.upsert_process_firestore) {
1063
+ savePromises.push(actions.upsert_process_firestore(ownerId, parentFileId, versionedChildNode));
1064
+ if (actions.upsert_process_link_firestore)
1065
+ savePromises.push(actions.upsert_process_link_firestore(ownerId, parentFileId, childVersionLink));
1010
1066
  }
1011
1067
  }
1012
1068
  if (versionedProcess.process_abstraction_tree) {
@@ -1138,7 +1194,7 @@ var userActionHandlers = {
1138
1194
  stateRef,
1139
1195
  creationMode,
1140
1196
  setters,
1141
- actions: actions2,
1197
+ actions,
1142
1198
  checkLimit
1143
1199
  } = context;
1144
1200
  if (!graphDataRef.current || !sceneDataRef.current) return;
@@ -1172,20 +1228,20 @@ var userActionHandlers = {
1172
1228
  const savePromises = [];
1173
1229
  if (isCrossDataset) {
1174
1230
  savePromises.push(
1175
- actions2.save_view_data(
1231
+ actions.save_view_data(
1176
1232
  `x_view_dbs/${sourceParentInfo.ownerId}/${sourceParentInfo.parentFileId}`,
1177
1233
  sourceDataToUpdate
1178
1234
  )
1179
1235
  );
1180
1236
  savePromises.push(
1181
- actions2.save_view_data(
1237
+ actions.save_view_data(
1182
1238
  `x_view_dbs/${targetParentInfo.owner_id}/${finalTargetDatasetId}`,
1183
1239
  targetDataToUpdate
1184
1240
  )
1185
1241
  );
1186
1242
  } else {
1187
1243
  savePromises.push(
1188
- actions2.save_view_data(
1244
+ actions.save_view_data(
1189
1245
  `x_view_dbs/${sourceParentInfo.ownerId}/${sourceParentInfo.parentFileId}`,
1190
1246
  sourceDataToUpdate
1191
1247
  )
@@ -1208,8 +1264,8 @@ var userActionHandlers = {
1208
1264
  ownerId: targetParentInfo.owner_id,
1209
1265
  datasetName: targetDataToUpdate.dataset_name || "Dataset Desconhecido"
1210
1266
  });
1211
- if (actions2.update_node_counter) {
1212
- await actions2.update_node_counter(targetParentInfo.owner_id, 1);
1267
+ if (actions.update_node_counter) {
1268
+ await actions.update_node_counter(targetParentInfo.owner_id, 1);
1213
1269
  }
1214
1270
  setters.setSceneVersion((v) => v + 1);
1215
1271
  } catch (error) {
@@ -1343,7 +1399,7 @@ var userActionHandlers = {
1343
1399
  },
1344
1400
  handleSaveVersionNode: async (context, newNodeData) => {
1345
1401
  var _a;
1346
- const { graphDataRef, sceneDataRef, stateRef, versionMode, setters, checkLimit } = context;
1402
+ const { graphDataRef, sceneDataRef, stateRef, versionMode, setters, checkLimit, actions } = context;
1347
1403
  if (!graphDataRef.current || !sceneDataRef.current) return;
1348
1404
  if (checkLimit && checkLimit()) return;
1349
1405
  const { sourceNodeData } = versionMode;
@@ -1454,8 +1510,8 @@ var userActionHandlers = {
1454
1510
  );
1455
1511
  const targetFinalPositions = [];
1456
1512
  const newVersionMeshes = [];
1513
+ const newNodesToCreate = [];
1457
1514
  linksToExpand.forEach((link) => {
1458
- var _a;
1459
1515
  const isSource = String(link.source) === String(sourceNode.id);
1460
1516
  const targetNodeId = isSource ? link.target : link.source;
1461
1517
  if (!nodeObjects[String(targetNodeId)]) {
@@ -1465,45 +1521,94 @@ var userActionHandlers = {
1465
1521
  const nodeData = allParentNodes.find(
1466
1522
  (n) => String(n.id) === String(targetNodeId)
1467
1523
  );
1468
- if (!nodeData) {
1469
- console.warn(
1470
- `Dados do Node com ID ${targetNodeId} n\xE3o encontrados no cache.`
1471
- );
1472
- return;
1524
+ if (nodeData) {
1525
+ newNodesToCreate.push({ link, nodeData, targetNodeId });
1473
1526
  }
1474
- const startPosition = sourceNodeMesh.position.clone();
1475
- const endPosition = startPosition.clone().add(
1476
- new THREE.Vector3(
1477
- (Math.random() - 0.5) * 60,
1478
- (Math.random() - 0.5) * 15,
1479
- (Math.random() - 0.5) * 60
1480
- )
1481
- );
1482
- targetFinalPositions.push(endPosition);
1483
- const targetMesh = createNodeMesh(
1484
- nodeData,
1485
- startPosition,
1486
- glowTexture,
1487
- !!nodeData.is_quest
1488
- );
1489
- targetMesh.scale.set(0.01, 0.01, 0.01);
1490
- graphGroup.add(targetMesh);
1491
- if (targetMesh.userData.labelObject) {
1492
- graphGroup.add(targetMesh.userData.labelObject);
1493
- }
1494
- nodeObjects[String(targetNodeId)] = targetMesh;
1495
- clickableNodes.push(targetMesh);
1496
- const posTween = new Tween(targetMesh.position).to(endPosition, 1200).easing(Easing.Quadratic.Out);
1497
- tweenGroup.add(posTween);
1498
- posTween.start();
1499
- const targetScale = NODE_SCALE_FACTORS[nodeData.size || "medium"] || 1;
1500
- const scaleTween = new Tween(targetMesh.scale).to({ x: targetScale, y: targetScale, z: targetScale }, 1e3).easing(Easing.Elastic.Out);
1501
- tweenGroup.add(scaleTween);
1502
- scaleTween.start();
1503
- if (((_a = nodeData.version_node) == null ? void 0 : _a.is_version) && String(nodeData.version_node.parent_node) === String(sourceNode.id)) {
1504
- newVersionMeshes.push(targetMesh);
1527
+ }
1528
+ });
1529
+ const N = newNodesToCreate.length;
1530
+ let referenceDir = new THREE.Vector3(0, 0, 1);
1531
+ if (N > 0) {
1532
+ const incomingVectors = [];
1533
+ allLinks.forEach((l) => {
1534
+ const srcId = String(l.userData.source);
1535
+ const tgtId = String(l.userData.target);
1536
+ if (srcId === String(sourceNode.id) || tgtId === String(sourceNode.id)) {
1537
+ const otherId = srcId === String(sourceNode.id) ? tgtId : srcId;
1538
+ const otherMesh = nodeObjects[otherId];
1539
+ if (otherMesh) {
1540
+ const dir = new THREE.Vector3().subVectors(
1541
+ sourceNodeMesh.position,
1542
+ otherMesh.position
1543
+ );
1544
+ if (dir.lengthSq() > 1e-3) {
1545
+ dir.normalize();
1546
+ incomingVectors.push(dir);
1547
+ }
1548
+ }
1549
+ }
1550
+ });
1551
+ if (incomingVectors.length > 0) {
1552
+ referenceDir.set(0, 0, 0);
1553
+ incomingVectors.forEach((v) => referenceDir.add(v));
1554
+ if (referenceDir.lengthSq() > 1e-3) {
1555
+ referenceDir.normalize();
1556
+ } else {
1557
+ referenceDir.set(0, 0, 1);
1558
+ }
1559
+ } else {
1560
+ const toSource = sourceNodeMesh.position.clone();
1561
+ if (toSource.lengthSq() > 1e-3) {
1562
+ referenceDir.copy(toSource).normalize();
1505
1563
  }
1506
1564
  }
1565
+ }
1566
+ const up = new THREE.Vector3(0, 1, 0);
1567
+ if (Math.abs(referenceDir.y) > 0.95) {
1568
+ up.set(1, 0, 0);
1569
+ }
1570
+ const right = new THREE.Vector3().crossVectors(up, referenceDir).normalize();
1571
+ const upActual = new THREE.Vector3().crossVectors(referenceDir, right).normalize();
1572
+ const maxSpread = N <= 1 ? 0 : Math.min(Math.PI * 0.4 + (N - 2) * 0.15, Math.PI * 0.8);
1573
+ const baseDistance = 40;
1574
+ newNodesToCreate.forEach((item, i) => {
1575
+ var _a;
1576
+ const { link, nodeData, targetNodeId } = item;
1577
+ let phi = 0;
1578
+ if (N > 1) {
1579
+ phi = -maxSpread / 2 + i / (N - 1) * maxSpread;
1580
+ }
1581
+ const angleY = N > 1 ? Math.sin(i * Math.PI) * 0.12 : 0;
1582
+ const nodeDir = referenceDir.clone().multiplyScalar(Math.cos(phi)).add(right.clone().multiplyScalar(Math.sin(phi))).add(upActual.clone().multiplyScalar(angleY)).normalize();
1583
+ const distFactor = N > 1 ? 1 + (i % 2 === 0 ? 0 : 0.15) : 1;
1584
+ const distance = baseDistance * distFactor;
1585
+ const startPosition = sourceNodeMesh.position.clone();
1586
+ const endPosition = startPosition.clone().add(nodeDir.multiplyScalar(distance));
1587
+ targetFinalPositions.push(endPosition);
1588
+ const targetMesh = createNodeMesh(
1589
+ nodeData,
1590
+ startPosition,
1591
+ glowTexture,
1592
+ !!nodeData.is_quest
1593
+ );
1594
+ targetMesh.scale.set(0.01, 0.01, 0.01);
1595
+ graphGroup.add(targetMesh);
1596
+ if (targetMesh.userData.labelObject) {
1597
+ graphGroup.add(targetMesh.userData.labelObject);
1598
+ }
1599
+ nodeObjects[String(targetNodeId)] = targetMesh;
1600
+ clickableNodes.push(targetMesh);
1601
+ const delay = i * 60;
1602
+ const posTween = new Tween(targetMesh.position).to(endPosition, 1200).delay(delay).easing(Easing.Quadratic.Out);
1603
+ tweenGroup.add(posTween);
1604
+ posTween.start();
1605
+ const targetScale = NODE_SCALE_FACTORS[nodeData.size || "medium"] || 1;
1606
+ const scaleTween = new Tween(targetMesh.scale).to({ x: targetScale, y: targetScale, z: targetScale }, 1e3).delay(delay).easing(Easing.Elastic.Out);
1607
+ tweenGroup.add(scaleTween);
1608
+ scaleTween.start();
1609
+ if (((_a = nodeData.version_node) == null ? void 0 : _a.is_version) && String(nodeData.version_node.parent_node) === String(sourceNode.id)) {
1610
+ newVersionMeshes.push(targetMesh);
1611
+ }
1507
1612
  });
1508
1613
  linksToExpand.forEach((link) => {
1509
1614
  const linkAlreadyInSceneObjects = allLinks.some(
@@ -1535,7 +1640,12 @@ var userActionHandlers = {
1535
1640
  newLine.material.opacity = 0;
1536
1641
  graphGroup.add(newLine);
1537
1642
  allLinks.push(newLine);
1538
- const opacityTween = new Tween(newLine.material).to({ opacity: 0.3 }, 800).delay(500);
1643
+ const targetId = String(newLine.userData.target);
1644
+ const newNodeIndex = newNodesToCreate.findIndex(
1645
+ (item) => String(item.targetNodeId) === targetId
1646
+ );
1647
+ const delay = newNodeIndex !== -1 ? newNodeIndex * 60 : 0;
1648
+ const opacityTween = new Tween(newLine.material).to({ opacity: 0.3 }, 800).delay(delay);
1539
1649
  tweenGroup.add(opacityTween);
1540
1650
  opacityTween.start();
1541
1651
  });
@@ -2032,7 +2142,7 @@ var userActionHandlers = {
2032
2142
  },
2033
2143
  syncQuestTemplateFlags: async (context) => {
2034
2144
  var _a, _b;
2035
- const { sceneDataRef, graphDataRef, actions: actions2, setters, sceneConfigId, ownerId, stateRef } = context;
2145
+ const { sceneDataRef, graphDataRef, actions, setters, sceneConfigId, ownerId, stateRef } = context;
2036
2146
  if (!((_a = sceneDataRef.current) == null ? void 0 : _a.process_nodes)) return;
2037
2147
  const allTemplateQuestIds = /* @__PURE__ */ new Set();
2038
2148
  sceneDataRef.current.process_nodes.forEach((proc) => {
@@ -2067,8 +2177,8 @@ var userActionHandlers = {
2067
2177
  const pIdx = graphDataRef.current[sceneConfigId].nodes.findIndex((n) => String(n.id) === String(quest.id));
2068
2178
  if (pIdx !== -1) graphDataRef.current[sceneConfigId].nodes[pIdx] = quest;
2069
2179
  }
2070
- if (actions2.upsert_quest_firestore) {
2071
- await actions2.upsert_quest_firestore(ownerId, sceneConfigId, quest);
2180
+ if (actions.upsert_quest_firestore) {
2181
+ await actions.upsert_quest_firestore(ownerId, sceneConfigId, quest);
2072
2182
  }
2073
2183
  const mesh = (_b = stateRef.current.nodeObjects) == null ? void 0 : _b[String(quest.id)];
2074
2184
  if (mesh) {
@@ -2084,6 +2194,7 @@ var userActionHandlers = {
2084
2194
  if (!nodeData || !sceneDataRef.current) return;
2085
2195
  const nodeIdToDismiss = nodeData.id;
2086
2196
  const strNodeId = String(nodeIdToDismiss);
2197
+ removeAllTimelineElements(stateRef.current);
2087
2198
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2088
2199
  if (ancestryGroup && ancestryLinks) {
2089
2200
  const remainingAncestryLinks = [];
@@ -2115,6 +2226,7 @@ var userActionHandlers = {
2115
2226
  (id) => id !== strNodeIdToKeep
2116
2227
  );
2117
2228
  if (nodeIdsToDismiss.length === 0) return;
2229
+ removeAllTimelineElements(stateRef.current);
2118
2230
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2119
2231
  if (ancestryGroup && ancestryLinks) {
2120
2232
  ancestryLinks.forEach((link) => {
@@ -2137,6 +2249,7 @@ var userActionHandlers = {
2137
2249
  setters.setMultiContextMenu({ visible: false });
2138
2250
  if (!nodeIds || nodeIds.size === 0 || !sceneDataRef.current) return;
2139
2251
  const strNodeIds = Array.from(nodeIds).map(String);
2252
+ removeAllTimelineElements(stateRef.current);
2140
2253
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2141
2254
  if (ancestryGroup && ancestryLinks) {
2142
2255
  const remainingAncestryLinks = [];
@@ -2176,6 +2289,7 @@ var userActionHandlers = {
2176
2289
  stateRef.current.selectedNodes.clear();
2177
2290
  return;
2178
2291
  }
2292
+ removeAllTimelineElements(stateRef.current);
2179
2293
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2180
2294
  if (ancestryGroup && ancestryLinks) {
2181
2295
  const remainingAncestryLinks = [];
@@ -2203,19 +2317,19 @@ var userActionHandlers = {
2203
2317
  },
2204
2318
  handleDeleteMultipleNodes: async (context, nodeIds) => {
2205
2319
  var _a;
2206
- const { stateRef, graphDataRef, sceneDataRef, setters, actions: actions2 } = context;
2320
+ const { stateRef, graphDataRef, sceneDataRef, setters, actions } = context;
2207
2321
  setters.setMultiContextMenu({ visible: false });
2208
2322
  if (!nodeIds || nodeIds.size === 0 || !graphDataRef.current || !sceneDataRef.current)
2209
2323
  return;
2210
2324
  const strNodeIdsToDelete = Array.from(nodeIds).map(String);
2211
- if (actions2.delete_file) {
2325
+ if (actions.delete_file) {
2212
2326
  strNodeIdsToDelete.forEach((id) => {
2213
2327
  const nodeMesh = stateRef.current.nodeObjects[id];
2214
2328
  const nodeData = nodeMesh ? nodeMesh.userData : null;
2215
2329
  if (nodeData) {
2216
2330
  const urls = extractFileUrlsFromProperties(nodeData);
2217
2331
  if (urls.length > 0) {
2218
- Promise.all(urls.map((url) => actions2.delete_file(url))).catch(
2332
+ Promise.all(urls.map((url) => actions.delete_file(url))).catch(
2219
2333
  (err) => console.error(`Erro ao deletar arquivos do node ${id}:`, err)
2220
2334
  );
2221
2335
  }
@@ -2346,9 +2460,9 @@ var userActionHandlers = {
2346
2460
  } else {
2347
2461
  filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2348
2462
  payloadToSave = originalData;
2349
- if (actions2.update_node_counter) {
2463
+ if (actions.update_node_counter) {
2350
2464
  savePromises.push(
2351
- actions2.update_node_counter(ownerId, -nodesToDelete.size)
2465
+ actions.update_node_counter(ownerId, -nodesToDelete.size)
2352
2466
  );
2353
2467
  }
2354
2468
  }
@@ -2393,11 +2507,11 @@ var userActionHandlers = {
2393
2507
  },
2394
2508
  handleDeleteNode: async (context, nodeData) => {
2395
2509
  var _a;
2396
- const { stateRef, graphDataRef, sceneDataRef, setters, actions: actions2 } = context;
2397
- if (actions2.delete_file && nodeData) {
2510
+ const { stateRef, graphDataRef, sceneDataRef, setters, actions } = context;
2511
+ if (actions.delete_file && nodeData) {
2398
2512
  const urls = extractFileUrlsFromProperties(nodeData);
2399
2513
  if (urls.length > 0) {
2400
- Promise.all(urls.map((url) => actions2.delete_file(url))).then(() => console.log("Arquivos do node deletados.")).catch(
2514
+ Promise.all(urls.map((url) => actions.delete_file(url))).then(() => console.log("Arquivos do node deletados.")).catch(
2401
2515
  (err) => console.error("Erro ao deletar arquivos do node:", err)
2402
2516
  );
2403
2517
  }
@@ -2501,8 +2615,8 @@ var userActionHandlers = {
2501
2615
  setters.setSceneVersion((v) => v + 1);
2502
2616
  if (nodeData.is_process) {
2503
2617
  await userActionHandlers.syncQuestTemplateFlags(context);
2504
- } else if (!nodeData.is_quest && actions2.update_node_counter) {
2505
- await actions2.update_node_counter(ownerId, -1);
2618
+ } else if (!nodeData.is_quest && actions.update_node_counter) {
2619
+ await actions.update_node_counter(ownerId, -1);
2506
2620
  }
2507
2621
  } catch (e) {
2508
2622
  console.error("Falha ao salvar remo\xE7\xE3o do Node:", e);
@@ -2520,8 +2634,8 @@ var userActionHandlers = {
2520
2634
  (prev) => String(prev == null ? void 0 : prev.id) === String(nodeIdToDelete) ? null : prev
2521
2635
  );
2522
2636
  removeNodeFromScene(stateRef.current, nodeIdToDelete);
2523
- if (!isQuestOrProcess && actions2.update_node_counter) {
2524
- await actions2.update_node_counter(ownerId, -1);
2637
+ if (!isQuestOrProcess && actions.update_node_counter) {
2638
+ await actions.update_node_counter(ownerId, -1);
2525
2639
  } else if (isQuestOrProcess && setters.setNodeCounter) {
2526
2640
  setters.setNodeCounter((prev) => prev - 1);
2527
2641
  }
@@ -3171,16 +3285,26 @@ var userActionHandlers = {
3171
3285
  links: specificParentData.links,
3172
3286
  milestones: sceneDataRef.current.milestones || []
3173
3287
  };
3174
- await context.actions.save_view_data(
3175
- context.sceneSaveUrl,
3176
- viewFilePayload
3177
- );
3178
- if (context.actions.upsert_quest_firestore) {
3288
+ const isQuestNode = nodeToSave.is_quest || (Array.isArray(nodeToSave.type) ? nodeToSave.type.includes("quest") : nodeToSave.type === "quest");
3289
+ const isProcessNode = nodeToSave.is_process || (Array.isArray(nodeToSave.type) ? nodeToSave.type.includes("process") : nodeToSave.type === "process");
3290
+ if (!isQuestNode && !isProcessNode) {
3291
+ await context.actions.save_view_data(
3292
+ context.sceneSaveUrl,
3293
+ viewFilePayload
3294
+ );
3295
+ }
3296
+ if (isQuestNode && context.actions.upsert_quest_firestore) {
3179
3297
  await context.actions.upsert_quest_firestore(
3180
3298
  context.ownerId,
3181
3299
  context.sceneConfigId,
3182
3300
  nodeToSave
3183
3301
  );
3302
+ } else if (isProcessNode && context.actions.upsert_process_firestore) {
3303
+ await context.actions.upsert_process_firestore(
3304
+ context.ownerId,
3305
+ context.sceneConfigId,
3306
+ nodeToSave
3307
+ );
3184
3308
  }
3185
3309
  } else {
3186
3310
  const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
@@ -3213,7 +3337,7 @@ var userActionHandlers = {
3213
3337
  },
3214
3338
  handleTriggerProcessBranch: async (context, parentProcess, selectedTemplateChild, triggeringQuestId) => {
3215
3339
  var _a, _b, _c;
3216
- const { graphDataRef, sceneDataRef, stateRef, setters, actions: actions2 } = context;
3340
+ const { graphDataRef, sceneDataRef, stateRef, setters, actions } = context;
3217
3341
  if (!graphDataRef.current || !sceneDataRef.current) return;
3218
3342
  try {
3219
3343
  setters.setIsProcessing(true);
@@ -3275,7 +3399,7 @@ var userActionHandlers = {
3275
3399
  }
3276
3400
  ] : originalChildNode.timeline || []
3277
3401
  };
3278
- if (actions2.copy_google_doc_action && versionedChildNode.Links) {
3402
+ if (actions.copy_google_doc_action && versionedChildNode.Links) {
3279
3403
  const docLinkIndex = versionedChildNode.Links.findIndex((l) => l.name === "Google docs");
3280
3404
  if (docLinkIndex !== -1) {
3281
3405
  const originalUrl = versionedChildNode.Links[docLinkIndex].value;
@@ -3283,7 +3407,7 @@ var userActionHandlers = {
3283
3407
  const docIdMatch = originalUrl.match(/\/d\/([a-zA-Z0-9-_]+)/);
3284
3408
  if (docIdMatch && docIdMatch[1]) {
3285
3409
  try {
3286
- const copyRes = await actions2.copy_google_doc_action(
3410
+ const copyRes = await actions.copy_google_doc_action(
3287
3411
  docIdMatch[1],
3288
3412
  nextName,
3289
3413
  (_c = sceneDataRef.current) == null ? void 0 : _c.view_google_folder_id
@@ -3346,15 +3470,15 @@ var userActionHandlers = {
3346
3470
  }
3347
3471
  }
3348
3472
  if (versionedChildNode.is_quest) {
3349
- savePromises.push(actions2.upsert_quest_firestore(ownerId, parentFileId, versionedChildNode));
3350
- savePromises.push(actions2.upsert_quest_link_firestore(ownerId, parentFileId, childVersionLink));
3473
+ savePromises.push(actions.upsert_quest_firestore(ownerId, parentFileId, versionedChildNode));
3474
+ savePromises.push(actions.upsert_quest_link_firestore(ownerId, parentFileId, childVersionLink));
3351
3475
  } else {
3352
- savePromises.push(actions2.upsert_process_firestore(ownerId, parentFileId, versionedChildNode));
3353
- savePromises.push(actions2.upsert_process_link_firestore(ownerId, parentFileId, childVersionLink));
3476
+ savePromises.push(actions.upsert_process_firestore(ownerId, parentFileId, versionedChildNode));
3477
+ savePromises.push(actions.upsert_process_link_firestore(ownerId, parentFileId, childVersionLink));
3354
3478
  }
3355
3479
  sceneDataRef.current.quest_counter = currentCounter;
3356
- savePromises.push(actions2.update_view_counter(ownerId, parentFileId, currentCounter));
3357
- savePromises.push(actions2.upsert_process_firestore(ownerId, parentFileId, parentProcess));
3480
+ savePromises.push(actions.update_view_counter(ownerId, parentFileId, currentCounter));
3481
+ savePromises.push(actions.upsert_process_firestore(ownerId, parentFileId, parentProcess));
3358
3482
  const specificParentData = graphDataRef.current[parentFileId];
3359
3483
  if (specificParentData) {
3360
3484
  specificParentData.nodes.push(...newNodes);
@@ -3521,7 +3645,7 @@ var userActionHandlers = {
3521
3645
  },
3522
3646
  handleInitiateProcess: async (context, processNode) => {
3523
3647
  var _a, _b, _c, _d, _e;
3524
- const { graphDataRef, sceneDataRef, stateRef, setters, actions: actions2 } = context;
3648
+ const { graphDataRef, sceneDataRef, stateRef, setters, actions } = context;
3525
3649
  if (!graphDataRef.current || !sceneDataRef.current) return;
3526
3650
  try {
3527
3651
  setters.setIsProcessing(true);
@@ -3559,9 +3683,9 @@ var userActionHandlers = {
3559
3683
  target: versionedProcessId
3560
3684
  };
3561
3685
  newLinks.push(processVersionLink);
3562
- if (actions2.upsert_process_link_firestore) {
3686
+ if (actions.upsert_process_link_firestore) {
3563
3687
  savePromises.push(
3564
- actions2.upsert_process_link_firestore(
3688
+ actions.upsert_process_link_firestore(
3565
3689
  ownerId,
3566
3690
  parentFileId,
3567
3691
  processVersionLink
@@ -3621,7 +3745,7 @@ var userActionHandlers = {
3621
3745
  }
3622
3746
  ] : originalChildNode.timeline || []
3623
3747
  };
3624
- if (actions2.copy_google_doc_action && versionedChildNode.Links) {
3748
+ if (actions.copy_google_doc_action && versionedChildNode.Links) {
3625
3749
  const docLinkIndex = versionedChildNode.Links.findIndex(
3626
3750
  (l) => l.name === "Google docs"
3627
3751
  );
@@ -3631,7 +3755,7 @@ var userActionHandlers = {
3631
3755
  const docIdMatch = originalUrl.match(/\/d\/([a-zA-Z0-9-_]+)/);
3632
3756
  if (docIdMatch && docIdMatch[1]) {
3633
3757
  try {
3634
- const copyRes = await actions2.copy_google_doc_action(
3758
+ const copyRes = await actions.copy_google_doc_action(
3635
3759
  docIdMatch[1],
3636
3760
  nextName,
3637
3761
  (_d = sceneDataRef.current) == null ? void 0 : _d.view_google_folder_id
@@ -3680,34 +3804,34 @@ var userActionHandlers = {
3680
3804
  newLinks.push(...subResult.newLinks);
3681
3805
  savePromises.push(...subResult.savePromises);
3682
3806
  }
3683
- if (versionedChildNode.is_quest && actions2.upsert_quest_firestore) {
3807
+ if (versionedChildNode.is_quest && actions.upsert_quest_firestore) {
3684
3808
  savePromises.push(
3685
- actions2.upsert_quest_firestore(
3809
+ actions.upsert_quest_firestore(
3686
3810
  ownerId,
3687
3811
  parentFileId,
3688
3812
  versionedChildNode
3689
3813
  )
3690
3814
  );
3691
- if (actions2.upsert_quest_link_firestore) {
3815
+ if (actions.upsert_quest_link_firestore) {
3692
3816
  savePromises.push(
3693
- actions2.upsert_quest_link_firestore(
3817
+ actions.upsert_quest_link_firestore(
3694
3818
  ownerId,
3695
3819
  parentFileId,
3696
3820
  childVersionLink
3697
3821
  )
3698
3822
  );
3699
3823
  }
3700
- } else if (versionedChildNode.is_process && actions2.upsert_process_firestore) {
3824
+ } else if (versionedChildNode.is_process && actions.upsert_process_firestore) {
3701
3825
  savePromises.push(
3702
- actions2.upsert_process_firestore(
3826
+ actions.upsert_process_firestore(
3703
3827
  ownerId,
3704
3828
  parentFileId,
3705
3829
  versionedChildNode
3706
3830
  )
3707
3831
  );
3708
- if (actions2.upsert_process_link_firestore) {
3832
+ if (actions.upsert_process_link_firestore) {
3709
3833
  savePromises.push(
3710
- actions2.upsert_process_link_firestore(
3834
+ actions.upsert_process_link_firestore(
3711
3835
  ownerId,
3712
3836
  parentFileId,
3713
3837
  childVersionLink
@@ -3724,14 +3848,14 @@ var userActionHandlers = {
3724
3848
  versionedProcess.process_abstraction_tree.node_id = versionedProcessId;
3725
3849
  }
3726
3850
  sceneDataRef.current.quest_counter = currentCounter;
3727
- if (actions2.update_view_counter) {
3851
+ if (actions.update_view_counter) {
3728
3852
  savePromises.push(
3729
- actions2.update_view_counter(ownerId, parentFileId, currentCounter)
3853
+ actions.update_view_counter(ownerId, parentFileId, currentCounter)
3730
3854
  );
3731
3855
  }
3732
- if (actions2.upsert_process_firestore) {
3856
+ if (actions.upsert_process_firestore) {
3733
3857
  savePromises.push(
3734
- actions2.upsert_process_firestore(
3858
+ actions.upsert_process_firestore(
3735
3859
  ownerId,
3736
3860
  parentFileId,
3737
3861
  versionedProcess
@@ -3772,7 +3896,7 @@ var userActionHandlers = {
3772
3896
  );
3773
3897
  } else {
3774
3898
  savePromises.push(
3775
- actions2.save_view_data(
3899
+ actions.save_view_data(
3776
3900
  `x_view_dbs/${ownerId}/${parentFileId}`,
3777
3901
  specificParentData
3778
3902
  )
@@ -3823,7 +3947,7 @@ var userActionHandlers = {
3823
3947
  var SECTION_SPLIT_REGEX = /\*\/\s*(\d+)(?::([a-zA-Z0-9-]+))?\s*\//;
3824
3948
  var MENTION_REGEX = /\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g;
3825
3949
  var IMAGE_MENTION_REGEX = /\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g;
3826
- var LINK_REGEX = /\[([^\]]+)\]\((https?:\/\/[^\)]+)\)/g;
3950
+ var LINK_REGEX = /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g;
3827
3951
  var UNIQUE_PROP_TYPES = ["links", "images", "date", "documents"];
3828
3952
  var QUEST_STATUS_COLORS = {
3829
3953
  Backlog: "#64748b",
@@ -3886,7 +4010,7 @@ var resolveDescriptionReference = (refString, availableNodes = [], availableAnce
3886
4010
  /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/
3887
4011
  );
3888
4012
  if (!match) return null;
3889
- const [_, type, itemId, sectionId] = match;
4013
+ const [, type, itemId, sectionId] = match;
3890
4014
  let sourceItem = null;
3891
4015
  if (type === "node") {
3892
4016
  sourceItem = availableNodes.find((n) => String(n.id) === String(itemId));
@@ -3918,7 +4042,7 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
3918
4042
  const allAncestries = ancestryData || [];
3919
4043
  text = text.replace(
3920
4044
  /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g,
3921
- (match, type, itemId, sectionId) => {
4045
+ (match) => {
3922
4046
  const resolved = resolveDescriptionReference(
3923
4047
  match,
3924
4048
  allNodes,
@@ -3947,7 +4071,7 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
3947
4071
  text = text.replace(/^#+\s*/gm, "");
3948
4072
  text = text.replace(/```[\s\S]*?```/g, "[C\xF3digo]");
3949
4073
  text = text.replace(/-\s\[[xX ]\]\s*/g, "\u2022 ");
3950
- text = text.replace(/\[([^\]]+)\]\((https?:\/\/[^\)]+)\)/g, "$1");
4074
+ text = text.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, "$1");
3951
4075
  return text.trim();
3952
4076
  }
3953
4077
  function generateTooltipHtml(data, parentData, ancestryData) {
@@ -4583,7 +4707,8 @@ function createTimelineEntry(actorId, description, type = "comment", extras = {}
4583
4707
  id: img.id || uuidv4(),
4584
4708
  name: img.name || "",
4585
4709
  value: img.value || ""
4586
- }))
4710
+ })),
4711
+ metadata
4587
4712
  };
4588
4713
  }
4589
4714
  function calculateNextRecurrenceDate(config) {
@@ -4594,14 +4719,15 @@ function calculateNextRecurrenceDate(config) {
4594
4719
  case "daily":
4595
4720
  nextDate.setDate(now.getDate() + 1);
4596
4721
  break;
4597
- case "weekly":
4722
+ case "weekly": {
4598
4723
  const targetDay = parseInt(config.day_of_week, 10);
4599
4724
  const currentDay = now.getDay();
4600
4725
  let daysUntilNext = targetDay - currentDay;
4601
4726
  if (daysUntilNext <= 0) daysUntilNext += 7;
4602
4727
  nextDate.setDate(now.getDate() + daysUntilNext);
4603
4728
  break;
4604
- case "monthly":
4729
+ }
4730
+ case "monthly": {
4605
4731
  const dayOfMonth = parseInt(config.day_of_month || 1, 10);
4606
4732
  let mYear = now.getFullYear();
4607
4733
  let mMonth = now.getMonth();
@@ -4614,7 +4740,8 @@ function calculateNextRecurrenceDate(config) {
4614
4740
  }
4615
4741
  nextDate = mDate;
4616
4742
  break;
4617
- case "custom":
4743
+ }
4744
+ case "custom": {
4618
4745
  if (config.custom_date) {
4619
4746
  const parts = config.custom_date.split("/");
4620
4747
  if (parts.length === 3) {
@@ -4630,6 +4757,7 @@ function calculateNextRecurrenceDate(config) {
4630
4757
  return null;
4631
4758
  }
4632
4759
  break;
4760
+ }
4633
4761
  default:
4634
4762
  return null;
4635
4763
  }
@@ -4642,6 +4770,11 @@ function calculateNextRecurrenceDate(config) {
4642
4770
  confirmed: true
4643
4771
  };
4644
4772
  }
4773
+ var createSafeDate = (year, month = 0, day = 1) => {
4774
+ const d = /* @__PURE__ */ new Date();
4775
+ d.setFullYear(year, month, day);
4776
+ return d;
4777
+ };
4645
4778
  var parseDate = (str) => {
4646
4779
  if (!str) return null;
4647
4780
  const parts = str.split("/");
@@ -4650,7 +4783,7 @@ var parseDate = (str) => {
4650
4783
  const month = parseInt(parts[1], 10) - 1;
4651
4784
  const year = parseInt(parts[2], 10);
4652
4785
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
4653
- return new Date(year, month, day);
4786
+ return createSafeDate(year, month, day);
4654
4787
  }
4655
4788
  }
4656
4789
  return null;
@@ -4705,7 +4838,7 @@ var parseDateProp = (dateProp) => {
4705
4838
  } else if (dateProp.year) {
4706
4839
  const y = parseInt(dateProp.year, 10);
4707
4840
  if (!isNaN(y)) {
4708
- const d = new Date(y, 0, 1);
4841
+ const d = createSafeDate(y, 0, 1);
4709
4842
  info = {
4710
4843
  startDate: d,
4711
4844
  endDate: d,
@@ -4718,8 +4851,8 @@ var parseDateProp = (dateProp) => {
4718
4851
  const sY = parseInt(dateProp.date_interval.start, 10);
4719
4852
  const eY = parseInt(dateProp.date_interval.end, 10) || sY;
4720
4853
  if (!isNaN(sY)) {
4721
- const sd = new Date(sY, 0, 1);
4722
- const ed = new Date(eY, 11, 31);
4854
+ const sd = createSafeDate(sY, 0, 1);
4855
+ const ed = createSafeDate(eY, 11, 31);
4723
4856
  info = {
4724
4857
  startDate: sd,
4725
4858
  endDate: ed,
@@ -5548,6 +5681,7 @@ function XViewSidebar({
5548
5681
  const [showList, setShowList] = useState3(false);
5549
5682
  const [selectedNodeId, setSelectedNodeId] = useState3(null);
5550
5683
  const [isSaving, setIsSaving] = useState3(false);
5684
+ const [isNavigatingBack, setIsNavigatingBack] = useState3(false);
5551
5685
  const [activeFilters, setActiveFilters] = useState3([]);
5552
5686
  const [isFilterMenuOpen, setIsFilterMenuOpen] = useState3(false);
5553
5687
  const [filterCategory, setFilterCategory] = useState3("Type");
@@ -5753,12 +5887,16 @@ function XViewSidebar({
5753
5887
  };
5754
5888
  const handleAddFilter = () => {
5755
5889
  if (!filterValue.trim()) return;
5756
- setActiveFilters((prev) => [
5757
- ...prev,
5758
- { type: filterCategory, value: filterValue.trim() }
5759
- ]);
5890
+ const exists = activeFilters.some(
5891
+ (f) => f.type === filterCategory && f.value.trim().toLowerCase() === filterValue.trim().toLowerCase()
5892
+ );
5893
+ if (!exists) {
5894
+ setActiveFilters((prev) => [
5895
+ ...prev,
5896
+ { type: filterCategory, value: filterValue.trim() }
5897
+ ]);
5898
+ }
5760
5899
  setFilterValue("");
5761
- setIsFilterMenuOpen(false);
5762
5900
  };
5763
5901
  const handleRemoveFilter = (index) => {
5764
5902
  setActiveFilters((prev) => prev.filter((_, i) => i !== index));
@@ -6292,15 +6430,63 @@ function XViewSidebar({
6292
6430
  /* @__PURE__ */ React2.createElement("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" })
6293
6431
  ),
6294
6432
  "Filters"
6295
- ), isFilterMenuOpen && /* @__PURE__ */ React2.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__ */ React2.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ React2.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color", "Dataset"].map((opt) => /* @__PURE__ */ React2.createElement(
6433
+ ), isFilterMenuOpen && /* @__PURE__ */ React2.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__ */ React2.createElement("div", { className: "space-y-1.5 border-b border-white/10 pb-2 mb-0.5" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Ativos (", activeFilters.length, ")"), /* @__PURE__ */ React2.createElement(
6434
+ "button",
6435
+ {
6436
+ type: "button",
6437
+ onClick: () => setActiveFilters([]),
6438
+ className: "text-[9px] text-red-400 hover:text-red-300 transition-colors uppercase font-bold tracking-wider"
6439
+ },
6440
+ "Limpar tudo"
6441
+ )), /* @__PURE__ */ React2.createElement("div", { className: "flex flex-wrap gap-1 max-h-24 overflow-y-auto custom-scrollbar pt-1" }, activeFilters.map((filter, idx) => /* @__PURE__ */ React2.createElement(
6442
+ "span",
6443
+ {
6444
+ key: idx,
6445
+ 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"
6446
+ },
6447
+ filter.type === "Color" && /* @__PURE__ */ React2.createElement(
6448
+ "span",
6449
+ {
6450
+ className: "inline-block w-2 h-2 rounded-full border border-white/20",
6451
+ style: { backgroundColor: filter.value }
6452
+ }
6453
+ ),
6454
+ /* @__PURE__ */ React2.createElement("span", { className: "truncate max-w-[100px]" }, filter.type, ": ", filter.value),
6455
+ /* @__PURE__ */ React2.createElement(
6456
+ "button",
6457
+ {
6458
+ type: "button",
6459
+ onClick: () => handleRemoveFilter(idx),
6460
+ className: "hover:text-red-400 transition-colors ml-0.5",
6461
+ title: "Remover"
6462
+ },
6463
+ /* @__PURE__ */ React2.createElement(
6464
+ "svg",
6465
+ {
6466
+ xmlns: "http://www.w3.org/2000/svg",
6467
+ width: "10",
6468
+ height: "10",
6469
+ viewBox: "0 0 24 24",
6470
+ fill: "none",
6471
+ stroke: "currentColor",
6472
+ strokeWidth: "3",
6473
+ strokeLinecap: "round",
6474
+ strokeLinejoin: "round"
6475
+ },
6476
+ /* @__PURE__ */ React2.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
6477
+ /* @__PURE__ */ React2.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
6478
+ )
6479
+ )
6480
+ )))), /* @__PURE__ */ React2.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ React2.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color", "Dataset"].map((opt) => /* @__PURE__ */ React2.createElement(
6296
6481
  "button",
6297
6482
  {
6298
6483
  key: opt,
6484
+ type: "button",
6299
6485
  onClick: () => {
6300
6486
  setFilterCategory(opt);
6301
6487
  setFilterValue("");
6302
6488
  },
6303
- className: `flex-1 text-xs py-1 rounded transition-colors ${filterCategory === opt ? "bg-indigo-600 text-white" : "text-slate-400 hover:text-slate-200"}`
6489
+ 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"}`
6304
6490
  },
6305
6491
  opt
6306
6492
  )))), /* @__PURE__ */ React2.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Valor"), /* @__PURE__ */ React2.createElement("div", { className: "relative", ref: typeDropdownRef }, /* @__PURE__ */ React2.createElement(
@@ -6337,11 +6523,12 @@ function XViewSidebar({
6337
6523
  )))), /* @__PURE__ */ React2.createElement(
6338
6524
  "button",
6339
6525
  {
6526
+ type: "button",
6340
6527
  onClick: handleAddFilter,
6341
6528
  disabled: !filterValue.trim(),
6342
- 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"
6529
+ 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"
6343
6530
  },
6344
- "Aplicar Filtro"
6531
+ "Adicionar Filtro"
6345
6532
  ))))), /* @__PURE__ */ React2.createElement("div", { className: "h-px bg-white/10" }), /* @__PURE__ */ React2.createElement("button", { onClick: onOpenImportModal, className: buttonBaseClass }, /* @__PURE__ */ React2.createElement(FiPlus, { size: 16 }), "Importar Datasets / Views"), importSuccessMessage && /* @__PURE__ */ React2.createElement("p", { className: "text-[10px] text-emerald-400 animate-pulse px-1 font-medium -mt-1" }, importSuccessMessage), /* @__PURE__ */ React2.createElement("section", { className: "space-y-2" }, /* @__PURE__ */ React2.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Lentes"), /* @__PURE__ */ React2.createElement("div", { className: "relative", ref: questLensRef }, /* @__PURE__ */ React2.createElement(
6346
6533
  "button",
6347
6534
  {
@@ -6443,7 +6630,45 @@ function XViewSidebar({
6443
6630
  className: isSaving ? buttonDisabledClass : buttonBaseClass
6444
6631
  },
6445
6632
  isSaving ? "Salvando..." : "Atualizar cen\xE1rio inicial"
6446
- ), /* @__PURE__ */ React2.createElement("button", { onClick: onNavigateBack, className: buttonBaseClass }, "Voltar ao Dashboard"))),
6633
+ ), /* @__PURE__ */ React2.createElement(
6634
+ "button",
6635
+ {
6636
+ onClick: () => {
6637
+ setIsNavigatingBack(true);
6638
+ onNavigateBack();
6639
+ },
6640
+ disabled: isNavigatingBack,
6641
+ className: isNavigatingBack ? buttonDisabledClass : buttonBaseClass
6642
+ },
6643
+ isNavigatingBack ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(
6644
+ "svg",
6645
+ {
6646
+ className: "animate-spin h-4 w-4",
6647
+ xmlns: "http://www.w3.org/2000/svg",
6648
+ fill: "none",
6649
+ viewBox: "0 0 24 24"
6650
+ },
6651
+ /* @__PURE__ */ React2.createElement(
6652
+ "circle",
6653
+ {
6654
+ className: "opacity-25",
6655
+ cx: "12",
6656
+ cy: "12",
6657
+ r: "10",
6658
+ stroke: "currentColor",
6659
+ strokeWidth: "4"
6660
+ }
6661
+ ),
6662
+ /* @__PURE__ */ React2.createElement(
6663
+ "path",
6664
+ {
6665
+ className: "opacity-75",
6666
+ fill: "currentColor",
6667
+ 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"
6668
+ }
6669
+ )
6670
+ ), "Voltar ao Dashboard") : "Voltar ao Dashboard"
6671
+ ))),
6447
6672
  contextLabel && /* @__PURE__ */ React2.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),
6448
6673
  /* @__PURE__ */ React2.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" })
6449
6674
  ),
@@ -6512,6 +6737,11 @@ function CustomPropertyDisplay({
6512
6737
  const file = (_a = e.target.files) == null ? void 0 : _a[0];
6513
6738
  const targetIndex = currentUploadTargetRef.current;
6514
6739
  if (!file || targetIndex === null || !onUploadFile) return;
6740
+ const MAX_SIZE = 10 * 1024 * 1024;
6741
+ if (file.size > MAX_SIZE) {
6742
+ alert("O arquivo \xE9 muito grande. O limite m\xE1ximo de tamanho permitido \xE9 de 10MB.");
6743
+ return;
6744
+ }
6515
6745
  try {
6516
6746
  setUploadingItemIndex(targetIndex);
6517
6747
  const formData = new FormData();
@@ -6596,9 +6826,9 @@ function CustomPropertyDisplay({
6596
6826
  };
6597
6827
  const handleRemoveListItem = (j) => setTempProp((p) => ({ ...p, value: p.value.filter((_, k) => k !== j) }));
6598
6828
  const handleListItemChange = (j, f, v) => setTempProp((p) => {
6599
- const newValue2 = [...p.value];
6600
- newValue2[j] = { ...newValue2[j], [f]: v };
6601
- return { ...p, value: newValue2 };
6829
+ const newValue = [...p.value];
6830
+ newValue[j] = { ...newValue[j], [f]: v };
6831
+ return { ...p, value: newValue };
6602
6832
  });
6603
6833
  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";
6604
6834
  const renderEditView = () => {
@@ -6652,14 +6882,14 @@ function CustomPropertyDisplay({
6652
6882
  const inputClass = `${baseInput} ${noSpinnerClass}`;
6653
6883
  const handleDateTypeChange = (newDateType) => {
6654
6884
  var _a3, _b2, _c2;
6655
- let newValue2 = { type: newDateType };
6885
+ let newValue = { type: newDateType };
6656
6886
  if (newDateType === "Date Interval") {
6657
- newValue2.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
6658
- newValue2.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
6887
+ newValue.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
6888
+ newValue.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
6659
6889
  } else {
6660
- newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
6890
+ newValue.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
6661
6891
  }
6662
- handlePropChange("value", newValue2);
6892
+ handlePropChange("value", newValue);
6663
6893
  };
6664
6894
  const handleDateValueChange = (dateField, dateValue) => {
6665
6895
  handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
@@ -9487,7 +9717,7 @@ function CreateAncestryPanel({
9487
9717
  }
9488
9718
  };
9489
9719
  const sideActions = useMemo8(() => {
9490
- const actions2 = { left: null, right: null };
9720
+ const actions = { left: null, right: null };
9491
9721
  const isInBranch = branchStack.length > 0;
9492
9722
  if (internalHighlightedNodeId) {
9493
9723
  const found = findNodePath2(activeTree, internalHighlightedNodeId);
@@ -9495,13 +9725,13 @@ function CreateAncestryPanel({
9495
9725
  const branches = found.node.parallel_branches || [];
9496
9726
  const leftBranch = branches.find((b) => (b.direction || "right") === "left");
9497
9727
  if (leftBranch) {
9498
- actions2.left = {
9728
+ actions.left = {
9499
9729
  type: "open",
9500
9730
  label: "Editar Branch Esq.",
9501
9731
  onClick: () => handleBranchNav(internalHighlightedNodeId, "open", "left")
9502
9732
  };
9503
9733
  } else {
9504
- actions2.left = {
9734
+ actions.left = {
9505
9735
  type: "create",
9506
9736
  label: "Criar Branch Esq.",
9507
9737
  onClick: () => handleBranchNav(internalHighlightedNodeId, "open", "left")
@@ -9509,32 +9739,32 @@ function CreateAncestryPanel({
9509
9739
  }
9510
9740
  const rightBranch = branches.find((b) => (b.direction || "right") === "right");
9511
9741
  if (rightBranch) {
9512
- actions2.right = {
9742
+ actions.right = {
9513
9743
  type: "open",
9514
9744
  label: "Editar Branch Dir.",
9515
9745
  onClick: () => handleBranchNav(internalHighlightedNodeId, "open", "right")
9516
9746
  };
9517
9747
  } else {
9518
- actions2.right = {
9748
+ actions.right = {
9519
9749
  type: "create",
9520
9750
  label: "Criar Branch Dir.",
9521
9751
  onClick: () => handleBranchNav(internalHighlightedNodeId, "open", "right")
9522
9752
  };
9523
9753
  }
9524
9754
  }
9525
- return actions2;
9755
+ return actions;
9526
9756
  }
9527
9757
  if (isInBranch && isAtStartOfBranch) {
9528
9758
  const lastStep = branchStack[branchStack.length - 1];
9529
9759
  const entryDir = lastStep.entryDirection || "right";
9530
9760
  const backSide = entryDir === "right" ? "left" : "right";
9531
- actions2[backSide] = {
9761
+ actions[backSide] = {
9532
9762
  type: "back",
9533
9763
  label: "Voltar",
9534
9764
  onClick: () => handleBranchNav(null, "back")
9535
9765
  };
9536
9766
  }
9537
- return actions2;
9767
+ return actions;
9538
9768
  }, [internalHighlightedNodeId, branchStack, activeTree, isAtStartOfBranch]);
9539
9769
  const applyDescriptionToTree = (baseTree, descText, descSections) => {
9540
9770
  const rootTreeClone = JSON.parse(JSON.stringify(baseTree));
@@ -11592,9 +11822,9 @@ function InSceneCreationForm({
11592
11822
  };
11593
11823
  const handleToggleImageMode = () => {
11594
11824
  var _a2, _b;
11595
- const newValue2 = !useImageAsTexture;
11596
- setUseImageAsTexture(newValue2);
11597
- if (newValue2) {
11825
+ const newValue = !useImageAsTexture;
11826
+ setUseImageAsTexture(newValue);
11827
+ if (newValue) {
11598
11828
  const firstImageProp = customProps.find((p) => p.type === "images");
11599
11829
  if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
11600
11830
  const url = firstImageProp.value[0].value;
@@ -12069,9 +12299,9 @@ function InSceneVersionForm({
12069
12299
  };
12070
12300
  const handleToggleImageMode = () => {
12071
12301
  var _a, _b;
12072
- const newValue2 = !useImageAsTexture;
12073
- setUseImageAsTexture(newValue2);
12074
- if (newValue2) {
12302
+ const newValue = !useImageAsTexture;
12303
+ setUseImageAsTexture(newValue);
12304
+ if (newValue) {
12075
12305
  const firstImageProp = customProps.find((p) => p.type === "images");
12076
12306
  if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
12077
12307
  const url = firstImageProp.value[0].value;
@@ -13663,6 +13893,7 @@ function NodeDetailsPanel({
13663
13893
  }
13664
13894
  };
13665
13895
  const handleToggleImageMode = () => {
13896
+ const newValue = !useImageAsTexture;
13666
13897
  setUseImageAsTexture(newValue);
13667
13898
  setHasUnsavedChanges(true);
13668
13899
  let activeUrl = null;
@@ -19199,7 +19430,13 @@ function MilestoneManager({
19199
19430
  // src/components/UpgradeModal.jsx
19200
19431
  import React35 from "react";
19201
19432
  import { Sparkles, X, Rocket, Zap, ShieldCheck } from "lucide-react";
19202
- function UpgradeModal({ isOpen, onClose, nodeCounter }) {
19433
+ function UpgradeModal({
19434
+ isOpen,
19435
+ onClose,
19436
+ nodeCounter,
19437
+ nodeLimit = 100,
19438
+ onUpgrade
19439
+ }) {
19203
19440
  if (!isOpen) return null;
19204
19441
  return /* @__PURE__ */ React35.createElement("div", { className: "fixed inset-0 z-[10000] flex items-center justify-center p-4" }, /* @__PURE__ */ React35.createElement("style", null, `
19205
19442
  @keyframes modalShow {
@@ -19225,12 +19462,16 @@ function UpgradeModal({ isOpen, onClose, nodeCounter }) {
19225
19462
  className: "absolute top-6 right-6 p-2 rounded-full hover:bg-white/5 text-white/40 hover:text-white transition-all duration-300"
19226
19463
  },
19227
19464
  /* @__PURE__ */ React35.createElement(X, { size: 20 })
19228
- ), /* @__PURE__ */ React35.createElement("div", { className: "p-12 flex flex-col items-center text-center" }, /* @__PURE__ */ React35.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__ */ React35.createElement("div", { className: "w-full h-full rounded-[2rem] bg-[#0d1117] flex items-center justify-center" }, /* @__PURE__ */ React35.createElement(Zap, { className: "text-sky-400 fill-sky-400/20", size: 32 }))), /* @__PURE__ */ React35.createElement("h2", { className: "text-3xl font-bold text-white mb-4 tracking-tight" }, "Limite de Nodes", " ", /* @__PURE__ */ React35.createElement("span", { className: "bg-clip-text text-transparent bg-gradient-to-r from-sky-400 to-indigo-400" }, "Atingido")), /* @__PURE__ */ React35.createElement("p", { className: "text-white/60 text-lg leading-relaxed mb-10 max-w-md" }, "Voc\xEA atingiu o limite de", " ", /* @__PURE__ */ React35.createElement("span", { className: "text-white font-semibold" }, "50.000 nodes"), " do seu plano Free. Seu universo est\xE1 crescendo r\xE1pido!"), /* @__PURE__ */ React35.createElement("div", { className: "w-full grid grid-cols-2 gap-4 mb-10" }, /* @__PURE__ */ React35.createElement("div", { className: "bg-white/5 border border-white/10 rounded-3xl p-6 backdrop-blur-md" }, /* @__PURE__ */ React35.createElement("p", { className: "text-white/30 text-[10px] font-bold uppercase tracking-widest mb-1" }, "Status Atual"), /* @__PURE__ */ React35.createElement("p", { className: "text-2xl font-bold text-white" }, nodeCounter, " / 50000")), /* @__PURE__ */ React35.createElement("div", { className: "bg-white/5 border border-white/10 rounded-3xl p-6 backdrop-blur-md" }, /* @__PURE__ */ React35.createElement("p", { className: "text-white/30 text-[10px] font-bold uppercase tracking-widest mb-1" }, "Pr\xF3ximo Plano"), /* @__PURE__ */ React35.createElement("p", { className: "text-2xl font-bold text-sky-400" }, "Ilimitado"))), /* @__PURE__ */ React35.createElement("div", { className: "w-full space-y-4 mb-10 text-left px-4" }, /* @__PURE__ */ React35.createElement("div", { className: "flex items-center gap-3 text-white/70 text-sm" }, /* @__PURE__ */ React35.createElement("div", { className: "w-6 h-6 rounded-full bg-sky-500/10 flex items-center justify-center shrink-0" }, /* @__PURE__ */ React35.createElement(Rocket, { size: 14, className: "text-sky-400" })), /* @__PURE__ */ React35.createElement("span", null, "Nodes, Quests e Processos ilimitados")), /* @__PURE__ */ React35.createElement("div", { className: "flex items-center gap-3 text-white/70 text-sm" }, /* @__PURE__ */ React35.createElement("div", { className: "w-6 h-6 rounded-full bg-sky-500/10 flex items-center justify-center shrink-0" }, /* @__PURE__ */ React35.createElement(ShieldCheck, { size: 14, className: "text-sky-400" })), /* @__PURE__ */ React35.createElement("span", null, "Suporte priorit\xE1rio e recursos exclusivos"))), /* @__PURE__ */ React35.createElement("div", { className: "w-full flex flex-col gap-4" }, /* @__PURE__ */ React35.createElement(
19465
+ ), /* @__PURE__ */ React35.createElement("div", { className: "p-12 flex flex-col items-center text-center" }, /* @__PURE__ */ React35.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__ */ React35.createElement("div", { className: "w-full h-full rounded-[2rem] bg-[#0d1117] flex items-center justify-center" }, /* @__PURE__ */ React35.createElement(Zap, { className: "text-sky-400 fill-sky-400/20", size: 32 }))), /* @__PURE__ */ React35.createElement("h2", { className: "text-3xl font-bold text-white mb-4 tracking-tight" }, "Limite de Nodes", " ", /* @__PURE__ */ React35.createElement("span", { className: "bg-clip-text text-transparent bg-gradient-to-r from-sky-400 to-indigo-400" }, "Atingido")), /* @__PURE__ */ React35.createElement("p", { className: "text-white/60 text-lg leading-relaxed mb-10 max-w-md" }, "Voc\xEA atingiu o limite de", " ", /* @__PURE__ */ React35.createElement("span", { className: "text-white font-semibold" }, nodeLimit, " nodes"), " ", "do seu plano Free. Seu universo est\xE1 crescendo r\xE1pido!"), /* @__PURE__ */ React35.createElement("div", { className: "w-full grid grid-cols-2 gap-4 mb-10" }, /* @__PURE__ */ React35.createElement("div", { className: "bg-white/5 border border-white/10 rounded-3xl p-6 backdrop-blur-md" }, /* @__PURE__ */ React35.createElement("p", { className: "text-white/30 text-[10px] font-bold uppercase tracking-widest mb-1" }, "Status Atual"), /* @__PURE__ */ React35.createElement("p", { className: "text-2xl font-bold text-white" }, nodeCounter, " / ", nodeLimit)), /* @__PURE__ */ React35.createElement("div", { className: "bg-white/5 border border-white/10 rounded-3xl p-6 backdrop-blur-md" }, /* @__PURE__ */ React35.createElement("p", { className: "text-white/30 text-[10px] font-bold uppercase tracking-widest mb-1" }, "Pr\xF3ximo Plano"), /* @__PURE__ */ React35.createElement("p", { className: "text-2xl font-bold text-sky-400" }, "Ilimitado"))), /* @__PURE__ */ React35.createElement("div", { className: "w-full space-y-4 mb-10 text-left px-4" }, /* @__PURE__ */ React35.createElement("div", { className: "flex items-center gap-3 text-white/70 text-sm" }, /* @__PURE__ */ React35.createElement("div", { className: "w-6 h-6 rounded-full bg-sky-500/10 flex items-center justify-center shrink-0" }, /* @__PURE__ */ React35.createElement(Rocket, { size: 14, className: "text-sky-400" })), /* @__PURE__ */ React35.createElement("span", null, "Cria\xE7\xE3o de Nodes ilimitada")), /* @__PURE__ */ React35.createElement("div", { className: "flex items-center gap-3 text-white/70 text-sm" }, /* @__PURE__ */ React35.createElement("div", { className: "w-6 h-6 rounded-full bg-sky-500/10 flex items-center justify-center shrink-0" }, /* @__PURE__ */ React35.createElement(ShieldCheck, { size: 14, className: "text-sky-400" })), /* @__PURE__ */ React35.createElement("span", null, "Acesso expandido \xE0s funcionalidades colaborativas e de comunidade"))), /* @__PURE__ */ React35.createElement("div", { className: "w-full flex flex-col gap-4" }, /* @__PURE__ */ React35.createElement(
19229
19466
  "button",
19230
19467
  {
19231
19468
  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",
19232
- onClick: () => {
19233
- alert("Redirecionando para upgrade... (Em breve)");
19469
+ onClick: async () => {
19470
+ if (onUpgrade) {
19471
+ await onUpgrade();
19472
+ } else {
19473
+ alert("Redirecionando para upgrade... (Em breve)");
19474
+ }
19234
19475
  onClose();
19235
19476
  }
19236
19477
  },
@@ -19271,7 +19512,62 @@ var TIMELINE_LAYER_SPACING_Y = 25;
19271
19512
  var TIMELINE_NODE_SPACING_X = 20;
19272
19513
  var TIMELINE_INTERVAL_BAR_RADIUS = 0.2;
19273
19514
  var TIMELINE_INTERVAL_BAR_OPACITY = 0.85;
19274
- var TIMELINE_GOLD_COLOR = 16766720;
19515
+ var TIMELINE_BLUE_COLOR = 30719;
19516
+ var TIMELINE_BRACKET_WIDTH = 4;
19517
+ var createTimelineIntervalBracket = () => {
19518
+ const group = new THREE3.Group();
19519
+ group.name = "timelineIntervalBracket";
19520
+ const barMaterial = new THREE3.MeshStandardMaterial({
19521
+ color: TIMELINE_BLUE_COLOR,
19522
+ emissive: 17578,
19523
+ emissiveIntensity: 0.5,
19524
+ transparent: true,
19525
+ opacity: TIMELINE_INTERVAL_BAR_OPACITY,
19526
+ roughness: 0.3,
19527
+ metalness: 0.8
19528
+ });
19529
+ const geoHorizontal = new THREE3.CylinderGeometry(
19530
+ TIMELINE_INTERVAL_BAR_RADIUS,
19531
+ TIMELINE_INTERVAL_BAR_RADIUS,
19532
+ 1,
19533
+ 16
19534
+ );
19535
+ const geoVertical = new THREE3.CylinderGeometry(
19536
+ TIMELINE_INTERVAL_BAR_RADIUS,
19537
+ TIMELINE_INTERVAL_BAR_RADIUS,
19538
+ 1,
19539
+ 16
19540
+ );
19541
+ const bottomArm = new THREE3.Mesh(geoHorizontal, barMaterial);
19542
+ bottomArm.rotation.z = Math.PI / 2;
19543
+ const verticalStem = new THREE3.Mesh(geoVertical, barMaterial);
19544
+ const topArm = new THREE3.Mesh(geoHorizontal.clone(), barMaterial);
19545
+ topArm.rotation.z = Math.PI / 2;
19546
+ group.add(bottomArm);
19547
+ group.add(verticalStem);
19548
+ group.add(topArm);
19549
+ group.userData.bottomArm = bottomArm;
19550
+ group.userData.verticalStem = verticalStem;
19551
+ group.userData.topArm = topArm;
19552
+ return group;
19553
+ };
19554
+ var updateTimelineIntervalBracket = (group, startPos, endPos) => {
19555
+ if (!group || !group.userData.bottomArm) return;
19556
+ const { bottomArm, verticalStem, topArm } = group.userData;
19557
+ const w = TIMELINE_BRACKET_WIDTH;
19558
+ const z = -0.5;
19559
+ const p1 = new THREE3.Vector3(startPos.x, startPos.y, z);
19560
+ const p2 = new THREE3.Vector3(startPos.x - w, startPos.y, z);
19561
+ const p3 = new THREE3.Vector3(endPos.x - w, endPos.y, z);
19562
+ const p4 = new THREE3.Vector3(endPos.x, endPos.y, z);
19563
+ bottomArm.scale.y = w;
19564
+ bottomArm.position.set((p1.x + p2.x) / 2, p1.y, z);
19565
+ const verticalLen = Math.abs(p3.y - p2.y);
19566
+ verticalStem.scale.y = Math.max(verticalLen, 1e-3);
19567
+ verticalStem.position.set(p2.x, (p2.y + p3.y) / 2, z);
19568
+ topArm.scale.y = w;
19569
+ topArm.position.set((p3.x + p4.x) / 2, p3.y, z);
19570
+ };
19275
19571
  var QUEST_LENS_COLUMN_SPACING = 80;
19276
19572
  var QUEST_LENS_ROW_SPACING = 40;
19277
19573
  var getAllNodeIdsFromTree = (treeNode) => {
@@ -19338,7 +19634,8 @@ function XViewScene({
19338
19634
  delete_process_firestore,
19339
19635
  upsert_process_link_firestore,
19340
19636
  delete_process_link_firestore,
19341
- update_node_counter
19637
+ update_node_counter,
19638
+ onUpgrade
19342
19639
  }) {
19343
19640
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
19344
19641
  const { data: session, status } = useSession();
@@ -19355,6 +19652,11 @@ function XViewScene({
19355
19652
  }
19356
19653
  return null;
19357
19654
  }, [encryptedConfig, session]);
19655
+ useEffect27(() => {
19656
+ if (viewParams == null ? void 0 : viewParams.name) {
19657
+ document.title = `${viewParams.name} - x.plore`;
19658
+ }
19659
+ }, [viewParams == null ? void 0 : viewParams.name]);
19358
19660
  useEffect27(() => {
19359
19661
  async function verifyPermission() {
19360
19662
  if (!viewParams || !session || !check_user_permission) return;
@@ -19518,6 +19820,7 @@ function XViewScene({
19518
19820
  const [isMilestoneManagerOpen, setIsMilestoneManagerOpen] = useState33(false);
19519
19821
  const [nodeCounter, setNodeCounter] = useState33(0);
19520
19822
  const [isUpgradeModalOpen, setIsUpgradeModalOpen] = useState33(false);
19823
+ const [nodeLimit, setNodeLimit] = useState33(100);
19521
19824
  const mountRef = useRef25(null);
19522
19825
  const tooltipRef = useRef25(null);
19523
19826
  const formRef = useRef25(null);
@@ -19953,7 +20256,9 @@ function XViewScene({
19953
20256
  const month = parseInt(parts[1], 10) - 1;
19954
20257
  const year = parseInt(parts[2], 10);
19955
20258
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
19956
- return new Date(year, month, day);
20259
+ const d = /* @__PURE__ */ new Date();
20260
+ d.setFullYear(year, month, day);
20261
+ return d;
19957
20262
  }
19958
20263
  }
19959
20264
  return null;
@@ -20010,10 +20315,12 @@ function XViewScene({
20010
20315
  });
20011
20316
  const timelineWidth = (columns.length > 0 ? columns.length - 1 : 0) * TIMELINE_NODE_SPACING_X;
20012
20317
  const offsetX = -timelineWidth / 2;
20318
+ const allFinalPoints = [];
20013
20319
  timelineNodes.forEach((node) => {
20014
20320
  const { mesh, type, startDate, endDate, label, columnIndex, isUndated } = node;
20015
20321
  const x = columnIndex * TIMELINE_NODE_SPACING_X + offsetX;
20016
20322
  const y = isUndated ? 0 : timeToYMap.get(startDate.getTime());
20323
+ allFinalPoints.push(new THREE3.Vector3(x, y, 0));
20017
20324
  const timelineTween = new Tween2(mesh.position).to({ x, y, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
20018
20325
  tweenGroup.add(timelineTween);
20019
20326
  timelineTween.start();
@@ -20026,25 +20333,16 @@ function XViewScene({
20026
20333
  if (type === "interval") {
20027
20334
  const endY = timeToYMap.get(endDate.getTime());
20028
20335
  if (endY > y) {
20029
- const barGeometry = new THREE3.CylinderGeometry(
20030
- TIMELINE_INTERVAL_BAR_RADIUS,
20031
- TIMELINE_INTERVAL_BAR_RADIUS,
20032
- 1,
20033
- 16
20034
- );
20035
- const barMaterial = new THREE3.MeshStandardMaterial({
20036
- color: TIMELINE_GOLD_COLOR,
20037
- emissive: 12092939,
20038
- emissiveIntensity: 0.4,
20039
- transparent: true,
20040
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20041
- roughness: 0.3,
20042
- metalness: 0.8
20043
- });
20044
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20336
+ allFinalPoints.push(new THREE3.Vector3(x, endY, 0));
20337
+ const intervalBar = createTimelineIntervalBracket();
20045
20338
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20046
20339
  const endPosition = new THREE3.Vector3(x, endY, -0.5);
20047
20340
  intervalBar.userData.endPosition = endPosition;
20341
+ updateTimelineIntervalBracket(
20342
+ intervalBar,
20343
+ new THREE3.Vector3(x, y, 0),
20344
+ endPosition
20345
+ );
20048
20346
  timelineIntervalsGroup.add(intervalBar);
20049
20347
  const endLabelText = endDate.getFullYear().toString();
20050
20348
  const endLabel = createTextSprite(endLabelText, 48);
@@ -20057,7 +20355,21 @@ function XViewScene({
20057
20355
  }
20058
20356
  }
20059
20357
  });
20060
- }, []);
20358
+ if (allFinalPoints.length > 0) {
20359
+ const boundingBox = new THREE3.Box3().setFromPoints(allFinalPoints);
20360
+ const center = new THREE3.Vector3();
20361
+ boundingBox.getCenter(center);
20362
+ const size = new THREE3.Vector3();
20363
+ boundingBox.getSize(size);
20364
+ const spread = Math.max(size.x, size.y, size.z, 1);
20365
+ const desiredDistance = Math.max(
20366
+ x_view_config.CAMERA_ZOOM_DISTANCE,
20367
+ spread * 0.75
20368
+ );
20369
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
20370
+ tweenToTarget(center, zoomFactor);
20371
+ }
20372
+ }, [tweenToTarget]);
20061
20373
  const handleVersionTimeline = useCallback8(
20062
20374
  (sourceMesh, versionMeshes) => {
20063
20375
  const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
@@ -20093,7 +20405,9 @@ function XViewScene({
20093
20405
  const month = parseInt(parts[1], 10) - 1;
20094
20406
  const year = parseInt(parts[2], 10);
20095
20407
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
20096
- return new Date(year, month, day);
20408
+ const d = /* @__PURE__ */ new Date();
20409
+ d.setFullYear(year, month, day);
20410
+ return d;
20097
20411
  }
20098
20412
  }
20099
20413
  return null;
@@ -20164,22 +20478,7 @@ function XViewScene({
20164
20478
  if (type === "interval") {
20165
20479
  const relativeEndY = timeToYMap.get(endDate.getTime()) - timeToYMap.get(startDate.getTime());
20166
20480
  if (relativeEndY > 0) {
20167
- const barGeometry = new THREE3.CylinderGeometry(
20168
- TIMELINE_INTERVAL_BAR_RADIUS,
20169
- TIMELINE_INTERVAL_BAR_RADIUS,
20170
- 1,
20171
- 16
20172
- );
20173
- const barMaterial = new THREE3.MeshStandardMaterial({
20174
- color: TIMELINE_GOLD_COLOR,
20175
- emissive: 12092939,
20176
- emissiveIntensity: 0.4,
20177
- transparent: true,
20178
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20179
- roughness: 0.3,
20180
- metalness: 0.8
20181
- });
20182
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20481
+ const intervalBar = createTimelineIntervalBracket();
20183
20482
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20184
20483
  const endPosition = new THREE3.Vector3(
20185
20484
  baseX,
@@ -20187,6 +20486,11 @@ function XViewScene({
20187
20486
  -0.5
20188
20487
  );
20189
20488
  intervalBar.userData.endPosition = endPosition;
20489
+ updateTimelineIntervalBracket(
20490
+ intervalBar,
20491
+ new THREE3.Vector3(x, y, 0),
20492
+ endPosition
20493
+ );
20190
20494
  timelineIntervalsGroup.add(intervalBar);
20191
20495
  const endLabelText = endDate.getFullYear().toString();
20192
20496
  const endLabel = createTextSprite(endLabelText, 48);
@@ -20279,6 +20583,9 @@ function XViewScene({
20279
20583
  ancestryDataRef.current
20280
20584
  );
20281
20585
  setNodeCounter(sceneResponse.data.node_counter || 0);
20586
+ if (sceneResponse.data.node_limit !== void 0) {
20587
+ setNodeLimit(sceneResponse.data.node_limit);
20588
+ }
20282
20589
  setIsInitialized(true);
20283
20590
  } else {
20284
20591
  console.error(
@@ -20679,25 +20986,15 @@ function XViewScene({
20679
20986
  const endY = timeToYMap.get(timeInfo.endDate.getTime());
20680
20987
  if (Math.abs(endY - targetY) > 0.1) {
20681
20988
  const barHeight = Math.abs(endY - targetY);
20682
- const barGeometry = new THREE3.CylinderGeometry(
20683
- TIMELINE_INTERVAL_BAR_RADIUS,
20684
- TIMELINE_INTERVAL_BAR_RADIUS,
20685
- 1,
20686
- 16
20687
- );
20688
- const barMaterial = new THREE3.MeshStandardMaterial({
20689
- color: TIMELINE_GOLD_COLOR,
20690
- emissive: 12092939,
20691
- emissiveIntensity: 0.4,
20692
- transparent: true,
20693
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20694
- roughness: 0.3,
20695
- metalness: 0.8
20696
- });
20697
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20989
+ const intervalBar = createTimelineIntervalBracket();
20698
20990
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20699
- intervalBar.position.set(finalX, (targetY + endY) / 2, -0.5);
20700
- intervalBar.scale.y = barHeight;
20991
+ const endPosition = new THREE3.Vector3(finalX, endY, -0.5);
20992
+ intervalBar.userData.endPosition = endPosition;
20993
+ updateTimelineIntervalBracket(
20994
+ intervalBar,
20995
+ new THREE3.Vector3(finalX, targetY, 0),
20996
+ endPosition
20997
+ );
20701
20998
  graphGroup.add(intervalBar);
20702
20999
  mesh.userData.timelineIntervalBar = intervalBar;
20703
21000
  const endLabel = createTextSprite(
@@ -21466,23 +21763,12 @@ function XViewScene({
21466
21763
  }
21467
21764
  });
21468
21765
  (_a2 = stateRef.current.timelineIntervalsGroup) == null ? void 0 : _a2.children.forEach((obj) => {
21469
- if (obj.isMesh && obj.userData.associatedNodeId && obj.userData.endPosition) {
21766
+ if (obj.userData.associatedNodeId && obj.userData.endPosition) {
21470
21767
  const bar = obj;
21471
21768
  const node = stateRef.current.nodeObjects[String(bar.userData.associatedNodeId)];
21472
21769
  const endPos = bar.userData.endPosition;
21473
21770
  if (node) {
21474
- const startPos = node.position;
21475
- const distance = startPos.distanceTo(endPos);
21476
- bar.scale.y = distance;
21477
- const midpoint = new THREE3.Vector3().lerpVectors(
21478
- startPos,
21479
- endPos,
21480
- 0.5
21481
- );
21482
- bar.position.copy(midpoint);
21483
- const direction = new THREE3.Vector3().subVectors(endPos, startPos).normalize();
21484
- const upVector = new THREE3.Vector3(0, 1, 0);
21485
- bar.quaternion.setFromUnitVectors(upVector, direction);
21771
+ updateTimelineIntervalBracket(bar, node.position, endPos);
21486
21772
  }
21487
21773
  }
21488
21774
  });
@@ -21958,7 +22244,8 @@ function XViewScene({
21958
22244
  handleRenderProcessAbstractionTree,
21959
22245
  nodeCounter,
21960
22246
  checkLimit: (countToAdd = 1) => {
21961
- if (nodeCounter + countToAdd > 5e4) {
22247
+ if (nodeLimit === -1) return false;
22248
+ if (nodeCounter + countToAdd > nodeLimit) {
21962
22249
  setIsUpgradeModalOpen(true);
21963
22250
  return true;
21964
22251
  }
@@ -22519,7 +22806,7 @@ function XViewScene({
22519
22806
  sceneDataRef: sceneDataRef2,
22520
22807
  stateRef: stateRef2,
22521
22808
  setters,
22522
- actions: actions2,
22809
+ actions,
22523
22810
  sceneSaveUrl: sceneSaveUrl2,
22524
22811
  viewType,
22525
22812
  sceneConfigId: sceneConfigId2,
@@ -22537,11 +22824,11 @@ function XViewScene({
22537
22824
  const currentCounter = sceneDataRef2.current.quest_counter || 1;
22538
22825
  const nextCounter = currentCounter + 1;
22539
22826
  try {
22540
- if (actions2.upsert_quest_firestore) {
22541
- await actions2.upsert_quest_firestore(ownerId2, sceneConfigId2, newNode);
22827
+ if (actions.upsert_quest_firestore) {
22828
+ await actions.upsert_quest_firestore(ownerId2, sceneConfigId2, newNode);
22542
22829
  }
22543
- if (actions2.update_view_counter) {
22544
- await actions2.update_view_counter(ownerId2, sceneConfigId2, nextCounter);
22830
+ if (actions.update_view_counter) {
22831
+ await actions.update_view_counter(ownerId2, sceneConfigId2, nextCounter);
22545
22832
  }
22546
22833
  sceneDataRef2.current.quest_counter = nextCounter;
22547
22834
  stateRef2.current.nodeIdToParentFileMap.set(String(newNode.id), {
@@ -22583,7 +22870,7 @@ function XViewScene({
22583
22870
  sceneDataRef: sceneDataRef2,
22584
22871
  stateRef: stateRef2,
22585
22872
  setters,
22586
- actions: actions2,
22873
+ actions,
22587
22874
  viewType,
22588
22875
  sceneConfigId: sceneConfigId2,
22589
22876
  ownerId: ownerId2,
@@ -22601,8 +22888,8 @@ function XViewScene({
22601
22888
  ]
22602
22889
  };
22603
22890
  try {
22604
- if (actions2.upsert_process_firestore) {
22605
- await actions2.upsert_process_firestore(ownerId2, sceneConfigId2, newNode);
22891
+ if (actions.upsert_process_firestore) {
22892
+ await actions.upsert_process_firestore(ownerId2, sceneConfigId2, newNode);
22606
22893
  }
22607
22894
  stateRef2.current.nodeIdToParentFileMap.set(String(newNode.id), {
22608
22895
  parentFileId: sceneConfigId2,
@@ -24463,8 +24750,9 @@ function XViewScene({
24463
24750
  )) == null ? void 0 : _b.parentFileId : null;
24464
24751
  const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
24465
24752
  useEffect27(() => {
24466
- if (isInitialized && focusNodeId && !hasFocusedInitial) {
24467
- const nodeObjects = stateRef.current.nodeObjects || {};
24753
+ if (!isInitialized || hasFocusedInitial) return;
24754
+ const nodeObjects = stateRef.current.nodeObjects || {};
24755
+ if (focusNodeId) {
24468
24756
  const targetMesh = nodeObjects[String(focusNodeId)];
24469
24757
  if (targetMesh) {
24470
24758
  setTimeout(() => {
@@ -24477,11 +24765,34 @@ function XViewScene({
24477
24765
  "O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
24478
24766
  );
24479
24767
  }
24768
+ } else if (!focusAncestryId) {
24769
+ const meshes = Object.values(nodeObjects);
24770
+ if (meshes.length > 0) {
24771
+ setTimeout(() => {
24772
+ const nodePositions = meshes.map((mesh) => mesh.position.clone());
24773
+ const boundingBox = new THREE3.Box3().setFromPoints(nodePositions);
24774
+ const center = new THREE3.Vector3();
24775
+ boundingBox.getCenter(center);
24776
+ const size = new THREE3.Vector3();
24777
+ boundingBox.getSize(size);
24778
+ const maxDim = Math.max(size.x, size.y, size.z);
24779
+ const desiredDistance = Math.max(
24780
+ x_view_config.CAMERA_ZOOM_DISTANCE,
24781
+ maxDim * 1.8
24782
+ );
24783
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
24784
+ tweenToTarget(center, zoomFactor);
24785
+ setHasFocusedInitial(true);
24786
+ }, 300);
24787
+ } else {
24788
+ setHasFocusedInitial(true);
24789
+ }
24480
24790
  }
24481
24791
  }, [
24482
24792
  isInitialized,
24483
24793
  sceneVersion,
24484
24794
  focusNodeId,
24795
+ focusAncestryId,
24485
24796
  hasFocusedInitial,
24486
24797
  tweenToTarget
24487
24798
  ]);
@@ -25475,7 +25786,20 @@ function XViewScene({
25475
25786
  {
25476
25787
  isOpen: isUpgradeModalOpen,
25477
25788
  onClose: () => setIsUpgradeModalOpen(false),
25478
- nodeCounter
25789
+ nodeCounter,
25790
+ nodeLimit,
25791
+ onUpgrade: async () => {
25792
+ if (onUpgrade) {
25793
+ const res = await onUpgrade();
25794
+ if (res.success) {
25795
+ setNodeLimit(-1);
25796
+ alert("Upgrade realizado com sucesso! Sua conta agora \xE9 Premium.");
25797
+ } else {
25798
+ alert("Erro ao realizar upgrade: " + res.error);
25799
+ }
25800
+ return res;
25801
+ }
25802
+ }
25479
25803
  }
25480
25804
  )
25481
25805
  );
@@ -25591,6 +25915,16 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
25591
25915
  }
25592
25916
  })();
25593
25917
  parentResponsesPromises.push(questFetchPromise, processFetchPromise, nodeCounterPromise);
25918
+ } else {
25919
+ const nodeCounterPromise = (async () => {
25920
+ try {
25921
+ if (!db_services.get_user_node_counter) return { success: true, node_counter: 0 };
25922
+ return await db_services.get_user_node_counter(owner_id);
25923
+ } catch (err) {
25924
+ return { success: false, error: err.message };
25925
+ }
25926
+ })();
25927
+ parentResponsesPromises.push(nodeCounterPromise);
25594
25928
  }
25595
25929
  const parentResponsesResults = await Promise.allSettled(
25596
25930
  parentResponsesPromises
@@ -25599,9 +25933,10 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
25599
25933
  let questDataObj = null;
25600
25934
  let processDataObj = null;
25601
25935
  let userNodeCounter = 0;
25936
+ let userNodeLimit = 100;
25602
25937
  for (let i = 0; i < parentResponsesResults.length; i++) {
25603
25938
  const result = parentResponsesResults[i];
25604
- if (isView && i >= parentDbObjects.length) {
25939
+ if (i >= parentDbObjects.length) {
25605
25940
  if (result.status === "fulfilled" && ((_a = result.value) == null ? void 0 : _a.success)) {
25606
25941
  if (result.value.type === "processes") {
25607
25942
  processDataObj = result.value.data;
@@ -25612,6 +25947,9 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
25612
25947
  }
25613
25948
  } else if (result.value.node_counter !== void 0) {
25614
25949
  userNodeCounter = result.value.node_counter;
25950
+ if (result.value.node_limit !== void 0) {
25951
+ userNodeLimit = result.value.node_limit;
25952
+ }
25615
25953
  }
25616
25954
  }
25617
25955
  continue;
@@ -25977,7 +26315,8 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
25977
26315
  scene: validatedSceneData,
25978
26316
  parent: parentData,
25979
26317
  ancestry: ancestryData,
25980
- node_counter: userNodeCounter
26318
+ node_counter: userNodeCounter,
26319
+ node_limit: userNodeLimit
25981
26320
  }
25982
26321
  };
25983
26322
  } catch (error) {