@lv-x-software-house/x_view 1.2.5-dev.2 → 1.2.5-dev.20

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 +832 -155
  2. package/dist/index.mjs +836 -160
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  get_ancestry_file_logic: () => get_ancestry_file_logic,
36
36
  get_scene_view_data_logic: () => get_scene_view_data_logic,
37
37
  get_single_parent_file_logic: () => get_single_parent_file_logic,
38
+ get_view_members_logic: () => get_view_members_logic,
38
39
  import_parent_file_modal_get_logic: () => import_parent_file_modal_get_logic,
39
40
  save_ancestry_board_logic: () => save_ancestry_board_logic,
40
41
  save_view_data_logic: () => save_view_data_logic,
@@ -141,7 +142,10 @@ function ContextMenu({
141
142
  onEditAncestry,
142
143
  onDeleteAncestry,
143
144
  onFocusNode,
144
- onClose
145
+ onClose,
146
+ viewMembers,
147
+ currentUser,
148
+ onStartQuest
145
149
  }) {
146
150
  var _a, _b, _c;
147
151
  const menuRef = (0, import_react.useRef)(null);
@@ -149,6 +153,7 @@ function ContextMenu({
149
153
  const [menuView, setMenuView] = (0, import_react.useState)("main");
150
154
  const [selectedAncestry, setSelectedAncestry] = (0, import_react.useState)(null);
151
155
  const [versionSubMenu, setVersionSubMenu] = (0, import_react.useState)(null);
156
+ const [labelSubMenu, setLabelSubMenu] = (0, import_react.useState)(null);
152
157
  const [isLinkCopied, setIsLinkCopied] = (0, import_react.useState)(false);
153
158
  const [selectedQuestStatus, setSelectedQuestStatus] = (0, import_react.useState)(null);
154
159
  const ability = (0, import_react.useMemo)(() => defineAbilityFor(userRole), [userRole]);
@@ -157,6 +162,7 @@ function ContextMenu({
157
162
  setMenuView("main");
158
163
  setSelectedAncestry(null);
159
164
  setVersionSubMenu(null);
165
+ setLabelSubMenu(null);
160
166
  setSelectedQuestStatus(null);
161
167
  }
162
168
  }, [data.visible, (_a = data.nodeData) == null ? void 0 : _a.id]);
@@ -172,7 +178,7 @@ function ContextMenu({
172
178
  if (left + w + 8 > vw) left = Math.max(8, vw - w - 8);
173
179
  if (top + h + 8 > vh) top = Math.max(8, vh - h - 8);
174
180
  setMenuPos({ left, top });
175
- }, [data, menuView, versionSubMenu, selectedQuestStatus]);
181
+ }, [data, menuView, versionSubMenu, labelSubMenu, selectedQuestStatus]);
176
182
  (0, import_react.useEffect)(() => {
177
183
  if (!data.visible) return;
178
184
  const handleClickOutside = (e) => {
@@ -224,7 +230,21 @@ function ContextMenu({
224
230
  var _a2;
225
231
  return (_a2 = c.targetNode) == null ? void 0 : _a2.is_quest;
226
232
  });
227
- const groupedConnections = commonConnections.reduce((acc, conn) => {
233
+ const getLabelForConnection = (conn) => {
234
+ if (conn.direction === "outgoing") return conn.link.source_label || null;
235
+ if (conn.direction === "incoming") return conn.link.target_label || null;
236
+ return null;
237
+ };
238
+ const commonWithLabel = commonConnections.filter((c) => getLabelForConnection(c));
239
+ const commonWithoutLabel = commonConnections.filter((c) => !getLabelForConnection(c));
240
+ const labelGroups = commonWithLabel.reduce((acc, conn) => {
241
+ const label = getLabelForConnection(conn);
242
+ if (!acc[label]) acc[label] = [];
243
+ acc[label].push(conn);
244
+ return acc;
245
+ }, {});
246
+ const labelGroupEntries = Object.entries(labelGroups).sort(([a], [b]) => a.localeCompare(b));
247
+ const groupedConnections = commonWithoutLabel.reduce((acc, conn) => {
228
248
  var _a2;
229
249
  const { targetNode } = conn;
230
250
  const groupingKey = ((_a2 = targetNode.version_node) == null ? void 0 : _a2.is_version) ? targetNode.version_node.parent_node : targetNode.id;
@@ -326,12 +346,25 @@ function ContextMenu({
326
346
  });
327
347
  };
328
348
  const renderMainView = () => {
329
- var _a2;
349
+ var _a2, _b2, _c2;
330
350
  const hasVersions = computedVersions.length > 0;
331
351
  const canCreateVersion = ability.can("create", "Versioning");
332
352
  const canReadVersion = ability.can("read", "Versioning");
333
353
  const shouldShowVersioningBtn = canCreateVersion || canReadVersion && hasVersions;
334
- return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_12px_1px_rgba(99,102,241,0.5)]" }), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, ability.can("create", "Connection") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conectar")), ability.can("create", "Node") && !((_a2 = data.nodeData) == null ? void 0 : _a2.is_quest) && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar Ancestralidade" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2-.8 2 .8s1.5-.3 2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("line", { x1: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ import_react.default.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
354
+ const canStartQuest = isCurrentNodeQuest && (((_a2 = data.nodeData) == null ? void 0 : _a2.status) !== "In Progress" || ((_b2 = data.nodeData) == null ? void 0 : _b2.assignee_id) !== (currentUser == null ? void 0 : currentUser.id));
355
+ return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_12px_1px_rgba(99,102,241,0.5)]" }), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, canStartQuest && /* @__PURE__ */ import_react.default.createElement(
356
+ "button",
357
+ {
358
+ onClick: () => {
359
+ onStartQuest == null ? void 0 : onStartQuest(data.nodeData);
360
+ onClose();
361
+ },
362
+ className: `w-full flex items-center gap-2.5 px-2 py-1.5 text-left text-sm rounded-md bg-yellow-500/10 text-yellow-400 hover:bg-yellow-500/20 hover:text-yellow-300 transition-colors duration-150 truncate font-semibold shadow-inner`,
363
+ title: "Atribuir a mim e iniciar"
364
+ },
365
+ /* @__PURE__ */ import_react.default.createElement("span", null, "\u{1F680}"),
366
+ /* @__PURE__ */ import_react.default.createElement("span", null, "Iniciar Quest")
367
+ ), ability.can("create", "Connection") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conectar")), ability.can("create", "Node") && !((_c2 = data.nodeData) == null ? void 0 : _c2.is_quest) && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar Ancestralidade" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2-.8 2 .8s1.5-.3 2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("line", { x1: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ import_react.default.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
335
368
  onFocusNode == null ? void 0 : onFocusNode(data.nodeData);
336
369
  onClose();
337
370
  }, className: baseButtonClass, title: "Focar na c\xE2mera" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Focar neste Node")), /* @__PURE__ */ import_react.default.createElement("button", { onClick: (e) => handleCopyLink(e, data.nodeData), className: baseButtonClass, title: "Copiar Link para Compartilhar" }, isLinkCopied ? /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#4ade80", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("polyline", { points: "20 6 9 17 4 12" })) : /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ import_react.default.createElement("span", { className: isLinkCopied ? "text-green-400" : "" }, isLinkCopied ? "Copiado!" : "Copiar Link")), ability.can("dismiss", "Node") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDismissNode == null ? void 0 : onDismissNode(data.nodeData), className: baseButtonClass, title: "Remover da visualiza\xE7\xE3o" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "2", y1: "2", x2: "22", y2: "22" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Dismiss")), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDismissOtherNodes == null ? void 0 : onDismissOtherNodes(data.nodeData), className: baseButtonClass, title: "Remover outros da visualiza\xE7\xE3o" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Dismiss other nodes"))), ability.can("delete", "Node") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("deleteConfirmation"), className: deleteButtonClass, title: "Excluir Node" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Excluir Node"))));
