@lv-x-software-house/x_view 1.2.6-dev.3 → 1.2.6-dev.31

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 +1597 -866
  2. package/dist/index.mjs +1518 -806
  3. package/package.json +4 -22
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- "use client";
2
1
  var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -30,14 +29,24 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
29
  // src/index.js
31
30
  var index_exports = {};
32
31
  __export(index_exports, {
33
- XViewScene: () => XViewScene
32
+ XViewScene: () => XViewScene,
33
+ delete_uploaded_file_logic: () => delete_uploaded_file_logic,
34
+ get_ancestry_file_logic: () => get_ancestry_file_logic,
35
+ get_boards_logic: () => get_boards_logic,
36
+ get_scene_view_data_logic: () => get_scene_view_data_logic,
37
+ get_single_parent_file_logic: () => get_single_parent_file_logic,
38
+ get_view_members_logic: () => get_view_members_logic,
39
+ import_parent_file_modal_get_logic: () => import_parent_file_modal_get_logic,
40
+ save_boards_logic: () => save_boards_logic,
41
+ save_view_data_logic: () => save_view_data_logic,
42
+ x_view_add_new_parent_file_to_scene_at_firebase: () => x_view_add_new_parent_file_to_scene_at_firebase
34
43
  });
35
44
  module.exports = __toCommonJS(index_exports);
36
45
 
37
46
  // src/XViewScene.jsx
38
- var import_react32 = __toESM(require("react"));
47
+ var import_react31 = __toESM(require("react"));
39
48
  var import_navigation = require("next/navigation");
40
- var import_react33 = require("next-auth/react");
49
+ var import_react32 = require("next-auth/react");
41
50
  var import_crypto_js = __toESM(require("crypto-js"));
42
51
  var THREE3 = __toESM(require("three"));
43
52
  var import_OrbitControls = require("three/examples/jsm/controls/OrbitControls.js");
