@lv-x-software-house/x_view 1.2.4-dev.13 → 1.2.4-dev.15

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
@@ -7975,13 +7975,9 @@ var import_react16 = __toESM(require("react"));
7975
7975
  var import_fi14 = require("react-icons/fi");
7976
7976
  var QUEST_STATUS_COLORS = {
7977
7977
  "Backlog": "#64748b",
7978
- // Slate (Cinza azulado)
7979
7978
  "In Progress": "#eab308",
7980
- // Yellow (Amarelo)
7981
7979
  "Review": "#a855f7",
7982
- // Purple (Roxo)
7983
7980
  "Done": "#22c55e"
7984
- // Green (Verde)
7985
7981
  };
7986
7982
  function InSceneQuestForm({
7987
7983
  onSave,
@@ -8001,6 +7997,7 @@ function InSceneQuestForm({
8001
7997
  const [size, setSize] = (0, import_react16.useState)("medium");
8002
7998
  const [intensity, setIntensity] = (0, import_react16.useState)(0);
8003
7999
  const [description, setDescription] = (0, import_react16.useState)("");
8000
+ const [isStatusDropdownOpen, setIsStatusDropdownOpen] = (0, import_react16.useState)(false);
8004
8001
  const [customProps, setCustomProps] = (0, import_react16.useState)([]);
8005
8002
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react16.useState)(false);
8006
8003
  const propsEndRef = (0, import_react16.useRef)(null);
@@ -8051,7 +8048,6 @@ function InSceneQuestForm({
8051
8048
  name: name.trim(),
8052
8049
  type: types,
8053
8050
  color: QUEST_STATUS_COLORS[status],
8054
- // Cor atrelada ao status
8055
8051
  status,
8056
8052
  size,
8057
8053
  intensity,
@@ -8078,20 +8074,38 @@ function InSceneQuestForm({
8078
8074
  onDoubleClick: swallow
8079
8075
  },
8080
8076
  /* @__PURE__ */ import_react16.default.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS[status]}, transparent)` } }),
8081
- /* @__PURE__ */ import_react16.default.createElement("div", { className: "px-6 pt-5 pb-3" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ import_react16.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Nova Tarefa / Objetivo")), /* @__PURE__ */ import_react16.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Criar Quest")),
8082
- /* @__PURE__ */ import_react16.default.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col max-h-[68vh]" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ import_react16.default.createElement(
8083
- "select",
8084
- {
8085
- value: status,
8086
- onChange: (e) => setStatus(e.target.value),
8087
- 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 appearance-none cursor-pointer",
8088
- style: { borderLeft: `4px solid ${QUEST_STATUS_COLORS[status]}` }
8077
+ /* @__PURE__ */ import_react16.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react16.default.createElement("div", null, /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ import_react16.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Nova Tarefa / Objetivo")), /* @__PURE__ */ import_react16.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Criar Quest")), /* @__PURE__ */ import_react16.default.createElement(
8078
+ "button",
8079
+ {
8080
+ type: "button",
8081
+ onClick: onCancel,
8082
+ 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",
8083
+ title: "Fechar"
8089
8084
  },
8090
- /* @__PURE__ */ import_react16.default.createElement("option", { value: "Backlog" }, "Backlog"),
8091
- /* @__PURE__ */ import_react16.default.createElement("option", { value: "In Progress" }, "In Progress"),
8092
- /* @__PURE__ */ import_react16.default.createElement("option", { value: "Review" }, "Review"),
8093
- /* @__PURE__ */ import_react16.default.createElement("option", { value: "Done" }, "Done")
8094
- )), /* @__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(
8085
+ "\xD7"
8086
+ )),
8087
+ /* @__PURE__ */ import_react16.default.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col max-h-[68vh]" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__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(
8088
+ "button",
8089
+ {
8090
+ type: "button",
8091
+ onClick: () => setIsStatusDropdownOpen(!isStatusDropdownOpen),
8092
+ className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 hover:border-white/20 focus:outline-none focus:ring-2 focus:ring-indigo-400/60 transition-colors flex items-center justify-between"
8093
+ },
8094
+ /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react16.default.createElement("span", { className: "w-3 h-3 rounded-full shadow-[0_0_8px_rgba(0,0,0,0.5)]", style: { backgroundColor: QUEST_STATUS_COLORS[status] } }), /* @__PURE__ */ import_react16.default.createElement("span", { className: "text-slate-200 font-medium" }, status)),
8095
+ /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isStatusDropdownOpen ? "rotate-180" : ""}` })
8096
+ ), isStatusDropdownOpen && /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsStatusDropdownOpen(false) }), /* @__PURE__ */ import_react16.default.createElement("ul", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-800 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden" }, Object.keys(QUEST_STATUS_COLORS).map((s) => /* @__PURE__ */ import_react16.default.createElement(
8097
+ "li",
8098
+ {
8099
+ key: s,
8100
+ onClick: () => {
8101
+ setStatus(s);
8102
+ setIsStatusDropdownOpen(false);
8103
+ },
8104
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${status === s ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
8105
+ },
8106
+ /* @__PURE__ */ import_react16.default.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS[s] } }),
8107
+ s
8108
+ )))))), /* @__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(
8095
8109
  "input",
8096
8110
  {
8097
8111
  type: "text",
@@ -10294,7 +10308,7 @@ function XViewScene({
10294
10308
  });
10295
10309
  }
10296
10310
  return {
10297
- node: effectiveNode || { id: nodeId, name: "Unknown" },
10311
+ ...effectiveNode ? { node: effectiveNode } : { node: { id: nodeId, name: "Unknown" } },
10298
10312
  relationship: treeItem.relationship || {},
10299
10313
  children: (treeItem.children || []).map(recursiveBuild).filter(Boolean),
10300
10314
  parallel_branches: processedBranches
@@ -11098,38 +11112,6 @@ function XViewScene({
11098
11112
  }
11099
11113
  }
11100
11114
  }
11101
- function handleCancelAncestryCreation() {
11102
- setAncestryMode({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", ancestryDescriptionSections: [], isAddingNodes: false });
11103
- if (mountRef.current) mountRef.current.style.cursor = "grab";
11104
- }
11105
- function handleKeyDown(event) {
11106
- var _a2, _b2, _c2, _d2;
11107
- const context = actionHandlerContext;
11108
- if (event.key === "Escape") {
11109
- if (stateRef.current.connection.isActive) userActionHandlers.handleCancelConnection(context);
11110
- if (stateRef.current.relink.isActive) userActionHandlers.handleCancelRelink(context);
11111
- if (stateRef.current.creation.isActive) userActionHandlers.handleCancelCreation(context);
11112
- if ((_a2 = stateRef.current.versionMode) == null ? void 0 : _a2.isActive) userActionHandlers.handleCancelVersioning(context);
11113
- if (stateRef.current.ancestry.isActive) handleCancelAncestryCreation();
11114
- if ((_b2 = context.questMode) == null ? void 0 : _b2.isActive) context.setters.setQuestMode({ isActive: false });
11115
- if (stateRef.current.selectedNodes.size > 0) {
11116
- stateRef.current.selectedNodes.clear();
11117
- }
11118
- setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
11119
- setMultiContextMenu((prev) => ({ ...prev, visible: false }));
11120
- setRelationshipMenu((prev) => ({ ...prev, visible: false }));
11121
- }
11122
- if (event.key.toLowerCase() === "q") {
11123
- const isUiClear = !stateRef.current.creation.isActive && !stateRef.current.connection.isActive && !stateRef.current.relink.isActive && !stateRef.current.ancestry.isActive && !((_c2 = context.versionMode) == null ? void 0 : _c2.isActive) && !contextMenu.visible && !multiContextMenu.visible && !relationshipMenu.visible && !readingMode.isActive && !isImportModalOpen && !isAncestryBoardOpen;
11124
- if (isUiClear) {
11125
- const isView = ((_d2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _d2.toLowerCase()) === "view";
11126
- if (!isView) {
11127
- return;
11128
- }
11129
- setQuestMode({ isActive: true });
11130
- }
11131
- }
11132
- }
11133
11115
  function handleDoubleClick(event) {
11134
11116
  if (stateRef.current.camera) stateRef.current.camera.layers.enableAll();
11135
11117
  if (isFromUiOverlay(event) || stateRef.current.isDragging || stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive) return;
@@ -11182,7 +11164,6 @@ function XViewScene({
11182
11164
  currentMount.addEventListener("dblclick", handleDoubleClick);
11183
11165
  currentMount.addEventListener("pointermove", onPointerMove);
11184
11166
  currentMount.addEventListener("contextmenu", handleContextMenu);
11185
- window.addEventListener("keydown", handleKeyDown);
11186
11167
  const originalBackground = scene.background;
11187
11168
  const clock = new THREE3.Clock();
11188
11169
  let animationFrameId = 0;
@@ -11320,7 +11301,6 @@ function XViewScene({
11320
11301
  return () => {
11321
11302
  cancelAnimationFrame(animationFrameId);
11322
11303
  window.removeEventListener("resize", handleResize);
11323
- window.removeEventListener("keydown", handleKeyDown);
11324
11304
  currentMount.removeEventListener("pointerdown", onPointerDown);
11325
11305
  currentMount.removeEventListener("pointerup", onPointerUp);
11326
11306
  currentMount.removeEventListener("dblclick", handleDoubleClick);
@@ -13124,6 +13104,62 @@ function XViewScene({
13124
13104
  }
13125
13105
  }
13126
13106
  }, [isInitialized, sceneVersion, focusAncestryId, hasOpenedInitialAncestry, handleStartReadingAncestry]);
13107
+ (0, import_react25.useEffect)(() => {
13108
+ function handleKeyDown(event) {
13109
+ var _a2, _b2, _c2, _d2;
13110
+ const context = actionHandlerContext;
13111
+ if (event.key === "Escape") {
13112
+ if (stateRef.current.connection.isActive) userActionHandlers.handleCancelConnection(context);
13113
+ if (stateRef.current.relink.isActive) userActionHandlers.handleCancelRelink(context);
13114
+ if (stateRef.current.creation.isActive) userActionHandlers.handleCancelCreation(context);
13115
+ if ((_a2 = stateRef.current.versionMode) == null ? void 0 : _a2.isActive) userActionHandlers.handleCancelVersioning(context);
13116
+ if (stateRef.current.ancestry.isActive) {
13117
+ setAncestryMode({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", ancestryDescriptionSections: [], isAddingNodes: false });
13118
+ if (mountRef.current) mountRef.current.style.cursor = "grab";
13119
+ }
13120
+ if ((_b2 = context.questMode) == null ? void 0 : _b2.isActive) context.setters.setQuestMode({ isActive: false });
13121
+ if (stateRef.current.selectedNodes.size > 0) {
13122
+ stateRef.current.selectedNodes.clear();
13123
+ }
13124
+ setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
13125
+ setMultiContextMenu((prev) => ({ ...prev, visible: false }));
13126
+ setRelationshipMenu((prev) => ({ ...prev, visible: false }));
13127
+ }
13128
+ if (event.key.toLowerCase() === "q") {
13129
+ const isUiClear = !stateRef.current.creation.isActive && !stateRef.current.connection.isActive && !stateRef.current.relink.isActive && !stateRef.current.ancestry.isActive && !((_c2 = context.versionMode) == null ? void 0 : _c2.isActive) && !contextMenu.visible && !multiContextMenu.visible && !relationshipMenu.visible && !readingMode.isActive && !isImportModalOpen && !isAncestryBoardOpen && !isSidebarOpen && // Condição nova
13130
+ !detailsNode && // Condição nova
13131
+ !detailsLink && // Condição nova
13132
+ !ancestryLinkDetails && // Condição nova
13133
+ !imageViewer.visible && // Condição nova
13134
+ !editingAncestryRel.visible && // Condição nova
13135
+ !questMode.isActive;
13136
+ if (isUiClear) {
13137
+ const isView = ((_d2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _d2.toLowerCase()) === "view";
13138
+ if (!isView) return;
13139
+ setQuestMode({ isActive: true });
13140
+ }
13141
+ }
13142
+ }
13143
+ window.addEventListener("keydown", handleKeyDown);
13144
+ return () => window.removeEventListener("keydown", handleKeyDown);
13145
+ }, [
13146
+ // Dependências: sempre que um painel abrir ou fechar, o React atualiza o listener com os dados frescos
13147
+ contextMenu.visible,
13148
+ multiContextMenu.visible,
13149
+ relationshipMenu.visible,
13150
+ readingMode.isActive,
13151
+ isImportModalOpen,
13152
+ isAncestryBoardOpen,
13153
+ isSidebarOpen,
13154
+ detailsNode,
13155
+ detailsLink,
13156
+ ancestryLinkDetails,
13157
+ imageViewer.visible,
13158
+ editingAncestryRel.visible,
13159
+ questMode.isActive,
13160
+ viewParams,
13161
+ actionHandlerContext
13162
+ ]);
13127
13163
  if (isLoading || status === "loading" || permissionStatus === "loading") {
13128
13164
  return /* @__PURE__ */ import_react25.default.createElement(LoadingScreen, null);
