@lv-x-software-house/x_view 1.2.2-dev.1 → 1.2.2-dev.11

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 +162 -62
  2. package/dist/index.mjs +164 -64
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -855,7 +855,6 @@ var x_view_config = {
855
855
  ANCESTOR_HIGHLIGHT_COLOR: "#FFD700",
856
856
  BLOOM_EFFECT: { strength: 1.35, radius: 0.6, threshold: 0.52 },
857
857
  EMISSIVE_MULTIPLIER: {
858
- // AJUSTE: Valores agora representam ADIÇÃO de intensidade, não multiplicação
859
858
  BASE: 0,
860
859
  HOVER: 2,
861
860
  SELECTED: 1.5,
@@ -907,7 +906,6 @@ var computeNodeMaterialProps = (colorHex) => {
907
906
  return {
908
907
  color: srgb,
909
908
  emissive: emissiveColor
910
- // Intensity é controlada externamente
911
909
  };
912
910
  };
913
911
  var createNodeMesh = (nodeData, position, glowTexture) => {
@@ -1198,6 +1196,7 @@ var createMultipleLinkLines = (linksArray, sourceNodeMesh, targetNodeMesh, resol
1198
1196
  resolution,
1199
1197
  isCurved,
1200
1198
  isCurved,
1199
+ isCurved,
1201
1200
  curveOffset
1202
1201
  );
1203
1202
  line.userData = {
@@ -1574,12 +1573,12 @@ var userActionHandlers = {
1574
1573
  stateRef.current.creation = { isActive: true, sourceNodeData };
1575
1574
  const ghostGeometry = new THREE.SphereGeometry(1.5, 32, 32);
1576
1575
  const sourceColor = sourceNodeData.color || "#cccccc";
1577
- const { emissiveIntensity } = computeNodeMaterialProps(sourceColor);
1578
- const ghostColor = new THREE.Color(sourceColor);
1576
+ const { color: ghostColor, emissive: ghostEmissive } = computeNodeMaterialProps(sourceColor);
1579
1577
  const ghostMaterial = new THREE.MeshStandardMaterial({
1580
1578
  color: ghostColor,
1581
- emissive: ghostColor,
1582
- emissiveIntensity,
1579
+ emissive: ghostEmissive,
1580
+ emissiveIntensity: MIN_VISIBILITY_INTENSITY,
1581
+ // <-- Forçamos o brilho mínimo
1583
1582
  roughness: 0.6,
1584
1583
  metalness: 0,
1585
1584
  transparent: true,
@@ -1650,27 +1649,50 @@ var userActionHandlers = {
1650
1649
  setters.setFormPosition((p) => ({ ...p, opacity: 0 }));
1651
1650
  },
1652
1651
  handleSaveNode: async (context, newNodeData) => {
1653
- const { graphDataRef, sceneDataRef, stateRef, creationMode, setters } = context;
1652
+ const { graphDataRef, sceneDataRef, stateRef, creationMode, setters, actions } = context;
1654
1653
  if (!graphDataRef.current || !sceneDataRef.current) return;
1655
1654
  const { sourceNodeData } = creationMode;
1656
- const newNode = { id: import_short_uuid.default.generate(), ...newNodeData };
1655
+ const { targetDatasetId, ...nodeDataToSave } = newNodeData;
1656
+ const newNode = { id: import_short_uuid.default.generate(), ...nodeDataToSave };
1657
1657
  const newLink = { id: `link_${import_short_uuid.default.generate()}`, source: sourceNodeData.id, target: newNode.id };
1658
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id);
1659
- if (!parentInfo || !parentInfo.ownerId) {
1660
- console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node de origem:", sourceNodeData.id);
1661
- alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio.");
1658
+ const sourceParentInfo = stateRef.current.nodeIdToParentFileMap.get(String(sourceNodeData.id));
1659
+ const finalTargetDatasetId = targetDatasetId || sourceParentInfo.parentFileId;
1660
+ const targetParentInfo = sceneDataRef.current.parent_dbs.find((db) => String(db.db_id) === String(finalTargetDatasetId));
1661
+ if (!sourceParentInfo || !targetParentInfo) {
1662
+ alert("Erro ao identificar os datasets de origem ou destino.");
1662
1663
  return;
1663
1664
  }
1664
- const { parentFileId, ownerId } = parentInfo;
1665
- const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileId]));
1666
- specificParentData.nodes.push(newNode);
1667
- specificParentData.links.push(newLink);
1668
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
1665
+ const isCrossDataset = String(sourceParentInfo.parentFileId) !== String(finalTargetDatasetId);
1666
+ const sourceDataToUpdate = JSON.parse(JSON.stringify(graphDataRef.current[sourceParentInfo.parentFileId]));
1667
+ let targetDataToUpdate = isCrossDataset ? JSON.parse(JSON.stringify(graphDataRef.current[finalTargetDatasetId])) : sourceDataToUpdate;
1668
+ targetDataToUpdate.nodes.push(newNode);
1669
+ sourceDataToUpdate.links.push(newLink);
1670
+ const savePromises = [];
1671
+ if (isCrossDataset) {
1672
+ savePromises.push(
1673
+ actions.save_view_data(`x_view_dbs/${sourceParentInfo.ownerId}/${sourceParentInfo.parentFileId}`, sourceDataToUpdate)
1674
+ );
1675
+ savePromises.push(
1676
+ actions.save_view_data(`x_view_dbs/${targetParentInfo.owner_id}/${finalTargetDatasetId}`, targetDataToUpdate)
1677
+ );
1678
+ } else {
1679
+ savePromises.push(
1680
+ actions.save_view_data(`x_view_dbs/${sourceParentInfo.ownerId}/${sourceParentInfo.parentFileId}`, sourceDataToUpdate)
1681
+ );
1682
+ }
1669
1683
  try {
1670
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
1671
- graphDataRef.current[parentFileId] = specificParentData;
1684
+ await Promise.all(savePromises);
1685
+ graphDataRef.current[sourceParentInfo.parentFileId] = sourceDataToUpdate;
1686
+ if (isCrossDataset) {
1687
+ graphDataRef.current[finalTargetDatasetId] = targetDataToUpdate;
1688
+ }
1672
1689
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1673
1690
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
1691
+ stateRef.current.nodeIdToParentFileMap.set(String(newNode.id), {
1692
+ parentFileId: finalTargetDatasetId,
1693
+ ownerId: targetParentInfo.owner_id,
1694
+ datasetName: targetDataToUpdate.dataset_name || "Dataset Desconhecido"
1695
+ });
1674
1696
  setters.setSceneVersion((v) => v + 1);
1675
1697
  } catch (error) {
1676
1698
  console.error("Falha ao salvar os dados do grafo:", error);
@@ -1708,12 +1730,12 @@ var userActionHandlers = {
1708
1730
  stateRef.current.creation = { isActive: true, sourceNodeData };
1709
1731
  const ghostGeometry = new THREE.SphereGeometry(1.5, 32, 32);
1710
1732
  const sourceColor = sourceNodeData.color || "#cccccc";
1711
- const { emissiveIntensity } = computeNodeMaterialProps(sourceColor);
1712
- const ghostColor = new THREE.Color(sourceColor);
1733
+ const { color: ghostColor, emissive: ghostEmissive } = computeNodeMaterialProps(sourceColor);
1713
1734
  const ghostMaterial = new THREE.MeshStandardMaterial({
1714
1735
  color: ghostColor,
1715
- emissive: ghostColor,
1716
- emissiveIntensity,
1736
+ emissive: ghostEmissive,
1737
+ emissiveIntensity: MIN_VISIBILITY_INTENSITY,
1738
+ // <-- Forçamos o brilho mínimo
1717
1739
  roughness: 0.6,
1718
1740
  metalness: 0,
1719
1741
  transparent: true,
@@ -3767,6 +3789,7 @@ function DescriptionEditModal({
3767
3789
  e.preventDefault();
3768
3790
  e.stopPropagation();
3769
3791
  e.stopImmediatePropagation();
3792
+ if (hoverTimeoutRef.current) clearTimeout(hoverTimeoutRef.current);
3770
3793
  setIsMentionModalOpen(false);
3771
3794
  setMentionTriggerIndex(null);
3772
3795
  setTooltipData(null);
@@ -3860,6 +3883,7 @@ function DescriptionEditModal({
3860
3883
  setIsImportModalOpen(false);
3861
3884
  };
3862
3885
  const handleMentionSelect = (node) => {
3886
+ if (hoverTimeoutRef.current) clearTimeout(hoverTimeoutRef.current);
3863
3887
  const tag = `[[MENTION:node:${node.id}]]`;
3864
3888
  const el = textareaRef.current;
3865
3889
  if (mentionTriggerIndex !== null && el) {
@@ -4073,7 +4097,7 @@ function DescriptionEditModal({
4073
4097
  className: "px-3 py-1.5 bg-indigo-600/30 hover:bg-indigo-600 text-indigo-200 text-xs rounded-lg transition-colors disabled:opacity-50"
4074
4098
  },
4075
4099
  "OK"
4076
- ))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "p-2 border-t border-white/10 bg-slate-950/50 flex justify-end" }, /* @__PURE__ */ import_react5.default.createElement("button", { onClick: () => setIsImageModalOpen(false), className: "px-3 py-1.5 text-xs text-slate-400 hover:text-white transition-colors" }, "Cancelar")))), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mt-2 text-xs text-slate-500 shrink-0" }, "Dica: Tags agora incluem ID (ex: ", /* @__PURE__ */ import_react5.default.createElement("b", null, "*/1:a9f2/"), ") para garantir que refer\xEAncias n\xE3o quebrem se voc\xEA reordenar o texto.")),
4100
+ ))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "p-2 border-t border-white/10 bg-slate-950/50 flex justify-end" }, /* @__PURE__ */ import_react5.default.createElement("button", { onClick: () => setIsImageModalOpen(false), className: "px-3 py-1.5 text-xs text-slate-400 hover:text-white transition-colors" }, "Cancelar"))))),
4077
4101
  /* @__PURE__ */ import_react5.default.createElement("div", { className: "sticky bottom-0 z-10 bg-slate-950/95 border-t border-white/10 px-6 py-4 flex justify-end gap-3 shrink-0" }, /* @__PURE__ */ import_react5.default.createElement("button", { onClick: handleSave, className: "px-6 py-2 rounded-lg bg-gradient-to-tr from-indigo-600 to-indigo-400 hover:from-indigo-500 hover:to-indigo-300 transition-colors font-semibold text-sm shadow-[0_8px_24px_rgba(99,102,241,0.35)]" }, "Salvar"))
4078
4102
  )), tooltipData && /* @__PURE__ */ import_react5.default.createElement(
4079
4103
  "div",
@@ -4549,7 +4573,6 @@ function DescriptionReadModePanel({
4549
4573
  title,
4550
4574
  description,
4551
4575
  ancestryId,
4552
- // <-- NOVO: Prop recebida do XViewScene
4553
4576
  savedSections,
4554
4577
  onBack,
4555
4578
  onEdit,
@@ -4658,7 +4681,7 @@ function DescriptionReadModePanel({
4658
4681
  return /* @__PURE__ */ import_react7.default.createElement(
4659
4682
  "div",
4660
4683
  {
4661
- className: "flex flex-col h-full w-full bg-slate-950/50 relative overflow-hidden group",
4684
+ className: "flex flex-col h-full max-h-full w-full bg-slate-950/50 relative overflow-hidden group min-h-0",
4662
4685
  onPointerDown: swallow,
4663
4686
  onClick: swallow
4664
4687
  },
@@ -4730,7 +4753,7 @@ function DescriptionReadModePanel({
4730
4753
  },
4731
4754
  "\xD7"
4732
4755
  ))),
4733
- /* @__PURE__ */ import_react7.default.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-6 bg-slate-900/20 relative z-10", onClick: () => setTargetRenderNodeId(null) }, showAbstraction ? /* @__PURE__ */ import_react7.default.createElement("div", { className: "space-y-4 animate-in fade-in slide-in-from-bottom-2 duration-300", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-4" }, /* @__PURE__ */ import_react7.default.createElement("h3", { className: "text-sm font-semibold text-purple-300 uppercase tracking-wider" }, "Explorar Hierarquia"), /* @__PURE__ */ import_react7.default.createElement("div", { className: "flex flex-wrap gap-2" }, targetRenderNodeId && onRenderAbstractionTree && /* @__PURE__ */ import_react7.default.createElement(
4756
+ /* @__PURE__ */ import_react7.default.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-6 bg-slate-900/20 relative z-10 min-h-0", onClick: () => setTargetRenderNodeId(null) }, showAbstraction ? /* @__PURE__ */ import_react7.default.createElement("div", { className: "space-y-4 animate-in fade-in slide-in-from-bottom-2 duration-300", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-4" }, /* @__PURE__ */ import_react7.default.createElement("h3", { className: "text-sm font-semibold text-purple-300 uppercase tracking-wider" }, "Explorar Hierarquia"), /* @__PURE__ */ import_react7.default.createElement("div", { className: "flex flex-wrap gap-2" }, targetRenderNodeId && onRenderAbstractionTree && /* @__PURE__ */ import_react7.default.createElement(
4734
4757
  "button",
4735
4758
  {
4736
4759
  onClick: (e) => {
@@ -5256,6 +5279,7 @@ function CreateAncestryPanel({
5256
5279
  const branchProgressMapRef = (0, import_react10.useRef)({});
5257
5280
  const [lastSavedSnapshot, setLastSavedSnapshot] = (0, import_react10.useState)(null);
5258
5281
  const [isPrivate, setIsPrivate] = (0, import_react10.useState)(ancestryMode.is_private || false);
5282
+ const initializedContextIdRef = (0, import_react10.useRef)(null);
5259
5283
  const availableImages = customProps.filter((p) => p.type === "images").flatMap((p) => Array.isArray(p.value) ? p.value : []).filter((img) => img.value && img.value.trim() !== "");
5260
5284
  const handleImageClickFromText = (url, name) => {
5261
5285
  if (onOpenImageViewer) {
@@ -5454,6 +5478,11 @@ function CreateAncestryPanel({
5454
5478
  }, [isContextLinked, branchStack]);
5455
5479
  (0, import_react10.useEffect)(() => {
5456
5480
  const ctx = getCurrentContext();
5481
+ const currentContextId = branchStack.length > 0 ? branchStack[branchStack.length - 1].branchId : ancestryMode.currentAncestryId || `new_${ancestryMode.ancestral_node}`;
5482
+ if (initializedContextIdRef.current === currentContextId) {
5483
+ return;
5484
+ }
5485
+ initializedContextIdRef.current = currentContextId;
5457
5486
  let sourceObject = {};
5458
5487
  if (ctx) {
5459
5488
  sourceObject = ctx;
@@ -5804,7 +5833,7 @@ function CreateAncestryPanel({
5804
5833
  const currentAbsTreeStr = JSON.stringify(ancestryMode.abstraction_tree);
5805
5834
  abstractionTreeChanged = currentAbsTreeStr !== lastSavedSnapshot.abstractionTree;
5806
5835
  }
5807
- return treeChanged || nameChanged || descChanged || sectionsChanged || propsChanged || privateChanged;
5836
+ return treeChanged || nameChanged || descChanged || sectionsChanged || propsChanged || privateChanged || abstractionTreeChanged;
5808
5837
  }, [
5809
5838
  ancestryName,
5810
5839
  description,
@@ -6772,8 +6801,12 @@ function InSceneCreationForm({
6772
6801
  availableAncestries = [],
6773
6802
  onMentionClick,
6774
6803
  sourceTypes,
6775
- onUploadFile
6804
+ onUploadFile,
6805
+ availableDatasets = [],
6806
+ sourceNodeDatasetId,
6807
+ viewType
6776
6808
  }) {
6809
+ var _a;
6777
6810
  const [name, setName] = (0, import_react13.useState)("");
6778
6811
  const [types, setTypes] = (0, import_react13.useState)([]);
6779
6812
  const [typeInput, setTypeInput] = (0, import_react13.useState)("");
@@ -6787,6 +6820,25 @@ function InSceneCreationForm({
6787
6820
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react13.useState)(false);
6788
6821
  const [useImageAsTexture, setUseImageAsTexture] = (0, import_react13.useState)(false);
6789
6822
  const [selectedImageUrl, setSelectedImageUrl] = (0, import_react13.useState)(null);
6823
+ const [targetDatasetId, setTargetDatasetId] = (0, import_react13.useState)(sourceNodeDatasetId || "");
6824
+ const [isDatasetDropdownOpen, setIsDatasetDropdownOpen] = (0, import_react13.useState)(false);
6825
+ const datasetDropdownRef = (0, import_react13.useRef)(null);
6826
+ (0, import_react13.useEffect)(() => {
6827
+ if (sourceNodeDatasetId) setTargetDatasetId(sourceNodeDatasetId);
6828
+ }, [sourceNodeDatasetId]);
6829
+ (0, import_react13.useEffect)(() => {
6830
+ function handleClickOutside(event) {
6831
+ if (datasetDropdownRef.current && !datasetDropdownRef.current.contains(event.target)) {
6832
+ setIsDatasetDropdownOpen(false);
6833
+ }
6834
+ }
6835
+ if (isDatasetDropdownOpen) {
6836
+ document.addEventListener("mousedown", handleClickOutside);
6837
+ }
6838
+ return () => {
6839
+ document.removeEventListener("mousedown", handleClickOutside);
6840
+ };
6841
+ }, [isDatasetDropdownOpen]);
6790
6842
  const propsEndRef = (0, import_react13.useRef)(null);
6791
6843
  const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
6792
6844
  (0, import_react13.useEffect)(() => {
@@ -6832,8 +6884,8 @@ function InSceneCreationForm({
6832
6884
  const newProp = createNewCustomProperty(customProps);
6833
6885
  setCustomProps([...customProps, newProp]);
6834
6886
  setTimeout(() => {
6835
- var _a;
6836
- (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
6887
+ var _a2;
6888
+ (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
6837
6889
  }, 100);
6838
6890
  };
6839
6891
  const handleRemoveProp = (index) => setCustomProps(customProps.filter((_, i) => i !== index));
@@ -6866,12 +6918,12 @@ function InSceneCreationForm({
6866
6918
  onSizeChange == null ? void 0 : onSizeChange(newSize);
6867
6919
  };
6868
6920
  const handleToggleImageMode = () => {
6869
- var _a, _b;
6921
+ var _a2, _b;
6870
6922
  const newValue = !useImageAsTexture;
6871
6923
  setUseImageAsTexture(newValue);
6872
6924
  if (newValue) {
6873
6925
  const firstImageProp = customProps.find((p) => p.type === "images");
6874
- if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
6926
+ if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
6875
6927
  const url = firstImageProp.value[0].value;
6876
6928
  setSelectedImageUrl(url);
6877
6929
  onImageChange == null ? void 0 : onImageChange(true, url);
@@ -6904,6 +6956,7 @@ function InSceneCreationForm({
6904
6956
  description_sections: processedSections,
6905
6957
  useImageAsTexture,
6906
6958
  textureImageUrl: useImageAsTexture ? selectedImageUrl : null,
6959
+ targetDatasetId,
6907
6960
  ...additionalData
6908
6961
  });
6909
6962
  };
@@ -6924,6 +6977,7 @@ function InSceneCreationForm({
6924
6977
  onOpenImageViewer([{ name: name2 || "Imagem", value: url }], 0);
6925
6978
  }
6926
6979
  };
6980
+ const selectedDatasetName = ((_a = availableDatasets.find((ds) => ds.id === targetDatasetId)) == null ? void 0 : _a.name) || "Selecione um Dataset...";
6927
6981
  return /* @__PURE__ */ import_react13.default.createElement(import_react13.default.Fragment, null, /* @__PURE__ */ import_react13.default.createElement(
6928
6982
  "div",
6929
6983
  {
@@ -6981,7 +7035,27 @@ function InSceneCreationForm({
6981
7035
  }
6982
7036
  },
6983
7037
  suggestedType
6984
- ))))), /* @__PURE__ */ import_react13.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react13.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome do Node"), /* @__PURE__ */ import_react13.default.createElement("input", { required: true, type: "text", placeholder: "Ex.: Cliente XPTO", value: name, onChange: handleNameInputChange, className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60" })), /* @__PURE__ */ import_react13.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react13.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o (Opcional)"), /* @__PURE__ */ import_react13.default.createElement("div", { className: "relative group min-h-[80px] bg-slate-800/70 p-2.5 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ import_react13.default.createElement(
7038
+ ))))), /* @__PURE__ */ import_react13.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react13.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome do Node"), /* @__PURE__ */ import_react13.default.createElement("input", { required: true, type: "text", placeholder: "Ex.: Cliente XPTO", value: name, onChange: handleNameInputChange, className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60" })), viewType === "view" && availableDatasets.length > 0 && /* @__PURE__ */ import_react13.default.createElement("div", { className: "space-y-1.5 relative", ref: datasetDropdownRef }, /* @__PURE__ */ import_react13.default.createElement("label", { className: "text-xs text-slate-300" }, "Criar Node no Dataset:"), /* @__PURE__ */ import_react13.default.createElement(
7039
+ "button",
7040
+ {
7041
+ type: "button",
7042
+ onClick: () => setIsDatasetDropdownOpen(!isDatasetDropdownOpen),
7043
+ className: "w-full flex items-center justify-between bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60 hover:bg-slate-700/70 transition-colors text-slate-200 text-left"
7044
+ },
7045
+ /* @__PURE__ */ import_react13.default.createElement("span", { className: "truncate pr-2" }, selectedDatasetName),
7046
+ /* @__PURE__ */ import_react13.default.createElement(import_fi12.FiChevronDown, { className: `flex-shrink-0 text-slate-400 transition-transform ${isDatasetDropdownOpen ? "rotate-180" : ""}` })
7047
+ ), isDatasetDropdownOpen && /* @__PURE__ */ import_react13.default.createElement("ul", { className: "custom-scrollbar absolute top-[66px] left-0 z-20 w-full max-h-48 overflow-y-auto rounded-lg bg-slate-800 border border-white/10 shadow-xl py-1" }, availableDatasets.map((ds) => /* @__PURE__ */ import_react13.default.createElement(
7048
+ "li",
7049
+ {
7050
+ key: ds.id,
7051
+ onClick: () => {
7052
+ setTargetDatasetId(ds.id);
7053
+ setIsDatasetDropdownOpen(false);
7054
+ },
7055
+ className: `px-3 py-2 text-sm cursor-pointer transition-colors ${targetDatasetId === ds.id ? "bg-indigo-600/40 text-indigo-200 font-medium" : "text-slate-300 hover:bg-white/5"}`
7056
+ },
7057
+ ds.name
7058
+ )))), /* @__PURE__ */ import_react13.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react13.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o (Opcional)"), /* @__PURE__ */ import_react13.default.createElement("div", { className: "relative group min-h-[80px] bg-slate-800/70 p-2.5 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ import_react13.default.createElement(
6985
7059
  DescriptionDisplay,
6986
7060
  {
6987
7061
  description,
@@ -7317,7 +7391,8 @@ function NodeDetailsPanel({
7317
7391
  onMentionClick,
7318
7392
  onIntensityChange,
7319
7393
  onUploadFile,
7320
- userRole
7394
+ userRole,
7395
+ currentDatasetName
7321
7396
  }) {
7322
7397
  const [name, setName] = (0, import_react15.useState)((node == null ? void 0 : node.name) ?? "");
7323
7398
  const [types, setTypes] = (0, import_react15.useState)([]);
@@ -7547,10 +7622,10 @@ function NodeDetailsPanel({
7547
7622
  return /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(
7548
7623
  "div",
7549
7624
  {
7550
- className: `ui-overlay absolute group rounded-2xl border border-white/10 bg-slate-950/70 backdrop-blur-xl shadow-[0_20px_80px_rgba(0,0,0,0.6)] ring-1 ring-white/10 text-white overflow-hidden transition-all duration-300 ease-out
7625
+ className: `ui-overlay absolute group rounded-2xl border border-white/10 bg-slate-950/70 backdrop-blur-xl shadow-[0_20px_80px_rgba(0,0,0,0.6)] ring-1 ring-white/10 text-white overflow-hidden flex flex-col transition-all duration-300 ease-out
7551
7626
  ${isReadMode ? "w-[min(92vw,700px)]" : "w-[min(92vw,440px)]"}
7552
7627
  `,
7553
- style: { top: 16, right: 16, zIndex: 1100 },
7628
+ style: { top: 16, right: 16, zIndex: 1100, maxHeight: "calc(100vh - 32px)" },
7554
7629
  onPointerDown: swallow,
7555
7630
  onPointerMove: swallow,
7556
7631
  onPointerUp: swallow,
@@ -7728,7 +7803,7 @@ function NodeDetailsPanel({
7728
7803
  onUploadFile: canEdit ? onUploadFile : void 0,
7729
7804
  readOnly: !canEdit
7730
7805
  }
7731
- )), /* @__PURE__ */ import_react15.default.createElement("div", { ref: propsEndRef })))), /* @__PURE__ */ import_react15.default.createElement("div", { className: "sticky bottom-0 z-10 bg-gradient-to-t from-slate-950/80 via-slate-950/50 to-transparent px-6 py-4 border-t border-white/10 flex justify-end gap-3" }, /* @__PURE__ */ import_react15.default.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "px-4 py-2 rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-sm disabled:opacity-50" }, canEdit ? "Cancelar" : "Fechar"), canEdit && /* @__PURE__ */ import_react15.default.createElement(
7806
+ )), /* @__PURE__ */ import_react15.default.createElement("div", { ref: propsEndRef }))), currentDatasetName && /* @__PURE__ */ import_react15.default.createElement("div", { className: "pt-3 mt-4 border-t border-white/10 flex items-center justify-end gap-2 text-xs text-slate-400" }, /* @__PURE__ */ import_react15.default.createElement("span", { className: "truncate text-right" }, /* @__PURE__ */ import_react15.default.createElement("span", { className: "text-slate-200 font-medium" }, currentDatasetName)))), /* @__PURE__ */ import_react15.default.createElement("div", { className: "sticky bottom-0 z-10 bg-gradient-to-t from-slate-950/80 via-slate-950/50 to-transparent px-6 py-4 border-t border-white/10 flex justify-end gap-3" }, /* @__PURE__ */ import_react15.default.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "px-4 py-2 rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-sm disabled:opacity-50" }, canEdit ? "Cancelar" : "Fechar"), canEdit && /* @__PURE__ */ import_react15.default.createElement(
7732
7807
  "button",
7733
7808
  {
7734
7809
  onClick: () => handleSave(false),
@@ -9049,7 +9124,7 @@ function XViewScene({
9049
9124
  delete_file_action,
9050
9125
  check_user_permission
9051
9126
  }) {
9052
- var _a, _b, _c, _d, _e, _f;
9127
+ var _a, _b, _c, _d, _e, _f, _g;
9053
9128
  const { data: session, status } = (0, import_react24.useSession)();
9054
9129
  const router = (0, import_navigation.useRouter)();
9055
9130
  const searchParams = (0, import_navigation.useSearchParams)();
@@ -9201,9 +9276,10 @@ function XViewScene({
9201
9276
  const parentFile = allParentData[parentFileId];
9202
9277
  const parentDbInfo = parentDbsArray.find((db) => String(db.db_id) === String(parentFileId));
9203
9278
  const ownerId2 = (parentDbInfo == null ? void 0 : parentDbInfo.owner_id) || null;
9279
+ const datasetName = parentFile.dataset_name || `Dataset #${parentFileId.substring(0, 6)}`;
9204
9280
  if (parentFile.nodes && ownerId2) {
9205
9281
  for (const node of parentFile.nodes) {
9206
- map.set(String(node.id), { parentFileId, ownerId: ownerId2 });
9282
+ map.set(String(node.id), { parentFileId, ownerId: ownerId2, datasetName });
9207
9283
  }
9208
9284
  }
9209
9285
  }
@@ -9723,6 +9799,9 @@ function XViewScene({
9723
9799
  sceneDataRef.current = sceneResponse.data.scene;
9724
9800
  parentDataRef.current = sceneResponse.data.parent;
9725
9801
  ancestryDataRef.current = sceneResponse.data.ancestry;
9802
+ console.log("Console de sceneResponse.data.scene:", sceneResponse.data.scene);
9803
+ console.log("Console de sceneResponse.data.parent:", sceneResponse.data.parent);
9804
+ console.log("Console de sceneResponse.data.ancestry:", sceneResponse.data.ancestry);
9726
9805
  setIsInitialized(true);
9727
9806
  } else {
9728
9807
  console.error("Falha ao buscar dados da cena:", (sceneResponse == null ? void 0 : sceneResponse.error) || "Resposta inv\xE1lida.");
@@ -10504,32 +10583,33 @@ function XViewScene({
10504
10583
  const handleGhostNodeColorChange = (newColor) => {
10505
10584
  const { node: ghostNode, aura: ghostAura } = stateRef.current.ghostElements;
10506
10585
  if (!ghostNode) return;
10507
- const { emissiveIntensity } = computeNodeMaterialProps(newColor);
10508
- const pureColor = new THREE3.Color(newColor);
10586
+ const { color, emissive } = computeNodeMaterialProps(newColor);
10587
+ const currentIntensity = ghostNode.userData.intensity !== void 0 ? Number(ghostNode.userData.intensity) : 0;
10588
+ const finalIntensity = currentIntensity + MIN_VISIBILITY_INTENSITY;
10509
10589
  const isImageNode = ghostNode.userData.useImageAsTexture === true || String(ghostNode.userData.useImageAsTexture) === "true";
10510
10590
  if (isImageNode) {
10511
10591
  const borderMesh = ghostNode.getObjectByName("borderRing");
10512
10592
  if (borderMesh && borderMesh.material) {
10513
- borderMesh.material.color.copy(pureColor);
10593
+ borderMesh.material.color.copy(color);
10514
10594
  if (borderMesh.material.emissive) {
10515
- borderMesh.material.emissive.copy(pureColor);
10516
- borderMesh.material.emissiveIntensity = emissiveIntensity;
10595
+ borderMesh.material.emissive.copy(emissive);
10596
+ borderMesh.material.emissiveIntensity = finalIntensity;
10517
10597
  }
10518
10598
  }
10519
10599
  } else {
10520
10600
  if (ghostNode.material) {
10521
- ghostNode.material.color.copy(pureColor);
10601
+ ghostNode.material.color.copy(color);
10522
10602
  if (ghostNode.material.emissive) {
10523
- ghostNode.material.emissive.copy(pureColor);
10524
- ghostNode.material.emissiveIntensity = emissiveIntensity;
10603
+ ghostNode.material.emissive.copy(emissive);
10604
+ ghostNode.material.emissiveIntensity = finalIntensity;
10525
10605
  }
10526
10606
  }
10527
10607
  }
10528
10608
  if (ghostAura && ghostAura.material) {
10529
- ghostAura.material.color.set(newColor);
10609
+ ghostAura.material.color.copy(color).lerp(new THREE3.Color("#ffffff"), 0.25);
10530
10610
  }
10531
10611
  ghostNode.userData.color = newColor;
10532
- ghostNode.userData._baseEmissiveIntensity = emissiveIntensity;
10612
+ ghostNode.userData._baseEmissiveIntensity = finalIntensity;
10533
10613
  };
10534
10614
  const handleGhostNodeSizeChange = (sizeKey) => {
10535
10615
  const { node: ghostNode } = stateRef.current.ghostElements;
@@ -10570,7 +10650,9 @@ function XViewScene({
10570
10650
  var _a2;
10571
10651
  const mesh = stateRef.current.nodeObjects[String(nodeId)];
10572
10652
  if (!mesh) return;
10573
- const { color, emissive, emissiveIntensity } = computeNodeMaterialProps(newColor);
10653
+ const { color, emissive } = computeNodeMaterialProps(newColor);
10654
+ const currentIntensity = mesh.userData.intensity !== void 0 ? Number(mesh.userData.intensity) : 0;
10655
+ const finalIntensity = currentIntensity + MIN_VISIBILITY_INTENSITY;
10574
10656
  const isImageNode = mesh.userData.useImageAsTexture === true || String(mesh.userData.useImageAsTexture) === "true";
10575
10657
  if (isImageNode) {
10576
10658
  const borderMesh = mesh.getObjectByName("borderRing");
@@ -10578,20 +10660,22 @@ function XViewScene({
10578
10660
  borderMesh.material.color.copy(color);
10579
10661
  if (borderMesh.material.emissive) {
10580
10662
  borderMesh.material.emissive.copy(emissive);
10581
- borderMesh.material.emissiveIntensity = emissiveIntensity;
10663
+ borderMesh.material.emissiveIntensity = finalIntensity;
10582
10664
  }
10583
10665
  }
10584
10666
  } else {
10585
10667
  if (mesh.material) {
10586
10668
  mesh.material.color.copy(color);
10587
10669
  mesh.material.emissive.copy(emissive);
10588
- mesh.material.emissiveIntensity = emissiveIntensity;
10670
+ mesh.material.emissiveIntensity = finalIntensity;
10589
10671
  }
10590
10672
  }
10591
10673
  const aura = mesh.getObjectByName("aura");
10592
- if ((_a2 = aura == null ? void 0 : aura.material) == null ? void 0 : _a2.color) aura.material.color.set(newColor);
10674
+ if ((_a2 = aura == null ? void 0 : aura.material) == null ? void 0 : _a2.color) {
10675
+ aura.material.color.copy(color).lerp(new THREE3.Color("#ffffff"), 0.25);
10676
+ }
10593
10677
  mesh.userData.color = newColor;
10594
- mesh.userData._baseEmissiveIntensity = emissiveIntensity;
10678
+ mesh.userData._baseEmissiveIntensity = finalIntensity;
10595
10679
  };
10596
10680
  const handleDetailNodeSizeChange = (nodeId, newSize) => {
10597
10681
  const mesh = stateRef.current.nodeObjects[String(nodeId)];
@@ -12044,6 +12128,18 @@ function XViewScene({
12044
12128
  tweenToTarget(nodeMesh, 1.2);
12045
12129
  }
12046
12130
  }, [tweenToTarget]);
12131
+ const availableDatasets = (0, import_react23.useMemo)(() => {
12132
+ if (!sceneDataRef.current || !parentDataRef.current) return [];
12133
+ return sceneDataRef.current.parent_dbs.map((db) => {
12134
+ var _a2;
12135
+ return {
12136
+ id: db.db_id,
12137
+ name: ((_a2 = parentDataRef.current[db.db_id]) == null ? void 0 : _a2.dataset_name) || `Dataset #${db.db_id.substring(0, 6)}`
12138
+ };
12139
+ });
12140
+ }, [sceneVersion, isInitialized]);
12141
+ const sourceNodeDatasetId = creationMode.sourceNodeData ? (_b = stateRef.current.nodeIdToParentFileMap.get(String(creationMode.sourceNodeData.id))) == null ? void 0 : _b.parentFileId : null;
12142
+ const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
12047
12143
  (0, import_react23.useEffect)(() => {
12048
12144
  if (isInitialized && focusNodeId && !hasFocusedInitial) {
12049
12145
  const nodeObjects = stateRef.current.nodeObjects || {};
@@ -12133,10 +12229,13 @@ function XViewScene({
12133
12229
  style: { position: "absolute", left: `${formPosition.left}px`, top: `${formPosition.top}px`, opacity: formPosition.opacity, zIndex: 20, transition: "opacity 200ms ease-out" },
12134
12230
  refEl: formRef,
12135
12231
  existingTypes: existingNodeTypes,
12136
- initialColor: (_b = creationMode.sourceNodeData) == null ? void 0 : _b.color,
12137
- sourceTypes: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.type,
12232
+ initialColor: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.color,
12233
+ sourceTypes: (_d = creationMode.sourceNodeData) == null ? void 0 : _d.type,
12138
12234
  onIntensityChange: handleGhostNodeIntensityChange,
12139
- onUploadFile: upload_file_action
12235
+ onUploadFile: upload_file_action,
12236
+ availableDatasets,
12237
+ sourceNodeDatasetId,
12238
+ viewType: viewParams == null ? void 0 : viewParams.type
12140
12239
  }
12141
12240
  ),
12142
12241
  versionMode.isActive && /* @__PURE__ */ import_react23.default.createElement(
@@ -12151,8 +12250,8 @@ function XViewScene({
12151
12250
  onMentionClick: handleAddExistingNode,
12152
12251
  style: { position: "absolute", left: `${formPosition.left}px`, top: `${formPosition.top}px`, opacity: formPosition.opacity, zIndex: 20, transition: "opacity 200ms ease-out" },
12153
12252
  refEl: formRef,
12154
- fixedType: (_d = versionMode.sourceNodeData) == null ? void 0 : _d.type,
12155
- fixedColor: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.color,
12253
+ fixedType: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.type,
12254
+ fixedColor: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.color,
12156
12255
  onUploadFile: upload_file_action
12157
12256
  }
12158
12257
  ),
@@ -12261,7 +12360,8 @@ function XViewScene({
12261
12360
  onMentionClick: handleAddExistingNode,
12262
12361
  onIntensityChange: handleDetailNodeIntensityChange,
12263
12362
  onUploadFile: upload_file_action,
12264
- userRole: userPermissionRole
12363
+ userRole: userPermissionRole,
12364
+ currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
12265
12365
  }
12266
12366
  ),
12267
12367
  detailsLink && /* @__PURE__ */ import_react23.default.createElement(
@@ -12389,7 +12489,7 @@ function XViewScene({
12389
12489
  onClose: () => setIsImportModalOpen(false),
12390
12490
  onConfirm: handleConfirmImport,
12391
12491
  session,
12392
- parentDbs: ((_f = sceneDataRef.current) == null ? void 0 : _f.parent_dbs) || [],
12492
+ parentDbs: ((_g = sceneDataRef.current) == null ? void 0 : _g.parent_dbs) || [],
12393
12493
  onFetchAvailableFiles: import_parent_file_modal_get,
12394
12494
  currentViewName: viewParams == null ? void 0 : viewParams.name,
12395
12495
  currentAncestries: ancestryDataRef.current || []
package/dist/index.mjs CHANGED
@@ -811,7 +811,6 @@ var x_view_config = {
811
811
  ANCESTOR_HIGHLIGHT_COLOR: "#FFD700",
812
812
  BLOOM_EFFECT: { strength: 1.35, radius: 0.6, threshold: 0.52 },
813
813
  EMISSIVE_MULTIPLIER: {
814
- // AJUSTE: Valores agora representam ADIÇÃO de intensidade, não multiplicação
815
814
  BASE: 0,
816
815
  HOVER: 2,
817
816
  SELECTED: 1.5,
@@ -863,7 +862,6 @@ var computeNodeMaterialProps = (colorHex) => {
863
862
  return {
864
863
  color: srgb,
865
864
  emissive: emissiveColor
866
- // Intensity é controlada externamente
867
865
  };
868
866
  };
869
867
  var createNodeMesh = (nodeData, position, glowTexture) => {
@@ -1154,6 +1152,7 @@ var createMultipleLinkLines = (linksArray, sourceNodeMesh, targetNodeMesh, resol
1154
1152
  resolution,
1155
1153
  isCurved,
1156
1154
  isCurved,
1155
+ isCurved,
1157
1156
  curveOffset
1158
1157
  );
1159
1158
  line.userData = {
@@ -1530,12 +1529,12 @@ var userActionHandlers = {
1530
1529
  stateRef.current.creation = { isActive: true, sourceNodeData };
1531
1530
  const ghostGeometry = new THREE.SphereGeometry(1.5, 32, 32);
1532
1531
  const sourceColor = sourceNodeData.color || "#cccccc";
1533
- const { emissiveIntensity } = computeNodeMaterialProps(sourceColor);
1534
- const ghostColor = new THREE.Color(sourceColor);
1532
+ const { color: ghostColor, emissive: ghostEmissive } = computeNodeMaterialProps(sourceColor);
1535
1533
  const ghostMaterial = new THREE.MeshStandardMaterial({
1536
1534
  color: ghostColor,
1537
- emissive: ghostColor,
1538
- emissiveIntensity,
1535
+ emissive: ghostEmissive,
1536
+ emissiveIntensity: MIN_VISIBILITY_INTENSITY,
1537
+ // <-- Forçamos o brilho mínimo
1539
1538
  roughness: 0.6,
1540
1539
  metalness: 0,
1541
1540
  transparent: true,
@@ -1606,27 +1605,50 @@ var userActionHandlers = {
1606
1605
  setters.setFormPosition((p) => ({ ...p, opacity: 0 }));
1607
1606
  },
1608
1607
  handleSaveNode: async (context, newNodeData) => {
1609
- const { graphDataRef, sceneDataRef, stateRef, creationMode, setters } = context;
1608
+ const { graphDataRef, sceneDataRef, stateRef, creationMode, setters, actions } = context;
1610
1609
  if (!graphDataRef.current || !sceneDataRef.current) return;
1611
1610
  const { sourceNodeData } = creationMode;
1612
- const newNode = { id: short.generate(), ...newNodeData };
1611
+ const { targetDatasetId, ...nodeDataToSave } = newNodeData;
1612
+ const newNode = { id: short.generate(), ...nodeDataToSave };
1613
1613
  const newLink = { id: `link_${short.generate()}`, source: sourceNodeData.id, target: newNode.id };
1614
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id);
1615
- if (!parentInfo || !parentInfo.ownerId) {
1616
- console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node de origem:", sourceNodeData.id);
1617
- alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio.");
1614
+ const sourceParentInfo = stateRef.current.nodeIdToParentFileMap.get(String(sourceNodeData.id));
1615
+ const finalTargetDatasetId = targetDatasetId || sourceParentInfo.parentFileId;
1616
+ const targetParentInfo = sceneDataRef.current.parent_dbs.find((db) => String(db.db_id) === String(finalTargetDatasetId));
1617
+ if (!sourceParentInfo || !targetParentInfo) {
1618
+ alert("Erro ao identificar os datasets de origem ou destino.");
1618
1619
  return;
1619
1620
  }
1620
- const { parentFileId, ownerId } = parentInfo;
1621
- const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileId]));
1622
- specificParentData.nodes.push(newNode);
1623
- specificParentData.links.push(newLink);
1624
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
1621
+ const isCrossDataset = String(sourceParentInfo.parentFileId) !== String(finalTargetDatasetId);
1622
+ const sourceDataToUpdate = JSON.parse(JSON.stringify(graphDataRef.current[sourceParentInfo.parentFileId]));
1623
+ let targetDataToUpdate = isCrossDataset ? JSON.parse(JSON.stringify(graphDataRef.current[finalTargetDatasetId])) : sourceDataToUpdate;
1624
+ targetDataToUpdate.nodes.push(newNode);
1625
+ sourceDataToUpdate.links.push(newLink);
1626
+ const savePromises = [];
1627
+ if (isCrossDataset) {
1628
+ savePromises.push(
1629
+ actions.save_view_data(`x_view_dbs/${sourceParentInfo.ownerId}/${sourceParentInfo.parentFileId}`, sourceDataToUpdate)
1630
+ );
1631
+ savePromises.push(
1632
+ actions.save_view_data(`x_view_dbs/${targetParentInfo.owner_id}/${finalTargetDatasetId}`, targetDataToUpdate)
1633
+ );
1634
+ } else {
1635
+ savePromises.push(
1636
+ actions.save_view_data(`x_view_dbs/${sourceParentInfo.ownerId}/${sourceParentInfo.parentFileId}`, sourceDataToUpdate)
1637
+ );
1638
+ }
1625
1639
  try {
1626
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
1627
- graphDataRef.current[parentFileId] = specificParentData;
1640
+ await Promise.all(savePromises);
1641
+ graphDataRef.current[sourceParentInfo.parentFileId] = sourceDataToUpdate;
1642
+ if (isCrossDataset) {
1643
+ graphDataRef.current[finalTargetDatasetId] = targetDataToUpdate;
1644
+ }
1628
1645
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1629
1646
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
1647
+ stateRef.current.nodeIdToParentFileMap.set(String(newNode.id), {
1648
+ parentFileId: finalTargetDatasetId,
1649
+ ownerId: targetParentInfo.owner_id,
1650
+ datasetName: targetDataToUpdate.dataset_name || "Dataset Desconhecido"
1651
+ });
1630
1652
  setters.setSceneVersion((v) => v + 1);
1631
1653
  } catch (error) {
1632
1654
  console.error("Falha ao salvar os dados do grafo:", error);
@@ -1664,12 +1686,12 @@ var userActionHandlers = {
1664
1686
  stateRef.current.creation = { isActive: true, sourceNodeData };
1665
1687
  const ghostGeometry = new THREE.SphereGeometry(1.5, 32, 32);
1666
1688
  const sourceColor = sourceNodeData.color || "#cccccc";
1667
- const { emissiveIntensity } = computeNodeMaterialProps(sourceColor);
1668
- const ghostColor = new THREE.Color(sourceColor);
1689
+ const { color: ghostColor, emissive: ghostEmissive } = computeNodeMaterialProps(sourceColor);
1669
1690
  const ghostMaterial = new THREE.MeshStandardMaterial({
1670
1691
  color: ghostColor,
1671
- emissive: ghostColor,
1672
- emissiveIntensity,
1692
+ emissive: ghostEmissive,
1693
+ emissiveIntensity: MIN_VISIBILITY_INTENSITY,
1694
+ // <-- Forçamos o brilho mínimo
1673
1695
  roughness: 0.6,
1674
1696
  metalness: 0,
1675
1697
  transparent: true,
@@ -3723,6 +3745,7 @@ function DescriptionEditModal({
3723
3745
  e.preventDefault();
3724
3746
  e.stopPropagation();
3725
3747
  e.stopImmediatePropagation();
3748
+ if (hoverTimeoutRef.current) clearTimeout(hoverTimeoutRef.current);
3726
3749
  setIsMentionModalOpen(false);
3727
3750
  setMentionTriggerIndex(null);
3728
3751
  setTooltipData(null);
@@ -3816,6 +3839,7 @@ function DescriptionEditModal({
3816
3839
  setIsImportModalOpen(false);
3817
3840
  };
3818
3841
  const handleMentionSelect = (node) => {
3842
+ if (hoverTimeoutRef.current) clearTimeout(hoverTimeoutRef.current);
3819
3843
  const tag = `[[MENTION:node:${node.id}]]`;
3820
3844
  const el = textareaRef.current;
3821
3845
  if (mentionTriggerIndex !== null && el) {
@@ -4029,7 +4053,7 @@ function DescriptionEditModal({
4029
4053
  className: "px-3 py-1.5 bg-indigo-600/30 hover:bg-indigo-600 text-indigo-200 text-xs rounded-lg transition-colors disabled:opacity-50"
4030
4054
  },
4031
4055
  "OK"
4032
- ))), /* @__PURE__ */ React5.createElement("div", { className: "p-2 border-t border-white/10 bg-slate-950/50 flex justify-end" }, /* @__PURE__ */ React5.createElement("button", { onClick: () => setIsImageModalOpen(false), className: "px-3 py-1.5 text-xs text-slate-400 hover:text-white transition-colors" }, "Cancelar")))), /* @__PURE__ */ React5.createElement("p", { className: "mt-2 text-xs text-slate-500 shrink-0" }, "Dica: Tags agora incluem ID (ex: ", /* @__PURE__ */ React5.createElement("b", null, "*/1:a9f2/"), ") para garantir que refer\xEAncias n\xE3o quebrem se voc\xEA reordenar o texto.")),
4056
+ ))), /* @__PURE__ */ React5.createElement("div", { className: "p-2 border-t border-white/10 bg-slate-950/50 flex justify-end" }, /* @__PURE__ */ React5.createElement("button", { onClick: () => setIsImageModalOpen(false), className: "px-3 py-1.5 text-xs text-slate-400 hover:text-white transition-colors" }, "Cancelar"))))),
4033
4057
  /* @__PURE__ */ React5.createElement("div", { className: "sticky bottom-0 z-10 bg-slate-950/95 border-t border-white/10 px-6 py-4 flex justify-end gap-3 shrink-0" }, /* @__PURE__ */ React5.createElement("button", { onClick: handleSave, className: "px-6 py-2 rounded-lg bg-gradient-to-tr from-indigo-600 to-indigo-400 hover:from-indigo-500 hover:to-indigo-300 transition-colors font-semibold text-sm shadow-[0_8px_24px_rgba(99,102,241,0.35)]" }, "Salvar"))
4034
4058
  )), tooltipData && /* @__PURE__ */ React5.createElement(
4035
4059
  "div",
@@ -4519,7 +4543,6 @@ function DescriptionReadModePanel({
4519
4543
  title,
4520
4544
  description,
4521
4545
  ancestryId,
4522
- // <-- NOVO: Prop recebida do XViewScene
4523
4546
  savedSections,
4524
4547
  onBack,
4525
4548
  onEdit,
@@ -4628,7 +4651,7 @@ function DescriptionReadModePanel({
4628
4651
  return /* @__PURE__ */ React7.createElement(
4629
4652
  "div",
4630
4653
  {
4631
- className: "flex flex-col h-full w-full bg-slate-950/50 relative overflow-hidden group",
4654
+ className: "flex flex-col h-full max-h-full w-full bg-slate-950/50 relative overflow-hidden group min-h-0",
4632
4655
  onPointerDown: swallow,
4633
4656
  onClick: swallow
4634
4657
  },
@@ -4700,7 +4723,7 @@ function DescriptionReadModePanel({
4700
4723
  },
4701
4724
  "\xD7"
4702
4725
  ))),
4703
- /* @__PURE__ */ React7.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-6 bg-slate-900/20 relative z-10", onClick: () => setTargetRenderNodeId(null) }, showAbstraction ? /* @__PURE__ */ React7.createElement("div", { className: "space-y-4 animate-in fade-in slide-in-from-bottom-2 duration-300", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React7.createElement("div", { className: "flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-4" }, /* @__PURE__ */ React7.createElement("h3", { className: "text-sm font-semibold text-purple-300 uppercase tracking-wider" }, "Explorar Hierarquia"), /* @__PURE__ */ React7.createElement("div", { className: "flex flex-wrap gap-2" }, targetRenderNodeId && onRenderAbstractionTree && /* @__PURE__ */ React7.createElement(
4726
+ /* @__PURE__ */ React7.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-6 bg-slate-900/20 relative z-10 min-h-0", onClick: () => setTargetRenderNodeId(null) }, showAbstraction ? /* @__PURE__ */ React7.createElement("div", { className: "space-y-4 animate-in fade-in slide-in-from-bottom-2 duration-300", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React7.createElement("div", { className: "flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-4" }, /* @__PURE__ */ React7.createElement("h3", { className: "text-sm font-semibold text-purple-300 uppercase tracking-wider" }, "Explorar Hierarquia"), /* @__PURE__ */ React7.createElement("div", { className: "flex flex-wrap gap-2" }, targetRenderNodeId && onRenderAbstractionTree && /* @__PURE__ */ React7.createElement(
4704
4727
  "button",
4705
4728
  {
4706
4729
  onClick: (e) => {
@@ -5242,6 +5265,7 @@ function CreateAncestryPanel({
5242
5265
  const branchProgressMapRef = useRef8({});
5243
5266
  const [lastSavedSnapshot, setLastSavedSnapshot] = useState10(null);
5244
5267
  const [isPrivate, setIsPrivate] = useState10(ancestryMode.is_private || false);
5268
+ const initializedContextIdRef = useRef8(null);
5245
5269
  const availableImages = customProps.filter((p) => p.type === "images").flatMap((p) => Array.isArray(p.value) ? p.value : []).filter((img) => img.value && img.value.trim() !== "");
5246
5270
  const handleImageClickFromText = (url, name) => {
5247
5271
  if (onOpenImageViewer) {
@@ -5440,6 +5464,11 @@ function CreateAncestryPanel({
5440
5464
  }, [isContextLinked, branchStack]);
5441
5465
  useEffect10(() => {
5442
5466
  const ctx = getCurrentContext();
5467
+ const currentContextId = branchStack.length > 0 ? branchStack[branchStack.length - 1].branchId : ancestryMode.currentAncestryId || `new_${ancestryMode.ancestral_node}`;
5468
+ if (initializedContextIdRef.current === currentContextId) {
5469
+ return;
5470
+ }
5471
+ initializedContextIdRef.current = currentContextId;
5443
5472
  let sourceObject = {};
5444
5473
  if (ctx) {
5445
5474
  sourceObject = ctx;
@@ -5790,7 +5819,7 @@ function CreateAncestryPanel({
5790
5819
  const currentAbsTreeStr = JSON.stringify(ancestryMode.abstraction_tree);
5791
5820
  abstractionTreeChanged = currentAbsTreeStr !== lastSavedSnapshot.abstractionTree;
5792
5821
  }
5793
- return treeChanged || nameChanged || descChanged || sectionsChanged || propsChanged || privateChanged;
5822
+ return treeChanged || nameChanged || descChanged || sectionsChanged || propsChanged || privateChanged || abstractionTreeChanged;
5794
5823
  }, [
5795
5824
  ancestryName,
5796
5825
  description,
@@ -6740,7 +6769,7 @@ function ColorPicker({ color, onChange, disabled }) {
6740
6769
  }
6741
6770
 
6742
6771
  // src/components/InSceneCreationForm.jsx
6743
- import { FiPlus as FiPlus3, FiMaximize2, FiX as FiX3, FiCheck as FiCheck7, FiEdit2 as FiEdit24, FiSun } from "react-icons/fi";
6772
+ import { FiPlus as FiPlus3, FiMaximize2, FiX as FiX3, FiCheck as FiCheck7, FiEdit2 as FiEdit24, FiSun, FiChevronDown as FiChevronDown4 } from "react-icons/fi";
6744
6773
  function InSceneCreationForm({
6745
6774
  onSave,
6746
6775
  onCancel,
@@ -6758,8 +6787,12 @@ function InSceneCreationForm({
6758
6787
  availableAncestries = [],
6759
6788
  onMentionClick,
6760
6789
  sourceTypes,
6761
- onUploadFile
6790
+ onUploadFile,
6791
+ availableDatasets = [],
6792
+ sourceNodeDatasetId,
6793
+ viewType
6762
6794
  }) {
6795
+ var _a;
6763
6796
  const [name, setName] = useState13("");
6764
6797
  const [types, setTypes] = useState13([]);
6765
6798
  const [typeInput, setTypeInput] = useState13("");
@@ -6773,6 +6806,25 @@ function InSceneCreationForm({
6773
6806
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState13(false);
6774
6807
  const [useImageAsTexture, setUseImageAsTexture] = useState13(false);
6775
6808
  const [selectedImageUrl, setSelectedImageUrl] = useState13(null);
6809
+ const [targetDatasetId, setTargetDatasetId] = useState13(sourceNodeDatasetId || "");
6810
+ const [isDatasetDropdownOpen, setIsDatasetDropdownOpen] = useState13(false);
6811
+ const datasetDropdownRef = useRef10(null);
6812
+ useEffect13(() => {
6813
+ if (sourceNodeDatasetId) setTargetDatasetId(sourceNodeDatasetId);
6814
+ }, [sourceNodeDatasetId]);
6815
+ useEffect13(() => {
6816
+ function handleClickOutside(event) {
6817
+ if (datasetDropdownRef.current && !datasetDropdownRef.current.contains(event.target)) {
6818
+ setIsDatasetDropdownOpen(false);
6819
+ }
6820
+ }
6821
+ if (isDatasetDropdownOpen) {
6822
+ document.addEventListener("mousedown", handleClickOutside);
6823
+ }
6824
+ return () => {
6825
+ document.removeEventListener("mousedown", handleClickOutside);
6826
+ };
6827
+ }, [isDatasetDropdownOpen]);
6776
6828
  const propsEndRef = useRef10(null);
6777
6829
  const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
6778
6830
  useEffect13(() => {
@@ -6818,8 +6870,8 @@ function InSceneCreationForm({
6818
6870
  const newProp = createNewCustomProperty(customProps);
6819
6871
  setCustomProps([...customProps, newProp]);
6820
6872
  setTimeout(() => {
6821
- var _a;
6822
- (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
6873
+ var _a2;
6874
+ (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
6823
6875
  }, 100);
6824
6876
  };
6825
6877
  const handleRemoveProp = (index) => setCustomProps(customProps.filter((_, i) => i !== index));
@@ -6852,12 +6904,12 @@ function InSceneCreationForm({
6852
6904
  onSizeChange == null ? void 0 : onSizeChange(newSize);
6853
6905
  };
6854
6906
  const handleToggleImageMode = () => {
6855
- var _a, _b;
6907
+ var _a2, _b;
6856
6908
  const newValue = !useImageAsTexture;
6857
6909
  setUseImageAsTexture(newValue);
6858
6910
  if (newValue) {
6859
6911
  const firstImageProp = customProps.find((p) => p.type === "images");
6860
- if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
6912
+ if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
6861
6913
  const url = firstImageProp.value[0].value;
6862
6914
  setSelectedImageUrl(url);
6863
6915
  onImageChange == null ? void 0 : onImageChange(true, url);
@@ -6890,6 +6942,7 @@ function InSceneCreationForm({
6890
6942
  description_sections: processedSections,
6891
6943
  useImageAsTexture,
6892
6944
  textureImageUrl: useImageAsTexture ? selectedImageUrl : null,
6945
+ targetDatasetId,
6893
6946
  ...additionalData
6894
6947
  });
6895
6948
  };
@@ -6910,6 +6963,7 @@ function InSceneCreationForm({
6910
6963
  onOpenImageViewer([{ name: name2 || "Imagem", value: url }], 0);
6911
6964
  }
6912
6965
  };
6966
+ const selectedDatasetName = ((_a = availableDatasets.find((ds) => ds.id === targetDatasetId)) == null ? void 0 : _a.name) || "Selecione um Dataset...";
6913
6967
  return /* @__PURE__ */ React13.createElement(React13.Fragment, null, /* @__PURE__ */ React13.createElement(
6914
6968
  "div",
6915
6969
  {
@@ -6967,7 +7021,27 @@ function InSceneCreationForm({
6967
7021
  }
6968
7022
  },
6969
7023
  suggestedType
6970
- ))))), /* @__PURE__ */ React13.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React13.createElement("label", { className: "text-xs text-slate-300" }, "Nome do Node"), /* @__PURE__ */ React13.createElement("input", { required: true, type: "text", placeholder: "Ex.: Cliente XPTO", value: name, onChange: handleNameInputChange, className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60" })), /* @__PURE__ */ React13.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React13.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o (Opcional)"), /* @__PURE__ */ React13.createElement("div", { className: "relative group min-h-[80px] bg-slate-800/70 p-2.5 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React13.createElement(
7024
+ ))))), /* @__PURE__ */ React13.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React13.createElement("label", { className: "text-xs text-slate-300" }, "Nome do Node"), /* @__PURE__ */ React13.createElement("input", { required: true, type: "text", placeholder: "Ex.: Cliente XPTO", value: name, onChange: handleNameInputChange, className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60" })), viewType === "view" && availableDatasets.length > 0 && /* @__PURE__ */ React13.createElement("div", { className: "space-y-1.5 relative", ref: datasetDropdownRef }, /* @__PURE__ */ React13.createElement("label", { className: "text-xs text-slate-300" }, "Criar Node no Dataset:"), /* @__PURE__ */ React13.createElement(
7025
+ "button",
7026
+ {
7027
+ type: "button",
7028
+ onClick: () => setIsDatasetDropdownOpen(!isDatasetDropdownOpen),
7029
+ className: "w-full flex items-center justify-between bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60 hover:bg-slate-700/70 transition-colors text-slate-200 text-left"
7030
+ },
7031
+ /* @__PURE__ */ React13.createElement("span", { className: "truncate pr-2" }, selectedDatasetName),
7032
+ /* @__PURE__ */ React13.createElement(FiChevronDown4, { className: `flex-shrink-0 text-slate-400 transition-transform ${isDatasetDropdownOpen ? "rotate-180" : ""}` })
7033
+ ), isDatasetDropdownOpen && /* @__PURE__ */ React13.createElement("ul", { className: "custom-scrollbar absolute top-[66px] left-0 z-20 w-full max-h-48 overflow-y-auto rounded-lg bg-slate-800 border border-white/10 shadow-xl py-1" }, availableDatasets.map((ds) => /* @__PURE__ */ React13.createElement(
7034
+ "li",
7035
+ {
7036
+ key: ds.id,
7037
+ onClick: () => {
7038
+ setTargetDatasetId(ds.id);
7039
+ setIsDatasetDropdownOpen(false);
7040
+ },
7041
+ className: `px-3 py-2 text-sm cursor-pointer transition-colors ${targetDatasetId === ds.id ? "bg-indigo-600/40 text-indigo-200 font-medium" : "text-slate-300 hover:bg-white/5"}`
7042
+ },
7043
+ ds.name
7044
+ )))), /* @__PURE__ */ React13.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React13.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o (Opcional)"), /* @__PURE__ */ React13.createElement("div", { className: "relative group min-h-[80px] bg-slate-800/70 p-2.5 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React13.createElement(
6971
7045
  DescriptionDisplay,
6972
7046
  {
6973
7047
  description,
@@ -7285,7 +7359,7 @@ function InSceneVersionForm({
7285
7359
 
7286
7360
  // src/components/NodeDetailsPanel.jsx
7287
7361
  import React15, { useState as useState15, useEffect as useEffect15, useRef as useRef12 } from "react";
7288
- import { FiPlus as FiPlus5, FiMaximize2 as FiMaximize23, FiX as FiX4, FiCheck as FiCheck9, FiImage as FiImage3, FiEdit2 as FiEdit26, FiLoader as FiLoader2, FiBookOpen as FiBookOpen3, FiSun as FiSun2, FiLink as FiLink5 } from "react-icons/fi";
7362
+ import { FiPlus as FiPlus5, FiMaximize2 as FiMaximize23, FiX as FiX4, FiCheck as FiCheck9, FiImage as FiImage3, FiEdit2 as FiEdit26, FiLoader as FiLoader2, FiBookOpen as FiBookOpen3, FiSun as FiSun2, FiLink as FiLink5, FiDatabase } from "react-icons/fi";
7289
7363
  function NodeDetailsPanel({
7290
7364
  node,
7291
7365
  onClose,
@@ -7303,7 +7377,8 @@ function NodeDetailsPanel({
7303
7377
  onMentionClick,
7304
7378
  onIntensityChange,
7305
7379
  onUploadFile,
7306
- userRole
7380
+ userRole,
7381
+ currentDatasetName
7307
7382
  }) {
7308
7383
  const [name, setName] = useState15((node == null ? void 0 : node.name) ?? "");
7309
7384
  const [types, setTypes] = useState15([]);
@@ -7533,10 +7608,10 @@ function NodeDetailsPanel({
7533
7608
  return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(
7534
7609
  "div",
7535
7610
  {
7536
- className: `ui-overlay absolute group rounded-2xl border border-white/10 bg-slate-950/70 backdrop-blur-xl shadow-[0_20px_80px_rgba(0,0,0,0.6)] ring-1 ring-white/10 text-white overflow-hidden transition-all duration-300 ease-out
7611
+ className: `ui-overlay absolute group rounded-2xl border border-white/10 bg-slate-950/70 backdrop-blur-xl shadow-[0_20px_80px_rgba(0,0,0,0.6)] ring-1 ring-white/10 text-white overflow-hidden flex flex-col transition-all duration-300 ease-out
7537
7612
  ${isReadMode ? "w-[min(92vw,700px)]" : "w-[min(92vw,440px)]"}
7538
7613
  `,
7539
- style: { top: 16, right: 16, zIndex: 1100 },
7614
+ style: { top: 16, right: 16, zIndex: 1100, maxHeight: "calc(100vh - 32px)" },
7540
7615
  onPointerDown: swallow,
7541
7616
  onPointerMove: swallow,
7542
7617
  onPointerUp: swallow,
@@ -7714,7 +7789,7 @@ function NodeDetailsPanel({
7714
7789
  onUploadFile: canEdit ? onUploadFile : void 0,
7715
7790
  readOnly: !canEdit
7716
7791
  }
7717
- )), /* @__PURE__ */ React15.createElement("div", { ref: propsEndRef })))), /* @__PURE__ */ React15.createElement("div", { className: "sticky bottom-0 z-10 bg-gradient-to-t from-slate-950/80 via-slate-950/50 to-transparent px-6 py-4 border-t border-white/10 flex justify-end gap-3" }, /* @__PURE__ */ React15.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "px-4 py-2 rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-sm disabled:opacity-50" }, canEdit ? "Cancelar" : "Fechar"), canEdit && /* @__PURE__ */ React15.createElement(
7792
+ )), /* @__PURE__ */ React15.createElement("div", { ref: propsEndRef }))), currentDatasetName && /* @__PURE__ */ React15.createElement("div", { className: "pt-3 mt-4 border-t border-white/10 flex items-center justify-end gap-2 text-xs text-slate-400" }, /* @__PURE__ */ React15.createElement("span", { className: "truncate text-right" }, /* @__PURE__ */ React15.createElement("span", { className: "text-slate-200 font-medium" }, currentDatasetName)))), /* @__PURE__ */ React15.createElement("div", { className: "sticky bottom-0 z-10 bg-gradient-to-t from-slate-950/80 via-slate-950/50 to-transparent px-6 py-4 border-t border-white/10 flex justify-end gap-3" }, /* @__PURE__ */ React15.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "px-4 py-2 rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-sm disabled:opacity-50" }, canEdit ? "Cancelar" : "Fechar"), canEdit && /* @__PURE__ */ React15.createElement(
7718
7793
  "button",
7719
7794
  {
7720
7795
  onClick: () => handleSave(false),
@@ -9048,7 +9123,7 @@ function XViewScene({
9048
9123
  delete_file_action,
9049
9124
  check_user_permission
9050
9125
  }) {
9051
- var _a, _b, _c, _d, _e, _f;
9126
+ var _a, _b, _c, _d, _e, _f, _g;
9052
9127
  const { data: session, status } = useSession();
9053
9128
  const router = useRouter();
9054
9129
  const searchParams = useSearchParams();
@@ -9200,9 +9275,10 @@ function XViewScene({
9200
9275
  const parentFile = allParentData[parentFileId];
9201
9276
  const parentDbInfo = parentDbsArray.find((db) => String(db.db_id) === String(parentFileId));
9202
9277
  const ownerId2 = (parentDbInfo == null ? void 0 : parentDbInfo.owner_id) || null;
9278
+ const datasetName = parentFile.dataset_name || `Dataset #${parentFileId.substring(0, 6)}`;
9203
9279
  if (parentFile.nodes && ownerId2) {
9204
9280
  for (const node of parentFile.nodes) {
9205
- map.set(String(node.id), { parentFileId, ownerId: ownerId2 });
9281
+ map.set(String(node.id), { parentFileId, ownerId: ownerId2, datasetName });
9206
9282
  }
9207
9283
  }
9208
9284
  }
@@ -9722,6 +9798,9 @@ function XViewScene({
9722
9798
  sceneDataRef.current = sceneResponse.data.scene;
9723
9799
  parentDataRef.current = sceneResponse.data.parent;
9724
9800
  ancestryDataRef.current = sceneResponse.data.ancestry;
9801
+ console.log("Console de sceneResponse.data.scene:", sceneResponse.data.scene);
9802
+ console.log("Console de sceneResponse.data.parent:", sceneResponse.data.parent);
9803
+ console.log("Console de sceneResponse.data.ancestry:", sceneResponse.data.ancestry);
9725
9804
  setIsInitialized(true);
9726
9805
  } else {
9727
9806
  console.error("Falha ao buscar dados da cena:", (sceneResponse == null ? void 0 : sceneResponse.error) || "Resposta inv\xE1lida.");
@@ -10503,32 +10582,33 @@ function XViewScene({
10503
10582
  const handleGhostNodeColorChange = (newColor) => {
10504
10583
  const { node: ghostNode, aura: ghostAura } = stateRef.current.ghostElements;
10505
10584
  if (!ghostNode) return;
10506
- const { emissiveIntensity } = computeNodeMaterialProps(newColor);
10507
- const pureColor = new THREE3.Color(newColor);
10585
+ const { color, emissive } = computeNodeMaterialProps(newColor);
10586
+ const currentIntensity = ghostNode.userData.intensity !== void 0 ? Number(ghostNode.userData.intensity) : 0;
10587
+ const finalIntensity = currentIntensity + MIN_VISIBILITY_INTENSITY;
10508
10588
  const isImageNode = ghostNode.userData.useImageAsTexture === true || String(ghostNode.userData.useImageAsTexture) === "true";
10509
10589
  if (isImageNode) {
10510
10590
  const borderMesh = ghostNode.getObjectByName("borderRing");
10511
10591
  if (borderMesh && borderMesh.material) {
10512
- borderMesh.material.color.copy(pureColor);
10592
+ borderMesh.material.color.copy(color);
10513
10593
  if (borderMesh.material.emissive) {
10514
- borderMesh.material.emissive.copy(pureColor);
10515
- borderMesh.material.emissiveIntensity = emissiveIntensity;
10594
+ borderMesh.material.emissive.copy(emissive);
10595
+ borderMesh.material.emissiveIntensity = finalIntensity;
10516
10596
  }
10517
10597
  }
10518
10598
  } else {
10519
10599
  if (ghostNode.material) {
10520
- ghostNode.material.color.copy(pureColor);
10600
+ ghostNode.material.color.copy(color);
10521
10601
  if (ghostNode.material.emissive) {
10522
- ghostNode.material.emissive.copy(pureColor);
10523
- ghostNode.material.emissiveIntensity = emissiveIntensity;
10602
+ ghostNode.material.emissive.copy(emissive);
10603
+ ghostNode.material.emissiveIntensity = finalIntensity;
10524
10604
  }
10525
10605
  }
10526
10606
  }
10527
10607
  if (ghostAura && ghostAura.material) {
10528
- ghostAura.material.color.set(newColor);
10608
+ ghostAura.material.color.copy(color).lerp(new THREE3.Color("#ffffff"), 0.25);
10529
10609
  }
10530
10610
  ghostNode.userData.color = newColor;
10531
- ghostNode.userData._baseEmissiveIntensity = emissiveIntensity;
10611
+ ghostNode.userData._baseEmissiveIntensity = finalIntensity;
10532
10612
  };
10533
10613
  const handleGhostNodeSizeChange = (sizeKey) => {
10534
10614
  const { node: ghostNode } = stateRef.current.ghostElements;
@@ -10569,7 +10649,9 @@ function XViewScene({
10569
10649
  var _a2;
10570
10650
  const mesh = stateRef.current.nodeObjects[String(nodeId)];
10571
10651
  if (!mesh) return;
10572
- const { color, emissive, emissiveIntensity } = computeNodeMaterialProps(newColor);
10652
+ const { color, emissive } = computeNodeMaterialProps(newColor);
10653
+ const currentIntensity = mesh.userData.intensity !== void 0 ? Number(mesh.userData.intensity) : 0;
10654
+ const finalIntensity = currentIntensity + MIN_VISIBILITY_INTENSITY;
10573
10655
  const isImageNode = mesh.userData.useImageAsTexture === true || String(mesh.userData.useImageAsTexture) === "true";
10574
10656
  if (isImageNode) {
10575
10657
  const borderMesh = mesh.getObjectByName("borderRing");
@@ -10577,20 +10659,22 @@ function XViewScene({
10577
10659
  borderMesh.material.color.copy(color);
10578
10660
  if (borderMesh.material.emissive) {
10579
10661
  borderMesh.material.emissive.copy(emissive);
10580
- borderMesh.material.emissiveIntensity = emissiveIntensity;
10662
+ borderMesh.material.emissiveIntensity = finalIntensity;
10581
10663
  }
10582
10664
  }
10583
10665
  } else {
10584
10666
  if (mesh.material) {
10585
10667
  mesh.material.color.copy(color);
10586
10668
  mesh.material.emissive.copy(emissive);
10587
- mesh.material.emissiveIntensity = emissiveIntensity;
10669
+ mesh.material.emissiveIntensity = finalIntensity;
10588
10670
  }
10589
10671
  }
10590
10672
  const aura = mesh.getObjectByName("aura");
10591
- if ((_a2 = aura == null ? void 0 : aura.material) == null ? void 0 : _a2.color) aura.material.color.set(newColor);
10673
+ if ((_a2 = aura == null ? void 0 : aura.material) == null ? void 0 : _a2.color) {
10674
+ aura.material.color.copy(color).lerp(new THREE3.Color("#ffffff"), 0.25);
10675
+ }
10592
10676
  mesh.userData.color = newColor;
10593
- mesh.userData._baseEmissiveIntensity = emissiveIntensity;
10677
+ mesh.userData._baseEmissiveIntensity = finalIntensity;
10594
10678
  };
10595
10679
  const handleDetailNodeSizeChange = (nodeId, newSize) => {
10596
10680
  const mesh = stateRef.current.nodeObjects[String(nodeId)];
@@ -12043,6 +12127,18 @@ function XViewScene({
12043
12127
  tweenToTarget(nodeMesh, 1.2);
12044
12128
  }
12045
12129
  }, [tweenToTarget]);
12130
+ const availableDatasets = useMemo12(() => {
12131
+ if (!sceneDataRef.current || !parentDataRef.current) return [];
12132
+ return sceneDataRef.current.parent_dbs.map((db) => {
12133
+ var _a2;
12134
+ return {
12135
+ id: db.db_id,
12136
+ name: ((_a2 = parentDataRef.current[db.db_id]) == null ? void 0 : _a2.dataset_name) || `Dataset #${db.db_id.substring(0, 6)}`
12137
+ };
12138
+ });
12139
+ }, [sceneVersion, isInitialized]);
12140
+ const sourceNodeDatasetId = creationMode.sourceNodeData ? (_b = stateRef.current.nodeIdToParentFileMap.get(String(creationMode.sourceNodeData.id))) == null ? void 0 : _b.parentFileId : null;
12141
+ const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
12046
12142
  useEffect21(() => {
12047
12143
  if (isInitialized && focusNodeId && !hasFocusedInitial) {
12048
12144
  const nodeObjects = stateRef.current.nodeObjects || {};
@@ -12132,10 +12228,13 @@ function XViewScene({
12132
12228
  style: { position: "absolute", left: `${formPosition.left}px`, top: `${formPosition.top}px`, opacity: formPosition.opacity, zIndex: 20, transition: "opacity 200ms ease-out" },
12133
12229
  refEl: formRef,
12134
12230
  existingTypes: existingNodeTypes,
12135
- initialColor: (_b = creationMode.sourceNodeData) == null ? void 0 : _b.color,
12136
- sourceTypes: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.type,
12231
+ initialColor: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.color,
12232
+ sourceTypes: (_d = creationMode.sourceNodeData) == null ? void 0 : _d.type,
12137
12233
  onIntensityChange: handleGhostNodeIntensityChange,
12138
- onUploadFile: upload_file_action
12234
+ onUploadFile: upload_file_action,
12235
+ availableDatasets,
12236
+ sourceNodeDatasetId,
12237
+ viewType: viewParams == null ? void 0 : viewParams.type
12139
12238
  }
12140
12239
  ),
12141
12240
  versionMode.isActive && /* @__PURE__ */ React23.createElement(
@@ -12150,8 +12249,8 @@ function XViewScene({
12150
12249
  onMentionClick: handleAddExistingNode,
12151
12250
  style: { position: "absolute", left: `${formPosition.left}px`, top: `${formPosition.top}px`, opacity: formPosition.opacity, zIndex: 20, transition: "opacity 200ms ease-out" },
12152
12251
  refEl: formRef,
12153
- fixedType: (_d = versionMode.sourceNodeData) == null ? void 0 : _d.type,
12154
- fixedColor: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.color,
12252
+ fixedType: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.type,
12253
+ fixedColor: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.color,
12155
12254
  onUploadFile: upload_file_action
12156
12255
  }
12157
12256
  ),
@@ -12260,7 +12359,8 @@ function XViewScene({
12260
12359
  onMentionClick: handleAddExistingNode,
12261
12360
  onIntensityChange: handleDetailNodeIntensityChange,
12262
12361
  onUploadFile: upload_file_action,
12263
- userRole: userPermissionRole
12362
+ userRole: userPermissionRole,
12363
+ currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
12264
12364
  }
12265
12365
  ),
12266
12366
  detailsLink && /* @__PURE__ */ React23.createElement(
@@ -12388,7 +12488,7 @@ function XViewScene({
12388
12488
  onClose: () => setIsImportModalOpen(false),
12389
12489
  onConfirm: handleConfirmImport,
12390
12490
  session,
12391
- parentDbs: ((_f = sceneDataRef.current) == null ? void 0 : _f.parent_dbs) || [],
12491
+ parentDbs: ((_g = sceneDataRef.current) == null ? void 0 : _g.parent_dbs) || [],
12392
12492
  onFetchAvailableFiles: import_parent_file_modal_get,
12393
12493
  currentViewName: viewParams == null ? void 0 : viewParams.name,
12394
12494
  currentAncestries: ancestryDataRef.current || []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lv-x-software-house/x_view",
3
- "version": "1.2.2-dev.1",
3
+ "version": "1.2.2-dev.11",
4
4
  "description": "Pacote privado contendo os componentes e lógica de renderização 3D do X View.",
5
5
  "author": "iv.x - Engenharia de Software - ivxsoftwarehouse@gmail.com",
6
6
  "license": "UNLICENSED",