@@ -68,8 +77,8 @@ function defineAbilityFor(role) {
68
77
  can("read", "Versioning");
69
78
  can("read", "Node");
70
79
  can("read", "Scene");
71
- can("read", "AncestryBoard");
72
- can("read", "AncestryGroup");
80
+ can("read", "Boards");
81
+ can("read", "BoardGroups");
73
82
  can("dismiss", "Node");
74
83
  cannot("update", "Scene");
75
84
  cannot("import", "File");
@@ -82,10 +91,10 @@ function defineAbilityFor(role) {
82
91
  cannot("create", "Ancestry");
83
92
  cannot("update", "Ancestry");
84
93
  cannot("delete", "Ancestry");
85
- cannot("create", "AncestryGroup");
86
- cannot("update", "AncestryGroup");
87
- cannot("delete", "AncestryGroup");
88
- cannot("manage", "AncestryBoard");
94
+ cannot("create", "BoardGroups");
95
+ cannot("update", "BoardGroups");
96
+ cannot("delete", "BoardGroups");
97
+ cannot("manage", "Boards");
89
98
  cannot("update", "Node");
90
99
  break;
91
100
  case "link_viewer":
@@ -94,8 +103,8 @@ function defineAbilityFor(role) {
94
103
  can("read", "Versioning");
95
104
  cannot("read", "Scene");
96
105
  cannot("read", "Connection");
97
- cannot("read", "AncestryBoard");
98
- cannot("read", "AncestryGroup");
106
+ cannot("read", "Boards");
107
+ cannot("read", "BoardGroups");
99
108
  cannot("dismiss", "Node");
100
109
  cannot("manage", "all");
101
110
  break;
@@ -1083,6 +1092,8 @@ var userActionHandlers = {
1083
1092
  if (isCrossDataset) {
1084
1093
  graphDataRef.current[finalTargetDatasetId] = targetDataToUpdate;
1085
1094
  }
1095
+ if (sceneDataRef.current.parent_dbs.some((db) => String(db.db_id) === String(sourceParentInfo.parentFileId))) {
1096
+ }
1086
1097
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1087
1098
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
1088
1099
  stateRef.current.nodeIdToParentFileMap.set(String(newNode.id), {
@@ -1281,6 +1292,9 @@ var userActionHandlers = {
1281
1292
  );
1282
1293
  }
1283
1294
  graphDataRef.current[parentFileId] = specificParentData;
1295
+ if (isView && parentFileId === context.sceneConfigId) {
1296
+ sceneDataRef.current.quest_nodes = [...specificParentData.nodes];
1297
+ }
1284
1298
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1285
1299
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
1286
1300
  setters.setSceneVersion((v) => v + 1);
@@ -2300,8 +2314,6 @@ var userActionHandlers = {
2300
2314
  milestones: sceneDataRef.current.milestones || [],
2301
2315
  quest_counter: sceneDataRef.current.quest_counter || 1
2302
2316
  };
2303
- sceneDataRef.current.quest_nodes = specificParentData.nodes;
2304
- sceneDataRef.current.quest_links = specificParentData.links;
2305
2317
  await context.actions.save_view_data(
2306
2318
  context.sceneSaveUrl,
2307
2319
  viewFilePayload
@@ -2314,6 +2326,10 @@ var userActionHandlers = {
2314
2326
  );
2315
2327
  }
2316
2328
  graphDataRef.current[parentFileId] = specificParentData;
2329
+ if (isView && parentFileId === context.sceneConfigId) {
2330
+ sceneDataRef.current.quest_nodes = [...specificParentData.nodes];
2331
+ sceneDataRef.current.quest_counter = specificParentData.quest_counter || sceneDataRef.current.quest_counter;
2332
+ }
2317
2333
  updateExistingNodeVisuals(stateRef.current, nodeToSave);
2318
2334
  setters.setSceneVersion((v) => v + 1);
2319
2335
  if (!keepOpen) {
@@ -3866,7 +3882,7 @@ function XViewSidebar({
3866
3882
  importSuccessMessage = "",
3867
3883
  onActivateTimeline = () => {
3868
3884
  },
3869
- onOpenAncestryBoard = () => {
3885
+ onOpenBoards = () => {
3870
3886
  },
3871
3887
  viewType,
3872
3888
  viewName,
@@ -4367,12 +4383,12 @@ function XViewSidebar({
4367
4383
  ), /* @__PURE__ */ import_react3.default.createElement(
4368
4384
  "button",
4369
4385
  {
4370
- onClick: onOpenAncestryBoard,
4386
+ onClick: onOpenBoards,
4371
4387
  className: buttonBaseClass,
4372
- title: "Abrir painel de Ancestralidades"
4388
+ title: "Abrir painel de Boards"
4373
4389
  },
4374
- /* @__PURE__ */ import_react3.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_react3.default.createElement("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }), /* @__PURE__ */ import_react3.default.createElement("polyline", { points: "2 17 12 22 22 17" }), /* @__PURE__ */ import_react3.default.createElement("polyline", { points: "2 12 12 17 22 12" })),
4375
- "Ancestry Board"
4390
+ /* @__PURE__ */ import_react3.default.createElement(import_fi2.FiLayers, { size: 16 }),
4391
+ "Boards"
4376
4392
  )), /* @__PURE__ */ import_react3.default.createElement("div", { className: "h-px bg-white/10" }), /* @__PURE__ */ import_react3.default.createElement("section", { className: "space-y-3" }, /* @__PURE__ */ import_react3.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Milestones"), /* @__PURE__ */ import_react3.default.createElement(
4377
4393
  "button",
4378
4394
  {
@@ -12902,559 +12918,787 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
12902
12918
  );
12903
12919
  }
12904
12920
 
12905
- // src/components/AncestryBoard.jsx
12906
- var import_react30 = __toESM(require("react"));
12921
+ // src/components/Boards.jsx
12922
+ var import_react29 = __toESM(require("react"));
12907
12923
  var import_fi23 = require("react-icons/fi");
12908
12924
  var import_uuid11 = require("uuid");
12909
12925
 
12910
- // src/components/MentionEditor.jsx
12926
+ // src/components/RichGroupEditor.jsx
12911
12927
  var import_react28 = __toESM(require("react"));
12912
- var import_react29 = require("@tiptap/react");
12913
- var import_starter_kit = __toESM(require("@tiptap/starter-kit"));
12914
- var import_extension_mention = __toESM(require("@tiptap/extension-mention"));
12915
- var import_tippy = __toESM(require("tippy.js"));
12916
12928
 
12917
- // src/components/MentionList.jsx
12929
+ // src/components/MentionPopover.jsx
12918
12930
  var import_react27 = __toESM(require("react"));
12919
12931
  var import_fi22 = require("react-icons/fi");
12920
- var MentionList = (0, import_react27.forwardRef)((props, ref) => {
12921
- const [selectedIndex, setSelectedIndex] = (0, import_react27.useState)(0);
12922
- const selectItem = (index) => {
12923
- const item = props.items[index];
12924
- if (item) {
12925
- props.command({ id: item.id, label: item.name, type: item.type });
12926
- }
12927
- };
12928
- const upHandler = () => {
12929
- setSelectedIndex(
12930
- (selectedIndex + props.items.length - 1) % props.items.length
12931
- );
12932
- };
12933
- const downHandler = () => {
12934
- setSelectedIndex((selectedIndex + 1) % props.items.length);
12935
- };
12936
- const enterHandler = () => {
12937
- selectItem(selectedIndex);
12938
- };
12939
- (0, import_react27.useEffect)(() => setSelectedIndex(0), [props.items]);
12940
- (0, import_react27.useImperativeHandle)(ref, () => ({
12941
- onKeyDown: ({ event }) => {
12942
- if (event.key === "ArrowUp") {
12943
- upHandler();
12944
- return true;
12945
- }
12946
- if (event.key === "ArrowDown") {
12947
- downHandler();
12948
- return true;
12949
- }
12950
- if (event.key === "Enter") {
12951
- enterHandler();
12952
- return true;
12932
+ var TYPE_CONFIG = {
12933
+ ancestry: { Icon: import_fi22.FiLayers, color: "text-indigo-400", label: "Ancestralidade" },
12934
+ quest: { Icon: import_fi22.FiCheckSquare, color: "text-emerald-400", label: "Quest" },
12935
+ version: { Icon: import_fi22.FiGitBranch, color: "text-amber-400", label: "Vers\xE3o" },
12936
+ node: { Icon: import_fi22.FiBox, color: "text-slate-400", label: "Node" }
12937
+ };
12938
+ function MentionPopover({ query, items, position, onSelect, onClose }) {
12939
+ const [activeIndex, setActiveIndex] = (0, import_react27.useState)(0);
12940
+ const listRef = (0, import_react27.useRef)(null);
12941
+ const filtered = (0, import_react27.useMemo)(() => {
12942
+ const term = (query || "").toLowerCase().trim();
12943
+ if (!term) return items.slice(0, 8);
12944
+ return items.filter(
12945
+ (item) => (item.searchLabel || "").toLowerCase().includes(term) || (item.searchDesc || "").toLowerCase().includes(term)
12946
+ ).slice(0, 8);
12947
+ }, [items, query]);
12948
+ (0, import_react27.useEffect)(() => {
12949
+ setActiveIndex(0);
12950
+ }, [query]);
12951
+ (0, import_react27.useEffect)(() => {
12952
+ const handleKeyDown = (e) => {
12953
+ if (e.key === "ArrowDown") {
12954
+ e.preventDefault();
12955
+ setActiveIndex((i) => Math.min(i + 1, filtered.length - 1));
12956
+ } else if (e.key === "ArrowUp") {
12957
+ e.preventDefault();
12958
+ setActiveIndex((i) => Math.max(i - 1, 0));
12959
+ } else if (e.key === "Enter" || e.key === "Tab") {
12960
+ e.preventDefault();
12961
+ if (filtered[activeIndex]) onSelect(filtered[activeIndex]);
12962
+ } else if (e.key === "Escape") {
12963
+ e.preventDefault();
12964
+ e.stopPropagation();
12965
+ e.stopImmediatePropagation();
12966
+ onClose();
12953
12967
  }
12954
- return false;
12955
- }
12956
- }));
12957
- return /* @__PURE__ */ import_react27.default.createElement("div", { className: "z-[3000] bg-slate-900 border border-white/10 rounded-lg shadow-2xl overflow-hidden min-w-[280px] max-w-[400px] animate-in fade-in zoom-in-95 duration-200" }, /* @__PURE__ */ import_react27.default.createElement("div", { className: "p-2 border-b border-white/5 bg-slate-950/50 flex items-center gap-2" }, /* @__PURE__ */ import_react27.default.createElement(import_fi22.FiSearch, { className: "text-indigo-400", size: 14 }), /* @__PURE__ */ import_react27.default.createElement("span", { className: "text-[10px] uppercase tracking-wider font-bold text-slate-500" }, "Mencionar Recurso")), /* @__PURE__ */ import_react27.default.createElement("div", { className: "p-1 max-h-[300px] overflow-y-auto custom-scrollbar" }, props.items.length ? props.items.map((item, index) => {
12958
- const isQuest = item.type === "quest";
12959
- const isVersion = item.type === "version";
12960
- const isNode = item.type === "node";
12961
- let Icon = import_fi22.FiLayers;
12962
- let colorClass = "text-indigo-400";
12963
- let badgeText = "Ancestralidade";
12964
- if (isQuest) {
12965
- Icon = import_fi22.FiCheckSquare;
12966
- colorClass = "text-amber-400";
12967
- badgeText = "Quest";
12968
- } else if (isVersion) {
12969
- Icon = import_fi22.FiGitBranch;
12970
- colorClass = "text-purple-400";
12971
- badgeText = "Vers\xE3o";
12972
- } else if (isNode) {
12973
- Icon = import_fi22.FiBox;
12974
- colorClass = "text-emerald-400";
12975
- badgeText = "Node";
12976
- }
12977
- return /* @__PURE__ */ import_react27.default.createElement(
12978
- "button",
12979
- {
12980
- className: `
12981
- w-full flex items-center gap-3 px-3 py-2 text-left rounded-md transition-all
12982
- ${index === selectedIndex ? "bg-indigo-500/20 border border-indigo-500/30 text-white" : "text-slate-400 hover:bg-white/5 border border-transparent"}
12983
- `,
12984
- key: item.id,
12985
- onClick: () => selectItem(index)
12986
- },
12987
- /* @__PURE__ */ import_react27.default.createElement("div", { className: `shrink-0 w-8 h-8 rounded border border-white/5 flex items-center justify-center bg-slate-800/50 ${colorClass}` }, /* @__PURE__ */ import_react27.default.createElement(Icon, { size: 14 })),
12988
- /* @__PURE__ */ import_react27.default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ import_react27.default.createElement("div", { className: "flex items-center justify-between gap-2" }, /* @__PURE__ */ import_react27.default.createElement("span", { className: "text-sm font-medium truncate" }, item.name), /* @__PURE__ */ import_react27.default.createElement("span", { className: `text-[9px] px-1 py-0.5 rounded border border-current opacity-60 ${colorClass}` }, badgeText)))
12989
- );
12990
- }) : /* @__PURE__ */ import_react27.default.createElement("div", { className: "p-4 text-center text-slate-500 text-sm italic" }, "Nenhum resultado encontrado...")));
12991
- });
12992
- MentionList.displayName = "MentionList";
12993
- var MentionList_default = MentionList;
12968
+ };
12969
+ window.addEventListener("keydown", handleKeyDown, true);
12970
+ return () => window.removeEventListener("keydown", handleKeyDown, true);
12971
+ }, [filtered, activeIndex, onSelect, onClose]);
12972
+ (0, import_react27.useEffect)(() => {
12973
+ if (listRef.current) {
12974
+ const el = listRef.current.children[activeIndex];
12975
+ if (el) el.scrollIntoView({ block: "nearest" });
12976
+ }
12977
+ }, [activeIndex]);
12978
+ return /* @__PURE__ */ import_react27.default.createElement(
12979
+ "div",
12980
+ {
12981
+ style: { top: position.top, left: position.left, position: "fixed", zIndex: 9999 },
12982
+ className: "bg-slate-900 border border-white/10 rounded-xl shadow-2xl py-1.5 min-w-[260px] max-h-[240px] overflow-y-auto custom-scrollbar animate-in fade-in zoom-in-95 duration-100",
12983
+ ref: listRef,
12984
+ onMouseDown: (e) => e.preventDefault()
12985
+ },
12986
+ filtered.length === 0 ? /* @__PURE__ */ import_react27.default.createElement("div", { className: "px-4 py-3 text-xs text-slate-500" }, "Nenhum resultado para", " ", /* @__PURE__ */ import_react27.default.createElement("span", { className: "text-slate-300 font-medium" }, '"', query, '"')) : filtered.map((item, idx) => {
12987
+ const config = TYPE_CONFIG[item.type] || TYPE_CONFIG.node;
12988
+ const { Icon } = config;
12989
+ return /* @__PURE__ */ import_react27.default.createElement(
12990
+ "div",
12991
+ {
12992
+ key: item.id,
12993
+ onMouseDown: (e) => {
12994
+ e.preventDefault();
12995
+ onSelect(item);
12996
+ },
12997
+ onMouseEnter: () => setActiveIndex(idx),
12998
+ className: `flex items-center gap-2.5 px-3 py-2 cursor-pointer text-sm transition-colors duration-100 ${idx === activeIndex ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5"}`
12999
+ },
13000
+ /* @__PURE__ */ import_react27.default.createElement(Icon, { size: 13, className: config.color }),
13001
+ /* @__PURE__ */ import_react27.default.createElement("span", { className: "flex-1 truncate font-medium" }, item.searchLabel || "Sem Nome"),
13002
+ /* @__PURE__ */ import_react27.default.createElement("span", { className: "text-[9px] px-1.5 py-0.5 rounded-full bg-white/5 text-slate-500 border border-white/5 uppercase tracking-wide shrink-0" }, config.label)
13003
+ );
13004
+ })
13005
+ );
13006
+ }
12994
13007
 
12995
- // src/components/MentionEditor.jsx
12996
- var CustomMention = import_extension_mention.default.extend({
12997
- addAttributes() {
13008
+ // src/components/RichGroupEditor.jsx
13009
+ var MENTION_BASE_STYLE = "display:inline-flex;align-items:center;padding:1px 7px;border-radius:5px;margin:0 2px;font-size:0.78em;font-weight:600;cursor:pointer;white-space:nowrap;vertical-align:middle;user-select:none;line-height:1.6;";
13010
+ var MENTION_TYPE_STYLE = {
13011
+ ancestry: "background:rgba(99,102,241,0.18);color:#a5b4fc;border:1px solid rgba(99,102,241,0.35);",
13012
+ quest: "background:rgba(16,185,129,0.18);color:#6ee7b7;border:1px solid rgba(16,185,129,0.35);",
13013
+ version: "background:rgba(245,158,11,0.18);color:#fcd34d;border:1px solid rgba(245,158,11,0.35);",
13014
+ node: "background:rgba(100,116,139,0.18);color:#94a3b8;border:1px solid rgba(100,116,139,0.35);"
13015
+ };
13016
+ function mentionStyle(type) {
13017
+ return MENTION_BASE_STYLE + (MENTION_TYPE_STYLE[type] || MENTION_TYPE_STYLE.node);
13018
+ }
13019
+ function escapeHtml(str) {
13020
+ return String(str || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
13021
+ }
13022
+ function contentToHTML(content) {
13023
+ if (!content || content.length === 0) return "";
13024
+ return content.map((seg) => {
13025
+ if (seg.type === "text") {
13026
+ return escapeHtml(seg.value).replace(/\n/g, "<br>");
13027
+ }
13028
+ if (seg.type === "mention") {
13029
+ const style = mentionStyle(seg.mentionType);
13030
+ return `<span contenteditable="false" data-mention="true" data-id="${escapeHtml(String(seg.id))}" data-name="${escapeHtml(seg.name || "")}" data-mention-type="${escapeHtml(seg.mentionType || "node")}" style="${style}" title="Clique para focar na cena">@${escapeHtml(seg.name || "")}</span>`;
13031
+ }
13032
+ return "";
13033
+ }).join("");
13034
+ }
13035
+ function domToContent(el) {
13036
+ const segments = [];
13037
+ function pushText(text) {
13038
+ const clean = text.replace(/\u200B/g, "");
13039
+ if (!clean) return;
13040
+ const last = segments[segments.length - 1];
13041
+ if (last && last.type === "text") {
13042
+ last.value += clean;
13043
+ } else {
13044
+ segments.push({ type: "text", value: clean });
13045
+ }
13046
+ }
13047
+ function walk(node) {
12998
13048
  var _a;
12999
- return {
13000
- ...(_a = this.parent) == null ? void 0 : _a.call(this),
13001
- type: {
13002
- default: null,
13003
- parseHTML: (element) => element.getAttribute("data-mention-type"),
13004
- renderHTML: (attributes) => {
13005
- if (!attributes.type) {
13006
- return {};
13007
- }
13008
- return {
13009
- "data-mention-type": attributes.type
13010
- };
13011
- }
13012
- },
13013
- label: {
13014
- default: null,
13015
- parseHTML: (element) => element.getAttribute("data-label"),
13016
- renderHTML: (attributes) => {
13017
- if (!attributes.label) {
13018
- return {};
13019
- }
13020
- return {
13021
- "data-label": attributes.label
13022
- };
13049
+ if (node.nodeType === Node.TEXT_NODE) {
13050
+ pushText(node.textContent);
13051
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
13052
+ if (((_a = node.dataset) == null ? void 0 : _a.mention) === "true") {
13053
+ segments.push({
13054
+ type: "mention",
13055
+ id: node.dataset.id,
13056
+ name: node.dataset.name,
13057
+ mentionType: node.dataset.mentionType
13058
+ });
13059
+ } else if (node.tagName === "BR") {
13060
+ pushText("\n");
13061
+ } else {
13062
+ if (["DIV", "P"].includes(node.tagName) && segments.length > 0) {
13063
+ pushText("\n");
13023
13064
  }
13065
+ node.childNodes.forEach(walk);
13024
13066
  }
13025
- };
13067
+ }
13026
13068
  }
13027
- });
13028
- var MentionEditor = ({
13029
- initialValue = "",
13069
+ el.childNodes.forEach(walk);
13070
+ return segments;
13071
+ }
13072
+ function RichGroupEditor({
13073
+ content,
13030
13074
  onChange,
13031
- onPlay,
13032
- availableItems = [],
13033
- canEdit = true,
13034
- placeholder = "Escreva algo... use @ para mencionar"
13035
- }) => {
13036
- const parseInitialContent = (text) => {
13037
- if (!text) return "<p></p>";
13038
- const parts = text.split(/(\[\[MENTION:[^\]]+\]\])/g);
13039
- return parts.map((part) => {
13040
- const match = part.match(/\[\[MENTION:([^:]+):([^:]+):([^\]]+)\]\]/);
13041
- if (match) {
13042
- const [, type, id, label] = match;
13043
- return `<span data-type="mention" data-id="${id}" data-mention-type="${type}" data-label="${label}">@${label}</span>`;
13044
- }
13045
- return part;
13046
- }).join("");
13047
- };
13048
- const content = (0, import_react28.useMemo)(() => parseInitialContent(initialValue), [initialValue]);
13049
- const editor = (0, import_react29.useEditor)({
13050
- extensions: [
13051
- import_starter_kit.default.configure({
13052
- heading: false,
13053
- bulletList: false,
13054
- orderedList: false,
13055
- codeBlock: false,
13056
- blockquote: false
13057
- }),
13058
- CustomMention.configure({
13059
- HTMLAttributes: {
13060
- class: "mention"
13061
- },
13062
- suggestion: {
13063
- items: ({ query }) => {
13064
- return availableItems.filter(
13065
- (item) => item.name.toLowerCase().includes(query.toLowerCase())
13066
- ).slice(0, 10);
13067
- },
13068
- render: () => {
13069
- let component;
13070
- let popup;
13071
- return {
13072
- onStart: (props) => {
13073
- component = new import_react29.ReactRenderer(MentionList_default, {
13074
- props,
13075
- editor: props.editor
13076
- });
13077
- if (!props.clientRect) {
13078
- return;
13079
- }
13080
- popup = (0, import_tippy.default)("body", {
13081
- getReferenceClientRect: props.clientRect,
13082
- appendTo: () => document.body,
13083
- content: component.element,
13084
- showOnCreate: true,
13085
- interactive: true,
13086
- trigger: "manual",
13087
- placement: "bottom-start",
13088
- theme: "x-view-dark"
13089
- });
13090
- },
13091
- onUpdate(props) {
13092
- component.updateProps(props);
13093
- if (!props.clientRect) {
13094
- return;
13095
- }
13096
- popup[0].setProps({
13097
- getReferenceClientRect: props.clientRect
13098
- });
13099
- },
13100
- onKeyDown(props) {
13101
- var _a;
13102
- if (props.event.key === "Escape") {
13103
- popup[0].hide();
13104
- return true;
13105
- }
13106
- return (_a = component == null ? void 0 : component.ref) == null ? void 0 : _a.onKeyDown(props);
13107
- },
13108
- onExit() {
13109
- if (popup && popup[0]) popup[0].destroy();
13110
- if (component) component.destroy();
13111
- }
13112
- };
13113
- }
13114
- }
13115
- })
13116
- ],
13117
- content,
13118
- editable: canEdit,
13119
- onUpdate: ({ editor: editor2 }) => {
13120
- const serialize = (json) => {
13121
- if (!json.content) return "";
13122
- return json.content.map((block) => {
13123
- if (block.type === "paragraph") {
13124
- const inner = block.content ? block.content.map((node) => {
13125
- if (node.type === "text") return node.text;
13126
- if (node.type === "mention") {
13127
- return `[[MENTION:${node.attrs.type}:${node.attrs.id}:${node.attrs.label}]]`;
13128
- }
13129
- return "";
13130
- }).join("") : "";
13131
- return inner;
13132
- }
13133
- return "";
13134
- }).join("\n").trim();
13135
- };
13136
- const serialized = serialize(editor2.getJSON());
13137
- onChange(serialized);
13075
+ onMentionClick,
13076
+ searchItems,
13077
+ readOnly,
13078
+ placeholder
13079
+ }) {
13080
+ const editorRef = (0, import_react28.useRef)(null);
13081
+ const isMountedRef = (0, import_react28.useRef)(false);
13082
+ const dismissedTriggerRef = (0, import_react28.useRef)(null);
13083
+ const [mentionState, setMentionState] = (0, import_react28.useState)(null);
13084
+ const [isEmpty, setIsEmpty] = (0, import_react28.useState)(true);
13085
+ (0, import_react28.useEffect)(() => {
13086
+ if (!isMountedRef.current && editorRef.current) {
13087
+ editorRef.current.innerHTML = contentToHTML(content);
13088
+ const hasContent = !!editorRef.current.innerText.replace(/\u200B/g, "").trim();
13089
+ const hasMentions = !!editorRef.current.querySelector("[data-mention]");
13090
+ setIsEmpty(!hasContent && !hasMentions);
13091
+ isMountedRef.current = true;
13138
13092
  }
13139
- });
13140
- if (!editor) {
13141
- return null;
13142
- }
13143
- return /* @__PURE__ */ import_react28.default.createElement("div", { className: "relative group/editor min-h-[1.5rem] w-full" }, /* @__PURE__ */ import_react28.default.createElement(
13144
- import_react29.EditorContent,
13145
- {
13146
- editor,
13147
- className: "text-sm text-slate-200 placeholder-slate-600 focus:outline-none"
13148
- }
13149
- ), /* @__PURE__ */ import_react28.default.createElement("style", null, `
13150
- .tiptap p {
13151
- margin: 0;
13152
- min-height: 1.25rem;
13153
- line-height: 1.5;
13154
- }
13155
- .tiptap .mention {
13156
- display: inline-flex;
13157
- align-items: center;
13158
- background: rgba(99, 102, 241, 0.12);
13159
- border: 1px solid rgba(99, 102, 241, 0.25);
13160
- border-radius: 6px;
13161
- padding: 1px 6px;
13162
- color: #a5b4fc;
13163
- font-weight: 600;
13164
- cursor: pointer;
13165
- margin: 0 1px;
13166
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
13167
- transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
13168
- font-size: 0.85rem;
13169
- text-decoration: none;
13170
- vertical-align: baseline;
13171
- }
13172
- .tiptap .mention:hover {
13173
- background: rgba(99, 102, 241, 0.22);
13174
- border-color: rgba(99, 102, 241, 0.5);
13175
- color: #fff;
13176
- transform: translateY(-1px);
13177
- box-shadow: 0 4px 12px rgba(99, 102, 241, 0.2);
13178
- }
13179
- .tiptap .mention::before {
13180
- content: "";
13181
- display: inline-block;
13182
- width: 12px;
13183
- height: 12px;
13184
- margin-right: 4px;
13185
- background-size: contain;
13186
- background-repeat: no-repeat;
13187
- opacity: 0.8;
13188
- filter: brightness(1.2);
13189
- }
13190
-
13191
- /* Type specific styles */
13192
- .tiptap .mention[data-mention-type="ancestry"] {
13193
- background: rgba(139, 92, 246, 0.15);
13194
- border-color: rgba(139, 92, 246, 0.3);
13195
- color: #c4b5fd;
13196
- }
13197
- .tiptap .mention[data-mention-type="quest"] {
13198
- background: rgba(16, 185, 129, 0.15);
13199
- border-color: rgba(16, 185, 129, 0.3);
13200
- color: #6ee7b7;
13201
- }
13202
- .tiptap .mention[data-mention-type="node"] {
13203
- background: rgba(59, 130, 246, 0.15);
13204
- border-color: rgba(59, 130, 246, 0.3);
13205
- color: #93c5fd;
13093
+ }, []);
13094
+ const getMentionContext = (0, import_react28.useCallback)(() => {
13095
+ const selection = window.getSelection();
13096
+ if (!selection || selection.rangeCount === 0) return null;
13097
+ const range = selection.getRangeAt(0);
13098
+ const node = range.startContainer;
13099
+ if (node.nodeType !== Node.TEXT_NODE) return null;
13100
+ const textBefore = node.textContent.substring(0, range.startOffset);
13101
+ const atIndex = textBefore.lastIndexOf("@");
13102
+ if (atIndex === -1) {
13103
+ dismissedTriggerRef.current = null;
13104
+ return null;
13105
+ }
13106
+ const charBeforeAt = atIndex > 0 ? textBefore[atIndex - 1] : null;
13107
+ if (charBeforeAt && !/\s/.test(charBeforeAt)) {
13108
+ return null;
13109
+ }
13110
+ if (dismissedTriggerRef.current && dismissedTriggerRef.current.node === node && dismissedTriggerRef.current.atIndex === atIndex) {
13111
+ return null;
13112
+ }
13113
+ const query = textBefore.substring(atIndex + 1);
13114
+ if (query.includes(" ") || query.includes(".") || query.includes(",") || query.length > 40) {
13115
+ return null;
13116
+ }
13117
+ const rect = range.getBoundingClientRect();
13118
+ return {
13119
+ query,
13120
+ position: { top: rect.bottom + 6, left: rect.left }
13121
+ };
13122
+ }, []);
13123
+ const handleInput = (0, import_react28.useCallback)(() => {
13124
+ if (!editorRef.current) return;
13125
+ const serialized = domToContent(editorRef.current);
13126
+ onChange(serialized);
13127
+ const text = editorRef.current.innerText.replace(/\u200B/g, "").trim();
13128
+ const hasMentions = !!editorRef.current.querySelector("[data-mention]");
13129
+ setIsEmpty(!text && !hasMentions);
13130
+ const ctx = getMentionContext();
13131
+ setMentionState(ctx);
13132
+ }, [onChange, getMentionContext]);
13133
+ const handleKeyDown = (0, import_react28.useCallback)(
13134
+ (e) => {
13135
+ if (mentionState && ["ArrowDown", "ArrowUp", "Enter", "Tab", "Escape"].includes(e.key)) {
13136
+ return;
13137
+ }
13138
+ if (e.key === "Enter") {
13139
+ e.preventDefault();
13140
+ const sel = window.getSelection();
13141
+ const range = sel.getRangeAt(0);
13142
+ const br = document.createElement("br");
13143
+ range.deleteContents();
13144
+ range.insertNode(br);
13145
+ const afterBr = br.nextSibling || (() => {
13146
+ const zws = document.createTextNode("\u200B");
13147
+ br.parentNode.insertBefore(zws, br.nextSibling);
13148
+ return zws;
13149
+ })();
13150
+ const newRange = document.createRange();
13151
+ newRange.setStartBefore(afterBr);
13152
+ newRange.collapse(true);
13153
+ sel.removeAllRanges();
13154
+ sel.addRange(newRange);
13155
+ handleInput();
13156
+ }
13157
+ },
13158
+ [mentionState, handleInput]
13159
+ );
13160
+ const insertMention = (0, import_react28.useCallback)(
13161
+ (item) => {
13162
+ if (!editorRef.current) return;
13163
+ const sel = window.getSelection();
13164
+ if (!sel || sel.rangeCount === 0) return;
13165
+ const range = sel.getRangeAt(0);
13166
+ const textNode = range.startContainer;
13167
+ if (textNode.nodeType !== Node.TEXT_NODE) {
13168
+ setMentionState(null);
13169
+ return;
13170
+ }
13171
+ const cursorPos = range.startOffset;
13172
+ const textContent = textNode.textContent;
13173
+ const atIndex = textContent.lastIndexOf("@", cursorPos - 1);
13174
+ if (atIndex === -1) {
13175
+ setMentionState(null);
13176
+ return;
13177
+ }
13178
+ const before = textContent.substring(0, atIndex);
13179
+ const after = textContent.substring(cursorPos);
13180
+ textNode.textContent = before;
13181
+ const parent = textNode.parentNode;
13182
+ const nextSib = textNode.nextSibling;
13183
+ const span = document.createElement("span");
13184
+ span.contentEditable = "false";
13185
+ span.dataset.mention = "true";
13186
+ span.dataset.id = item.id;
13187
+ span.dataset.name = item.searchLabel || item.name || "";
13188
+ span.dataset.mentionType = item.type;
13189
+ span.style.cssText = mentionStyle(item.type);
13190
+ span.title = "Clique para focar na cena";
13191
+ span.textContent = `@${item.searchLabel || item.name || ""}`;
13192
+ parent.insertBefore(span, nextSib);
13193
+ const trailing = document.createTextNode(after || "\u200B");
13194
+ parent.insertBefore(trailing, span.nextSibling);
13195
+ const newRange = document.createRange();
13196
+ newRange.setStart(trailing, after ? 0 : 1);
13197
+ newRange.collapse(true);
13198
+ sel.removeAllRanges();
13199
+ sel.addRange(newRange);
13200
+ const serialized = domToContent(editorRef.current);
13201
+ onChange(serialized);
13202
+ setMentionState(null);
13203
+ setIsEmpty(false);
13204
+ },
13205
+ [onChange]
13206
+ );
13207
+ const handlePaste = (0, import_react28.useCallback)(
13208
+ (e) => {
13209
+ e.preventDefault();
13210
+ const text = e.clipboardData.getData("text/plain");
13211
+ if (text) {
13212
+ document.execCommand("insertText", false, text);
13213
+ handleInput();
13214
+ }
13215
+ },
13216
+ [handleInput]
13217
+ );
13218
+ const handleClick = (0, import_react28.useCallback)(
13219
+ (e) => {
13220
+ const chip = e.target.closest("[data-mention='true']");
13221
+ if (chip && onMentionClick) {
13222
+ e.stopPropagation();
13223
+ const id = chip.dataset.id;
13224
+ const full = searchItems == null ? void 0 : searchItems.find((i) => String(i.id) === String(id));
13225
+ if (full) {
13226
+ onMentionClick(full);
13227
+ } else {
13228
+ onMentionClick({
13229
+ id,
13230
+ name: chip.dataset.name,
13231
+ type: chip.dataset.mentionType,
13232
+ searchLabel: chip.dataset.name
13233
+ });
13206
13234
  }
13207
- .tiptap .mention[data-mention-type="version"] {
13208
- background: rgba(245, 158, 11, 0.15);
13209
- border-color: rgba(245, 158, 11, 0.3);
13210
- color: #fcd34d;
13235
+ return;
13236
+ }
13237
+ },
13238
+ [onMentionClick, getMentionContext, searchItems]
13239
+ );
13240
+ const handleBlur = (0, import_react28.useCallback)(() => {
13241
+ setTimeout(() => setMentionState(null), 200);
13242
+ }, []);
13243
+ const handleMentionDismiss = (0, import_react28.useCallback)(() => {
13244
+ const selection = window.getSelection();
13245
+ if (selection && selection.rangeCount > 0) {
13246
+ const range = selection.getRangeAt(0);
13247
+ const node = range.startContainer;
13248
+ if (node.nodeType === Node.TEXT_NODE) {
13249
+ const textBefore = node.textContent.substring(0, range.startOffset);
13250
+ const atIndex = textBefore.lastIndexOf("@");
13251
+ if (atIndex !== -1) {
13252
+ dismissedTriggerRef.current = { node, atIndex };
13211
13253
  }
13254
+ }
13255
+ }
13256
+ setMentionState(null);
13257
+ }, []);
13258
+ return /* @__PURE__ */ import_react28.default.createElement("div", { className: "relative" }, isEmpty && !readOnly && /* @__PURE__ */ import_react28.default.createElement("div", { className: "absolute top-0 left-0 text-sm text-slate-600 pointer-events-none select-none" }, placeholder || "Escreva sobre este grupo\u2026 ou digite @ para mencionar"), /* @__PURE__ */ import_react28.default.createElement(
13259
+ "div",
13260
+ {
13261
+ ref: editorRef,
13262
+ contentEditable: readOnly ? "false" : "true",
13263
+ suppressContentEditableWarning: true,
13264
+ onInput: handleInput,
13265
+ onKeyDown: handleKeyDown,
13266
+ onPaste: handlePaste,
13267
+ onClick: handleClick,
13268
+ onBlur: handleBlur,
13269
+ className: `w-full min-h-[1.5rem] max-h-[250px] overflow-y-auto text-sm text-slate-200 focus:outline-none leading-relaxed ${!readOnly ? "cursor-text" : "cursor-default"}`,
13270
+ style: { overflowWrap: "break-word", wordBreak: "break-word" }
13271
+ }
13272
+ ), mentionState && !readOnly && /* @__PURE__ */ import_react28.default.createElement(
13273
+ MentionPopover,
13274
+ {
13275
+ query: mentionState.query,
13276
+ items: searchItems || [],
13277
+ position: mentionState.position,
13278
+ onSelect: insertMention,
13279
+ onClose: handleMentionDismiss
13280
+ }
13281
+ ));
13282
+ }
13212
13283
 
13213
- .tiptap:focus {
13214
- outline: none;
13215
- }
13216
-
13217
- .ProseMirror-placeholder:first-child::before {
13218
- content: attr(data-placeholder);
13219
- float: left;
13220
- color: #475569;
13221
- pointer-events: none;
13222
- height: 0;
13223
- font-style: italic;
13224
- }
13225
- `));
13284
+ // src/components/Boards.jsx
13285
+ var Tooltip = ({ text, children }) => {
13286
+ if (!text) return children;
13287
+ return /* @__PURE__ */ import_react29.default.createElement("div", { className: "relative group/tooltip" }, children, /* @__PURE__ */ import_react29.default.createElement("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-slate-900 border border-white/10 text-white text-[10px] rounded opacity-0 group-hover/tooltip:opacity-100 transition-opacity pointer-events-none whitespace-nowrap z-[3000] shadow-2xl" }, text));
13226
13288
  };
13227
- var MentionEditor_default = MentionEditor;
13228
-
13229
- // src/components/AncestryBoard.jsx
13230
- var GroupItem = ({
13231
- group,
13289
+ var BoardItem = ({
13290
+ item,
13232
13291
  index,
13292
+ prevItem,
13293
+ nextItem,
13233
13294
  onUpdate,
13234
13295
  onDelete,
13235
13296
  onAddSubgroup,
13236
13297
  onIndent,
13298
+ onIndentDown,
13237
13299
  onOutdent,
13238
- onPlayAncestry,
13239
- availableMentionItems,
13240
- canEdit
13300
+ onMoveUp,
13301
+ onMoveDown,
13302
+ searchItems,
13303
+ onMentionClick,
13304
+ onEnterFolder,
13305
+ canEdit,
13306
+ isAtRoot = false
13241
13307
  }) => {
13242
- const canIndent = index > 0;
13243
- return /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex flex-col gap-2 mb-3 pl-3 border-l border-white/10 relative group/item animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ import_react30.default.createElement(
13244
- "div",
13245
- {
13246
- className: "flex flex-col gap-2 py-2 px-3 transition-all duration-200 hover:bg-white/5 border-l-2 border-transparent hover:border-white/20"
13247
- },
13248
- /* @__PURE__ */ import_react30.default.createElement(
13249
- MentionEditor_default,
13250
- {
13251
- initialValue: group.text,
13252
- onChange: (newText) => {
13253
- if (canEdit) onUpdate(group.id, { ...group, text: newText });
13254
- },
13255
- onPlay: onPlayAncestry,
13256
- availableItems: availableMentionItems,
13257
- canEdit,
13258
- placeholder: "Escreva sobre este grupo... use @ para mencionar"
13259
- }
13260
- ),
13261
- canEdit && /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center justify-between pt-2 mt-1 border-t border-white/5 opacity-0 group-hover/item:opacity-100 transition-opacity" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react30.default.createElement(
13262
- "button",
13263
- {
13264
- onClick: () => onAddSubgroup(group.id),
13265
- className: "flex items-center gap-1.5 px-3 py-1.5 text-xs text-slate-500 hover:text-white hover:bg-white/10 rounded-md transition-colors",
13266
- title: "Criar Subgrupo"
13267
- },
13268
- /* @__PURE__ */ import_react30.default.createElement(import_fi23.FiPlus, { size: 14 }),
13269
- /* @__PURE__ */ import_react30.default.createElement("span", null, "Adicionar Subgrupo")
13270
- )), /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react30.default.createElement(
13271
- "button",
13272
- {
13273
- onClick: () => onIndent(group.id),
13274
- disabled: !canIndent,
13275
- className: `p-1.5 rounded transition-colors ${!canIndent ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`,
13276
- title: "Aninhar no grupo acima"
13277
- },
13278
- /* @__PURE__ */ import_react30.default.createElement(import_fi23.FiArrowRight, { size: 14 })
13279
- ), /* @__PURE__ */ import_react30.default.createElement(
13280
- "button",
13281
- {
13282
- onClick: () => onOutdent(group.id),
13283
- className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
13284
- title: "Desaninhar"
13285
- },
13286
- /* @__PURE__ */ import_react30.default.createElement(import_fi23.FiArrowLeft, { size: 14 })
13287
- ), /* @__PURE__ */ import_react30.default.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), /* @__PURE__ */ import_react30.default.createElement(
13288
- "button",
13308
+ var _a;
13309
+ const isFolder = item.type === "folder";
13310
+ const canMoveUp = index > 0;
13311
+ const canMoveDown = nextItem !== void 0;
13312
+ const isNested = onOutdent !== void 0;
13313
+ const getIndentUpTooltip = () => {
13314
+ if (!prevItem) return null;
13315
+ const typeLabel = prevItem.type === "folder" ? "pasta" : "grupo de texto";
13316
+ return isFolder ? prevItem.type === "folder" ? "Aninhar na pasta acima" : null : `Mover para dentro do ${typeLabel} acima`;
13317
+ };
13318
+ const getIndentDownTooltip = () => {
13319
+ if (!nextItem) return null;
13320
+ const typeLabel = nextItem.type === "folder" ? "pasta" : "grupo de texto";
13321
+ return isFolder ? nextItem.type === "folder" ? "Aninhar na pasta abaixo" : null : `Mover para dentro do ${typeLabel} abaixo`;
13322
+ };
13323
+ const showIndentUp = !!getIndentUpTooltip();
13324
+ const showIndentDown = !!getIndentDownTooltip();
13325
+ const handleContentChange = (content) => {
13326
+ onUpdate(item.id, { ...item, content });
13327
+ };
13328
+ const handleNameChange = (e) => {
13329
+ onUpdate(item.id, { ...item, name: e.target.value });
13330
+ };
13331
+ if (isFolder) {
13332
+ return /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex flex-col gap-2 mb-3 relative group/item animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ import_react29.default.createElement(
13333
+ "div",
13289
13334
  {
13290
- onClick: () => onDelete(group.id),
13291
- className: "p-1.5 text-slate-600 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors",
13292
- title: "Remover Grupo"
13335
+ onClick: () => onEnterFolder(item.id),
13336
+ className: "flex items-center gap-3 py-3 px-4 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 rounded-xl cursor-pointer transition-all duration-200 group/folder"
13293
13337
  },
13294
- /* @__PURE__ */ import_react30.default.createElement(import_fi23.FiTrash2, { size: 14 })
13295
- )))
13296
- ), group.children && group.children.length > 0 && /* @__PURE__ */ import_react30.default.createElement("div", { className: "ml-2" }, group.children.map((childGroup, idx) => /* @__PURE__ */ import_react30.default.createElement(
13297
- GroupItem,
13298
- {
13299
- key: childGroup.id,
13300
- index: idx,
13301
- group: childGroup,
13302
- siblings: group.children,
13303
- onUpdate,
13304
- onDelete,
13305
- onAddSubgroup,
13306
- onIndent,
13307
- onOutdent,
13308
- onPlayAncestry,
13309
- availableMentionItems,
13310
- canEdit
13311
- }
13312
- ))));
13313
- };
13314
- function AncestryBoard({
13315
- isOpen,
13316
- onClose,
13317
- onSelect,
13318
- availableAncestries = [],
13319
- availableNodes = [],
13320
- initialGroups = [],
13321
- onSave,
13322
- userRole
13323
- }) {
13324
- const [groups, setGroups] = (0, import_react30.useState)([]);
13325
- const [isLoaded, setIsLoaded] = (0, import_react30.useState)(false);
13326
- const [saveStatus, setSaveStatus] = (0, import_react30.useState)("idle");
13327
- const canEdit = (0, import_react30.useMemo)(() => {
13328
- return userRole !== "viewer";
13329
- }, [userRole]);
13330
- (0, import_react30.useEffect)(() => {
13331
- if (initialGroups && !isLoaded) {
13332
- setGroups(initialGroups);
13333
- setIsLoaded(true);
13334
- }
13335
- }, [initialGroups, isLoaded]);
13336
- const nodeNamesMap = (0, import_react30.useMemo)(() => {
13337
- const map = /* @__PURE__ */ new Map();
13338
- if (availableNodes && Array.isArray(availableNodes)) {
13339
- availableNodes.forEach((node) => {
13340
- if (node && node.id) {
13341
- map.set(String(node.id), node.name);
13338
+ /* @__PURE__ */ import_react29.default.createElement("div", { className: "w-10 h-10 rounded-lg bg-indigo-500/10 flex items-center justify-center text-indigo-400 group-hover/folder:scale-110 transition-transform" }, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiFolder, { size: 20 })),
13339
+ /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex-1 min-w-0" }, canEdit ? /* @__PURE__ */ import_react29.default.createElement(
13340
+ "input",
13341
+ {
13342
+ type: "text",
13343
+ value: item.name,
13344
+ onChange: handleNameChange,
13345
+ onClick: (e) => e.stopPropagation(),
13346
+ className: "bg-transparent border-none outline-none text-sm font-medium text-slate-200 w-full focus:text-white",
13347
+ placeholder: "Nome da pasta..."
13342
13348
  }
13343
- });
13344
- }
13345
- return map;
13346
- }, [availableNodes]);
13347
- const availableIds = (0, import_react30.useMemo)(() => {
13348
- return new Set(availableAncestries.map((a) => String(a.ancestry_id)));
13349
- }, [availableAncestries]);
13350
- const availableMentionItems = (0, import_react30.useMemo)(() => {
13351
- const ancestries = availableAncestries.map((a) => ({
13352
- id: String(a.ancestry_id),
13353
- ancestry_id: a.ancestry_id,
13354
- name: a.name,
13355
- description: a.description,
13356
- type: "ancestry",
13357
- ancestral_node: a.ancestral_node,
13358
- is_private: a.is_private
13359
- }));
13360
- const nodes = availableNodes.map((n) => {
13361
- let type = "node";
13362
- if (n.is_quest) type = "quest";
13363
- else if (n.is_version) type = "version";
13364
- return {
13365
- id: String(n.id),
13366
- name: n.name,
13367
- description: n.description,
13368
- type,
13369
- raw_node: n
13370
- };
13371
- });
13372
- return [...ancestries, ...nodes];
13373
- }, [availableAncestries, availableNodes]);
13374
- const sanitizeGroups = (groupList) => {
13375
- return groupList.map((g) => ({
13376
- id: g.id,
13377
- text: g.text || "",
13378
- children: sanitizeGroups(g.children || [])
13379
- }));
13380
- };
13381
- (0, import_react30.useEffect)(() => {
13382
- if (!isLoaded || !onSave) return;
13383
- const timeoutId = setTimeout(async () => {
13384
- setSaveStatus("saving");
13385
- const cleanData = sanitizeGroups(groups);
13386
- try {
13387
- await onSave(cleanData);
13388
- setSaveStatus("saved");
13389
- setTimeout(() => {
13390
- setSaveStatus("idle");
13391
- }, 3e3);
13392
- } catch (error) {
13393
- console.error("Erro ao salvar board:", error);
13394
- setSaveStatus("error");
13395
- }
13396
- }, 3e3);
13397
- return () => clearTimeout(timeoutId);
13398
- }, [groups, isLoaded, onSave]);
13399
- (0, import_react30.useEffect)(() => {
13400
- if (!isOpen) return;
13401
- const handleKeyDown = (e) => {
13402
- if (e.key === "Escape") {
13403
- onClose();
13404
- e.preventDefault();
13405
- e.stopPropagation();
13406
- }
13407
- };
13408
- window.addEventListener("keydown", handleKeyDown);
13409
- return () => window.removeEventListener("keydown", handleKeyDown);
13410
- }, [isOpen, onClose]);
13411
- const handleAddRootGroup = () => {
13412
- const newGroup = {
13413
- id: (0, import_uuid11.v4)(),
13414
- text: "",
13415
- children: []
13349
+ ) : /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-sm font-medium text-slate-200" }, item.name), /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-2 mt-0.5" }, /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-wider font-semibold" }, ((_a = item.children) == null ? void 0 : _a.length) || 0, " itens"))),
13350
+ /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-1.5 opacity-0 group-item/item:opacity-100 group-hover/folder:opacity-100 transition-opacity" }, canEdit && /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-0.5 bg-slate-900/80 backdrop-blur-sm p-1 rounded-lg border border-white/5 shadow-xl" }, /* @__PURE__ */ import_react29.default.createElement(Tooltip, { text: "Subir" }, /* @__PURE__ */ import_react29.default.createElement(
13351
+ "button",
13352
+ {
13353
+ onClick: (e) => {
13354
+ e.stopPropagation();
13355
+ onMoveUp(item.id);
13356
+ },
13357
+ disabled: !canMoveUp,
13358
+ className: `p-1.5 rounded transition-colors ${!canMoveUp ? "text-slate-800 cursor-not-allowed" : "text-slate-400 hover:text-white hover:bg-white/10"}`
13359
+ },
13360
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiArrowUp, { size: 14 })
13361
+ )), /* @__PURE__ */ import_react29.default.createElement(Tooltip, { text: "Descer" }, /* @__PURE__ */ import_react29.default.createElement(
13362
+ "button",
13363
+ {
13364
+ onClick: (e) => {
13365
+ e.stopPropagation();
13366
+ onMoveDown(item.id);
13367
+ },
13368
+ disabled: !canMoveDown,
13369
+ className: `p-1.5 rounded transition-colors ${!canMoveDown ? "text-slate-800 cursor-not-allowed" : "text-slate-400 hover:text-white hover:bg-white/10"}`
13370
+ },
13371
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiArrowDown, { size: 14 })
13372
+ )), /* @__PURE__ */ import_react29.default.createElement("div", { className: "w-px h-3 bg-white/10 mx-0.5" }), showIndentUp && /* @__PURE__ */ import_react29.default.createElement(Tooltip, { text: getIndentUpTooltip() }, /* @__PURE__ */ import_react29.default.createElement(
13373
+ "button",
13374
+ {
13375
+ onClick: (e) => {
13376
+ e.stopPropagation();
13377
+ onIndent(item.id);
13378
+ },
13379
+ className: "p-1.5 text-slate-400 hover:text-white hover:bg-white/10 rounded transition-colors"
13380
+ },
13381
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiArrowLeft, { size: 14 })
13382
+ )), showIndentDown && /* @__PURE__ */ import_react29.default.createElement(Tooltip, { text: getIndentDownTooltip() }, /* @__PURE__ */ import_react29.default.createElement(
13383
+ "button",
13384
+ {
13385
+ onClick: (e) => {
13386
+ e.stopPropagation();
13387
+ onIndentDown(item.id);
13388
+ },
13389
+ className: "p-1.5 text-slate-400 hover:text-white hover:bg-white/10 rounded transition-colors"
13390
+ },
13391
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiArrowRight, { size: 14 })
13392
+ )), !isAtRoot && /* @__PURE__ */ import_react29.default.createElement(Tooltip, { text: "Desaninhar / Recuar n\xEDvel" }, /* @__PURE__ */ import_react29.default.createElement(
13393
+ "button",
13394
+ {
13395
+ onClick: (e) => {
13396
+ e.stopPropagation();
13397
+ onOutdent(item.id);
13398
+ },
13399
+ disabled: !onOutdent,
13400
+ className: `p-1.5 rounded transition-colors ${!onOutdent ? "text-slate-800 cursor-not-allowed" : "text-slate-400 hover:text-white hover:bg-white/10"}`
13401
+ },
13402
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiCornerUpLeft, { size: 14 })
13403
+ )), /* @__PURE__ */ import_react29.default.createElement("div", { className: "w-px h-3 bg-white/10 mx-0.5" }), /* @__PURE__ */ import_react29.default.createElement(Tooltip, { text: "Remover Pasta" }, /* @__PURE__ */ import_react29.default.createElement(
13404
+ "button",
13405
+ {
13406
+ onClick: (e) => {
13407
+ e.stopPropagation();
13408
+ onDelete(item.id);
13409
+ },
13410
+ className: "p-1.5 text-slate-500 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors"
13411
+ },
13412
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiTrash2, { size: 14 })
13413
+ ))), /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiChevronRight, { className: "text-slate-500 group-hover/folder:translate-x-1 transition-transform" }))
13414
+ ));
13415
+ }
13416
+ return /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex flex-col gap-2 mb-3 pl-3 border-l border-white/10 relative group/item animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ import_react29.default.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex flex-col gap-2 py-2 px-3 transition-all duration-200 hover:bg-white/5 border-l-2 border-transparent hover:border-white/20" }, /* @__PURE__ */ import_react29.default.createElement(
13417
+ RichGroupEditor,
13418
+ {
13419
+ content: item.content,
13420
+ onChange: handleContentChange,
13421
+ onMentionClick,
13422
+ searchItems,
13423
+ readOnly: !canEdit,
13424
+ placeholder: "Escreva sobre este grupo de texto\u2026 ou @ para mencionar"
13425
+ }
13426
+ ), canEdit && /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center justify-between pt-2 mt-1 border-t border-white/5 opacity-40 group-hover/item:opacity-100 transition-opacity" }, /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react29.default.createElement(
13427
+ "button",
13428
+ {
13429
+ onClick: () => onAddSubgroup(item.id, "text_group"),
13430
+ className: "flex items-center gap-1 px-1.5 py-1 text-[10px] uppercase font-bold tracking-wider text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
13431
+ title: "Criar Subgrupo de Texto"
13432
+ },
13433
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiPlus, { size: 12 }),
13434
+ /* @__PURE__ */ import_react29.default.createElement("span", null, "Subgrupo")
13435
+ )), /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react29.default.createElement(Tooltip, { text: "Subir" }, /* @__PURE__ */ import_react29.default.createElement(
13436
+ "button",
13437
+ {
13438
+ onClick: () => onMoveUp(item.id),
13439
+ disabled: !canMoveUp,
13440
+ className: `p-1.5 rounded transition-colors ${!canMoveUp ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`
13441
+ },
13442
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiArrowUp, { size: 14 })
13443
+ )), /* @__PURE__ */ import_react29.default.createElement(Tooltip, { text: "Descer" }, /* @__PURE__ */ import_react29.default.createElement(
13444
+ "button",
13445
+ {
13446
+ onClick: () => onMoveDown(item.id),
13447
+ disabled: !canMoveDown,
13448
+ className: `p-1.5 rounded transition-colors ${!canMoveDown ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`
13449
+ },
13450
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiArrowDown, { size: 14 })
13451
+ )), /* @__PURE__ */ import_react29.default.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), showIndentUp && /* @__PURE__ */ import_react29.default.createElement(Tooltip, { text: getIndentUpTooltip() }, /* @__PURE__ */ import_react29.default.createElement(
13452
+ "button",
13453
+ {
13454
+ onClick: () => onIndent(item.id),
13455
+ className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors"
13456
+ },
13457
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiArrowLeft, { size: 14 })
13458
+ )), showIndentDown && /* @__PURE__ */ import_react29.default.createElement(Tooltip, { text: getIndentDownTooltip() }, /* @__PURE__ */ import_react29.default.createElement(
13459
+ "button",
13460
+ {
13461
+ onClick: () => onIndentDown(item.id),
13462
+ className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors"
13463
+ },
13464
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiArrowRight, { size: 14 })
13465
+ )), !isAtRoot && /* @__PURE__ */ import_react29.default.createElement(Tooltip, { text: "Desaninhar / Recuar n\xEDvel" }, /* @__PURE__ */ import_react29.default.createElement(
13466
+ "button",
13467
+ {
13468
+ onClick: () => onOutdent(item.id),
13469
+ disabled: !onOutdent,
13470
+ className: `p-1.5 rounded transition-colors ${!onOutdent ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`
13471
+ },
13472
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiCornerUpLeft, { size: 14 })
13473
+ )), /* @__PURE__ */ import_react29.default.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), /* @__PURE__ */ import_react29.default.createElement(Tooltip, { text: "Remover Grupo de Texto" }, /* @__PURE__ */ import_react29.default.createElement(
13474
+ "button",
13475
+ {
13476
+ onClick: () => onDelete(item.id),
13477
+ className: "p-1.5 text-slate-600 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors"
13478
+ },
13479
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiTrash2, { size: 14 })
13480
+ ))))), item.children && item.children.length > 0 && /* @__PURE__ */ import_react29.default.createElement("div", { className: "ml-2" }, item.children.map((child, idx) => /* @__PURE__ */ import_react29.default.createElement(
13481
+ BoardItem,
13482
+ {
13483
+ key: child.id,
13484
+ index: idx,
13485
+ prevItem: item.children[idx - 1],
13486
+ nextItem: item.children[idx + 1],
13487
+ item: child,
13488
+ onUpdate,
13489
+ onDelete,
13490
+ onAddSubgroup,
13491
+ onIndent,
13492
+ onIndentDown,
13493
+ onOutdent,
13494
+ onMoveUp,
13495
+ onMoveDown,
13496
+ searchItems,
13497
+ onMentionClick,
13498
+ onEnterFolder,
13499
+ canEdit,
13500
+ isAtRoot: false
13501
+ }
13502
+ ))));
13503
+ };
13504
+ function Boards({
13505
+ isOpen,
13506
+ onClose,
13507
+ onSelect,
13508
+ availableAncestries = [],
13509
+ availableNodes = [],
13510
+ initialBoardItems = [],
13511
+ onSave,
13512
+ userRole
13513
+ }) {
13514
+ const [boardItems, setBoardItems] = (0, import_react29.useState)([]);
13515
+ const [isLoaded, setIsLoaded] = (0, import_react29.useState)(false);
13516
+ const [saveStatus, setSaveStatus] = (0, import_react29.useState)("idle");
13517
+ const [navigationStack, setNavigationStack] = (0, import_react29.useState)([]);
13518
+ const lastSavedBoardItemsRef = (0, import_react29.useRef)(null);
13519
+ const canEdit = (0, import_react29.useMemo)(() => userRole !== "viewer", [userRole]);
13520
+ const sanitizeBoardItems = (list) => list.map((g) => ({
13521
+ id: g.id,
13522
+ type: g.type || "text_group",
13523
+ name: g.name || "",
13524
+ content: g.content || [],
13525
+ children: sanitizeBoardItems(g.children || [])
13526
+ }));
13527
+ (0, import_react29.useEffect)(() => {
13528
+ if (initialBoardItems && !isLoaded) {
13529
+ const hasLegacyTypes = (list) => list.some(
13530
+ (g) => g.type === "group" || hasLegacyTypes(g.children || [])
13531
+ );
13532
+ let preparedItems = [];
13533
+ if (hasLegacyTypes(initialBoardItems)) {
13534
+ console.warn("Legacy Boards detected. Resetting board to empty list.");
13535
+ preparedItems = [];
13536
+ } else {
13537
+ const prepare = (list) => list.map((g) => ({
13538
+ id: g.id || (0, import_uuid11.v4)(),
13539
+ type: g.type || "text_group",
13540
+ name: g.name || "",
13541
+ content: Array.isArray(g.content) ? g.content : [],
13542
+ children: prepare(g.children || [])
13543
+ }));
13544
+ preparedItems = prepare(initialBoardItems);
13545
+ }
13546
+ setBoardItems(preparedItems);
13547
+ lastSavedBoardItemsRef.current = JSON.stringify(
13548
+ sanitizeBoardItems(preparedItems)
13549
+ );
13550
+ setIsLoaded(true);
13551
+ }
13552
+ }, [initialBoardItems, isLoaded]);
13553
+ const allSearchableItems = (0, import_react29.useMemo)(() => {
13554
+ const items = [];
13555
+ availableAncestries.forEach((anc) => {
13556
+ items.push({
13557
+ ...anc,
13558
+ id: String(anc.ancestry_id),
13559
+ type: "ancestry",
13560
+ searchLabel: anc.name || "",
13561
+ searchDesc: anc.description || ""
13562
+ });
13563
+ });
13564
+ availableNodes.forEach((node) => {
13565
+ var _a;
13566
+ let type = "node";
13567
+ if (node.is_quest) type = "quest";
13568
+ else if ((_a = node.version_node) == null ? void 0 : _a.is_version) type = "version";
13569
+ items.push({
13570
+ ...node,
13571
+ id: String(node.id),
13572
+ type,
13573
+ searchLabel: node.name || node.raw_title || "",
13574
+ searchDesc: node.description || ""
13575
+ });
13576
+ });
13577
+ return items;
13578
+ }, [availableAncestries, availableNodes]);
13579
+ (0, import_react29.useEffect)(() => {
13580
+ if (!isLoaded || !onSave) return;
13581
+ const id = setTimeout(async () => {
13582
+ const currentSanitized = sanitizeBoardItems(boardItems);
13583
+ const currentJson = JSON.stringify(currentSanitized);
13584
+ if (currentJson === lastSavedBoardItemsRef.current) return;
13585
+ setSaveStatus("saving");
13586
+ try {
13587
+ await onSave(currentSanitized);
13588
+ lastSavedBoardItemsRef.current = currentJson;
13589
+ setSaveStatus("saved");
13590
+ setTimeout(() => setSaveStatus("idle"), 3e3);
13591
+ } catch (err) {
13592
+ console.error("Erro ao salvar board:", err);
13593
+ setSaveStatus("error");
13594
+ }
13595
+ }, 3e3);
13596
+ return () => clearTimeout(id);
13597
+ }, [boardItems, isLoaded, onSave]);
13598
+ const triggerInstantSave = async (updatedBoardItems) => {
13599
+ if (!onSave) return;
13600
+ const sanitized = sanitizeBoardItems(updatedBoardItems);
13601
+ const sanitizedJson = JSON.stringify(sanitized);
13602
+ lastSavedBoardItemsRef.current = sanitizedJson;
13603
+ setSaveStatus("saving");
13604
+ try {
13605
+ await onSave(sanitized);
13606
+ setSaveStatus("saved");
13607
+ setTimeout(() => setSaveStatus("idle"), 3e3);
13608
+ } catch (err) {
13609
+ console.error("Erro ao salvar board:", err);
13610
+ setSaveStatus("error");
13611
+ }
13612
+ };
13613
+ (0, import_react29.useEffect)(() => {
13614
+ if (!isOpen) return;
13615
+ const handler = (e) => {
13616
+ if (e.key === "Escape") {
13617
+ onClose();
13618
+ e.preventDefault();
13619
+ e.stopPropagation();
13620
+ }
13416
13621
  };
13417
- setGroups((prev) => [newGroup, ...prev]);
13622
+ window.addEventListener("keydown", handler);
13623
+ return () => window.removeEventListener("keydown", handler);
13624
+ }, [isOpen, onClose]);
13625
+ const currentPath = (0, import_react29.useMemo)(() => {
13626
+ const path = [];
13627
+ let currentLevel = boardItems;
13628
+ for (const folderId of navigationStack) {
13629
+ const folder = currentLevel.find((f) => f.id === folderId);
13630
+ if (folder) {
13631
+ path.push(folder);
13632
+ currentLevel = folder.children;
13633
+ }
13634
+ }
13635
+ return path;
13636
+ }, [boardItems, navigationStack]);
13637
+ const currentBoardItems = (0, import_react29.useMemo)(() => {
13638
+ const lastFolder = currentPath[currentPath.length - 1];
13639
+ return lastFolder ? lastFolder.children : boardItems;
13640
+ }, [boardItems, currentPath]);
13641
+ const navigateUp = () => {
13642
+ setNavigationStack((prev) => prev.slice(0, -1));
13643
+ };
13644
+ const navigateTo = (folderId) => {
13645
+ setNavigationStack((prev) => [...prev, folderId]);
13646
+ };
13647
+ const navigateToBreadcrumb = (index) => {
13648
+ setNavigationStack((prev) => prev.slice(0, index + 1));
13418
13649
  };
13419
- const handleAddSubgroup = (parentId) => {
13420
- const newGroup = {
13650
+ const navigateHome = () => {
13651
+ setNavigationStack([]);
13652
+ };
13653
+ const handleAddItem = (type = "text_group") => {
13654
+ const newItem = {
13421
13655
  id: (0, import_uuid11.v4)(),
13422
- text: "",
13656
+ type,
13657
+ name: type === "folder" ? "Nova Pasta" : "",
13658
+ content: [],
13423
13659
  children: []
13424
13660
  };
13425
- const addRecursive = (list) => {
13426
- return list.map((g) => {
13427
- if (g.id === parentId) {
13428
- return { ...g, children: [newGroup, ...g.children] };
13429
- }
13430
- return { ...g, children: addRecursive(g.children) };
13431
- });
13432
- };
13433
- setGroups((prev) => addRecursive(prev));
13661
+ if (navigationStack.length === 0) {
13662
+ setBoardItems((prev) => [...prev, newItem]);
13663
+ } else {
13664
+ const parentId = navigationStack[navigationStack.length - 1];
13665
+ const add = (list) => list.map(
13666
+ (g) => g.id === parentId ? { ...g, children: [...g.children, newItem] } : { ...g, children: add(g.children) }
13667
+ );
13668
+ setBoardItems((prev) => add(prev));
13669
+ }
13434
13670
  };
13435
- const handleUpdateGroup = (id, newData) => {
13436
- const updateRecursive = (list) => {
13437
- return list.map((g) => {
13438
- if (g.id === id) return newData;
13439
- return { ...g, children: updateRecursive(g.children) };
13440
- });
13441
- };
13442
- setGroups((prev) => updateRecursive(prev));
13671
+ const handleAddSubgroup = (parentId, type = "text_group") => {
13672
+ const add = (list) => list.map((g) => {
13673
+ if (g.id === parentId) {
13674
+ if (g.type === "text_group" && type === "folder") return g;
13675
+ const newItem = {
13676
+ id: (0, import_uuid11.v4)(),
13677
+ type,
13678
+ name: type === "folder" ? "Nova Pasta" : "",
13679
+ content: [],
13680
+ children: []
13681
+ };
13682
+ return { ...g, children: [...g.children, newItem] };
13683
+ }
13684
+ return { ...g, children: add(g.children) };
13685
+ });
13686
+ setBoardItems((prev) => add(prev));
13443
13687
  };
13444
- const handleDeleteGroup = (id) => {
13445
- const deleteRecursive = (list) => {
13446
- return list.filter((g) => g.id !== id).map((g) => ({
13447
- ...g,
13448
- children: deleteRecursive(g.children)
13449
- }));
13450
- };
13451
- setGroups((prev) => deleteRecursive(prev));
13688
+ const handleUpdateItem = (id, newData) => {
13689
+ const update = (list) => list.map(
13690
+ (g) => g.id === id ? newData : { ...g, children: update(g.children) }
13691
+ );
13692
+ setBoardItems((prev) => update(prev));
13693
+ };
13694
+ const handleDeleteItem = (id) => {
13695
+ const del = (list) => list.filter((g) => g.id !== id).map((g) => ({ ...g, children: del(g.children) }));
13696
+ setBoardItems((prev) => del(prev));
13452
13697
  };
13453
13698
  const findGroupAndParent = (list, targetId, parent = null) => {
13454
13699
  for (let i = 0; i < list.length; i++) {
13455
- if (list[i].id === targetId) {
13700
+ if (list[i].id === targetId)
13456
13701
  return { group: list[i], parent, index: i, siblings: list };
13457
- }
13458
13702
  if (list[i].children.length > 0) {
13459
13703
  const found = findGroupAndParent(list[i].children, targetId, list[i]);
13460
13704
  if (found) return found;
@@ -13463,113 +13707,169 @@ function AncestryBoard({
13463
13707
  return null;
13464
13708
  };
13465
13709
  const handleIndent = (id) => {
13466
- setGroups((prev) => {
13467
- const newList = JSON.parse(JSON.stringify(prev));
13468
- const found = findGroupAndParent(newList, id);
13469
- if (!found || found.index === 0) return prev;
13470
- const { group, siblings, index } = found;
13471
- const prevSibling = siblings[index - 1];
13472
- siblings.splice(index, 1);
13473
- prevSibling.children.push(group);
13474
- return newList;
13475
- });
13710
+ const next = JSON.parse(JSON.stringify(boardItems));
13711
+ const found = findGroupAndParent(next, id);
13712
+ if (!found || found.index === 0) return;
13713
+ const { group, siblings, index } = found;
13714
+ const targetParent = siblings[index - 1];
13715
+ if (group.type === "folder" && targetParent.type === "text_group") {
13716
+ return;
13717
+ }
13718
+ siblings.splice(index, 1);
13719
+ targetParent.children.push(group);
13720
+ setBoardItems(next);
13721
+ triggerInstantSave(next);
13476
13722
  };
13477
- const handleOutdent = (id) => {
13478
- setGroups((prev) => {
13479
- const newList = JSON.parse(JSON.stringify(prev));
13480
- const found = findGroupAndParent(newList, id);
13481
- if (!found || !found.parent) return prev;
13482
- const { group, siblings, index, parent } = found;
13483
- siblings.splice(index, 1);
13484
- const parentFound = findGroupAndParent(newList, parent.id);
13485
- if (parentFound) {
13486
- parentFound.siblings.splice(parentFound.index + 1, 0, group);
13487
- } else {
13488
- newList.push(group);
13489
- }
13490
- return newList;
13491
- });
13723
+ const handleIndentDown = (id) => {
13724
+ const next = JSON.parse(JSON.stringify(boardItems));
13725
+ const found = findGroupAndParent(next, id);
13726
+ if (!found || found.index === found.siblings.length - 1) return;
13727
+ const { group, siblings, index } = found;
13728
+ const targetParent = siblings[index + 1];
13729
+ if (group.type === "folder" && targetParent.type === "text_group") {
13730
+ return;
13731
+ }
13732
+ siblings.splice(index, 1);
13733
+ targetParent.children.push(group);
13734
+ setBoardItems(next);
13735
+ triggerInstantSave(next);
13492
13736
  };
13493
- const handlePlayFromGroup = (item) => {
13494
- let fullItem = item;
13495
- if (item.type === "ancestry" || !item.type) {
13496
- fullItem = availableAncestries.find(
13497
- (a) => String(a.ancestry_id) === String(item.id || item.ancestry_id)
13498
- ) || item;
13737
+ const handleOutdent = (id) => {
13738
+ const next = JSON.parse(JSON.stringify(boardItems));
13739
+ const found = findGroupAndParent(next, id);
13740
+ if (!found || !found.parent) return;
13741
+ const { group, siblings, index, parent } = found;
13742
+ siblings.splice(index, 1);
13743
+ const parentFound = findGroupAndParent(next, parent.id);
13744
+ if (parentFound) {
13745
+ parentFound.siblings.splice(parentFound.index + 1, 0, group);
13499
13746
  } else {
13500
- fullItem = availableNodes.find(
13501
- (n) => String(n.id) === String(item.id)
13502
- ) || item;
13503
- }
13504
- if (fullItem && onSelect) {
13505
- onSelect(fullItem);
13747
+ next.push(group);
13506
13748
  }
13749
+ setBoardItems(next);
13750
+ triggerInstantSave(next);
13751
+ };
13752
+ const handleMoveUp = (id) => {
13753
+ const next = JSON.parse(JSON.stringify(boardItems));
13754
+ const found = findGroupAndParent(next, id);
13755
+ if (!found || found.index === 0) return;
13756
+ const { siblings, index } = found;
13757
+ const item = siblings[index];
13758
+ siblings.splice(index, 1);
13759
+ siblings.splice(index - 1, 0, item);
13760
+ setBoardItems(next);
13761
+ triggerInstantSave(next);
13762
+ };
13763
+ const handleMoveDown = (id) => {
13764
+ const next = JSON.parse(JSON.stringify(boardItems));
13765
+ const found = findGroupAndParent(next, id);
13766
+ if (!found || found.index === found.siblings.length - 1) return;
13767
+ const { siblings, index } = found;
13768
+ const item = siblings[index];
13769
+ siblings.splice(index, 1);
13770
+ siblings.splice(index + 1, 0, item);
13771
+ setBoardItems(next);
13772
+ triggerInstantSave(next);
13773
+ };
13774
+ const handleMentionClick = (item) => {
13775
+ if (onSelect) onSelect(item);
13507
13776
  };
13508
13777
  if (!isOpen) return null;
13509
- return /* @__PURE__ */ import_react30.default.createElement(
13778
+ return /* @__PURE__ */ import_react29.default.createElement(
13510
13779
  "div",
13511
13780
  {
13512
13781
  className: "fixed inset-0 z-[2200] bg-black/80 backdrop-blur-sm flex items-center justify-center p-2",
13513
13782
  onClick: onClose
13514
13783
  },
13515
- /* @__PURE__ */ import_react30.default.createElement(
13784
+ /* @__PURE__ */ import_react29.default.createElement(
13516
13785
  "div",
13517
13786
  {
13518
- className: "bg-slate-950 border border-white/10 rounded-xl w-[90vw] max-w-[1200px] h-[90vh] flex flex-col shadow-2xl overflow-hidden animate-in fade-in zoom-in-95 duration-200",
13787
+ className: "bg-slate-950 border border-white/10 rounded-xl w-full max-w-[calc(100vw-32px)] h-[97vh] flex flex-col shadow-2xl overflow-hidden animate-in fade-in zoom-in-95 duration-200",
13519
13788
  onClick: (e) => e.stopPropagation()
13520
13789
  },
13521
- /* @__PURE__ */ import_react30.default.createElement("div", { className: "h-14 px-6 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ import_react30.default.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ import_react30.default.createElement(import_fi23.FiLayers, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-slate-900/50 border border-white/5" }, saveStatus === "saving" && /* @__PURE__ */ import_react30.default.createElement(import_react30.default.Fragment, null, /* @__PURE__ */ import_react30.default.createElement(
13790
+ /* @__PURE__ */ import_react29.default.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ import_react29.default.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiLayers, { className: "text-indigo-400" }), "Boards"), saveStatus !== "idle" && /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ import_react29.default.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-slate-900/50 border border-white/5" }, saveStatus === "saving" && /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement(
13522
13791
  import_fi23.FiLoader,
13523
13792
  {
13524
13793
  className: "animate-spin text-indigo-400",
13525
13794
  size: 12
13526
13795
  }
13527
- ), /* @__PURE__ */ import_react30.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ import_react30.default.createElement(import_react30.default.Fragment, null, /* @__PURE__ */ import_react30.default.createElement(import_fi23.FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ import_react30.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ import_react30.default.createElement(import_react30.default.Fragment, null, /* @__PURE__ */ import_react30.default.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ import_react30.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center gap-3" }, canEdit && /* @__PURE__ */ import_react30.default.createElement(
13796
+ ), /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-3" }, canEdit && /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement(
13528
13797
  "button",
13529
13798
  {
13530
- onClick: handleAddRootGroup,
13531
- className: "\n flex items-center gap-2 px-3 py-1.5 \n bg-white/5 hover:bg-white/10 \n border border-white/10 hover:border-white/20\n backdrop-blur-sm\n text-slate-200 hover:text-white\n rounded-md transition-all duration-200\n text-xs font-medium shadow-sm\n "
13799
+ onClick: () => handleAddItem("folder"),
13800
+ className: "flex items-center gap-2 px-3 py-1.5 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 backdrop-blur-sm text-slate-200 hover:text-white rounded-md transition-all duration-200 text-xs font-medium shadow-sm"
13532
13801
  },
13533
- /* @__PURE__ */ import_react30.default.createElement(import_fi23.FiPlus, { size: 14, className: "text-indigo-400" }),
13534
- /* @__PURE__ */ import_react30.default.createElement("span", { className: "hidden sm:inline" }, "Novo Grupo")
13535
- ), /* @__PURE__ */ import_react30.default.createElement(
13802
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiPlus, { size: 14, className: "text-amber-400" }),
13803
+ /* @__PURE__ */ import_react29.default.createElement("span", { className: "hidden sm:inline" }, "Pasta")
13804
+ ), /* @__PURE__ */ import_react29.default.createElement(
13805
+ "button",
13806
+ {
13807
+ onClick: () => handleAddItem("text_group"),
13808
+ className: "flex items-center gap-2 px-3 py-1.5 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 backdrop-blur-sm text-slate-200 hover:text-white rounded-md transition-all duration-200 text-xs font-medium shadow-sm"
13809
+ },
13810
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiPlus, { size: 14, className: "text-indigo-400" }),
13811
+ /* @__PURE__ */ import_react29.default.createElement("span", { className: "hidden sm:inline" }, "Texto")
13812
+ )), /* @__PURE__ */ import_react29.default.createElement(
13536
13813
  "button",
13537
13814
  {
13538
13815
  onClick: onClose,
13539
13816
  className: "w-8 h-8 rounded-lg flex items-center justify-center text-slate-400 hover:text-white hover:bg-white/10 transition-colors"
13540
13817
  },
13541
- "\xD7"
13818
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiX, { size: 16 })
13542
13819
  ))),
13543
- /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex flex-1 overflow-hidden bg-slate-900/20" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex flex-col flex-1" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-8 max-w-[900px] mx-auto w-full space-y-6" }, groups.length === 0 ? /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex flex-col items-center justify-center h-full text-slate-500 gap-4 border-2 border-dashed border-white/5 rounded-2xl bg-slate-900/40 p-12" }, /* @__PURE__ */ import_react30.default.createElement(import_fi23.FiLayers, { size: 40, className: "opacity-10 text-indigo-400" }), /* @__PURE__ */ import_react30.default.createElement("div", { className: "text-center space-y-1" }, /* @__PURE__ */ import_react30.default.createElement("p", { className: "text-sm font-medium text-slate-300" }, "Nenhum grupo criado"), /* @__PURE__ */ import_react30.default.createElement("p", { className: "text-xs text-slate-500 max-w-[200px]" }, canEdit ? "Comece organizando sua narrativa criando o primeiro grupo." : "Este board ainda n\xE3o possui conte\xFAdo.")), canEdit && /* @__PURE__ */ import_react30.default.createElement(
13820
+ /* @__PURE__ */ import_react29.default.createElement("div", { className: "h-10 px-6 bg-slate-900/40 border-b border-white/5 flex items-center gap-2 text-[10px] uppercase font-bold tracking-widest text-slate-500 overflow-x-auto no-scrollbar whitespace-nowrap" }, /* @__PURE__ */ import_react29.default.createElement(
13544
13821
  "button",
13545
13822
  {
13546
- onClick: handleAddRootGroup,
13547
- className: "mt-2 px-4 py-2 bg-indigo-500/10 hover:bg-indigo-500/20 text-indigo-400 rounded-lg border border-indigo-500/20 transition-all text-xs font-bold uppercase tracking-wider"
13823
+ onClick: navigateHome,
13824
+ className: `hover:text-white transition-colors flex items-center gap-1 ${navigationStack.length === 0 ? "text-indigo-400" : ""}`
13548
13825
  },
13549
- "Criar Primeiro Grupo"
13550
- )) : groups.map((group, index) => /* @__PURE__ */ import_react30.default.createElement(
13551
- GroupItem,
13826
+ "Home"
13827
+ ), currentPath.map((folder, idx) => /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, { key: folder.id }, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiChevronRight, { size: 10, className: "shrink-0" }), /* @__PURE__ */ import_react29.default.createElement(
13828
+ "button",
13829
+ {
13830
+ onClick: () => navigateToBreadcrumb(idx),
13831
+ className: `hover:text-white transition-colors ${idx === currentPath.length - 1 ? "text-indigo-400" : ""}`
13832
+ },
13833
+ folder.name || "Sem Nome"
13834
+ ))), navigationStack.length > 0 && /* @__PURE__ */ import_react29.default.createElement("div", { className: "ml-auto" }, /* @__PURE__ */ import_react29.default.createElement(
13835
+ "button",
13552
13836
  {
13553
- key: group.id,
13837
+ onClick: navigateUp,
13838
+ className: "flex items-center gap-1 px-2 py-1 rounded bg-white/5 hover:bg-white/10 text-slate-400 hover:text-white transition-all"
13839
+ },
13840
+ /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiArrowLeft, { size: 10 }),
13841
+ /* @__PURE__ */ import_react29.default.createElement("span", null, "Voltar")
13842
+ ))),
13843
+ /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex-1 overflow-y-auto overflow-x-hidden custom-scrollbar bg-slate-950/30 p-6 space-y-4" }, currentBoardItems.length === 0 ? /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex flex-col items-center justify-center h-full text-slate-500 gap-3 border-2 border-dashed border-white/5 rounded-xl bg-slate-900/20" }, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiLayers, { size: 24, className: "opacity-20" }), /* @__PURE__ */ import_react29.default.createElement("p", { className: "text-xs text-center px-4" }, canEdit ? /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, "Esta pasta est\xE1 vazia.", /* @__PURE__ */ import_react29.default.createElement("br", null), "Use os bot\xF5es acima para criar conte\xFAdo.") : /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, "Nenhum conte\xFAdo dispon\xEDvel nesta pasta."))) : currentBoardItems.map((item, index) => /* @__PURE__ */ import_react29.default.createElement(
13844
+ BoardItem,
13845
+ {
13846
+ key: item.id,
13554
13847
  index,
13555
- group,
13556
- onUpdate: handleUpdateGroup,
13557
- onDelete: handleDeleteGroup,
13848
+ prevItem: currentBoardItems[index - 1],
13849
+ nextItem: currentBoardItems[index + 1],
13850
+ item,
13851
+ onUpdate: handleUpdateItem,
13852
+ onDelete: handleDeleteItem,
13558
13853
  onAddSubgroup: handleAddSubgroup,
13559
13854
  onIndent: handleIndent,
13855
+ onIndentDown: handleIndentDown,
13560
13856
  onOutdent: handleOutdent,
13561
- onPlayAncestry: handlePlayFromGroup,
13562
- availableMentionItems,
13563
- canEdit
13857
+ onMoveUp: handleMoveUp,
13858
+ onMoveDown: handleMoveDown,
13859
+ searchItems: allSearchableItems,
13860
+ onMentionClick: handleMentionClick,
13861
+ onEnterFolder: navigateTo,
13862
+ canEdit,
13863
+ isAtRoot: navigationStack.length === 0
13564
13864
  }
13565
- ))))),
13566
- /* @__PURE__ */ import_react30.default.createElement("div", { className: "px-6 py-3 border-t border-white/10 bg-slate-950/50 text-[10px] uppercase tracking-widest font-bold text-slate-600 flex justify-between items-center flex-shrink-0" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ import_react30.default.createElement("span", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "w-1.5 h-1.5 rounded-full bg-indigo-500" }), " ", availableMentionItems.length, " RECURSOS DISPON\xCDVEIS"), /* @__PURE__ */ import_react30.default.createElement("span", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500" }), " ", groups.length, " GRUPOS PRINCIPAIS")), /* @__PURE__ */ import_react30.default.createElement("span", { className: "text-slate-700" }, "X.VIEW :: ANCESTRY BOARD ENGINE"))
13865
+ ))),
13866
+ /* @__PURE__ */ import_react29.default.createElement("div", { className: "px-5 py-2 border-t border-white/10 bg-slate-950/50 text-xs text-slate-500 flex justify-end flex-shrink-0" }, /* @__PURE__ */ import_react29.default.createElement("span", null, currentBoardItems.length, " itens nesta visualiza\xE7\xE3o"))
13567
13867
  )
