@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.js CHANGED
@@ -836,6 +836,55 @@ var removeLinkFromScene = (state, linkObject) => {
836
836
  else linkObject.material.dispose();
837
837
  }
838
838
  };
839
+ var removeAllTimelineElements = (state) => {
840
+ if (!state) return;
841
+ const { timelineIntervalsGroup, nodeObjects } = state;
842
+ if (timelineIntervalsGroup) {
843
+ while (timelineIntervalsGroup.children.length > 0) {
844
+ const obj = timelineIntervalsGroup.children[0];
845
+ timelineIntervalsGroup.remove(obj);
846
+ if (obj.traverse) {
847
+ obj.traverse((child) => {
848
+ if (child.geometry) child.geometry.dispose();
849
+ if (child.material) {
850
+ if (child.material.map) child.material.map.dispose();
851
+ child.material.dispose();
852
+ }
853
+ });
854
+ } else {
855
+ if (obj.geometry) obj.geometry.dispose();
856
+ if (obj.material) {
857
+ if (obj.material.map) obj.material.map.dispose();
858
+ obj.material.dispose();
859
+ }
860
+ }
861
+ }
862
+ }
863
+ if (nodeObjects) {
864
+ Object.values(nodeObjects).forEach((mesh) => {
865
+ var _a, _b, _c, _d;
866
+ if (!mesh) return;
867
+ const oldTimelineLabel = mesh.getObjectByName("timelineLabel");
868
+ if (oldTimelineLabel) {
869
+ mesh.remove(oldTimelineLabel);
870
+ if ((_a = oldTimelineLabel.material) == null ? void 0 : _a.map) {
871
+ oldTimelineLabel.material.map.dispose();
872
+ }
873
+ (_b = oldTimelineLabel.material) == null ? void 0 : _b.dispose();
874
+ }
875
+ const oldQuestDateLabel = mesh.getObjectByName("questDateLabel");
876
+ if (oldQuestDateLabel) {
877
+ mesh.remove(oldQuestDateLabel);
878
+ if ((_c = oldQuestDateLabel.material) == null ? void 0 : _c.map) {
879
+ oldQuestDateLabel.material.map.dispose();
880
+ }
881
+ (_d = oldQuestDateLabel.material) == null ? void 0 : _d.dispose();
882
+ }
883
+ delete mesh.userData.timelineIntervalBar;
884
+ delete mesh.userData.timelineEndLabel;
885
+ });
886
+ }
887
+ };
839
888
  var removeNodeFromScene = (state, nodeId) => {
840
889
  const {
841
890
  graphGroup,
@@ -864,8 +913,15 @@ var removeNodeFromScene = (state, nodeId) => {
864
913
  const { timelineIntervalBar, timelineEndLabel } = nodeToRemove.userData;
865
914
  if (timelineIntervalBar) {
866
915
  timelineIntervalsGroup.remove(timelineIntervalBar);
867
- if (timelineIntervalBar.geometry) timelineIntervalBar.geometry.dispose();
868
- if (timelineIntervalBar.material) timelineIntervalBar.material.dispose();
916
+ if (timelineIntervalBar.traverse) {
917
+ timelineIntervalBar.traverse((child) => {
918
+ if (child.geometry) child.geometry.dispose();
919
+ if (child.material) child.material.dispose();
920
+ });
921
+ } else {
922
+ if (timelineIntervalBar.geometry) timelineIntervalBar.geometry.dispose();
923
+ if (timelineIntervalBar.material) timelineIntervalBar.material.dispose();
924
+ }
869
925
  }
870
926
  if (timelineEndLabel) {
871
927
  timelineIntervalsGroup.remove(timelineEndLabel);
@@ -2177,6 +2233,7 @@ var userActionHandlers = {
2177
2233
  if (!nodeData || !sceneDataRef.current) return;
2178
2234
  const nodeIdToDismiss = nodeData.id;
2179
2235
  const strNodeId = String(nodeIdToDismiss);
2236
+ removeAllTimelineElements(stateRef.current);
2180
2237
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2181
2238
  if (ancestryGroup && ancestryLinks) {
2182
2239
  const remainingAncestryLinks = [];
@@ -2208,6 +2265,7 @@ var userActionHandlers = {
2208
2265
  (id) => id !== strNodeIdToKeep
2209
2266
  );
2210
2267
  if (nodeIdsToDismiss.length === 0) return;
2268
+ removeAllTimelineElements(stateRef.current);
2211
2269
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2212
2270
  if (ancestryGroup && ancestryLinks) {
2213
2271
  ancestryLinks.forEach((link) => {
@@ -2230,6 +2288,7 @@ var userActionHandlers = {
2230
2288
  setters.setMultiContextMenu({ visible: false });
2231
2289
  if (!nodeIds || nodeIds.size === 0 || !sceneDataRef.current) return;
2232
2290
  const strNodeIds = Array.from(nodeIds).map(String);
2291
+ removeAllTimelineElements(stateRef.current);
2233
2292
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2234
2293
  if (ancestryGroup && ancestryLinks) {
2235
2294
  const remainingAncestryLinks = [];
@@ -2269,6 +2328,7 @@ var userActionHandlers = {
2269
2328
  stateRef.current.selectedNodes.clear();
2270
2329
  return;
2271
2330
  }
2331
+ removeAllTimelineElements(stateRef.current);
2272
2332
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2273
2333
  if (ancestryGroup && ancestryLinks) {
2274
2334
  const remainingAncestryLinks = [];
@@ -4749,6 +4809,11 @@ function calculateNextRecurrenceDate(config) {
4749
4809
  confirmed: true
4750
4810
  };
4751
4811
  }
4812
+ var createSafeDate = (year, month = 0, day = 1) => {
4813
+ const d = /* @__PURE__ */ new Date();
4814
+ d.setFullYear(year, month, day);
4815
+ return d;
4816
+ };
4752
4817
  var parseDate = (str) => {
4753
4818
  if (!str) return null;
4754
4819
  const parts = str.split("/");
@@ -4757,7 +4822,7 @@ var parseDate = (str) => {
4757
4822
  const month = parseInt(parts[1], 10) - 1;
4758
4823
  const year = parseInt(parts[2], 10);
4759
4824
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
4760
- return new Date(year, month, day);
4825
+ return createSafeDate(year, month, day);
4761
4826
  }
4762
4827
  }
4763
4828
  return null;
@@ -4812,7 +4877,7 @@ var parseDateProp = (dateProp) => {
4812
4877
  } else if (dateProp.year) {
4813
4878
  const y = parseInt(dateProp.year, 10);
4814
4879
  if (!isNaN(y)) {
4815
- const d = new Date(y, 0, 1);
4880
+ const d = createSafeDate(y, 0, 1);
4816
4881
  info = {
4817
4882
  startDate: d,
4818
4883
  endDate: d,
@@ -4825,8 +4890,8 @@ var parseDateProp = (dateProp) => {
4825
4890
  const sY = parseInt(dateProp.date_interval.start, 10);
4826
4891
  const eY = parseInt(dateProp.date_interval.end, 10) || sY;
4827
4892
  if (!isNaN(sY)) {
4828
- const sd = new Date(sY, 0, 1);
4829
- const ed = new Date(eY, 11, 31);
4893
+ const sd = createSafeDate(sY, 0, 1);
4894
+ const ed = createSafeDate(eY, 11, 31);
4830
4895
  info = {
4831
4896
  startDate: sd,
4832
4897
  endDate: ed,
@@ -6713,7 +6778,9 @@ function CustomPropertyDisplay({
6713
6778
  if (!file || targetIndex === null || !onUploadFile) return;
6714
6779
  const MAX_SIZE = 10 * 1024 * 1024;
6715
6780
  if (file.size > MAX_SIZE) {
6716
- alert("O arquivo \xE9 muito grande. O limite m\xE1ximo de tamanho permitido \xE9 de 10MB.");
6781
+ alert(
6782
+ "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."
6783
+ );
6717
6784
  return;
6718
6785
  }
6719
6786
  try {
@@ -6807,7 +6874,9 @@ function CustomPropertyDisplay({
6807
6874
  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";
6808
6875
  const renderEditView = () => {
6809
6876
  var _a;
6810
- const isKeyFixed = ["links", "images", "date", "documents"].includes(tempProp.type);
6877
+ const isKeyFixed = ["links", "images", "date", "documents"].includes(
6878
+ tempProp.type
6879
+ );
6811
6880
  const availableOptions = [
6812
6881
  { value: "links", label: "Links", unique: true },
6813
6882
  { value: "images", label: "Images", unique: true },
@@ -6816,7 +6885,9 @@ function CustomPropertyDisplay({
6816
6885
  { value: "text", label: "Texto", unique: false },
6817
6886
  { value: "number", label: "N\xFAmero", unique: false },
6818
6887
  { value: "list", label: "Lista", unique: false }
6819
- ].filter((opt) => !opt.unique || !unavailableTypes.includes(opt.value) || tempProp.type === opt.value);
6888
+ ].filter(
6889
+ (opt) => !opt.unique || !unavailableTypes.includes(opt.value) || tempProp.type === opt.value
6890
+ );
6820
6891
  const currentTypeLabel = ((_a = availableOptions.find((o) => o.value === tempProp.type)) == null ? void 0 : _a.label) || tempProp.type;
6821
6892
  return /* @__PURE__ */ import_react4.default.createElement("div", { className: "w-full space-y-2" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react4.default.createElement(
6822
6893
  "input",
@@ -6836,7 +6907,12 @@ function CustomPropertyDisplay({
6836
6907
  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"
6837
6908
  },
6838
6909
  /* @__PURE__ */ import_react4.default.createElement("span", { className: "truncate" }, currentTypeLabel),
6839
- /* @__PURE__ */ import_react4.default.createElement(import_fi3.FiChevronDown, { className: `text-slate-400 transition-transform ${isTypeDropdownOpen ? "rotate-180" : ""}` })
6910
+ /* @__PURE__ */ import_react4.default.createElement(
6911
+ import_fi3.FiChevronDown,
6912
+ {
6913
+ className: `text-slate-400 transition-transform ${isTypeDropdownOpen ? "rotate-180" : ""}`
6914
+ }
6915
+ )
6840
6916
  ), isTypeDropdownOpen && /* @__PURE__ */ import_react4.default.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__ */ import_react4.default.createElement("li", { key: opt.value }, /* @__PURE__ */ import_react4.default.createElement(
6841
6917
  "button",
6842
6918
  {
@@ -6849,7 +6925,16 @@ function CustomPropertyDisplay({
6849
6925
  var _a2, _b, _c, _d, _e;
6850
6926
  switch (tempProp.type) {
6851
6927
  case "number":
6852
- return /* @__PURE__ */ import_react4.default.createElement("input", { type: "number", placeholder: "Valor num\xE9rico", value: tempProp.value, onChange: (e) => handlePropChange("value", e.target.value), className: baseInput });
6928
+ return /* @__PURE__ */ import_react4.default.createElement(
6929
+ "input",
6930
+ {
6931
+ type: "number",
6932
+ placeholder: "Valor num\xE9rico",
6933
+ value: tempProp.value,
6934
+ onChange: (e) => handlePropChange("value", e.target.value),
6935
+ className: baseInput
6936
+ }
6937
+ );
6853
6938
  case "date": {
6854
6939
  const dateType = ((_a2 = tempProp.value) == null ? void 0 : _a2.type) || "Data";
6855
6940
  const noSpinnerClass = "[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none";
@@ -6866,15 +6951,20 @@ function CustomPropertyDisplay({
6866
6951
  handlePropChange("value", newValue);
6867
6952
  };
6868
6953
  const handleDateValueChange = (dateField, dateValue) => {
6869
- handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
6954
+ handlePropChange("value", {
6955
+ ...tempProp.value,
6956
+ [dateField]: dateValue
6957
+ });
6870
6958
  };
6871
6959
  const handleFullDateChange = (e) => {
6872
6960
  const rawValue = e.target.value;
6873
6961
  const digits = rawValue.replace(/\D/g, "");
6874
6962
  const len = digits.length;
6875
6963
  let formattedValue = digits;
6876
- if (len > 2 && len <= 4) formattedValue = `${digits.slice(0, 2)}/${digits.slice(2)}`;
6877
- else if (len > 4) formattedValue = `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4, 8)}`;
6964
+ if (len > 2 && len <= 4)
6965
+ formattedValue = `${digits.slice(0, 2)}/${digits.slice(2)}`;
6966
+ else if (len > 4)
6967
+ formattedValue = `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4, 8)}`;
6878
6968
  handleDateValueChange("value", formattedValue);
6879
6969
  };
6880
6970
  return /* @__PURE__ */ import_react4.default.createElement("div", { className: "space-y-2 p-2 bg-slate-900/60 rounded-lg border border-white/10" }, /* @__PURE__ */ import_react4.default.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__ */ import_react4.default.createElement(
@@ -6886,7 +6976,17 @@ function CustomPropertyDisplay({
6886
6976
  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"}`
6887
6977
  },
6888
6978
  t === "Date Interval" ? "Intervalo" : t
6889
- ))), dateType === "Data" && /* @__PURE__ */ import_react4.default.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__ */ import_react4.default.createElement(
6979
+ ))), dateType === "Data" && /* @__PURE__ */ import_react4.default.createElement(
6980
+ "input",
6981
+ {
6982
+ type: "text",
6983
+ placeholder: "DD/MM/AAAA",
6984
+ value: ((_b = tempProp.value) == null ? void 0 : _b.value) || "",
6985
+ onChange: handleFullDateChange,
6986
+ maxLength: "10",
6987
+ className: baseInput
6988
+ }
6989
+ ), dateType === "Ano" && /* @__PURE__ */ import_react4.default.createElement(
6890
6990
  "input",
6891
6991
  {
6892
6992
  type: "number",
@@ -6934,7 +7034,11 @@ function CustomPropertyDisplay({
6934
7034
  type: "text",
6935
7035
  placeholder: tempProp.type === "images" || tempProp.type === "documents" ? "ID" : "Nome",
6936
7036
  value: item.name,
6937
- onChange: (e) => handleListItemChange(itemIndex, "name", e.target.value),
7037
+ onChange: (e) => handleListItemChange(
7038
+ itemIndex,
7039
+ "name",
7040
+ e.target.value
7041
+ ),
6938
7042
  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"
6939
7043
  }
6940
7044
  ), /* @__PURE__ */ import_react4.default.createElement(
@@ -6943,7 +7047,11 @@ function CustomPropertyDisplay({
6943
7047
  type: "text",
6944
7048
  placeholder: tempProp.type === "images" ? "URL da Imagem" : tempProp.type === "documents" ? "URL do Documento" : "Valor",
6945
7049
  value: item.value,
6946
- onChange: (e) => handleListItemChange(itemIndex, "value", e.target.value),
7050
+ onChange: (e) => handleListItemChange(
7051
+ itemIndex,
7052
+ "value",
7053
+ e.target.value
7054
+ ),
6947
7055
  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"
6948
7056
  }
6949
7057
  ), (tempProp.type === "images" || tempProp.type === "documents") && /* @__PURE__ */ import_react4.default.createElement(
@@ -6956,11 +7064,53 @@ function CustomPropertyDisplay({
6956
7064
  title: "Upload de Arquivo"
6957
7065
  },
6958
7066
  uploadingItemIndex === itemIndex ? /* @__PURE__ */ import_react4.default.createElement(import_fi3.FiLoader, { className: "animate-spin" }) : /* @__PURE__ */ import_react4.default.createElement(import_fi3.FiUpload, { size: 12 })
6959
- ), /* @__PURE__ */ import_react4.default.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__ */ import_react4.default.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"));
7067
+ ), /* @__PURE__ */ import_react4.default.createElement(
7068
+ "button",
7069
+ {
7070
+ type: "button",
7071
+ onClick: () => handleRemoveListItem(itemIndex),
7072
+ 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",
7073
+ "aria-label": "Remover item"
7074
+ },
7075
+ "\xD7"
7076
+ ))), /* @__PURE__ */ import_react4.default.createElement(
7077
+ "button",
7078
+ {
7079
+ type: "button",
7080
+ onClick: () => handleAddListItem(),
7081
+ className: "mt-1 w-full px-2 py-1.5 bg-slate-700/70 hover:bg-slate-600 rounded-md transition-colors text-xs"
7082
+ },
7083
+ "Adicionar Item"
7084
+ ));
6960
7085
  default:
6961
- return /* @__PURE__ */ import_react4.default.createElement("input", { type: "text", placeholder: "Valor", value: tempProp.value, onChange: (e) => handlePropChange("value", e.target.value), className: baseInput });
7086
+ return /* @__PURE__ */ import_react4.default.createElement(
7087
+ "input",
7088
+ {
7089
+ type: "text",
7090
+ placeholder: "Valor",
7091
+ value: tempProp.value,
7092
+ onChange: (e) => handlePropChange("value", e.target.value),
7093
+ className: baseInput
7094
+ }
7095
+ );
6962
7096
  }
6963
- })()), /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex justify-end items-center gap-2 pt-1" }, /* @__PURE__ */ import_react4.default.createElement("button", { onClick: handleCancel, type: "button", className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors" }, /* @__PURE__ */ import_react4.default.createElement(import_fi3.FiX, { className: "text-red-400" })), /* @__PURE__ */ import_react4.default.createElement("button", { onClick: handleSave, type: "button", className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors" }, /* @__PURE__ */ import_react4.default.createElement(import_fi3.FiCheck, { className: "text-green-400" }))));
7097
+ })()), /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex justify-end items-center gap-2 pt-1" }, /* @__PURE__ */ import_react4.default.createElement(
7098
+ "button",
7099
+ {
7100
+ onClick: handleCancel,
7101
+ type: "button",
7102
+ className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors"
7103
+ },
7104
+ /* @__PURE__ */ import_react4.default.createElement(import_fi3.FiX, { className: "text-red-400" })
7105
+ ), /* @__PURE__ */ import_react4.default.createElement(
7106
+ "button",
7107
+ {
7108
+ onClick: handleSave,
7109
+ type: "button",
7110
+ className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors"
7111
+ },
7112
+ /* @__PURE__ */ import_react4.default.createElement(import_fi3.FiCheck, { className: "text-green-400" })
7113
+ )));
6964
7114
  };
6965
7115
  const renderDisplayView = () => /* @__PURE__ */ import_react4.default.createElement("div", { className: "w-full space-y-2 text-sm" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ import_react4.default.createElement("p", { className: "font-semibold text-slate-200" }, prop.key), /* @__PURE__ */ import_react4.default.createElement("span", { className: "text-xs capitalize bg-slate-700/50 px-2 py-0.5 rounded text-slate-400" }, prop.type)), /* @__PURE__ */ import_react4.default.createElement("div", { className: "text-slate-300 pl-1" }, (() => {
6966
7116
  switch (prop.type) {
@@ -6988,7 +7138,10 @@ function CustomPropertyDisplay({
6988
7138
  if (isTextureMode) {
6989
7139
  onSelectTexture(item.value);
6990
7140
  } else {
6991
- onOpenImageViewer == null ? void 0 : onOpenImageViewer(prop.value.map((img) => ({ ...img })), index);
7141
+ onOpenImageViewer == null ? void 0 : onOpenImageViewer(
7142
+ prop.value.map((img) => ({ ...img })),
7143
+ index
7144
+ );
6992
7145
  }
6993
7146
  },
6994
7147
  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"}`,
@@ -6998,7 +7151,17 @@ function CustomPropertyDisplay({
6998
7151
  ), isSelected && /* @__PURE__ */ import_react4.default.createElement("div", { className: "absolute -top-2 -right-2 bg-green-500 text-white rounded-full p-0.5 shadow-sm" }, /* @__PURE__ */ import_react4.default.createElement(import_fi3.FiCheck, { size: 12 })));
6999
7152
  }));
7000
7153
  case "links":
7001
- return /* @__PURE__ */ import_react4.default.createElement("ul", { className: "space-y-1.5 max-h-40 overflow-y-auto custom-scrollbar" }, Array.isArray(prop.value) && prop.value.map((item) => /* @__PURE__ */ import_react4.default.createElement("li", { key: item.id }, /* @__PURE__ */ import_react4.default.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__ */ import_react4.default.createElement(import_fi3.FiExternalLink, { className: "opacity-60", size: 12 })))));
7154
+ return /* @__PURE__ */ import_react4.default.createElement("ul", { className: "space-y-1.5 max-h-40 overflow-y-auto custom-scrollbar" }, Array.isArray(prop.value) && prop.value.map((item) => /* @__PURE__ */ import_react4.default.createElement("li", { key: item.id }, /* @__PURE__ */ import_react4.default.createElement(
7155
+ "a",
7156
+ {
7157
+ href: item.value,
7158
+ target: "_blank",
7159
+ rel: "noopener noreferrer",
7160
+ className: "inline-flex items-center gap-2 text-indigo-400 hover:text-indigo-300 hover:underline transition-colors"
7161
+ },
7162
+ item.name,
7163
+ /* @__PURE__ */ import_react4.default.createElement(import_fi3.FiExternalLink, { className: "opacity-60", size: 12 })
7164
+ ))));
7002
7165
  case "documents":
7003
7166
  return /* @__PURE__ */ import_react4.default.createElement("div", { className: "space-y-2 mt-1" }, Array.isArray(prop.value) && prop.value.map((item) => /* @__PURE__ */ import_react4.default.createElement(
7004
7167
  "a",
@@ -7026,7 +7189,30 @@ function CustomPropertyDisplay({
7026
7189
  onMouseLeave: () => setIsHovered(false)
7027
7190
  },
7028
7191
  isEditing ? renderEditView() : renderDisplayView(),
7029
- !isEditing && !readOnly && /* @__PURE__ */ import_react4.default.createElement("div", { className: `absolute top-2 right-2 flex items-center gap-1 transition-opacity ${isHovered ? "opacity-100" : "opacity-0"}` }, /* @__PURE__ */ import_react4.default.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__ */ import_react4.default.createElement(import_fi3.FiEdit3, { size: 14 })), /* @__PURE__ */ import_react4.default.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__ */ import_react4.default.createElement(import_fi3.FiTrash2, { size: 14 })))
7192
+ !isEditing && !readOnly && /* @__PURE__ */ import_react4.default.createElement(
7193
+ "div",
7194
+ {
7195
+ className: `absolute top-2 right-2 flex items-center gap-1 transition-opacity ${isHovered ? "opacity-100" : "opacity-0"}`
7196
+ },
7197
+ /* @__PURE__ */ import_react4.default.createElement(
7198
+ "button",
7199
+ {
7200
+ onClick: () => setIsEditing(true),
7201
+ 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",
7202
+ title: "Editar"
7203
+ },
7204
+ /* @__PURE__ */ import_react4.default.createElement(import_fi3.FiEdit3, { size: 14 })
7205
+ ),
7206
+ /* @__PURE__ */ import_react4.default.createElement(
7207
+ "button",
7208
+ {
7209
+ onClick: onRemove,
7210
+ 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",
7211
+ title: "Remover"
7212
+ },
7213
+ /* @__PURE__ */ import_react4.default.createElement(import_fi3.FiTrash2, { size: 14 })
7214
+ )
7215
+ )
7030
7216
  );
7031
7217
  }
7032
7218
 
@@ -19355,7 +19541,62 @@ var TIMELINE_LAYER_SPACING_Y = 25;
19355
19541
  var TIMELINE_NODE_SPACING_X = 20;
19356
19542
  var TIMELINE_INTERVAL_BAR_RADIUS = 0.2;
19357
19543
  var TIMELINE_INTERVAL_BAR_OPACITY = 0.85;
19358
- var TIMELINE_GOLD_COLOR = 16766720;
19544
+ var TIMELINE_BLUE_COLOR = 30719;
19545
+ var TIMELINE_BRACKET_WIDTH = 4;
19546
+ var createTimelineIntervalBracket = () => {
19547
+ const group = new THREE3.Group();
19548
+ group.name = "timelineIntervalBracket";
19549
+ const barMaterial = new THREE3.MeshStandardMaterial({
19550
+ color: TIMELINE_BLUE_COLOR,
19551
+ emissive: 17578,
19552
+ emissiveIntensity: 0.5,
19553
+ transparent: true,
19554
+ opacity: TIMELINE_INTERVAL_BAR_OPACITY,
19555
+ roughness: 0.3,
19556
+ metalness: 0.8
19557
+ });
19558
+ const geoHorizontal = new THREE3.CylinderGeometry(
19559
+ TIMELINE_INTERVAL_BAR_RADIUS,
19560
+ TIMELINE_INTERVAL_BAR_RADIUS,
19561
+ 1,
19562
+ 16
19563
+ );
19564
+ const geoVertical = new THREE3.CylinderGeometry(
19565
+ TIMELINE_INTERVAL_BAR_RADIUS,
19566
+ TIMELINE_INTERVAL_BAR_RADIUS,
19567
+ 1,
19568
+ 16
19569
+ );
19570
+ const bottomArm = new THREE3.Mesh(geoHorizontal, barMaterial);
19571
+ bottomArm.rotation.z = Math.PI / 2;
19572
+ const verticalStem = new THREE3.Mesh(geoVertical, barMaterial);
19573
+ const topArm = new THREE3.Mesh(geoHorizontal.clone(), barMaterial);
19574
+ topArm.rotation.z = Math.PI / 2;
19575
+ group.add(bottomArm);
19576
+ group.add(verticalStem);
19577
+ group.add(topArm);
19578
+ group.userData.bottomArm = bottomArm;
19579
+ group.userData.verticalStem = verticalStem;
19580
+ group.userData.topArm = topArm;
19581
+ return group;
19582
+ };
19583
+ var updateTimelineIntervalBracket = (group, startPos, endPos) => {
19584
+ if (!group || !group.userData.bottomArm) return;
19585
+ const { bottomArm, verticalStem, topArm } = group.userData;
19586
+ const w = TIMELINE_BRACKET_WIDTH;
19587
+ const z = -0.5;
19588
+ const p1 = new THREE3.Vector3(startPos.x, startPos.y, z);
19589
+ const p2 = new THREE3.Vector3(startPos.x - w, startPos.y, z);
19590
+ const p3 = new THREE3.Vector3(endPos.x - w, endPos.y, z);
19591
+ const p4 = new THREE3.Vector3(endPos.x, endPos.y, z);
19592
+ bottomArm.scale.y = w;
19593
+ bottomArm.position.set((p1.x + p2.x) / 2, p1.y, z);
19594
+ const verticalLen = Math.abs(p3.y - p2.y);
19595
+ verticalStem.scale.y = Math.max(verticalLen, 1e-3);
19596
+ verticalStem.position.set(p2.x, (p2.y + p3.y) / 2, z);
19597
+ topArm.scale.y = w;
19598
+ topArm.position.set((p3.x + p4.x) / 2, p3.y, z);
19599
+ };
19359
19600
  var QUEST_LENS_COLUMN_SPACING = 80;
19360
19601
  var QUEST_LENS_ROW_SPACING = 40;
19361
19602
  var getAllNodeIdsFromTree = (treeNode) => {
@@ -20044,7 +20285,9 @@ function XViewScene({
20044
20285
  const month = parseInt(parts[1], 10) - 1;
20045
20286
  const year = parseInt(parts[2], 10);
20046
20287
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
20047
- return new Date(year, month, day);
20288
+ const d = /* @__PURE__ */ new Date();
20289
+ d.setFullYear(year, month, day);
20290
+ return d;
20048
20291
  }
20049
20292
  }
20050
20293
  return null;
@@ -20101,10 +20344,12 @@ function XViewScene({
20101
20344
  });
20102
20345
  const timelineWidth = (columns.length > 0 ? columns.length - 1 : 0) * TIMELINE_NODE_SPACING_X;
20103
20346
  const offsetX = -timelineWidth / 2;
20347
+ const allFinalPoints = [];
20104
20348
  timelineNodes.forEach((node) => {
20105
20349
  const { mesh, type, startDate, endDate, label, columnIndex, isUndated } = node;
20106
20350
  const x = columnIndex * TIMELINE_NODE_SPACING_X + offsetX;
20107
20351
  const y = isUndated ? 0 : timeToYMap.get(startDate.getTime());
20352
+ allFinalPoints.push(new THREE3.Vector3(x, y, 0));
20108
20353
  const timelineTween = new import_tween2.Tween(mesh.position).to({ x, y, z: 0 }, 1500).easing(import_tween2.Easing.Cubic.InOut);
20109
20354
  tweenGroup.add(timelineTween);
20110
20355
  timelineTween.start();
@@ -20117,25 +20362,16 @@ function XViewScene({
20117
20362
  if (type === "interval") {
20118
20363
  const endY = timeToYMap.get(endDate.getTime());
20119
20364
  if (endY > y) {
20120
- const barGeometry = new THREE3.CylinderGeometry(
20121
- TIMELINE_INTERVAL_BAR_RADIUS,
20122
- TIMELINE_INTERVAL_BAR_RADIUS,
20123
- 1,
20124
- 16
20125
- );
20126
- const barMaterial = new THREE3.MeshStandardMaterial({
20127
- color: TIMELINE_GOLD_COLOR,
20128
- emissive: 12092939,
20129
- emissiveIntensity: 0.4,
20130
- transparent: true,
20131
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20132
- roughness: 0.3,
20133
- metalness: 0.8
20134
- });
20135
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20365
+ allFinalPoints.push(new THREE3.Vector3(x, endY, 0));
20366
+ const intervalBar = createTimelineIntervalBracket();
20136
20367
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20137
20368
  const endPosition = new THREE3.Vector3(x, endY, -0.5);
20138
20369
  intervalBar.userData.endPosition = endPosition;
20370
+ updateTimelineIntervalBracket(
20371
+ intervalBar,
20372
+ new THREE3.Vector3(x, y, 0),
20373
+ endPosition
20374
+ );
20139
20375
  timelineIntervalsGroup.add(intervalBar);
20140
20376
  const endLabelText = endDate.getFullYear().toString();
20141
20377
  const endLabel = createTextSprite(endLabelText, 48);
@@ -20148,7 +20384,21 @@ function XViewScene({
20148
20384
  }
20149
20385
  }
20150
20386
  });
20151
- }, []);
20387
+ if (allFinalPoints.length > 0) {
20388
+ const boundingBox = new THREE3.Box3().setFromPoints(allFinalPoints);
20389
+ const center = new THREE3.Vector3();
20390
+ boundingBox.getCenter(center);
20391
+ const size = new THREE3.Vector3();
20392
+ boundingBox.getSize(size);
20393
+ const spread = Math.max(size.x, size.y, size.z, 1);
20394
+ const desiredDistance = Math.max(
20395
+ x_view_config.CAMERA_ZOOM_DISTANCE,
20396
+ spread * 0.75
20397
+ );
20398
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
20399
+ tweenToTarget(center, zoomFactor);
20400
+ }
20401
+ }, [tweenToTarget]);
20152
20402
  const handleVersionTimeline = (0, import_react37.useCallback)(
20153
20403
  (sourceMesh, versionMeshes) => {
20154
20404
  const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
@@ -20184,7 +20434,9 @@ function XViewScene({
20184
20434
  const month = parseInt(parts[1], 10) - 1;
20185
20435
  const year = parseInt(parts[2], 10);
20186
20436
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
20187
- return new Date(year, month, day);
20437
+ const d = /* @__PURE__ */ new Date();
20438
+ d.setFullYear(year, month, day);
20439
+ return d;
20188
20440
  }
20189
20441
  }
20190
20442
  return null;
@@ -20255,22 +20507,7 @@ function XViewScene({
20255
20507
  if (type === "interval") {
20256
20508
  const relativeEndY = timeToYMap.get(endDate.getTime()) - timeToYMap.get(startDate.getTime());
20257
20509
  if (relativeEndY > 0) {
20258
- const barGeometry = new THREE3.CylinderGeometry(
20259
- TIMELINE_INTERVAL_BAR_RADIUS,
20260
- TIMELINE_INTERVAL_BAR_RADIUS,
20261
- 1,
20262
- 16
20263
- );
20264
- const barMaterial = new THREE3.MeshStandardMaterial({
20265
- color: TIMELINE_GOLD_COLOR,
20266
- emissive: 12092939,
20267
- emissiveIntensity: 0.4,
20268
- transparent: true,
20269
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20270
- roughness: 0.3,
20271
- metalness: 0.8
20272
- });
20273
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20510
+ const intervalBar = createTimelineIntervalBracket();
20274
20511
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20275
20512
  const endPosition = new THREE3.Vector3(
20276
20513
  baseX,
@@ -20278,6 +20515,11 @@ function XViewScene({
20278
20515
  -0.5
20279
20516
  );
20280
20517
  intervalBar.userData.endPosition = endPosition;
20518
+ updateTimelineIntervalBracket(
20519
+ intervalBar,
20520
+ new THREE3.Vector3(x, y, 0),
20521
+ endPosition
20522
+ );
20281
20523
  timelineIntervalsGroup.add(intervalBar);
20282
20524
  const endLabelText = endDate.getFullYear().toString();
20283
20525
  const endLabel = createTextSprite(endLabelText, 48);
@@ -20773,25 +21015,15 @@ function XViewScene({
20773
21015
  const endY = timeToYMap.get(timeInfo.endDate.getTime());
20774
21016
  if (Math.abs(endY - targetY) > 0.1) {
20775
21017
  const barHeight = Math.abs(endY - targetY);
20776
- const barGeometry = new THREE3.CylinderGeometry(
20777
- TIMELINE_INTERVAL_BAR_RADIUS,
20778
- TIMELINE_INTERVAL_BAR_RADIUS,
20779
- 1,
20780
- 16
20781
- );
20782
- const barMaterial = new THREE3.MeshStandardMaterial({
20783
- color: TIMELINE_GOLD_COLOR,
20784
- emissive: 12092939,
20785
- emissiveIntensity: 0.4,
20786
- transparent: true,
20787
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20788
- roughness: 0.3,
20789
- metalness: 0.8
20790
- });
20791
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
21018
+ const intervalBar = createTimelineIntervalBracket();
20792
21019
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20793
- intervalBar.position.set(finalX, (targetY + endY) / 2, -0.5);
20794
- intervalBar.scale.y = barHeight;
21020
+ const endPosition = new THREE3.Vector3(finalX, endY, -0.5);
21021
+ intervalBar.userData.endPosition = endPosition;
21022
+ updateTimelineIntervalBracket(
21023
+ intervalBar,
21024
+ new THREE3.Vector3(finalX, targetY, 0),
21025
+ endPosition
21026
+ );
20795
21027
  graphGroup.add(intervalBar);
20796
21028
  mesh.userData.timelineIntervalBar = intervalBar;
20797
21029
  const endLabel = createTextSprite(
@@ -21560,23 +21792,12 @@ function XViewScene({
21560
21792
  }
21561
21793
  });
21562
21794
  (_a2 = stateRef.current.timelineIntervalsGroup) == null ? void 0 : _a2.children.forEach((obj) => {
21563
- if (obj.isMesh && obj.userData.associatedNodeId && obj.userData.endPosition) {
21795
+ if (obj.userData.associatedNodeId && obj.userData.endPosition) {
21564
21796
  const bar = obj;
21565
21797
  const node = stateRef.current.nodeObjects[String(bar.userData.associatedNodeId)];
21566
21798
  const endPos = bar.userData.endPosition;
21567
21799
  if (node) {
21568
- const startPos = node.position;
21569
- const distance = startPos.distanceTo(endPos);
21570
- bar.scale.y = distance;
21571
- const midpoint = new THREE3.Vector3().lerpVectors(
21572
- startPos,
21573
- endPos,
21574
- 0.5
21575
- );
21576
- bar.position.copy(midpoint);
21577
- const direction = new THREE3.Vector3().subVectors(endPos, startPos).normalize();
21578
- const upVector = new THREE3.Vector3(0, 1, 0);
21579
- bar.quaternion.setFromUnitVectors(upVector, direction);
21800
+ updateTimelineIntervalBracket(bar, node.position, endPos);
21580
21801
  }
21581
21802
  }
21582
21803
  });
@@ -24558,8 +24779,9 @@ function XViewScene({
24558
24779
  )) == null ? void 0 : _b.parentFileId : null;
24559
24780
  const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
24560
24781
  (0, import_react37.useEffect)(() => {
24561
- if (isInitialized && focusNodeId && !hasFocusedInitial) {
24562
- const nodeObjects = stateRef.current.nodeObjects || {};
24782
+ if (!isInitialized || hasFocusedInitial) return;
24783
+ const nodeObjects = stateRef.current.nodeObjects || {};
24784
+ if (focusNodeId) {
24563
24785
  const targetMesh = nodeObjects[String(focusNodeId)];
24564
24786
  if (targetMesh) {
24565
24787
  setTimeout(() => {
@@ -24572,11 +24794,34 @@ function XViewScene({
24572
24794
  "O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
24573
24795
  );
24574
24796
  }
24797
+ } else if (!focusAncestryId) {
24798
+ const meshes = Object.values(nodeObjects);
24799
+ if (meshes.length > 0) {
24800
+ setTimeout(() => {
24801
+ const nodePositions = meshes.map((mesh) => mesh.position.clone());
24802
+ const boundingBox = new THREE3.Box3().setFromPoints(nodePositions);
24803
+ const center = new THREE3.Vector3();
24804
+ boundingBox.getCenter(center);
24805
+ const size = new THREE3.Vector3();
24806
+ boundingBox.getSize(size);
24807
+ const maxDim = Math.max(size.x, size.y, size.z);
24808
+ const desiredDistance = Math.max(
24809
+ x_view_config.CAMERA_ZOOM_DISTANCE,
24810
+ maxDim * 1.8
24811
+ );
24812
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
24813
+ tweenToTarget(center, zoomFactor);
24814
+ setHasFocusedInitial(true);
24815
+ }, 300);
24816
+ } else {
24817
+ setHasFocusedInitial(true);
24818
+ }
24575
24819
  }
24576
24820
  }, [
24577
24821
  isInitialized,
24578
24822
  sceneVersion,
24579
24823
  focusNodeId,
24824
+ focusAncestryId,
24580
24825
  hasFocusedInitial,
24581
24826
  tweenToTarget
24582
24827
  ]);