@lv-x-software-house/x_view 1.2.4-dev.2 → 1.2.4-dev.5

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 +107 -28
  2. package/dist/index.mjs +114 -35
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7969,7 +7969,7 @@ function InSceneQuestForm({
7969
7969
  /* @__PURE__ */ import_react16.default.createElement("option", { value: "In Progress" }, "In Progress"),
7970
7970
  /* @__PURE__ */ import_react16.default.createElement("option", { value: "Review" }, "Review"),
7971
7971
  /* @__PURE__ */ import_react16.default.createElement("option", { value: "Done" }, "Done")
7972
- )), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Quest"), /* @__PURE__ */ import_react16.default.createElement("input", { required: true, type: "text", placeholder: "Ex.: Refatorar M\xF3dulo X", value: name, onChange: (e) => setName(e.target.value), 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_react16.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 focus-within:ring-2 focus-within:ring-indigo-400/60 transition-all" }, types.map((t, index) => /* @__PURE__ */ import_react16.default.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, t !== "quest" && /* @__PURE__ */ import_react16.default.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ import_react16.default.createElement(FiX, { size: 12 })))), /* @__PURE__ */ import_react16.default.createElement(
7972
+ )), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Quest"), /* @__PURE__ */ import_react16.default.createElement("input", { required: true, type: "text", placeholder: "Ex.: Refatorar M\xF3dulo X", value: name, onChange: (e) => setName(e.target.value), 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_react16.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 focus-within:ring-2 focus-within:ring-indigo-400/60 transition-all" }, types.map((t, index) => /* @__PURE__ */ import_react16.default.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, t !== "quest" && /* @__PURE__ */ import_react16.default.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiX, { size: 12 })))), /* @__PURE__ */ import_react16.default.createElement(
7973
7973
  "input",
7974
7974
  {
7975
7975
  type: "text",
@@ -10872,20 +10872,6 @@ function XViewScene({
10872
10872
  const context = actionHandlerContext;
10873
10873
  if (connection.isActive) {
10874
10874
  if (hoveredNode && String(hoveredNode.userData.id) !== String(connection.sourceNodeData.id)) {
10875
- const sourceId = String(connection.sourceNodeData.id);
10876
- const targetId = String(hoveredNode.userData.id);
10877
- let parentInfo = stateRef.current.nodeIdToParentFileMap.get(sourceId);
10878
- if (!parentInfo || !parentInfo.ownerId) {
10879
- parentInfo = stateRef.current.nodeIdToParentFileMap.get(targetId);
10880
- if (parentInfo && parentInfo.ownerId) {
10881
- stateRef.current.nodeIdToParentFileMap.set(sourceId, parentInfo);
10882
- } else {
10883
- console.error("Nenhum dos Nodes possui um Dataset pai v\xE1lido para salvar a conex\xE3o.");
10884
- alert("N\xE3o \xE9 poss\xEDvel conectar dois Nodes de Quest diretamente sem um Dataset pai, ou ocorreu um erro.");
10885
- userActionHandlers.handleCancelConnection(context);
10886
- return;
10887
- }
10888
- }
10889
10875
  await userActionHandlers.handleCompleteConnection(context, hoveredNode.userData);
10890
10876
  } else {
10891
10877
  userActionHandlers.handleCancelConnection(context);
@@ -11491,6 +11477,10 @@ function XViewScene({
11491
11477
  // <-- Adicionado
11492
11478
  sceneSaveUrl,
11493
11479
  // <-- Adicionado
11480
+ sceneConfigId,
11481
+ // <-- Adicionado
11482
+ ownerId,
11483
+ // <-- Adicionado
11494
11484
  viewType: viewParams == null ? void 0 : viewParams.type,
11495
11485
  // <-- Adicionado
11496
11486
  userId: (_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id,
@@ -11524,9 +11514,11 @@ function XViewScene({
11524
11514
  versionMode,
11525
11515
  questMode,
11526
11516
  sceneSaveUrl,
11517
+ sceneConfigId,
11518
+ ownerId,
11527
11519
  viewParams == null ? void 0 : viewParams.type,
11528
- tweenToTarget,
11529
11520
  (_a = session == null ? void 0 : session.user) == null ? void 0 : _a.id,
11521
+ tweenToTarget,
11530
11522
  handleVersionTimeline,
11531
11523
  save_view_data,
11532
11524
  get_single_parent_file,
@@ -11539,32 +11531,110 @@ function XViewScene({
11539
11531
  userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
11540
11532
  };
11541
11533
  const handleSaveQuestNode = async (context, newQuestData) => {
11542
- const { sceneDataRef: sceneDataRef2, stateRef: stateRef2, setters, actions, sceneSaveUrl: sceneSaveUrl2, viewType } = context;
11543
- if (!sceneDataRef2.current || (viewType == null ? void 0 : viewType.toLowerCase()) !== "view") return;
11534
+ const { graphDataRef, sceneDataRef: sceneDataRef2, stateRef: stateRef2, setters, actions, sceneSaveUrl: sceneSaveUrl2, viewType, sceneConfigId: sceneConfigId2, ownerId: ownerId2 } = context;
11535
+ if (!graphDataRef.current || (viewType == null ? void 0 : viewType.toLowerCase()) !== "view") return;
11544
11536
  const newNode = {
11545
11537
  id: import_short_uuid2.default.generate(),
11546
11538
  ...newQuestData,
11547
11539
  type: ["quest", ...newQuestData.type.filter((t) => t !== "quest")]
11548
11540
  };
11549
- const updatedSceneData = {
11550
- ...sceneDataRef2.current,
11551
- nodes: [...sceneDataRef2.current.nodes, newNode]
11541
+ if (!graphDataRef.current[sceneConfigId2]) {
11542
+ graphDataRef.current[sceneConfigId2] = { nodes: [], links: [] };
11543
+ }
11544
+ graphDataRef.current[sceneConfigId2].nodes.push(newNode);
11545
+ sceneDataRef2.current.nodes.push(newNode);
11546
+ const currentVisualNodes = Object.values(stateRef2.current.nodeObjects).map((mesh) => {
11547
+ const { _baseEmissiveIntensity, labelObject, labelOffset, timelineIntervalBar, timelineEndLabel, ...rest } = mesh.userData;
11548
+ return rest;
11549
+ });
11550
+ currentVisualNodes.push(newNode);
11551
+ const currentVisualLinks = stateRef2.current.allLinks.map((line) => {
11552
+ const { sourceNode, targetNode, ...rest } = line.userData;
11553
+ return rest;
11554
+ });
11555
+ const sceneFileData = {
11556
+ parent_dbs: sceneDataRef2.current.parent_dbs,
11557
+ nodes: currentVisualNodes,
11558
+ links: currentVisualLinks,
11559
+ quest_nodes: graphDataRef.current[sceneConfigId2].nodes,
11560
+ quest_links: graphDataRef.current[sceneConfigId2].links
11552
11561
  };
11553
11562
  try {
11554
- await actions.save_view_data(sceneSaveUrl2, updatedSceneData);
11555
- sceneDataRef2.current.nodes.push(newNode);
11563
+ await actions.save_view_data(sceneSaveUrl2, sceneFileData);
11564
+ stateRef2.current.nodeIdToParentFileMap.set(String(newNode.id), {
11565
+ parentFileId: sceneConfigId2,
11566
+ ownerId: ownerId2,
11567
+ datasetName: "Quests Internas (View)"
11568
+ });
11556
11569
  const basePosition = stateRef2.current.controls.target.clone();
11557
- const offset = new THREE3.Vector3((Math.random() - 0.5) * 10, (Math.random() - 0.5) * 5, (Math.random() - 0.5) * 10);
11570
+ const offset = new THREE3.Vector3((Math.random() - 0.5) * 15, (Math.random() - 0.5) * 5, 0);
11558
11571
  const finalPosition = basePosition.add(offset);
11559
- addOrUpdateNodeMesh(newNode, finalPosition);
11572
+ addStandaloneNodeToScene(stateRef2.current, newNode, finalPosition);
11560
11573
  context.tweenToTarget(finalPosition, 1.2);
11561
11574
  setters.setQuestMode({ isActive: false });
11562
11575
  setters.setSceneVersion((v) => v + 1);
11563
11576
  } catch (error) {
11564
- console.error("Falha ao salvar a Quest na View:", error);
11577
+ console.error("Falha ao salvar Quest na View:", error);
11565
11578
  alert("Ocorreu um erro ao criar a Quest.");
11566
11579
  }
11567
11580
  };
11581
+ userActionHandlers.handleCompleteConnection = async (context, targetNodeData) => {
11582
+ const { stateRef: stateRef2, graphDataRef, sceneDataRef: sceneDataRef2, sceneConfigId: sceneConfigId2, sceneSaveUrl: sceneSaveUrl2 } = context;
11583
+ const { sourceNodeData } = stateRef2.current.connection;
11584
+ if (!graphDataRef.current || !sceneDataRef2.current || !sourceNodeData || !targetNodeData) {
11585
+ userActionHandlers.handleCancelConnection(context);
11586
+ return;
11587
+ }
11588
+ const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef2.current, sourceNodeData.id);
11589
+ const targetParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef2.current, targetNodeData.id);
11590
+ let parentInfoToSave = sourceParentInfo;
11591
+ if (sourceParentInfo.parentFileId === sceneConfigId2 && targetParentInfo.parentFileId !== sceneConfigId2) {
11592
+ parentInfoToSave = targetParentInfo;
11593
+ } else if (targetParentInfo.parentFileId === sceneConfigId2 && sourceParentInfo.parentFileId !== sceneConfigId2) {
11594
+ parentInfoToSave = sourceParentInfo;
11595
+ }
11596
+ const { parentFileId: parentFileIdToSave, ownerId: ownerIdToSave } = parentInfoToSave;
11597
+ const newLink = {
11598
+ id: `link_${import_short_uuid2.default.generate()}`,
11599
+ source: sourceNodeData.id,
11600
+ target: targetNodeData.id
11601
+ };
11602
+ try {
11603
+ if (parentFileIdToSave === sceneConfigId2) {
11604
+ const specificParentData = graphDataRef.current[sceneConfigId2];
11605
+ specificParentData.links.push(newLink);
11606
+ const currentVisualNodes = Object.values(stateRef2.current.nodeObjects).map((m) => {
11607
+ const { _baseEmissiveIntensity, labelObject, labelOffset, timelineIntervalBar, timelineEndLabel, ...rest } = m.userData;
11608
+ return rest;
11609
+ });
11610
+ const currentVisualLinks = stateRef2.current.allLinks.map((l) => {
11611
+ const { sourceNode, targetNode, ...rest } = l.userData;
11612
+ return rest;
11613
+ });
11614
+ currentVisualLinks.push(newLink);
11615
+ const viewFilePayload = {
11616
+ parent_dbs: sceneDataRef2.current.parent_dbs,
11617
+ nodes: currentVisualNodes,
11618
+ links: currentVisualLinks,
11619
+ quest_nodes: specificParentData.nodes,
11620
+ quest_links: specificParentData.links
11621
+ // Salva a conexão aqui!
11622
+ };
11623
+ await context.actions.save_view_data(sceneSaveUrl2, viewFilePayload);
11624
+ } else {
11625
+ const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
11626
+ specificParentData.links.push(newLink);
11627
+ const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
11628
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
11629
+ graphDataRef.current[parentFileIdToSave] = specificParentData;
11630
+ }
11631
+ addNewLinkToScene(stateRef2.current, newLink);
11632
+ } catch (error) {
11633
+ console.error("Falha ao salvar a nova conex\xE3o:", error);
11634
+ alert("Ocorreu um erro ao salvar a nova conex\xE3o.");
11635
+ }
11636
+ userActionHandlers.handleCancelConnection(context);
11637
+ };
11568
11638
  const handleClearAncestryVisuals = (0, import_react25.useCallback)((ancestryId) => {
11569
11639
  const { renderedAncestries, ancestryGroup } = stateRef.current;
11570
11640
  const renderIndex = renderedAncestries.findIndex((a) => String(a.id) === String(ancestryId));
@@ -12832,6 +12902,7 @@ function XViewScene({
12832
12902
  [actionHandlerContext]
12833
12903
  );
12834
12904
  const handleSaveCurrentView = (0, import_react25.useCallback)(async () => {
12905
+ var _a2, _b2;
12835
12906
  const { nodeObjects, allLinks } = stateRef.current;
12836
12907
  if (!nodeObjects || !allLinks || !sceneSaveUrl || !parentDataRef.current) {
12837
12908
  console.warn("N\xE3o \xE9 poss\xEDvel salvar a cena: estado n\xE3o inicializado ou URL de salvamento ausente.");
@@ -12856,14 +12927,17 @@ function XViewScene({
12856
12927
  const sceneFileData = {
12857
12928
  parent_dbs: sceneDataRef.current.parent_dbs,
12858
12929
  nodes: currentNodes,
12859
- links: currentLinks
12930
+ links: currentLinks,
12931
+ // --- ADICIONE ESTAS DUAS LINHAS PARA PRESERVAR A FONTE DA VERDADE ---
12932
+ quest_nodes: ((_a2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _a2.nodes) || [],
12933
+ quest_links: ((_b2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _b2.links) || []
12860
12934
  };
12861
12935
  try {
12862
12936
  await save_view_data(sceneSaveUrl, sceneFileData);
12863
12937
  } catch (error) {
12864
12938
  console.error("Erro na chamada de save_view_data:", error);
12865
12939
  }
12866
- }, [sceneSaveUrl, save_view_data]);
12940
+ }, [sceneSaveUrl, save_view_data, sceneConfigId]);
12867
12941
  const allAvailableNodes = (0, import_react25.useMemo)(() => {
12868
12942
  if (!parentDataRef.current) return [];
12869
12943
  return Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes || []);
@@ -13351,6 +13425,11 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
13351
13425
  );
13352
13426
  }
13353
13427
  }
13428
+ parentData[scene_config] = {
13429
+ dataset_name: "Quests Internas (View)",
13430
+ nodes: sceneData.quest_nodes || [],
13431
+ links: sceneData.quest_links || []
13432
+ };
13354
13433
  const allNodes = Object.values(parentData).flatMap((db) => db.nodes || []);
13355
13434
  const allLinks = Object.values(parentData).flatMap((db) => db.links || []);
13356
13435
  const parentNodeMap = new Map(allNodes.map((node) => [String(node.id), node]));
package/dist/index.mjs CHANGED
@@ -3097,7 +3097,7 @@ function useResizablePanel({ initialWidth, minWidth, maxWidth }) {
3097
3097
 
3098
3098
  // src/components/CustomPropertyDisplay.jsx
3099
3099
  import React3, { useState as useState4, useRef as useRef3, useEffect as useEffect3 } from "react";
3100
- import { FiCheck, FiX as FiX2, FiEdit3, FiTrash2, FiExternalLink, FiFileText, FiChevronDown, FiUpload, FiLoader } from "react-icons/fi";
3100
+ import { FiCheck, FiX, FiEdit3, FiTrash2, FiExternalLink, FiFileText, FiChevronDown, FiUpload, FiLoader } from "react-icons/fi";
3101
3101
  function CustomPropertyDisplay({
3102
3102
  prop,
3103
3103
  onUpdate,
@@ -3388,7 +3388,7 @@ function CustomPropertyDisplay({
3388
3388
  default:
3389
3389
  return /* @__PURE__ */ React3.createElement("input", { type: "text", placeholder: "Valor", value: tempProp.value, onChange: (e) => handlePropChange("value", e.target.value), className: baseInput });
3390
3390
  }
3391
- })()), /* @__PURE__ */ React3.createElement("div", { className: "flex justify-end items-center gap-2 pt-1" }, /* @__PURE__ */ React3.createElement("button", { onClick: handleCancel, type: "button", className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors" }, /* @__PURE__ */ React3.createElement(FiX2, { className: "text-red-400" })), /* @__PURE__ */ React3.createElement("button", { onClick: handleSave, type: "button", className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors" }, /* @__PURE__ */ React3.createElement(FiCheck, { className: "text-green-400" }))));
3391
+ })()), /* @__PURE__ */ React3.createElement("div", { className: "flex justify-end items-center gap-2 pt-1" }, /* @__PURE__ */ React3.createElement("button", { onClick: handleCancel, type: "button", className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors" }, /* @__PURE__ */ React3.createElement(FiX, { className: "text-red-400" })), /* @__PURE__ */ React3.createElement("button", { onClick: handleSave, type: "button", className: "w-8 h-8 grid place-items-center rounded-lg hover:bg-white/10 transition-colors" }, /* @__PURE__ */ React3.createElement(FiCheck, { className: "text-green-400" }))));
3392
3392
  };
3393
3393
  const renderDisplayView = () => /* @__PURE__ */ React3.createElement("div", { className: "w-full space-y-2 text-sm" }, /* @__PURE__ */ React3.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React3.createElement("p", { className: "font-semibold text-slate-200" }, prop.key), /* @__PURE__ */ React3.createElement("span", { className: "text-xs capitalize bg-slate-700/50 px-2 py-0.5 rounded text-slate-400" }, prop.type)), /* @__PURE__ */ React3.createElement("div", { className: "text-slate-300 pl-1" }, (() => {
3394
3394
  switch (prop.type) {
@@ -7024,7 +7024,7 @@ function CreateAncestryPanel({
7024
7024
 
7025
7025
  // src/components/ImageViewer.jsx
7026
7026
  import React11, { useState as useState12, useEffect as useEffect11, useLayoutEffect as useLayoutEffect2, useCallback as useCallback3 } from "react";
7027
- import { FiX as FiX3, FiChevronLeft as FiChevronLeft3, FiChevronRight as FiChevronRight5 } from "react-icons/fi";
7027
+ import { FiX as FiX2, FiChevronLeft as FiChevronLeft3, FiChevronRight as FiChevronRight5 } from "react-icons/fi";
7028
7028
  function ImageViewer({ data, onClose }) {
7029
7029
  var _a;
7030
7030
  const { images = [], startIndex = 0, visible } = data;
@@ -7109,7 +7109,7 @@ function ImageViewer({ data, onClose }) {
7109
7109
  className: "absolute top-4 right-4 z-10 w-10 h-10 flex items-center justify-center bg-white/10 hover:bg-white/20 rounded-full text-white text-2xl transition-colors",
7110
7110
  "aria-label": "Fechar"
7111
7111
  },
7112
- /* @__PURE__ */ React11.createElement(FiX3, null)
7112
+ /* @__PURE__ */ React11.createElement(FiX2, null)
7113
7113
  ),
7114
7114
  /* @__PURE__ */ React11.createElement("div", { className: "relative max-w-full max-h-full flex items-center justify-center" }, isLoading && /* @__PURE__ */ React11.createElement("div", { className: "absolute inset-0 flex items-center justify-center" }, /* @__PURE__ */ React11.createElement("div", { className: "h-10 w-10 border-2 border-white/40 border-t-white rounded-full animate-spin" })), loadedSrc && /* @__PURE__ */ React11.createElement(
7115
7115
  "img",
@@ -7245,7 +7245,7 @@ function ColorPicker({ color, onChange, disabled }) {
7245
7245
  }
7246
7246
 
7247
7247
  // src/components/InSceneCreationForm.jsx
7248
- import { FiPlus as FiPlus3, FiMaximize2, FiX as FiX4, FiCheck as FiCheck7, FiEdit2 as FiEdit24, FiSun, FiChevronDown as FiChevronDown4 } from "react-icons/fi";
7248
+ import { FiPlus as FiPlus3, FiMaximize2, FiX as FiX3, FiCheck as FiCheck7, FiEdit2 as FiEdit24, FiSun, FiChevronDown as FiChevronDown4 } from "react-icons/fi";
7249
7249
  function InSceneCreationForm({
7250
7250
  onSave,
7251
7251
  onCancel,
@@ -7463,7 +7463,7 @@ function InSceneCreationForm({
7463
7463
  onClick: () => handleRemoveType(index),
7464
7464
  className: "hover:text-white transition-colors"
7465
7465
  },
7466
- /* @__PURE__ */ React13.createElement(FiX4, { size: 12 })
7466
+ /* @__PURE__ */ React13.createElement(FiX3, { size: 12 })
7467
7467
  ))), /* @__PURE__ */ React13.createElement(
7468
7468
  "input",
7469
7469
  {
@@ -7837,7 +7837,7 @@ function InSceneVersionForm({
7837
7837
 
7838
7838
  // src/components/InSceneQuestForm.jsx
7839
7839
  import React15, { useState as useState16, useRef as useRef12 } from "react";
7840
- import { FiPlus as FiPlus5, FiCheck as FiCheck9, FiEdit2 as FiEdit26, FiTarget } from "react-icons/fi";
7840
+ import { FiPlus as FiPlus5, FiCheck as FiCheck9, FiEdit2 as FiEdit26, FiTarget, FiX as FiX4 } from "react-icons/fi";
7841
7841
  var QUEST_STATUS_COLORS = {
7842
7842
  "Backlog": "#64748b",
7843
7843
  // Slate (Cinza azulado)
@@ -7956,7 +7956,7 @@ function InSceneQuestForm({
7956
7956
  /* @__PURE__ */ React15.createElement("option", { value: "In Progress" }, "In Progress"),
7957
7957
  /* @__PURE__ */ React15.createElement("option", { value: "Review" }, "Review"),
7958
7958
  /* @__PURE__ */ React15.createElement("option", { value: "Done" }, "Done")
7959
- )), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Quest"), /* @__PURE__ */ React15.createElement("input", { required: true, type: "text", placeholder: "Ex.: Refatorar M\xF3dulo X", value: name, onChange: (e) => setName(e.target.value), 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__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ React15.createElement("div", { className: "relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 focus-within:ring-2 focus-within:ring-indigo-400/60 transition-all" }, types.map((t, index) => /* @__PURE__ */ React15.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, t !== "quest" && /* @__PURE__ */ React15.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ React15.createElement(FiX, { size: 12 })))), /* @__PURE__ */ React15.createElement(
7959
+ )), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Quest"), /* @__PURE__ */ React15.createElement("input", { required: true, type: "text", placeholder: "Ex.: Refatorar M\xF3dulo X", value: name, onChange: (e) => setName(e.target.value), 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__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ React15.createElement("div", { className: "relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 focus-within:ring-2 focus-within:ring-indigo-400/60 transition-all" }, types.map((t, index) => /* @__PURE__ */ React15.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, t !== "quest" && /* @__PURE__ */ React15.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ React15.createElement(FiX4, { size: 12 })))), /* @__PURE__ */ React15.createElement(
7960
7960
  "input",
7961
7961
  {
7962
7962
  type: "text",
@@ -10872,20 +10872,6 @@ function XViewScene({
10872
10872
  const context = actionHandlerContext;
10873
10873
  if (connection.isActive) {
10874
10874
  if (hoveredNode && String(hoveredNode.userData.id) !== String(connection.sourceNodeData.id)) {
10875
- const sourceId = String(connection.sourceNodeData.id);
10876
- const targetId = String(hoveredNode.userData.id);
10877
- let parentInfo = stateRef.current.nodeIdToParentFileMap.get(sourceId);
10878
- if (!parentInfo || !parentInfo.ownerId) {
10879
- parentInfo = stateRef.current.nodeIdToParentFileMap.get(targetId);
10880
- if (parentInfo && parentInfo.ownerId) {
10881
- stateRef.current.nodeIdToParentFileMap.set(sourceId, parentInfo);
10882
- } else {
10883
- console.error("Nenhum dos Nodes possui um Dataset pai v\xE1lido para salvar a conex\xE3o.");
10884
- alert("N\xE3o \xE9 poss\xEDvel conectar dois Nodes de Quest diretamente sem um Dataset pai, ou ocorreu um erro.");
10885
- userActionHandlers.handleCancelConnection(context);
10886
- return;
10887
- }
10888
- }
10889
10875
  await userActionHandlers.handleCompleteConnection(context, hoveredNode.userData);
10890
10876
  } else {
10891
10877
  userActionHandlers.handleCancelConnection(context);
@@ -11491,6 +11477,10 @@ function XViewScene({
11491
11477
  // <-- Adicionado
11492
11478
  sceneSaveUrl,
11493
11479
  // <-- Adicionado
11480
+ sceneConfigId,
11481
+ // <-- Adicionado
11482
+ ownerId,
11483
+ // <-- Adicionado
11494
11484
  viewType: viewParams == null ? void 0 : viewParams.type,
11495
11485
  // <-- Adicionado
11496
11486
  userId: (_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id,
@@ -11524,9 +11514,11 @@ function XViewScene({
11524
11514
  versionMode,
11525
11515
  questMode,
11526
11516
  sceneSaveUrl,
11517
+ sceneConfigId,
11518
+ ownerId,
11527
11519
  viewParams == null ? void 0 : viewParams.type,
11528
- tweenToTarget,
11529
11520
  (_a = session == null ? void 0 : session.user) == null ? void 0 : _a.id,
11521
+ tweenToTarget,
11530
11522
  handleVersionTimeline,
11531
11523
  save_view_data,
11532
11524
  get_single_parent_file,
@@ -11539,32 +11531,110 @@ function XViewScene({
11539
11531
  userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
11540
11532
  };
11541
11533
  const handleSaveQuestNode = async (context, newQuestData) => {
11542
- const { sceneDataRef: sceneDataRef2, stateRef: stateRef2, setters, actions, sceneSaveUrl: sceneSaveUrl2, viewType } = context;
11543
- if (!sceneDataRef2.current || (viewType == null ? void 0 : viewType.toLowerCase()) !== "view") return;
11534
+ const { graphDataRef, sceneDataRef: sceneDataRef2, stateRef: stateRef2, setters, actions, sceneSaveUrl: sceneSaveUrl2, viewType, sceneConfigId: sceneConfigId2, ownerId: ownerId2 } = context;
11535
+ if (!graphDataRef.current || (viewType == null ? void 0 : viewType.toLowerCase()) !== "view") return;
11544
11536
  const newNode = {
11545
11537
  id: short2.generate(),
11546
11538
  ...newQuestData,
11547
11539
  type: ["quest", ...newQuestData.type.filter((t) => t !== "quest")]
11548
11540
  };
11549
- const updatedSceneData = {
11550
- ...sceneDataRef2.current,
11551
- nodes: [...sceneDataRef2.current.nodes, newNode]
11541
+ if (!graphDataRef.current[sceneConfigId2]) {
11542
+ graphDataRef.current[sceneConfigId2] = { nodes: [], links: [] };
11543
+ }
11544
+ graphDataRef.current[sceneConfigId2].nodes.push(newNode);
11545
+ sceneDataRef2.current.nodes.push(newNode);
11546
+ const currentVisualNodes = Object.values(stateRef2.current.nodeObjects).map((mesh) => {
11547
+ const { _baseEmissiveIntensity, labelObject, labelOffset, timelineIntervalBar, timelineEndLabel, ...rest } = mesh.userData;
11548
+ return rest;
11549
+ });
11550
+ currentVisualNodes.push(newNode);
11551
+ const currentVisualLinks = stateRef2.current.allLinks.map((line) => {
11552
+ const { sourceNode, targetNode, ...rest } = line.userData;
11553
+ return rest;
11554
+ });
11555
+ const sceneFileData = {
11556
+ parent_dbs: sceneDataRef2.current.parent_dbs,
11557
+ nodes: currentVisualNodes,
11558
+ links: currentVisualLinks,
11559
+ quest_nodes: graphDataRef.current[sceneConfigId2].nodes,
11560
+ quest_links: graphDataRef.current[sceneConfigId2].links
11552
11561
  };
11553
11562
  try {
11554
- await actions.save_view_data(sceneSaveUrl2, updatedSceneData);
11555
- sceneDataRef2.current.nodes.push(newNode);
11563
+ await actions.save_view_data(sceneSaveUrl2, sceneFileData);
11564
+ stateRef2.current.nodeIdToParentFileMap.set(String(newNode.id), {
11565
+ parentFileId: sceneConfigId2,
11566
+ ownerId: ownerId2,
11567
+ datasetName: "Quests Internas (View)"
11568
+ });
11556
11569
  const basePosition = stateRef2.current.controls.target.clone();
11557
- const offset = new THREE3.Vector3((Math.random() - 0.5) * 10, (Math.random() - 0.5) * 5, (Math.random() - 0.5) * 10);
11570
+ const offset = new THREE3.Vector3((Math.random() - 0.5) * 15, (Math.random() - 0.5) * 5, 0);
11558
11571
  const finalPosition = basePosition.add(offset);
11559
- addOrUpdateNodeMesh(newNode, finalPosition);
11572
+ addStandaloneNodeToScene(stateRef2.current, newNode, finalPosition);
11560
11573
  context.tweenToTarget(finalPosition, 1.2);
11561
11574
  setters.setQuestMode({ isActive: false });
11562
11575
  setters.setSceneVersion((v) => v + 1);
11563
11576
  } catch (error) {
11564
- console.error("Falha ao salvar a Quest na View:", error);
11577
+ console.error("Falha ao salvar Quest na View:", error);
11565
11578
  alert("Ocorreu um erro ao criar a Quest.");
11566
11579
  }
11567
11580
  };
11581
+ userActionHandlers.handleCompleteConnection = async (context, targetNodeData) => {
11582
+ const { stateRef: stateRef2, graphDataRef, sceneDataRef: sceneDataRef2, sceneConfigId: sceneConfigId2, sceneSaveUrl: sceneSaveUrl2 } = context;
11583
+ const { sourceNodeData } = stateRef2.current.connection;
11584
+ if (!graphDataRef.current || !sceneDataRef2.current || !sourceNodeData || !targetNodeData) {
11585
+ userActionHandlers.handleCancelConnection(context);
11586
+ return;
11587
+ }
11588
+ const sourceParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef2.current, sourceNodeData.id);
11589
+ const targetParentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef2.current, targetNodeData.id);
11590
+ let parentInfoToSave = sourceParentInfo;
11591
+ if (sourceParentInfo.parentFileId === sceneConfigId2 && targetParentInfo.parentFileId !== sceneConfigId2) {
11592
+ parentInfoToSave = targetParentInfo;
11593
+ } else if (targetParentInfo.parentFileId === sceneConfigId2 && sourceParentInfo.parentFileId !== sceneConfigId2) {
11594
+ parentInfoToSave = sourceParentInfo;
11595
+ }
11596
+ const { parentFileId: parentFileIdToSave, ownerId: ownerIdToSave } = parentInfoToSave;
11597
+ const newLink = {
11598
+ id: `link_${short2.generate()}`,
11599
+ source: sourceNodeData.id,
11600
+ target: targetNodeData.id
11601
+ };
11602
+ try {
11603
+ if (parentFileIdToSave === sceneConfigId2) {
11604
+ const specificParentData = graphDataRef.current[sceneConfigId2];
11605
+ specificParentData.links.push(newLink);
11606
+ const currentVisualNodes = Object.values(stateRef2.current.nodeObjects).map((m) => {
11607
+ const { _baseEmissiveIntensity, labelObject, labelOffset, timelineIntervalBar, timelineEndLabel, ...rest } = m.userData;
11608
+ return rest;
11609
+ });
11610
+ const currentVisualLinks = stateRef2.current.allLinks.map((l) => {
11611
+ const { sourceNode, targetNode, ...rest } = l.userData;
11612
+ return rest;
11613
+ });
11614
+ currentVisualLinks.push(newLink);
11615
+ const viewFilePayload = {
11616
+ parent_dbs: sceneDataRef2.current.parent_dbs,
11617
+ nodes: currentVisualNodes,
11618
+ links: currentVisualLinks,
11619
+ quest_nodes: specificParentData.nodes,
11620
+ quest_links: specificParentData.links
11621
+ // Salva a conexão aqui!
11622
+ };
11623
+ await context.actions.save_view_data(sceneSaveUrl2, viewFilePayload);
11624
+ } else {
11625
+ const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileIdToSave]));
11626
+ specificParentData.links.push(newLink);
11627
+ const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
11628
+ await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
11629
+ graphDataRef.current[parentFileIdToSave] = specificParentData;
11630
+ }
11631
+ addNewLinkToScene(stateRef2.current, newLink);
11632
+ } catch (error) {
11633
+ console.error("Falha ao salvar a nova conex\xE3o:", error);
11634
+ alert("Ocorreu um erro ao salvar a nova conex\xE3o.");
11635
+ }
11636
+ userActionHandlers.handleCancelConnection(context);
11637
+ };
11568
11638
  const handleClearAncestryVisuals = useCallback4((ancestryId) => {
11569
11639
  const { renderedAncestries, ancestryGroup } = stateRef.current;
11570
11640
  const renderIndex = renderedAncestries.findIndex((a) => String(a.id) === String(ancestryId));
@@ -12832,6 +12902,7 @@ function XViewScene({
12832
12902
  [actionHandlerContext]
12833
12903
  );
12834
12904
  const handleSaveCurrentView = useCallback4(async () => {
12905
+ var _a2, _b2;
12835
12906
  const { nodeObjects, allLinks } = stateRef.current;
12836
12907
  if (!nodeObjects || !allLinks || !sceneSaveUrl || !parentDataRef.current) {
12837
12908
  console.warn("N\xE3o \xE9 poss\xEDvel salvar a cena: estado n\xE3o inicializado ou URL de salvamento ausente.");
@@ -12856,14 +12927,17 @@ function XViewScene({
12856
12927
  const sceneFileData = {
12857
12928
  parent_dbs: sceneDataRef.current.parent_dbs,
12858
12929
  nodes: currentNodes,
12859
- links: currentLinks
12930
+ links: currentLinks,
12931
+ // --- ADICIONE ESTAS DUAS LINHAS PARA PRESERVAR A FONTE DA VERDADE ---
12932
+ quest_nodes: ((_a2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _a2.nodes) || [],
12933
+ quest_links: ((_b2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _b2.links) || []
12860
12934
  };
12861
12935
  try {
12862
12936
  await save_view_data(sceneSaveUrl, sceneFileData);
12863
12937
  } catch (error) {
12864
12938
  console.error("Erro na chamada de save_view_data:", error);
12865
12939
  }
12866
- }, [sceneSaveUrl, save_view_data]);
12940
+ }, [sceneSaveUrl, save_view_data, sceneConfigId]);
12867
12941
  const allAvailableNodes = useMemo12(() => {
12868
12942
  if (!parentDataRef.current) return [];
12869
12943
  return Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes || []);
@@ -13351,6 +13425,11 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
13351
13425
  );
13352
13426
  }
13353
13427
  }
13428
+ parentData[scene_config] = {
13429
+ dataset_name: "Quests Internas (View)",
13430
+ nodes: sceneData.quest_nodes || [],
13431
+ links: sceneData.quest_links || []
13432
+ };
13354
13433
  const allNodes = Object.values(parentData).flatMap((db) => db.nodes || []);
13355
13434
  const allLinks = Object.values(parentData).flatMap((db) => db.links || []);
13356
13435
  const parentNodeMap = new Map(allNodes.map((node) => [String(node.id), node]));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lv-x-software-house/x_view",
3
- "version": "1.2.4-dev.2",
3
+ "version": "1.2.4-dev.5",
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",