13568
13868
  );
13569
13869
  }
13570
13870
 
13571
13871
  // src/components/MilestoneManager.jsx
13572
- var import_react31 = __toESM(require("react"));
13872
+ var import_react30 = __toESM(require("react"));
13573
13873
  var import_fi24 = require("react-icons/fi");
13574
13874
  function MilestoneManager({
13575
13875
  isOpen,
@@ -13578,7 +13878,7 @@ function MilestoneManager({
13578
13878
  onUpdateMilestones = () => {
13579
13879
  }
13580
13880
  }) {
13581
- const [newMilestoneName, setNewMilestoneName] = (0, import_react31.useState)("");
13881
+ const [newMilestoneName, setNewMilestoneName] = (0, import_react30.useState)("");
13582
13882
  const handleAddMilestone = () => {
13583
13883
  if (!newMilestoneName.trim()) return;
13584
13884
  const newMilestone = {
@@ -13603,27 +13903,27 @@ function MilestoneManager({
13603
13903
  };
13604
13904
  if (!isOpen) return null;
13605
13905
  const swallow = (e) => e.stopPropagation();
13606
- return /* @__PURE__ */ import_react31.default.createElement(
13906
+ return /* @__PURE__ */ import_react30.default.createElement(
13607
13907
  "div",
13608
13908
  {
13609
13909
  className: "fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/40 backdrop-blur-sm animate-in fade-in duration-300",
13610
13910
  onClick: onClose
13611
13911
  },
13612
- /* @__PURE__ */ import_react31.default.createElement(
13912
+ /* @__PURE__ */ import_react30.default.createElement(
13613
13913
  "div",
13614
13914
  {
13615
13915
  className: "w-full max-w-lg bg-slate-900/90 backdrop-blur-xl border border-white/10 rounded-2xl shadow-2xl flex flex-col max-h-[85vh] animate-in zoom-in-95 duration-300",
13616
13916
  onClick: swallow
13617
13917
  },
13618
- /* @__PURE__ */ import_react31.default.createElement("div", { className: "px-6 py-4 border-b border-white/10 flex items-center justify-between" }, /* @__PURE__ */ import_react31.default.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ import_react31.default.createElement("div", { className: "p-2 rounded-lg bg-indigo-500/20 text-indigo-400" }, /* @__PURE__ */ import_react31.default.createElement(import_fi24.FiTarget, { size: 20 })), /* @__PURE__ */ import_react31.default.createElement("div", null, /* @__PURE__ */ import_react31.default.createElement("h2", { className: "text-lg font-semibold text-slate-100" }, "Milestones"), /* @__PURE__ */ import_react31.default.createElement("p", { className: "text-xs text-slate-400" }, "Gerencie os marcos do seu cen\xE1rio"))), /* @__PURE__ */ import_react31.default.createElement(
13918
+ /* @__PURE__ */ import_react30.default.createElement("div", { className: "px-6 py-4 border-b border-white/10 flex items-center justify-between" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "p-2 rounded-lg bg-indigo-500/20 text-indigo-400" }, /* @__PURE__ */ import_react30.default.createElement(import_fi24.FiTarget, { size: 20 })), /* @__PURE__ */ import_react30.default.createElement("div", null, /* @__PURE__ */ import_react30.default.createElement("h2", { className: "text-lg font-semibold text-slate-100" }, "Milestones"), /* @__PURE__ */ import_react30.default.createElement("p", { className: "text-xs text-slate-400" }, "Gerencie os marcos do seu cen\xE1rio"))), /* @__PURE__ */ import_react30.default.createElement(
13619
13919
  "button",
13620
13920
  {
13621
13921
  onClick: onClose,
13622
13922
  className: "p-2 rounded-full hover:bg-white/10 text-slate-400 hover:text-white transition-colors"
13623
13923
  },
13624
- /* @__PURE__ */ import_react31.default.createElement(import_fi24.FiX, { size: 20 })
13924
+ /* @__PURE__ */ import_react30.default.createElement(import_fi24.FiX, { size: 20 })
13625
13925
  )),
13626
- /* @__PURE__ */ import_react31.default.createElement("div", { className: "px-6 py-5 bg-white/5 border-b border-white/5" }, /* @__PURE__ */ import_react31.default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ import_react31.default.createElement(
13926
+ /* @__PURE__ */ import_react30.default.createElement("div", { className: "px-6 py-5 bg-white/5 border-b border-white/5" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ import_react30.default.createElement(
13627
13927
  "input",
13628
13928
  {
13629
13929
  type: "text",
@@ -13633,47 +13933,47 @@ function MilestoneManager({
13633
13933
  placeholder: "Adicionar novo milestone...",
13634
13934
  className: "flex-1 bg-slate-800/50 border border-white/10 rounded-xl px-4 py-2.5 text-sm text-slate-100 placeholder:text-slate-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/40 focus:border-indigo-500/50 transition-all shadow-inner"
13635
13935
  }
13636
- ), /* @__PURE__ */ import_react31.default.createElement(
13936
+ ), /* @__PURE__ */ import_react30.default.createElement(
13637
13937
  "button",
13638
13938
  {
13639
13939
  onClick: handleAddMilestone,
13640
13940
  disabled: !newMilestoneName.trim(),
13641
13941
  className: "px-4 py-2.5 rounded-xl bg-indigo-600 hover:bg-indigo-500 disabled:opacity-30 disabled:cursor-not-allowed text-white text-sm font-medium transition-all flex items-center gap-2 shadow-lg shadow-indigo-500/20 active:scale-95"
13642
13942
  },
13643
- /* @__PURE__ */ import_react31.default.createElement(import_fi24.FiPlus, { size: 18 }),
13943
+ /* @__PURE__ */ import_react30.default.createElement(import_fi24.FiPlus, { size: 18 }),
13644
13944
  "Adicionar"
13645
13945
  ))),
13646
- /* @__PURE__ */ import_react31.default.createElement("div", { className: "flex-1 overflow-y-auto px-6 py-4 custom-scrollbar" }, milestones.length > 0 ? /* @__PURE__ */ import_react31.default.createElement("div", { className: "space-y-3" }, milestones.map((ms) => /* @__PURE__ */ import_react31.default.createElement(
13946
+ /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex-1 overflow-y-auto px-6 py-4 custom-scrollbar" }, milestones.length > 0 ? /* @__PURE__ */ import_react30.default.createElement("div", { className: "space-y-3" }, milestones.map((ms) => /* @__PURE__ */ import_react30.default.createElement(
13647
13947
  "div",
13648
13948
  {
13649
13949
  key: ms.id,
13650
13950
  className: `group flex items-center justify-between p-4 rounded-xl border transition-all duration-200 ${ms.status === "active" ? "bg-slate-800/40 border-indigo-500/20 hover:border-indigo-500/40" : "bg-slate-900/40 border-white/5 opacity-60 grayscale-[0.3]"}`
13651
13951
  },
13652
- /* @__PURE__ */ import_react31.default.createElement("div", { className: "flex items-center gap-3 min-w-0" }, /* @__PURE__ */ import_react31.default.createElement(
13952
+ /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center gap-3 min-w-0" }, /* @__PURE__ */ import_react30.default.createElement(
13653
13953
  "div",
13654
13954
  {
13655
13955
  className: `w-2 h-2 rounded-full shrink-0 ${ms.status === "active" ? "bg-indigo-400 shadow-[0_0_8px_rgba(129,140,248,0.6)]" : "bg-slate-600"}`
13656
13956
  }
13657
- ), /* @__PURE__ */ import_react31.default.createElement("div", { className: "flex flex-col min-w-0" }, /* @__PURE__ */ import_react31.default.createElement("span", { className: `text-sm font-medium truncate ${ms.status === "active" ? "text-slate-100" : "text-slate-400 line-through"}` }, ms.name), /* @__PURE__ */ import_react31.default.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-wider" }, ms.status === "active" ? "Ativo" : "Inativo"))),
13658
- /* @__PURE__ */ import_react31.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react31.default.createElement(
13957
+ ), /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex flex-col min-w-0" }, /* @__PURE__ */ import_react30.default.createElement("span", { className: `text-sm font-medium truncate ${ms.status === "active" ? "text-slate-100" : "text-slate-400 line-through"}` }, ms.name), /* @__PURE__ */ import_react30.default.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-wider" }, ms.status === "active" ? "Ativo" : "Inativo"))),
13958
+ /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react30.default.createElement(
13659
13959
  "button",
13660
13960
  {
13661
13961
  onClick: () => handleToggleMilestoneStatus(ms.id),
13662
13962
  className: `p-2 rounded-lg transition-all ${ms.status === "active" ? "hover:bg-slate-700 text-slate-400" : "hover:bg-indigo-500/20 text-indigo-400"}`,
13663
13963
  title: ms.status === "active" ? "Inativar" : "Ativar"
13664
13964
  },
13665
- ms.status === "active" ? /* @__PURE__ */ import_react31.default.createElement(import_fi24.FiPauseCircle, { size: 18 }) : /* @__PURE__ */ import_react31.default.createElement(import_fi24.FiPlayCircle, { size: 18 })
13666
- ), /* @__PURE__ */ import_react31.default.createElement(
13965
+ ms.status === "active" ? /* @__PURE__ */ import_react30.default.createElement(import_fi24.FiPauseCircle, { size: 18 }) : /* @__PURE__ */ import_react30.default.createElement(import_fi24.FiPlayCircle, { size: 18 })
13966
+ ), /* @__PURE__ */ import_react30.default.createElement(
13667
13967
  "button",
13668
13968
  {
13669
13969
  onClick: () => handleRemoveMilestone(ms.id),
13670
13970
  className: "p-2 rounded-lg hover:bg-red-500/20 text-slate-500 hover:text-red-400 transition-all",
13671
13971
  title: "Remover"
13672
13972
  },
13673
- /* @__PURE__ */ import_react31.default.createElement(import_fi24.FiTrash2, { size: 18 })
13973
+ /* @__PURE__ */ import_react30.default.createElement(import_fi24.FiTrash2, { size: 18 })
13674
13974
  ))
13675
- ))) : /* @__PURE__ */ import_react31.default.createElement("div", { className: "flex flex-col items-center justify-center py-12 text-center" }, /* @__PURE__ */ import_react31.default.createElement("div", { className: "p-4 rounded-full bg-slate-800/30 text-slate-600 mb-4" }, /* @__PURE__ */ import_react31.default.createElement(import_fi24.FiTarget, { size: 40 })), /* @__PURE__ */ import_react31.default.createElement("h3", { className: "text-slate-400 font-medium" }, "Nenhum milestone definido"), /* @__PURE__ */ import_react31.default.createElement("p", { className: "text-xs text-slate-600 max-w-[200px] mt-1 italic" }, "Defina marcos importantes para orientar o progresso do projeto."))),
13676
- /* @__PURE__ */ import_react31.default.createElement("div", { className: "px-6 py-4 border-t border-white/5 bg-slate-900/50 flex items-center justify-between rounded-b-2xl" }, /* @__PURE__ */ import_react31.default.createElement("div", { className: "flex gap-4" }, /* @__PURE__ */ import_react31.default.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ import_react31.default.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-widest" }, "Total:"), /* @__PURE__ */ import_react31.default.createElement("span", { className: "text-xs font-bold text-slate-300" }, milestones.length)), /* @__PURE__ */ import_react31.default.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ import_react31.default.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-widest" }, "Ativos:"), /* @__PURE__ */ import_react31.default.createElement("span", { className: "text-xs font-bold text-indigo-400" }, milestones.filter((m) => m.status === "active").length))))
13975
+ ))) : /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex flex-col items-center justify-center py-12 text-center" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "p-4 rounded-full bg-slate-800/30 text-slate-600 mb-4" }, /* @__PURE__ */ import_react30.default.createElement(import_fi24.FiTarget, { size: 40 })), /* @__PURE__ */ import_react30.default.createElement("h3", { className: "text-slate-400 font-medium" }, "Nenhum milestone definido"), /* @__PURE__ */ import_react30.default.createElement("p", { className: "text-xs text-slate-600 max-w-[200px] mt-1 italic" }, "Defina marcos importantes para orientar o progresso do projeto."))),
13976
+ /* @__PURE__ */ import_react30.default.createElement("div", { className: "px-6 py-4 border-t border-white/5 bg-slate-900/50 flex items-center justify-between rounded-b-2xl" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex gap-4" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ import_react30.default.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-widest" }, "Total:"), /* @__PURE__ */ import_react30.default.createElement("span", { className: "text-xs font-bold text-slate-300" }, milestones.length)), /* @__PURE__ */ import_react30.default.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ import_react30.default.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-widest" }, "Ativos:"), /* @__PURE__ */ import_react30.default.createElement("span", { className: "text-xs font-bold text-indigo-400" }, milestones.filter((m) => m.status === "active").length))))
13677
13977
  )
13678
13978
  );
13679
13979
  }