@@ -352,11 +385,32 @@ function ContextMenu({
352
385
  /* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
353
386
  ))));
354
387
  };
388
+ const renderLabelSubMenuView = () => {
389
+ const group = labelSubMenu;
390
+ const isScrollable = group.connections.length > 10;
391
+ return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
392
+ setLabelSubMenu(null);
393
+ setMenuView("connections");
394
+ }, className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-violet-300/80 truncate", title: group.label }, group.label))), group.connections.length > 0 && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => handleExpandAndClose(group.connections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ import_react.default.createElement("div", { className: `flex flex-col gap-1 ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, group.connections.map((conn) => /* @__PURE__ */ import_react.default.createElement(
395
+ "button",
396
+ {
397
+ key: conn.targetNode.id,
398
+ onClick: () => handleExpandAndClose([conn.link]),
399
+ className: baseButtonClass,
400
+ title: `Expandir conex\xE3o com ${conn.targetNode.name}`
401
+ },
402
+ /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }), /* @__PURE__ */ import_react.default.createElement("polyline", { points: "12 5 19 12 12 19" })),
403
+ /* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
404
+ ))));
405
+ };
355
406
  const renderConnectionsView = () => {
356
407
  if (versionSubMenu) {
357
408
  return renderVersionSubMenuView();
358
409
  }
359
- const totalItems = availableAncestries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
410
+ if (labelSubMenu) {
411
+ return renderLabelSubMenuView();
412
+ }
413
+ const totalItems = availableAncestries.length + labelGroupEntries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
360
414
  const isScrollable = totalItems > 10;
361
415
  return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("main"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Conex\xF5es")), commonConnections.length > 0 && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => handleExpandAndClose(commonConnections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ import_react.default.createElement("div", { className: `flex flex-col ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: `flex flex-col gap-1 ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, /* @__PURE__ */ import_react.default.createElement("div", { className: "px-2 py-1 text-[11px] uppercase tracking-wider text-indigo-400/90" }, "Ancestralidades Salvas"), availableAncestries.map((anc) => /* @__PURE__ */ import_react.default.createElement(
362
416
  "button",
@@ -368,7 +422,20 @@ function ContextMenu({
368
422
  },
369
423
  /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M6 3v4a2 2 0 0 0 2 2h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 3v4a2 2 0 0 1-2 2h-4" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "3", r: "2" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "3", r: "2" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "11", r: "2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 13v6" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "21", r: "2" })),
370
424
  /* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, anc.name || `Ancestralidade #${anc.ancestry_id.substring(0, 8)}`)
371
- ))), finalRenderableConnections.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, availableAncestries.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
425
+ ))), labelGroupEntries.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: `flex flex-col gap-1 ${availableAncestries.length > 0 ? "mt-2" : ""} ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), labelGroupEntries.map(([label, conns]) => /* @__PURE__ */ import_react.default.createElement(
426
+ "button",
427
+ {
428
+ key: label,
429
+ onClick: () => {
430
+ setLabelSubMenu({ label, connections: conns });
431
+ },
432
+ className: baseButtonClass,
433
+ title: `Ver grupo: ${label}`
434
+ },
435
+ /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })),
436
+ /* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, label),
437
+ /* @__PURE__ */ import_react.default.createElement("span", { className: "text-xs px-2 py-0.5 bg-violet-500/20 text-violet-300 rounded-full" }, conns.length)
438
+ ))), finalRenderableConnections.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, (availableAncestries.length > 0 || labelGroupEntries.length > 0) && /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
372
439
  if (group.isVersionGroup) {
373
440
  return /* @__PURE__ */ import_react.default.createElement(
374
441
  "button",
@@ -552,15 +619,18 @@ function XViewSidebar({
552
619
  const [isFilterMenuOpen, setIsFilterMenuOpen] = (0, import_react2.useState)(false);
553
620
  const [filterCategory, setFilterCategory] = (0, import_react2.useState)("Type");
554
621
  const [filterValue, setFilterValue] = (0, import_react2.useState)("");
622
+ const [isTypeDropdownOpen, setIsTypeDropdownOpen] = (0, import_react2.useState)(false);
623
+ const [highlightedTypeIndex, setHighlightedTypeIndex] = (0, import_react2.useState)(-1);
555
624
  const containerRef = (0, import_react2.useRef)(null);
556
625
  const inputRef = (0, import_react2.useRef)(null);
557
626
  const filterMenuRef = (0, import_react2.useRef)(null);
627
+ const typeDropdownRef = (0, import_react2.useRef)(null);
558
628
  const ability = (0, import_react2.useMemo)(() => {
559
629
  return defineAbilityFor(userRole);
560
630
  }, [userRole]);
561
631
  const contextLabel = (0, import_react2.useMemo)(() => {
562
632
  if (!viewType || !viewName) return null;
563
- const typeLower = viewType.toLowerCase();
633
+ const typeLower = String(viewType).toLowerCase();
564
634
  if (typeLower === "database") {
565
635
  return `Dataset: ${viewName}`;
566
636
  } else if (typeLower === "view") {
@@ -570,7 +640,12 @@ function XViewSidebar({
570
640
  }
571
641
  return `${viewType}: ${viewName}`;
572
642
  }, [viewType, viewName]);
573
- const normalize = (str = "") => String(str).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[._\-–—:,;!?'"()\[\]{}/\\]/g, " ").replace(/\s+/g, " ").trim();
643
+ const normalize = (str = "") => {
644
+ if (str === void 0 || str === null) {
645
+ console.warn("XViewSidebar: normalize recebeu valor nulo/indefinido:", str);
646
+ }
647
+ return String(str).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[._\-–—:,;!?'"()\[\]{}/\\]/g, " ").replace(/\s+/g, " ").trim();
648
+ };
574
649
  const collator = (0, import_react2.useMemo)(
575
650
  () => new Intl.Collator("pt-BR", { sensitivity: "base", numeric: true }),
576
651
  []
@@ -579,13 +654,30 @@ function XViewSidebar({
579
654
  const typesSet = /* @__PURE__ */ new Set();
580
655
  (dbNodes || []).forEach((node) => {
581
656
  if (Array.isArray(node.type)) {
582
- node.type.forEach((t) => typesSet.add(t));
583
- } else if (node.type) {
657
+ node.type.forEach((t) => {
658
+ if (t && typeof t === "string" && t.trim() !== "") typesSet.add(t);
659
+ });
660
+ } else if (node.type && typeof node.type === "string" && node.type.trim() !== "") {
584
661
  typesSet.add(node.type);
585
662
  }
586
663
  });
587
664
  return Array.from(typesSet).sort();
588
665
  }, [dbNodes]);
666
+ const availableDatasets = (0, import_react2.useMemo)(() => {
667
+ const datasetsSet = /* @__PURE__ */ new Set();
668
+ (dbNodes || []).forEach((node) => {
669
+ if (node.dataset_name) {
670
+ datasetsSet.add(node.dataset_name);
671
+ }
672
+ });
673
+ return Array.from(datasetsSet).sort();
674
+ }, [dbNodes]);
675
+ const filteredAutocompleteOptions = (0, import_react2.useMemo)(() => {
676
+ const source = filterCategory === "Dataset" ? availableDatasets : availableTypes;
677
+ if (!filterValue.trim()) return source;
678
+ const search = filterValue.toLowerCase().trim();
679
+ return source.filter((item) => item.toLowerCase().includes(search));
680
+ }, [availableTypes, availableDatasets, filterValue, filterCategory]);
589
681
  const filteredAndSorted = (0, import_react2.useMemo)(() => {
590
682
  const base = Array.isArray(dbNodes) ? dbNodes : [];
591
683
  let pool = base;
@@ -605,6 +697,8 @@ function XViewSidebar({
605
697
  return nodeTypes.some((t) => String(t).toLowerCase() === String(filter.value).toLowerCase());
606
698
  } else if (filter.type === "Color") {
607
699
  return String(node.color).toLowerCase() === String(filter.value).toLowerCase();
700
+ } else if (filter.type === "Dataset") {
701
+ return String(node.dataset_name || "").toLowerCase() === String(filter.value).toLowerCase();
608
702
  }
609
703
  return true;
610
704
  });
@@ -620,18 +714,21 @@ function XViewSidebar({
620
714
  inView.sort(byName);
621
715
  const ordered = [...notInView, ...inView];
622
716
  return ordered.slice(0, 200);
623
- }, [dbNodes, query, isNodeInView, viewVersion, activeFilters]);
717
+ }, [dbNodes, query, isNodeInView, viewVersion, activeFilters, filterCategory]);
624
718
  (0, import_react2.useEffect)(() => {
625
719
  const handleClickOutside = (event) => {
626
720
  if (filterMenuRef.current && !filterMenuRef.current.contains(event.target)) {
627
721
  setIsFilterMenuOpen(false);
628
722
  }
723
+ if (typeDropdownRef.current && !typeDropdownRef.current.contains(event.target)) {
724
+ setIsTypeDropdownOpen(false);
725
+ }
629
726
  };
630
- if (isFilterMenuOpen) {
727
+ if (isFilterMenuOpen || isTypeDropdownOpen) {
631
728
  document.addEventListener("mousedown", handleClickOutside);
632
729
  }
633
730
  return () => document.removeEventListener("mousedown", handleClickOutside);
634
- }, [isFilterMenuOpen]);
731
+ }, [isFilterMenuOpen, isTypeDropdownOpen]);
635
732
  (0, import_react2.useEffect)(() => {
636
733
  if (!query) setSelectedNodeId(null);
637
734
  }, [query]);
@@ -685,6 +782,35 @@ function XViewSidebar({
685
782
  const handleRemoveFilter = (index) => {
686
783
  setActiveFilters((prev) => prev.filter((_, i) => i !== index));
687
784
  };
785
+ const handleTypeKeyDown = (e) => {
786
+ if (!isTypeDropdownOpen) {
787
+ setIsTypeDropdownOpen(true);
788
+ }
789
+ if (e.key === "ArrowDown") {
790
+ e.preventDefault();
791
+ setHighlightedTypeIndex(
792
+ (prev) => prev < filteredAutocompleteOptions.length - 1 ? prev + 1 : prev
793
+ );
794
+ } else if (e.key === "ArrowUp") {
795
+ e.preventDefault();
796
+ setHighlightedTypeIndex((prev) => prev > 0 ? prev - 1 : 0);
797
+ } else if (e.key === "Enter") {
798
+ e.preventDefault();
799
+ if (highlightedTypeIndex >= 0 && filteredAutocompleteOptions[highlightedTypeIndex]) {
800
+ setFilterValue(filteredAutocompleteOptions[highlightedTypeIndex]);
801
+ setIsTypeDropdownOpen(false);
802
+ setHighlightedTypeIndex(-1);
803
+ } else {
804
+ handleAddFilter();
805
+ }
806
+ } else if (e.key === "Escape") {
807
+ e.preventDefault();
808
+ setIsTypeDropdownOpen(false);
809
+ setHighlightedTypeIndex(-1);
810
+ } else if (e.key === "Tab") {
811
+ setIsTypeDropdownOpen(false);
812
+ }
813
+ };
688
814
  const ToggleButton = /* @__PURE__ */ import_react2.default.createElement(
689
815
  "button",
690
816
  {
@@ -704,7 +830,7 @@ function XViewSidebar({
704
830
  "div",
705
831
  {
706
832
  ref: containerRef,
707
- className: "ui-overlay fixed left-0 top-0 h-full w-[min(92vw,320px)] z-40",
833
+ className: "ui-overlay fixed left-0 top-0 h-[100dvh] w-[min(92vw,320px)] z-40 overflow-hidden",
708
834
  onPointerDown: swallow,
709
835
  onPointerMove: swallow,
710
836
  onPointerUp: swallow,
@@ -713,7 +839,7 @@ function XViewSidebar({
713
839
  onContextMenu: swallow,
714
840
  onDoubleClick: swallow
715
841
  },
716
- /* @__PURE__ */ import_react2.default.createElement("div", { className: "h-full flex flex-col bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ import_react2.default.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ import_react2.default.createElement(
842
+ /* @__PURE__ */ import_react2.default.createElement("div", { className: "h-full flex flex-col overflow-hidden bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ import_react2.default.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ import_react2.default.createElement(
717
843
  "button",
718
844
  {
719
845
  className: "ml-auto p-2 rounded-md text-slate-400 hover:text-white hover:bg-white/10 transition-colors",
@@ -770,7 +896,7 @@ function XViewSidebar({
770
896
  autoComplete: "off"
771
897
  }
772
898
  )
773
- ), showList && /* @__PURE__ */ import_react2.default.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[28rem] overflow-y-auto rounded-lg bg-slate-900/95 border border-white/10 shadow-xl" }, filteredAndSorted.length > 0 ? filteredAndSorted.map((n) => {
899
+ ), showList && /* @__PURE__ */ import_react2.default.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[min(448px,50dvh)] overflow-y-auto rounded-lg bg-slate-900/95 border border-white/10 shadow-xl" }, filteredAndSorted.length > 0 ? filteredAndSorted.map((n) => {
774
900
  const inView = isNodeInView(n.id);
775
901
  const active = selectedNodeId === n.id;
776
902
  const typeLabel = Array.isArray(n.type) ? n.type.join(", ") : n.type;
@@ -804,7 +930,7 @@ function XViewSidebar({
804
930
  },
805
931
  /* @__PURE__ */ import_react2.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" })),
806
932
  "Filters"
807
- ), isFilterMenuOpen && /* @__PURE__ */ import_react2.default.createElement("div", { className: "absolute right-0 top-full mt-2 w-56 p-3 rounded-lg bg-slate-900 border border-white/10 shadow-xl z-50 flex flex-col gap-3 animate-in fade-in zoom-in-95 duration-100" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color"].map((opt) => /* @__PURE__ */ import_react2.default.createElement(
933
+ ), isFilterMenuOpen && /* @__PURE__ */ import_react2.default.createElement("div", { className: "absolute right-0 top-full mt-2 w-56 p-3 rounded-lg bg-slate-900 border border-white/10 shadow-xl z-50 flex flex-col gap-3 animate-in fade-in zoom-in-95 duration-100" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color", "Dataset"].map((opt) => /* @__PURE__ */ import_react2.default.createElement(
808
934
  "button",
809
935
  {
810
936
  key: opt,
@@ -815,18 +941,36 @@ function XViewSidebar({
815
941
  className: `flex-1 text-xs py-1 rounded transition-colors ${filterCategory === opt ? "bg-indigo-600 text-white shadow-sm" : "text-slate-400 hover:text-slate-200"}`
816
942
  },
817
943
  opt
818
- )))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Valor"), filterCategory === "Type" ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react2.default.createElement(
944
+ )))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Valor"), filterCategory === "Type" || filterCategory === "Dataset" ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative", ref: typeDropdownRef }, /* @__PURE__ */ import_react2.default.createElement(
819
945
  "input",
820
946
  {
821
- list: "typeOptions",
822
947
  type: "text",
823
948
  value: filterValue,
824
- onChange: (e) => setFilterValue(e.target.value),
825
- placeholder: "Ex: Concept",
949
+ onChange: (e) => {
950
+ setFilterValue(e.target.value);
951
+ if (!isTypeDropdownOpen) setIsTypeDropdownOpen(true);
952
+ setHighlightedTypeIndex(-1);
953
+ },
954
+ onFocus: () => setIsTypeDropdownOpen(true),
955
+ onKeyDown: handleTypeKeyDown,
956
+ placeholder: filterCategory === "Type" ? "Ex: Concept" : "Ex: Dataset 1",
826
957
  className: "w-full bg-slate-800 p-2 text-xs rounded border border-white/10 focus:outline-none focus:border-indigo-500/50 text-white",
827
958
  autoFocus: true
828
959
  }
829
- ), /* @__PURE__ */ import_react2.default.createElement("datalist", { id: "typeOptions" }, availableTypes.map((t) => /* @__PURE__ */ import_react2.default.createElement("option", { key: t, value: t })))) : /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative flex items-center" }, /* @__PURE__ */ import_react2.default.createElement(
960
+ ), isTypeDropdownOpen && filteredAutocompleteOptions.length > 0 && /* @__PURE__ */ import_react2.default.createElement("div", { className: "absolute z-[60] left-0 right-0 mt-1 max-h-48 overflow-y-auto bg-slate-900/95 backdrop-blur-xl border border-white/10 rounded-md shadow-2xl custom-scrollbar" }, filteredAutocompleteOptions.map((t, idx) => /* @__PURE__ */ import_react2.default.createElement(
961
+ "div",
962
+ {
963
+ key: t,
964
+ className: `px-3 py-2 text-xs cursor-pointer transition-colors ${idx === highlightedTypeIndex ? "bg-indigo-600 text-white" : "text-slate-300 hover:bg-white/10"}`,
965
+ onMouseDown: (e) => {
966
+ e.preventDefault();
967
+ setFilterValue(t);
968
+ setIsTypeDropdownOpen(false);
969
+ setHighlightedTypeIndex(-1);
970
+ }
971
+ },
972
+ t
973
+ )))) : /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative flex items-center" }, /* @__PURE__ */ import_react2.default.createElement(
830
974
  "div",
831
975
  {
832
976
  className: "absolute left-2 w-3 h-3 rounded-full border border-white/20 shadow-sm",
@@ -2814,18 +2958,27 @@ function createNewCustomProperty(existingProps = []) {
2814
2958
  };
2815
2959
  }
2816
2960
  var resolveDescriptionReference = (refString, availableNodes = [], availableAncestries = []) => {
2817
- const match = refString.match(/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/);
2961
+ const match = refString.match(
2962
+ /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/
2963
+ );
2818
2964
  if (!match) return null;
2819
2965
  const [_, type, itemId, sectionId] = match;
2820
2966
  let sourceItem = null;
2821
2967
  if (type === "node") {
2822
2968
  sourceItem = availableNodes.find((n) => String(n.id) === String(itemId));
2823
2969
  } else {
2824
- sourceItem = availableAncestries.find((a) => String(a.ancestry_id) === String(itemId));
2970
+ sourceItem = availableAncestries.find(
2971
+ (a) => String(a.ancestry_id) === String(itemId)
2972
+ );
2825
2973
  }
2826
2974
  if (!sourceItem) return null;
2827
- const sections = parseDescriptionSections(sourceItem.description, sourceItem.description_sections);
2828
- const targetSection = sections.find((s) => String(s.id) === String(sectionId));
2975
+ const sections = parseDescriptionSections(
2976
+ sourceItem.description,
2977
+ sourceItem.description_sections
2978
+ );
2979
+ const targetSection = sections.find(
2980
+ (s) => String(s.id) === String(sectionId)
2981
+ );
2829
2982
  if (!targetSection) return null;
2830
2983
  return {
2831
2984
  content: targetSection.content,
@@ -2839,21 +2992,34 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
2839
2992
  let text = rawText;
2840
2993
  const allNodes = parentData ? Object.values(parentData).flatMap((f) => f.nodes || []) : [];
2841
2994
  const allAncestries = ancestryData || [];
2842
- text = text.replace(/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g, (match, type, itemId, sectionId) => {
2843
- const resolved = resolveDescriptionReference(match, allNodes, allAncestries);
2844
- if (resolved && !resolved.error) {
2845
- return resolved.content;
2995
+ text = text.replace(
2996
+ /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g,
2997
+ (match, type, itemId, sectionId) => {
2998
+ const resolved = resolveDescriptionReference(
2999
+ match,
3000
+ allNodes,
3001
+ allAncestries
3002
+ );
3003
+ if (resolved && !resolved.error) {
3004
+ return resolved.content;
3005
+ }
3006
+ return "[Refer\xEAncia indispon\xEDvel]";
2846
3007
  }
2847
- return "[Refer\xEAncia indispon\xEDvel]";
2848
- });
3008
+ );
2849
3009
  text = text.replace(/\*\/\s*\d+(?::[a-zA-Z0-9-]+)?\s*\//g, "");
2850
- text = text.replace(/\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g, (match, nodeId) => {
2851
- const node = allNodes.find((n) => String(n.id) === String(nodeId));
2852
- return node ? `@${node.name}` : `@Men\xE7\xE3o`;
2853
- });
2854
- text = text.replace(/\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g, (match, url, name) => {
2855
- return name ? `[Imagem: ${name}]` : `[Imagem]`;
2856
- });
3010
+ text = text.replace(
3011
+ /\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g,
3012
+ (match, nodeId) => {
3013
+ const node = allNodes.find((n) => String(n.id) === String(nodeId));
3014
+ return node ? `@${node.name}` : `@Men\xE7\xE3o`;
3015
+ }
3016
+ );
3017
+ text = text.replace(
3018
+ /\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g,
3019
+ (match, url, name) => {
3020
+ return name ? `[Imagem: ${name}]` : `[Imagem]`;
3021
+ }
3022
+ );
2857
3023
  text = text.replace(/^#+\s*/gm, "");
2858
3024
  text = text.replace(/```[\s\S]*?```/g, "[C\xF3digo]");
2859
3025
  text = text.replace(/-\s\[[xX ]\]\s*/g, "\u2022 ");
@@ -2861,7 +3027,14 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
2861
3027
  return text.trim();
2862
3028
  }
2863
3029
  function generateTooltipHtml(data, parentData, ancestryData) {
2864
- const ignoredKeys = ["id", "name", "type", "color", "_baseEmissiveIntensity", "description"];
3030
+ const ignoredKeys = [
3031
+ "id",
3032
+ "name",
3033
+ "type",
3034
+ "color",
3035
+ "_baseEmissiveIntensity",
3036
+ "description"
3037
+ ];
2865
3038
  const customKeys = Object.keys(data).filter((k) => !ignoredKeys.includes(k));
2866
3039
  const extras = customKeys.length;
2867
3040
  let typeDisplay = "Node";
@@ -2876,7 +3049,11 @@ function generateTooltipHtml(data, parentData, ancestryData) {
2876
3049
  <div style="font-weight:600; font-size: 14px; color: #fff;">${typeDisplay}</div>
2877
3050
  <div style="margin-bottom: 2px; color: #e2e8f0;">${data.name || ""}</div>`;
2878
3051
  if (data.description) {
2879
- const cleanDesc = formatDescriptionForTooltip(data.description, parentData, ancestryData);
3052
+ const cleanDesc = formatDescriptionForTooltip(
3053
+ data.description,
3054
+ parentData,
3055
+ ancestryData
3056
+ );
2880
3057
  if (cleanDesc) {
2881
3058
  html += `<div style="
2882
3059
  margin-top: 6px;
@@ -2914,7 +3091,11 @@ function generateLinkTooltipHtml(data, parentData, ancestryData) {
2914
3091
  html += `<div style="font-weight:600; font-size: 14px; color: #a5f3fc; margin-bottom: 4px;">${data.name}</div>`;
2915
3092
  }
2916
3093
  if (hasDescription) {
2917
- const cleanDesc = formatDescriptionForTooltip(data.description, parentData, ancestryData);
3094
+ const cleanDesc = formatDescriptionForTooltip(
3095
+ data.description,
3096
+ parentData,
3097
+ ancestryData
3098
+ );
2918
3099
  if (cleanDesc) {
2919
3100
  html += `<div style="
2920
3101
  display: -webkit-box;
@@ -2996,7 +3177,8 @@ var IGNORED_KEYS = [
2996
3177
  "isAddingAbstractionNodes",
2997
3178
  "status",
2998
3179
  "is_quest",
2999
- "raw_title"
3180
+ "raw_title",
3181
+ "assignee_id"
3000
3182
  ];
3001
3183
  function extractCustomPropsFromNode(node) {
3002
3184
  const customPropTypes = node._customPropTypes || {};
@@ -3006,16 +3188,40 @@ function extractCustomPropsFromNode(node) {
3006
3188
  if (t === "date") {
3007
3189
  if (val && typeof val === "object") {
3008
3190
  if ("date_interval" in val) {
3009
- return { id: v4_default(), key, type: "date", value: { type: "Date Interval", start: val.date_interval.start || "", end: val.date_interval.end || "" } };
3191
+ return {
3192
+ id: v4_default(),
3193
+ key,
3194
+ type: "date",
3195
+ value: {
3196
+ type: "Date Interval",
3197
+ start: val.date_interval.start || "",
3198
+ end: val.date_interval.end || ""
3199
+ }
3200
+ };
3010
3201
  }
3011
3202
  if ("year" in val) {
3012
- return { id: v4_default(), key, type: "date", value: { type: "Ano", value: val.year || "" } };
3203
+ return {
3204
+ id: v4_default(),
3205
+ key,
3206
+ type: "date",
3207
+ value: { type: "Ano", value: val.year || "" }
3208
+ };
3013
3209
  }
3014
3210
  if ("date" in val) {
3015
- return { id: v4_default(), key, type: "date", value: { type: "Data", value: val.date || "" } };
3211
+ return {
3212
+ id: v4_default(),
3213
+ key,
3214
+ type: "date",
3215
+ value: { type: "Data", value: val.date || "" }
3216
+ };
3016
3217
  }
3017
3218
  }
3018
- return { id: v4_default(), key, type: "date", value: { type: "Data", value: "" } };
3219
+ return {
3220
+ id: v4_default(),
3221
+ key,
3222
+ type: "date",
3223
+ value: { type: "Data", value: "" }
3224
+ };
3019
3225
  }
3020
3226
  if (t === "list" || t === "links" || t === "images" || t === "documents") {
3021
3227
  const safeList = (val || []).map((item) => ({
@@ -3025,7 +3231,12 @@ function extractCustomPropsFromNode(node) {
3025
3231
  }));
3026
3232
  return { id: v4_default(), key, type: t, value: safeList };
3027
3233
  }
3028
- return { id: v4_default(), key, type: t, value: t === "number" ? Number(val) || 0 : String(val ?? "") };
3234
+ return {
3235
+ id: v4_default(),
3236
+ key,
3237
+ type: t,
3238
+ value: t === "number" ? Number(val) || 0 : String(val ?? "")
3239
+ };
3029
3240
  });
3030
3241
  }
3031
3242
  function toObjectFromCustomProps(customProps) {
@@ -3040,7 +3251,12 @@ function toObjectFromCustomProps(customProps) {
3040
3251
  const { type: uiDateType, ...dateValues } = p.value || {};
3041
3252
  if (uiDateType) {
3042
3253
  if (uiDateType === "Date Interval") {
3043
- out[key] = { date_interval: { start: dateValues.start || "", end: dateValues.end || "" } };
3254
+ out[key] = {
3255
+ date_interval: {
3256
+ start: dateValues.start || "",
3257
+ end: dateValues.end || ""
3258
+ }
3259
+ };
3044
3260
  } else if (uiDateType === "Ano") {
3045
3261
  out[key] = { year: dateValues.value || "" };
3046
3262
  } else {
@@ -3076,10 +3292,20 @@ function createTextSprite(text, fontSize = 64) {
3076
3292
  context.fillText(effectiveText, canvas.width / 2, canvas.height / 2);
3077
3293
  const texture = new THREE2.CanvasTexture(canvas);
3078
3294
  texture.colorSpace = THREE2.SRGBColorSpace;
3079
- const spriteMaterial = new THREE2.SpriteMaterial({ map: texture, sizeAttenuation: true, depthWrite: false, transparent: true, toneMapped: false });
3295
+ const spriteMaterial = new THREE2.SpriteMaterial({
3296
+ map: texture,
3297
+ sizeAttenuation: true,
3298
+ depthWrite: false,
3299
+ transparent: true,
3300
+ toneMapped: false
3301
+ });
3080
3302
  const sprite = new THREE2.Sprite(spriteMaterial);
3081
3303
  const scaleFactor = 0.05;
3082
- sprite.scale.set(canvas.width * scaleFactor, canvas.height * scaleFactor, 1);
3304
+ sprite.scale.set(
3305
+ canvas.width * scaleFactor,
3306
+ canvas.height * scaleFactor,
3307
+ 1
3308
+ );
3083
3309
  sprite.layers.set(DEFAULT_LAYER);
3084
3310
  return sprite;
3085
3311
  }
@@ -3095,7 +3321,14 @@ function makeGlowTexture() {
3095
3321
  const canvas = document.createElement("canvas");
3096
3322
  canvas.width = canvas.height = size;
3097
3323
  const ctx = canvas.getContext("2d");
3098
- const grd = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
3324
+ const grd = ctx.createRadialGradient(
3325
+ size / 2,
3326
+ size / 2,
3327
+ 0,
3328
+ size / 2,
3329
+ size / 2,
3330
+ size / 2
3331
+ );
3099
3332
  grd.addColorStop(0, "rgba(255,255,255,1)");
3100
3333
  grd.addColorStop(0.4, "rgba(255,255,255,0.45)");
3101
3334
  grd.addColorStop(1, "rgba(255,255,255,0)");
@@ -3111,8 +3344,19 @@ function addGlowAura(mesh, hexColor, glowTexture, auraScale) {
3111
3344
  let midBoost = 1 - Math.abs(L - 0.5) * 2;
3112
3345
  midBoost = THREE2.MathUtils.clamp(midBoost, 0, 1);
3113
3346
  const auraOpacity = THREE2.MathUtils.lerp(0.25, 0.8, midBoost);
3114
- const auraColor = new THREE2.Color(hexColor).lerp(new THREE2.Color("#ffffff"), 0.25);
3115
- const auraMat = new THREE2.SpriteMaterial({ map: glowTexture, color: auraColor, opacity: auraOpacity, blending: THREE2.AdditiveBlending, transparent: true, depthWrite: false, toneMapped: false });
3347
+ const auraColor = new THREE2.Color(hexColor).lerp(
3348
+ new THREE2.Color("#ffffff"),
3349
+ 0.25
3350
+ );
3351
+ const auraMat = new THREE2.SpriteMaterial({
3352
+ map: glowTexture,
3353
+ color: auraColor,
3354
+ opacity: auraOpacity,
3355
+ blending: THREE2.AdditiveBlending,
3356
+ transparent: true,
3357
+ depthWrite: false,
3358
+ toneMapped: false
3359
+ });
3116
3360
  const aura = new THREE2.Sprite(auraMat);
3117
3361
  aura.scale.setScalar(auraScale);
3118
3362
  aura.name = "aura";
@@ -3145,7 +3389,17 @@ function calculateNodePositions(nodes) {
3145
3389
  }
3146
3390
  return positions;
3147
3391
  }
3148
- function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, isSceneBusy, parentData, ancestryData }) {
3392
+ function updateTooltip({
3393
+ tooltipEl,
3394
+ hoveredNode,
3395
+ hoveredLink,
3396
+ camera,
3397
+ mountEl,
3398
+ isSceneBusy,
3399
+ parentData,
3400
+ ancestryData
3401
+ }) {
3402
+ var _a, _b;
3149
3403
  if (!tooltipEl || !camera || !mountEl) return;
3150
3404
  let content = "";
3151
3405
  let positionTarget = null;
@@ -3155,20 +3409,35 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3155
3409
  currentId = `node_${hoveredNode.userData.id}`;
3156
3410
  positionTarget = hoveredNode;
3157
3411
  if (tooltipEl.dataset.currentId !== currentId) {
3158
- content = generateTooltipHtml(hoveredNode.userData, parentData, ancestryData);
3412
+ content = generateTooltipHtml(
3413
+ hoveredNode.userData,
3414
+ parentData,
3415
+ ancestryData
3416
+ );
3159
3417
  }
3160
3418
  } else if (hoveredLink && !isSceneBusy) {
3161
- currentId = `link_${hoveredLink.userData.id}`;
3162
- if (hoveredLink.userData.isCurved) {
3163
- const positions = hoveredLink.geometry.attributes.position.array;
3164
- const midIndex = Math.floor(positions.length / 2 / 3) * 3;
3165
- positionTarget = new THREE2.Vector3(positions[midIndex], positions[midIndex + 1], positions[midIndex + 2]);
3166
- } else {
3167
- positionTarget = new THREE2.Vector3().addVectors(hoveredLink.userData.sourceNode.position, hoveredLink.userData.targetNode.position).multiplyScalar(0.5);
3168
- }
3169
- isLink = true;
3170
- if (tooltipEl.dataset.currentId !== currentId) {
3171
- content = hoveredLink.userData.isAncestryLink ? generateLinkTooltipHtml(hoveredLink.userData.relationship || {}, parentData, ancestryData) : generateLinkTooltipHtml(hoveredLink.userData, parentData, ancestryData);
3419
+ const linkData = hoveredLink.userData.isAncestryLink ? hoveredLink.userData.relationship || {} : hoveredLink.userData;
3420
+ const hasContent = ((_a = linkData.name) == null ? void 0 : _a.trim()) || ((_b = linkData.description) == null ? void 0 : _b.trim());
3421
+ if (hasContent) {
3422
+ currentId = `link_${hoveredLink.userData.id}`;
3423
+ if (hoveredLink.userData.isCurved) {
3424
+ const positions = hoveredLink.geometry.attributes.position.array;
3425
+ const midIndex = Math.floor(positions.length / 2 / 3) * 3;
3426
+ positionTarget = new THREE2.Vector3(
3427
+ positions[midIndex],
3428
+ positions[midIndex + 1],
3429
+ positions[midIndex + 2]
3430
+ );
3431
+ } else {
3432
+ positionTarget = new THREE2.Vector3().addVectors(
3433
+ hoveredLink.userData.sourceNode.position,
3434
+ hoveredLink.userData.targetNode.position
3435
+ ).multiplyScalar(0.5);
3436
+ }
3437
+ isLink = true;
3438
+ if (tooltipEl.dataset.currentId !== currentId) {
3439
+ content = generateLinkTooltipHtml(linkData, parentData, ancestryData);
3440
+ }
3172
3441
  }
3173
3442
  }
3174
3443
  if (positionTarget) {
@@ -3192,9 +3461,11 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3192
3461
  const tooltipRect = tooltipEl.getBoundingClientRect();
3193
3462
  const offsetX = 20;
3194
3463
  const offsetY = 20;
3195
- if (x + tooltipRect.width + offsetX > clientWidth) x = x - tooltipRect.width - offsetX;
3464
+ if (x + tooltipRect.width + offsetX > clientWidth)
3465
+ x = x - tooltipRect.width - offsetX;
3196
3466
  else x = x + offsetX;
3197
- if (y + tooltipRect.height + offsetY > clientHeight) y = y - tooltipRect.height - offsetY;
3467
+ if (y + tooltipRect.height + offsetY > clientHeight)
3468
+ y = y - tooltipRect.height - offsetY;
3198
3469
  else y = y + offsetY;
3199
3470
  tooltipEl.style.display = "block";
3200
3471
  tooltipEl.style.left = `${x}px`;
@@ -3228,7 +3499,9 @@ var processDescriptionForSave = (text, existingSections = []) => {
3228
3499
  const content = parts[i + 2] || "";
3229
3500
  let finalUuid = null;
3230
3501
  if (suffix) {
3231
- const existingMatch = existingSections.find((s) => s.id && s.id.includes(suffix));
3502
+ const existingMatch = existingSections.find(
3503
+ (s) => s.id && s.id.includes(suffix)
3504
+ );
3232
3505
  if (existingMatch) {
3233
3506
  finalUuid = existingMatch.id;
3234
3507
  } else {
@@ -3319,28 +3592,34 @@ var extractFileUrlsFromProperties = (dataObject) => {
3319
3592
  function useResizablePanel({ initialWidth, minWidth, maxWidth }) {
3320
3593
  const [width, setWidth] = (0, import_react3.useState)(initialWidth);
3321
3594
  const [isResizing, setIsResizing] = (0, import_react3.useState)(false);
3322
- const handlePointerDown = (0, import_react3.useCallback)((e) => {
3323
- e.preventDefault();
3324
- e.stopPropagation();
3325
- setIsResizing(true);
3326
- const startX = e.clientX;
3327
- const startWidth = width;
3328
- const originalUserSelect = document.body.style.userSelect;
3329
- document.body.style.userSelect = "none";
3330
- const handlePointerMove = (moveEvent) => {
3331
- const deltaX = startX - moveEvent.clientX;
3332
- const newWidth = Math.min(Math.max(startWidth + deltaX, minWidth), maxWidth);
3333
- setWidth(newWidth);
3334
- };
3335
- const handlePointerUp = () => {
3336
- setIsResizing(false);
3337
- document.body.style.userSelect = originalUserSelect;
3338
- document.removeEventListener("pointermove", handlePointerMove);
3339
- document.removeEventListener("pointerup", handlePointerUp);
3340
- };
3341
- document.addEventListener("pointermove", handlePointerMove);
3342
- document.addEventListener("pointerup", handlePointerUp);
3343
- }, [width, minWidth, maxWidth]);
3595
+ const handlePointerDown = (0, import_react3.useCallback)(
3596
+ (e) => {
3597
+ e.preventDefault();
3598
+ e.stopPropagation();
3599
+ setIsResizing(true);
3600
+ const startX = e.clientX;
3601
+ const startWidth = width;
3602
+ const originalUserSelect = document.body.style.userSelect;
3603
+ document.body.style.userSelect = "none";
3604
+ const handlePointerMove = (moveEvent) => {
3605
+ const deltaX = startX - moveEvent.clientX;
3606
+ const newWidth = Math.min(
3607
+ Math.max(startWidth + deltaX, minWidth),
3608
+ maxWidth
3609
+ );
3610
+ setWidth(newWidth);
3611
+ };
3612
+ const handlePointerUp = () => {
3613
+ setIsResizing(false);
3614
+ document.body.style.userSelect = originalUserSelect;
3615
+ document.removeEventListener("pointermove", handlePointerMove);
3616
+ document.removeEventListener("pointerup", handlePointerUp);
3617
+ };
3618
+ document.addEventListener("pointermove", handlePointerMove);
3619
+ document.addEventListener("pointerup", handlePointerUp);
3620
+ },
3621
+ [width, minWidth, maxWidth]
3622
+ );
3344
3623
  return { width, isResizing, handlePointerDown, setWidth };
3345
3624
  }
3346
3625
 
@@ -3477,9 +3756,9 @@ function CustomPropertyDisplay({
3477
3756
  };
3478
3757
  const handleRemoveListItem = (j) => setTempProp((p) => ({ ...p, value: p.value.filter((_, k) => k !== j) }));
3479
3758
  const handleListItemChange = (j, f, v) => setTempProp((p) => {
3480
- const newValue = [...p.value];
3481
- newValue[j] = { ...newValue[j], [f]: v };
3482
- return { ...p, value: newValue };
3759
+ const newValue2 = [...p.value];
3760
+ newValue2[j] = { ...newValue2[j], [f]: v };
3761
+ return { ...p, value: newValue2 };
3483
3762
  });
3484
3763
  const baseInput = "w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60 transition-all duration-150";
3485
3764
  const renderEditView = () => {
@@ -3533,14 +3812,14 @@ function CustomPropertyDisplay({
3533
3812
  const inputClass = `${baseInput} ${noSpinnerClass}`;
3534
3813
  const handleDateTypeChange = (newDateType) => {
3535
3814
  var _a3, _b2, _c2;
3536
- let newValue = { type: newDateType };
3815
+ let newValue2 = { type: newDateType };
3537
3816
  if (newDateType === "Date Interval") {
3538
- newValue.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
3539
- newValue.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
3817
+ newValue2.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
3818
+ newValue2.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
3540
3819
  } else {
3541
- newValue.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
3820
+ newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
3542
3821
  }
3543
- handlePropChange("value", newValue);
3822
+ handlePropChange("value", newValue2);
3544
3823
  };
3545
3824
  const handleDateValueChange = (dateField, dateValue) => {
3546
3825
  handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
@@ -4388,7 +4667,7 @@ ${space}${bullet} `);
4388
4667
  }
4389
4668
  ),
4390
4669
  /* @__PURE__ */ import_react6.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0 shrink-0" }),
4391
- /* @__PURE__ */ import_react6.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-sm font-medium text-slate-200" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ import_react6.default.createElement("button", { onClick: handleClose, 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", title: "Fechar" }, "\xD7")),
4670
+ /* @__PURE__ */ import_react6.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shrink-0" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-sm font-medium text-slate-200 truncate" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ import_react6.default.createElement("button", { onClick: handleClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")),
4392
4671
  /* @__PURE__ */ import_react6.default.createElement("div", { className: "px-6 py-3 flex flex-col gap-3 border-b border-white/5 bg-white/5 shrink-0" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-2 flex-wrap" }, /* @__PURE__ */ import_react6.default.createElement(
4393
4672
  "button",
4394
4673
  {
@@ -5503,7 +5782,7 @@ function AncestryRelationshipPanel({
5503
5782
  onImageClick: handleImageClickFromText,
5504
5783
  onSaveDescription: handleSaveDescriptionInline
5505
5784
  }
5506
- ) : /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react9.default.createElement("div", null, /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react9.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-cyan-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ import_react9.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o de Ancestralidade")), /* @__PURE__ */ import_react9.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Editar Rela\xE7\xE3o")), /* @__PURE__ */ import_react9.default.createElement("button", { onClick: onClose, 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", title: "Fechar" }, "\xD7")), /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 pb-4 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react9.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o da Rela\xE7\xE3o"), /* @__PURE__ */ import_react9.default.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ import_react9.default.createElement(
5785
+ ) : /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react9.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-cyan-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ import_react9.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o de Ancestralidade")), /* @__PURE__ */ import_react9.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Editar Rela\xE7\xE3o")), /* @__PURE__ */ import_react9.default.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 pb-4 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react9.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o da Rela\xE7\xE3o"), /* @__PURE__ */ import_react9.default.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ import_react9.default.createElement(
5507
5786
  DescriptionDisplay,
5508
5787
  {
5509
5788
  description,
@@ -5808,6 +6087,7 @@ function CreateAncestryPanel({
5808
6087
  } = ancestryMode;
5809
6088
  const [isSaving, setIsSaving] = (0, import_react11.useState)(false);
5810
6089
  const [isLinkCopied, setIsLinkCopied] = (0, import_react11.useState)(false);
6090
+ const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react11.useState)(false);
5811
6091
  const [showDeleteBranchConfirm, setShowDeleteBranchConfirm] = (0, import_react11.useState)(false);
5812
6092
  const handleCopyLink = (e) => {
5813
6093
  e.stopPropagation();
@@ -5875,12 +6155,14 @@ function CreateAncestryPanel({
5875
6155
  };
5876
6156
  const handleSelectAncestryParent = (nodeId, isAbstraction = false) => {
5877
6157
  setAncestryMode((prev) => isAbstraction ? { ...prev, selectedAbstractionParentId: nodeId } : { ...prev, selectedParentId: nodeId });
6158
+ setHasUnsavedChanges(true);
5878
6159
  };
5879
6160
  const handleToggleAddMode = (isAbstraction = false) => {
5880
6161
  if (isAbstraction && !ancestryMode.isAddingAbstractionNodes) {
5881
6162
  setTargetRenderNodeId(null);
5882
6163
  }
5883
6164
  setAncestryMode((prev) => isAbstraction ? { ...prev, isAddingAbstractionNodes: !prev.isAddingAbstractionNodes } : { ...prev, isAddingNodes: !prev.isAddingNodes });
6165
+ setHasUnsavedChanges(true);
5884
6166
  };
5885
6167
  const handleRemoveNode = (0, import_react11.useCallback)((pathToRemove, isAbstraction = false) => {
5886
6168
  if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) return;
@@ -5898,6 +6180,7 @@ function CreateAncestryPanel({
5898
6180
  const indexToRemove = pathToRemove[pathToRemove.length - 1];
5899
6181
  if (currentParent.children && currentParent.children.length > indexToRemove) {
5900
6182
  currentParent.children.splice(indexToRemove, 1);
6183
+ setHasUnsavedChanges(true);
5901
6184
  }
5902
6185
  return { ...prev, [treeKey]: newTree };
5903
6186
  });
@@ -5956,6 +6239,7 @@ function CreateAncestryPanel({
5956
6239
  updateGlobalTree(rootTreeClone);
5957
6240
  }
5958
6241
  setAncestryMode((prev) => ({ ...prev, [treeKey]: rootTreeClone }));
6242
+ setHasUnsavedChanges(true);
5959
6243
  } else {
5960
6244
  alert("N\xE3o \xE9 poss\xEDvel mover um node para dentro de seus pr\xF3prios descendentes.");
5961
6245
  }
@@ -6028,6 +6312,7 @@ function CreateAncestryPanel({
6028
6312
  const handleAddProp = () => {
6029
6313
  const newProp = createNewCustomProperty(customProps);
6030
6314
  setCustomProps((p) => [...p, newProp]);
6315
+ setHasUnsavedChanges(true);
6031
6316
  setTimeout(() => {
6032
6317
  var _a;
6033
6318
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -6036,11 +6321,13 @@ function CreateAncestryPanel({
6036
6321
  const handleRemoveProp = (i) => {
6037
6322
  const newProps = customProps.filter((_, idx) => idx !== i);
6038
6323
  setCustomProps(newProps);
6324
+ setHasUnsavedChanges(true);
6039
6325
  };
6040
6326
  const handleUpdateProp = (index, updatedProp) => {
6041
6327
  const newProps = [...customProps];
6042
6328
  newProps[index] = updatedProp;
6043
6329
  setCustomProps(newProps);
6330
+ setHasUnsavedChanges(true);
6044
6331
  };
6045
6332
  const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
6046
6333
  (0, import_react11.useEffect)(() => {
@@ -6150,6 +6437,7 @@ function CreateAncestryPanel({
6150
6437
  updateGlobalTree(rootTreeClone);
6151
6438
  setBranchStack([...branchStack]);
6152
6439
  setIsPickerOpen(false);
6440
+ setHasUnsavedChanges(true);
6153
6441
  try {
6154
6442
  setIsSaving(true);
6155
6443
  const rootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6163,6 +6451,7 @@ function CreateAncestryPanel({
6163
6451
  rootExtras
6164
6452
  );
6165
6453
  setLastSavedSnapshot(takeSnapshot(rootTreeClone, ancestryName, description, processedSections, [], isPrivate, ancestryMode.abstraction_tree));
6454
+ setHasUnsavedChanges(false);
6166
6455
  if (onRenderFullAncestry) {
6167
6456
  const fullTreePayload = {
6168
6457
  ancestry_id: ancestryMode.currentAncestryId || "temp_root",
@@ -6205,6 +6494,7 @@ function CreateAncestryPanel({
6205
6494
  if (branchIndex !== -1) {
6206
6495
  foundParentPath.node.parallel_branches.splice(branchIndex, 1);
6207
6496
  updateGlobalTree(rootTreeClone);
6497
+ setHasUnsavedChanges(true);
6208
6498
  try {
6209
6499
  setIsSaving(true);
6210
6500
  const currentRootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6226,6 +6516,7 @@ function CreateAncestryPanel({
6226
6516
  isPrivate,
6227
6517
  ancestryMode.abstraction_tree
6228
6518
  ));
6519
+ setHasUnsavedChanges(false);
6229
6520
  if (onClearAncestryVisuals) {
6230
6521
  onClearAncestryVisuals(currentStep.branchId);
6231
6522
  }
@@ -6258,6 +6549,7 @@ function CreateAncestryPanel({
6258
6549
  if (branchIndex !== -1) {
6259
6550
  foundParentPath.node.parallel_branches.splice(branchIndex, 1);
6260
6551
  updateGlobalTree(rootTreeClone);
6552
+ setHasUnsavedChanges(true);
6261
6553
  try {
6262
6554
  setIsSaving(true);
6263
6555
  const currentRootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6279,6 +6571,7 @@ function CreateAncestryPanel({
6279
6571
  isPrivate,
6280
6572
  ancestryMode.abstraction_tree
6281
6573
  ));
6574
+ setHasUnsavedChanges(false);
6282
6575
  if (onClearAncestryVisuals) {
6283
6576
  onClearAncestryVisuals(currentStep.branchId);
6284
6577
  }
@@ -6540,6 +6833,7 @@ function CreateAncestryPanel({
6540
6833
  }
6541
6834
  setBranchStack(parentStack);
6542
6835
  setTargetScrollSectionId(targetFocusId);
6836
+ setHasUnsavedChanges(true);
6543
6837
  if (onRenderFullAncestry) {
6544
6838
  const parentStack2 = currentStack;
6545
6839
  const rotation = parentStack2.reduce((acc, step) => {
@@ -6601,7 +6895,6 @@ function CreateAncestryPanel({
6601
6895
  direction,
6602
6896
  tree: {
6603
6897
  node: nodeData,
6604
- node_id: nodeId,
6605
6898
  children: [],
6606
6899
  relationship: {}
6607
6900
  }
@@ -6623,6 +6916,7 @@ function CreateAncestryPanel({
6623
6916
  savedMaxIndex: parentIndexToSave,
6624
6917
  entryDirection: direction
6625
6918
  }]);
6919
+ setHasUnsavedChanges(true);
6626
6920
  if (branch && branch.tree && onRenderFullAncestry) {
6627
6921
  const branchAncestryObj = {
6628
6922
  ancestry_id: branch.id,
@@ -6673,6 +6967,10 @@ function CreateAncestryPanel({
6673
6967
  const currentInputName = overrides.ancestryName !== void 0 ? overrides.ancestryName : ancestryName;
6674
6968
  const currentInputDesc = overrides.description !== void 0 ? overrides.description : description;
6675
6969
  const currentInputSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
6970
+ if (!keepOpen && !hasUnsavedChanges) {
6971
+ onClose();
6972
+ return;
6973
+ }
6676
6974
  if (!currentInputName.trim()) {
6677
6975
  alert("O nome n\xE3o pode estar vazio.");
6678
6976
  return;
@@ -6680,11 +6978,7 @@ function CreateAncestryPanel({
6680
6978
  setIsSaving(true);
6681
6979
  const processedSections = processDescriptionForSave(currentInputDesc, currentInputSections);
6682
6980
  setExistingSections(processedSections);
6683
- const updatedRootTree = applyDescriptionToTree(
6684
- ancestryMode.tree,
6685
- currentInputDesc,
6686
- processedSections
6687
- );
6981
+ const updatedRootTree = JSON.parse(JSON.stringify(ancestryMode.tree));
6688
6982
  const extrasObj = {
6689
6983
  ...toObjectFromCustomProps(customProps.filter((p) => !p.isEditing)),
6690
6984
  is_private: isPrivate
@@ -6726,6 +7020,7 @@ function CreateAncestryPanel({
6726
7020
  isPrivate,
6727
7021
  ancestryMode.abstraction_tree
6728
7022
  ));
7023
+ setHasUnsavedChanges(false);
6729
7024
  if (onRenderFullAncestry) {
6730
7025
  const rotation = branchStack.reduce((acc, step) => {
6731
7026
  return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
@@ -6777,6 +7072,7 @@ function CreateAncestryPanel({
6777
7072
  updatedRootTree,
6778
7073
  extrasObj
6779
7074
  );
7075
+ setHasUnsavedChanges(false);
6780
7076
  setLastSavedSnapshot(takeSnapshot(
6781
7077
  updatedRootTree,
6782
7078
  currentInputName,
@@ -6799,6 +7095,7 @@ function CreateAncestryPanel({
6799
7095
  const newTreeString = JSON.stringify(newRootTree);
6800
7096
  if (currentTreeString !== newTreeString) {
6801
7097
  updateGlobalTree(newRootTree);
7098
+ setHasUnsavedChanges(true);
6802
7099
  }
6803
7100
  }, [description, existingSections]);
6804
7101
  const handleTriggerFullRender = () => {
@@ -6821,6 +7118,7 @@ function CreateAncestryPanel({
6821
7118
  };
6822
7119
  const handleSaveDescriptionInline = (newDesc) => {
6823
7120
  setDescription(newDesc);
7121
+ setHasUnsavedChanges(true);
6824
7122
  handleLocalSave(true, { description: newDesc });
6825
7123
  };
6826
7124
  const swallow = (e) => e.stopPropagation();
@@ -6950,7 +7248,11 @@ function CreateAncestryPanel({
6950
7248
  {
6951
7249
  type: "text",
6952
7250
  value: ancestryName,
6953
- onChange: (e) => setAncestryName(e.target.value),
7251
+ onChange: (e) => {
7252
+ setAncestryName(e.target.value);
7253
+ setHasUnsavedChanges(true);
7254
+ },
7255
+ readOnly: isContextLinked,
6954
7256
  placeholder: "Nome da Ancestralidade",
6955
7257
  className: "text-xl sm:text-2xl font-semibold tracking-tight bg-transparent border-none p-0 focus:ring-2 focus:ring-indigo-500 rounded-md -ml-1.5 px-1.5 w-full outline-none transition-all focus:bg-slate-800/70"
6956
7258
  }
@@ -7438,7 +7740,7 @@ function ColorPicker({ color, onChange, disabled }) {
7438
7740
  style: { backgroundColor: preset },
7439
7741
  title: preset
7440
7742
  },
7441
- color.toLowerCase() === preset.toLowerCase() && /* @__PURE__ */ import_react13.default.createElement(import_fi11.FiCheck, { className: `drop-shadow-md ${["#ffffff", "#4df5cb", "#84cc16", "#f59e0b"].includes(preset) ? "text-black" : "text-white"}`, size: 12 })
7743
+ (color || "").toLowerCase() === (preset || "").toLowerCase() && /* @__PURE__ */ import_react13.default.createElement(import_fi11.FiCheck, { className: `drop-shadow-md ${["#ffffff", "#4df5cb", "#84cc16", "#f59e0b"].includes(preset) ? "text-black" : "text-white"}`, size: 12 })
7442
7744
  )))), /* @__PURE__ */ import_react13.default.createElement("style", null, `
7443
7745
  .custom-react-colorful .react-colorful {
7444
7746
  width: 100%;
@@ -7530,13 +7832,23 @@ function InSceneCreationForm({
7530
7832
  }, [hasImages, useImageAsTexture, onImageChange]);
7531
7833
  (0, import_react14.useEffect)(() => {
7532
7834
  let result = [];
7835
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
7533
7836
  if (typeInput.trim() === "") {
7534
- result = existingTypes.filter((t) => !types.includes(t));
7837
+ result = validExistingTypes.filter((t) => !types.includes(t));
7535
7838
  } else {
7536
- const lowercasedInput = typeInput.toLowerCase();
7537
- result = existingTypes.filter(
7538
- (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
7539
- );
7839
+ console.log("InSceneCreationForm: Filtrando tipos com input:", typeInput);
7840
+ try {
7841
+ const lowercasedInput = typeInput.toLowerCase();
7842
+ result = validExistingTypes.filter((t) => {
7843
+ if (!t) {
7844
+ console.warn("InSceneCreationForm: Tipo encontrado como undefined/null durante filtragem");
7845
+ return false;
7846
+ }
7847
+ return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
7848
+ });
7849
+ } catch (err) {
7850
+ console.error("InSceneCreationForm: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
7851
+ }
7540
7852
  }
7541
7853
  if (sourceTypes) {
7542
7854
  const priorityTypes = Array.isArray(sourceTypes) ? sourceTypes : [sourceTypes];
@@ -7599,9 +7911,9 @@ function InSceneCreationForm({
7599
7911
  };
7600
7912
  const handleToggleImageMode = () => {
7601
7913
  var _a2, _b;
7602
- const newValue = !useImageAsTexture;
7603
- setUseImageAsTexture(newValue);
7604
- if (newValue) {
7914
+ const newValue2 = !useImageAsTexture;
7915
+ setUseImageAsTexture(newValue2);
7916
+ if (newValue2) {
7605
7917
  const firstImageProp = customProps.find((p) => p.type === "images");
7606
7918
  if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
7607
7919
  const url = firstImageProp.value[0].value;
@@ -7899,9 +8211,9 @@ function InSceneVersionForm({
7899
8211
  };
7900
8212
  const handleToggleImageMode = () => {
7901
8213
  var _a, _b;
7902
- const newValue = !useImageAsTexture;
7903
- setUseImageAsTexture(newValue);
7904
- if (newValue) {
8214
+ const newValue2 = !useImageAsTexture;
8215
+ setUseImageAsTexture(newValue2);
8216
+ if (newValue2) {
7905
8217
  const firstImageProp = customProps.find((p) => p.type === "images");
7906
8218
  if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
7907
8219
  const url = firstImageProp.value[0].value;
@@ -8077,10 +8389,13 @@ function InSceneQuestForm({
8077
8389
  onSizeChange,
8078
8390
  viewName = "Projeto",
8079
8391
  // NOVA PROP
8080
- questCounter = 1
8392
+ questCounter = 1,
8081
8393
  // NOVA PROP
8394
+ viewMembers = []
8082
8395
  }) {
8396
+ var _a, _b;
8083
8397
  const [name, setName] = (0, import_react16.useState)("");
8398
+ const [assigneeId, setAssigneeId] = (0, import_react16.useState)("");
8084
8399
  const [types, setTypes] = (0, import_react16.useState)(["quest"]);
8085
8400
  const [typeInput, setTypeInput] = (0, import_react16.useState)("");
8086
8401
  const [status, setStatus] = (0, import_react16.useState)("Backlog");
@@ -8088,6 +8403,8 @@ function InSceneQuestForm({
8088
8403
  const [intensity, setIntensity] = (0, import_react16.useState)(0);
8089
8404
  const [description, setDescription] = (0, import_react16.useState)("");
8090
8405
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = (0, import_react16.useState)(false);
8406
+ const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = (0, import_react16.useState)(false);
8407
+ const [assigneeSearchQuery, setAssigneeSearchQuery] = (0, import_react16.useState)("");
8091
8408
  const [customProps, setCustomProps] = (0, import_react16.useState)([]);
8092
8409
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react16.useState)(false);
8093
8410
  const propsEndRef = (0, import_react16.useRef)(null);
@@ -8096,8 +8413,8 @@ function InSceneQuestForm({
8096
8413
  const newProp = createNewCustomProperty(customProps);
8097
8414
  setCustomProps([...customProps, newProp]);
8098
8415
  setTimeout(() => {
8099
- var _a;
8100
- (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
8416
+ var _a2;
8417
+ (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
8101
8418
  }, 100);
8102
8419
  };
8103
8420
  const handleRemoveProp = (index) => setCustomProps(customProps.filter((_, i) => i !== index));
@@ -8143,6 +8460,7 @@ function InSceneQuestForm({
8143
8460
  type: types,
8144
8461
  color: QUEST_STATUS_COLORS2[status],
8145
8462
  status,
8463
+ assignee_id: assigneeId || null,
8146
8464
  size,
8147
8465
  intensity,
8148
8466
  description: description.trim(),
@@ -8214,7 +8532,59 @@ function InSceneQuestForm({
8214
8532
  },
8215
8533
  /* @__PURE__ */ import_react16.default.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS2[s] } }),
8216
8534
  s
8217
- )))))), /* @__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(
8535
+ )))))), /* @__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" }, "Assignee (Respons\xE1vel)"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react16.default.createElement(
8536
+ "button",
8537
+ {
8538
+ type: "button",
8539
+ onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
8540
+ 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"
8541
+ },
8542
+ /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiUser, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ import_react16.default.createElement("span", { className: "text-slate-200 font-medium" }, ((_a = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _a.name) || ((_b = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _b.email) || "Nenhum")),
8543
+ /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isAssigneeDropdownOpen ? "rotate-180" : ""}` })
8544
+ ), isAssigneeDropdownOpen && /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
8545
+ setIsAssigneeDropdownOpen(false);
8546
+ setAssigneeSearchQuery("");
8547
+ } }), /* @__PURE__ */ import_react16.default.createElement("div", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-900 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden flex flex-col" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiSearch, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ import_react16.default.createElement(
8548
+ "input",
8549
+ {
8550
+ type: "text",
8551
+ autoFocus: true,
8552
+ placeholder: "Buscar membro...",
8553
+ value: assigneeSearchQuery,
8554
+ onChange: (e) => setAssigneeSearchQuery(e.target.value),
8555
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
8556
+ onClick: (e) => e.stopPropagation()
8557
+ }
8558
+ )), /* @__PURE__ */ import_react16.default.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ import_react16.default.createElement(
8559
+ "li",
8560
+ {
8561
+ onClick: () => {
8562
+ setAssigneeId("");
8563
+ setIsAssigneeDropdownOpen(false);
8564
+ setAssigneeSearchQuery("");
8565
+ },
8566
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${!assigneeId ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
8567
+ },
8568
+ "Nenhum"
8569
+ ), viewMembers.filter((member) => {
8570
+ const search = assigneeSearchQuery.toLowerCase();
8571
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
8572
+ }).map((member) => /* @__PURE__ */ import_react16.default.createElement(
8573
+ "li",
8574
+ {
8575
+ key: member.id,
8576
+ onClick: () => {
8577
+ setAssigneeId(member.id);
8578
+ setIsAssigneeDropdownOpen(false);
8579
+ setAssigneeSearchQuery("");
8580
+ },
8581
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${assigneeId === member.id ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
8582
+ },
8583
+ member.name || member.email || member.id
8584
+ )), viewMembers.filter((member) => {
8585
+ const search = assigneeSearchQuery.toLowerCase();
8586
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
8587
+ }).length === 0 && assigneeSearchQuery && /* @__PURE__ */ import_react16.default.createElement("li", { className: "px-3 py-4 text-xs text-slate-500 text-center italic" }, "Nenhum membro encontrado")))))), /* @__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(
8218
8588
  "input",
8219
8589
  {
8220
8590
  type: "text",
@@ -8321,6 +8691,7 @@ function NodeDetailsPanel({
8321
8691
  return !!(node == null ? void 0 : node.useImageAsTexture);
8322
8692
  });
8323
8693
  const [selectedImageUrl, setSelectedImageUrl] = (0, import_react17.useState)((node == null ? void 0 : node.textureImageUrl) ?? null);
8694
+ const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react17.useState)(false);
8324
8695
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
8325
8696
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
8326
8697
  initialWidth: isReadMode ? 700 : 440,
@@ -8358,6 +8729,7 @@ function NodeDetailsPanel({
8358
8729
  else if ((node == null ? void 0 : node.useImageAsTexture) === "false") setUseImageAsTexture(false);
8359
8730
  else setUseImageAsTexture(!!(node == null ? void 0 : node.useImageAsTexture));
8360
8731
  setSelectedImageUrl((node == null ? void 0 : node.textureImageUrl) ?? null);
8732
+ setHasUnsavedChanges(false);
8361
8733
  }
8362
8734
  }, [node]);
8363
8735
  const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
@@ -8368,15 +8740,22 @@ function NodeDetailsPanel({
8368
8740
  }
8369
8741
  }, [hasImages, useImageAsTexture]);
8370
8742
  (0, import_react17.useEffect)(() => {
8743
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
8371
8744
  if (typeInput.trim() === "") {
8372
- setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
8745
+ setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
8373
8746
  } else {
8374
- const lowercasedInput = typeInput.toLowerCase();
8375
- setFilteredTypes(
8376
- existingTypes.filter(
8377
- (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
8378
- )
8379
- );
8747
+ console.log("NodeDetailsPanel: Filtrando tipos com input:", typeInput);
8748
+ try {
8749
+ const lowercasedInput = typeInput.toLowerCase();
8750
+ setFilteredTypes(
8751
+ validExistingTypes.filter((t) => {
8752
+ if (!t) return false;
8753
+ return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
8754
+ })
8755
+ );
8756
+ } catch (err) {
8757
+ console.error("NodeDetailsPanel: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
8758
+ }
8380
8759
  }
8381
8760
  }, [typeInput, existingTypes, types]);
8382
8761
  const handleIntensityChangeLocal = (e) => {
@@ -8384,6 +8763,7 @@ function NodeDetailsPanel({
8384
8763
  setIntensity(val);
8385
8764
  onIntensityChange == null ? void 0 : onIntensityChange(node.id, val);
8386
8765
  onDataUpdate == null ? void 0 : onDataUpdate({ ...node, intensity: val });
8766
+ setHasUnsavedChanges(true);
8387
8767
  };
8388
8768
  const handleCopyLink = () => {
8389
8769
  if (!(node == null ? void 0 : node.id)) return;
@@ -8401,14 +8781,17 @@ function NodeDetailsPanel({
8401
8781
  const v = e.target.value;
8402
8782
  setName(v);
8403
8783
  onNameChange == null ? void 0 : onNameChange(node.id, v);
8784
+ setHasUnsavedChanges(true);
8404
8785
  };
8405
8786
  const handleColorChange = (val) => {
8406
8787
  setColor(val);
8407
8788
  onColorChange == null ? void 0 : onColorChange(node.id, val);
8789
+ setHasUnsavedChanges(true);
8408
8790
  };
8409
8791
  const handleSizeChange = (newSize) => {
8410
8792
  setSize(newSize);
8411
8793
  onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
8794
+ setHasUnsavedChanges(true);
8412
8795
  };
8413
8796
  const handleAddType = (newType) => {
8414
8797
  const trimmed = newType.trim();
@@ -8416,10 +8799,12 @@ function NodeDetailsPanel({
8416
8799
  setTypes([...types, trimmed]);
8417
8800
  setTypeInput("");
8418
8801
  setShowTypeSuggestions(false);
8802
+ setHasUnsavedChanges(true);
8419
8803
  }
8420
8804
  };
8421
8805
  const handleRemoveType = (indexToRemove) => {
8422
8806
  setTypes(types.filter((_, index) => index !== indexToRemove));
8807
+ setHasUnsavedChanges(true);
8423
8808
  };
8424
8809
  const handleTypeInputKeyDown = (e) => {
8425
8810
  if (e.key === "Enter") {
@@ -8432,6 +8817,7 @@ function NodeDetailsPanel({
8432
8817
  const handleAddProp = () => {
8433
8818
  const newProp = createNewCustomProperty(customProps);
8434
8819
  setCustomProps((p) => [...p, newProp]);
8820
+ setHasUnsavedChanges(true);
8435
8821
  setTimeout(() => {
8436
8822
  var _a;
8437
8823
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -8440,19 +8826,21 @@ function NodeDetailsPanel({
8440
8826
  const handleRemoveProp = (i) => {
8441
8827
  const newProps = customProps.filter((_, idx) => idx !== i);
8442
8828
  setCustomProps(newProps);
8829
+ setHasUnsavedChanges(true);
8443
8830
  triggerAutoSave({ customProps: newProps });
8444
8831
  };
8445
8832
  const handleUpdateProp = (index, updatedProp) => {
8446
8833
  const newProps = [...customProps];
8447
8834
  newProps[index] = updatedProp;
8448
8835
  setCustomProps(newProps);
8836
+ setHasUnsavedChanges(true);
8449
8837
  if (!updatedProp.isEditing) {
8450
8838
  triggerAutoSave({ customProps: newProps });
8451
8839
  }
8452
8840
  };
8453
8841
  const handleToggleImageMode = () => {
8454
- const newValue = !useImageAsTexture;
8455
8842
  setUseImageAsTexture(newValue);
8843
+ setHasUnsavedChanges(true);
8456
8844
  let activeUrl = null;
8457
8845
  if (newValue) {
8458
8846
  const firstImageProp = customProps.find((p) => p.type === "images");
@@ -8474,6 +8862,7 @@ function NodeDetailsPanel({
8474
8862
  };
8475
8863
  const handleSelectTexture = (url) => {
8476
8864
  setSelectedImageUrl(url);
8865
+ setHasUnsavedChanges(true);
8477
8866
  onImageChange == null ? void 0 : onImageChange(true, url, color);
8478
8867
  onDataUpdate == null ? void 0 : onDataUpdate({
8479
8868
  ...node,
@@ -8484,6 +8873,7 @@ function NodeDetailsPanel({
8484
8873
  };
8485
8874
  const handleSaveDescriptionInline = (newDescription) => {
8486
8875
  setDescription(newDescription);
8876
+ setHasUnsavedChanges(true);
8487
8877
  onDataUpdate({ ...node, description: newDescription });
8488
8878
  triggerAutoSave({ description: newDescription });
8489
8879
  };
@@ -8494,6 +8884,10 @@ function NodeDetailsPanel({
8494
8884
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
8495
8885
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
8496
8886
  const currentIntensity = overrides.intensity !== void 0 ? overrides.intensity : intensity;
8887
+ if (!keepOpen && !hasUnsavedChanges) {
8888
+ onClose();
8889
+ return;
8890
+ }
8497
8891
  if (!currentName.trim() || currentTypes.length === 0) {
8498
8892
  alert("O campo 'Nome' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8499
8893
  return;
@@ -8518,6 +8912,7 @@ function NodeDetailsPanel({
8518
8912
  };
8519
8913
  await onSave(dataToSave, keepOpen);
8520
8914
  onDataUpdate(dataToSave);
8915
+ setHasUnsavedChanges(false);
8521
8916
  if (!keepOpen) {
8522
8917
  onClose();
8523
8918
  }
@@ -8584,7 +8979,7 @@ function NodeDetailsPanel({
8584
8979
  onImageClick: handleImageClickFromText,
8585
8980
  onSaveDescription: handleSaveDescriptionInline
8586
8981
  }
8587
- ) : /* @__PURE__ */ import_react17.default.createElement(import_react17.default.Fragment, null, /* @__PURE__ */ import_react17.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ import_react17.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react17.default.createElement("div", null, /* @__PURE__ */ import_react17.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react17.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shadow-[0_0_18px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ import_react17.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ import_react17.default.createElement(
8982
+ ) : /* @__PURE__ */ import_react17.default.createElement(import_react17.default.Fragment, null, /* @__PURE__ */ import_react17.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ import_react17.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react17.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shadow-[0_0_18px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ import_react17.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ import_react17.default.createElement(
8588
8983
  "button",
8589
8984
  {
8590
8985
  onClick: handleCopyLink,
@@ -8592,7 +8987,7 @@ function NodeDetailsPanel({
8592
8987
  title: isLinkCopied ? "Link Copiado!" : "Copiar link para este Node"
8593
8988
  },
8594
8989
  isLinkCopied ? /* @__PURE__ */ import_react17.default.createElement(import_fi15.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react17.default.createElement(import_fi15.FiLink, { size: 12 })
8595
- )), /* @__PURE__ */ import_react17.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react17.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_react17.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react17.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ import_react17.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_react17.default.createElement("span", { key: index, className: "flex items-center gap-1 bg-indigo-500/30 text-indigo-100 px-1.5 py-0.5 rounded-md text-xs font-medium border border-indigo-500/20" }, t, canEdit && /* @__PURE__ */ import_react17.default.createElement(
8990
+ )), /* @__PURE__ */ import_react17.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react17.default.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 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_react17.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react17.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ import_react17.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_react17.default.createElement("span", { key: index, className: "flex items-center gap-1 bg-indigo-500/30 text-indigo-100 px-1.5 py-0.5 rounded-md text-xs font-medium border border-indigo-500/20" }, t, canEdit && /* @__PURE__ */ import_react17.default.createElement(
8596
8991
  "button",
8597
8992
  {
8598
8993
  type: "button",
@@ -8754,6 +9149,7 @@ function NodeDetailsPanel({
8754
9149
  initialValue: description,
8755
9150
  onSave: (newDescription) => {
8756
9151
  setDescription(newDescription);
9152
+ setHasUnsavedChanges(true);
8757
9153
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
8758
9154
  triggerAutoSave({ description: newDescription });
8759
9155
  },
@@ -8790,7 +9186,8 @@ function QuestDetailsPanel({
8790
9186
  onMentionClick,
8791
9187
  onUploadFile,
8792
9188
  userRole,
8793
- currentDatasetName
9189
+ currentDatasetName,
9190
+ viewMembers = []
8794
9191
  }) {
8795
9192
  var _a;
8796
9193
  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) || "";
@@ -8802,9 +9199,12 @@ function QuestDetailsPanel({
8802
9199
  const [typeInput, setTypeInput] = (0, import_react18.useState)("");
8803
9200
  const [status, setStatus] = (0, import_react18.useState)((node == null ? void 0 : node.status) ?? "Backlog");
8804
9201
  const [size, setSize] = (0, import_react18.useState)((node == null ? void 0 : node.size) ?? "medium");
9202
+ const [assigneeId, setAssigneeId] = (0, import_react18.useState)((node == null ? void 0 : node.assignee_id) || "");
8805
9203
  const [description, setDescription] = (0, import_react18.useState)((node == null ? void 0 : node.description) ?? "");
8806
9204
  const [intensity, setIntensity] = (0, import_react18.useState)((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
8807
9205
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = (0, import_react18.useState)(false);
9206
+ const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = (0, import_react18.useState)(false);
9207
+ const [assigneeSearchQuery, setAssigneeSearchQuery] = (0, import_react18.useState)("");
8808
9208
  const [customProps, setCustomProps] = (0, import_react18.useState)(() => extractCustomPropsFromNode(node || {}));
8809
9209
  const [showTypeSuggestions, setShowTypeSuggestions] = (0, import_react18.useState)(false);
8810
9210
  const [filteredTypes, setFilteredTypes] = (0, import_react18.useState)([]);
@@ -8813,6 +9213,7 @@ function QuestDetailsPanel({
8813
9213
  const [existingSections, setExistingSections] = (0, import_react18.useState)((node == null ? void 0 : node.description_sections) || []);
8814
9214
  const [isSaving, setIsSaving] = (0, import_react18.useState)(false);
8815
9215
  const [isLinkCopied, setIsLinkCopied] = (0, import_react18.useState)(false);
9216
+ const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react18.useState)(false);
8816
9217
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
8817
9218
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
8818
9219
  initialWidth: isReadMode ? 700 : 440,
@@ -8840,22 +9241,34 @@ function QuestDetailsPanel({
8840
9241
  setTypes((node == null ? void 0 : node.type) ? Array.isArray(node.type) ? node.type : [node.type] : ["quest"]);
8841
9242
  setStatus((node == null ? void 0 : node.status) ?? "Backlog");
8842
9243
  setSize((node == null ? void 0 : node.size) ?? "medium");
9244
+ setAssigneeId((node == null ? void 0 : node.assignee_id) || "");
8843
9245
  setDescription((node == null ? void 0 : node.description) ?? "");
8844
9246
  setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
8845
9247
  setExistingSections((node == null ? void 0 : node.description_sections) || []);
8846
9248
  setCustomProps(extractCustomPropsFromNode(node || {}));
9249
+ setHasUnsavedChanges(false);
8847
9250
  }
8848
9251
  }, [node]);
8849
9252
  (0, import_react18.useEffect)(() => {
9253
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
8850
9254
  if (typeInput.trim() === "") {
8851
- setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
9255
+ setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
8852
9256
  } else {
8853
- const lowercasedInput = typeInput.toLowerCase();
8854
- setFilteredTypes(
8855
- existingTypes.filter(
8856
- (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
8857
- )
8858
- );
9257
+ console.log("QuestDetailsPanel: Filtrando tipos com input:", typeInput);
9258
+ try {
9259
+ const lowercasedInput = typeInput.toLowerCase();
9260
+ setFilteredTypes(
9261
+ validExistingTypes.filter((t) => {
9262
+ if (!t) {
9263
+ console.warn("QuestDetailsPanel: Tipo encontrado como undefined/null durante filtragem");
9264
+ return false;
9265
+ }
9266
+ return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
9267
+ })
9268
+ );
9269
+ } catch (err) {
9270
+ console.error("QuestDetailsPanel: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
9271
+ }
8859
9272
  }
8860
9273
  }, [typeInput, existingTypes, types]);
8861
9274
  const handleCopyLink = () => {
@@ -8875,16 +9288,19 @@ function QuestDetailsPanel({
8875
9288
  setRawTitle(val);
8876
9289
  const newStandardName = questPrefix ? `${questPrefix} - \xBB ${val || "Sem t\xEDtulo"}` : val;
8877
9290
  onNameChange == null ? void 0 : onNameChange(node.id, newStandardName, val);
9291
+ setHasUnsavedChanges(true);
8878
9292
  };
8879
9293
  const handleSizeChange = (newSize) => {
8880
9294
  setSize(newSize);
8881
9295
  onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
9296
+ setHasUnsavedChanges(true);
8882
9297
  };
8883
9298
  const handleStatusChange = (newStatus) => {
8884
9299
  setStatus(newStatus);
8885
9300
  const newColor = QUEST_STATUS_COLORS3[newStatus];
8886
9301
  onColorChange == null ? void 0 : onColorChange(node.id, newColor);
8887
9302
  onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor });
9303
+ setHasUnsavedChanges(true);
8888
9304
  };
8889
9305
  const handleAddType = (newType) => {
8890
9306
  const trimmed = newType.trim();
@@ -8892,11 +9308,13 @@ function QuestDetailsPanel({
8892
9308
  setTypes([...types, trimmed]);
8893
9309
  setTypeInput("");
8894
9310
  setShowTypeSuggestions(false);
9311
+ setHasUnsavedChanges(true);
8895
9312
  }
8896
9313
  };
8897
9314
  const handleRemoveType = (indexToRemove) => {
8898
9315
  if (types[indexToRemove] === "quest") return;
8899
9316
  setTypes(types.filter((_, index) => index !== indexToRemove));
9317
+ setHasUnsavedChanges(true);
8900
9318
  };
8901
9319
  const handleTypeInputKeyDown = (e) => {
8902
9320
  if (e.key === "Enter") {
@@ -8909,6 +9327,7 @@ function QuestDetailsPanel({
8909
9327
  const handleAddProp = () => {
8910
9328
  const newProp = createNewCustomProperty(customProps);
8911
9329
  setCustomProps((p) => [...p, newProp]);
9330
+ setHasUnsavedChanges(true);
8912
9331
  setTimeout(() => {
8913
9332
  var _a2;
8914
9333
  (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -8917,18 +9336,21 @@ function QuestDetailsPanel({
8917
9336
  const handleRemoveProp = (i) => {
8918
9337
  const newProps = customProps.filter((_, idx) => idx !== i);
8919
9338
  setCustomProps(newProps);
9339
+ setHasUnsavedChanges(true);
8920
9340
  triggerAutoSave({ customProps: newProps });
8921
9341
  };
8922
9342
  const handleUpdateProp = (index, updatedProp) => {
8923
9343
  const newProps = [...customProps];
8924
9344
  newProps[index] = updatedProp;
8925
9345
  setCustomProps(newProps);
9346
+ setHasUnsavedChanges(true);
8926
9347
  if (!updatedProp.isEditing) {
8927
9348
  triggerAutoSave({ customProps: newProps });
8928
9349
  }
8929
9350
  };
8930
9351
  const handleSaveDescriptionInline = (newDescription) => {
8931
9352
  setDescription(newDescription);
9353
+ setHasUnsavedChanges(true);
8932
9354
  onDataUpdate({ ...node, description: newDescription });
8933
9355
  triggerAutoSave({ description: newDescription });
8934
9356
  };
@@ -8936,10 +9358,15 @@ function QuestDetailsPanel({
8936
9358
  const currentRawTitle = overrides.rawTitle !== void 0 ? overrides.rawTitle : rawTitle;
8937
9359
  const currentStandardName = questPrefix ? `${questPrefix} - \xBB ${currentRawTitle || "Sem t\xEDtulo"}` : currentRawTitle;
8938
9360
  const currentTypes = overrides.types !== void 0 ? overrides.types : types;
9361
+ const currentAssigneeId = overrides.assigneeId !== void 0 ? overrides.assigneeId : assigneeId;
8939
9362
  const currentDescription = overrides.description !== void 0 ? overrides.description : description;
8940
9363
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
8941
9364
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
8942
9365
  const currentStatus = overrides.status !== void 0 ? overrides.status : status;
9366
+ if (!keepOpen && !hasUnsavedChanges) {
9367
+ onClose();
9368
+ return;
9369
+ }
8943
9370
  if (!currentRawTitle.trim() || currentTypes.length === 0) {
8944
9371
  alert("O campo 'T\xEDtulo' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8945
9372
  return;
@@ -8957,6 +9384,7 @@ function QuestDetailsPanel({
8957
9384
  type: currentTypes,
8958
9385
  color: QUEST_STATUS_COLORS3[currentStatus],
8959
9386
  status: currentStatus,
9387
+ assignee_id: currentAssigneeId || null,
8960
9388
  size,
8961
9389
  description: currentDescription,
8962
9390
  description_sections: processedSections,
@@ -8969,6 +9397,7 @@ function QuestDetailsPanel({
8969
9397
  };
8970
9398
  await onSave(dataToSave, keepOpen);
8971
9399
  onDataUpdate(dataToSave);
9400
+ setHasUnsavedChanges(false);
8972
9401
  if (!keepOpen) {
8973
9402
  onClose();
8974
9403
  }
@@ -8988,6 +9417,8 @@ function QuestDetailsPanel({
8988
9417
  onClose();
8989
9418
  };
8990
9419
  const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
9420
+ const assigneeMember = viewMembers.find((m) => m.id === assigneeId);
9421
+ const isAssigneeUndefined = assigneeId && !assigneeMember;
8991
9422
  return /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement(
8992
9423
  "div",
8993
9424
  {
@@ -9026,7 +9457,7 @@ function QuestDetailsPanel({
9026
9457
  onImageClick: handleImageClickFromText,
9027
9458
  onSaveDescription: handleSaveDescriptionInline
9028
9459
  }
9029
- ) : /* @__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_COLORS3[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(
9460
+ ) : /* @__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_COLORS3[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", { className: "min-w-0" }, /* @__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 flex-shrink-0 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(
9030
9461
  "input",
9031
9462
  {
9032
9463
  type: "text",
@@ -9057,7 +9488,71 @@ function QuestDetailsPanel({
9057
9488
  },
9058
9489
  /* @__PURE__ */ import_react18.default.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS3[s] } }),
9059
9490
  s
9060
- )))))), /* @__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(
9491
+ )))))), /* @__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" }, "Assignee (Respons\xE1vel)"), canEdit ? /* @__PURE__ */ import_react18.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react18.default.createElement(
9492
+ "button",
9493
+ {
9494
+ type: "button",
9495
+ onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
9496
+ 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"
9497
+ },
9498
+ /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiUser, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ import_react18.default.createElement("span", { className: "text-slate-200 font-medium" }, isAssigneeUndefined ? "Undefined" : (assigneeMember == null ? void 0 : assigneeMember.name) || (assigneeMember == null ? void 0 : assigneeMember.email) || "Nenhum")),
9499
+ /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isAssigneeDropdownOpen ? "rotate-180" : ""}` })
9500
+ ), isAssigneeDropdownOpen && /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
9501
+ setIsAssigneeDropdownOpen(false);
9502
+ setAssigneeSearchQuery("");
9503
+ } }), /* @__PURE__ */ import_react18.default.createElement("div", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-900 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden flex flex-col" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiSearch, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ import_react18.default.createElement(
9504
+ "input",
9505
+ {
9506
+ type: "text",
9507
+ autoFocus: true,
9508
+ placeholder: "Buscar membro...",
9509
+ value: assigneeSearchQuery,
9510
+ onChange: (e) => setAssigneeSearchQuery(e.target.value),
9511
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
9512
+ onClick: (e) => e.stopPropagation()
9513
+ }
9514
+ )), /* @__PURE__ */ import_react18.default.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ import_react18.default.createElement(
9515
+ "li",
9516
+ {
9517
+ onClick: () => {
9518
+ setAssigneeId("");
9519
+ setHasUnsavedChanges(true);
9520
+ setIsAssigneeDropdownOpen(false);
9521
+ setAssigneeSearchQuery("");
9522
+ },
9523
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${!assigneeId ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
9524
+ },
9525
+ "Nenhum"
9526
+ ), isAssigneeUndefined && /* @__PURE__ */ import_react18.default.createElement(
9527
+ "li",
9528
+ {
9529
+ onClick: () => {
9530
+ setIsAssigneeDropdownOpen(false);
9531
+ setAssigneeSearchQuery("");
9532
+ },
9533
+ className: "px-3 py-2.5 text-sm cursor-pointer bg-red-500/10 text-red-300 flex items-center gap-2"
9534
+ },
9535
+ "Undefined (Removido)"
9536
+ ), viewMembers.filter((member) => {
9537
+ const search = assigneeSearchQuery.toLowerCase();
9538
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
9539
+ }).map((member) => /* @__PURE__ */ import_react18.default.createElement(
9540
+ "li",
9541
+ {
9542
+ key: member.id,
9543
+ onClick: () => {
9544
+ setAssigneeId(member.id);
9545
+ setHasUnsavedChanges(true);
9546
+ setIsAssigneeDropdownOpen(false);
9547
+ setAssigneeSearchQuery("");
9548
+ },
9549
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${assigneeId === member.id ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
9550
+ },
9551
+ member.name || member.email || member.id
9552
+ )), viewMembers.filter((member) => {
9553
+ const search = assigneeSearchQuery.toLowerCase();
9554
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
9555
+ }).length === 0 && assigneeSearchQuery && /* @__PURE__ */ import_react18.default.createElement("li", { className: "px-3 py-4 text-xs text-slate-500 text-center italic" }, "Nenhum membro encontrado"))))) : /* @__PURE__ */ import_react18.default.createElement("div", { className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 text-slate-400 flex items-center gap-2" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiUser, { className: "opacity-50", size: 14 }), assigneeId ? assigneeMember ? assigneeMember.name || assigneeMember.email : "Undefined" : "Nenhum")), /* @__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(
9061
9556
  "input",
9062
9557
  {
9063
9558
  type: "text",
@@ -9200,9 +9695,12 @@ function RelationshipDetailsPanel({
9200
9695
  const [description, setDescription] = (0, import_react20.useState)((link == null ? void 0 : link.description) ?? "");
9201
9696
  const [customProps, setCustomProps] = (0, import_react20.useState)(() => extractCustomPropsFromNode(link || {}));
9202
9697
  const [existingSections, setExistingSections] = (0, import_react20.useState)((link == null ? void 0 : link.description_sections) || []);
9698
+ const [sourceLabel, setSourceLabel] = (0, import_react20.useState)((link == null ? void 0 : link.source_label) ?? "");
9699
+ const [targetLabel, setTargetLabel] = (0, import_react20.useState)((link == null ? void 0 : link.target_label) ?? "");
9203
9700
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react20.useState)(false);
9204
9701
  const [isSaving, setIsSaving] = (0, import_react20.useState)(false);
9205
9702
  const [isReadMode, setIsReadMode] = (0, import_react20.useState)(false);
9703
+ const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react20.useState)(false);
9206
9704
  const propsEndRef = (0, import_react20.useRef)(null);
9207
9705
  const canEdit = (0, import_react20.useMemo)(() => {
9208
9706
  const ability = defineAbilityFor(userRole);
@@ -9213,12 +9711,16 @@ function RelationshipDetailsPanel({
9213
9711
  setDescription((link == null ? void 0 : link.description) ?? "");
9214
9712
  setExistingSections((link == null ? void 0 : link.description_sections) || []);
9215
9713
  setCustomProps(extractCustomPropsFromNode(link || {}));
9714
+ setSourceLabel((link == null ? void 0 : link.source_label) ?? "");
9715
+ setTargetLabel((link == null ? void 0 : link.target_label) ?? "");
9716
+ setHasUnsavedChanges(false);
9216
9717
  }, [link]);
9217
9718
  const swallow = (e) => e.stopPropagation();
9218
9719
  const handleAddProp = () => {
9219
9720
  if (!canEdit) return;
9220
9721
  const newProp = createNewCustomProperty(customProps);
9221
9722
  setCustomProps((p) => [...p, newProp]);
9723
+ setHasUnsavedChanges(true);
9222
9724
  setTimeout(() => {
9223
9725
  var _a;
9224
9726
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -9230,6 +9732,12 @@ function RelationshipDetailsPanel({
9230
9732
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
9231
9733
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
9232
9734
  const currentName = overrides.name !== void 0 ? overrides.name : name;
9735
+ const currentSourceLabel = overrides.sourceLabel !== void 0 ? overrides.sourceLabel : sourceLabel;
9736
+ const currentTargetLabel = overrides.targetLabel !== void 0 ? overrides.targetLabel : targetLabel;
9737
+ if (!keepOpen && !hasUnsavedChanges) {
9738
+ onClose();
9739
+ return;
9740
+ }
9233
9741
  setIsSaving(true);
9234
9742
  try {
9235
9743
  const extrasObj = toObjectFromCustomProps(currentCustomProps.filter((p) => !p.isEditing));
@@ -9245,8 +9753,11 @@ function RelationshipDetailsPanel({
9245
9753
  isCurved: link.isCurved,
9246
9754
  curveOffset: link.curveOffset
9247
9755
  };
9756
+ if (currentSourceLabel.trim()) dataToSave.source_label = currentSourceLabel.trim();
9757
+ if (currentTargetLabel.trim()) dataToSave.target_label = currentTargetLabel.trim();
9248
9758
  await onSave(dataToSave, keepOpen);
9249
9759
  onDataUpdate(dataToSave);
9760
+ setHasUnsavedChanges(false);
9250
9761
  if (!keepOpen) {
9251
9762
  onClose();
9252
9763
  }
@@ -9260,18 +9771,21 @@ function RelationshipDetailsPanel({
9260
9771
  const handleSaveDescriptionInline = (newDescription) => {
9261
9772
  if (!canEdit) return;
9262
9773
  setDescription(newDescription);
9774
+ setHasUnsavedChanges(true);
9263
9775
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
9264
9776
  triggerAutoSave({ description: newDescription });
9265
9777
  };
9266
9778
  const handleRemoveProp = (i) => {
9267
9779
  const newProps = customProps.filter((_, idx) => idx !== i);
9268
9780
  setCustomProps(newProps);
9781
+ setHasUnsavedChanges(true);
9269
9782
  triggerAutoSave({ customProps: newProps });
9270
9783
  };
9271
9784
  const handleUpdateProp = (index, updatedProp) => {
9272
9785
  const newProps = [...customProps];
9273
9786
  newProps[index] = updatedProp;
9274
9787
  setCustomProps(newProps);
9788
+ setHasUnsavedChanges(true);
9275
9789
  if (!updatedProp.isEditing) {
9276
9790
  triggerAutoSave({ customProps: newProps });
9277
9791
  }
@@ -9319,19 +9833,52 @@ function RelationshipDetailsPanel({
9319
9833
  onImageClick: handleImageClickFromText,
9320
9834
  onSaveDescription: handleSaveDescriptionInline
9321
9835
  }
9322
- ) : /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ import_react20.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react20.default.createElement("div", null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react20.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-teal-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ import_react20.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o")), /* @__PURE__ */ import_react20.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || "Rela\xE7\xE3o")), /* @__PURE__ */ import_react20.default.createElement("button", { onClick: onClose, 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: "Fechar" }, "\xD7")), /* @__PURE__ */ import_react20.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Rela\xE7\xE3o (Opcional)"), /* @__PURE__ */ import_react20.default.createElement(
9836
+ ) : /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ import_react20.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react20.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-teal-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ import_react20.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o")), /* @__PURE__ */ import_react20.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || "Rela\xE7\xE3o")), /* @__PURE__ */ import_react20.default.createElement("button", { onClick: onClose, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Fechar" }, "\xD7")), /* @__PURE__ */ import_react20.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Rela\xE7\xE3o (Opcional)"), /* @__PURE__ */ import_react20.default.createElement(
9323
9837
  "input",
9324
9838
  {
9325
9839
  type: "text",
9326
9840
  value: name,
9327
- onChange: (e) => setName(e.target.value),
9841
+ onChange: (e) => {
9842
+ setName(e.target.value);
9843
+ setHasUnsavedChanges(true);
9844
+ },
9328
9845
  placeholder: "Ex: Controla, Pertence a, Fornece...",
9329
9846
  disabled: !canEdit,
9330
9847
  className: `w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60
9331
9848
  ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9332
9849
  `
9333
9850
  }
9334
- )), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react20.default.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ import_react20.default.createElement(
9851
+ )), /* @__PURE__ */ import_react20.default.createElement("div", { className: "rounded-xl border border-white/8 bg-slate-800/30 p-3 space-y-3" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react20.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ import_react20.default.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ import_react20.default.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ import_react20.default.createElement("p", { className: "text-xs font-medium text-violet-300/80 uppercase tracking-wider" }, "Labels de Agrupamento")), /* @__PURE__ */ import_react20.default.createElement("p", { className: "text-[11px] text-slate-400 leading-relaxed" }, "Labels agrupam conex\xF5es no menu de Conex\xF5es. Cada lado da conex\xE3o pode ter sua pr\xF3pria label."), /* @__PURE__ */ import_react20.default.createElement("div", { className: "grid grid-cols-2 gap-2" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Source"), /* @__PURE__ */ import_react20.default.createElement(
9852
+ "input",
9853
+ {
9854
+ type: "text",
9855
+ value: sourceLabel,
9856
+ onChange: (e) => {
9857
+ setSourceLabel(e.target.value);
9858
+ setHasUnsavedChanges(true);
9859
+ },
9860
+ placeholder: "Ex: Conceitos",
9861
+ disabled: !canEdit,
9862
+ className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
9863
+ ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9864
+ `
9865
+ }
9866
+ )), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Target"), /* @__PURE__ */ import_react20.default.createElement(
9867
+ "input",
9868
+ {
9869
+ type: "text",
9870
+ value: targetLabel,
9871
+ onChange: (e) => {
9872
+ setTargetLabel(e.target.value);
9873
+ setHasUnsavedChanges(true);
9874
+ },
9875
+ placeholder: "Ex: Refer\xEAncias",
9876
+ disabled: !canEdit,
9877
+ className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
9878
+ ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9879
+ `
9880
+ }
9881
+ )))), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react20.default.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ import_react20.default.createElement(
9335
9882
  DescriptionDisplay,
9336
9883
  {
9337
9884
  description,
@@ -9401,6 +9948,7 @@ function RelationshipDetailsPanel({
9401
9948
  onSave: (newDescription) => {
9402
9949
  if (!canEdit) return;
9403
9950
  setDescription(newDescription);
9951
+ setHasUnsavedChanges(true);
9404
9952
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
9405
9953
  triggerAutoSave({ description: newDescription });
9406
9954
  },
@@ -9848,7 +10396,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
9848
10396
  onMentionClick,
9849
10397
  onImageClick: handleImageClickFromText
9850
10398
  }
9851
- ) : /* @__PURE__ */ import_react24.default.createElement(import_react24.default.Fragment, null, /* @__PURE__ */ import_react24.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ import_react24.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react24.default.createElement("div", null, /* @__PURE__ */ import_react24.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react24.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ import_react24.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ import_react24.default.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ import_react24.default.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ import_react24.default.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ import_react24.default.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ import_react24.default.createElement("button", { onClick: onClose, 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", title: "Fechar" }, "\xD7")), /* @__PURE__ */ import_react24.default.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ import_react24.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react24.default.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react24.default.createElement(
10399
+ ) : /* @__PURE__ */ import_react24.default.createElement(import_react24.default.Fragment, null, /* @__PURE__ */ import_react24.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ import_react24.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react24.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ import_react24.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ import_react24.default.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ import_react24.default.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ import_react24.default.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ import_react24.default.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ import_react24.default.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ import_react24.default.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ import_react24.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react24.default.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react24.default.createElement(
9852
10400
  "button",
9853
10401
  {
9854
10402
  onClick: () => setIsReadMode(true),
@@ -10487,7 +11035,8 @@ function XViewScene({
10487
11035
  save_ancestry_board_action,
10488
11036
  upload_file_action,
10489
11037
  delete_file_action,
10490
- check_user_permission
11038
+ check_user_permission,
11039
+ get_view_members
10491
11040
  }) {
10492
11041
  var _a, _b, _c, _d, _e, _f, _g, _h;
10493
11042
  const { data: session, status } = (0, import_react27.useSession)();
@@ -10520,6 +11069,13 @@ function XViewScene({
10520
11069
  } else {
10521
11070
  setPermissionStatus("denied");
10522
11071
  setIsLoading(false);
11072
+ return;
11073
+ }
11074
+ if (get_view_members) {
11075
+ const membersRes = await get_view_members(owner_id, type, id);
11076
+ if (membersRes.success && membersRes.members) {
11077
+ setViewMembers(membersRes.members);
11078
+ }
10523
11079
  }
10524
11080
  } catch (error) {
10525
11081
  console.error("Erro ao verificar permiss\xE3o:", error);
@@ -10560,6 +11116,7 @@ function XViewScene({
10560
11116
  const [isLoading, setIsLoading] = (0, import_react26.useState)(true);
10561
11117
  const [permissionStatus, setPermissionStatus] = (0, import_react26.useState)("loading");
10562
11118
  const [userPermissionRole, setUserPermissionRole] = (0, import_react26.useState)(null);
11119
+ const [viewMembers, setViewMembers] = (0, import_react26.useState)([]);
10563
11120
  const [isInitialized, setIsInitialized] = (0, import_react26.useState)(false);
10564
11121
  const [sceneVersion, setSceneVersion] = (0, import_react26.useState)(0);
10565
11122
  const [contextMenu, setContextMenu] = (0, import_react26.useState)({
@@ -10628,6 +11185,7 @@ function XViewScene({
10628
11185
  });
10629
11186
  const [isImportModalOpen, setIsImportModalOpen] = (0, import_react26.useState)(false);
10630
11187
  const [importSuccessMessage, setImportSuccessMessage] = (0, import_react26.useState)("");
11188
+ const [invalidTargetError, setInvalidTargetError] = (0, import_react26.useState)(null);
10631
11189
  const [highlightedNodeId, setHighlightedNodeId] = (0, import_react26.useState)(null);
10632
11190
  const [isAncestryBoardOpen, setIsAncestryBoardOpen] = (0, import_react26.useState)(false);
10633
11191
  const [ancestryBoardData, setAncestryBoardData] = (0, import_react26.useState)([]);
@@ -12476,6 +13034,19 @@ function XViewScene({
12476
13034
  const handleStartVersioning = (nodeData) => {
12477
13035
  userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
12478
13036
  };
13037
+ const handleStartQuestQuick = (0, import_react26.useCallback)((questNode) => {
13038
+ var _a2;
13039
+ if (!questNode || !actionHandlerContext) return;
13040
+ const updatedNode = {
13041
+ ...questNode,
13042
+ status: "In Progress",
13043
+ color: "#eab308",
13044
+ assignee_id: (_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id
13045
+ };
13046
+ if (userActionHandlers.handleSaveNodeDetails) {
13047
+ userActionHandlers.handleSaveNodeDetails(actionHandlerContext, updatedNode);
13048
+ }
13049
+ }, [session, actionHandlerContext]);
12479
13050
  const handleCancelQuest = (0, import_react26.useCallback)(() => {
12480
13051
  const { graphGroup, ghostElements } = stateRef.current;
12481
13052
  if (ghostElements.node && graphGroup) {
@@ -14160,7 +14731,13 @@ function XViewScene({
14160
14731
  if (!parentDataRef.current) {
14161
14732
  return [];
14162
14733
  }
14163
- return Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes).filter((node) => {
14734
+ return Object.entries(parentDataRef.current).flatMap(([dbId, fileData]) => {
14735
+ const datasetName = fileData.dataset_name || `Dataset #${dbId.substring(0, 6)}`;
14736
+ return (fileData.nodes || []).map((node) => ({
14737
+ ...node,
14738
+ dataset_name: datasetName
14739
+ }));
14740
+ }).filter((node) => {
14164
14741
  var _a2;
14165
14742
  return !((_a2 = node.version_node) == null ? void 0 : _a2.is_version);
14166
14743
  });
@@ -14304,6 +14881,9 @@ function XViewScene({
14304
14881
  }, 300);
14305
14882
  } else {
14306
14883
  setHasFocusedInitial(true);
14884
+ setInvalidTargetError(
14885
+ "O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
14886
+ );
14307
14887
  }
14308
14888
  }
14309
14889
  }, [
@@ -14326,6 +14906,9 @@ function XViewScene({
14326
14906
  }, 300);
14327
14907
  } else {
14328
14908
  setHasOpenedInitialAncestry(true);
14909
+ setInvalidTargetError(
14910
+ "O link aponta para uma ancestralidade que n\xE3o foi encontrada ou foi exclu\xEDda."
14911
+ );
14329
14912
  }
14330
14913
  }
14331
14914
  }, [
@@ -14619,7 +15202,8 @@ function XViewScene({
14619
15202
  availableNodes: allAvailableNodes,
14620
15203
  availableAncestries: allAvailableAncestries,
14621
15204
  viewName: viewParams == null ? void 0 : viewParams.name,
14622
- questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1
15205
+ questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1,
15206
+ viewMembers
14623
15207
  }
14624
15208
  ),
14625
15209
  readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ import_react26.default.createElement(
@@ -14749,7 +15333,8 @@ function XViewScene({
14749
15333
  onMentionClick: handleAddExistingNode,
14750
15334
  onUploadFile: upload_file_action,
14751
15335
  userRole: userPermissionRole,
14752
- currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
15336
+ currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName,
15337
+ viewMembers
14753
15338
  }
14754
15339
  ),
14755
15340
  detailsNode && !detailsNode.is_quest && /* @__PURE__ */ import_react26.default.createElement(
@@ -14862,7 +15447,10 @@ function XViewScene({
14862
15447
  onRenderAncestry: handleStartReadingAncestry,
14863
15448
  onEditAncestry: handleEditAncestry,
14864
15449
  onDeleteAncestry: (ancestryId) => handleDeleteAncestry(ancestryId),
14865
- onFocusNode: handleFocusNode
15450
+ onFocusNode: handleFocusNode,
15451
+ viewMembers,
15452
+ currentUser: session == null ? void 0 : session.user,
15453
+ onStartQuest: handleStartQuestQuick
14866
15454
  }
14867
15455
  ),
14868
15456
  /* @__PURE__ */ import_react26.default.createElement(
@@ -14935,6 +15523,83 @@ function XViewScene({
14935
15523
  currentViewName: viewParams == null ? void 0 : viewParams.name,
14936
15524
  currentAncestries: ancestryDataRef.current || []
14937
15525
  }
15526
+ ),
15527
+ invalidTargetError && /* @__PURE__ */ import_react26.default.createElement(
15528
+ "div",
15529
+ {
15530
+ className: "ui-overlay",
15531
+ style: {
15532
+ position: "fixed",
15533
+ top: "24px",
15534
+ left: "50%",
15535
+ transform: "translateX(-50%)",
15536
+ zIndex: 1e4,
15537
+ padding: "16px 24px",
15538
+ background: "rgba(30, 20, 20, 0.85)",
15539
+ backdropFilter: "blur(12px)",
15540
+ WebkitBackdropFilter: "blur(12px)",
15541
+ border: "1px solid rgba(255, 70, 70, 0.35)",
15542
+ borderRadius: "16px",
15543
+ boxShadow: "0 12px 40px rgba(0,0,0,0.5), 0 0 30px rgba(255, 50, 50, 0.1)",
15544
+ color: "#ffa0a0",
15545
+ display: "flex",
15546
+ alignItems: "center",
15547
+ gap: "16px",
15548
+ fontFamily: "Inter, sans-serif",
15549
+ animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
15550
+ }
15551
+ },
15552
+ /* @__PURE__ */ import_react26.default.createElement("style", null, `
15553
+ @keyframes fadeInDown {
15554
+ from { opacity: 0; transform: translate(-50%, -20px); }
15555
+ to { opacity: 1; transform: translate(-50%, 0); }
15556
+ }
15557
+ `),
15558
+ /* @__PURE__ */ import_react26.default.createElement(
15559
+ "svg",
15560
+ {
15561
+ width: "20",
15562
+ height: "20",
15563
+ viewBox: "0 0 24 24",
15564
+ fill: "none",
15565
+ stroke: "currentColor",
15566
+ strokeWidth: "2",
15567
+ strokeLinecap: "round",
15568
+ strokeLinejoin: "round"
15569
+ },
15570
+ /* @__PURE__ */ import_react26.default.createElement("circle", { cx: "12", cy: "12", r: "10" }),
15571
+ /* @__PURE__ */ import_react26.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
15572
+ /* @__PURE__ */ import_react26.default.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
15573
+ ),
15574
+ /* @__PURE__ */ import_react26.default.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
15575
+ /* @__PURE__ */ import_react26.default.createElement(
15576
+ "button",
15577
+ {
15578
+ onClick: () => setInvalidTargetError(null),
15579
+ style: {
15580
+ background: "rgba(255, 255, 255, 0.1)",
15581
+ border: "none",
15582
+ color: "white",
15583
+ padding: "4px 10px",
15584
+ borderRadius: "8px",
15585
+ cursor: "pointer",
15586
+ fontSize: "12px",
15587
+ fontWeight: 600,
15588
+ transition: "all 0.2s",
15589
+ marginLeft: "8px",
15590
+ border: "1px solid rgba(255,255,255,0.1)"
15591
+ },
15592
+ onMouseOver: (e) => {
15593
+ e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
15594
+ e.currentTarget.style.transform = "translateY(-1px)";
15595
+ },
15596
+ onMouseOut: (e) => {
15597
+ e.currentTarget.style.background = "rgba(255, 255, 255, 0.1)";
15598
+ e.currentTarget.style.transform = "translateY(0)";
15599
+ }
15600
+ },
15601
+ "Fechar"
15602
+ )
14938
15603
  )
14939
15604
  );
14940
15605
  }
@@ -15361,6 +16026,17 @@ async function delete_uploaded_file_logic(db_services, fileUrl) {
15361
16026
  return { success: false, error: error.message };
15362
16027
  }
15363
16028
  }
16029
+ async function get_view_members_logic(db_services, ownerId, type, itemId) {
16030
+ try {
16031
+ if (!db_services.get_view_members) {
16032
+ return { success: false, error: "Servi\xE7o de busca de membros n\xE3o configurado." };
16033
+ }
16034
+ return await db_services.get_view_members(ownerId, type, itemId);
16035
+ } catch (error) {
16036
+ console.error("Erro em get_view_members_logic:", error);
16037
+ return { success: false, error: error.message };
16038
+ }
16039
+ }
15364
16040
  // Annotate the CommonJS export names for ESM import in node:
15365
16041
  0 && (module.exports = {
15366
16042
  XViewScene,
@@ -15369,6 +16045,7 @@ async function delete_uploaded_file_logic(db_services, fileUrl) {
15369
16045
  get_ancestry_file_logic,
15370
16046
  get_scene_view_data_logic,
15371
16047
  get_single_parent_file_logic,
16048
+ get_view_members_logic,
15372
16049
  import_parent_file_modal_get_logic,
15373
16050
  save_ancestry_board_logic,
15374
16051
  save_view_data_logic,