@lv-x-software-house/x_view 1.2.4-dev.25 → 1.2.4-dev.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -505,7 +505,11 @@ function XViewSidebar({
505
505
  let pool = base;
506
506
  const qNorm = normalize(query);
507
507
  if (qNorm) {
508
- pool = pool.filter((n) => normalize((n == null ? void 0 : n.name) || "").includes(qNorm));
508
+ pool = pool.filter((n) => {
509
+ const matchName = normalize((n == null ? void 0 : n.name) || "").includes(qNorm);
510
+ const matchRawTitle = n.is_quest && n.raw_title ? normalize(n.raw_title).includes(qNorm) : false;
511
+ return matchName || matchRawTitle;
512
+ });
509
513
  }
510
514
  if (activeFilters.length > 0) {
511
515
  pool = pool.filter((node) => {
@@ -2552,6 +2556,13 @@ var userActionHandlers = {
2552
2556
  try {
2553
2557
  const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2554
2558
  if (isView && parentFileId === context.sceneConfigId) {
2559
+ const visualNodeIndex = sceneDataRef.current.nodes.findIndex((n) => String(n.id) === String(nodeToSave.id));
2560
+ if (visualNodeIndex > -1) {
2561
+ sceneDataRef.current.nodes[visualNodeIndex] = {
2562
+ ...sceneDataRef.current.nodes[visualNodeIndex],
2563
+ ...nodeToSave
2564
+ };
2565
+ }
2555
2566
  const viewFilePayload = {
2556
2567
  parent_dbs: sceneDataRef.current.parent_dbs,
2557
2568
  nodes: sceneDataRef.current.nodes,
@@ -2895,7 +2906,8 @@ var IGNORED_KEYS = [
2895
2906
  "selectedAbstractionParentId",
2896
2907
  "isAddingAbstractionNodes",
2897
2908
  "status",
2898
- "is_quest"
2909
+ "is_quest",
2910
+ "raw_title"
2899
2911
  ];
2900
2912
  function extractCustomPropsFromNode(node) {
2901
2913
  const customPropTypes = node._customPropTypes || {};
@@ -8091,7 +8103,7 @@ function InSceneQuestForm({
8091
8103
  },
8092
8104
  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"
8093
8105
  }
8094
- ), /* @__PURE__ */ import_react16.default.createElement("div", { className: "pt-1 flex items-center gap-1.5" }, /* @__PURE__ */ import_react16.default.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }, "Preview:"), /* @__PURE__ */ import_react16.default.createElement("p", { className: "text-xs text-indigo-300 font-medium truncate", title: standardizedName }, standardizedName))), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react16.default.createElement(
8106
+ ), /* @__PURE__ */ import_react16.default.createElement("div", { className: "pt-1 flex items-center gap-1.5" }, /* @__PURE__ */ import_react16.default.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }), /* @__PURE__ */ import_react16.default.createElement("p", { className: "text-xs text-indigo-300 font-medium truncate", title: standardizedName }, standardizedName))), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react16.default.createElement(
8095
8107
  "button",
8096
8108
  {
8097
8109
  type: "button",
@@ -8691,7 +8703,12 @@ function QuestDetailsPanel({
8691
8703
  userRole,
8692
8704
  currentDatasetName
8693
8705
  }) {
8694
- const [name, setName] = (0, import_react18.useState)((node == null ? void 0 : node.name) ?? "");
8706
+ var _a;
8707
+ const initialRawTitle = (node == null ? void 0 : node.raw_title) || (((_a = node == null ? void 0 : node.name) == null ? void 0 : _a.includes(" - \xBB ")) ? node.name.split(" - \xBB ")[1] : node == null ? void 0 : node.name) || "";
8708
+ const [rawTitle, setRawTitle] = (0, import_react18.useState)(initialRawTitle);
8709
+ const prefixParts = ((node == null ? void 0 : node.name) || "").split(" - \xBB ");
8710
+ const questPrefix = prefixParts.length > 1 ? prefixParts[0] : "";
8711
+ const standardizedName = questPrefix ? `${questPrefix} - \xBB ${rawTitle || "Sem t\xEDtulo"}` : rawTitle;
8695
8712
  const [types, setTypes] = (0, import_react18.useState)((node == null ? void 0 : node.type) ? Array.isArray(node.type) ? node.type : [node.type] : ["quest"]);
8696
8713
  const [typeInput, setTypeInput] = (0, import_react18.useState)("");
8697
8714
  const [status, setStatus] = (0, import_react18.useState)((node == null ? void 0 : node.status) ?? "Backlog");
@@ -8720,15 +8737,17 @@ function QuestDetailsPanel({
8720
8737
  const propsEndRef = (0, import_react18.useRef)(null);
8721
8738
  const canEdit = userRole !== "viewer";
8722
8739
  const availableImages = customProps.filter((p) => p.type === "images").flatMap((p) => Array.isArray(p.value) ? p.value : []).filter((img) => img.value && img.value.trim() !== "");
8723
- const handleImageClickFromText = (url, name2) => {
8740
+ const handleImageClickFromText = (url, name) => {
8724
8741
  if (onOpenImageViewer) {
8725
- onOpenImageViewer([{ name: name2 || "Imagem", value: url }], 0);
8742
+ onOpenImageViewer([{ name: name || "Imagem", value: url }], 0);
8726
8743
  }
8727
8744
  };
8728
8745
  (0, import_react18.useEffect)(() => {
8746
+ var _a2;
8729
8747
  if ((node == null ? void 0 : node.id) !== prevNodeIdRef.current) {
8730
8748
  prevNodeIdRef.current = node == null ? void 0 : node.id;
8731
- setName((node == null ? void 0 : node.name) ?? "");
8749
+ const newRawTitle = (node == null ? void 0 : node.raw_title) || (((_a2 = node == null ? void 0 : node.name) == null ? void 0 : _a2.includes(" - \xBB ")) ? node.name.split(" - \xBB ")[1] : node == null ? void 0 : node.name) || "";
8750
+ setRawTitle(newRawTitle);
8732
8751
  setTypes((node == null ? void 0 : node.type) ? Array.isArray(node.type) ? node.type : [node.type] : ["quest"]);
8733
8752
  setStatus((node == null ? void 0 : node.status) ?? "Backlog");
8734
8753
  setSize((node == null ? void 0 : node.size) ?? "medium");
@@ -8763,9 +8782,10 @@ function QuestDetailsPanel({
8763
8782
  };
8764
8783
  const swallow = (e) => e.stopPropagation();
8765
8784
  const handleNameChange = (e) => {
8766
- const v = e.target.value;
8767
- setName(v);
8768
- onNameChange == null ? void 0 : onNameChange(node.id, v);
8785
+ const val = e.target.value;
8786
+ setRawTitle(val);
8787
+ const newStandardName = questPrefix ? `${questPrefix} - \xBB ${val || "Sem t\xEDtulo"}` : val;
8788
+ onNameChange == null ? void 0 : onNameChange(node.id, newStandardName, val);
8769
8789
  };
8770
8790
  const handleSizeChange = (newSize) => {
8771
8791
  setSize(newSize);
@@ -8801,8 +8821,8 @@ function QuestDetailsPanel({
8801
8821
  const newProp = createNewCustomProperty(customProps);
8802
8822
  setCustomProps((p) => [...p, newProp]);
8803
8823
  setTimeout(() => {
8804
- var _a;
8805
- (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
8824
+ var _a2;
8825
+ (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
8806
8826
  }, 100);
8807
8827
  };
8808
8828
  const handleRemoveProp = (i) => {
@@ -8824,14 +8844,15 @@ function QuestDetailsPanel({
8824
8844
  triggerAutoSave({ description: newDescription });
8825
8845
  };
8826
8846
  const handleSave = async (keepOpen = false, overrides = {}) => {
8827
- const currentName = overrides.name !== void 0 ? overrides.name : name;
8847
+ const currentRawTitle = overrides.rawTitle !== void 0 ? overrides.rawTitle : rawTitle;
8848
+ const currentStandardName = questPrefix ? `${questPrefix} - \xBB ${currentRawTitle || "Sem t\xEDtulo"}` : currentRawTitle;
8828
8849
  const currentTypes = overrides.types !== void 0 ? overrides.types : types;
8829
8850
  const currentDescription = overrides.description !== void 0 ? overrides.description : description;
8830
8851
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
8831
8852
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
8832
8853
  const currentStatus = overrides.status !== void 0 ? overrides.status : status;
8833
- if (!currentName.trim() || currentTypes.length === 0) {
8834
- alert("O campo 'Nome' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8854
+ if (!currentRawTitle.trim() || currentTypes.length === 0) {
8855
+ alert("O campo 'T\xEDtulo' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8835
8856
  return;
8836
8857
  }
8837
8858
  setIsSaving(true);
@@ -8840,7 +8861,10 @@ function QuestDetailsPanel({
8840
8861
  const processedSections = processDescriptionForSave(currentDescription, currentExistingSections);
8841
8862
  const dataToSave = {
8842
8863
  id: node.id,
8843
- name: currentName.trim(),
8864
+ name: currentStandardName.trim(),
8865
+ // Salva o nome completo formatado
8866
+ raw_title: currentRawTitle.trim(),
8867
+ // Salva o título simples
8844
8868
  type: currentTypes,
8845
8869
  color: QUEST_STATUS_COLORS2[currentStatus],
8846
8870
  status: currentStatus,
@@ -8895,7 +8919,7 @@ function QuestDetailsPanel({
8895
8919
  isReadMode ? /* @__PURE__ */ import_react18.default.createElement(
8896
8920
  DescriptionReadModePanel,
8897
8921
  {
8898
- title: name || (node == null ? void 0 : node.name),
8922
+ title: standardizedName || (node == null ? void 0 : node.name),
8899
8923
  description,
8900
8924
  savedSections: existingSections,
8901
8925
  onBack: () => setIsReadMode(false),
@@ -8913,7 +8937,16 @@ function QuestDetailsPanel({
8913
8937
  onImageClick: handleImageClickFromText,
8914
8938
  onSaveDescription: handleSaveDescriptionInline
8915
8939
  }
8916
- ) : /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS2[status]}, transparent)` } }), /* @__PURE__ */ import_react18.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react18.default.createElement("div", null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ import_react18.default.createElement("button", { onClick: handleCopyLink, className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-sky-400"}`, title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Quest" }, isLinkCopied ? /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiLink, { size: 12 }))), /* @__PURE__ */ import_react18.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react18.default.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ import_react18.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ import_react18.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react18.default.createElement(
8940
+ ) : /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS2[status]}, transparent)` } }), /* @__PURE__ */ import_react18.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react18.default.createElement("div", null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ import_react18.default.createElement("button", { onClick: handleCopyLink, className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-sky-400"}`, title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Quest" }, isLinkCopied ? /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiLink, { size: 12 }))), /* @__PURE__ */ import_react18.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, standardizedName || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react18.default.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ import_react18.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "T\xEDtulo da Quest"), /* @__PURE__ */ import_react18.default.createElement(
8941
+ "input",
8942
+ {
8943
+ type: "text",
8944
+ value: rawTitle,
8945
+ onChange: handleNameChange,
8946
+ readOnly: !canEdit,
8947
+ className: `w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none ${canEdit ? "focus:ring-2 focus:ring-indigo-400/60" : "cursor-default text-slate-400"}`
8948
+ }
8949
+ ), /* @__PURE__ */ import_react18.default.createElement("div", { className: "pt-1 flex items-center gap-1.5" }, /* @__PURE__ */ import_react18.default.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }), /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-xs text-indigo-300 font-medium truncate", title: standardizedName }, standardizedName))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ import_react18.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react18.default.createElement(
8917
8950
  "button",
8918
8951
  {
8919
8952
  type: "button",
@@ -8935,7 +8968,7 @@ function QuestDetailsPanel({
8935
8968
  },
8936
8969
  /* @__PURE__ */ import_react18.default.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS2[s] } }),
8937
8970
  s
8938
- )))))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome"), /* @__PURE__ */ import_react18.default.createElement("input", { type: "text", value: name, onChange: handleNameChange, readOnly: !canEdit, className: `w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none ${canEdit ? "focus:ring-2 focus:ring-indigo-400/60" : "cursor-default text-slate-400"}` })), /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ import_react18.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 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ import_react18.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, canEdit && t !== "quest" && /* @__PURE__ */ import_react18.default.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiX, { size: 12 })))), canEdit && /* @__PURE__ */ import_react18.default.createElement(
8971
+ )))))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ import_react18.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 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ import_react18.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, canEdit && t !== "quest" && /* @__PURE__ */ import_react18.default.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiX, { size: 12 })))), canEdit && /* @__PURE__ */ import_react18.default.createElement(
8939
8972
  "input",
8940
8973
  {
8941
8974
  type: "text",
@@ -11809,7 +11842,7 @@ function XViewScene({
11809
11842
  graphGroup.add(newLabel);
11810
11843
  ghostNode.userData.labelObject = newLabel;
11811
11844
  };
11812
- const handleDetailNodeNameChange = (nodeId, newName) => {
11845
+ const handleDetailNodeNameChange = (nodeId, newName, newRawTitle) => {
11813
11846
  const mesh = stateRef.current.nodeObjects[String(nodeId)];
11814
11847
  const { graphGroup } = stateRef.current;
11815
11848
  if (!mesh || !graphGroup) return;
@@ -11823,6 +11856,9 @@ function XViewScene({
11823
11856
  graphGroup.add(newLabel);
11824
11857
  mesh.userData.labelObject = newLabel;
11825
11858
  mesh.userData.name = newName;
11859
+ if (newRawTitle !== void 0) {
11860
+ mesh.userData.raw_title = newRawTitle;
11861
+ }
11826
11862
  };
11827
11863
  const handleDetailNodeColorChange = (nodeId, newColor) => {
11828
11864
  var _a2;
@@ -11997,6 +12033,7 @@ function XViewScene({
11997
12033
  graphDataRef.current[sceneConfigId2] = { nodes: [], links: [] };
11998
12034
  }
11999
12035
  graphDataRef.current[sceneConfigId2].nodes.push(newNode);
12036
+ sceneDataRef2.current.nodes.push(newNode);
12000
12037
  const sceneFileData = {
12001
12038
  parent_dbs: sceneDataRef2.current.parent_dbs,
12002
12039
  nodes: sceneDataRef2.current.nodes,
@@ -14013,8 +14050,13 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
14013
14050
  const parentNodeMap = new Map(allNodes.map((node) => [String(node.id), node]));
14014
14051
  const parentLinkMap = new Map(allLinks.map((link) => [`${link.source}-${link.target}`, link]));
14015
14052
  const validatedNodes = (sceneData.nodes || []).map((sceneNode) => {
14053
+ var _a2, _b2;
14016
14054
  const nodeTypes = Array.isArray(sceneNode.type) ? sceneNode.type : [sceneNode.type];
14017
14055
  if (nodeTypes.includes("quest")) {
14056
+ const dbQuestNode = (_b2 = (_a2 = parentData[scene_config]) == null ? void 0 : _a2.nodes) == null ? void 0 : _b2.find((qn) => String(qn.id) === String(sceneNode.id));
14057
+ if (dbQuestNode) {
14058
+ return { ...sceneNode, ...dbQuestNode };
14059
+ }
14018
14060
  return sceneNode;
14019
14061
  }
14020
14062
  const dbNode = parentNodeMap.get(String(sceneNode.id));
package/dist/index.mjs CHANGED
@@ -461,7 +461,11 @@ function XViewSidebar({
461
461
  let pool = base;
462
462
  const qNorm = normalize(query);
463
463
  if (qNorm) {
464
- pool = pool.filter((n) => normalize((n == null ? void 0 : n.name) || "").includes(qNorm));
464
+ pool = pool.filter((n) => {
465
+ const matchName = normalize((n == null ? void 0 : n.name) || "").includes(qNorm);
466
+ const matchRawTitle = n.is_quest && n.raw_title ? normalize(n.raw_title).includes(qNorm) : false;
467
+ return matchName || matchRawTitle;
468
+ });
465
469
  }
466
470
  if (activeFilters.length > 0) {
467
471
  pool = pool.filter((node) => {
@@ -2508,6 +2512,13 @@ var userActionHandlers = {
2508
2512
  try {
2509
2513
  const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2510
2514
  if (isView && parentFileId === context.sceneConfigId) {
2515
+ const visualNodeIndex = sceneDataRef.current.nodes.findIndex((n) => String(n.id) === String(nodeToSave.id));
2516
+ if (visualNodeIndex > -1) {
2517
+ sceneDataRef.current.nodes[visualNodeIndex] = {
2518
+ ...sceneDataRef.current.nodes[visualNodeIndex],
2519
+ ...nodeToSave
2520
+ };
2521
+ }
2511
2522
  const viewFilePayload = {
2512
2523
  parent_dbs: sceneDataRef.current.parent_dbs,
2513
2524
  nodes: sceneDataRef.current.nodes,
@@ -2851,7 +2862,8 @@ var IGNORED_KEYS = [
2851
2862
  "selectedAbstractionParentId",
2852
2863
  "isAddingAbstractionNodes",
2853
2864
  "status",
2854
- "is_quest"
2865
+ "is_quest",
2866
+ "raw_title"
2855
2867
  ];
2856
2868
  function extractCustomPropsFromNode(node) {
2857
2869
  const customPropTypes = node._customPropTypes || {};
@@ -8078,7 +8090,7 @@ function InSceneQuestForm({
8078
8090
  },
8079
8091
  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"
8080
8092
  }
8081
- ), /* @__PURE__ */ React15.createElement("div", { className: "pt-1 flex items-center gap-1.5" }, /* @__PURE__ */ React15.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }, "Preview:"), /* @__PURE__ */ React15.createElement("p", { className: "text-xs text-indigo-300 font-medium truncate", title: standardizedName }, standardizedName))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
8093
+ ), /* @__PURE__ */ React15.createElement("div", { className: "pt-1 flex items-center gap-1.5" }, /* @__PURE__ */ React15.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }), /* @__PURE__ */ React15.createElement("p", { className: "text-xs text-indigo-300 font-medium truncate", title: standardizedName }, standardizedName))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
8082
8094
  "button",
8083
8095
  {
8084
8096
  type: "button",
@@ -8678,7 +8690,12 @@ function QuestDetailsPanel({
8678
8690
  userRole,
8679
8691
  currentDatasetName
8680
8692
  }) {
8681
- const [name, setName] = useState18((node == null ? void 0 : node.name) ?? "");
8693
+ var _a;
8694
+ const initialRawTitle = (node == null ? void 0 : node.raw_title) || (((_a = node == null ? void 0 : node.name) == null ? void 0 : _a.includes(" - \xBB ")) ? node.name.split(" - \xBB ")[1] : node == null ? void 0 : node.name) || "";
8695
+ const [rawTitle, setRawTitle] = useState18(initialRawTitle);
8696
+ const prefixParts = ((node == null ? void 0 : node.name) || "").split(" - \xBB ");
8697
+ const questPrefix = prefixParts.length > 1 ? prefixParts[0] : "";
8698
+ const standardizedName = questPrefix ? `${questPrefix} - \xBB ${rawTitle || "Sem t\xEDtulo"}` : rawTitle;
8682
8699
  const [types, setTypes] = useState18((node == null ? void 0 : node.type) ? Array.isArray(node.type) ? node.type : [node.type] : ["quest"]);
8683
8700
  const [typeInput, setTypeInput] = useState18("");
8684
8701
  const [status, setStatus] = useState18((node == null ? void 0 : node.status) ?? "Backlog");
@@ -8707,15 +8724,17 @@ function QuestDetailsPanel({
8707
8724
  const propsEndRef = useRef14(null);
8708
8725
  const canEdit = userRole !== "viewer";
8709
8726
  const availableImages = customProps.filter((p) => p.type === "images").flatMap((p) => Array.isArray(p.value) ? p.value : []).filter((img) => img.value && img.value.trim() !== "");
8710
- const handleImageClickFromText = (url, name2) => {
8727
+ const handleImageClickFromText = (url, name) => {
8711
8728
  if (onOpenImageViewer) {
8712
- onOpenImageViewer([{ name: name2 || "Imagem", value: url }], 0);
8729
+ onOpenImageViewer([{ name: name || "Imagem", value: url }], 0);
8713
8730
  }
8714
8731
  };
8715
8732
  useEffect16(() => {
8733
+ var _a2;
8716
8734
  if ((node == null ? void 0 : node.id) !== prevNodeIdRef.current) {
8717
8735
  prevNodeIdRef.current = node == null ? void 0 : node.id;
8718
- setName((node == null ? void 0 : node.name) ?? "");
8736
+ const newRawTitle = (node == null ? void 0 : node.raw_title) || (((_a2 = node == null ? void 0 : node.name) == null ? void 0 : _a2.includes(" - \xBB ")) ? node.name.split(" - \xBB ")[1] : node == null ? void 0 : node.name) || "";
8737
+ setRawTitle(newRawTitle);
8719
8738
  setTypes((node == null ? void 0 : node.type) ? Array.isArray(node.type) ? node.type : [node.type] : ["quest"]);
8720
8739
  setStatus((node == null ? void 0 : node.status) ?? "Backlog");
8721
8740
  setSize((node == null ? void 0 : node.size) ?? "medium");
@@ -8750,9 +8769,10 @@ function QuestDetailsPanel({
8750
8769
  };
8751
8770
  const swallow = (e) => e.stopPropagation();
8752
8771
  const handleNameChange = (e) => {
8753
- const v = e.target.value;
8754
- setName(v);
8755
- onNameChange == null ? void 0 : onNameChange(node.id, v);
8772
+ const val = e.target.value;
8773
+ setRawTitle(val);
8774
+ const newStandardName = questPrefix ? `${questPrefix} - \xBB ${val || "Sem t\xEDtulo"}` : val;
8775
+ onNameChange == null ? void 0 : onNameChange(node.id, newStandardName, val);
8756
8776
  };
8757
8777
  const handleSizeChange = (newSize) => {
8758
8778
  setSize(newSize);
@@ -8788,8 +8808,8 @@ function QuestDetailsPanel({
8788
8808
  const newProp = createNewCustomProperty(customProps);
8789
8809
  setCustomProps((p) => [...p, newProp]);
8790
8810
  setTimeout(() => {
8791
- var _a;
8792
- (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
8811
+ var _a2;
8812
+ (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
8793
8813
  }, 100);
8794
8814
  };
8795
8815
  const handleRemoveProp = (i) => {
@@ -8811,14 +8831,15 @@ function QuestDetailsPanel({
8811
8831
  triggerAutoSave({ description: newDescription });
8812
8832
  };
8813
8833
  const handleSave = async (keepOpen = false, overrides = {}) => {
8814
- const currentName = overrides.name !== void 0 ? overrides.name : name;
8834
+ const currentRawTitle = overrides.rawTitle !== void 0 ? overrides.rawTitle : rawTitle;
8835
+ const currentStandardName = questPrefix ? `${questPrefix} - \xBB ${currentRawTitle || "Sem t\xEDtulo"}` : currentRawTitle;
8815
8836
  const currentTypes = overrides.types !== void 0 ? overrides.types : types;
8816
8837
  const currentDescription = overrides.description !== void 0 ? overrides.description : description;
8817
8838
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
8818
8839
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
8819
8840
  const currentStatus = overrides.status !== void 0 ? overrides.status : status;
8820
- if (!currentName.trim() || currentTypes.length === 0) {
8821
- alert("O campo 'Nome' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8841
+ if (!currentRawTitle.trim() || currentTypes.length === 0) {
8842
+ alert("O campo 'T\xEDtulo' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8822
8843
  return;
8823
8844
  }
8824
8845
  setIsSaving(true);
@@ -8827,7 +8848,10 @@ function QuestDetailsPanel({
8827
8848
  const processedSections = processDescriptionForSave(currentDescription, currentExistingSections);
8828
8849
  const dataToSave = {
8829
8850
  id: node.id,
8830
- name: currentName.trim(),
8851
+ name: currentStandardName.trim(),
8852
+ // Salva o nome completo formatado
8853
+ raw_title: currentRawTitle.trim(),
8854
+ // Salva o título simples
8831
8855
  type: currentTypes,
8832
8856
  color: QUEST_STATUS_COLORS2[currentStatus],
8833
8857
  status: currentStatus,
@@ -8882,7 +8906,7 @@ function QuestDetailsPanel({
8882
8906
  isReadMode ? /* @__PURE__ */ React17.createElement(
8883
8907
  DescriptionReadModePanel,
8884
8908
  {
8885
- title: name || (node == null ? void 0 : node.name),
8909
+ title: standardizedName || (node == null ? void 0 : node.name),
8886
8910
  description,
8887
8911
  savedSections: existingSections,
8888
8912
  onBack: () => setIsReadMode(false),
@@ -8900,7 +8924,16 @@ function QuestDetailsPanel({
8900
8924
  onImageClick: handleImageClickFromText,
8901
8925
  onSaveDescription: handleSaveDescriptionInline
8902
8926
  }
8903
- ) : /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS2[status]}, transparent)` } }), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React17.createElement(FiTarget2, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ React17.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ React17.createElement("button", { onClick: handleCopyLink, className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-sky-400"}`, title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Quest" }, isLinkCopied ? /* @__PURE__ */ React17.createElement(FiCheck11, { size: 12 }) : /* @__PURE__ */ React17.createElement(FiLink6, { size: 12 }))), /* @__PURE__ */ React17.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ React17.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ React17.createElement("div", { className: "relative" }, /* @__PURE__ */ React17.createElement(
8927
+ ) : /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS2[status]}, transparent)` } }), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React17.createElement(FiTarget2, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ React17.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ React17.createElement("button", { onClick: handleCopyLink, className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-sky-400"}`, title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Quest" }, isLinkCopied ? /* @__PURE__ */ React17.createElement(FiCheck11, { size: 12 }) : /* @__PURE__ */ React17.createElement(FiLink6, { size: 12 }))), /* @__PURE__ */ React17.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, standardizedName || (node == null ? void 0 : node.name))), /* @__PURE__ */ React17.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "T\xEDtulo da Quest"), /* @__PURE__ */ React17.createElement(
8928
+ "input",
8929
+ {
8930
+ type: "text",
8931
+ value: rawTitle,
8932
+ onChange: handleNameChange,
8933
+ readOnly: !canEdit,
8934
+ className: `w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none ${canEdit ? "focus:ring-2 focus:ring-indigo-400/60" : "cursor-default text-slate-400"}`
8935
+ }
8936
+ ), /* @__PURE__ */ React17.createElement("div", { className: "pt-1 flex items-center gap-1.5" }, /* @__PURE__ */ React17.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }), /* @__PURE__ */ React17.createElement("p", { className: "text-xs text-indigo-300 font-medium truncate", title: standardizedName }, standardizedName))), /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ React17.createElement("div", { className: "relative" }, /* @__PURE__ */ React17.createElement(
8904
8937
  "button",
8905
8938
  {
8906
8939
  type: "button",
@@ -8922,7 +8955,7 @@ function QuestDetailsPanel({
8922
8955
  },
8923
8956
  /* @__PURE__ */ React17.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS2[s] } }),
8924
8957
  s
8925
- )))))), /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "Nome"), /* @__PURE__ */ React17.createElement("input", { type: "text", value: name, onChange: handleNameChange, readOnly: !canEdit, className: `w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none ${canEdit ? "focus:ring-2 focus:ring-indigo-400/60" : "cursor-default text-slate-400"}` })), /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ React17.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 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React17.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, canEdit && t !== "quest" && /* @__PURE__ */ React17.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ React17.createElement(FiX6, { size: 12 })))), canEdit && /* @__PURE__ */ React17.createElement(
8958
+ )))))), /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ React17.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 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React17.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, canEdit && t !== "quest" && /* @__PURE__ */ React17.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ React17.createElement(FiX6, { size: 12 })))), canEdit && /* @__PURE__ */ React17.createElement(
8926
8959
  "input",
8927
8960
  {
8928
8961
  type: "text",
@@ -11809,7 +11842,7 @@ function XViewScene({
11809
11842
  graphGroup.add(newLabel);
11810
11843
  ghostNode.userData.labelObject = newLabel;
11811
11844
  };
11812
- const handleDetailNodeNameChange = (nodeId, newName) => {
11845
+ const handleDetailNodeNameChange = (nodeId, newName, newRawTitle) => {
11813
11846
  const mesh = stateRef.current.nodeObjects[String(nodeId)];
11814
11847
  const { graphGroup } = stateRef.current;
11815
11848
  if (!mesh || !graphGroup) return;
@@ -11823,6 +11856,9 @@ function XViewScene({
11823
11856
  graphGroup.add(newLabel);
11824
11857
  mesh.userData.labelObject = newLabel;
11825
11858
  mesh.userData.name = newName;
11859
+ if (newRawTitle !== void 0) {
11860
+ mesh.userData.raw_title = newRawTitle;
11861
+ }
11826
11862
  };
11827
11863
  const handleDetailNodeColorChange = (nodeId, newColor) => {
11828
11864
  var _a2;
@@ -11997,6 +12033,7 @@ function XViewScene({
11997
12033
  graphDataRef.current[sceneConfigId2] = { nodes: [], links: [] };
11998
12034
  }
11999
12035
  graphDataRef.current[sceneConfigId2].nodes.push(newNode);
12036
+ sceneDataRef2.current.nodes.push(newNode);
12000
12037
  const sceneFileData = {
12001
12038
  parent_dbs: sceneDataRef2.current.parent_dbs,
12002
12039
  nodes: sceneDataRef2.current.nodes,
@@ -14013,8 +14050,13 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
14013
14050
  const parentNodeMap = new Map(allNodes.map((node) => [String(node.id), node]));
14014
14051
  const parentLinkMap = new Map(allLinks.map((link) => [`${link.source}-${link.target}`, link]));
14015
14052
  const validatedNodes = (sceneData.nodes || []).map((sceneNode) => {
14053
+ var _a2, _b2;
14016
14054
  const nodeTypes = Array.isArray(sceneNode.type) ? sceneNode.type : [sceneNode.type];
14017
14055
  if (nodeTypes.includes("quest")) {
14056
+ const dbQuestNode = (_b2 = (_a2 = parentData[scene_config]) == null ? void 0 : _a2.nodes) == null ? void 0 : _b2.find((qn) => String(qn.id) === String(sceneNode.id));
14057
+ if (dbQuestNode) {
14058
+ return { ...sceneNode, ...dbQuestNode };
14059
+ }
14018
14060
  return sceneNode;
14019
14061
  }
14020
14062
  const dbNode = parentNodeMap.get(String(sceneNode.id));
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.25",
3
+ "version": "1.2.4-dev.27",
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",