@@ -13752,20 +14052,20 @@ function XViewScene({
13752
14052
  get_ancestry_file,
13753
14053
  import_parent_file_modal_get,
13754
14054
  add_new_parent_file_to_scene_at_firebase_action,
13755
- get_ancestry_board_action,
13756
- save_ancestry_board_action,
14055
+ get_boards_action,
14056
+ save_boards_action,
13757
14057
  upload_file_action,
13758
14058
  delete_file_action,
13759
14059
  check_user_permission,
13760
14060
  get_view_members
13761
14061
  }) {
13762
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
13763
- const { data: session, status } = (0, import_react33.useSession)();
14062
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
14063
+ const { data: session, status } = (0, import_react32.useSession)();
13764
14064
  const router = (0, import_navigation.useRouter)();
13765
14065
  const searchParams = (0, import_navigation.useSearchParams)();
13766
14066
  const focusNodeId = searchParams == null ? void 0 : searchParams.get("focus");
13767
14067
  const focusAncestryId = searchParams == null ? void 0 : searchParams.get("ancestry");
13768
- const viewParams = (0, import_react32.useMemo)(() => {
14068
+ const viewParams = (0, import_react31.useMemo)(() => {
13769
14069
  if (encryptedConfig) {
13770
14070
  const data = decryptData(encryptedConfig);
13771
14071
  if (data) {
@@ -13774,7 +14074,7 @@ function XViewScene({
13774
14074
  }
13775
14075
  return null;
13776
14076
  }, [encryptedConfig, session]);
13777
- (0, import_react32.useEffect)(() => {
14077
+ (0, import_react31.useEffect)(() => {
13778
14078
  async function verifyPermission() {
13779
14079
  if (!viewParams || !session || !check_user_permission) return;
13780
14080
  const { id, type, owner_id } = viewParams;
@@ -13811,65 +14111,65 @@ function XViewScene({
13811
14111
  setIsLoading(false);
13812
14112
  }
13813
14113
  }, [viewParams, session, status, check_user_permission]);
13814
- const sceneConfigId = (0, import_react32.useMemo)(() => (viewParams == null ? void 0 : viewParams.id) || null, [viewParams]);
13815
- const ownerId = (0, import_react32.useMemo)(() => (viewParams == null ? void 0 : viewParams.owner_id) || null, [viewParams]);
13816
- const dbSaveUrl = (0, import_react32.useMemo)(() => {
14114
+ const sceneConfigId = (0, import_react31.useMemo)(() => (viewParams == null ? void 0 : viewParams.id) || null, [viewParams]);
14115
+ const ownerId = (0, import_react31.useMemo)(() => (viewParams == null ? void 0 : viewParams.owner_id) || null, [viewParams]);
14116
+ const dbSaveUrl = (0, import_react31.useMemo)(() => {
13817
14117
  if (ownerId && sceneConfigId) {
13818
14118
  return `x_view_dbs/${ownerId}/${sceneConfigId}`;
13819
14119
  }
13820
14120
  return null;
13821
14121
  }, [ownerId, sceneConfigId]);
13822
- const sceneSaveUrl = (0, import_react32.useMemo)(() => {
14122
+ const sceneSaveUrl = (0, import_react31.useMemo)(() => {
13823
14123
  if (ownerId && sceneConfigId) {
13824
14124
  return `x_view_scenes/${ownerId}/${sceneConfigId}`;
13825
14125
  }
13826
14126
  return null;
13827
14127
  }, [ownerId, sceneConfigId]);
13828
- const ancestry_save_url = (0, import_react32.useMemo)(() => {
14128
+ const ancestry_save_url = (0, import_react31.useMemo)(() => {
13829
14129
  if (ownerId && sceneConfigId) {
13830
14130
  return `x_view_ancestry/${ownerId}/${sceneConfigId}`;
13831
14131
  }
13832
14132
  return null;
13833
14133
  }, [ownerId, sceneConfigId]);
13834
- const sceneDataRef = (0, import_react32.useRef)(null);
13835
- const parentDataRef = (0, import_react32.useRef)(null);
13836
- const ancestryDataRef = (0, import_react32.useRef)(null);
13837
- const [isLoading, setIsLoading] = (0, import_react32.useState)(true);
13838
- const [permissionStatus, setPermissionStatus] = (0, import_react32.useState)("loading");
13839
- const [userPermissionRole, setUserPermissionRole] = (0, import_react32.useState)(null);
13840
- const [viewMembers, setViewMembers] = (0, import_react32.useState)([]);
13841
- const [isInitialized, setIsInitialized] = (0, import_react32.useState)(false);
13842
- const [sceneVersion, setSceneVersion] = (0, import_react32.useState)(0);
13843
- const [contextMenu, setContextMenu] = (0, import_react32.useState)({
14134
+ const sceneDataRef = (0, import_react31.useRef)(null);
14135
+ const parentDataRef = (0, import_react31.useRef)(null);
14136
+ const ancestryDataRef = (0, import_react31.useRef)(null);
14137
+ const [isLoading, setIsLoading] = (0, import_react31.useState)(true);
14138
+ const [permissionStatus, setPermissionStatus] = (0, import_react31.useState)("loading");
14139
+ const [userPermissionRole, setUserPermissionRole] = (0, import_react31.useState)(null);
14140
+ const [viewMembers, setViewMembers] = (0, import_react31.useState)([]);
14141
+ const [isInitialized, setIsInitialized] = (0, import_react31.useState)(false);
14142
+ const [sceneVersion, setSceneVersion] = (0, import_react31.useState)(0);
14143
+ const [contextMenu, setContextMenu] = (0, import_react31.useState)({
13844
14144
  visible: false,
13845
14145
  x: 0,
13846
14146
  y: 0,
13847
14147
  nodeData: null
13848
14148
  });
13849
- const [multiContextMenu, setMultiContextMenu] = (0, import_react32.useState)({
14149
+ const [multiContextMenu, setMultiContextMenu] = (0, import_react31.useState)({
13850
14150
  visible: false,
13851
14151
  x: 0,
13852
14152
  y: 0,
13853
14153
  nodeIds: null
13854
14154
  });
13855
- const [relationshipMenu, setRelationshipMenu] = (0, import_react32.useState)({
14155
+ const [relationshipMenu, setRelationshipMenu] = (0, import_react31.useState)({
13856
14156
  visible: false,
13857
14157
  x: 0,
13858
14158
  y: 0,
13859
14159
  linkObject: null
13860
14160
  });
13861
- const [creationMode, setCreationMode] = (0, import_react32.useState)({
14161
+ const [creationMode, setCreationMode] = (0, import_react31.useState)({
13862
14162
  isActive: false,
13863
14163
  sourceNodeData: null
13864
14164
  });
13865
- const [versionMode, setVersionMode] = (0, import_react32.useState)({
14165
+ const [versionMode, setVersionMode] = (0, import_react31.useState)({
13866
14166
  isActive: false,
13867
14167
  sourceNodeData: null
13868
14168
  });
13869
- const [questMode, setQuestMode] = (0, import_react32.useState)({ isActive: false });
13870
- const [hasFocusedInitial, setHasFocusedInitial] = (0, import_react32.useState)(false);
13871
- const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = (0, import_react32.useState)(false);
13872
- const [ancestryMode, setAncestryMode] = (0, import_react32.useState)({
14169
+ const [questMode, setQuestMode] = (0, import_react31.useState)({ isActive: false });
14170
+ const [hasFocusedInitial, setHasFocusedInitial] = (0, import_react31.useState)(false);
14171
+ const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = (0, import_react31.useState)(false);
14172
+ const [ancestryMode, setAncestryMode] = (0, import_react31.useState)({
13873
14173
  isActive: false,
13874
14174
  tree: null,
13875
14175
  selectedParentId: null,
@@ -13880,42 +14180,42 @@ function XViewScene({
13880
14180
  ancestryDescriptionSections: [],
13881
14181
  isAddingNodes: false
13882
14182
  });
13883
- const [readingMode, setReadingMode] = (0, import_react32.useState)({
14183
+ const [readingMode, setReadingMode] = (0, import_react31.useState)({
13884
14184
  isActive: false,
13885
14185
  ancestry: null,
13886
14186
  branchStack: [],
13887
14187
  autoAbstraction: false
13888
14188
  });
13889
- const [formPosition, setFormPosition] = (0, import_react32.useState)({
14189
+ const [formPosition, setFormPosition] = (0, import_react31.useState)({
13890
14190
  left: 16,
13891
14191
  top: 16,
13892
14192
  opacity: 0
13893
14193
  });
13894
- const [detailsNode, setDetailsNode] = (0, import_react32.useState)(null);
13895
- const [detailsLink, setDetailsLink] = (0, import_react32.useState)(null);
13896
- const [ancestryLinkDetails, setAncestryLinkDetails] = (0, import_react32.useState)(null);
13897
- const [imageViewer, setImageViewer] = (0, import_react32.useState)({
14194
+ const [detailsNode, setDetailsNode] = (0, import_react31.useState)(null);
14195
+ const [detailsLink, setDetailsLink] = (0, import_react31.useState)(null);
14196
+ const [ancestryLinkDetails, setAncestryLinkDetails] = (0, import_react31.useState)(null);
14197
+ const [imageViewer, setImageViewer] = (0, import_react31.useState)({
13898
14198
  visible: false,
13899
14199
  images: [],
13900
14200
  startIndex: 0
13901
14201
  });
13902
- const [editingAncestryRel, setEditingAncestryRel] = (0, import_react32.useState)({
14202
+ const [editingAncestryRel, setEditingAncestryRel] = (0, import_react31.useState)({
13903
14203
  visible: false,
13904
14204
  data: null,
13905
14205
  path: null
13906
14206
  });
13907
- const [isImportModalOpen, setIsImportModalOpen] = (0, import_react32.useState)(false);
13908
- const [importSuccessMessage, setImportSuccessMessage] = (0, import_react32.useState)("");
13909
- const [invalidTargetError, setInvalidTargetError] = (0, import_react32.useState)(null);
13910
- const [highlightedNodeId, setHighlightedNodeId] = (0, import_react32.useState)(null);
13911
- const [isAncestryBoardOpen, setIsAncestryBoardOpen] = (0, import_react32.useState)(false);
13912
- const [ancestryBoardData, setAncestryBoardData] = (0, import_react32.useState)([]);
13913
- const [isSidebarOpen, setIsSidebarOpen] = (0, import_react32.useState)(false);
13914
- const [isMilestoneManagerOpen, setIsMilestoneManagerOpen] = (0, import_react32.useState)(false);
13915
- const mountRef = (0, import_react32.useRef)(null);
13916
- const tooltipRef = (0, import_react32.useRef)(null);
13917
- const formRef = (0, import_react32.useRef)(null);
13918
- const stateRef = (0, import_react32.useRef)({
14207
+ const [isImportModalOpen, setIsImportModalOpen] = (0, import_react31.useState)(false);
14208
+ const [importSuccessMessage, setImportSuccessMessage] = (0, import_react31.useState)("");
14209
+ const [invalidTargetError, setInvalidTargetError] = (0, import_react31.useState)(null);
14210
+ const [highlightedNodeId, setHighlightedNodeId] = (0, import_react31.useState)(null);
14211
+ const [isBoardsOpen, setIsBoardsOpen] = (0, import_react31.useState)(false);
14212
+ const [boardItems, setBoardItems] = (0, import_react31.useState)([]);
14213
+ const [isSidebarOpen, setIsSidebarOpen] = (0, import_react31.useState)(false);
14214
+ const [isMilestoneManagerOpen, setIsMilestoneManagerOpen] = (0, import_react31.useState)(false);
14215
+ const mountRef = (0, import_react31.useRef)(null);
14216
+ const tooltipRef = (0, import_react31.useRef)(null);
14217
+ const formRef = (0, import_react31.useRef)(null);
14218
+ const stateRef = (0, import_react31.useRef)({
13919
14219
  readMode: {
13920
14220
  currentMaxIndex: 0,
13921
14221
  progressMap: {}
@@ -13979,10 +14279,10 @@ function XViewScene({
13979
14279
  minWidth: 320,
13980
14280
  maxWidth: maxReadPanelW
13981
14281
  });
13982
- (0, import_react32.useEffect)(() => {
14282
+ (0, import_react31.useEffect)(() => {
13983
14283
  stateRef.current.ancestry = ancestryMode;
13984
14284
  }, [ancestryMode]);
13985
- (0, import_react32.useEffect)(() => {
14285
+ (0, import_react31.useEffect)(() => {
13986
14286
  var _a2;
13987
14287
  if (!isInitialized) return;
13988
14288
  const map = /* @__PURE__ */ new Map();
@@ -14005,10 +14305,10 @@ function XViewScene({
14005
14305
  }
14006
14306
  stateRef.current.nodeIdToParentFileMap = map;
14007
14307
  }, [isInitialized, sceneVersion]);
14008
- const handleNavigateBack = (0, import_react32.useCallback)(() => {
14308
+ const handleNavigateBack = (0, import_react31.useCallback)(() => {
14009
14309
  router.push("/dashboard/scenes");
14010
14310
  }, [router]);
14011
- const handleConfirmImport = (0, import_react32.useCallback)(
14311
+ const handleConfirmImport = (0, import_react31.useCallback)(
14012
14312
  async (importPayload) => {
14013
14313
  var _a2, _b2;
14014
14314
  let files = [];
@@ -14136,7 +14436,7 @@ function XViewScene({
14136
14436
  const handleOpenImageViewer = (images, startIndex) => {
14137
14437
  setImageViewer({ visible: true, images, startIndex });
14138
14438
  };
14139
- const tweenToTarget = (0, import_react32.useCallback)(
14439
+ const tweenToTarget = (0, import_react31.useCallback)(
14140
14440
  (target, zoomFactor = 1, forcedDirection = null) => {
14141
14441
  const { camera, controls, tweenGroup } = stateRef.current;
14142
14442
  if (!camera || !controls || !tweenGroup) return;
@@ -14162,7 +14462,7 @@ function XViewScene({
14162
14462
  if (!t || typeof t.closest !== "function") return false;
14163
14463
  return !!t.closest(".ui-overlay");
14164
14464
  };
14165
- const buildFullAncestryTree = (0, import_react32.useCallback)(
14465
+ const buildFullAncestryTree = (0, import_react31.useCallback)(
14166
14466
  (idTree, nodes, ancestries = []) => {
14167
14467
  if (!idTree) return null;
14168
14468
  const nodeMap = new Map(nodes.map((n) => [String(n.id), n]));
@@ -14247,7 +14547,7 @@ function XViewScene({
14247
14547
  },
14248
14548
  []
14249
14549
  );
14250
- const handleActivateTimeline = (0, import_react32.useCallback)(() => {
14550
+ const handleActivateTimeline = (0, import_react31.useCallback)(() => {
14251
14551
  const { nodeObjects, tweenGroup, timelineIntervalsGroup } = stateRef.current;
14252
14552
  if (!nodeObjects || !tweenGroup || !timelineIntervalsGroup) return;
14253
14553
  while (timelineIntervalsGroup.children.length > 0) {
@@ -14407,7 +14707,7 @@ function XViewScene({
14407
14707
  }
14408
14708
  });
14409
14709
  }, []);
14410
- const handleVersionTimeline = (0, import_react32.useCallback)((sourceMesh, versionMeshes) => {
14710
+ const handleVersionTimeline = (0, import_react31.useCallback)((sourceMesh, versionMeshes) => {
14411
14711
  const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
14412
14712
  if (!tweenGroup || !timelineIntervalsGroup || versionMeshes.length === 0)
14413
14713
  return;
@@ -14544,7 +14844,7 @@ function XViewScene({
14544
14844
  }
14545
14845
  });
14546
14846
  }, []);
14547
- (0, import_react32.useEffect)(() => {
14847
+ (0, import_react31.useEffect)(() => {
14548
14848
  async function fetchAllData(configPath, ownerId2) {
14549
14849
  var _a2, _b2;
14550
14850
  if (!get_scene_view_data) {
@@ -14563,7 +14863,7 @@ function XViewScene({
14563
14863
  focusNodeId,
14564
14864
  focusAncestryId
14565
14865
  );
14566
- const boardPromise = get_ancestry_board_action && session ? get_ancestry_board_action(configPath, sceneType, session, ownerId2) : Promise.resolve({ success: false, data: [] });
14866
+ const boardPromise = get_boards_action && session ? get_boards_action(configPath, sceneType, session, ownerId2) : Promise.resolve({ success: false, data: [] });
14567
14867
  const [sceneResponse, boardResponse] = await Promise.all([
14568
14868
  scenePromise,
14569
14869
  boardPromise
@@ -14614,10 +14914,10 @@ function XViewScene({
14614
14914
  (sceneResponse == null ? void 0 : sceneResponse.error) || "Resposta inv\xE1lida."
14615
14915
  );
14616
14916
  }
14617
- if (boardResponse == null ? void 0 : boardResponse.success) {
14618
- setAncestryBoardData(boardResponse.data);
14917
+ if (boardResponse.success && boardResponse.data) {
14918
+ setBoardItems(boardResponse.data);
14619
14919
  } else {
14620
- setAncestryBoardData([]);
14920
+ setBoardItems([]);
14621
14921
  }
14622
14922
  } catch (error) {
14623
14923
  console.error("Erro cr\xEDtico no carregamento inicial:", error);
@@ -14643,18 +14943,18 @@ function XViewScene({
14643
14943
  ownerId,
14644
14944
  viewParams,
14645
14945
  get_scene_view_data,
14646
- get_ancestry_board_action,
14946
+ get_boards_action,
14647
14947
  isInitialized,
14648
14948
  permissionStatus,
14649
14949
  focusNodeId,
14650
14950
  focusAncestryId
14651
14951
  ]);
14652
- const isNodeInView = (0, import_react32.useCallback)((id) => {
14952
+ const isNodeInView = (0, import_react31.useCallback)((id) => {
14653
14953
  const key = String(id);
14654
14954
  const objs = stateRef.current.nodeObjects || {};
14655
14955
  return !!objs[key];
14656
14956
  }, []);
14657
- const addOrUpdateNodeMesh = (0, import_react32.useCallback)(
14957
+ const addOrUpdateNodeMesh = (0, import_react31.useCallback)(
14658
14958
  (nodeData, position, suppressVersionUpdate = false, forceShowQuestDate = false) => {
14659
14959
  const {
14660
14960
  graphGroup,
@@ -14695,7 +14995,7 @@ function XViewScene({
14695
14995
  },
14696
14996
  []
14697
14997
  );
14698
- const handleActivateQuestLens = (0, import_react32.useCallback)(({ assigneeId, status: status2, milestoneId }) => {
14998
+ const handleActivateQuestLens = (0, import_react31.useCallback)(({ assigneeId, status: status2, milestoneId }) => {
14699
14999
  var _a2;
14700
15000
  const { nodeObjects, tweenGroup, graphGroup } = stateRef.current;
14701
15001
  if (!nodeObjects || !tweenGroup || !graphGroup) return;
@@ -14909,7 +15209,7 @@ function XViewScene({
14909
15209
  camTween.start();
14910
15210
  }
14911
15211
  }, [addOrUpdateNodeMesh]);
14912
- (0, import_react32.useEffect)(() => {
15212
+ (0, import_react31.useEffect)(() => {
14913
15213
  if (!isInitialized || !sceneDataRef.current) return;
14914
15214
  const currentMount = mountRef.current;
14915
15215
  if (!currentMount) return;
@@ -15665,7 +15965,7 @@ function XViewScene({
15665
15965
  get_scene_view_data,
15666
15966
  save_view_data
15667
15967
  ]);
15668
- const handleGhostNodeImageChange = (0, import_react32.useCallback)((useImage, imageUrl) => {
15968
+ const handleGhostNodeImageChange = (0, import_react31.useCallback)((useImage, imageUrl) => {
15669
15969
  const {
15670
15970
  node: ghostNode,
15671
15971
  line: ghostLine,
@@ -15713,7 +16013,7 @@ function XViewScene({
15713
16013
  aura: newGhostNode.getObjectByName("aura")
15714
16014
  };
15715
16015
  }, []);
15716
- const handleGhostNodeIntensityChange = (0, import_react32.useCallback)((newIntensity) => {
16016
+ const handleGhostNodeIntensityChange = (0, import_react31.useCallback)((newIntensity) => {
15717
16017
  const { node: ghostNode, aura: ghostAura } = stateRef.current.ghostElements;
15718
16018
  if (!ghostNode) return;
15719
16019
  const adjustedIntensity = newIntensity + MIN_VISIBILITY_INTENSITY;
@@ -15734,7 +16034,7 @@ function XViewScene({
15734
16034
  ghostAura.material.opacity = Math.min(0.8, newIntensity * 0.15);
15735
16035
  }
15736
16036
  }, []);
15737
- const handleDetailNodeIntensityChange = (0, import_react32.useCallback)(
16037
+ const handleDetailNodeIntensityChange = (0, import_react31.useCallback)(
15738
16038
  (nodeId, newIntensity) => {
15739
16039
  const mesh = stateRef.current.nodeObjects[String(nodeId)];
15740
16040
  if (!mesh) return;
@@ -15890,7 +16190,7 @@ function XViewScene({
15890
16190
  mountRef.current.style.cursor = "default";
15891
16191
  }
15892
16192
  };
15893
- const handleAncestryTreeUpdate = (0, import_react32.useCallback)((newTree, extraData = null) => {
16193
+ const handleAncestryTreeUpdate = (0, import_react31.useCallback)((newTree, extraData = null) => {
15894
16194
  setAncestryMode((prev) => {
15895
16195
  const prevTreeStr = JSON.stringify(prev.tree);
15896
16196
  const newTreeStr = JSON.stringify(newTree);
@@ -15913,7 +16213,7 @@ function XViewScene({
15913
16213
  };
15914
16214
  });
15915
16215
  }, []);
15916
- const actionHandlerContext = (0, import_react32.useMemo)(
16216
+ const actionHandlerContext = (0, import_react31.useMemo)(
15917
16217
  () => {
15918
16218
  var _a2;
15919
16219
  return {
@@ -15974,7 +16274,7 @@ function XViewScene({
15974
16274
  const handleStartVersioning = (nodeData) => {
15975
16275
  userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
15976
16276
  };
15977
- const handleStartQuestQuick = (0, import_react32.useCallback)((questNode) => {
16277
+ const handleStartQuestQuick = (0, import_react31.useCallback)((questNode) => {
15978
16278
  var _a2, _b2, _c2;
15979
16279
  if (!questNode || !actionHandlerContext) return;
15980
16280
  const allParentNodes = Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes || []);
@@ -16009,7 +16309,7 @@ function XViewScene({
16009
16309
  }
16010
16310
  setDetailsNode(updatedNode);
16011
16311
  }, [session, actionHandlerContext]);
16012
- const handleFinishQuestQuick = (0, import_react32.useCallback)((questNode) => {
16312
+ const handleFinishQuestQuick = (0, import_react31.useCallback)((questNode) => {
16013
16313
  var _a2, _b2, _c2, _d2;
16014
16314
  if (!questNode || !actionHandlerContext) return;
16015
16315
  const {
@@ -16083,7 +16383,7 @@ function XViewScene({
16083
16383
  setDetailsNode(null);
16084
16384
  }
16085
16385
  }, [session, actionHandlerContext, viewMembers, detailsNode]);
16086
- const handleCancelQuest = (0, import_react32.useCallback)(() => {
16386
+ const handleCancelQuest = (0, import_react31.useCallback)(() => {
16087
16387
  const { graphGroup, ghostElements } = stateRef.current;
16088
16388
  if (ghostElements.node && graphGroup) {
16089
16389
  if (ghostElements.node.userData.labelObject) {
@@ -16142,13 +16442,15 @@ function XViewScene({
16142
16442
  try {
16143
16443
  await actions.save_view_data(sceneSaveUrl2, sceneFileData);
16144
16444
  sceneDataRef2.current.quest_counter = currentCounter + 1;
16145
- sceneDataRef2.current.quest_nodes = sceneFileData.quest_nodes;
16146
- sceneDataRef2.current.quest_links = sceneFileData.quest_links;
16147
16445
  stateRef2.current.nodeIdToParentFileMap.set(String(newNode.id), {
16148
16446
  parentFileId: sceneConfigId2,
16149
16447
  ownerId: ownerId2,
16150
16448
  datasetName: "Quests Internas (View)"
16151
16449
  });
16450
+ sceneDataRef2.current.quest_nodes = [...graphDataRef.current[sceneConfigId2].nodes];
16451
+ if (parentDataRef.current[sceneConfigId2]) {
16452
+ parentDataRef.current[sceneConfigId2].nodes = [...graphDataRef.current[sceneConfigId2].nodes];
16453
+ }
16152
16454
  const finalPosition = stateRef2.current.ghostElements.node ? stateRef2.current.ghostElements.node.position.clone() : stateRef2.current.controls.target.clone();
16153
16455
  const { graphGroup, ghostElements } = stateRef2.current;
16154
16456
  if (ghostElements.node && graphGroup) {
@@ -16238,7 +16540,7 @@ function XViewScene({
16238
16540
  }
16239
16541
  userActionHandlers.handleCancelConnection(context);
16240
16542
  };
16241
- const handleClearAncestryVisuals = (0, import_react32.useCallback)((ancestryId) => {
16543
+ const handleClearAncestryVisuals = (0, import_react31.useCallback)((ancestryId) => {
16242
16544
  const { renderedAncestries, ancestryGroup } = stateRef.current;
16243
16545
  const renderIndex = renderedAncestries.findIndex(
16244
16546
  (a) => String(a.id) === String(ancestryId)
@@ -16256,7 +16558,7 @@ function XViewScene({
16256
16558
  );
16257
16559
  }
16258
16560
  }, []);
16259
- const handleRenderAncestry = (0, import_react32.useCallback)(
16561
+ const handleRenderAncestry = (0, import_react31.useCallback)(
16260
16562
  async (ancestryObject, allowedSectionIds = null, activeSectionIdForFocus = null, baseRotation = 0, forceReprocess = true) => {
16261
16563
  setContextMenu(
16262
16564
  (prev) => prev.visible ? { ...prev, visible: false } : prev
@@ -16750,7 +17052,7 @@ function XViewScene({
16750
17052
  ancestryMode.isActive
16751
17053
  ]
16752
17054
  );
16753
- const handleRenderAbstractionTree = (0, import_react32.useCallback)(
17055
+ const handleRenderAbstractionTree = (0, import_react31.useCallback)(
16754
17056
  (ancestryObject, targetNodeId = null) => {
16755
17057
  setContextMenu(
16756
17058
  (prev) => prev.visible ? { ...prev, visible: false } : prev
@@ -16838,7 +17140,7 @@ function XViewScene({
16838
17140
  handleClearAncestryVisuals
16839
17141
  ]
16840
17142
  );
16841
- const handleReadModeBranchNav = (0, import_react32.useCallback)(
17143
+ const handleReadModeBranchNav = (0, import_react31.useCallback)(
16842
17144
  (nodeId, action, direction = "right") => {
16843
17145
  const { ancestry, branchStack } = readingMode;
16844
17146
  if (!ancestry || !ancestry.tree) return;
@@ -17016,13 +17318,13 @@ function XViewScene({
17016
17318
  },
17017
17319
  [readingMode, handleRenderAncestry, buildFullAncestryTree, tweenToTarget]
17018
17320
  );
17019
- const handleReadModeHighlight = (0, import_react32.useCallback)((nodeId) => {
17321
+ const handleReadModeHighlight = (0, import_react31.useCallback)((nodeId) => {
17020
17322
  if (stateRef.current.highlightedNodeId !== nodeId) {
17021
17323
  stateRef.current.highlightedNodeId = nodeId;
17022
17324
  }
17023
17325
  setHighlightedNodeId(nodeId);
17024
17326
  }, []);
17025
- const activeNodeBranches = (0, import_react32.useMemo)(() => {
17327
+ const activeNodeBranches = (0, import_react31.useMemo)(() => {
17026
17328
  if (!highlightedNodeId || !readingMode.ancestry || !readingMode.ancestry.tree)
17027
17329
  return null;
17028
17330
  const fullTree = buildFullAncestryTree(
@@ -17066,7 +17368,7 @@ function XViewScene({
17066
17368
  readingMode.branchStack,
17067
17369
  ancestryDataRef.current
17068
17370
  ]);
17069
- const backNavigationInfo = (0, import_react32.useMemo)(() => {
17371
+ const backNavigationInfo = (0, import_react31.useMemo)(() => {
17070
17372
  const { branchStack } = readingMode;
17071
17373
  if (!branchStack || branchStack.length === 0) return null;
17072
17374
  const lastStep = branchStack[branchStack.length - 1];
@@ -17077,7 +17379,7 @@ function XViewScene({
17077
17379
  name: "Voltar para anterior"
17078
17380
  };
17079
17381
  }, [readingMode.branchStack]);
17080
- const getReadModeDisplayContext = (0, import_react32.useMemo)(() => {
17382
+ const getReadModeDisplayContext = (0, import_react31.useMemo)(() => {
17081
17383
  const { ancestry, branchStack } = readingMode;
17082
17384
  if (!ancestry) return null;
17083
17385
  if (branchStack.length === 0) {
@@ -17120,7 +17422,7 @@ function XViewScene({
17120
17422
  customProperties: branchProps
17121
17423
  };
17122
17424
  }, [readingMode, buildFullAncestryTree, ancestryDataRef.current]);
17123
- const readModeAbstractionTree = (0, import_react32.useMemo)(() => {
17425
+ const readModeAbstractionTree = (0, import_react31.useMemo)(() => {
17124
17426
  if (!readingMode.isActive || !readingMode.ancestry || !readingMode.ancestry.abstraction_tree) {
17125
17427
  return null;
17126
17428
  }
@@ -17139,7 +17441,7 @@ function XViewScene({
17139
17441
  buildFullAncestryTree,
17140
17442
  sceneVersion
17141
17443
  ]);
17142
- const handleStartReadingAncestry = (0, import_react32.useCallback)(
17444
+ const handleStartReadingAncestry = (0, import_react31.useCallback)(
17143
17445
  async (ancestryObject, renderMode = "full") => {
17144
17446
  setContextMenu(
17145
17447
  (prev) => prev.visible ? { ...prev, visible: false } : prev
@@ -17182,7 +17484,7 @@ function XViewScene({
17182
17484
  },
17183
17485
  [handleRenderAncestry, handleRenderAbstractionTree]
17184
17486
  );
17185
- const handleReadModeSectionChange = (0, import_react32.useCallback)(
17487
+ const handleReadModeSectionChange = (0, import_react31.useCallback)(
17186
17488
  (activeSectionId) => {
17187
17489
  const { ancestry, branchStack } = readingMode;
17188
17490
  if (!ancestry || !readingMode.isActive) return;
@@ -17267,10 +17569,10 @@ function XViewScene({
17267
17569
  ancestryDataRef.current
17268
17570
  ]
17269
17571
  );
17270
- const handleCloseReadMode = (0, import_react32.useCallback)(() => {
17572
+ const handleCloseReadMode = (0, import_react31.useCallback)(() => {
17271
17573
  setReadingMode({ isActive: false, ancestry: null, branchStack: [] });
17272
17574
  }, []);
17273
- const handleAncestrySectionChange = (0, import_react32.useCallback)(
17575
+ const handleAncestrySectionChange = (0, import_react31.useCallback)(
17274
17576
  (activeSectionId, ancestryOverride = null, rotation = 0) => {
17275
17577
  var _a2, _b2;
17276
17578
  const currentMode = stateRef.current.ancestry;
@@ -17332,7 +17634,7 @@ function XViewScene({
17332
17634
  },
17333
17635
  [handleRenderAncestry]
17334
17636
  );
17335
- const handleEditAncestry = (0, import_react32.useCallback)(
17637
+ const handleEditAncestry = (0, import_react31.useCallback)(
17336
17638
  async (ancestryObject) => {
17337
17639
  setContextMenu(
17338
17640
  (prev) => prev.visible ? { ...prev, visible: false } : prev
@@ -17387,7 +17689,7 @@ function XViewScene({
17387
17689
  const handleSelectAncestryParent = (nodeId) => {
17388
17690
  setAncestryMode((prev) => ({ ...prev, selectedParentId: nodeId }));
17389
17691
  };
17390
- const handleRemoveFromAncestry = (0, import_react32.useCallback)((pathToRemove) => {
17692
+ const handleRemoveFromAncestry = (0, import_react31.useCallback)((pathToRemove) => {
17391
17693
  if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) {
17392
17694
  console.warn("Tentativa de remover a raiz ou caminho inv\xE1lido.");
17393
17695
  return;
@@ -17412,7 +17714,7 @@ function XViewScene({
17412
17714
  return { ...prev, tree: newTree };
17413
17715
  });
17414
17716
  }, []);
17415
- const handleSaveAncestry = (0, import_react32.useCallback)(
17717
+ const handleSaveAncestry = (0, import_react31.useCallback)(
17416
17718
  async (ancestryName, ancestryDescription, ancestrySections, keepOpen = false, treeOverride = null, ancestryCustomProps = {}) => {
17417
17719
  const treeToUse = treeOverride || ancestryMode.tree;
17418
17720
  const { isEditMode, currentAncestryId } = ancestryMode;
@@ -17648,7 +17950,7 @@ function XViewScene({
17648
17950
  });
17649
17951
  setEditingAncestryRel({ visible: false, data: null, path: null });
17650
17952
  };
17651
- const handleDeleteAncestry = (0, import_react32.useCallback)(
17953
+ const handleDeleteAncestry = (0, import_react31.useCallback)(
17652
17954
  async (ancestryIdToDelete) => {
17653
17955
  if (!ancestryIdToDelete) {
17654
17956
  alert("ID da ancestralidade n\xE3o encontrado.");
@@ -17730,42 +18032,97 @@ function XViewScene({
17730
18032
  },
17731
18033
  [save_view_data, delete_file_action]
17732
18034
  );
17733
- const handleOpenAncestryBoard = (0, import_react32.useCallback)(() => {
17734
- setIsAncestryBoardOpen(true);
18035
+ const handleOpenBoards = (0, import_react31.useCallback)(() => {
18036
+ setIsBoardsOpen(true);
17735
18037
  }, []);
17736
- const handleSelectAncestryFromBoard = (0, import_react32.useCallback)(
18038
+ const allAvailableNodes = (0, import_react31.useMemo)(() => {
18039
+ if (!parentDataRef.current) return [];
18040
+ return Object.values(parentDataRef.current).flatMap(
18041
+ (fileData) => fileData.nodes || []
18042
+ );
18043
+ }, [sceneVersion, isInitialized]);
18044
+ const allAvailableAncestries = (0, import_react31.useMemo)(() => {
18045
+ return ancestryDataRef.current || [];
18046
+ }, [sceneVersion, isInitialized]);
18047
+ const handleFocusNode = (0, import_react31.useCallback)(
18048
+ (nodeData) => {
18049
+ var _a2;
18050
+ if (!nodeData) return;
18051
+ let nodeMesh = stateRef.current.nodeObjects[String(nodeData.id)];
18052
+ if (!nodeMesh) {
18053
+ const spawnPos = ((_a2 = stateRef.current.controls) == null ? void 0 : _a2.target.clone()) || new THREE3.Vector3();
18054
+ addStandaloneNodeToScene(stateRef.current, nodeData, spawnPos);
18055
+ nodeMesh = stateRef.current.nodeObjects[String(nodeData.id)];
18056
+ }
18057
+ if (nodeMesh) {
18058
+ tweenToTarget(nodeMesh, 1.2);
18059
+ }
18060
+ },
18061
+ [tweenToTarget]
18062
+ );
18063
+ const handleOpenReference = (0, import_react31.useCallback)(
18064
+ (referenceData) => {
18065
+ const { type, id } = referenceData;
18066
+ if (type === "node") {
18067
+ const targetNode = allAvailableNodes.find(
18068
+ (n) => String(n.id) === String(id)
18069
+ );
18070
+ if (targetNode) {
18071
+ setAncestryLinkDetails(null);
18072
+ setDetailsLink(null);
18073
+ setDetailsNode(targetNode);
18074
+ handleFocusNode(targetNode);
18075
+ } else {
18076
+ alert("Node original n\xE3o encontrado neste contexto.");
18077
+ }
18078
+ } else if (type === "ancestry") {
18079
+ const targetAncestry = allAvailableAncestries.find(
18080
+ (a) => String(a.ancestry_id) === String(id)
18081
+ );
18082
+ if (targetAncestry) {
18083
+ setDetailsNode(null);
18084
+ setDetailsLink(null);
18085
+ setAncestryLinkDetails(null);
18086
+ handleEditAncestry(targetAncestry);
18087
+ } else {
18088
+ alert("Ancestralidade original n\xE3o encontrada neste contexto.");
18089
+ }
18090
+ }
18091
+ },
18092
+ [
18093
+ allAvailableNodes,
18094
+ allAvailableAncestries,
18095
+ handleEditAncestry,
18096
+ handleFocusNode
18097
+ ]
18098
+ );
18099
+ const handleSelectAncestryFromBoard = (0, import_react31.useCallback)(
17737
18100
  (item) => {
17738
- setIsAncestryBoardOpen(false);
18101
+ setIsBoardsOpen(false);
17739
18102
  setIsSidebarOpen(false);
17740
- if (item.type === "ancestry" || !item.type) {
18103
+ if (item.type === "ancestry") {
17741
18104
  handleStartReadingAncestry(item);
17742
18105
  } else {
17743
- const nodeId = String(item.id);
17744
- const sceneMesh = stateRef.current.nodeObjects[nodeId];
17745
- if (sceneMesh) {
17746
- tweenToTarget(sceneMesh);
17747
- } else {
17748
- console.warn("Objeto n\xE3o encontrado na cena para foco:", nodeId);
17749
- }
18106
+ handleOpenReference({ type: "node", id: item.id });
17750
18107
  }
17751
18108
  },
17752
- [handleStartReadingAncestry, tweenToTarget]
18109
+ [handleStartReadingAncestry, handleOpenReference]
17753
18110
  );
17754
- const handleSaveAncestryBoard = (0, import_react32.useCallback)(
17755
- async (groups) => {
18111
+ const handleSaveBoards = (0, import_react31.useCallback)(
18112
+ async (boardItems2) => {
17756
18113
  if (!sceneConfigId || !viewParams || !session) return;
17757
18114
  const sceneType = (viewParams.type || "").toLowerCase().includes("database") ? "database" : "view";
17758
- await save_ancestry_board_action(
18115
+ await save_boards_action(
17759
18116
  sceneConfigId,
17760
18117
  sceneType,
17761
- groups,
18118
+ boardItems2,
17762
18119
  session,
17763
18120
  ownerId
17764
18121
  );
17765
18122
  },
17766
- [sceneConfigId, viewParams, session, save_ancestry_board_action, ownerId]
18123
+ [sceneConfigId, viewParams, session, save_boards_action, ownerId]
17767
18124
  );
17768
- const existingNodeTypes = (0, import_react32.useMemo)(() => {
18125
+ const existingNodeTypes = (0, import_react31.useMemo)(() => {
17769
18126
  if (!parentDataRef.current) {
17770
18127
  return [];
17771
18128
  }
@@ -17777,28 +18134,22 @@ function XViewScene({
17777
18134
  ).filter((t) => Boolean(t) && String(t).toLowerCase() !== "quest");
17778
18135
  return [...new Set(allTypes)];
17779
18136
  }, [parentDataRef.current, sceneVersion]);
17780
- const searchableDbNodes = (0, import_react32.useMemo)(() => {
17781
- var _a2;
18137
+ const searchableDbNodes = (0, import_react31.useMemo)(() => {
17782
18138
  if (!parentDataRef.current) {
17783
18139
  return [];
17784
18140
  }
17785
- const datasetsPool = Object.entries(parentDataRef.current).flatMap(([dbId, fileData]) => {
18141
+ return Object.entries(parentDataRef.current).flatMap(([dbId, fileData]) => {
17786
18142
  const datasetName = fileData.dataset_name || `Dataset #${dbId.substring(0, 6)}`;
17787
18143
  return (fileData.nodes || []).map((node) => ({
17788
18144
  ...node,
17789
18145
  dataset_name: datasetName
17790
18146
  }));
18147
+ }).filter((node) => {
18148
+ var _a2;
18149
+ return !((_a2 = node.version_node) == null ? void 0 : _a2.is_version);
17791
18150
  });
17792
- const questsPool = (((_a2 = sceneDataRef.current) == null ? void 0 : _a2.quest_nodes) || []).map((node) => ({
17793
- ...node,
17794
- dataset_name: "Quests Internas (View)"
17795
- }));
17796
- return [...datasetsPool, ...questsPool].filter((node) => {
17797
- var _a3;
17798
- return !((_a3 = node.version_node) == null ? void 0 : _a3.is_version);
17799
- });
17800
- }, [parentDataRef.current, (_b = sceneDataRef.current) == null ? void 0 : _b.quest_nodes, sceneVersion]);
17801
- const handleAddExistingNode = (0, import_react32.useCallback)(
18151
+ }, [parentDataRef.current, sceneVersion]);
18152
+ const handleAddExistingNode = (0, import_react31.useCallback)(
17802
18153
  (nodeId) => {
17803
18154
  return userActionHandlers.handleAddExistingNodeById(
17804
18155
  actionHandlerContext,
@@ -17807,7 +18158,7 @@ function XViewScene({
17807
18158
  },
17808
18159
  [actionHandlerContext]
17809
18160
  );
17810
- const handleUpdateMilestones = (0, import_react32.useCallback)(
18161
+ const handleUpdateMilestones = (0, import_react31.useCallback)(
17811
18162
  async (newMilestones) => {
17812
18163
  var _a2, _b2, _c2;
17813
18164
  if (!sceneDataRef.current) return;
@@ -17853,7 +18204,7 @@ function XViewScene({
17853
18204
  viewParams == null ? void 0 : viewParams.type
17854
18205
  ]
17855
18206
  );
17856
- const handleSaveCurrentView = (0, import_react32.useCallback)(async () => {
18207
+ const handleSaveCurrentView = (0, import_react31.useCallback)(async () => {
17857
18208
  var _a2, _b2, _c2;
17858
18209
  const { nodeObjects, allLinks } = stateRef.current;
17859
18210
  if (!nodeObjects || !allLinks || !sceneSaveUrl || !parentDataRef.current) {
@@ -17896,77 +18247,13 @@ function XViewScene({
17896
18247
  console.error("Erro na chamada de save_view_data:", error);
17897
18248
  }
17898
18249
  }, [sceneSaveUrl, save_view_data, sceneConfigId, viewParams == null ? void 0 : viewParams.type]);
17899
- const allAvailableNodes = (0, import_react32.useMemo)(() => {
17900
- if (!parentDataRef.current) return [];
17901
- return Object.values(parentDataRef.current).flatMap(
17902
- (fileData) => fileData.nodes || []
17903
- );
17904
- }, [sceneVersion, isInitialized]);
17905
- const allAvailableAncestries = (0, import_react32.useMemo)(() => {
17906
- return ancestryDataRef.current || [];
17907
- }, [sceneVersion, isInitialized]);
17908
- const handleOpenReference = (0, import_react32.useCallback)(
17909
- (referenceData) => {
17910
- const { type, id } = referenceData;
17911
- if (type === "node") {
17912
- const targetNode = allAvailableNodes.find(
17913
- (n) => String(n.id) === String(id)
17914
- );
17915
- if (targetNode) {
17916
- setAncestryLinkDetails(null);
17917
- setDetailsLink(null);
17918
- setDetailsNode(targetNode);
17919
- const sceneMesh = stateRef.current.nodeObjects[String(id)];
17920
- if (sceneMesh) {
17921
- tweenToTarget(sceneMesh);
17922
- }
17923
- } else {
17924
- alert("Node original n\xE3o encontrado neste contexto.");
17925
- }
17926
- } else if (type === "ancestry") {
17927
- const targetAncestry = allAvailableAncestries.find(
17928
- (a) => String(a.ancestry_id) === String(id)
17929
- );
17930
- if (targetAncestry) {
17931
- setDetailsNode(null);
17932
- setDetailsLink(null);
17933
- setAncestryLinkDetails(null);
17934
- handleEditAncestry(targetAncestry);
17935
- } else {
17936
- alert("Ancestralidade original n\xE3o encontrada neste contexto.");
17937
- }
17938
- }
17939
- },
17940
- [
17941
- allAvailableNodes,
17942
- allAvailableAncestries,
17943
- handleEditAncestry,
17944
- tweenToTarget
17945
- ]
17946
- );
17947
- const handleToggleAncestryAddMode = (0, import_react32.useCallback)(() => {
18250
+ const handleToggleAncestryAddMode = (0, import_react31.useCallback)(() => {
17948
18251
  setAncestryMode((prev) => ({
17949
18252
  ...prev,
17950
18253
  isAddingNodes: !prev.isAddingNodes
17951
18254
  }));
17952
18255
  }, []);
17953
- const handleFocusNode = (0, import_react32.useCallback)(
17954
- (nodeData) => {
17955
- var _a2;
17956
- if (!nodeData) return;
17957
- let nodeMesh = stateRef.current.nodeObjects[String(nodeData.id)];
17958
- if (!nodeMesh) {
17959
- const spawnPos = ((_a2 = stateRef.current.controls) == null ? void 0 : _a2.target.clone()) || new THREE3.Vector3();
17960
- addStandaloneNodeToScene(stateRef.current, nodeData, spawnPos);
17961
- nodeMesh = stateRef.current.nodeObjects[String(nodeData.id)];
17962
- }
17963
- if (nodeMesh) {
17964
- tweenToTarget(nodeMesh, 1.2);
17965
- }
17966
- },
17967
- [tweenToTarget]
17968
- );
17969
- const availableDatasets = (0, import_react32.useMemo)(() => {
18256
+ const availableDatasets = (0, import_react31.useMemo)(() => {
17970
18257
  if (!sceneDataRef.current || !parentDataRef.current) return [];
17971
18258
  return sceneDataRef.current.parent_dbs.map((db) => {
17972
18259
  var _a2;
@@ -17976,11 +18263,11 @@ function XViewScene({
17976
18263
  };
17977
18264
  });
17978
18265
  }, [sceneVersion, isInitialized]);
17979
- const sourceNodeDatasetId = creationMode.sourceNodeData ? (_c = stateRef.current.nodeIdToParentFileMap.get(
18266
+ const sourceNodeDatasetId = creationMode.sourceNodeData ? (_b = stateRef.current.nodeIdToParentFileMap.get(
17980
18267
  String(creationMode.sourceNodeData.id)
17981
- )) == null ? void 0 : _c.parentFileId : null;
18268
+ )) == null ? void 0 : _b.parentFileId : null;
17982
18269
  const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
17983
- (0, import_react32.useEffect)(() => {
18270
+ (0, import_react31.useEffect)(() => {
17984
18271
  if (isInitialized && focusNodeId && !hasFocusedInitial) {
17985
18272
  const nodeObjects = stateRef.current.nodeObjects || {};
17986
18273
  const targetMesh = nodeObjects[String(focusNodeId)];
@@ -18003,7 +18290,7 @@ function XViewScene({
18003
18290
  hasFocusedInitial,
18004
18291
  tweenToTarget
18005
18292
  ]);
18006
- (0, import_react32.useEffect)(() => {
18293
+ (0, import_react31.useEffect)(() => {
18007
18294
  if (isInitialized && focusAncestryId && !hasOpenedInitialAncestry) {
18008
18295
  const ancestries = ancestryDataRef.current || [];
18009
18296
  const targetAncestry = ancestries.find(
@@ -18028,7 +18315,7 @@ function XViewScene({
18028
18315
  hasOpenedInitialAncestry,
18029
18316
  handleStartReadingAncestry
18030
18317
  ]);
18031
- (0, import_react32.useEffect)(() => {
18318
+ (0, import_react31.useEffect)(() => {
18032
18319
  function handleKeyDown(event) {
18033
18320
  var _a2, _b2, _c2;
18034
18321
  const context = actionHandlerContext;
@@ -18068,7 +18355,7 @@ function XViewScene({
18068
18355
  setRelationshipMenu((prev) => ({ ...prev, visible: false }));
18069
18356
  }
18070
18357
  if (event.key.toLowerCase() === "q") {
18071
- const isUiClear = !stateRef.current.creation.isActive && !stateRef.current.connection.isActive && !stateRef.current.relink.isActive && !stateRef.current.ancestry.isActive && !((_b2 = context.versionMode) == null ? void 0 : _b2.isActive) && !contextMenu.visible && !multiContextMenu.visible && !relationshipMenu.visible && !readingMode.isActive && !isImportModalOpen && !isAncestryBoardOpen && !isSidebarOpen && !detailsNode && !detailsLink && !ancestryLinkDetails && !imageViewer.visible && !editingAncestryRel.visible && !questMode.isActive;
18358
+ const isUiClear = !stateRef.current.creation.isActive && !stateRef.current.connection.isActive && !stateRef.current.relink.isActive && !stateRef.current.ancestry.isActive && !((_b2 = context.versionMode) == null ? void 0 : _b2.isActive) && !contextMenu.visible && !multiContextMenu.visible && !relationshipMenu.visible && !readingMode.isActive && !isImportModalOpen && !isBoardsOpen && !isSidebarOpen && !detailsNode && !detailsLink && !ancestryLinkDetails && !imageViewer.visible && !editingAncestryRel.visible && !questMode.isActive;
18072
18359
  if (isUiClear) {
18073
18360
  const isView = ((_c2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _c2.toLowerCase()) === "view";
18074
18361
  if (!isView) return;
@@ -18137,7 +18424,7 @@ function XViewScene({
18137
18424
  relationshipMenu.visible,
18138
18425
  readingMode.isActive,
18139
18426
  isImportModalOpen,
18140
- isAncestryBoardOpen,
18427
+ isBoardsOpen,
18141
18428
  isSidebarOpen,
18142
18429
  detailsNode,
18143
18430
  detailsLink,
@@ -18151,10 +18438,10 @@ function XViewScene({
18151
18438
  // <-- handleCancelQuest adicionado aqui
18152
18439
  ]);
18153
18440
  if (isLoading || status === "loading" || permissionStatus === "loading") {
18154
- return /* @__PURE__ */ import_react32.default.createElement(LoadingScreen, null);
18441
+ return /* @__PURE__ */ import_react31.default.createElement(LoadingScreen, null);
18155
18442
  }
18156
18443
  if (permissionStatus === "denied") {
18157
- return /* @__PURE__ */ import_react32.default.createElement("div", { className: "flex flex-col items-center justify-center min-h-screen w-full bg-slate-950 text-white" }, /* @__PURE__ */ import_react32.default.createElement("div", { className: "bg-slate-900/50 p-8 rounded-2xl border border-slate-800 shadow-2xl text-center max-w-md" }, /* @__PURE__ */ import_react32.default.createElement("div", { className: "mb-4 text-red-500" }, /* @__PURE__ */ import_react32.default.createElement(
18444
+ return /* @__PURE__ */ import_react31.default.createElement("div", { className: "flex flex-col items-center justify-center min-h-screen w-full bg-slate-950 text-white" }, /* @__PURE__ */ import_react31.default.createElement("div", { className: "bg-slate-900/50 p-8 rounded-2xl border border-slate-800 shadow-2xl text-center max-w-md" }, /* @__PURE__ */ import_react31.default.createElement("div", { className: "mb-4 text-red-500" }, /* @__PURE__ */ import_react31.default.createElement(
18158
18445
  "svg",
18159
18446
  {
18160
18447
  xmlns: "http://www.w3.org/2000/svg",
@@ -18164,7 +18451,7 @@ function XViewScene({
18164
18451
  stroke: "currentColor",
18165
18452
  className: "w-16 h-16 mx-auto"
18166
18453
  },
18167
- /* @__PURE__ */ import_react32.default.createElement(
18454
+ /* @__PURE__ */ import_react31.default.createElement(
18168
18455
  "path",
18169
18456
  {
18170
18457
  strokeLinecap: "round",
@@ -18172,13 +18459,13 @@ function XViewScene({
18172
18459
  d: "M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
18173
18460
  }
18174
18461
  )
18175
- )), /* @__PURE__ */ import_react32.default.createElement("h2", { className: "text-2xl font-bold mb-2" }, "Acesso Negado"), /* @__PURE__ */ import_react32.default.createElement("p", { className: "text-slate-400 mb-6" }, "Voc\xEA n\xE3o tem permiss\xE3o para acessar este conte\xFAdo. Solicite acesso ao propriet\xE1rio ou verifique se est\xE1 na conta correta."), /* @__PURE__ */ import_react32.default.createElement(
18462
+ )), /* @__PURE__ */ import_react31.default.createElement("h2", { className: "text-2xl font-bold mb-2" }, "Acesso Negado"), /* @__PURE__ */ import_react31.default.createElement("p", { className: "text-slate-400 mb-6" }, "Voc\xEA n\xE3o tem permiss\xE3o para acessar este conte\xFAdo. Solicite acesso ao propriet\xE1rio ou verifique se est\xE1 na conta correta."), /* @__PURE__ */ import_react31.default.createElement(
18176
18463
  "button",
18177
18464
  {
18178
18465
  onClick: () => router.push("/dashboard/scenes"),
18179
18466
  className: "flex items-center justify-center gap-2 w-full py-3 px-4 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors font-medium"
18180
18467
  },
18181
- /* @__PURE__ */ import_react32.default.createElement(
18468
+ /* @__PURE__ */ import_react31.default.createElement(
18182
18469
  "svg",
18183
18470
  {
18184
18471
  xmlns: "http://www.w3.org/2000/svg",
@@ -18188,7 +18475,7 @@ function XViewScene({
18188
18475
  stroke: "currentColor",
18189
18476
  className: "w-5 h-5"
18190
18477
  },
18191
- /* @__PURE__ */ import_react32.default.createElement(
18478
+ /* @__PURE__ */ import_react31.default.createElement(
18192
18479
  "path",
18193
18480
  {
18194
18481
  strokeLinecap: "round",
@@ -18200,7 +18487,7 @@ function XViewScene({
18200
18487
  "Voltar para Scenes"
18201
18488
  )));
18202
18489
  }
18203
- return /* @__PURE__ */ import_react32.default.createElement(
18490
+ return /* @__PURE__ */ import_react31.default.createElement(
18204
18491
  "div",
18205
18492
  {
18206
18493
  ref: mountRef,
@@ -18212,7 +18499,7 @@ function XViewScene({
18212
18499
  cursor: stateRef.current.connection.isActive || stateRef.current.relink.isActive || ancestryMode.isActive ? "crosshair" : creationMode.isActive ? "default" : "grab"
18213
18500
  }
18214
18501
  },
18215
- userPermissionRole !== "link_viewer" && /* @__PURE__ */ import_react32.default.createElement(
18502
+ userPermissionRole !== "link_viewer" && /* @__PURE__ */ import_react31.default.createElement(
18216
18503
  XViewSidebar,
18217
18504
  {
18218
18505
  dbNodes: searchableDbNodes,
@@ -18224,7 +18511,7 @@ function XViewScene({
18224
18511
  importSuccessMessage,
18225
18512
  onActivateTimeline: handleActivateTimeline,
18226
18513
  onActivateQuestLens: handleActivateQuestLens,
18227
- onOpenAncestryBoard: handleOpenAncestryBoard,
18514
+ onOpenBoards: handleOpenBoards,
18228
18515
  viewType: viewParams == null ? void 0 : viewParams.type,
18229
18516
  viewName: viewParams == null ? void 0 : viewParams.name,
18230
18517
  onNavigateBack: handleNavigateBack,
@@ -18232,11 +18519,11 @@ function XViewScene({
18232
18519
  onToggle: setIsSidebarOpen,
18233
18520
  userRole: userPermissionRole,
18234
18521
  viewMembers,
18235
- milestones: ((_d = sceneDataRef.current) == null ? void 0 : _d.milestones) || [],
18522
+ milestones: ((_c = sceneDataRef.current) == null ? void 0 : _c.milestones) || [],
18236
18523
  onOpenMilestoneManager: () => setIsMilestoneManagerOpen(true)
18237
18524
  }
18238
18525
  ),
18239
- creationMode.isActive && /* @__PURE__ */ import_react32.default.createElement(
18526
+ creationMode.isActive && /* @__PURE__ */ import_react31.default.createElement(
18240
18527
  InSceneCreationForm,
18241
18528
  {
18242
18529
  onSave: (data) => userActionHandlers.handleSaveNode(actionHandlerContext, data),
@@ -18257,8 +18544,8 @@ function XViewScene({
18257
18544
  },
18258
18545
  refEl: formRef,
18259
18546
  existingTypes: existingNodeTypes,
18260
- initialColor: (_e = creationMode.sourceNodeData) == null ? void 0 : _e.color,
18261
- sourceTypes: (_f = creationMode.sourceNodeData) == null ? void 0 : _f.type,
18547
+ initialColor: (_d = creationMode.sourceNodeData) == null ? void 0 : _d.color,
18548
+ sourceTypes: (_e = creationMode.sourceNodeData) == null ? void 0 : _e.type,
18262
18549
  onIntensityChange: handleGhostNodeIntensityChange,
18263
18550
  onUploadFile: upload_file_action,
18264
18551
  availableDatasets,
@@ -18268,7 +18555,7 @@ function XViewScene({
18268
18555
  availableAncestries: allAvailableAncestries
18269
18556
  }
18270
18557
  ),
18271
- versionMode.isActive && /* @__PURE__ */ import_react32.default.createElement(
18558
+ versionMode.isActive && /* @__PURE__ */ import_react31.default.createElement(
18272
18559
  InSceneVersionForm,
18273
18560
  {
18274
18561
  onSave: (data) => userActionHandlers.handleSaveVersionNode(actionHandlerContext, data),
@@ -18287,14 +18574,14 @@ function XViewScene({
18287
18574
  transition: "opacity 200ms ease-out"
18288
18575
  },
18289
18576
  refEl: formRef,
18290
- fixedType: (_g = versionMode.sourceNodeData) == null ? void 0 : _g.type,
18291
- fixedColor: (_h = versionMode.sourceNodeData) == null ? void 0 : _h.color,
18577
+ fixedType: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.type,
18578
+ fixedColor: (_g = versionMode.sourceNodeData) == null ? void 0 : _g.color,
18292
18579
  onUploadFile: upload_file_action,
18293
18580
  availableNodes: allAvailableNodes,
18294
18581
  availableAncestries: allAvailableAncestries
18295
18582
  }
18296
18583
  ),
18297
- questMode.isActive && /* @__PURE__ */ import_react32.default.createElement(
18584
+ questMode.isActive && /* @__PURE__ */ import_react31.default.createElement(
18298
18585
  InSceneQuestForm,
18299
18586
  {
18300
18587
  onSave: (data) => handleSaveQuestNode(actionHandlerContext, data),
@@ -18316,13 +18603,13 @@ function XViewScene({
18316
18603
  availableNodes: allAvailableNodes,
18317
18604
  availableAncestries: allAvailableAncestries,
18318
18605
  viewName: viewParams == null ? void 0 : viewParams.name,
18319
- questCounter: ((_i = sceneDataRef.current) == null ? void 0 : _i.quest_counter) || 1,
18606
+ questCounter: ((_h = sceneDataRef.current) == null ? void 0 : _h.quest_counter) || 1,
18320
18607
  viewMembers,
18321
- milestones: ((_j = sceneDataRef.current) == null ? void 0 : _j.milestones) || [],
18608
+ milestones: ((_i = sceneDataRef.current) == null ? void 0 : _i.milestones) || [],
18322
18609
  onUpdateMilestones: handleUpdateMilestones
18323
18610
  }
18324
18611
  ),
18325
- readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ import_react32.default.createElement(
18612
+ readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ import_react31.default.createElement(
18326
18613
  "div",
18327
18614
  {
18328
18615
  className: `ui-overlay absolute group rounded-2xl border border-white/10 bg-slate-950/70 backdrop-blur-xl shadow-[0_20px_80px_rgba(0,0,0,0.6)] ring-1 ring-white/10 text-white overflow-hidden flex flex-col ${isReadModeResizing ? "transition-none" : "transition-all duration-300 ease-out"}`,
@@ -18335,7 +18622,7 @@ function XViewScene({
18335
18622
  maxWidth: "92vw"
18336
18623
  }
18337
18624
  },
18338
- /* @__PURE__ */ import_react32.default.createElement(
18625
+ /* @__PURE__ */ import_react31.default.createElement(
18339
18626
  "div",
18340
18627
  {
18341
18628
  onPointerDown: (e) => {
@@ -18346,7 +18633,7 @@ function XViewScene({
18346
18633
  title: "Arraste para redimensionar"
18347
18634
  }
18348
18635
  ),
18349
- /* @__PURE__ */ import_react32.default.createElement(
18636
+ /* @__PURE__ */ import_react31.default.createElement(
18350
18637
  DescriptionReadModePanel,
18351
18638
  {
18352
18639
  key: readingMode.branchStack.length > 0 ? readingMode.branchStack[readingMode.branchStack.length - 1].branchId : readingMode.ancestry.ancestry_id,
@@ -18381,7 +18668,7 @@ function XViewScene({
18381
18668
  }
18382
18669
  )
18383
18670
  ),
18384
- ancestryMode.isActive && ancestryMode.tree && /* @__PURE__ */ import_react32.default.createElement(
18671
+ ancestryMode.isActive && ancestryMode.tree && /* @__PURE__ */ import_react31.default.createElement(
18385
18672
  CreateAncestryPanel,
18386
18673
  {
18387
18674
  ancestryMode,
@@ -18417,7 +18704,7 @@ function XViewScene({
18417
18704
  onRenderAbstractionTree: (data, targetId) => handleRenderAbstractionTree(data, targetId)
18418
18705
  }
18419
18706
  ),
18420
- editingAncestryRel.visible && /* @__PURE__ */ import_react32.default.createElement(
18707
+ editingAncestryRel.visible && /* @__PURE__ */ import_react31.default.createElement(
18421
18708
  AncestryRelationshipPanel,
18422
18709
  {
18423
18710
  data: editingAncestryRel.data,
@@ -18431,7 +18718,7 @@ function XViewScene({
18431
18718
  onUploadFile: upload_file_action
18432
18719
  }
18433
18720
  ),
18434
- detailsNode && detailsNode.is_quest && /* @__PURE__ */ import_react32.default.createElement(
18721
+ detailsNode && detailsNode.is_quest && /* @__PURE__ */ import_react31.default.createElement(
18435
18722
  QuestDetailsPanel,
18436
18723
  {
18437
18724
  node: detailsNode,
@@ -18453,11 +18740,11 @@ function XViewScene({
18453
18740
  currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName,
18454
18741
  viewMembers,
18455
18742
  currentUser: session == null ? void 0 : session.user,
18456
- milestones: ((_k = sceneDataRef.current) == null ? void 0 : _k.milestones) || [],
18743
+ milestones: ((_j = sceneDataRef.current) == null ? void 0 : _j.milestones) || [],
18457
18744
  onUpdateMilestones: handleUpdateMilestones
18458
18745
  }
18459
18746
  ),
18460
- detailsNode && !detailsNode.is_quest && /* @__PURE__ */ import_react32.default.createElement(
18747
+ detailsNode && !detailsNode.is_quest && /* @__PURE__ */ import_react31.default.createElement(
18461
18748
  NodeDetailsPanel,
18462
18749
  {
18463
18750
  node: detailsNode,
@@ -18489,7 +18776,7 @@ function XViewScene({
18489
18776
  currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
18490
18777
  }
18491
18778
  ),
18492
- detailsLink && /* @__PURE__ */ import_react32.default.createElement(
18779
+ detailsLink && /* @__PURE__ */ import_react31.default.createElement(
18493
18780
  RelationshipDetailsPanel,
18494
18781
  {
18495
18782
  link: detailsLink,
@@ -18503,7 +18790,7 @@ function XViewScene({
18503
18790
  userRole: userPermissionRole
18504
18791
  }
18505
18792
  ),
18506
- ancestryLinkDetails && /* @__PURE__ */ import_react32.default.createElement(
18793
+ ancestryLinkDetails && /* @__PURE__ */ import_react31.default.createElement(
18507
18794
  AncestryLinkDetailsPanel,
18508
18795
  {
18509
18796
  data: ancestryLinkDetails,
@@ -18514,7 +18801,7 @@ function XViewScene({
18514
18801
  onUploadFile: upload_file_action
18515
18802
  }
18516
18803
  ),
18517
- /* @__PURE__ */ import_react32.default.createElement(
18804
+ /* @__PURE__ */ import_react31.default.createElement(
18518
18805
  "div",
18519
18806
  {
18520
18807
  ref: tooltipRef,
@@ -18541,7 +18828,7 @@ function XViewScene({
18541
18828
  }
18542
18829
  }
18543
18830
  ),
18544
- /* @__PURE__ */ import_react32.default.createElement(
18831
+ /* @__PURE__ */ import_react31.default.createElement(
18545
18832
  ContextMenu,
18546
18833
  {
18547
18834
  data: contextMenu,
@@ -18574,7 +18861,7 @@ function XViewScene({
18574
18861
  onFinishQuest: handleFinishQuestQuick
18575
18862
  }
18576
18863
  ),
18577
- /* @__PURE__ */ import_react32.default.createElement(
18864
+ /* @__PURE__ */ import_react31.default.createElement(
18578
18865
  MultiNodeContextMenu,
18579
18866
  {
18580
18867
  data: multiContextMenu,
@@ -18594,7 +18881,7 @@ function XViewScene({
18594
18881
  )
18595
18882
  }
18596
18883
  ),
18597
- /* @__PURE__ */ import_react32.default.createElement(
18884
+ /* @__PURE__ */ import_react31.default.createElement(
18598
18885
  RelationshipContextMenu,
18599
18886
  {
18600
18887
  data: relationshipMenu,
@@ -18612,49 +18899,49 @@ function XViewScene({
18612
18899
  onDelete: (data) => userActionHandlers.handleDeleteLink(actionHandlerContext, data)
18613
18900
  }
18614
18901
  ),
18615
- /* @__PURE__ */ import_react32.default.createElement(
18902
+ /* @__PURE__ */ import_react31.default.createElement(
18616
18903
  ImageViewer,
18617
18904
  {
18618
18905
  data: imageViewer,
18619
18906
  onClose: () => setImageViewer({ ...imageViewer, visible: false })
18620
18907
  }
18621
18908
  ),
18622
- /* @__PURE__ */ import_react32.default.createElement(
18623
- AncestryBoard,
18909
+ /* @__PURE__ */ import_react31.default.createElement(
18910
+ Boards,
18624
18911
  {
18625
- isOpen: isAncestryBoardOpen,
18626
- onClose: () => setIsAncestryBoardOpen(false),
18912
+ isOpen: isBoardsOpen,
18913
+ onClose: () => setIsBoardsOpen(false),
18627
18914
  onSelect: handleSelectAncestryFromBoard,
18628
18915
  availableAncestries: allAvailableAncestries,
18629
18916
  availableNodes: allAvailableNodes,
18630
- initialGroups: ancestryBoardData,
18631
- onSave: handleSaveAncestryBoard,
18917
+ initialBoardItems: boardItems,
18918
+ onSave: handleSaveBoards,
18632
18919
  userRole: userPermissionRole
18633
18920
  }
18634
18921
  ),
18635
- /* @__PURE__ */ import_react32.default.createElement(
18922
+ /* @__PURE__ */ import_react31.default.createElement(
18636
18923
  MilestoneManager,
18637
18924
  {
18638
18925
  isOpen: isMilestoneManagerOpen,
18639
18926
  onClose: () => setIsMilestoneManagerOpen(false),
18640
- milestones: ((_l = sceneDataRef.current) == null ? void 0 : _l.milestones) || [],
18927
+ milestones: ((_k = sceneDataRef.current) == null ? void 0 : _k.milestones) || [],
18641
18928
  onUpdateMilestones: handleUpdateMilestones
18642
18929
  }
18643
18930
  ),
18644
- /* @__PURE__ */ import_react32.default.createElement(
18931
+ /* @__PURE__ */ import_react31.default.createElement(
18645
18932
  ImportParentFileModal,
18646
18933
  {
18647
18934
  isOpen: isImportModalOpen,
18648
18935
  onClose: () => setIsImportModalOpen(false),
18649
18936
  onConfirm: handleConfirmImport,
18650
18937
  session,
18651
- parentDbs: ((_m = sceneDataRef.current) == null ? void 0 : _m.parent_dbs) || [],
18938
+ parentDbs: ((_l = sceneDataRef.current) == null ? void 0 : _l.parent_dbs) || [],
18652
18939
  onFetchAvailableFiles: import_parent_file_modal_get,
18653
18940
  currentViewName: viewParams == null ? void 0 : viewParams.name,
18654
18941
  currentAncestries: ancestryDataRef.current || []
18655
18942
  }
18656
18943
  ),
18657
- invalidTargetError && /* @__PURE__ */ import_react32.default.createElement(
18944
+ invalidTargetError && /* @__PURE__ */ import_react31.default.createElement(
18658
18945
  "div",
18659
18946
  {
18660
18947
  className: "ui-overlay",
@@ -18679,13 +18966,13 @@ function XViewScene({
18679
18966
  animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
18680
18967
  }
18681
18968
  },
18682
- /* @__PURE__ */ import_react32.default.createElement("style", null, `
18969
+ /* @__PURE__ */ import_react31.default.createElement("style", null, `
18683
18970
  @keyframes fadeInDown {
18684
18971
  from { opacity: 0; transform: translate(-50%, -20px); }
18685
18972
  to { opacity: 1; transform: translate(-50%, 0); }
18686
18973
  }
18687
18974
  `),
18688
- /* @__PURE__ */ import_react32.default.createElement(
18975
+ /* @__PURE__ */ import_react31.default.createElement(
18689
18976
  "svg",
18690
18977
  {
18691
18978
  width: "20",
@@ -18697,12 +18984,12 @@ function XViewScene({
18697
18984
  strokeLinecap: "round",
18698
18985
  strokeLinejoin: "round"
18699
18986
  },
18700
- /* @__PURE__ */ import_react32.default.createElement("circle", { cx: "12", cy: "12", r: "10" }),
18701
- /* @__PURE__ */ import_react32.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
18702
- /* @__PURE__ */ import_react32.default.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
18987
+ /* @__PURE__ */ import_react31.default.createElement("circle", { cx: "12", cy: "12", r: "10" }),
18988
+ /* @__PURE__ */ import_react31.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
18989
+ /* @__PURE__ */ import_react31.default.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
18703
18990
  ),
18704
- /* @__PURE__ */ import_react32.default.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
18705
- /* @__PURE__ */ import_react32.default.createElement(
18991
+ /* @__PURE__ */ import_react31.default.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
18992
+ /* @__PURE__ */ import_react31.default.createElement(
18706
18993
  "button",
18707
18994
  {
18708
18995
  onClick: () => setInvalidTargetError(null),
@@ -18732,7 +19019,451 @@ function XViewScene({
18732
19019
  )
18733
19020
  );
18734
19021
  }
19022
+
19023
+ // src/x_view_server_logic.js
19024
+ async function save_view_data_logic(db_services, filename, data) {
19025
+ try {
19026
+ const saveResult = await db_services.save_file(filename, data);
19027
+ return saveResult;
19028
+ } catch (error) {
19029
+ console.error("catch (error) em save_view_data_logic:", error);
19030
+ return { success: false, error: error.message };
19031
+ }
19032
+ }
19033
+ async function get_scene_view_data_logic(db_services, scene_config, owner_id, type, session, focusNodeId = null, focusAncestryId = null) {
19034
+ var _a, _b;
19035
+ try {
19036
+ if (!session || !session.user) {
19037
+ return { success: false, error: "Usu\xE1rio n\xE3o autenticado." };
19038
+ }
19039
+ const membershipCheck = await db_services.check_membership(owner_id, type, scene_config, session.user.id);
19040
+ const isFullMember = membershipCheck.success && membershipCheck.isValidMember;
19041
+ if (!isFullMember && !focusNodeId && !focusAncestryId) {
19042
+ return { success: false, error: "Acesso Negado: Voc\xEA n\xE3o possui permiss\xE3o para acessar este cen\xE1rio." };
19043
+ }
19044
+ const sceneResponse = await db_services.get_file(`x_view_scenes/${owner_id}/${scene_config}`);
19045
+ if (!sceneResponse || !sceneResponse.success) {
19046
+ console.error("Falha ao buscar dados da cena:", sceneResponse == null ? void 0 : sceneResponse.error);
19047
+ return { success: false, error: `N\xE3o foi poss\xEDvel carregar o arquivo da cena: ${scene_config}` };
19048
+ }
19049
+ const sceneData = sceneResponse.data;
19050
+ const parentDbObjects = sceneData.parent_dbs || [];
19051
+ if (type && type.toLowerCase().includes("database")) {
19052
+ const selfExists = parentDbObjects.some((db) => String(db.db_id) === String(scene_config));
19053
+ if (!selfExists) {
19054
+ parentDbObjects.push({ db_id: scene_config, owner_id });
19055
+ }
19056
+ }
19057
+ const parentResponsesPromises = parentDbObjects.map(
19058
+ (db_info) => db_services.get_file(`x_view_dbs/${db_info.owner_id}/${db_info.db_id}`)
19059
+ );
19060
+ const parentResponsesResults = await Promise.allSettled(parentResponsesPromises);
19061
+ const parentData = {};
19062
+ for (let i = 0; i < parentResponsesResults.length; i++) {
19063
+ const result = parentResponsesResults[i];
19064
+ const db_info = parentDbObjects[i];
19065
+ if (result.status === "fulfilled" && ((_a = result.value) == null ? void 0 : _a.success)) {
19066
+ parentData[db_info.db_id] = result.value.data;
19067
+ } else {
19068
+ console.warn(
19069
+ `Arquivo pai n\xE3o carregado: ${db_info.db_id}. Pode ter sido deletado.`,
19070
+ result.status === "rejected" ? result.reason : (_b = result.value) == null ? void 0 : _b.error
19071
+ );
19072
+ }
19073
+ }
19074
+ if (type && type.toLowerCase() === "view") {
19075
+ parentData[scene_config] = {
19076
+ dataset_name: "Quests Internas (View)",
19077
+ nodes: sceneData.quest_nodes || [],
19078
+ links: sceneData.quest_links || []
19079
+ };
19080
+ }
19081
+ const allNodes = Object.values(parentData).flatMap((db) => db.nodes || []);
19082
+ const allLinks = Object.values(parentData).flatMap((db) => db.links || []);
19083
+ const parentNodeMap = new Map(allNodes.map((node) => [String(node.id), node]));
19084
+ const parentLinkMap = new Map(allLinks.map((link) => [`${link.source}-${link.target}`, link]));
19085
+ const validatedNodes = (sceneData.nodes || []).map((sceneNode) => {
19086
+ var _a2, _b2;
19087
+ const nodeTypes = Array.isArray(sceneNode.type) ? sceneNode.type : [sceneNode.type];
19088
+ if (nodeTypes.includes("quest")) {
19089
+ const dbQuestNode = (_b2 = (_a2 = parentData[scene_config]) == null ? void 0 : _a2.nodes) == null ? void 0 : _b2.find((qn) => String(qn.id) === String(sceneNode.id));
19090
+ if (dbQuestNode) {
19091
+ return { ...sceneNode, ...dbQuestNode };
19092
+ }
19093
+ return sceneNode;
19094
+ }
19095
+ const dbNode = parentNodeMap.get(String(sceneNode.id));
19096
+ if (dbNode) {
19097
+ return { ...sceneNode, ...dbNode };
19098
+ }
19099
+ return null;
19100
+ }).filter(Boolean);
19101
+ const validNodeIdsInScene = new Set(validatedNodes.map((node) => String(node.id)));
19102
+ const validatedLinks = (sceneData.links || []).filter((sceneLink) => {
19103
+ const linkExistsInParent = parentLinkMap.has(`${sceneLink.source}-${sceneLink.target}`);
19104
+ const nodesAreValidForScene = validNodeIdsInScene.has(String(sceneLink.source)) && validNodeIdsInScene.has(String(sceneLink.target));
19105
+ return linkExistsInParent && nodesAreValidForScene;
19106
+ }).map((sceneLink) => parentLinkMap.get(`${sceneLink.source}-${sceneLink.target}`));
19107
+ const validatedSceneData = {
19108
+ ...sceneData,
19109
+ nodes: validatedNodes,
19110
+ links: validatedLinks
19111
+ };
19112
+ const ancestryFilePaths = [];
19113
+ ancestryFilePaths.push({
19114
+ path: `x_view_ancestry/${owner_id}/${scene_config}`,
19115
+ source_id: scene_config,
19116
+ owner_id,
19117
+ isSceneFile: true
19118
+ });
19119
+ parentDbObjects.forEach((db_info) => {
19120
+ ancestryFilePaths.push({
19121
+ path: `x_view_ancestry/${db_info.owner_id}/${db_info.db_id}`,
19122
+ source_id: db_info.db_id,
19123
+ owner_id: db_info.owner_id,
19124
+ isSceneFile: false
19125
+ });
19126
+ });
19127
+ const ancestryPromises = ancestryFilePaths.map((file) => db_services.get_file(file.path));
19128
+ const ancestryResults = await Promise.allSettled(ancestryPromises);
19129
+ const ancestriesFromSceneFile = [];
19130
+ const ancestriesFromDbFiles = [];
19131
+ ancestryResults.forEach((result, index) => {
19132
+ var _a2;
19133
+ const fileInfo = ancestryFilePaths[index];
19134
+ if (result.status === "fulfilled" && ((_a2 = result.value) == null ? void 0 : _a2.success) && Array.isArray(result.value.data)) {
19135
+ const ancestriesFromFile = result.value.data.map((ancestry) => {
19136
+ const existingSource = ancestry._source_file_id;
19137
+ const existingOwner = ancestry._source_owner_id;
19138
+ const isImportedItem = existingSource && existingSource !== fileInfo.source_id;
19139
+ return {
19140
+ ...ancestry,
19141
+ _source_file_id: isImportedItem ? existingSource : fileInfo.source_id,
19142
+ _source_owner_id: isImportedItem ? existingOwner : fileInfo.owner_id
19143
+ };
19144
+ });
19145
+ if (fileInfo.isSceneFile) {
19146
+ ancestriesFromSceneFile.push(...ancestriesFromFile);
19147
+ } else {
19148
+ ancestriesFromDbFiles.push(...ancestriesFromFile);
19149
+ }
19150
+ }
19151
+ });
19152
+ const ancestryMap = /* @__PURE__ */ new Map();
19153
+ ancestriesFromDbFiles.forEach((anc) => ancestryMap.set(anc.ancestry_id, anc));
19154
+ ancestriesFromSceneFile.forEach((anc) => ancestryMap.set(anc.ancestry_id, anc));
19155
+ let ancestryData = Array.from(ancestryMap.values());
19156
+ const externalSourcesToRefresh = /* @__PURE__ */ new Map();
19157
+ ancestryData.forEach((anc) => {
19158
+ const targetSourceId = anc._source_file_id || anc._imported_from_view_id;
19159
+ const targetOwnerId = anc._source_owner_id || anc._imported_from_view_owner_id;
19160
+ if (targetSourceId && targetSourceId !== scene_config && targetOwnerId) {
19161
+ const isAlreadyLoadedAsParent = parentDbObjects.some((db) => String(db.db_id) === String(targetSourceId));
19162
+ if (!isAlreadyLoadedAsParent) {
19163
+ const key = `${targetOwnerId}/${targetSourceId}`;
19164
+ externalSourcesToRefresh.set(key, {
19165
+ owner_id: targetOwnerId,
19166
+ source_id: targetSourceId,
19167
+ key
19168
+ });
19169
+ }
19170
+ }
19171
+ });
19172
+ const successfullyFetchedSources = /* @__PURE__ */ new Set();
19173
+ const failedSources = /* @__PURE__ */ new Set();
19174
+ if (externalSourcesToRefresh.size > 0) {
19175
+ const refreshPromises = Array.from(externalSourcesToRefresh.values()).map(
19176
+ (src) => db_services.get_file(`x_view_ancestry/${src.owner_id}/${src.source_id}`).then((res) => ({ ...res, _meta_key: src.key }))
19177
+ );
19178
+ const refreshResults = await Promise.allSettled(refreshPromises);
19179
+ const freshAncestryMap = /* @__PURE__ */ new Map();
19180
+ refreshResults.forEach((result) => {
19181
+ var _a2, _b2;
19182
+ if (result.status === "fulfilled" && ((_a2 = result.value) == null ? void 0 : _a2.success) && Array.isArray(result.value.data)) {
19183
+ successfullyFetchedSources.add(result.value._meta_key);
19184
+ result.value.data.forEach((freshAnc) => {
19185
+ if (!freshAnc.is_private) {
19186
+ freshAncestryMap.set(String(freshAnc.ancestry_id), freshAnc);
19187
+ }
19188
+ });
19189
+ } else if (result.status === "fulfilled" && !((_b2 = result.value) == null ? void 0 : _b2.success)) {
19190
+ failedSources.add(result.value._meta_key);
19191
+ }
19192
+ });
19193
+ ancestryData = ancestryData.filter((localAnc) => {
19194
+ const targetSourceId = localAnc._source_file_id || localAnc._imported_from_view_id;
19195
+ const targetOwnerId = localAnc._source_owner_id || localAnc._imported_from_view_owner_id;
19196
+ if (!targetSourceId || targetSourceId === scene_config) return true;
19197
+ const key = `${targetOwnerId}/${targetSourceId}`;
19198
+ if (failedSources.has(key)) {
19199
+ return false;
19200
+ }
19201
+ if (externalSourcesToRefresh.has(key) && successfullyFetchedSources.has(key)) {
19202
+ const stillExists = freshAncestryMap.has(String(localAnc.ancestry_id));
19203
+ return stillExists;
19204
+ }
19205
+ return true;
19206
+ }).map((localAnc) => {
19207
+ const freshVersion = freshAncestryMap.get(String(localAnc.ancestry_id));
19208
+ if (freshVersion) {
19209
+ return {
19210
+ ...freshVersion,
19211
+ _imported_from_view_id: localAnc._imported_from_view_id,
19212
+ _imported_from_view_owner_id: localAnc._imported_from_view_owner_id,
19213
+ _origin_db_ids: localAnc._origin_db_ids,
19214
+ _source_file_id: localAnc._source_file_id,
19215
+ _source_owner_id: localAnc._source_owner_id
19216
+ };
19217
+ }
19218
+ return localAnc;
19219
+ });
19220
+ }
19221
+ const ancestryFilePath = `x_view_ancestry/${owner_id}/${scene_config}`;
19222
+ const parentNodeIds = new Set(allNodes.map((node) => String(node.id)));
19223
+ const pruneTree = (node) => {
19224
+ if (!node) return null;
19225
+ if (node.is_section) {
19226
+ const validChildren2 = (node.children || []).map((child) => pruneTree(child)).filter(Boolean);
19227
+ return { ...node, children: validChildren2 };
19228
+ }
19229
+ if (!parentNodeIds.has(String(node.node_id))) {
19230
+ return null;
19231
+ }
19232
+ const validChildren = (node.children || []).map((child) => pruneTree(child)).filter(Boolean);
19233
+ let validBranches = [];
19234
+ if (node.parallel_branches) {
19235
+ validBranches = node.parallel_branches.map((branch) => ({
19236
+ ...branch,
19237
+ tree: pruneTree(branch.tree)
19238
+ })).filter((b) => b.tree !== null);
19239
+ }
19240
+ return {
19241
+ ...node,
19242
+ children: validChildren,
19243
+ parallel_branches: validBranches
19244
+ };
19245
+ };
19246
+ const originalAncestryDataString = JSON.stringify(Array.from(ancestryMap.values()));
19247
+ const cleanedAncestryData = ancestryData.map((ancestry) => {
19248
+ if (!parentNodeIds.has(String(ancestry.ancestral_node))) return null;
19249
+ const cleanedTree = pruneTree(ancestry.tree);
19250
+ if (!cleanedTree) return null;
19251
+ let cleanedAbstraction = null;
19252
+ if (ancestry.abstraction_tree) {
19253
+ cleanedAbstraction = pruneTree(ancestry.abstraction_tree);
19254
+ }
19255
+ return {
19256
+ ...ancestry,
19257
+ tree: cleanedTree,
19258
+ abstraction_tree: cleanedAbstraction
19259
+ };
19260
+ }).filter(Boolean);
19261
+ if (JSON.stringify(cleanedAncestryData) !== originalAncestryDataString) {
19262
+ ancestryData = cleanedAncestryData;
19263
+ try {
19264
+ await db_services.save_file(ancestryFilePath, ancestryData);
19265
+ } catch (saveError) {
19266
+ console.error("Falha ao salvar o arquivo de ancestralidade da cena atualizado:", saveError);
19267
+ }
19268
+ }
19269
+ if (!isFullMember) {
19270
+ const requiredNodeIds = /* @__PURE__ */ new Set();
19271
+ const requiredAncestryIds = /* @__PURE__ */ new Set();
19272
+ if (focusNodeId) requiredNodeIds.add(String(focusNodeId));
19273
+ if (focusAncestryId) requiredAncestryIds.add(String(focusAncestryId));
19274
+ const allNodesFromDb = Object.values(parentData).flatMap((db) => db.nodes || []);
19275
+ const extractDependenciesFromText = (text) => {
19276
+ if (!text) return;
19277
+ const mentionRegex = /\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g;
19278
+ const refRegex = /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g;
19279
+ let match;
19280
+ while ((match = mentionRegex.exec(text)) !== null) {
19281
+ requiredNodeIds.add(match[1]);
19282
+ }
19283
+ while ((match = refRegex.exec(text)) !== null) {
19284
+ if (match[1] === "node") requiredNodeIds.add(match[2]);
19285
+ if (match[1] === "ancestry") requiredAncestryIds.add(match[2]);
19286
+ }
19287
+ };
19288
+ const extractTreeDependencies = (treeItem) => {
19289
+ var _a2;
19290
+ if (!treeItem) return;
19291
+ if (treeItem.node_id) requiredNodeIds.add(String(treeItem.node_id));
19292
+ if ((_a2 = treeItem.node) == null ? void 0 : _a2.id) requiredNodeIds.add(String(treeItem.node.id));
19293
+ if (treeItem.children) {
19294
+ treeItem.children.forEach(extractTreeDependencies);
19295
+ }
19296
+ if (treeItem.parallel_branches) {
19297
+ treeItem.parallel_branches.forEach((branch) => {
19298
+ if (branch.linked_ancestry_id) {
19299
+ requiredAncestryIds.add(String(branch.linked_ancestry_id));
19300
+ }
19301
+ if (branch.description) extractDependenciesFromText(branch.description);
19302
+ if (branch.description_sections) {
19303
+ branch.description_sections.forEach((s) => extractDependenciesFromText(s.content));
19304
+ }
19305
+ extractTreeDependencies(branch.tree);
19306
+ });
19307
+ }
19308
+ };
19309
+ let previousSize;
19310
+ do {
19311
+ previousSize = requiredNodeIds.size + requiredAncestryIds.size;
19312
+ requiredAncestryIds.forEach((ancId) => {
19313
+ const anc = ancestryData.find((a) => String(a.ancestry_id) === ancId);
19314
+ if (anc && !anc.is_private) {
19315
+ if (anc.description) extractDependenciesFromText(anc.description);
19316
+ if (anc.description_sections) {
19317
+ anc.description_sections.forEach((s) => extractDependenciesFromText(s.content));
19318
+ }
19319
+ extractTreeDependencies(anc.tree);
19320
+ extractTreeDependencies(anc.abstraction_tree);
19321
+ }
19322
+ });
19323
+ requiredNodeIds.forEach((nodeId) => {
19324
+ const node = allNodesFromDb.find((n) => String(n.id) === nodeId);
19325
+ if (node) {
19326
+ if (node.description) extractDependenciesFromText(node.description);
19327
+ if (node.description_sections) {
19328
+ node.description_sections.forEach((s) => extractDependenciesFromText(s.content));
19329
+ }
19330
+ }
19331
+ });
19332
+ } while (requiredNodeIds.size + requiredAncestryIds.size > previousSize);
19333
+ const restrictedSceneData = { ...validatedSceneData, nodes: [], links: [] };
19334
+ const restrictedParentData = {};
19335
+ const restrictedAncestryData = ancestryData.filter((a) => requiredAncestryIds.has(String(a.ancestry_id)) && !a.is_private);
19336
+ for (const [dbId, dbContent] of Object.entries(parentData)) {
19337
+ const filteredNodes = (dbContent.nodes || []).filter((n) => requiredNodeIds.has(String(n.id)));
19338
+ if (filteredNodes.length > 0) {
19339
+ restrictedParentData[dbId] = { ...dbContent, nodes: filteredNodes, links: [] };
19340
+ }
19341
+ }
19342
+ if (focusNodeId) {
19343
+ const mainNode = allNodesFromDb.find((n) => String(n.id) === String(focusNodeId));
19344
+ if (mainNode) {
19345
+ restrictedSceneData.nodes = [mainNode];
19346
+ }
19347
+ }
19348
+ return {
19349
+ success: true,
19350
+ data: {
19351
+ scene: restrictedSceneData,
19352
+ parent: restrictedParentData,
19353
+ ancestry: restrictedAncestryData
19354
+ }
19355
+ };
19356
+ }
19357
+ return {
19358
+ success: true,
19359
+ data: { scene: validatedSceneData, parent: parentData, ancestry: ancestryData }
19360
+ };
19361
+ } catch (error) {
19362
+ console.error("Erro em get_scene_view_data_logic:", error);
19363
+ return { success: false, error: error.message };
19364
+ }
19365
+ }
19366
+ async function import_parent_file_modal_get_logic(db_services, session) {
19367
+ try {
19368
+ const call_firestore_get_x_view_data = await db_services.get_firestore_data(session);
19369
+ const x_view_databases = call_firestore_get_x_view_data.data.sub_collections.x_view_databases || [];
19370
+ const x_views = call_firestore_get_x_view_data.data.sub_collections.x_views || [];
19371
+ return {
19372
+ success: true,
19373
+ data: {
19374
+ databases: x_view_databases,
19375
+ views: x_views
19376
+ }
19377
+ };
19378
+ } catch (error) {
19379
+ console.error("catch (error) em import_parent_file_modal_get_logic:", error);
19380
+ return { success: false, error: error.message };
19381
+ }
19382
+ }
19383
+ async function get_single_parent_file_logic(db_services, file_id, session) {
19384
+ try {
19385
+ const call_GCP_cloud_storage_get_file = await db_services.get_file(`x_view_dbs/${session.user.id}/${file_id}`);
19386
+ return call_GCP_cloud_storage_get_file;
19387
+ } catch (error) {
19388
+ console.error("catch (error) em get_single_parent_file_logic:", error);
19389
+ return { success: false, error: error.message };
19390
+ }
19391
+ }
19392
+ async function x_view_add_new_parent_file_to_scene_at_firebase(file_id, session, new_parent, db_services) {
19393
+ try {
19394
+ const update_data = {
19395
+ scene_id: file_id,
19396
+ new_parent,
19397
+ session
19398
+ };
19399
+ const call_firestore_add_new_parent_file_to_scene_at_firebase = await db_services.add_new_parent_file_to_scene_at_firebase(update_data);
19400
+ return call_firestore_add_new_parent_file_to_scene_at_firebase;
19401
+ } catch (error) {
19402
+ console.error("catch (error) em get_single_parent_file_logic:", error);
19403
+ return { success: false, error: error.message };
19404
+ }
19405
+ }
19406
+ async function get_ancestry_file_logic(db_services, file_id, owner_id) {
19407
+ try {
19408
+ const path = `x_view_ancestry/${owner_id}/${file_id}`;
19409
+ const call_GCP_cloud_storage_get_file = await db_services.get_file(path);
19410
+ return call_GCP_cloud_storage_get_file;
19411
+ } catch (error) {
19412
+ console.error("catch (error) em get_ancestry_file_logic:", error);
19413
+ return { success: false, error: error.message };
19414
+ }
19415
+ }
19416
+ async function save_boards_logic(db_services, scene_id, type, boardItems, session, owner_id) {
19417
+ try {
19418
+ const targetOwnerId = owner_id || session.user.id;
19419
+ return await db_services.save_boards(session, scene_id, type, boardItems, targetOwnerId);
19420
+ } catch (error) {
19421
+ console.error("Erro em save_boards_logic:", error);
19422
+ return { success: false, error: error.message };
19423
+ }
19424
+ }
19425
+ async function get_boards_logic(db_services, scene_id, type, session, owner_id) {
19426
+ try {
19427
+ const targetOwnerId = owner_id || session.user.id;
19428
+ return await db_services.get_boards(session, scene_id, type, targetOwnerId);
19429
+ } catch (error) {
19430
+ console.error("Erro em get_boards_logic:", error);
19431
+ return { success: false, error: error.message };
19432
+ }
19433
+ }
19434
+ async function delete_uploaded_file_logic(db_services, fileUrl) {
19435
+ try {
19436
+ if (db_services.delete_public_file) {
19437
+ return await db_services.delete_public_file(fileUrl);
19438
+ }
19439
+ return { success: false, error: "Servi\xE7o de dele\xE7\xE3o n\xE3o configurado." };
19440
+ } catch (error) {
19441
+ console.error("Erro em delete_uploaded_file_logic:", error);
19442
+ return { success: false, error: error.message };
19443
+ }
19444
+ }
19445
+ async function get_view_members_logic(db_services, ownerId, type, itemId) {
19446
+ try {
19447
+ if (!db_services.get_view_members) {
19448
+ return { success: false, error: "Servi\xE7o de busca de membros n\xE3o configurado." };
19449
+ }
19450
+ return await db_services.get_view_members(ownerId, type, itemId);
19451
+ } catch (error) {
19452
+ console.error("Erro em get_view_members_logic:", error);
19453
+ return { success: false, error: error.message };
19454
+ }
19455
+ }
18735
19456
  // Annotate the CommonJS export names for ESM import in node:
18736
19457
  0 && (module.exports = {
18737
- XViewScene
19458
+ XViewScene,
19459
+ delete_uploaded_file_logic,
19460
+ get_ancestry_file_logic,
19461
+ get_boards_logic,
19462
+ get_scene_view_data_logic,
19463
+ get_single_parent_file_logic,
19464
+ get_view_members_logic,
19465
+ import_parent_file_modal_get_logic,
19466
+ save_boards_logic,
19467
+ save_view_data_logic,
19468
+ x_view_add_new_parent_file_to_scene_at_firebase
18738
19469
  });