13129
13165
  }
package/dist/index.mjs CHANGED
@@ -7959,16 +7959,12 @@ function InSceneVersionForm({
7959
7959
 
7960
7960
  // src/components/InSceneQuestForm.jsx
7961
7961
  import React15, { useState as useState16, useRef as useRef12 } from "react";
7962
- import { FiPlus as FiPlus5, FiCheck as FiCheck9, FiEdit2 as FiEdit26, FiTarget, FiX as FiX4 } from "react-icons/fi";
7962
+ import { FiPlus as FiPlus5, FiCheck as FiCheck9, FiEdit2 as FiEdit26, FiTarget, FiX as FiX4, FiChevronDown as FiChevronDown5 } from "react-icons/fi";
7963
7963
  var QUEST_STATUS_COLORS = {
7964
7964
  "Backlog": "#64748b",
7965
- // Slate (Cinza azulado)
7966
7965
  "In Progress": "#eab308",
7967
- // Yellow (Amarelo)
7968
7966
  "Review": "#a855f7",
7969
- // Purple (Roxo)
7970
7967
  "Done": "#22c55e"
7971
- // Green (Verde)
7972
7968
  };
7973
7969
  function InSceneQuestForm({
7974
7970
  onSave,
@@ -7988,6 +7984,7 @@ function InSceneQuestForm({
7988
7984
  const [size, setSize] = useState16("medium");
7989
7985
  const [intensity, setIntensity] = useState16(0);
7990
7986
  const [description, setDescription] = useState16("");
7987
+ const [isStatusDropdownOpen, setIsStatusDropdownOpen] = useState16(false);
7991
7988
  const [customProps, setCustomProps] = useState16([]);
7992
7989
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState16(false);
7993
7990
  const propsEndRef = useRef12(null);
@@ -8038,7 +8035,6 @@ function InSceneQuestForm({
8038
8035
  name: name.trim(),
8039
8036
  type: types,
8040
8037
  color: QUEST_STATUS_COLORS[status],
8041
- // Cor atrelada ao status
8042
8038
  status,
8043
8039
  size,
8044
8040
  intensity,
@@ -8065,20 +8061,38 @@ function InSceneQuestForm({
8065
8061
  onDoubleClick: swallow
8066
8062
  },
8067
8063
  /* @__PURE__ */ React15.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS[status]}, transparent)` } }),
8068
- /* @__PURE__ */ React15.createElement("div", { className: "px-6 pt-5 pb-3" }, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React15.createElement(FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ React15.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Nova Tarefa / Objetivo")), /* @__PURE__ */ React15.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Criar Quest")),
8069
- /* @__PURE__ */ React15.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col max-h-[68vh]" }, /* @__PURE__ */ React15.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ React15.createElement(
8070
- "select",
8071
- {
8072
- value: status,
8073
- onChange: (e) => setStatus(e.target.value),
8074
- 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 appearance-none cursor-pointer",
8075
- style: { borderLeft: `4px solid ${QUEST_STATUS_COLORS[status]}` }
8064
+ /* @__PURE__ */ React15.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React15.createElement(FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ React15.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Nova Tarefa / Objetivo")), /* @__PURE__ */ React15.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Criar Quest")), /* @__PURE__ */ React15.createElement(
8065
+ "button",
8066
+ {
8067
+ type: "button",
8068
+ onClick: onCancel,
8069
+ 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",
8070
+ title: "Fechar"
8076
8071
  },
8077
- /* @__PURE__ */ React15.createElement("option", { value: "Backlog" }, "Backlog"),
8078
- /* @__PURE__ */ React15.createElement("option", { value: "In Progress" }, "In Progress"),
8079
- /* @__PURE__ */ React15.createElement("option", { value: "Review" }, "Review"),
8080
- /* @__PURE__ */ React15.createElement("option", { value: "Done" }, "Done")
8081
- )), /* @__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(
8072
+ "\xD7"
8073
+ )),
8074
+ /* @__PURE__ */ React15.createElement("form", { onSubmit: handleSubmit, className: "flex flex-col max-h-[68vh]" }, /* @__PURE__ */ React15.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
8075
+ "button",
8076
+ {
8077
+ type: "button",
8078
+ onClick: () => setIsStatusDropdownOpen(!isStatusDropdownOpen),
8079
+ className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 hover:border-white/20 focus:outline-none focus:ring-2 focus:ring-indigo-400/60 transition-colors flex items-center justify-between"
8080
+ },
8081
+ /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement("span", { className: "w-3 h-3 rounded-full shadow-[0_0_8px_rgba(0,0,0,0.5)]", style: { backgroundColor: QUEST_STATUS_COLORS[status] } }), /* @__PURE__ */ React15.createElement("span", { className: "text-slate-200 font-medium" }, status)),
8082
+ /* @__PURE__ */ React15.createElement(FiChevronDown5, { className: `text-slate-400 transition-transform duration-200 ${isStatusDropdownOpen ? "rotate-180" : ""}` })
8083
+ ), isStatusDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsStatusDropdownOpen(false) }), /* @__PURE__ */ React15.createElement("ul", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-800 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden" }, Object.keys(QUEST_STATUS_COLORS).map((s) => /* @__PURE__ */ React15.createElement(
8084
+ "li",
8085
+ {
8086
+ key: s,
8087
+ onClick: () => {
8088
+ setStatus(s);
8089
+ setIsStatusDropdownOpen(false);
8090
+ },
8091
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${status === s ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
8092
+ },
8093
+ /* @__PURE__ */ React15.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS[s] } }),
8094
+ s
8095
+ )))))), /* @__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(
8082
8096
  "input",
8083
8097
  {
8084
8098
  type: "text",
@@ -10294,7 +10308,7 @@ function XViewScene({
10294
10308
  });
10295
10309
  }
10296
10310
  return {
10297
- node: effectiveNode || { id: nodeId, name: "Unknown" },
10311
+ ...effectiveNode ? { node: effectiveNode } : { node: { id: nodeId, name: "Unknown" } },
10298
10312
  relationship: treeItem.relationship || {},
10299
10313
  children: (treeItem.children || []).map(recursiveBuild).filter(Boolean),
10300
10314
  parallel_branches: processedBranches
@@ -11098,38 +11112,6 @@ function XViewScene({
11098
11112
  }
11099
11113
  }
11100
11114
  }
11101
- function handleCancelAncestryCreation() {
11102
- setAncestryMode({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", ancestryDescriptionSections: [], isAddingNodes: false });
11103
- if (mountRef.current) mountRef.current.style.cursor = "grab";
11104
- }
11105
- function handleKeyDown(event) {
11106
- var _a2, _b2, _c2, _d2;
11107
- const context = actionHandlerContext;
11108
- if (event.key === "Escape") {
11109
- if (stateRef.current.connection.isActive) userActionHandlers.handleCancelConnection(context);
11110
- if (stateRef.current.relink.isActive) userActionHandlers.handleCancelRelink(context);
11111
- if (stateRef.current.creation.isActive) userActionHandlers.handleCancelCreation(context);
11112
- if ((_a2 = stateRef.current.versionMode) == null ? void 0 : _a2.isActive) userActionHandlers.handleCancelVersioning(context);
11113
- if (stateRef.current.ancestry.isActive) handleCancelAncestryCreation();
11114
- if ((_b2 = context.questMode) == null ? void 0 : _b2.isActive) context.setters.setQuestMode({ isActive: false });
11115
- if (stateRef.current.selectedNodes.size > 0) {
11116
- stateRef.current.selectedNodes.clear();
11117
- }
11118
- setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
11119
- setMultiContextMenu((prev) => ({ ...prev, visible: false }));
11120
- setRelationshipMenu((prev) => ({ ...prev, visible: false }));
11121
- }
11122
- if (event.key.toLowerCase() === "q") {
11123
- const isUiClear = !stateRef.current.creation.isActive && !stateRef.current.connection.isActive && !stateRef.current.relink.isActive && !stateRef.current.ancestry.isActive && !((_c2 = context.versionMode) == null ? void 0 : _c2.isActive) && !contextMenu.visible && !multiContextMenu.visible && !relationshipMenu.visible && !readingMode.isActive && !isImportModalOpen && !isAncestryBoardOpen;
11124
- if (isUiClear) {
11125
- const isView = ((_d2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _d2.toLowerCase()) === "view";
11126
- if (!isView) {
11127
- return;
11128
- }
11129
- setQuestMode({ isActive: true });
11130
- }
11131
- }
11132
- }
11133
11115
  function handleDoubleClick(event) {
11134
11116
  if (stateRef.current.camera) stateRef.current.camera.layers.enableAll();
11135
11117
  if (isFromUiOverlay(event) || stateRef.current.isDragging || stateRef.current.creation.isActive || stateRef.current.connection.isActive || stateRef.current.relink.isActive) return;
@@ -11182,7 +11164,6 @@ function XViewScene({
11182
11164
  currentMount.addEventListener("dblclick", handleDoubleClick);
11183
11165
  currentMount.addEventListener("pointermove", onPointerMove);
11184
11166
  currentMount.addEventListener("contextmenu", handleContextMenu);
11185
- window.addEventListener("keydown", handleKeyDown);
11186
11167
  const originalBackground = scene.background;
11187
11168
  const clock = new THREE3.Clock();
11188
11169
  let animationFrameId = 0;
@@ -11320,7 +11301,6 @@ function XViewScene({
11320
11301
  return () => {
11321
11302
  cancelAnimationFrame(animationFrameId);
11322
11303
  window.removeEventListener("resize", handleResize);
11323
- window.removeEventListener("keydown", handleKeyDown);
11324
11304
  currentMount.removeEventListener("pointerdown", onPointerDown);
11325
11305
  currentMount.removeEventListener("pointerup", onPointerUp);
11326
11306
  currentMount.removeEventListener("dblclick", handleDoubleClick);
@@ -13124,6 +13104,62 @@ function XViewScene({
13124
13104
  }
13125
13105
  }
13126
13106
  }, [isInitialized, sceneVersion, focusAncestryId, hasOpenedInitialAncestry, handleStartReadingAncestry]);
13107
+ useEffect21(() => {
13108
+ function handleKeyDown(event) {
13109
+ var _a2, _b2, _c2, _d2;
13110
+ const context = actionHandlerContext;
13111
+ if (event.key === "Escape") {
13112
+ if (stateRef.current.connection.isActive) userActionHandlers.handleCancelConnection(context);
13113
+ if (stateRef.current.relink.isActive) userActionHandlers.handleCancelRelink(context);
13114
+ if (stateRef.current.creation.isActive) userActionHandlers.handleCancelCreation(context);
13115
+ if ((_a2 = stateRef.current.versionMode) == null ? void 0 : _a2.isActive) userActionHandlers.handleCancelVersioning(context);
13116
+ if (stateRef.current.ancestry.isActive) {
13117
+ setAncestryMode({ isActive: false, tree: null, selectedParentId: null, isEditMode: false, currentAncestryId: null, ancestryName: "", ancestryDescription: "", ancestryDescriptionSections: [], isAddingNodes: false });
13118
+ if (mountRef.current) mountRef.current.style.cursor = "grab";
13119
+ }
13120
+ if ((_b2 = context.questMode) == null ? void 0 : _b2.isActive) context.setters.setQuestMode({ isActive: false });
13121
+ if (stateRef.current.selectedNodes.size > 0) {
13122
+ stateRef.current.selectedNodes.clear();
13123
+ }
13124
+ setContextMenu((prev) => prev.visible ? { ...prev, visible: false } : prev);
13125
+ setMultiContextMenu((prev) => ({ ...prev, visible: false }));
13126
+ setRelationshipMenu((prev) => ({ ...prev, visible: false }));
13127
+ }
13128
+ if (event.key.toLowerCase() === "q") {
13129
+ const isUiClear = !stateRef.current.creation.isActive && !stateRef.current.connection.isActive && !stateRef.current.relink.isActive && !stateRef.current.ancestry.isActive && !((_c2 = context.versionMode) == null ? void 0 : _c2.isActive) && !contextMenu.visible && !multiContextMenu.visible && !relationshipMenu.visible && !readingMode.isActive && !isImportModalOpen && !isAncestryBoardOpen && !isSidebarOpen && // Condição nova
13130
+ !detailsNode && // Condição nova
13131
+ !detailsLink && // Condição nova
13132
+ !ancestryLinkDetails && // Condição nova
13133
+ !imageViewer.visible && // Condição nova
13134
+ !editingAncestryRel.visible && // Condição nova
13135
+ !questMode.isActive;
13136
+ if (isUiClear) {
13137
+ const isView = ((_d2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _d2.toLowerCase()) === "view";
13138
+ if (!isView) return;
13139
+ setQuestMode({ isActive: true });
13140
+ }
13141
+ }
13142
+ }
13143
+ window.addEventListener("keydown", handleKeyDown);
13144
+ return () => window.removeEventListener("keydown", handleKeyDown);
13145
+ }, [
13146
+ // Dependências: sempre que um painel abrir ou fechar, o React atualiza o listener com os dados frescos
13147
+ contextMenu.visible,
13148
+ multiContextMenu.visible,
13149
+ relationshipMenu.visible,
13150
+ readingMode.isActive,
13151
+ isImportModalOpen,
13152
+ isAncestryBoardOpen,
13153
+ isSidebarOpen,
13154
+ detailsNode,
13155
+ detailsLink,
13156
+ ancestryLinkDetails,
13157
+ imageViewer.visible,
13158
+ editingAncestryRel.visible,
13159
+ questMode.isActive,
13160
+ viewParams,
13161
+ actionHandlerContext
13162
+ ]);
13127
13163
  if (isLoading || status === "loading" || permissionStatus === "loading") {
13128
13164
  return /* @__PURE__ */ React24.createElement(LoadingScreen, null);
13129
13165
  }
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.13",
3
+ "version": "1.2.4-dev.15",
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",