@lv-x-software-house/x_view 1.3.5 → 1.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +337 -92
  2. package/dist/index.mjs +348 -93
  3. package/package.json +1 -1
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);
@@ -2138,6 +2194,7 @@ var userActionHandlers = {
2138
2194
  if (!nodeData || !sceneDataRef.current) return;
2139
2195
  const nodeIdToDismiss = nodeData.id;
2140
2196
  const strNodeId = String(nodeIdToDismiss);
2197
+ removeAllTimelineElements(stateRef.current);
2141
2198
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2142
2199
  if (ancestryGroup && ancestryLinks) {
2143
2200
  const remainingAncestryLinks = [];
@@ -2169,6 +2226,7 @@ var userActionHandlers = {
2169
2226
  (id) => id !== strNodeIdToKeep
2170
2227
  );
2171
2228
  if (nodeIdsToDismiss.length === 0) return;
2229
+ removeAllTimelineElements(stateRef.current);
2172
2230
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2173
2231
  if (ancestryGroup && ancestryLinks) {
2174
2232
  ancestryLinks.forEach((link) => {
@@ -2191,6 +2249,7 @@ var userActionHandlers = {
2191
2249
  setters.setMultiContextMenu({ visible: false });
2192
2250
  if (!nodeIds || nodeIds.size === 0 || !sceneDataRef.current) return;
2193
2251
  const strNodeIds = Array.from(nodeIds).map(String);
2252
+ removeAllTimelineElements(stateRef.current);
2194
2253
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2195
2254
  if (ancestryGroup && ancestryLinks) {
2196
2255
  const remainingAncestryLinks = [];
@@ -2230,6 +2289,7 @@ var userActionHandlers = {
2230
2289
  stateRef.current.selectedNodes.clear();
2231
2290
  return;
2232
2291
  }
2292
+ removeAllTimelineElements(stateRef.current);
2233
2293
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2234
2294
  if (ancestryGroup && ancestryLinks) {
2235
2295
  const remainingAncestryLinks = [];
@@ -4710,6 +4770,11 @@ function calculateNextRecurrenceDate(config) {
4710
4770
  confirmed: true
4711
4771
  };
4712
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
+ };
4713
4778
  var parseDate = (str) => {
4714
4779
  if (!str) return null;
4715
4780
  const parts = str.split("/");
@@ -4718,7 +4783,7 @@ var parseDate = (str) => {
4718
4783
  const month = parseInt(parts[1], 10) - 1;
4719
4784
  const year = parseInt(parts[2], 10);
4720
4785
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
4721
- return new Date(year, month, day);
4786
+ return createSafeDate(year, month, day);
4722
4787
  }
4723
4788
  }
4724
4789
  return null;
@@ -4773,7 +4838,7 @@ var parseDateProp = (dateProp) => {
4773
4838
  } else if (dateProp.year) {
4774
4839
  const y = parseInt(dateProp.year, 10);
4775
4840
  if (!isNaN(y)) {
4776
- const d = new Date(y, 0, 1);
4841
+ const d = createSafeDate(y, 0, 1);
4777
4842
  info = {
4778
4843
  startDate: d,
4779
4844
  endDate: d,
@@ -4786,8 +4851,8 @@ var parseDateProp = (dateProp) => {
4786
4851
  const sY = parseInt(dateProp.date_interval.start, 10);
4787
4852
  const eY = parseInt(dateProp.date_interval.end, 10) || sY;
4788
4853
  if (!isNaN(sY)) {
4789
- const sd = new Date(sY, 0, 1);
4790
- const ed = new Date(eY, 11, 31);
4854
+ const sd = createSafeDate(sY, 0, 1);
4855
+ const ed = createSafeDate(eY, 11, 31);
4791
4856
  info = {
4792
4857
  startDate: sd,
4793
4858
  endDate: ed,
@@ -6625,7 +6690,17 @@ import { v4 as uuidv43 } from "uuid";
6625
6690
 
6626
6691
  // src/components/CustomPropertyDisplay.jsx
6627
6692
  import React3, { useState as useState4, useRef as useRef3, useEffect as useEffect3 } from "react";
6628
- import { FiCheck, FiX, FiEdit3, FiTrash2, FiExternalLink, FiFileText, FiChevronDown, FiUpload, FiLoader } from "react-icons/fi";
6693
+ import {
6694
+ FiCheck,
6695
+ FiX,
6696
+ FiEdit3,
6697
+ FiTrash2,
6698
+ FiExternalLink,
6699
+ FiFileText,
6700
+ FiChevronDown,
6701
+ FiUpload,
6702
+ FiLoader
6703
+ } from "react-icons/fi";
6629
6704
  function CustomPropertyDisplay({
6630
6705
  prop,
6631
6706
  onUpdate,
@@ -6674,7 +6749,9 @@ function CustomPropertyDisplay({
6674
6749
  if (!file || targetIndex === null || !onUploadFile) return;
6675
6750
  const MAX_SIZE = 10 * 1024 * 1024;
6676
6751
  if (file.size > MAX_SIZE) {
6677
- alert("O arquivo \xE9 muito grande. O limite m\xE1ximo de tamanho permitido \xE9 de 10MB.");
6752
+ alert(
6753
+ "Upload de imagens acima de 10MB n\xE3o est\xE1 dispon\xEDvel neste momento. Recomendamos armazen\xE1-la em um servi\xE7o de personal cloud storage e guardar apenas o link no x.plore."
6754
+ );
6678
6755
  return;
6679
6756
  }
6680
6757
  try {
@@ -6768,7 +6845,9 @@ function CustomPropertyDisplay({
6768
6845
  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";
6769
6846
  const renderEditView = () => {
6770
6847
  var _a;
6771
- const isKeyFixed = ["links", "images", "date", "documents"].includes(tempProp.type);
6848
+ const isKeyFixed = ["links", "images", "date", "documents"].includes(
6849
+ tempProp.type
6850
+ );
6772
6851
  const availableOptions = [
6773
6852
  { value: "links", label: "Links", unique: true },
6774
6853
  { value: "images", label: "Images", unique: true },
@@ -6777,7 +6856,9 @@ function CustomPropertyDisplay({
6777
6856
  { value: "text", label: "Texto", unique: false },
6778
6857
  { value: "number", label: "N\xFAmero", unique: false },
6779
6858
  { value: "list", label: "Lista", unique: false }
6780
- ].filter((opt) => !opt.unique || !unavailableTypes.includes(opt.value) || tempProp.type === opt.value);
6859
+ ].filter(
6860
+ (opt) => !opt.unique || !unavailableTypes.includes(opt.value) || tempProp.type === opt.value
6861
+ );
6781
6862
  const currentTypeLabel = ((_a = availableOptions.find((o) => o.value === tempProp.type)) == null ? void 0 : _a.label) || tempProp.type;
6782
6863
  return /* @__PURE__ */ React3.createElement("div", { className: "w-full space-y-2" }, /* @__PURE__ */ React3.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React3.createElement(
6783
6864
  "input",
@@ -6797,7 +6878,12 @@ function CustomPropertyDisplay({
6797
6878
  className: "w-full flex items-center justify-between gap-2 bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-1 focus:ring-indigo-400/60 hover:bg-slate-700/70 transition-colors"
6798
6879
  },
6799
6880
  /* @__PURE__ */ React3.createElement("span", { className: "truncate" }, currentTypeLabel),
6800
- /* @__PURE__ */ React3.createElement(FiChevronDown, { className: `text-slate-400 transition-transform ${isTypeDropdownOpen ? "rotate-180" : ""}` })
6881
+ /* @__PURE__ */ React3.createElement(
6882
+ FiChevronDown,
6883
+ {
6884
+ className: `text-slate-400 transition-transform ${isTypeDropdownOpen ? "rotate-180" : ""}`
6885
+ }
6886
+ )
6801
6887
  ), isTypeDropdownOpen && /* @__PURE__ */ React3.createElement("ul", { className: "absolute right-0 top-full mt-1 z-50 w-32 bg-slate-800 border border-white/10 rounded-lg shadow-xl overflow-y-auto max-h-48 custom-scrollbar py-1" }, availableOptions.map((opt) => /* @__PURE__ */ React3.createElement("li", { key: opt.value }, /* @__PURE__ */ React3.createElement(
6802
6888
  "button",
6803
6889
  {
@@ -6810,7 +6896,16 @@ function CustomPropertyDisplay({
6810
6896
  var _a2, _b, _c, _d, _e;
6811
6897
  switch (tempProp.type) {
6812
6898
  case "number":
6813
- return /* @__PURE__ */ React3.createElement("input", { type: "number", placeholder: "Valor num\xE9rico", value: tempProp.value, onChange: (e) => handlePropChange("value", e.target.value), className: baseInput });
6899
+ return /* @__PURE__ */ React3.createElement(
6900
+ "input",
6901
+ {
6902
+ type: "number",
6903
+ placeholder: "Valor num\xE9rico",
6904
+ value: tempProp.value,
6905
+ onChange: (e) => handlePropChange("value", e.target.value),
6906
+ className: baseInput
6907
+ }
6908
+ );
6814
6909
  case "date": {
6815
6910
  const dateType = ((_a2 = tempProp.value) == null ? void 0 : _a2.type) || "Data";
6816
6911
  const noSpinnerClass = "[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none";
@@ -6827,15 +6922,20 @@ function CustomPropertyDisplay({
6827
6922
  handlePropChange("value", newValue);
6828
6923
  };
6829
6924
  const handleDateValueChange = (dateField, dateValue) => {
6830
- handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
6925
+ handlePropChange("value", {
6926
+ ...tempProp.value,
6927
+ [dateField]: dateValue
6928
+ });
6831
6929
  };
6832
6930
  const handleFullDateChange = (e) => {
6833
6931
  const rawValue = e.target.value;
6834
6932
  const digits = rawValue.replace(/\D/g, "");
6835
6933
  const len = digits.length;
6836
6934
  let formattedValue = digits;
6837
- if (len > 2 && len <= 4) formattedValue = `${digits.slice(0, 2)}/${digits.slice(2)}`;
6838
- else if (len > 4) formattedValue = `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4, 8)}`;
6935
+ if (len > 2 && len <= 4)
6936
+ formattedValue = `${digits.slice(0, 2)}/${digits.slice(2)}`;
6937
+ else if (len > 4)
6938
+ formattedValue = `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4, 8)}`;
6839
6939
  handleDateValueChange("value", formattedValue);
6840
6940
  };
6841
6941
  return /* @__PURE__ */ React3.createElement("div", { className: "space-y-2 p-2 bg-slate-900/60 rounded-lg border border-white/10" }, /* @__PURE__ */ React3.createElement("div", { className: "flex items-center justify-center gap-1 rounded-lg bg-slate-800/50 p-1" }, ["Data", "Ano", "Date Interval"].map((t) => /* @__PURE__ */ React3.createElement(
@@ -6847,7 +6947,17 @@ function CustomPropertyDisplay({
6847
6947
  className: `flex-1 text-xs px-2 py-1.5 rounded-md transition-colors ${dateType === t ? "bg-indigo-600 text-white font-semibold shadow" : "hover:bg-slate-700/50"}`
6848
6948
  },
6849
6949
  t === "Date Interval" ? "Intervalo" : t
6850
- ))), dateType === "Data" && /* @__PURE__ */ React3.createElement("input", { type: "text", placeholder: "DD/MM/AAAA", value: ((_b = tempProp.value) == null ? void 0 : _b.value) || "", onChange: handleFullDateChange, maxLength: "10", className: baseInput }), dateType === "Ano" && /* @__PURE__ */ React3.createElement(
6950
+ ))), dateType === "Data" && /* @__PURE__ */ React3.createElement(
6951
+ "input",
6952
+ {
6953
+ type: "text",
6954
+ placeholder: "DD/MM/AAAA",
6955
+ value: ((_b = tempProp.value) == null ? void 0 : _b.value) || "",
6956
+ onChange: handleFullDateChange,
6957
+ maxLength: "10",
6958
+ className: baseInput
6959
+ }
6960
+ ), dateType === "Ano" && /* @__PURE__ */ React3.createElement(
6851
6961
  "input",
6852
6962
  {
6853
6963
  type: "number",
@@ -6895,7 +7005,11 @@ function CustomPropertyDisplay({
6895
7005
  type: "text",
6896
7006
  placeholder: tempProp.type === "images" || tempProp.type === "documents" ? "ID" : "Nome",
6897
7007
  value: item.name,
6898
- onChange: (e) => handleListItemChange(itemIndex, "name", e.target.value),
7008
+ onChange: (e) => handleListItemChange(
7009
+ itemIndex,
7010
+ "name",
7011
+ e.target.value
7012
+ ),
6899
7013
  className: "min-w-0 flex-1 bg-slate-800/70 p-2 text-xs rounded-md border border-white/10 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-indigo-400/60"
6900
7014
  }
6901
7015
  ), /* @__PURE__ */ React3.createElement(
@@ -6904,7 +7018,11 @@ function CustomPropertyDisplay({
6904
7018
  type: "text",
6905
7019
  placeholder: tempProp.type === "images" ? "URL da Imagem" : tempProp.type === "documents" ? "URL do Documento" : "Valor",
6906
7020
  value: item.value,
6907
- onChange: (e) => handleListItemChange(itemIndex, "value", e.target.value),
7021
+ onChange: (e) => handleListItemChange(
7022
+ itemIndex,
7023
+ "value",
7024
+ e.target.value
7025
+ ),
6908
7026
  className: "min-w-0 flex-1 bg-slate-800/70 p-2 text-xs rounded-md border border-white/10 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-indigo-400/60"
6909
7027
  }
6910
7028
  ), (tempProp.type === "images" || tempProp.type === "documents") && /* @__PURE__ */ React3.createElement(
@@ -6917,11 +7035,53 @@ function CustomPropertyDisplay({
6917
7035
  title: "Upload de Arquivo"
6918
7036
  },
6919
7037
  uploadingItemIndex === itemIndex ? /* @__PURE__ */ React3.createElement(FiLoader, { className: "animate-spin" }) : /* @__PURE__ */ React3.createElement(FiUpload, { size: 12 })
6920
- ), /* @__PURE__ */ React3.createElement("button", { type: "button", onClick: () => handleRemoveListItem(itemIndex), className: "flex-shrink-0 w-7 h-7 grid place-items-center bg-red-600/60 hover:bg-red-600 rounded-full text-xs font-bold transition-colors", "aria-label": "Remover item" }, "\xD7"))), /* @__PURE__ */ React3.createElement("button", { type: "button", onClick: () => handleAddListItem(), className: "mt-1 w-full px-2 py-1.5 bg-slate-700/70 hover:bg-slate-600 rounded-md transition-colors text-xs" }, "Adicionar Item"));
7038
+ ), /* @__PURE__ */ React3.createElement(
7039
+ "button",
7040
+ {
7041
+ type: "button",
7042
+ onClick: () => handleRemoveListItem(itemIndex),
7043
+ className: "flex-shrink-0 w-7 h-7 grid place-items-center bg-red-600/60 hover:bg-red-600 rounded-full text-xs font-bold transition-colors",
7044
+ "aria-label": "Remover item"
7045
+ },
7046
+ "\xD7"
7047
+ ))), /* @__PURE__ */ React3.createElement(
7048
+ "button",
7049
+ {
7050
+ type: "button",
7051
+ onClick: () => handleAddListItem(),
7052
+ className: "mt-1 w-full px-2 py-1.5 bg-slate-700/70 hover:bg-slate-600 rounded-md transition-colors text-xs"
7053
+ },
7054
+ "Adicionar Item"
7055
+ ));
6921
7056
  default:
6922
- return /* @__PURE__ */ React3.createElement("input", { type: "text", placeholder: "Valor", value: tempProp.value, onChange: (e) => handlePropChange("value", e.target.value), className: baseInput });
7057
+ return /* @__PURE__ */ React3.createElement(
7058
+ "input",
7059
+ {
7060
+ type: "text",
7061
+ placeholder: "Valor",
7062
+ value: tempProp.value,
7063
+ onChange: (e) => handlePropChange("value", e.target.value),
7064
+ className: baseInput
7065
+ }
7066
+ );
6923
7067
  }
6924
- })()), /* @__PURE__ */ React3.createElement("div", { className: "flex justify-end items-center gap-2 pt-1" }, /* @__PURE__ */ React3.createElement("button", { onClick: handleCancel, type: "button", className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors" }, /* @__PURE__ */ React3.createElement(FiX, { className: "text-red-400" })), /* @__PURE__ */ React3.createElement("button", { onClick: handleSave, type: "button", className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors" }, /* @__PURE__ */ React3.createElement(FiCheck, { className: "text-green-400" }))));
7068
+ })()), /* @__PURE__ */ React3.createElement("div", { className: "flex justify-end items-center gap-2 pt-1" }, /* @__PURE__ */ React3.createElement(
7069
+ "button",
7070
+ {
7071
+ onClick: handleCancel,
7072
+ type: "button",
7073
+ className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors"
7074
+ },
7075
+ /* @__PURE__ */ React3.createElement(FiX, { className: "text-red-400" })
7076
+ ), /* @__PURE__ */ React3.createElement(
7077
+ "button",
7078
+ {
7079
+ onClick: handleSave,
7080
+ type: "button",
7081
+ className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors"
7082
+ },
7083
+ /* @__PURE__ */ React3.createElement(FiCheck, { className: "text-green-400" })
7084
+ )));
6925
7085
  };
6926
7086
  const renderDisplayView = () => /* @__PURE__ */ React3.createElement("div", { className: "w-full space-y-2 text-sm" }, /* @__PURE__ */ React3.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React3.createElement("p", { className: "font-semibold text-slate-200" }, prop.key), /* @__PURE__ */ React3.createElement("span", { className: "text-xs capitalize bg-slate-700/50 px-2 py-0.5 rounded text-slate-400" }, prop.type)), /* @__PURE__ */ React3.createElement("div", { className: "text-slate-300 pl-1" }, (() => {
6927
7087
  switch (prop.type) {
@@ -6949,7 +7109,10 @@ function CustomPropertyDisplay({
6949
7109
  if (isTextureMode) {
6950
7110
  onSelectTexture(item.value);
6951
7111
  } else {
6952
- onOpenImageViewer == null ? void 0 : onOpenImageViewer(prop.value.map((img) => ({ ...img })), index);
7112
+ onOpenImageViewer == null ? void 0 : onOpenImageViewer(
7113
+ prop.value.map((img) => ({ ...img })),
7114
+ index
7115
+ );
6953
7116
  }
6954
7117
  },
6955
7118
  className: `w-16 h-16 rounded-md bg-cover bg-center bg-slate-700 outline-none transition-all shadow-md ${isSelected ? "ring-4 ring-green-500 scale-105 z-10" : isTextureMode ? "hover:ring-4 hover:ring-indigo-400/50 cursor-pointer" : "hover:ring-2 hover:ring-indigo-400 cursor-zoom-in"}`,
@@ -6959,7 +7122,17 @@ function CustomPropertyDisplay({
6959
7122
  ), isSelected && /* @__PURE__ */ React3.createElement("div", { className: "absolute -top-2 -right-2 bg-green-500 text-white rounded-full p-0.5 shadow-sm" }, /* @__PURE__ */ React3.createElement(FiCheck, { size: 12 })));
6960
7123
  }));
6961
7124
  case "links":
6962
- return /* @__PURE__ */ React3.createElement("ul", { className: "space-y-1.5 max-h-40 overflow-y-auto custom-scrollbar" }, Array.isArray(prop.value) && prop.value.map((item) => /* @__PURE__ */ React3.createElement("li", { key: item.id }, /* @__PURE__ */ React3.createElement("a", { href: item.value, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-2 text-indigo-400 hover:text-indigo-300 hover:underline transition-colors" }, item.name, /* @__PURE__ */ React3.createElement(FiExternalLink, { className: "opacity-60", size: 12 })))));
7125
+ return /* @__PURE__ */ React3.createElement("ul", { className: "space-y-1.5 max-h-40 overflow-y-auto custom-scrollbar" }, Array.isArray(prop.value) && prop.value.map((item) => /* @__PURE__ */ React3.createElement("li", { key: item.id }, /* @__PURE__ */ React3.createElement(
7126
+ "a",
7127
+ {
7128
+ href: item.value,
7129
+ target: "_blank",
7130
+ rel: "noopener noreferrer",
7131
+ className: "inline-flex items-center gap-2 text-indigo-400 hover:text-indigo-300 hover:underline transition-colors"
7132
+ },
7133
+ item.name,
7134
+ /* @__PURE__ */ React3.createElement(FiExternalLink, { className: "opacity-60", size: 12 })
7135
+ ))));
6963
7136
  case "documents":
6964
7137
  return /* @__PURE__ */ React3.createElement("div", { className: "space-y-2 mt-1" }, Array.isArray(prop.value) && prop.value.map((item) => /* @__PURE__ */ React3.createElement(
6965
7138
  "a",
@@ -6987,7 +7160,30 @@ function CustomPropertyDisplay({
6987
7160
  onMouseLeave: () => setIsHovered(false)
6988
7161
  },
6989
7162
  isEditing ? renderEditView() : renderDisplayView(),
6990
- !isEditing && !readOnly && /* @__PURE__ */ React3.createElement("div", { className: `absolute top-2 right-2 flex items-center gap-1 transition-opacity ${isHovered ? "opacity-100" : "opacity-0"}` }, /* @__PURE__ */ React3.createElement("button", { onClick: () => setIsEditing(true), className: "w-7 h-7 grid place-items-center rounded-lg bg-slate-700/80 hover:bg-slate-600/80 text-slate-200 transition-colors", title: "Editar" }, /* @__PURE__ */ React3.createElement(FiEdit3, { size: 14 })), /* @__PURE__ */ React3.createElement("button", { onClick: onRemove, className: "w-7 h-7 grid place-items-center rounded-lg bg-red-700/80 hover:bg-red-600/80 text-slate-200 transition-colors", title: "Remover" }, /* @__PURE__ */ React3.createElement(FiTrash2, { size: 14 })))
7163
+ !isEditing && !readOnly && /* @__PURE__ */ React3.createElement(
7164
+ "div",
7165
+ {
7166
+ className: `absolute top-2 right-2 flex items-center gap-1 transition-opacity ${isHovered ? "opacity-100" : "opacity-0"}`
7167
+ },
7168
+ /* @__PURE__ */ React3.createElement(
7169
+ "button",
7170
+ {
7171
+ onClick: () => setIsEditing(true),
7172
+ className: "w-7 h-7 grid place-items-center rounded-lg bg-slate-700/80 hover:bg-slate-600/80 text-slate-200 transition-colors",
7173
+ title: "Editar"
7174
+ },
7175
+ /* @__PURE__ */ React3.createElement(FiEdit3, { size: 14 })
7176
+ ),
7177
+ /* @__PURE__ */ React3.createElement(
7178
+ "button",
7179
+ {
7180
+ onClick: onRemove,
7181
+ className: "w-7 h-7 grid place-items-center rounded-lg bg-red-700/80 hover:bg-red-600/80 text-slate-200 transition-colors",
7182
+ title: "Remover"
7183
+ },
7184
+ /* @__PURE__ */ React3.createElement(FiTrash2, { size: 14 })
7185
+ )
7186
+ )
6991
7187
  );
6992
7188
  }
6993
7189
 
@@ -19447,7 +19643,62 @@ var TIMELINE_LAYER_SPACING_Y = 25;
19447
19643
  var TIMELINE_NODE_SPACING_X = 20;
19448
19644
  var TIMELINE_INTERVAL_BAR_RADIUS = 0.2;
19449
19645
  var TIMELINE_INTERVAL_BAR_OPACITY = 0.85;
19450
- var TIMELINE_GOLD_COLOR = 16766720;
19646
+ var TIMELINE_BLUE_COLOR = 30719;
19647
+ var TIMELINE_BRACKET_WIDTH = 4;
19648
+ var createTimelineIntervalBracket = () => {
19649
+ const group = new THREE3.Group();
19650
+ group.name = "timelineIntervalBracket";
19651
+ const barMaterial = new THREE3.MeshStandardMaterial({
19652
+ color: TIMELINE_BLUE_COLOR,
19653
+ emissive: 17578,
19654
+ emissiveIntensity: 0.5,
19655
+ transparent: true,
19656
+ opacity: TIMELINE_INTERVAL_BAR_OPACITY,
19657
+ roughness: 0.3,
19658
+ metalness: 0.8
19659
+ });
19660
+ const geoHorizontal = new THREE3.CylinderGeometry(
19661
+ TIMELINE_INTERVAL_BAR_RADIUS,
19662
+ TIMELINE_INTERVAL_BAR_RADIUS,
19663
+ 1,
19664
+ 16
19665
+ );
19666
+ const geoVertical = new THREE3.CylinderGeometry(
19667
+ TIMELINE_INTERVAL_BAR_RADIUS,
19668
+ TIMELINE_INTERVAL_BAR_RADIUS,
19669
+ 1,
19670
+ 16
19671
+ );
19672
+ const bottomArm = new THREE3.Mesh(geoHorizontal, barMaterial);
19673
+ bottomArm.rotation.z = Math.PI / 2;
19674
+ const verticalStem = new THREE3.Mesh(geoVertical, barMaterial);
19675
+ const topArm = new THREE3.Mesh(geoHorizontal.clone(), barMaterial);
19676
+ topArm.rotation.z = Math.PI / 2;
19677
+ group.add(bottomArm);
19678
+ group.add(verticalStem);
19679
+ group.add(topArm);
19680
+ group.userData.bottomArm = bottomArm;
19681
+ group.userData.verticalStem = verticalStem;
19682
+ group.userData.topArm = topArm;
19683
+ return group;
19684
+ };
19685
+ var updateTimelineIntervalBracket = (group, startPos, endPos) => {
19686
+ if (!group || !group.userData.bottomArm) return;
19687
+ const { bottomArm, verticalStem, topArm } = group.userData;
19688
+ const w = TIMELINE_BRACKET_WIDTH;
19689
+ const z = -0.5;
19690
+ const p1 = new THREE3.Vector3(startPos.x, startPos.y, z);
19691
+ const p2 = new THREE3.Vector3(startPos.x - w, startPos.y, z);
19692
+ const p3 = new THREE3.Vector3(endPos.x - w, endPos.y, z);
19693
+ const p4 = new THREE3.Vector3(endPos.x, endPos.y, z);
19694
+ bottomArm.scale.y = w;
19695
+ bottomArm.position.set((p1.x + p2.x) / 2, p1.y, z);
19696
+ const verticalLen = Math.abs(p3.y - p2.y);
19697
+ verticalStem.scale.y = Math.max(verticalLen, 1e-3);
19698
+ verticalStem.position.set(p2.x, (p2.y + p3.y) / 2, z);
19699
+ topArm.scale.y = w;
19700
+ topArm.position.set((p3.x + p4.x) / 2, p3.y, z);
19701
+ };
19451
19702
  var QUEST_LENS_COLUMN_SPACING = 80;
19452
19703
  var QUEST_LENS_ROW_SPACING = 40;
19453
19704
  var getAllNodeIdsFromTree = (treeNode) => {
@@ -20136,7 +20387,9 @@ function XViewScene({
20136
20387
  const month = parseInt(parts[1], 10) - 1;
20137
20388
  const year = parseInt(parts[2], 10);
20138
20389
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
20139
- return new Date(year, month, day);
20390
+ const d = /* @__PURE__ */ new Date();
20391
+ d.setFullYear(year, month, day);
20392
+ return d;
20140
20393
  }
20141
20394
  }
20142
20395
  return null;
@@ -20193,10 +20446,12 @@ function XViewScene({
20193
20446
  });
20194
20447
  const timelineWidth = (columns.length > 0 ? columns.length - 1 : 0) * TIMELINE_NODE_SPACING_X;
20195
20448
  const offsetX = -timelineWidth / 2;
20449
+ const allFinalPoints = [];
20196
20450
  timelineNodes.forEach((node) => {
20197
20451
  const { mesh, type, startDate, endDate, label, columnIndex, isUndated } = node;
20198
20452
  const x = columnIndex * TIMELINE_NODE_SPACING_X + offsetX;
20199
20453
  const y = isUndated ? 0 : timeToYMap.get(startDate.getTime());
20454
+ allFinalPoints.push(new THREE3.Vector3(x, y, 0));
20200
20455
  const timelineTween = new Tween2(mesh.position).to({ x, y, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
20201
20456
  tweenGroup.add(timelineTween);
20202
20457
  timelineTween.start();
@@ -20209,25 +20464,16 @@ function XViewScene({
20209
20464
  if (type === "interval") {
20210
20465
  const endY = timeToYMap.get(endDate.getTime());
20211
20466
  if (endY > y) {
20212
- const barGeometry = new THREE3.CylinderGeometry(
20213
- TIMELINE_INTERVAL_BAR_RADIUS,
20214
- TIMELINE_INTERVAL_BAR_RADIUS,
20215
- 1,
20216
- 16
20217
- );
20218
- const barMaterial = new THREE3.MeshStandardMaterial({
20219
- color: TIMELINE_GOLD_COLOR,
20220
- emissive: 12092939,
20221
- emissiveIntensity: 0.4,
20222
- transparent: true,
20223
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20224
- roughness: 0.3,
20225
- metalness: 0.8
20226
- });
20227
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20467
+ allFinalPoints.push(new THREE3.Vector3(x, endY, 0));
20468
+ const intervalBar = createTimelineIntervalBracket();
20228
20469
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20229
20470
  const endPosition = new THREE3.Vector3(x, endY, -0.5);
20230
20471
  intervalBar.userData.endPosition = endPosition;
20472
+ updateTimelineIntervalBracket(
20473
+ intervalBar,
20474
+ new THREE3.Vector3(x, y, 0),
20475
+ endPosition
20476
+ );
20231
20477
  timelineIntervalsGroup.add(intervalBar);
20232
20478
  const endLabelText = endDate.getFullYear().toString();
20233
20479
  const endLabel = createTextSprite(endLabelText, 48);
@@ -20240,7 +20486,21 @@ function XViewScene({
20240
20486
  }
20241
20487
  }
20242
20488
  });
20243
- }, []);
20489
+ if (allFinalPoints.length > 0) {
20490
+ const boundingBox = new THREE3.Box3().setFromPoints(allFinalPoints);
20491
+ const center = new THREE3.Vector3();
20492
+ boundingBox.getCenter(center);
20493
+ const size = new THREE3.Vector3();
20494
+ boundingBox.getSize(size);
20495
+ const spread = Math.max(size.x, size.y, size.z, 1);
20496
+ const desiredDistance = Math.max(
20497
+ x_view_config.CAMERA_ZOOM_DISTANCE,
20498
+ spread * 0.75
20499
+ );
20500
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
20501
+ tweenToTarget(center, zoomFactor);
20502
+ }
20503
+ }, [tweenToTarget]);
20244
20504
  const handleVersionTimeline = useCallback8(
20245
20505
  (sourceMesh, versionMeshes) => {
20246
20506
  const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
@@ -20276,7 +20536,9 @@ function XViewScene({
20276
20536
  const month = parseInt(parts[1], 10) - 1;
20277
20537
  const year = parseInt(parts[2], 10);
20278
20538
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
20279
- return new Date(year, month, day);
20539
+ const d = /* @__PURE__ */ new Date();
20540
+ d.setFullYear(year, month, day);
20541
+ return d;
20280
20542
  }
20281
20543
  }
20282
20544
  return null;
@@ -20347,22 +20609,7 @@ function XViewScene({
20347
20609
  if (type === "interval") {
20348
20610
  const relativeEndY = timeToYMap.get(endDate.getTime()) - timeToYMap.get(startDate.getTime());
20349
20611
  if (relativeEndY > 0) {
20350
- const barGeometry = new THREE3.CylinderGeometry(
20351
- TIMELINE_INTERVAL_BAR_RADIUS,
20352
- TIMELINE_INTERVAL_BAR_RADIUS,
20353
- 1,
20354
- 16
20355
- );
20356
- const barMaterial = new THREE3.MeshStandardMaterial({
20357
- color: TIMELINE_GOLD_COLOR,
20358
- emissive: 12092939,
20359
- emissiveIntensity: 0.4,
20360
- transparent: true,
20361
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20362
- roughness: 0.3,
20363
- metalness: 0.8
20364
- });
20365
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20612
+ const intervalBar = createTimelineIntervalBracket();
20366
20613
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20367
20614
  const endPosition = new THREE3.Vector3(
20368
20615
  baseX,
@@ -20370,6 +20617,11 @@ function XViewScene({
20370
20617
  -0.5
20371
20618
  );
20372
20619
  intervalBar.userData.endPosition = endPosition;
20620
+ updateTimelineIntervalBracket(
20621
+ intervalBar,
20622
+ new THREE3.Vector3(x, y, 0),
20623
+ endPosition
20624
+ );
20373
20625
  timelineIntervalsGroup.add(intervalBar);
20374
20626
  const endLabelText = endDate.getFullYear().toString();
20375
20627
  const endLabel = createTextSprite(endLabelText, 48);
@@ -20865,25 +21117,15 @@ function XViewScene({
20865
21117
  const endY = timeToYMap.get(timeInfo.endDate.getTime());
20866
21118
  if (Math.abs(endY - targetY) > 0.1) {
20867
21119
  const barHeight = Math.abs(endY - targetY);
20868
- const barGeometry = new THREE3.CylinderGeometry(
20869
- TIMELINE_INTERVAL_BAR_RADIUS,
20870
- TIMELINE_INTERVAL_BAR_RADIUS,
20871
- 1,
20872
- 16
20873
- );
20874
- const barMaterial = new THREE3.MeshStandardMaterial({
20875
- color: TIMELINE_GOLD_COLOR,
20876
- emissive: 12092939,
20877
- emissiveIntensity: 0.4,
20878
- transparent: true,
20879
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20880
- roughness: 0.3,
20881
- metalness: 0.8
20882
- });
20883
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
21120
+ const intervalBar = createTimelineIntervalBracket();
20884
21121
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20885
- intervalBar.position.set(finalX, (targetY + endY) / 2, -0.5);
20886
- intervalBar.scale.y = barHeight;
21122
+ const endPosition = new THREE3.Vector3(finalX, endY, -0.5);
21123
+ intervalBar.userData.endPosition = endPosition;
21124
+ updateTimelineIntervalBracket(
21125
+ intervalBar,
21126
+ new THREE3.Vector3(finalX, targetY, 0),
21127
+ endPosition
21128
+ );
20887
21129
  graphGroup.add(intervalBar);
20888
21130
  mesh.userData.timelineIntervalBar = intervalBar;
20889
21131
  const endLabel = createTextSprite(
@@ -21652,23 +21894,12 @@ function XViewScene({
21652
21894
  }
21653
21895
  });
21654
21896
  (_a2 = stateRef.current.timelineIntervalsGroup) == null ? void 0 : _a2.children.forEach((obj) => {
21655
- if (obj.isMesh && obj.userData.associatedNodeId && obj.userData.endPosition) {
21897
+ if (obj.userData.associatedNodeId && obj.userData.endPosition) {
21656
21898
  const bar = obj;
21657
21899
  const node = stateRef.current.nodeObjects[String(bar.userData.associatedNodeId)];
21658
21900
  const endPos = bar.userData.endPosition;
21659
21901
  if (node) {
21660
- const startPos = node.position;
21661
- const distance = startPos.distanceTo(endPos);
21662
- bar.scale.y = distance;
21663
- const midpoint = new THREE3.Vector3().lerpVectors(
21664
- startPos,
21665
- endPos,
21666
- 0.5
21667
- );
21668
- bar.position.copy(midpoint);
21669
- const direction = new THREE3.Vector3().subVectors(endPos, startPos).normalize();
21670
- const upVector = new THREE3.Vector3(0, 1, 0);
21671
- bar.quaternion.setFromUnitVectors(upVector, direction);
21902
+ updateTimelineIntervalBracket(bar, node.position, endPos);
21672
21903
  }
21673
21904
  }
21674
21905
  });
@@ -24650,8 +24881,9 @@ function XViewScene({
24650
24881
  )) == null ? void 0 : _b.parentFileId : null;
24651
24882
  const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
24652
24883
  useEffect27(() => {
24653
- if (isInitialized && focusNodeId && !hasFocusedInitial) {
24654
- const nodeObjects = stateRef.current.nodeObjects || {};
24884
+ if (!isInitialized || hasFocusedInitial) return;
24885
+ const nodeObjects = stateRef.current.nodeObjects || {};
24886
+ if (focusNodeId) {
24655
24887
  const targetMesh = nodeObjects[String(focusNodeId)];
24656
24888
  if (targetMesh) {
24657
24889
  setTimeout(() => {
@@ -24664,11 +24896,34 @@ function XViewScene({
24664
24896
  "O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
24665
24897
  );
24666
24898
  }
24899
+ } else if (!focusAncestryId) {
24900
+ const meshes = Object.values(nodeObjects);
24901
+ if (meshes.length > 0) {
24902
+ setTimeout(() => {
24903
+ const nodePositions = meshes.map((mesh) => mesh.position.clone());
24904
+ const boundingBox = new THREE3.Box3().setFromPoints(nodePositions);
24905
+ const center = new THREE3.Vector3();
24906
+ boundingBox.getCenter(center);
24907
+ const size = new THREE3.Vector3();
24908
+ boundingBox.getSize(size);
24909
+ const maxDim = Math.max(size.x, size.y, size.z);
24910
+ const desiredDistance = Math.max(
24911
+ x_view_config.CAMERA_ZOOM_DISTANCE,
24912
+ maxDim * 1.8
24913
+ );
24914
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
24915
+ tweenToTarget(center, zoomFactor);
24916
+ setHasFocusedInitial(true);
24917
+ }, 300);
24918
+ } else {
24919
+ setHasFocusedInitial(true);
24920
+ }
24667
24921
  }
24668
24922
  }, [
24669
24923
  isInitialized,
24670
24924
  sceneVersion,
24671
24925
  focusNodeId,
24926
+ focusAncestryId,
24672
24927
  hasFocusedInitial,
24673
24928
  tweenToTarget
24674
24929
  ]);