@lv-x-software-house/x_view 1.2.6-dev.2 → 1.2.6-dev.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  // src/XViewScene.jsx
2
2
  import React30, {
3
- useCallback as useCallback4,
3
+ useCallback as useCallback5,
4
4
  useEffect as useEffect24,
5
- useRef as useRef19,
6
- useState as useState28,
5
+ useRef as useRef21,
6
+ useState as useState29,
7
7
  useMemo as useMemo13
8
8
  } from "react";
9
9
  import { useRouter, useSearchParams } from "next/navigation";
@@ -1053,6 +1053,8 @@ var userActionHandlers = {
1053
1053
  if (isCrossDataset) {
1054
1054
  graphDataRef.current[finalTargetDatasetId] = targetDataToUpdate;
1055
1055
  }
1056
+ if (sceneDataRef.current.parent_dbs.some((db) => String(db.db_id) === String(sourceParentInfo.parentFileId))) {
1057
+ }
1056
1058
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1057
1059
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
1058
1060
  stateRef.current.nodeIdToParentFileMap.set(String(newNode.id), {
@@ -1251,6 +1253,9 @@ var userActionHandlers = {
1251
1253
  );
1252
1254
  }
1253
1255
  graphDataRef.current[parentFileId] = specificParentData;
1256
+ if (isView && parentFileId === context.sceneConfigId) {
1257
+ sceneDataRef.current.quest_nodes = [...specificParentData.nodes];
1258
+ }
1254
1259
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1255
1260
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
1256
1261
  setters.setSceneVersion((v) => v + 1);
@@ -2270,8 +2275,6 @@ var userActionHandlers = {
2270
2275
  milestones: sceneDataRef.current.milestones || [],
2271
2276
  quest_counter: sceneDataRef.current.quest_counter || 1
2272
2277
  };
2273
- sceneDataRef.current.quest_nodes = specificParentData.nodes;
2274
- sceneDataRef.current.quest_links = specificParentData.links;
2275
2278
  await context.actions.save_view_data(
2276
2279
  context.sceneSaveUrl,
2277
2280
  viewFilePayload
@@ -2284,6 +2287,10 @@ var userActionHandlers = {
2284
2287
  );
2285
2288
  }
2286
2289
  graphDataRef.current[parentFileId] = specificParentData;
2290
+ if (isView && parentFileId === context.sceneConfigId) {
2291
+ sceneDataRef.current.quest_nodes = [...specificParentData.nodes];
2292
+ sceneDataRef.current.quest_counter = specificParentData.quest_counter || sceneDataRef.current.quest_counter;
2293
+ }
2287
2294
  updateExistingNodeVisuals(stateRef.current, nodeToSave);
2288
2295
  setters.setSceneVersion((v) => v + 1);
2289
2296
  if (!keepOpen) {
@@ -12943,9 +12950,9 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
12943
12950
  }
12944
12951
 
12945
12952
  // src/components/AncestryBoard.jsx
12946
- import React28, { useState as useState26, useMemo as useMemo12, useEffect as useEffect23, useRef as useRef18 } from "react";
12953
+ import React28, { useState as useState27, useMemo as useMemo12, useEffect as useEffect23, useRef as useRef20 } from "react";
12947
12954
  import {
12948
- FiSearch as FiSearch9,
12955
+ FiSearch as FiSearch8,
12949
12956
  FiLayers as FiLayers7,
12950
12957
  FiCornerUpRight as FiCornerUpRight4,
12951
12958
  FiPlay,
@@ -12953,365 +12960,590 @@ import {
12953
12960
  FiTrash2 as FiTrash23,
12954
12961
  FiArrowLeft as FiArrowLeft3,
12955
12962
  FiArrowRight as FiArrowRight2,
12963
+ FiArrowUp,
12964
+ FiArrowDown,
12956
12965
  FiCheckCircle,
12957
12966
  FiLoader as FiLoader5,
12958
12967
  FiX as FiX8,
12959
- FiAlertTriangle,
12960
- FiCheckSquare as FiCheckSquare4,
12961
12968
  FiBox as FiBox2,
12962
- FiGitBranch as FiGitBranch4
12969
+ FiCheckSquare as FiCheckSquare4,
12970
+ FiGitBranch as FiGitBranch4,
12971
+ FiFolder as FiFolder3,
12972
+ FiChevronRight as FiChevronRight7
12963
12973
  } from "react-icons/fi";
12964
12974
  import { v4 as uuidv411 } from "uuid";
12965
12975
 
12966
- // src/components/MentionEditor.jsx
12967
- import React27, { useMemo as useMemo11, useEffect as useEffect22 } from "react";
12968
- import { useEditor, EditorContent, ReactRenderer } from "@tiptap/react";
12969
- import StarterKit from "@tiptap/starter-kit";
12970
- import Mention from "@tiptap/extension-mention";
12971
- import tippy from "tippy.js";
12976
+ // src/components/RichGroupEditor.jsx
12977
+ import React27, { useEffect as useEffect22, useRef as useRef19, useState as useState26, useCallback as useCallback4 } from "react";
12972
12978
 
12973
- // src/components/MentionList.jsx
12974
- import React26, {
12975
- forwardRef,
12976
- useEffect as useEffect21,
12977
- useImperativeHandle,
12978
- useState as useState25
12979
- } from "react";
12980
- import {
12981
- FiLayers as FiLayers6,
12982
- FiCheckSquare as FiCheckSquare3,
12983
- FiBox,
12984
- FiGitBranch as FiGitBranch3,
12985
- FiSearch as FiSearch8
12986
- } from "react-icons/fi";
12987
- var MentionList = forwardRef((props, ref) => {
12988
- const [selectedIndex, setSelectedIndex] = useState25(0);
12989
- const selectItem = (index) => {
12990
- const item = props.items[index];
12991
- if (item) {
12992
- props.command({ id: item.id, label: item.name, type: item.type });
12993
- }
12994
- };
12995
- const upHandler = () => {
12996
- setSelectedIndex(
12997
- (selectedIndex + props.items.length - 1) % props.items.length
12998
- );
12999
- };
13000
- const downHandler = () => {
13001
- setSelectedIndex((selectedIndex + 1) % props.items.length);
13002
- };
13003
- const enterHandler = () => {
13004
- selectItem(selectedIndex);
13005
- };
13006
- useEffect21(() => setSelectedIndex(0), [props.items]);
13007
- useImperativeHandle(ref, () => ({
13008
- onKeyDown: ({ event }) => {
13009
- if (event.key === "ArrowUp") {
13010
- upHandler();
13011
- return true;
13012
- }
13013
- if (event.key === "ArrowDown") {
13014
- downHandler();
13015
- return true;
13016
- }
13017
- if (event.key === "Enter") {
13018
- enterHandler();
13019
- return true;
12979
+ // src/components/MentionPopover.jsx
12980
+ import React26, { useEffect as useEffect21, useRef as useRef18, useState as useState25, useMemo as useMemo11 } from "react";
12981
+ import { FiLayers as FiLayers6, FiCheckSquare as FiCheckSquare3, FiGitBranch as FiGitBranch3, FiBox } from "react-icons/fi";
12982
+ var TYPE_CONFIG = {
12983
+ ancestry: { Icon: FiLayers6, color: "text-indigo-400", label: "Ancestralidade" },
12984
+ quest: { Icon: FiCheckSquare3, color: "text-emerald-400", label: "Quest" },
12985
+ version: { Icon: FiGitBranch3, color: "text-amber-400", label: "Vers\xE3o" },
12986
+ node: { Icon: FiBox, color: "text-slate-400", label: "Node" }
12987
+ };
12988
+ function MentionPopover({ query, items, position, onSelect, onClose }) {
12989
+ const [activeIndex, setActiveIndex] = useState25(0);
12990
+ const listRef = useRef18(null);
12991
+ const filtered = useMemo11(() => {
12992
+ const term = (query || "").toLowerCase().trim();
12993
+ if (!term) return items.slice(0, 8);
12994
+ return items.filter(
12995
+ (item) => (item.searchLabel || "").toLowerCase().includes(term) || (item.searchDesc || "").toLowerCase().includes(term)
12996
+ ).slice(0, 8);
12997
+ }, [items, query]);
12998
+ useEffect21(() => {
12999
+ setActiveIndex(0);
13000
+ }, [query]);
13001
+ useEffect21(() => {
13002
+ const handleKeyDown = (e) => {
13003
+ if (e.key === "ArrowDown") {
13004
+ e.preventDefault();
13005
+ setActiveIndex((i) => Math.min(i + 1, filtered.length - 1));
13006
+ } else if (e.key === "ArrowUp") {
13007
+ e.preventDefault();
13008
+ setActiveIndex((i) => Math.max(i - 1, 0));
13009
+ } else if (e.key === "Enter" || e.key === "Tab") {
13010
+ e.preventDefault();
13011
+ if (filtered[activeIndex]) onSelect(filtered[activeIndex]);
13012
+ } else if (e.key === "Escape") {
13013
+ e.preventDefault();
13014
+ e.stopPropagation();
13015
+ e.stopImmediatePropagation();
13016
+ onClose();
13020
13017
  }
13021
- return false;
13022
- }
13023
- }));
13024
- return /* @__PURE__ */ React26.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__ */ React26.createElement("div", { className: "p-2 border-b border-white/5 bg-slate-950/50 flex items-center gap-2" }, /* @__PURE__ */ React26.createElement(FiSearch8, { className: "text-indigo-400", size: 14 }), /* @__PURE__ */ React26.createElement("span", { className: "text-[10px] uppercase tracking-wider font-bold text-slate-500" }, "Mencionar Recurso")), /* @__PURE__ */ React26.createElement("div", { className: "p-1 max-h-[300px] overflow-y-auto custom-scrollbar" }, props.items.length ? props.items.map((item, index) => {
13025
- const isQuest = item.type === "quest";
13026
- const isVersion = item.type === "version";
13027
- const isNode = item.type === "node";
13028
- let Icon = FiLayers6;
13029
- let colorClass = "text-indigo-400";
13030
- let badgeText = "Ancestralidade";
13031
- if (isQuest) {
13032
- Icon = FiCheckSquare3;
13033
- colorClass = "text-amber-400";
13034
- badgeText = "Quest";
13035
- } else if (isVersion) {
13036
- Icon = FiGitBranch3;
13037
- colorClass = "text-purple-400";
13038
- badgeText = "Vers\xE3o";
13039
- } else if (isNode) {
13040
- Icon = FiBox;
13041
- colorClass = "text-emerald-400";
13042
- badgeText = "Node";
13043
- }
13044
- return /* @__PURE__ */ React26.createElement(
13045
- "button",
13046
- {
13047
- className: `
13048
- w-full flex items-center gap-3 px-3 py-2 text-left rounded-md transition-all
13049
- ${index === selectedIndex ? "bg-indigo-500/20 border border-indigo-500/30 text-white" : "text-slate-400 hover:bg-white/5 border border-transparent"}
13050
- `,
13051
- key: item.id,
13052
- onClick: () => selectItem(index)
13053
- },
13054
- /* @__PURE__ */ React26.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__ */ React26.createElement(Icon, { size: 14 })),
13055
- /* @__PURE__ */ React26.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React26.createElement("div", { className: "flex items-center justify-between gap-2" }, /* @__PURE__ */ React26.createElement("span", { className: "text-sm font-medium truncate" }, item.name), /* @__PURE__ */ React26.createElement("span", { className: `text-[9px] px-1 py-0.5 rounded border border-current opacity-60 ${colorClass}` }, badgeText)))
13056
- );
13057
- }) : /* @__PURE__ */ React26.createElement("div", { className: "p-4 text-center text-slate-500 text-sm italic" }, "Nenhum resultado encontrado...")));
13058
- });
13059
- MentionList.displayName = "MentionList";
13060
- var MentionList_default = MentionList;
13018
+ };
13019
+ window.addEventListener("keydown", handleKeyDown, true);
13020
+ return () => window.removeEventListener("keydown", handleKeyDown, true);
13021
+ }, [filtered, activeIndex, onSelect, onClose]);
13022
+ useEffect21(() => {
13023
+ if (listRef.current) {
13024
+ const el = listRef.current.children[activeIndex];
13025
+ if (el) el.scrollIntoView({ block: "nearest" });
13026
+ }
13027
+ }, [activeIndex]);
13028
+ return /* @__PURE__ */ React26.createElement(
13029
+ "div",
13030
+ {
13031
+ style: { top: position.top, left: position.left, position: "fixed", zIndex: 9999 },
13032
+ 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",
13033
+ ref: listRef,
13034
+ onMouseDown: (e) => e.preventDefault()
13035
+ },
13036
+ filtered.length === 0 ? /* @__PURE__ */ React26.createElement("div", { className: "px-4 py-3 text-xs text-slate-500" }, "Nenhum resultado para", " ", /* @__PURE__ */ React26.createElement("span", { className: "text-slate-300 font-medium" }, '"', query, '"')) : filtered.map((item, idx) => {
13037
+ const config = TYPE_CONFIG[item.type] || TYPE_CONFIG.node;
13038
+ const { Icon } = config;
13039
+ return /* @__PURE__ */ React26.createElement(
13040
+ "div",
13041
+ {
13042
+ key: item.id,
13043
+ onMouseDown: (e) => {
13044
+ e.preventDefault();
13045
+ onSelect(item);
13046
+ },
13047
+ onMouseEnter: () => setActiveIndex(idx),
13048
+ 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"}`
13049
+ },
13050
+ /* @__PURE__ */ React26.createElement(Icon, { size: 13, className: config.color }),
13051
+ /* @__PURE__ */ React26.createElement("span", { className: "flex-1 truncate font-medium" }, item.searchLabel || "Sem Nome"),
13052
+ /* @__PURE__ */ React26.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)
13053
+ );
13054
+ })
13055
+ );
13056
+ }
13061
13057
 
13062
- // src/components/MentionEditor.jsx
13063
- var CustomMention = Mention.extend({
13064
- addAttributes() {
13058
+ // src/components/RichGroupEditor.jsx
13059
+ 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;";
13060
+ var MENTION_TYPE_STYLE = {
13061
+ ancestry: "background:rgba(99,102,241,0.18);color:#a5b4fc;border:1px solid rgba(99,102,241,0.35);",
13062
+ quest: "background:rgba(16,185,129,0.18);color:#6ee7b7;border:1px solid rgba(16,185,129,0.35);",
13063
+ version: "background:rgba(245,158,11,0.18);color:#fcd34d;border:1px solid rgba(245,158,11,0.35);",
13064
+ node: "background:rgba(100,116,139,0.18);color:#94a3b8;border:1px solid rgba(100,116,139,0.35);"
13065
+ };
13066
+ function mentionStyle(type) {
13067
+ return MENTION_BASE_STYLE + (MENTION_TYPE_STYLE[type] || MENTION_TYPE_STYLE.node);
13068
+ }
13069
+ function escapeHtml(str) {
13070
+ return String(str || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
13071
+ }
13072
+ function contentToHTML(content) {
13073
+ if (!content || content.length === 0) return "";
13074
+ return content.map((seg) => {
13075
+ if (seg.type === "text") {
13076
+ return escapeHtml(seg.value).replace(/\n/g, "<br>");
13077
+ }
13078
+ if (seg.type === "mention") {
13079
+ const style = mentionStyle(seg.mentionType);
13080
+ 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>`;
13081
+ }
13082
+ return "";
13083
+ }).join("");
13084
+ }
13085
+ function domToContent(el) {
13086
+ const segments = [];
13087
+ function pushText(text) {
13088
+ const clean = text.replace(/\u200B/g, "");
13089
+ if (!clean) return;
13090
+ const last = segments[segments.length - 1];
13091
+ if (last && last.type === "text") {
13092
+ last.value += clean;
13093
+ } else {
13094
+ segments.push({ type: "text", value: clean });
13095
+ }
13096
+ }
13097
+ function walk(node) {
13065
13098
  var _a;
13066
- return {
13067
- ...(_a = this.parent) == null ? void 0 : _a.call(this),
13068
- type: {
13069
- default: null,
13070
- parseHTML: (element) => element.getAttribute("data-mention-type"),
13071
- renderHTML: (attributes) => {
13072
- if (!attributes.type) {
13073
- return {};
13074
- }
13075
- return {
13076
- "data-mention-type": attributes.type
13077
- };
13099
+ if (node.nodeType === Node.TEXT_NODE) {
13100
+ pushText(node.textContent);
13101
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
13102
+ if (((_a = node.dataset) == null ? void 0 : _a.mention) === "true") {
13103
+ segments.push({
13104
+ type: "mention",
13105
+ id: node.dataset.id,
13106
+ name: node.dataset.name,
13107
+ mentionType: node.dataset.mentionType
13108
+ });
13109
+ } else if (node.tagName === "BR") {
13110
+ pushText("\n");
13111
+ } else {
13112
+ if (["DIV", "P"].includes(node.tagName) && segments.length > 0) {
13113
+ pushText("\n");
13078
13114
  }
13115
+ node.childNodes.forEach(walk);
13079
13116
  }
13080
- };
13117
+ }
13081
13118
  }
13082
- });
13083
- var MentionEditor = ({
13084
- initialValue = "",
13119
+ el.childNodes.forEach(walk);
13120
+ return segments;
13121
+ }
13122
+ function RichGroupEditor({
13123
+ content,
13085
13124
  onChange,
13086
- onPlay,
13087
- // Mantido para futura expansão se quisermos badges clicáveis reais no editor
13088
- availableItems = [],
13089
- canEdit = true,
13090
- placeholder = "Escreva algo... use @ para mencionar"
13091
- }) => {
13092
- const parseInitialContent = (text) => {
13093
- if (!text) return "<p></p>";
13094
- const parts = text.split(/(\[\[MENTION:[^\]]+\]\])/g);
13095
- return parts.map((part) => {
13096
- const match = part.match(/\[\[MENTION:([^:]+):([^:]+):([^\]]+)\]\]/);
13097
- if (match) {
13098
- const [, type, id, label] = match;
13099
- return `<span data-type="mention" data-id="${id}" data-mention-type="${type}" data-label="${label}">@${label}</span>`;
13100
- }
13101
- return part;
13102
- }).join("");
13103
- };
13104
- const content = useMemo11(() => parseInitialContent(initialValue), [initialValue]);
13105
- const editor = useEditor({
13106
- extensions: [
13107
- StarterKit.configure({
13108
- heading: false,
13109
- bulletList: false,
13110
- orderedList: false,
13111
- codeBlock: false,
13112
- blockquote: false
13113
- }),
13114
- CustomMention.configure({
13115
- HTMLAttributes: {
13116
- class: "mention"
13117
- },
13118
- suggestion: {
13119
- items: ({ query }) => {
13120
- return availableItems.filter(
13121
- (item) => item.name.toLowerCase().includes(query.toLowerCase())
13122
- ).slice(0, 10);
13123
- },
13124
- render: () => {
13125
- let component;
13126
- let popup;
13127
- return {
13128
- onStart: (props) => {
13129
- component = new ReactRenderer(MentionList_default, {
13130
- props,
13131
- editor: props.editor
13132
- });
13133
- if (!props.clientRect) {
13134
- return;
13135
- }
13136
- popup = tippy("body", {
13137
- getReferenceClientRect: props.clientRect,
13138
- appendTo: () => document.body,
13139
- content: component.element,
13140
- showOnCreate: true,
13141
- interactive: true,
13142
- trigger: "manual",
13143
- placement: "bottom-start"
13144
- });
13145
- },
13146
- onUpdate(props) {
13147
- component.updateProps(props);
13148
- if (!props.clientRect) {
13149
- return;
13150
- }
13151
- popup[0].setProps({
13152
- getReferenceClientRect: props.clientRect
13153
- });
13154
- },
13155
- onKeyDown(props) {
13156
- var _a;
13157
- if (props.event.key === "Escape") {
13158
- popup[0].hide();
13159
- return true;
13160
- }
13161
- return (_a = component == null ? void 0 : component.ref) == null ? void 0 : _a.onKeyDown(props);
13162
- },
13163
- onExit() {
13164
- if (popup && popup[0]) popup[0].destroy();
13165
- if (component) component.destroy();
13166
- }
13167
- };
13168
- }
13169
- }
13170
- })
13171
- ],
13172
- content,
13173
- editable: canEdit,
13174
- onUpdate: ({ editor: editor2 }) => {
13175
- const serialize = (json) => {
13176
- if (!json.content) return "";
13177
- return json.content.map((block) => {
13178
- if (block.type === "paragraph") {
13179
- const inner = block.content ? block.content.map((node) => {
13180
- if (node.type === "text") return node.text;
13181
- if (node.type === "mention") {
13182
- return `[[MENTION:${node.attrs.type}:${node.attrs.id}:${node.attrs.label}]]`;
13183
- }
13184
- return "";
13185
- }).join("") : "";
13186
- return inner;
13187
- }
13188
- return "";
13189
- }).join("\n").trim();
13190
- };
13191
- const serialized = serialize(editor2.getJSON());
13192
- onChange(serialized);
13125
+ onMentionClick,
13126
+ searchItems,
13127
+ readOnly,
13128
+ placeholder
13129
+ }) {
13130
+ const editorRef = useRef19(null);
13131
+ const isMountedRef = useRef19(false);
13132
+ const dismissedTriggerRef = useRef19(null);
13133
+ const [mentionState, setMentionState] = useState26(null);
13134
+ const [isEmpty, setIsEmpty] = useState26(true);
13135
+ useEffect22(() => {
13136
+ if (!isMountedRef.current && editorRef.current) {
13137
+ editorRef.current.innerHTML = contentToHTML(content);
13138
+ const hasContent = !!editorRef.current.innerText.replace(/\u200B/g, "").trim();
13139
+ const hasMentions = !!editorRef.current.querySelector("[data-mention]");
13140
+ setIsEmpty(!hasContent && !hasMentions);
13141
+ isMountedRef.current = true;
13193
13142
  }
13194
- });
13195
- if (!editor) {
13196
- return null;
13197
- }
13198
- return /* @__PURE__ */ React27.createElement("div", { className: "relative group/editor min-h-[1.5rem] w-full" }, /* @__PURE__ */ React27.createElement(
13199
- EditorContent,
13200
- {
13201
- editor,
13202
- className: "text-sm text-slate-200 placeholder-slate-600 focus:outline-none"
13143
+ }, []);
13144
+ const getMentionContext = useCallback4(() => {
13145
+ const selection = window.getSelection();
13146
+ if (!selection || selection.rangeCount === 0) return null;
13147
+ const range = selection.getRangeAt(0);
13148
+ const node = range.startContainer;
13149
+ if (node.nodeType !== Node.TEXT_NODE) return null;
13150
+ const textBefore = node.textContent.substring(0, range.startOffset);
13151
+ const atIndex = textBefore.lastIndexOf("@");
13152
+ if (atIndex === -1) {
13153
+ dismissedTriggerRef.current = null;
13154
+ return null;
13203
13155
  }
13204
- ), /* @__PURE__ */ React27.createElement("style", null, `
13205
- .tiptap p {
13206
- margin: 0;
13207
- min-height: 1.2em;
13208
- }
13209
- .tiptap .mention {
13210
- display: inline-flex;
13211
- align-items: center;
13212
- background-color: rgba(99, 102, 241, 0.15);
13213
- border: 1px solid rgba(99, 102, 241, 0.3);
13214
- border-radius: 4px;
13215
- padding: 0 4px;
13216
- color: #a5b4fc;
13217
- font-weight: 500;
13218
- cursor: pointer;
13219
- margin: 0 2px;
13220
- transition: all 0.2s;
13221
- }
13222
- .tiptap .mention:hover {
13223
- background-color: rgba(99, 102, 241, 0.25);
13224
- border-color: rgba(99, 102, 241, 0.5);
13225
- color: white;
13156
+ if (dismissedTriggerRef.current && dismissedTriggerRef.current.node === node && dismissedTriggerRef.current.atIndex === atIndex) {
13157
+ return null;
13158
+ }
13159
+ const query = textBefore.substring(atIndex + 1);
13160
+ if (query.includes(" ") || query.includes(".") || query.includes(",") || query.length > 40) {
13161
+ return null;
13162
+ }
13163
+ const rect = range.getBoundingClientRect();
13164
+ return {
13165
+ query,
13166
+ position: { top: rect.bottom + 6, left: rect.left }
13167
+ };
13168
+ }, []);
13169
+ const handleInput = useCallback4(() => {
13170
+ if (!editorRef.current) return;
13171
+ const serialized = domToContent(editorRef.current);
13172
+ onChange(serialized);
13173
+ const text = editorRef.current.innerText.replace(/\u200B/g, "").trim();
13174
+ const hasMentions = !!editorRef.current.querySelector("[data-mention]");
13175
+ setIsEmpty(!text && !hasMentions);
13176
+ const ctx = getMentionContext();
13177
+ setMentionState(ctx);
13178
+ }, [onChange, getMentionContext]);
13179
+ const handleKeyDown = useCallback4(
13180
+ (e) => {
13181
+ if (mentionState && ["ArrowDown", "ArrowUp", "Enter", "Tab", "Escape"].includes(e.key)) {
13182
+ return;
13183
+ }
13184
+ if (e.key === "Enter") {
13185
+ e.preventDefault();
13186
+ const sel = window.getSelection();
13187
+ const range = sel.getRangeAt(0);
13188
+ const br = document.createElement("br");
13189
+ range.deleteContents();
13190
+ range.insertNode(br);
13191
+ const afterBr = br.nextSibling || (() => {
13192
+ const zws = document.createTextNode("\u200B");
13193
+ br.parentNode.insertBefore(zws, br.nextSibling);
13194
+ return zws;
13195
+ })();
13196
+ const newRange = document.createRange();
13197
+ newRange.setStartBefore(afterBr);
13198
+ newRange.collapse(true);
13199
+ sel.removeAllRanges();
13200
+ sel.addRange(newRange);
13201
+ handleInput();
13202
+ }
13203
+ },
13204
+ [mentionState, handleInput]
13205
+ );
13206
+ const insertMention = useCallback4(
13207
+ (item) => {
13208
+ if (!editorRef.current) return;
13209
+ const sel = window.getSelection();
13210
+ if (!sel || sel.rangeCount === 0) return;
13211
+ const range = sel.getRangeAt(0);
13212
+ const textNode = range.startContainer;
13213
+ if (textNode.nodeType !== Node.TEXT_NODE) {
13214
+ setMentionState(null);
13215
+ return;
13216
+ }
13217
+ const cursorPos = range.startOffset;
13218
+ const textContent = textNode.textContent;
13219
+ const atIndex = textContent.lastIndexOf("@", cursorPos - 1);
13220
+ if (atIndex === -1) {
13221
+ setMentionState(null);
13222
+ return;
13223
+ }
13224
+ const before = textContent.substring(0, atIndex);
13225
+ const after = textContent.substring(cursorPos);
13226
+ textNode.textContent = before;
13227
+ const parent = textNode.parentNode;
13228
+ const nextSib = textNode.nextSibling;
13229
+ const span = document.createElement("span");
13230
+ span.contentEditable = "false";
13231
+ span.dataset.mention = "true";
13232
+ span.dataset.id = item.id;
13233
+ span.dataset.name = item.searchLabel || item.name || "";
13234
+ span.dataset.mentionType = item.type;
13235
+ span.style.cssText = mentionStyle(item.type);
13236
+ span.title = "Clique para focar na cena";
13237
+ span.textContent = `@${item.searchLabel || item.name || ""}`;
13238
+ parent.insertBefore(span, nextSib);
13239
+ const trailing = document.createTextNode(after || "\u200B");
13240
+ parent.insertBefore(trailing, span.nextSibling);
13241
+ const newRange = document.createRange();
13242
+ newRange.setStart(trailing, after ? 0 : 1);
13243
+ newRange.collapse(true);
13244
+ sel.removeAllRanges();
13245
+ sel.addRange(newRange);
13246
+ const serialized = domToContent(editorRef.current);
13247
+ onChange(serialized);
13248
+ setMentionState(null);
13249
+ setIsEmpty(false);
13250
+ },
13251
+ [onChange]
13252
+ );
13253
+ const handlePaste = useCallback4(
13254
+ (e) => {
13255
+ e.preventDefault();
13256
+ const text = e.clipboardData.getData("text/plain");
13257
+ if (text) {
13258
+ document.execCommand("insertText", false, text);
13259
+ handleInput();
13260
+ }
13261
+ },
13262
+ [handleInput]
13263
+ );
13264
+ const handleClick = useCallback4(
13265
+ (e) => {
13266
+ const chip = e.target.closest("[data-mention='true']");
13267
+ if (chip && onMentionClick) {
13268
+ e.stopPropagation();
13269
+ const id = chip.dataset.id;
13270
+ const full = searchItems == null ? void 0 : searchItems.find((i) => String(i.id) === String(id));
13271
+ if (full) {
13272
+ onMentionClick(full);
13273
+ } else {
13274
+ onMentionClick({
13275
+ id,
13276
+ name: chip.dataset.name,
13277
+ type: chip.dataset.mentionType,
13278
+ searchLabel: chip.dataset.name
13279
+ });
13226
13280
  }
13227
- .tiptap:focus {
13228
- outline: none;
13281
+ return;
13282
+ }
13283
+ setTimeout(() => {
13284
+ const ctx = getMentionContext();
13285
+ setMentionState(ctx);
13286
+ }, 0);
13287
+ },
13288
+ [onMentionClick, getMentionContext, searchItems]
13289
+ );
13290
+ const handleBlur = useCallback4(() => {
13291
+ setTimeout(() => setMentionState(null), 200);
13292
+ }, []);
13293
+ const handleMentionDismiss = useCallback4(() => {
13294
+ const selection = window.getSelection();
13295
+ if (selection && selection.rangeCount > 0) {
13296
+ const range = selection.getRangeAt(0);
13297
+ const node = range.startContainer;
13298
+ if (node.nodeType === Node.TEXT_NODE) {
13299
+ const textBefore = node.textContent.substring(0, range.startOffset);
13300
+ const atIndex = textBefore.lastIndexOf("@");
13301
+ if (atIndex !== -1) {
13302
+ dismissedTriggerRef.current = { node, atIndex };
13229
13303
  }
13230
- `));
13231
- };
13232
- var MentionEditor_default = MentionEditor;
13304
+ }
13305
+ }
13306
+ setMentionState(null);
13307
+ }, []);
13308
+ return /* @__PURE__ */ React27.createElement("div", { className: "relative" }, isEmpty && !readOnly && /* @__PURE__ */ React27.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__ */ React27.createElement(
13309
+ "div",
13310
+ {
13311
+ ref: editorRef,
13312
+ contentEditable: readOnly ? "false" : "true",
13313
+ suppressContentEditableWarning: true,
13314
+ onInput: handleInput,
13315
+ onKeyDown: handleKeyDown,
13316
+ onPaste: handlePaste,
13317
+ onClick: handleClick,
13318
+ onBlur: handleBlur,
13319
+ 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"}`,
13320
+ style: { overflowWrap: "break-word", wordBreak: "break-word" }
13321
+ }
13322
+ ), mentionState && !readOnly && /* @__PURE__ */ React27.createElement(
13323
+ MentionPopover,
13324
+ {
13325
+ query: mentionState.query,
13326
+ items: searchItems || [],
13327
+ position: mentionState.position,
13328
+ onSelect: insertMention,
13329
+ onClose: handleMentionDismiss
13330
+ }
13331
+ ));
13332
+ }
13233
13333
 
13234
13334
  // src/components/AncestryBoard.jsx
13235
- var GroupItem = ({
13236
- group,
13335
+ var Tooltip = ({ text, children }) => {
13336
+ if (!text) return children;
13337
+ return /* @__PURE__ */ React28.createElement("div", { className: "relative group/tooltip" }, children, /* @__PURE__ */ React28.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));
13338
+ };
13339
+ var BoardItem = ({
13340
+ item,
13237
13341
  index,
13342
+ prevItem,
13343
+ nextItem,
13238
13344
  onUpdate,
13239
13345
  onDelete,
13240
- onAddSubgroup,
13241
13346
  onIndent,
13347
+ onIndentDown,
13242
13348
  onOutdent,
13243
- onPlayAncestry,
13244
- availableMentionItems,
13349
+ onMoveUp,
13350
+ onMoveDown,
13351
+ searchItems,
13352
+ onMentionClick,
13353
+ onEnterFolder,
13245
13354
  canEdit
13246
13355
  }) => {
13247
- const canIndent = index > 0;
13248
- return /* @__PURE__ */ React28.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__ */ React28.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ React28.createElement(
13249
- "div",
13250
- {
13251
- 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"
13252
- },
13253
- /* @__PURE__ */ React28.createElement(
13254
- MentionEditor_default,
13255
- {
13256
- initialValue: group.text,
13257
- onChange: (newText) => {
13258
- if (canEdit) onUpdate(group.id, { ...group, text: newText });
13259
- },
13260
- onPlay: onPlayAncestry,
13261
- availableItems: availableMentionItems,
13262
- canEdit,
13263
- placeholder: "Escreva sobre este grupo... use @ para mencionar"
13264
- }
13265
- ),
13266
- canEdit && /* @__PURE__ */ React28.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__ */ React28.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React28.createElement(
13267
- "button",
13268
- {
13269
- onClick: () => onAddSubgroup(group.id),
13270
- 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",
13271
- title: "Criar Subgrupo"
13272
- },
13273
- /* @__PURE__ */ React28.createElement(FiPlus10, { size: 14 }),
13274
- /* @__PURE__ */ React28.createElement("span", null, "Adicionar Subgrupo")
13275
- )), /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React28.createElement(
13276
- "button",
13277
- {
13278
- onClick: () => onIndent(group.id),
13279
- disabled: !canIndent,
13280
- className: `p-1.5 rounded transition-colors ${!canIndent ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`,
13281
- title: "Aninhar no grupo acima"
13282
- },
13283
- /* @__PURE__ */ React28.createElement(FiArrowRight2, { size: 14 })
13284
- ), /* @__PURE__ */ React28.createElement(
13285
- "button",
13286
- {
13287
- onClick: () => onOutdent(group.id),
13288
- className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
13289
- title: "Desaninhar"
13290
- },
13291
- /* @__PURE__ */ React28.createElement(FiArrowLeft3, { size: 14 })
13292
- ), /* @__PURE__ */ React28.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), /* @__PURE__ */ React28.createElement(
13293
- "button",
13356
+ var _a;
13357
+ const isFolder = item.type === "folder";
13358
+ const canMoveUp = index > 0;
13359
+ const canMoveDown = nextItem !== void 0;
13360
+ const isNested = onOutdent !== void 0;
13361
+ const getIndentUpTooltip = () => {
13362
+ if (!prevItem) return null;
13363
+ const typeLabel = prevItem.type === "folder" ? "folder" : "text_group";
13364
+ return isFolder ? prevItem.type === "folder" ? "Aninhar na pasta acima" : null : `Mover para dentro do ${typeLabel} acima`;
13365
+ };
13366
+ const getIndentDownTooltip = () => {
13367
+ if (!nextItem) return null;
13368
+ const typeLabel = nextItem.type === "folder" ? "folder" : "text_group";
13369
+ return isFolder ? nextItem.type === "folder" ? "Aninhar na pasta abaixo" : null : `Mover para dentro do ${typeLabel} abaixo`;
13370
+ };
13371
+ const showIndentUp = !!getIndentUpTooltip();
13372
+ const showIndentDown = !!getIndentDownTooltip();
13373
+ const handleContentChange = (content) => {
13374
+ onUpdate(item.id, { ...item, content });
13375
+ };
13376
+ const handleNameChange = (e) => {
13377
+ onUpdate(item.id, { ...item, name: e.target.value });
13378
+ };
13379
+ if (isFolder) {
13380
+ return /* @__PURE__ */ React28.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__ */ React28.createElement(
13381
+ "div",
13294
13382
  {
13295
- onClick: () => onDelete(group.id),
13296
- className: "p-1.5 text-slate-600 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors",
13297
- title: "Remover Grupo"
13383
+ onClick: () => onEnterFolder(item.id),
13384
+ 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"
13298
13385
  },
13299
- /* @__PURE__ */ React28.createElement(FiTrash23, { size: 14 })
13300
- )))
13301
- ), group.children && group.children.length > 0 && /* @__PURE__ */ React28.createElement("div", { className: "ml-2" }, group.children.map((childGroup, idx) => /* @__PURE__ */ React28.createElement(
13302
- GroupItem,
13386
+ /* @__PURE__ */ React28.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__ */ React28.createElement(FiFolder3, { size: 20 })),
13387
+ /* @__PURE__ */ React28.createElement("div", { className: "flex-1 min-w-0" }, canEdit ? /* @__PURE__ */ React28.createElement(
13388
+ "input",
13389
+ {
13390
+ type: "text",
13391
+ value: item.name,
13392
+ onChange: handleNameChange,
13393
+ onClick: (e) => e.stopPropagation(),
13394
+ className: "bg-transparent border-none outline-none text-sm font-medium text-slate-200 w-full focus:text-white",
13395
+ placeholder: "Nome da pasta..."
13396
+ }
13397
+ ) : /* @__PURE__ */ React28.createElement("span", { className: "text-sm font-medium text-slate-200" }, item.name), /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-2 mt-0.5" }, /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-wider font-semibold" }, ((_a = item.children) == null ? void 0 : _a.length) || 0, " itens"))),
13398
+ /* @__PURE__ */ React28.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__ */ React28.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__ */ React28.createElement(Tooltip, { text: "Subir" }, /* @__PURE__ */ React28.createElement(
13399
+ "button",
13400
+ {
13401
+ onClick: (e) => {
13402
+ e.stopPropagation();
13403
+ onMoveUp(item.id);
13404
+ },
13405
+ disabled: !canMoveUp,
13406
+ className: `p-1.5 rounded transition-colors ${!canMoveUp ? "text-slate-800 cursor-not-allowed" : "text-slate-400 hover:text-white hover:bg-white/10"}`
13407
+ },
13408
+ /* @__PURE__ */ React28.createElement(FiArrowUp, { size: 14 })
13409
+ )), /* @__PURE__ */ React28.createElement(Tooltip, { text: "Descer" }, /* @__PURE__ */ React28.createElement(
13410
+ "button",
13411
+ {
13412
+ onClick: (e) => {
13413
+ e.stopPropagation();
13414
+ onMoveDown(item.id);
13415
+ },
13416
+ disabled: !canMoveDown,
13417
+ className: `p-1.5 rounded transition-colors ${!canMoveDown ? "text-slate-800 cursor-not-allowed" : "text-slate-400 hover:text-white hover:bg-white/10"}`
13418
+ },
13419
+ /* @__PURE__ */ React28.createElement(FiArrowDown, { size: 14 })
13420
+ )), /* @__PURE__ */ React28.createElement("div", { className: "w-px h-3 bg-white/10 mx-0.5" }), showIndentUp && /* @__PURE__ */ React28.createElement(Tooltip, { text: getIndentUpTooltip() }, /* @__PURE__ */ React28.createElement(
13421
+ "button",
13422
+ {
13423
+ onClick: (e) => {
13424
+ e.stopPropagation();
13425
+ onIndent(item.id);
13426
+ },
13427
+ className: "p-1.5 text-slate-400 hover:text-white hover:bg-white/10 rounded transition-colors"
13428
+ },
13429
+ /* @__PURE__ */ React28.createElement(FiArrowLeft3, { size: 14 })
13430
+ )), showIndentDown && /* @__PURE__ */ React28.createElement(Tooltip, { text: getIndentDownTooltip() }, /* @__PURE__ */ React28.createElement(
13431
+ "button",
13432
+ {
13433
+ onClick: (e) => {
13434
+ e.stopPropagation();
13435
+ onIndentDown(item.id);
13436
+ },
13437
+ className: "p-1.5 text-slate-400 hover:text-white hover:bg-white/10 rounded transition-colors"
13438
+ },
13439
+ /* @__PURE__ */ React28.createElement(FiArrowRight2, { size: 14 })
13440
+ )), (isNested || !showIndentUp && !showIndentDown) && /* @__PURE__ */ React28.createElement(Tooltip, { text: "Desaninhar / Recuar n\xEDvel" }, /* @__PURE__ */ React28.createElement(
13441
+ "button",
13442
+ {
13443
+ onClick: (e) => {
13444
+ e.stopPropagation();
13445
+ onOutdent(item.id);
13446
+ },
13447
+ disabled: !onOutdent,
13448
+ className: `p-1.5 rounded transition-colors ${!onOutdent ? "text-slate-800 cursor-not-allowed" : "text-slate-400 hover:text-white hover:bg-white/10"}`
13449
+ },
13450
+ /* @__PURE__ */ React28.createElement(FiCornerUpRight4, { size: 14 })
13451
+ )), /* @__PURE__ */ React28.createElement("div", { className: "w-px h-3 bg-white/10 mx-0.5" }), /* @__PURE__ */ React28.createElement(Tooltip, { text: "Remover Pasta" }, /* @__PURE__ */ React28.createElement(
13452
+ "button",
13453
+ {
13454
+ onClick: (e) => {
13455
+ e.stopPropagation();
13456
+ onDelete(item.id);
13457
+ },
13458
+ className: "p-1.5 text-slate-500 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors"
13459
+ },
13460
+ /* @__PURE__ */ React28.createElement(FiTrash23, { size: 14 })
13461
+ ))), /* @__PURE__ */ React28.createElement(FiChevronRight7, { className: "text-slate-500 group-hover/folder:translate-x-1 transition-transform" }))
13462
+ ));
13463
+ }
13464
+ return /* @__PURE__ */ React28.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__ */ React28.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ React28.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__ */ React28.createElement(
13465
+ RichGroupEditor,
13466
+ {
13467
+ content: item.content,
13468
+ onChange: handleContentChange,
13469
+ onMentionClick,
13470
+ searchItems,
13471
+ readOnly: !canEdit,
13472
+ placeholder: "Escreva sobre este grupo\u2026 ou @ para mencionar"
13473
+ }
13474
+ ), canEdit && /* @__PURE__ */ React28.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__ */ React28.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React28.createElement(
13475
+ "button",
13476
+ {
13477
+ onClick: () => onAddSubgroup(item.id, "group"),
13478
+ 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",
13479
+ title: "Criar Subgrupo"
13480
+ },
13481
+ /* @__PURE__ */ React28.createElement(FiPlus10, { size: 12 }),
13482
+ /* @__PURE__ */ React28.createElement("span", null, "Subgrupo")
13483
+ )), /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React28.createElement(Tooltip, { text: "Subir" }, /* @__PURE__ */ React28.createElement(
13484
+ "button",
13485
+ {
13486
+ onClick: () => onMoveUp(item.id),
13487
+ disabled: !canMoveUp,
13488
+ className: `p-1.5 rounded transition-colors ${!canMoveUp ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`
13489
+ },
13490
+ /* @__PURE__ */ React28.createElement(FiArrowUp, { size: 14 })
13491
+ )), /* @__PURE__ */ React28.createElement(Tooltip, { text: "Descer" }, /* @__PURE__ */ React28.createElement(
13492
+ "button",
13493
+ {
13494
+ onClick: () => onMoveDown(item.id),
13495
+ disabled: !canMoveDown,
13496
+ className: `p-1.5 rounded transition-colors ${!canMoveDown ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`
13497
+ },
13498
+ /* @__PURE__ */ React28.createElement(FiArrowDown, { size: 14 })
13499
+ )), /* @__PURE__ */ React28.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), showIndentUp && /* @__PURE__ */ React28.createElement(Tooltip, { text: getIndentUpTooltip() }, /* @__PURE__ */ React28.createElement(
13500
+ "button",
13501
+ {
13502
+ onClick: () => onIndent(item.id),
13503
+ className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors"
13504
+ },
13505
+ /* @__PURE__ */ React28.createElement(FiArrowLeft3, { size: 14 })
13506
+ )), showIndentDown && /* @__PURE__ */ React28.createElement(Tooltip, { text: getIndentDownTooltip() }, /* @__PURE__ */ React28.createElement(
13507
+ "button",
13303
13508
  {
13304
- key: childGroup.id,
13509
+ onClick: () => onIndentDown(item.id),
13510
+ className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors"
13511
+ },
13512
+ /* @__PURE__ */ React28.createElement(FiArrowRight2, { size: 14 })
13513
+ )), (isNested || !showIndentUp && !showIndentDown) && /* @__PURE__ */ React28.createElement(Tooltip, { text: "Desaninhar / Recuar n\xEDvel" }, /* @__PURE__ */ React28.createElement(
13514
+ "button",
13515
+ {
13516
+ onClick: () => onOutdent(item.id),
13517
+ disabled: !onOutdent,
13518
+ className: `p-1.5 rounded transition-colors ${!onOutdent ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`
13519
+ },
13520
+ /* @__PURE__ */ React28.createElement(FiCornerUpRight4, { size: 14 })
13521
+ )), /* @__PURE__ */ React28.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), /* @__PURE__ */ React28.createElement(Tooltip, { text: "Remover Grupo" }, /* @__PURE__ */ React28.createElement(
13522
+ "button",
13523
+ {
13524
+ onClick: () => onDelete(item.id),
13525
+ className: "p-1.5 text-slate-600 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors"
13526
+ },
13527
+ /* @__PURE__ */ React28.createElement(FiTrash23, { size: 14 })
13528
+ ))))), item.children && item.children.length > 0 && /* @__PURE__ */ React28.createElement("div", { className: "ml-2" }, item.children.map((child, idx) => /* @__PURE__ */ React28.createElement(
13529
+ BoardItem,
13530
+ {
13531
+ key: child.id,
13305
13532
  index: idx,
13306
- group: childGroup,
13307
- siblings: group.children,
13533
+ prevItem: item.children[idx - 1],
13534
+ nextItem: item.children[idx + 1],
13535
+ item: child,
13308
13536
  onUpdate,
13309
13537
  onDelete,
13310
13538
  onAddSubgroup,
13311
13539
  onIndent,
13540
+ onIndentDown,
13312
13541
  onOutdent,
13313
- onPlayAncestry,
13314
- availableMentionItems,
13542
+ onMoveUp,
13543
+ onMoveDown,
13544
+ searchItems,
13545
+ onMentionClick,
13546
+ onEnterFolder,
13315
13547
  canEdit
13316
13548
  }
13317
13549
  ))));
@@ -13326,140 +13558,181 @@ function AncestryBoard({
13326
13558
  onSave,
13327
13559
  userRole
13328
13560
  }) {
13329
- const [groups, setGroups] = useState26([]);
13330
- const [isLoaded, setIsLoaded] = useState26(false);
13331
- const [saveStatus, setSaveStatus] = useState26("idle");
13332
- const canEdit = useMemo12(() => {
13333
- return userRole !== "viewer";
13334
- }, [userRole]);
13561
+ const [groups, setGroups] = useState27([]);
13562
+ const [isLoaded, setIsLoaded] = useState27(false);
13563
+ const [saveStatus, setSaveStatus] = useState27("idle");
13564
+ const [navigationStack, setNavigationStack] = useState27([]);
13565
+ const lastSavedGroupsRef = useRef20(null);
13566
+ const canEdit = useMemo12(() => userRole !== "viewer", [userRole]);
13335
13567
  useEffect23(() => {
13336
13568
  if (initialGroups && !isLoaded) {
13337
- setGroups(initialGroups);
13569
+ const migrate = (list) => list.map((g) => ({
13570
+ id: g.id || uuidv411(),
13571
+ type: g.type || "group",
13572
+ name: g.name || "",
13573
+ content: Array.isArray(g.content) ? g.content : [],
13574
+ children: migrate(g.children || [])
13575
+ }));
13576
+ setGroups(migrate(initialGroups));
13338
13577
  setIsLoaded(true);
13339
13578
  }
13340
13579
  }, [initialGroups, isLoaded]);
13341
- const nodeNamesMap = useMemo12(() => {
13342
- const map = /* @__PURE__ */ new Map();
13343
- if (availableNodes && Array.isArray(availableNodes)) {
13344
- availableNodes.forEach((node) => {
13345
- if (node && node.id) {
13346
- map.set(String(node.id), node.name);
13347
- }
13580
+ const allSearchableItems = useMemo12(() => {
13581
+ const items = [];
13582
+ availableAncestries.forEach((anc) => {
13583
+ items.push({
13584
+ ...anc,
13585
+ id: String(anc.ancestry_id),
13586
+ type: "ancestry",
13587
+ searchLabel: anc.name || "",
13588
+ searchDesc: anc.description || ""
13348
13589
  });
13349
- }
13350
- return map;
13351
- }, [availableNodes]);
13352
- const availableIds = useMemo12(() => {
13353
- return new Set(availableAncestries.map((a) => String(a.ancestry_id)));
13354
- }, [availableAncestries]);
13355
- const availableMentionItems = useMemo12(() => {
13356
- const ancestries = availableAncestries.map((a) => ({
13357
- id: String(a.ancestry_id),
13358
- ancestry_id: a.ancestry_id,
13359
- name: a.name,
13360
- description: a.description,
13361
- type: "ancestry",
13362
- ancestral_node: a.ancestral_node,
13363
- is_private: a.is_private
13364
- }));
13365
- const nodes = availableNodes.map((n) => {
13590
+ });
13591
+ availableNodes.forEach((node) => {
13592
+ var _a;
13366
13593
  let type = "node";
13367
- if (n.is_quest) type = "quest";
13368
- else if (n.is_version) type = "version";
13369
- return {
13370
- id: String(n.id),
13371
- name: n.name,
13372
- description: n.description,
13594
+ if (node.is_quest) type = "quest";
13595
+ else if ((_a = node.version_node) == null ? void 0 : _a.is_version) type = "version";
13596
+ items.push({
13597
+ ...node,
13598
+ id: String(node.id),
13373
13599
  type,
13374
- raw_node: n
13375
- };
13600
+ searchLabel: node.name || node.raw_title || "",
13601
+ searchDesc: node.description || ""
13602
+ });
13376
13603
  });
13377
- return [...ancestries, ...nodes];
13604
+ return items;
13378
13605
  }, [availableAncestries, availableNodes]);
13379
- const sanitizeGroups = (groupList) => {
13380
- return groupList.map((g) => ({
13381
- id: g.id,
13382
- text: g.text || "",
13383
- children: sanitizeGroups(g.children || [])
13384
- }));
13385
- };
13606
+ const sanitizeGroups = (list) => list.map((g) => ({
13607
+ id: g.id,
13608
+ type: g.type || "group",
13609
+ name: g.name || "",
13610
+ content: g.content || [],
13611
+ children: sanitizeGroups(g.children || [])
13612
+ }));
13386
13613
  useEffect23(() => {
13387
13614
  if (!isLoaded || !onSave) return;
13388
- const timeoutId = setTimeout(async () => {
13615
+ const id = setTimeout(async () => {
13616
+ const currentSanitized = sanitizeGroups(groups);
13617
+ const currentJson = JSON.stringify(currentSanitized);
13618
+ if (currentJson === lastSavedGroupsRef.current) return;
13389
13619
  setSaveStatus("saving");
13390
- const cleanData = sanitizeGroups(groups);
13391
13620
  try {
13392
- await onSave(cleanData);
13621
+ await onSave(currentSanitized);
13622
+ lastSavedGroupsRef.current = currentJson;
13393
13623
  setSaveStatus("saved");
13394
- setTimeout(() => {
13395
- setSaveStatus("idle");
13396
- }, 3e3);
13397
- } catch (error) {
13398
- console.error("Erro ao salvar board:", error);
13624
+ setTimeout(() => setSaveStatus("idle"), 3e3);
13625
+ } catch (err) {
13626
+ console.error("Erro ao salvar board:", err);
13399
13627
  setSaveStatus("error");
13400
13628
  }
13401
13629
  }, 3e3);
13402
- return () => clearTimeout(timeoutId);
13630
+ return () => clearTimeout(id);
13403
13631
  }, [groups, isLoaded, onSave]);
13632
+ const triggerInstantSave = async (updatedGroups) => {
13633
+ if (!onSave) return;
13634
+ const sanitized = sanitizeGroups(updatedGroups);
13635
+ const sanitizedJson = JSON.stringify(sanitized);
13636
+ lastSavedGroupsRef.current = sanitizedJson;
13637
+ setSaveStatus("saving");
13638
+ try {
13639
+ await onSave(sanitized);
13640
+ setSaveStatus("saved");
13641
+ setTimeout(() => setSaveStatus("idle"), 3e3);
13642
+ } catch (err) {
13643
+ console.error("Erro ao salvar board:", err);
13644
+ setSaveStatus("error");
13645
+ }
13646
+ };
13404
13647
  useEffect23(() => {
13405
13648
  if (!isOpen) return;
13406
- const handleKeyDown = (e) => {
13649
+ const handler = (e) => {
13407
13650
  if (e.key === "Escape") {
13408
13651
  onClose();
13409
13652
  e.preventDefault();
13410
13653
  e.stopPropagation();
13411
13654
  }
13412
13655
  };
13413
- window.addEventListener("keydown", handleKeyDown);
13414
- return () => window.removeEventListener("keydown", handleKeyDown);
13656
+ window.addEventListener("keydown", handler);
13657
+ return () => window.removeEventListener("keydown", handler);
13415
13658
  }, [isOpen, onClose]);
13416
- const handleAddRootGroup = () => {
13417
- const newGroup = {
13418
- id: uuidv411(),
13419
- text: "",
13420
- children: []
13421
- };
13422
- setGroups((prev) => [newGroup, ...prev]);
13659
+ const currentPath = useMemo12(() => {
13660
+ const path = [];
13661
+ let currentLevel = groups;
13662
+ for (const folderId of navigationStack) {
13663
+ const folder = currentLevel.find((f) => f.id === folderId);
13664
+ if (folder) {
13665
+ path.push(folder);
13666
+ currentLevel = folder.children;
13667
+ }
13668
+ }
13669
+ return path;
13670
+ }, [groups, navigationStack]);
13671
+ const currentGroups = useMemo12(() => {
13672
+ const lastFolder = currentPath[currentPath.length - 1];
13673
+ return lastFolder ? lastFolder.children : groups;
13674
+ }, [groups, currentPath]);
13675
+ const navigateUp = () => {
13676
+ setNavigationStack((prev) => prev.slice(0, -1));
13677
+ };
13678
+ const navigateTo = (folderId) => {
13679
+ setNavigationStack((prev) => [...prev, folderId]);
13680
+ };
13681
+ const navigateToBreadcrumb = (index) => {
13682
+ setNavigationStack((prev) => prev.slice(0, index + 1));
13683
+ };
13684
+ const navigateHome = () => {
13685
+ setNavigationStack([]);
13423
13686
  };
13424
- const handleAddSubgroup = (parentId) => {
13425
- const newGroup = {
13687
+ const handleAddItem = (type = "group") => {
13688
+ const newItem = {
13426
13689
  id: uuidv411(),
13427
- text: "",
13690
+ type,
13691
+ name: type === "folder" ? "Nova Pasta" : "",
13692
+ content: [],
13428
13693
  children: []
13429
13694
  };
13430
- const addRecursive = (list) => {
13431
- return list.map((g) => {
13432
- if (g.id === parentId) {
13433
- return { ...g, children: [newGroup, ...g.children] };
13434
- }
13435
- return { ...g, children: addRecursive(g.children) };
13436
- });
13437
- };
13438
- setGroups((prev) => addRecursive(prev));
13695
+ if (navigationStack.length === 0) {
13696
+ setGroups((prev) => [...prev, newItem]);
13697
+ } else {
13698
+ const parentId = navigationStack[navigationStack.length - 1];
13699
+ const add = (list) => list.map(
13700
+ (g) => g.id === parentId ? { ...g, children: [...g.children, newItem] } : { ...g, children: add(g.children) }
13701
+ );
13702
+ setGroups((prev) => add(prev));
13703
+ }
13439
13704
  };
13440
- const handleUpdateGroup = (id, newData) => {
13441
- const updateRecursive = (list) => {
13442
- return list.map((g) => {
13443
- if (g.id === id) return newData;
13444
- return { ...g, children: updateRecursive(g.children) };
13445
- });
13446
- };
13447
- setGroups((prev) => updateRecursive(prev));
13705
+ const handleAddSubgroup = (parentId, type = "group") => {
13706
+ const add = (list) => list.map((g) => {
13707
+ if (g.id === parentId) {
13708
+ if (g.type === "group" && type === "folder") return g;
13709
+ const newItem = {
13710
+ id: uuidv411(),
13711
+ type,
13712
+ name: type === "folder" ? "Nova Pasta" : "",
13713
+ content: [],
13714
+ children: []
13715
+ };
13716
+ return { ...g, children: [...g.children, newItem] };
13717
+ }
13718
+ return { ...g, children: add(g.children) };
13719
+ });
13720
+ setGroups((prev) => add(prev));
13448
13721
  };
13449
- const handleDeleteGroup = (id) => {
13450
- const deleteRecursive = (list) => {
13451
- return list.filter((g) => g.id !== id).map((g) => ({
13452
- ...g,
13453
- children: deleteRecursive(g.children)
13454
- }));
13455
- };
13456
- setGroups((prev) => deleteRecursive(prev));
13722
+ const handleUpdateItem = (id, newData) => {
13723
+ const update = (list) => list.map(
13724
+ (g) => g.id === id ? newData : { ...g, children: update(g.children) }
13725
+ );
13726
+ setGroups((prev) => update(prev));
13727
+ };
13728
+ const handleDeleteItem = (id) => {
13729
+ const del = (list) => list.filter((g) => g.id !== id).map((g) => ({ ...g, children: del(g.children) }));
13730
+ setGroups((prev) => del(prev));
13457
13731
  };
13458
13732
  const findGroupAndParent = (list, targetId, parent = null) => {
13459
13733
  for (let i = 0; i < list.length; i++) {
13460
- if (list[i].id === targetId) {
13734
+ if (list[i].id === targetId)
13461
13735
  return { group: list[i], parent, index: i, siblings: list };
13462
- }
13463
13736
  if (list[i].children.length > 0) {
13464
13737
  const found = findGroupAndParent(list[i].children, targetId, list[i]);
13465
13738
  if (found) return found;
@@ -13468,47 +13741,72 @@ function AncestryBoard({
13468
13741
  return null;
13469
13742
  };
13470
13743
  const handleIndent = (id) => {
13471
- setGroups((prev) => {
13472
- const newList = JSON.parse(JSON.stringify(prev));
13473
- const found = findGroupAndParent(newList, id);
13474
- if (!found || found.index === 0) return prev;
13475
- const { group, siblings, index } = found;
13476
- const prevSibling = siblings[index - 1];
13477
- siblings.splice(index, 1);
13478
- prevSibling.children.push(group);
13479
- return newList;
13480
- });
13744
+ const next = JSON.parse(JSON.stringify(groups));
13745
+ const found = findGroupAndParent(next, id);
13746
+ if (!found || found.index === 0) return;
13747
+ const { group, siblings, index } = found;
13748
+ const targetParent = siblings[index - 1];
13749
+ if (group.type === "folder" && targetParent.type === "group") {
13750
+ return;
13751
+ }
13752
+ siblings.splice(index, 1);
13753
+ targetParent.children.push(group);
13754
+ setGroups(next);
13755
+ triggerInstantSave(next);
13481
13756
  };
13482
- const handleOutdent = (id) => {
13483
- setGroups((prev) => {
13484
- const newList = JSON.parse(JSON.stringify(prev));
13485
- const found = findGroupAndParent(newList, id);
13486
- if (!found || !found.parent) return prev;
13487
- const { group, siblings, index, parent } = found;
13488
- siblings.splice(index, 1);
13489
- const parentFound = findGroupAndParent(newList, parent.id);
13490
- if (parentFound) {
13491
- parentFound.siblings.splice(parentFound.index + 1, 0, group);
13492
- } else {
13493
- newList.push(group);
13494
- }
13495
- return newList;
13496
- });
13757
+ const handleIndentDown = (id) => {
13758
+ const next = JSON.parse(JSON.stringify(groups));
13759
+ const found = findGroupAndParent(next, id);
13760
+ if (!found || found.index === found.siblings.length - 1) return;
13761
+ const { group, siblings, index } = found;
13762
+ const targetParent = siblings[index + 1];
13763
+ if (group.type === "folder" && targetParent.type === "group") {
13764
+ return;
13765
+ }
13766
+ siblings.splice(index, 1);
13767
+ targetParent.children.push(group);
13768
+ setGroups(next);
13769
+ triggerInstantSave(next);
13497
13770
  };
13498
- const handlePlayFromGroup = (item) => {
13499
- let fullItem = item;
13500
- if (item.type === "ancestry" || !item.type) {
13501
- fullItem = availableAncestries.find(
13502
- (a) => String(a.ancestry_id) === String(item.id || item.ancestry_id)
13503
- ) || item;
13771
+ const handleOutdent = (id) => {
13772
+ const next = JSON.parse(JSON.stringify(groups));
13773
+ const found = findGroupAndParent(next, id);
13774
+ if (!found || !found.parent) return;
13775
+ const { group, siblings, index, parent } = found;
13776
+ siblings.splice(index, 1);
13777
+ const parentFound = findGroupAndParent(next, parent.id);
13778
+ if (parentFound) {
13779
+ parentFound.siblings.splice(parentFound.index + 1, 0, group);
13504
13780
  } else {
13505
- fullItem = availableNodes.find(
13506
- (n) => String(n.id) === String(item.id)
13507
- ) || item;
13508
- }
13509
- if (fullItem && onSelect) {
13510
- onSelect(fullItem);
13781
+ next.push(group);
13511
13782
  }
13783
+ setGroups(next);
13784
+ triggerInstantSave(next);
13785
+ };
13786
+ const handleMoveUp = (id) => {
13787
+ const next = JSON.parse(JSON.stringify(groups));
13788
+ const found = findGroupAndParent(next, id);
13789
+ if (!found || found.index === 0) return;
13790
+ const { siblings, index } = found;
13791
+ const item = siblings[index];
13792
+ siblings.splice(index, 1);
13793
+ siblings.splice(index - 1, 0, item);
13794
+ setGroups(next);
13795
+ triggerInstantSave(next);
13796
+ };
13797
+ const handleMoveDown = (id) => {
13798
+ const next = JSON.parse(JSON.stringify(groups));
13799
+ const found = findGroupAndParent(next, id);
13800
+ if (!found || found.index === found.siblings.length - 1) return;
13801
+ const { siblings, index } = found;
13802
+ const item = siblings[index];
13803
+ siblings.splice(index, 1);
13804
+ siblings.splice(index + 1, 0, item);
13805
+ setGroups(next);
13806
+ triggerInstantSave(next);
13807
+ };
13808
+ const handleMentionClick = (item) => {
13809
+ if (onSelect) onSelect(item);
13512
13810
  };
13513
13811
  if (!isOpen) return null;
13514
13812
  return /* @__PURE__ */ React28.createElement(
@@ -13520,61 +13818,85 @@ function AncestryBoard({
13520
13818
  /* @__PURE__ */ React28.createElement(
13521
13819
  "div",
13522
13820
  {
13523
- 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",
13821
+ className: "bg-slate-950 border border-white/10 rounded-xl w-[98vw] h-[97vh] flex flex-col shadow-2xl overflow-hidden animate-in fade-in zoom-in-95 duration-200",
13524
13822
  onClick: (e) => e.stopPropagation()
13525
13823
  },
13526
- /* @__PURE__ */ React28.createElement("div", { className: "h-14 px-6 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React28.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ React28.createElement(FiLayers7, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React28.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ React28.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__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement(
13527
- FiLoader5,
13824
+ /* @__PURE__ */ React28.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React28.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ React28.createElement(FiLayers7, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React28.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ React28.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__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement(FiLoader5, { className: "animate-spin text-indigo-400", size: 12 }), /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement(FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-3" }, canEdit && /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement(
13825
+ "button",
13528
13826
  {
13529
- className: "animate-spin text-indigo-400",
13530
- size: 12
13531
- }
13532
- ), /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement(FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-3" }, canEdit && /* @__PURE__ */ React28.createElement(
13827
+ onClick: () => handleAddItem("folder"),
13828
+ 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"
13829
+ },
13830
+ /* @__PURE__ */ React28.createElement(FiPlus10, { size: 14, className: "text-amber-400" }),
13831
+ /* @__PURE__ */ React28.createElement("span", { className: "hidden sm:inline" }, "Nova Pasta")
13832
+ ), /* @__PURE__ */ React28.createElement(
13533
13833
  "button",
13534
13834
  {
13535
- onClick: handleAddRootGroup,
13536
- 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 "
13835
+ onClick: () => handleAddItem("group"),
13836
+ 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"
13537
13837
  },
13538
13838
  /* @__PURE__ */ React28.createElement(FiPlus10, { size: 14, className: "text-indigo-400" }),
13539
13839
  /* @__PURE__ */ React28.createElement("span", { className: "hidden sm:inline" }, "Novo Grupo")
13540
- ), /* @__PURE__ */ React28.createElement(
13840
+ )), /* @__PURE__ */ React28.createElement(
13541
13841
  "button",
13542
13842
  {
13543
13843
  onClick: onClose,
13544
13844
  className: "w-8 h-8 rounded-lg flex items-center justify-center text-slate-400 hover:text-white hover:bg-white/10 transition-colors"
13545
13845
  },
13546
- "\xD7"
13846
+ /* @__PURE__ */ React28.createElement(FiX8, { size: 16 })
13547
13847
  ))),
13548
- /* @__PURE__ */ React28.createElement("div", { className: "flex flex-1 overflow-hidden bg-slate-900/20" }, /* @__PURE__ */ React28.createElement("div", { className: "flex flex-col flex-1" }, /* @__PURE__ */ React28.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__ */ React28.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__ */ React28.createElement(FiLayers7, { size: 40, className: "opacity-10 text-indigo-400" }), /* @__PURE__ */ React28.createElement("div", { className: "text-center space-y-1" }, /* @__PURE__ */ React28.createElement("p", { className: "text-sm font-medium text-slate-300" }, "Nenhum grupo criado"), /* @__PURE__ */ React28.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__ */ React28.createElement(
13848
+ /* @__PURE__ */ React28.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__ */ React28.createElement(
13549
13849
  "button",
13550
13850
  {
13551
- onClick: handleAddRootGroup,
13552
- 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"
13851
+ onClick: navigateHome,
13852
+ className: `hover:text-white transition-colors flex items-center gap-1 ${navigationStack.length === 0 ? "text-indigo-400" : ""}`
13553
13853
  },
13554
- "Criar Primeiro Grupo"
13555
- )) : groups.map((group, index) => /* @__PURE__ */ React28.createElement(
13556
- GroupItem,
13854
+ "Home"
13855
+ ), currentPath.map((folder, idx) => /* @__PURE__ */ React28.createElement(React28.Fragment, { key: folder.id }, /* @__PURE__ */ React28.createElement(FiChevronRight7, { size: 10, className: "shrink-0" }), /* @__PURE__ */ React28.createElement(
13856
+ "button",
13857
+ {
13858
+ onClick: () => navigateToBreadcrumb(idx),
13859
+ className: `hover:text-white transition-colors ${idx === currentPath.length - 1 ? "text-indigo-400" : ""}`
13860
+ },
13861
+ folder.name || "Sem Nome"
13862
+ ))), navigationStack.length > 0 && /* @__PURE__ */ React28.createElement("div", { className: "ml-auto" }, /* @__PURE__ */ React28.createElement(
13863
+ "button",
13864
+ {
13865
+ onClick: navigateUp,
13866
+ 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"
13867
+ },
13868
+ /* @__PURE__ */ React28.createElement(FiArrowLeft3, { size: 10 }),
13869
+ /* @__PURE__ */ React28.createElement("span", null, "Voltar")
13870
+ ))),
13871
+ /* @__PURE__ */ React28.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar bg-slate-950/30 p-6 space-y-4" }, currentGroups.length === 0 ? /* @__PURE__ */ React28.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__ */ React28.createElement(FiLayers7, { size: 24, className: "opacity-20" }), /* @__PURE__ */ React28.createElement("p", { className: "text-xs text-center px-4" }, canEdit ? /* @__PURE__ */ React28.createElement(React28.Fragment, null, "Esta pasta est\xE1 vazia.", /* @__PURE__ */ React28.createElement("br", null), "Use os bot\xF5es acima para criar conte\xFAdo.") : /* @__PURE__ */ React28.createElement(React28.Fragment, null, "Nenhum conte\xFAdo dispon\xEDvel nesta pasta."))) : currentGroups.map((item, index) => /* @__PURE__ */ React28.createElement(
13872
+ BoardItem,
13557
13873
  {
13558
- key: group.id,
13874
+ key: item.id,
13559
13875
  index,
13560
- group,
13561
- onUpdate: handleUpdateGroup,
13562
- onDelete: handleDeleteGroup,
13876
+ prevItem: currentGroups[index - 1],
13877
+ nextItem: currentGroups[index + 1],
13878
+ item,
13879
+ onUpdate: handleUpdateItem,
13880
+ onDelete: handleDeleteItem,
13563
13881
  onAddSubgroup: handleAddSubgroup,
13564
13882
  onIndent: handleIndent,
13883
+ onIndentDown: handleIndentDown,
13565
13884
  onOutdent: handleOutdent,
13566
- onPlayAncestry: handlePlayFromGroup,
13567
- availableMentionItems,
13885
+ onMoveUp: handleMoveUp,
13886
+ onMoveDown: handleMoveDown,
13887
+ searchItems: allSearchableItems,
13888
+ onMentionClick: handleMentionClick,
13889
+ onEnterFolder: navigateTo,
13568
13890
  canEdit
13569
13891
  }
13570
- ))))),
13571
- /* @__PURE__ */ React28.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__ */ React28.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React28.createElement("span", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ React28.createElement("div", { className: "w-1.5 h-1.5 rounded-full bg-indigo-500" }), " ", availableMentionItems.length, " RECURSOS DISPON\xCDVEIS"), /* @__PURE__ */ React28.createElement("span", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ React28.createElement("div", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500" }), " ", groups.length, " GRUPOS PRINCIPAIS")), /* @__PURE__ */ React28.createElement("span", { className: "text-slate-700" }, "X.VIEW :: ANCESTRY BOARD ENGINE"))
13892
+ ))),
13893
+ /* @__PURE__ */ React28.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__ */ React28.createElement("span", null, currentGroups.length, " itens nesta visualiza\xE7\xE3o"))
13572
13894
  )
13573
13895
  );
13574
13896
  }
13575
13897
 
13576
13898
  // src/components/MilestoneManager.jsx
13577
- import React29, { useState as useState27 } from "react";
13899
+ import React29, { useState as useState28 } from "react";
13578
13900
  import { FiTarget as FiTarget4, FiPlus as FiPlus11, FiTrash2 as FiTrash24, FiX as FiX9, FiCheck as FiCheck12, FiPauseCircle, FiPlayCircle } from "react-icons/fi";
13579
13901
  function MilestoneManager({
13580
13902
  isOpen,
@@ -13583,7 +13905,7 @@ function MilestoneManager({
13583
13905
  onUpdateMilestones = () => {
13584
13906
  }
13585
13907
  }) {
13586
- const [newMilestoneName, setNewMilestoneName] = useState27("");
13908
+ const [newMilestoneName, setNewMilestoneName] = useState28("");
13587
13909
  const handleAddMilestone = () => {
13588
13910
  if (!newMilestoneName.trim()) return;
13589
13911
  const newMilestone = {
@@ -13764,7 +14086,7 @@ function XViewScene({
13764
14086
  check_user_permission,
13765
14087
  get_view_members
13766
14088
  }) {
13767
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
14089
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
13768
14090
  const { data: session, status } = useSession();
13769
14091
  const router = useRouter();
13770
14092
  const searchParams = useSearchParams();
@@ -13836,45 +14158,45 @@ function XViewScene({
13836
14158
  }
13837
14159
  return null;
13838
14160
  }, [ownerId, sceneConfigId]);
13839
- const sceneDataRef = useRef19(null);
13840
- const parentDataRef = useRef19(null);
13841
- const ancestryDataRef = useRef19(null);
13842
- const [isLoading, setIsLoading] = useState28(true);
13843
- const [permissionStatus, setPermissionStatus] = useState28("loading");
13844
- const [userPermissionRole, setUserPermissionRole] = useState28(null);
13845
- const [viewMembers, setViewMembers] = useState28([]);
13846
- const [isInitialized, setIsInitialized] = useState28(false);
13847
- const [sceneVersion, setSceneVersion] = useState28(0);
13848
- const [contextMenu, setContextMenu] = useState28({
14161
+ const sceneDataRef = useRef21(null);
14162
+ const parentDataRef = useRef21(null);
14163
+ const ancestryDataRef = useRef21(null);
14164
+ const [isLoading, setIsLoading] = useState29(true);
14165
+ const [permissionStatus, setPermissionStatus] = useState29("loading");
14166
+ const [userPermissionRole, setUserPermissionRole] = useState29(null);
14167
+ const [viewMembers, setViewMembers] = useState29([]);
14168
+ const [isInitialized, setIsInitialized] = useState29(false);
14169
+ const [sceneVersion, setSceneVersion] = useState29(0);
14170
+ const [contextMenu, setContextMenu] = useState29({
13849
14171
  visible: false,
13850
14172
  x: 0,
13851
14173
  y: 0,
13852
14174
  nodeData: null
13853
14175
  });
13854
- const [multiContextMenu, setMultiContextMenu] = useState28({
14176
+ const [multiContextMenu, setMultiContextMenu] = useState29({
13855
14177
  visible: false,
13856
14178
  x: 0,
13857
14179
  y: 0,
13858
14180
  nodeIds: null
13859
14181
  });
13860
- const [relationshipMenu, setRelationshipMenu] = useState28({
14182
+ const [relationshipMenu, setRelationshipMenu] = useState29({
13861
14183
  visible: false,
13862
14184
  x: 0,
13863
14185
  y: 0,
13864
14186
  linkObject: null
13865
14187
  });
13866
- const [creationMode, setCreationMode] = useState28({
14188
+ const [creationMode, setCreationMode] = useState29({
13867
14189
  isActive: false,
13868
14190
  sourceNodeData: null
13869
14191
  });
13870
- const [versionMode, setVersionMode] = useState28({
14192
+ const [versionMode, setVersionMode] = useState29({
13871
14193
  isActive: false,
13872
14194
  sourceNodeData: null
13873
14195
  });
13874
- const [questMode, setQuestMode] = useState28({ isActive: false });
13875
- const [hasFocusedInitial, setHasFocusedInitial] = useState28(false);
13876
- const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = useState28(false);
13877
- const [ancestryMode, setAncestryMode] = useState28({
14196
+ const [questMode, setQuestMode] = useState29({ isActive: false });
14197
+ const [hasFocusedInitial, setHasFocusedInitial] = useState29(false);
14198
+ const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = useState29(false);
14199
+ const [ancestryMode, setAncestryMode] = useState29({
13878
14200
  isActive: false,
13879
14201
  tree: null,
13880
14202
  selectedParentId: null,
@@ -13885,42 +14207,42 @@ function XViewScene({
13885
14207
  ancestryDescriptionSections: [],
13886
14208
  isAddingNodes: false
13887
14209
  });
13888
- const [readingMode, setReadingMode] = useState28({
14210
+ const [readingMode, setReadingMode] = useState29({
13889
14211
  isActive: false,
13890
14212
  ancestry: null,
13891
14213
  branchStack: [],
13892
14214
  autoAbstraction: false
13893
14215
  });
13894
- const [formPosition, setFormPosition] = useState28({
14216
+ const [formPosition, setFormPosition] = useState29({
13895
14217
  left: 16,
13896
14218
  top: 16,
13897
14219
  opacity: 0
13898
14220
  });
13899
- const [detailsNode, setDetailsNode] = useState28(null);
13900
- const [detailsLink, setDetailsLink] = useState28(null);
13901
- const [ancestryLinkDetails, setAncestryLinkDetails] = useState28(null);
13902
- const [imageViewer, setImageViewer] = useState28({
14221
+ const [detailsNode, setDetailsNode] = useState29(null);
14222
+ const [detailsLink, setDetailsLink] = useState29(null);
14223
+ const [ancestryLinkDetails, setAncestryLinkDetails] = useState29(null);
14224
+ const [imageViewer, setImageViewer] = useState29({
13903
14225
  visible: false,
13904
14226
  images: [],
13905
14227
  startIndex: 0
13906
14228
  });
13907
- const [editingAncestryRel, setEditingAncestryRel] = useState28({
14229
+ const [editingAncestryRel, setEditingAncestryRel] = useState29({
13908
14230
  visible: false,
13909
14231
  data: null,
13910
14232
  path: null
13911
14233
  });
13912
- const [isImportModalOpen, setIsImportModalOpen] = useState28(false);
13913
- const [importSuccessMessage, setImportSuccessMessage] = useState28("");
13914
- const [invalidTargetError, setInvalidTargetError] = useState28(null);
13915
- const [highlightedNodeId, setHighlightedNodeId] = useState28(null);
13916
- const [isAncestryBoardOpen, setIsAncestryBoardOpen] = useState28(false);
13917
- const [ancestryBoardData, setAncestryBoardData] = useState28([]);
13918
- const [isSidebarOpen, setIsSidebarOpen] = useState28(false);
13919
- const [isMilestoneManagerOpen, setIsMilestoneManagerOpen] = useState28(false);
13920
- const mountRef = useRef19(null);
13921
- const tooltipRef = useRef19(null);
13922
- const formRef = useRef19(null);
13923
- const stateRef = useRef19({
14234
+ const [isImportModalOpen, setIsImportModalOpen] = useState29(false);
14235
+ const [importSuccessMessage, setImportSuccessMessage] = useState29("");
14236
+ const [invalidTargetError, setInvalidTargetError] = useState29(null);
14237
+ const [highlightedNodeId, setHighlightedNodeId] = useState29(null);
14238
+ const [isAncestryBoardOpen, setIsAncestryBoardOpen] = useState29(false);
14239
+ const [ancestryBoardData, setAncestryBoardData] = useState29([]);
14240
+ const [isSidebarOpen, setIsSidebarOpen] = useState29(false);
14241
+ const [isMilestoneManagerOpen, setIsMilestoneManagerOpen] = useState29(false);
14242
+ const mountRef = useRef21(null);
14243
+ const tooltipRef = useRef21(null);
14244
+ const formRef = useRef21(null);
14245
+ const stateRef = useRef21({
13924
14246
  readMode: {
13925
14247
  currentMaxIndex: 0,
13926
14248
  progressMap: {}
@@ -14010,10 +14332,10 @@ function XViewScene({
14010
14332
  }
14011
14333
  stateRef.current.nodeIdToParentFileMap = map;
14012
14334
  }, [isInitialized, sceneVersion]);
14013
- const handleNavigateBack = useCallback4(() => {
14335
+ const handleNavigateBack = useCallback5(() => {
14014
14336
  router.push("/dashboard/scenes");
14015
14337
  }, [router]);
14016
- const handleConfirmImport = useCallback4(
14338
+ const handleConfirmImport = useCallback5(
14017
14339
  async (importPayload) => {
14018
14340
  var _a2, _b2;
14019
14341
  let files = [];
@@ -14141,7 +14463,7 @@ function XViewScene({
14141
14463
  const handleOpenImageViewer = (images, startIndex) => {
14142
14464
  setImageViewer({ visible: true, images, startIndex });
14143
14465
  };
14144
- const tweenToTarget = useCallback4(
14466
+ const tweenToTarget = useCallback5(
14145
14467
  (target, zoomFactor = 1, forcedDirection = null) => {
14146
14468
  const { camera, controls, tweenGroup } = stateRef.current;
14147
14469
  if (!camera || !controls || !tweenGroup) return;
@@ -14167,7 +14489,7 @@ function XViewScene({
14167
14489
  if (!t || typeof t.closest !== "function") return false;
14168
14490
  return !!t.closest(".ui-overlay");
14169
14491
  };
14170
- const buildFullAncestryTree = useCallback4(
14492
+ const buildFullAncestryTree = useCallback5(
14171
14493
  (idTree, nodes, ancestries = []) => {
14172
14494
  if (!idTree) return null;
14173
14495
  const nodeMap = new Map(nodes.map((n) => [String(n.id), n]));
@@ -14252,7 +14574,7 @@ function XViewScene({
14252
14574
  },
14253
14575
  []
14254
14576
  );
14255
- const handleActivateTimeline = useCallback4(() => {
14577
+ const handleActivateTimeline = useCallback5(() => {
14256
14578
  const { nodeObjects, tweenGroup, timelineIntervalsGroup } = stateRef.current;
14257
14579
  if (!nodeObjects || !tweenGroup || !timelineIntervalsGroup) return;
14258
14580
  while (timelineIntervalsGroup.children.length > 0) {
@@ -14412,7 +14734,7 @@ function XViewScene({
14412
14734
  }
14413
14735
  });
14414
14736
  }, []);
14415
- const handleVersionTimeline = useCallback4((sourceMesh, versionMeshes) => {
14737
+ const handleVersionTimeline = useCallback5((sourceMesh, versionMeshes) => {
14416
14738
  const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
14417
14739
  if (!tweenGroup || !timelineIntervalsGroup || versionMeshes.length === 0)
14418
14740
  return;
@@ -14654,12 +14976,12 @@ function XViewScene({
14654
14976
  focusNodeId,
14655
14977
  focusAncestryId
14656
14978
  ]);
14657
- const isNodeInView = useCallback4((id) => {
14979
+ const isNodeInView = useCallback5((id) => {
14658
14980
  const key = String(id);
14659
14981
  const objs = stateRef.current.nodeObjects || {};
14660
14982
  return !!objs[key];
14661
14983
  }, []);
14662
- const addOrUpdateNodeMesh = useCallback4(
14984
+ const addOrUpdateNodeMesh = useCallback5(
14663
14985
  (nodeData, position, suppressVersionUpdate = false, forceShowQuestDate = false) => {
14664
14986
  const {
14665
14987
  graphGroup,
@@ -14700,7 +15022,7 @@ function XViewScene({
14700
15022
  },
14701
15023
  []
14702
15024
  );
14703
- const handleActivateQuestLens = useCallback4(({ assigneeId, status: status2, milestoneId }) => {
15025
+ const handleActivateQuestLens = useCallback5(({ assigneeId, status: status2, milestoneId }) => {
14704
15026
  var _a2;
14705
15027
  const { nodeObjects, tweenGroup, graphGroup } = stateRef.current;
14706
15028
  if (!nodeObjects || !tweenGroup || !graphGroup) return;
@@ -15670,7 +15992,7 @@ function XViewScene({
15670
15992
  get_scene_view_data,
15671
15993
  save_view_data
15672
15994
  ]);
15673
- const handleGhostNodeImageChange = useCallback4((useImage, imageUrl) => {
15995
+ const handleGhostNodeImageChange = useCallback5((useImage, imageUrl) => {
15674
15996
  const {
15675
15997
  node: ghostNode,
15676
15998
  line: ghostLine,
@@ -15718,7 +16040,7 @@ function XViewScene({
15718
16040
  aura: newGhostNode.getObjectByName("aura")
15719
16041
  };
15720
16042
  }, []);
15721
- const handleGhostNodeIntensityChange = useCallback4((newIntensity) => {
16043
+ const handleGhostNodeIntensityChange = useCallback5((newIntensity) => {
15722
16044
  const { node: ghostNode, aura: ghostAura } = stateRef.current.ghostElements;
15723
16045
  if (!ghostNode) return;
15724
16046
  const adjustedIntensity = newIntensity + MIN_VISIBILITY_INTENSITY;
@@ -15739,7 +16061,7 @@ function XViewScene({
15739
16061
  ghostAura.material.opacity = Math.min(0.8, newIntensity * 0.15);
15740
16062
  }
15741
16063
  }, []);
15742
- const handleDetailNodeIntensityChange = useCallback4(
16064
+ const handleDetailNodeIntensityChange = useCallback5(
15743
16065
  (nodeId, newIntensity) => {
15744
16066
  const mesh = stateRef.current.nodeObjects[String(nodeId)];
15745
16067
  if (!mesh) return;
@@ -15895,7 +16217,7 @@ function XViewScene({
15895
16217
  mountRef.current.style.cursor = "default";
15896
16218
  }
15897
16219
  };
15898
- const handleAncestryTreeUpdate = useCallback4((newTree, extraData = null) => {
16220
+ const handleAncestryTreeUpdate = useCallback5((newTree, extraData = null) => {
15899
16221
  setAncestryMode((prev) => {
15900
16222
  const prevTreeStr = JSON.stringify(prev.tree);
15901
16223
  const newTreeStr = JSON.stringify(newTree);
@@ -15979,7 +16301,7 @@ function XViewScene({
15979
16301
  const handleStartVersioning = (nodeData) => {
15980
16302
  userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
15981
16303
  };
15982
- const handleStartQuestQuick = useCallback4((questNode) => {
16304
+ const handleStartQuestQuick = useCallback5((questNode) => {
15983
16305
  var _a2, _b2, _c2;
15984
16306
  if (!questNode || !actionHandlerContext) return;
15985
16307
  const allParentNodes = Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes || []);
@@ -16014,7 +16336,7 @@ function XViewScene({
16014
16336
  }
16015
16337
  setDetailsNode(updatedNode);
16016
16338
  }, [session, actionHandlerContext]);
16017
- const handleFinishQuestQuick = useCallback4((questNode) => {
16339
+ const handleFinishQuestQuick = useCallback5((questNode) => {
16018
16340
  var _a2, _b2, _c2, _d2;
16019
16341
  if (!questNode || !actionHandlerContext) return;
16020
16342
  const {
@@ -16088,7 +16410,7 @@ function XViewScene({
16088
16410
  setDetailsNode(null);
16089
16411
  }
16090
16412
  }, [session, actionHandlerContext, viewMembers, detailsNode]);
16091
- const handleCancelQuest = useCallback4(() => {
16413
+ const handleCancelQuest = useCallback5(() => {
16092
16414
  const { graphGroup, ghostElements } = stateRef.current;
16093
16415
  if (ghostElements.node && graphGroup) {
16094
16416
  if (ghostElements.node.userData.labelObject) {
@@ -16147,13 +16469,15 @@ function XViewScene({
16147
16469
  try {
16148
16470
  await actions.save_view_data(sceneSaveUrl2, sceneFileData);
16149
16471
  sceneDataRef2.current.quest_counter = currentCounter + 1;
16150
- sceneDataRef2.current.quest_nodes = sceneFileData.quest_nodes;
16151
- sceneDataRef2.current.quest_links = sceneFileData.quest_links;
16152
16472
  stateRef2.current.nodeIdToParentFileMap.set(String(newNode.id), {
16153
16473
  parentFileId: sceneConfigId2,
16154
16474
  ownerId: ownerId2,
16155
16475
  datasetName: "Quests Internas (View)"
16156
16476
  });
16477
+ sceneDataRef2.current.quest_nodes = [...graphDataRef.current[sceneConfigId2].nodes];
16478
+ if (parentDataRef.current[sceneConfigId2]) {
16479
+ parentDataRef.current[sceneConfigId2].nodes = [...graphDataRef.current[sceneConfigId2].nodes];
16480
+ }
16157
16481
  const finalPosition = stateRef2.current.ghostElements.node ? stateRef2.current.ghostElements.node.position.clone() : stateRef2.current.controls.target.clone();
16158
16482
  const { graphGroup, ghostElements } = stateRef2.current;
16159
16483
  if (ghostElements.node && graphGroup) {
@@ -16243,7 +16567,7 @@ function XViewScene({
16243
16567
  }
16244
16568
  userActionHandlers.handleCancelConnection(context);
16245
16569
  };
16246
- const handleClearAncestryVisuals = useCallback4((ancestryId) => {
16570
+ const handleClearAncestryVisuals = useCallback5((ancestryId) => {
16247
16571
  const { renderedAncestries, ancestryGroup } = stateRef.current;
16248
16572
  const renderIndex = renderedAncestries.findIndex(
16249
16573
  (a) => String(a.id) === String(ancestryId)
@@ -16261,7 +16585,7 @@ function XViewScene({
16261
16585
  );
16262
16586
  }
16263
16587
  }, []);
16264
- const handleRenderAncestry = useCallback4(
16588
+ const handleRenderAncestry = useCallback5(
16265
16589
  async (ancestryObject, allowedSectionIds = null, activeSectionIdForFocus = null, baseRotation = 0, forceReprocess = true) => {
16266
16590
  setContextMenu(
16267
16591
  (prev) => prev.visible ? { ...prev, visible: false } : prev
@@ -16755,7 +17079,7 @@ function XViewScene({
16755
17079
  ancestryMode.isActive
16756
17080
  ]
16757
17081
  );
16758
- const handleRenderAbstractionTree = useCallback4(
17082
+ const handleRenderAbstractionTree = useCallback5(
16759
17083
  (ancestryObject, targetNodeId = null) => {
16760
17084
  setContextMenu(
16761
17085
  (prev) => prev.visible ? { ...prev, visible: false } : prev
@@ -16843,7 +17167,7 @@ function XViewScene({
16843
17167
  handleClearAncestryVisuals
16844
17168
  ]
16845
17169
  );
16846
- const handleReadModeBranchNav = useCallback4(
17170
+ const handleReadModeBranchNav = useCallback5(
16847
17171
  (nodeId, action, direction = "right") => {
16848
17172
  const { ancestry, branchStack } = readingMode;
16849
17173
  if (!ancestry || !ancestry.tree) return;
@@ -17021,7 +17345,7 @@ function XViewScene({
17021
17345
  },
17022
17346
  [readingMode, handleRenderAncestry, buildFullAncestryTree, tweenToTarget]
17023
17347
  );
17024
- const handleReadModeHighlight = useCallback4((nodeId) => {
17348
+ const handleReadModeHighlight = useCallback5((nodeId) => {
17025
17349
  if (stateRef.current.highlightedNodeId !== nodeId) {
17026
17350
  stateRef.current.highlightedNodeId = nodeId;
17027
17351
  }
@@ -17144,7 +17468,7 @@ function XViewScene({
17144
17468
  buildFullAncestryTree,
17145
17469
  sceneVersion
17146
17470
  ]);
17147
- const handleStartReadingAncestry = useCallback4(
17471
+ const handleStartReadingAncestry = useCallback5(
17148
17472
  async (ancestryObject, renderMode = "full") => {
17149
17473
  setContextMenu(
17150
17474
  (prev) => prev.visible ? { ...prev, visible: false } : prev
@@ -17187,7 +17511,7 @@ function XViewScene({
17187
17511
  },
17188
17512
  [handleRenderAncestry, handleRenderAbstractionTree]
17189
17513
  );
17190
- const handleReadModeSectionChange = useCallback4(
17514
+ const handleReadModeSectionChange = useCallback5(
17191
17515
  (activeSectionId) => {
17192
17516
  const { ancestry, branchStack } = readingMode;
17193
17517
  if (!ancestry || !readingMode.isActive) return;
@@ -17272,10 +17596,10 @@ function XViewScene({
17272
17596
  ancestryDataRef.current
17273
17597
  ]
17274
17598
  );
17275
- const handleCloseReadMode = useCallback4(() => {
17599
+ const handleCloseReadMode = useCallback5(() => {
17276
17600
  setReadingMode({ isActive: false, ancestry: null, branchStack: [] });
17277
17601
  }, []);
17278
- const handleAncestrySectionChange = useCallback4(
17602
+ const handleAncestrySectionChange = useCallback5(
17279
17603
  (activeSectionId, ancestryOverride = null, rotation = 0) => {
17280
17604
  var _a2, _b2;
17281
17605
  const currentMode = stateRef.current.ancestry;
@@ -17337,7 +17661,7 @@ function XViewScene({
17337
17661
  },
17338
17662
  [handleRenderAncestry]
17339
17663
  );
17340
- const handleEditAncestry = useCallback4(
17664
+ const handleEditAncestry = useCallback5(
17341
17665
  async (ancestryObject) => {
17342
17666
  setContextMenu(
17343
17667
  (prev) => prev.visible ? { ...prev, visible: false } : prev
@@ -17392,7 +17716,7 @@ function XViewScene({
17392
17716
  const handleSelectAncestryParent = (nodeId) => {
17393
17717
  setAncestryMode((prev) => ({ ...prev, selectedParentId: nodeId }));
17394
17718
  };
17395
- const handleRemoveFromAncestry = useCallback4((pathToRemove) => {
17719
+ const handleRemoveFromAncestry = useCallback5((pathToRemove) => {
17396
17720
  if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) {
17397
17721
  console.warn("Tentativa de remover a raiz ou caminho inv\xE1lido.");
17398
17722
  return;
@@ -17417,7 +17741,7 @@ function XViewScene({
17417
17741
  return { ...prev, tree: newTree };
17418
17742
  });
17419
17743
  }, []);
17420
- const handleSaveAncestry = useCallback4(
17744
+ const handleSaveAncestry = useCallback5(
17421
17745
  async (ancestryName, ancestryDescription, ancestrySections, keepOpen = false, treeOverride = null, ancestryCustomProps = {}) => {
17422
17746
  const treeToUse = treeOverride || ancestryMode.tree;
17423
17747
  const { isEditMode, currentAncestryId } = ancestryMode;
@@ -17653,7 +17977,7 @@ function XViewScene({
17653
17977
  });
17654
17978
  setEditingAncestryRel({ visible: false, data: null, path: null });
17655
17979
  };
17656
- const handleDeleteAncestry = useCallback4(
17980
+ const handleDeleteAncestry = useCallback5(
17657
17981
  async (ancestryIdToDelete) => {
17658
17982
  if (!ancestryIdToDelete) {
17659
17983
  alert("ID da ancestralidade n\xE3o encontrado.");
@@ -17735,28 +18059,70 @@ function XViewScene({
17735
18059
  },
17736
18060
  [save_view_data, delete_file_action]
17737
18061
  );
17738
- const handleOpenAncestryBoard = useCallback4(() => {
18062
+ const handleOpenAncestryBoard = useCallback5(() => {
17739
18063
  setIsAncestryBoardOpen(true);
17740
18064
  }, []);
17741
- const handleSelectAncestryFromBoard = useCallback4(
18065
+ const allAvailableNodes = useMemo13(() => {
18066
+ if (!parentDataRef.current) return [];
18067
+ return Object.values(parentDataRef.current).flatMap(
18068
+ (fileData) => fileData.nodes || []
18069
+ );
18070
+ }, [sceneVersion, isInitialized]);
18071
+ const allAvailableAncestries = useMemo13(() => {
18072
+ return ancestryDataRef.current || [];
18073
+ }, [sceneVersion, isInitialized]);
18074
+ const handleOpenReference = useCallback5(
18075
+ (referenceData) => {
18076
+ const { type, id } = referenceData;
18077
+ if (type === "node") {
18078
+ const targetNode = allAvailableNodes.find(
18079
+ (n) => String(n.id) === String(id)
18080
+ );
18081
+ if (targetNode) {
18082
+ setAncestryLinkDetails(null);
18083
+ setDetailsLink(null);
18084
+ setDetailsNode(targetNode);
18085
+ const sceneMesh = stateRef.current.nodeObjects[String(id)];
18086
+ if (sceneMesh) {
18087
+ tweenToTarget(sceneMesh);
18088
+ }
18089
+ } else {
18090
+ alert("Node original n\xE3o encontrado neste contexto.");
18091
+ }
18092
+ } else if (type === "ancestry") {
18093
+ const targetAncestry = allAvailableAncestries.find(
18094
+ (a) => String(a.ancestry_id) === String(id)
18095
+ );
18096
+ if (targetAncestry) {
18097
+ setDetailsNode(null);
18098
+ setDetailsLink(null);
18099
+ setAncestryLinkDetails(null);
18100
+ handleEditAncestry(targetAncestry);
18101
+ } else {
18102
+ alert("Ancestralidade original n\xE3o encontrada neste contexto.");
18103
+ }
18104
+ }
18105
+ },
18106
+ [
18107
+ allAvailableNodes,
18108
+ allAvailableAncestries,
18109
+ handleEditAncestry,
18110
+ tweenToTarget
18111
+ ]
18112
+ );
18113
+ const handleSelectAncestryFromBoard = useCallback5(
17742
18114
  (item) => {
17743
18115
  setIsAncestryBoardOpen(false);
17744
18116
  setIsSidebarOpen(false);
17745
- if (item.type === "ancestry" || !item.type) {
18117
+ if (item.type === "ancestry") {
17746
18118
  handleStartReadingAncestry(item);
17747
18119
  } else {
17748
- const nodeId = String(item.id);
17749
- const sceneMesh = stateRef.current.nodeObjects[nodeId];
17750
- if (sceneMesh) {
17751
- tweenToTarget(sceneMesh);
17752
- } else {
17753
- console.warn("Objeto n\xE3o encontrado na cena para foco:", nodeId);
17754
- }
18120
+ handleOpenReference({ type: "node", id: item.id });
17755
18121
  }
17756
18122
  },
17757
- [handleStartReadingAncestry, tweenToTarget]
18123
+ [handleStartReadingAncestry, handleOpenReference]
17758
18124
  );
17759
- const handleSaveAncestryBoard = useCallback4(
18125
+ const handleSaveAncestryBoard = useCallback5(
17760
18126
  async (groups) => {
17761
18127
  if (!sceneConfigId || !viewParams || !session) return;
17762
18128
  const sceneType = (viewParams.type || "").toLowerCase().includes("database") ? "database" : "view";
@@ -17783,27 +18149,21 @@ function XViewScene({
17783
18149
  return [...new Set(allTypes)];
17784
18150
  }, [parentDataRef.current, sceneVersion]);
17785
18151
  const searchableDbNodes = useMemo13(() => {
17786
- var _a2;
17787
18152
  if (!parentDataRef.current) {
17788
18153
  return [];
17789
18154
  }
17790
- const datasetsPool = Object.entries(parentDataRef.current).flatMap(([dbId, fileData]) => {
18155
+ return Object.entries(parentDataRef.current).flatMap(([dbId, fileData]) => {
17791
18156
  const datasetName = fileData.dataset_name || `Dataset #${dbId.substring(0, 6)}`;
17792
18157
  return (fileData.nodes || []).map((node) => ({
17793
18158
  ...node,
17794
18159
  dataset_name: datasetName
17795
18160
  }));
18161
+ }).filter((node) => {
18162
+ var _a2;
18163
+ return !((_a2 = node.version_node) == null ? void 0 : _a2.is_version);
17796
18164
  });
17797
- const questsPool = (((_a2 = sceneDataRef.current) == null ? void 0 : _a2.quest_nodes) || []).map((node) => ({
17798
- ...node,
17799
- dataset_name: "Quests Internas (View)"
17800
- }));
17801
- return [...datasetsPool, ...questsPool].filter((node) => {
17802
- var _a3;
17803
- return !((_a3 = node.version_node) == null ? void 0 : _a3.is_version);
17804
- });
17805
- }, [parentDataRef.current, (_b = sceneDataRef.current) == null ? void 0 : _b.quest_nodes, sceneVersion]);
17806
- const handleAddExistingNode = useCallback4(
18165
+ }, [parentDataRef.current, sceneVersion]);
18166
+ const handleAddExistingNode = useCallback5(
17807
18167
  (nodeId) => {
17808
18168
  return userActionHandlers.handleAddExistingNodeById(
17809
18169
  actionHandlerContext,
@@ -17812,7 +18172,7 @@ function XViewScene({
17812
18172
  },
17813
18173
  [actionHandlerContext]
17814
18174
  );
17815
- const handleUpdateMilestones = useCallback4(
18175
+ const handleUpdateMilestones = useCallback5(
17816
18176
  async (newMilestones) => {
17817
18177
  var _a2, _b2, _c2;
17818
18178
  if (!sceneDataRef.current) return;
@@ -17858,7 +18218,7 @@ function XViewScene({
17858
18218
  viewParams == null ? void 0 : viewParams.type
17859
18219
  ]
17860
18220
  );
17861
- const handleSaveCurrentView = useCallback4(async () => {
18221
+ const handleSaveCurrentView = useCallback5(async () => {
17862
18222
  var _a2, _b2, _c2;
17863
18223
  const { nodeObjects, allLinks } = stateRef.current;
17864
18224
  if (!nodeObjects || !allLinks || !sceneSaveUrl || !parentDataRef.current) {
@@ -17901,61 +18261,13 @@ function XViewScene({
17901
18261
  console.error("Erro na chamada de save_view_data:", error);
17902
18262
  }
17903
18263
  }, [sceneSaveUrl, save_view_data, sceneConfigId, viewParams == null ? void 0 : viewParams.type]);
17904
- const allAvailableNodes = useMemo13(() => {
17905
- if (!parentDataRef.current) return [];
17906
- return Object.values(parentDataRef.current).flatMap(
17907
- (fileData) => fileData.nodes || []
17908
- );
17909
- }, [sceneVersion, isInitialized]);
17910
- const allAvailableAncestries = useMemo13(() => {
17911
- return ancestryDataRef.current || [];
17912
- }, [sceneVersion, isInitialized]);
17913
- const handleOpenReference = useCallback4(
17914
- (referenceData) => {
17915
- const { type, id } = referenceData;
17916
- if (type === "node") {
17917
- const targetNode = allAvailableNodes.find(
17918
- (n) => String(n.id) === String(id)
17919
- );
17920
- if (targetNode) {
17921
- setAncestryLinkDetails(null);
17922
- setDetailsLink(null);
17923
- setDetailsNode(targetNode);
17924
- const sceneMesh = stateRef.current.nodeObjects[String(id)];
17925
- if (sceneMesh) {
17926
- tweenToTarget(sceneMesh);
17927
- }
17928
- } else {
17929
- alert("Node original n\xE3o encontrado neste contexto.");
17930
- }
17931
- } else if (type === "ancestry") {
17932
- const targetAncestry = allAvailableAncestries.find(
17933
- (a) => String(a.ancestry_id) === String(id)
17934
- );
17935
- if (targetAncestry) {
17936
- setDetailsNode(null);
17937
- setDetailsLink(null);
17938
- setAncestryLinkDetails(null);
17939
- handleEditAncestry(targetAncestry);
17940
- } else {
17941
- alert("Ancestralidade original n\xE3o encontrada neste contexto.");
17942
- }
17943
- }
17944
- },
17945
- [
17946
- allAvailableNodes,
17947
- allAvailableAncestries,
17948
- handleEditAncestry,
17949
- tweenToTarget
17950
- ]
17951
- );
17952
- const handleToggleAncestryAddMode = useCallback4(() => {
18264
+ const handleToggleAncestryAddMode = useCallback5(() => {
17953
18265
  setAncestryMode((prev) => ({
17954
18266
  ...prev,
17955
18267
  isAddingNodes: !prev.isAddingNodes
17956
18268
  }));
17957
18269
  }, []);
17958
- const handleFocusNode = useCallback4(
18270
+ const handleFocusNode = useCallback5(
17959
18271
  (nodeData) => {
17960
18272
  var _a2;
17961
18273
  if (!nodeData) return;
@@ -17981,9 +18293,9 @@ function XViewScene({
17981
18293
  };
17982
18294
  });
17983
18295
  }, [sceneVersion, isInitialized]);
17984
- const sourceNodeDatasetId = creationMode.sourceNodeData ? (_c = stateRef.current.nodeIdToParentFileMap.get(
18296
+ const sourceNodeDatasetId = creationMode.sourceNodeData ? (_b = stateRef.current.nodeIdToParentFileMap.get(
17985
18297
  String(creationMode.sourceNodeData.id)
17986
- )) == null ? void 0 : _c.parentFileId : null;
18298
+ )) == null ? void 0 : _b.parentFileId : null;
17987
18299
  const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
17988
18300
  useEffect24(() => {
17989
18301
  if (isInitialized && focusNodeId && !hasFocusedInitial) {
@@ -18237,7 +18549,7 @@ function XViewScene({
18237
18549
  onToggle: setIsSidebarOpen,
18238
18550
  userRole: userPermissionRole,
18239
18551
  viewMembers,
18240
- milestones: ((_d = sceneDataRef.current) == null ? void 0 : _d.milestones) || [],
18552
+ milestones: ((_c = sceneDataRef.current) == null ? void 0 : _c.milestones) || [],
18241
18553
  onOpenMilestoneManager: () => setIsMilestoneManagerOpen(true)
18242
18554
  }
18243
18555
  ),
@@ -18262,8 +18574,8 @@ function XViewScene({
18262
18574
  },
18263
18575
  refEl: formRef,
18264
18576
  existingTypes: existingNodeTypes,
18265
- initialColor: (_e = creationMode.sourceNodeData) == null ? void 0 : _e.color,
18266
- sourceTypes: (_f = creationMode.sourceNodeData) == null ? void 0 : _f.type,
18577
+ initialColor: (_d = creationMode.sourceNodeData) == null ? void 0 : _d.color,
18578
+ sourceTypes: (_e = creationMode.sourceNodeData) == null ? void 0 : _e.type,
18267
18579
  onIntensityChange: handleGhostNodeIntensityChange,
18268
18580
  onUploadFile: upload_file_action,
18269
18581
  availableDatasets,
@@ -18292,8 +18604,8 @@ function XViewScene({
18292
18604
  transition: "opacity 200ms ease-out"
18293
18605
  },
18294
18606
  refEl: formRef,
18295
- fixedType: (_g = versionMode.sourceNodeData) == null ? void 0 : _g.type,
18296
- fixedColor: (_h = versionMode.sourceNodeData) == null ? void 0 : _h.color,
18607
+ fixedType: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.type,
18608
+ fixedColor: (_g = versionMode.sourceNodeData) == null ? void 0 : _g.color,
18297
18609
  onUploadFile: upload_file_action,
18298
18610
  availableNodes: allAvailableNodes,
18299
18611
  availableAncestries: allAvailableAncestries
@@ -18321,9 +18633,9 @@ function XViewScene({
18321
18633
  availableNodes: allAvailableNodes,
18322
18634
  availableAncestries: allAvailableAncestries,
18323
18635
  viewName: viewParams == null ? void 0 : viewParams.name,
18324
- questCounter: ((_i = sceneDataRef.current) == null ? void 0 : _i.quest_counter) || 1,
18636
+ questCounter: ((_h = sceneDataRef.current) == null ? void 0 : _h.quest_counter) || 1,
18325
18637
  viewMembers,
18326
- milestones: ((_j = sceneDataRef.current) == null ? void 0 : _j.milestones) || [],
18638
+ milestones: ((_i = sceneDataRef.current) == null ? void 0 : _i.milestones) || [],
18327
18639
  onUpdateMilestones: handleUpdateMilestones
18328
18640
  }
18329
18641
  ),
@@ -18458,7 +18770,7 @@ function XViewScene({
18458
18770
  currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName,
18459
18771
  viewMembers,
18460
18772
  currentUser: session == null ? void 0 : session.user,
18461
- milestones: ((_k = sceneDataRef.current) == null ? void 0 : _k.milestones) || [],
18773
+ milestones: ((_j = sceneDataRef.current) == null ? void 0 : _j.milestones) || [],
18462
18774
  onUpdateMilestones: handleUpdateMilestones
18463
18775
  }
18464
18776
  ),
@@ -18642,7 +18954,7 @@ function XViewScene({
18642
18954
  {
18643
18955
  isOpen: isMilestoneManagerOpen,
18644
18956
  onClose: () => setIsMilestoneManagerOpen(false),
18645
- milestones: ((_l = sceneDataRef.current) == null ? void 0 : _l.milestones) || [],
18957
+ milestones: ((_k = sceneDataRef.current) == null ? void 0 : _k.milestones) || [],
18646
18958
  onUpdateMilestones: handleUpdateMilestones
18647
18959
  }
18648
18960
  ),
@@ -18653,7 +18965,7 @@ function XViewScene({
18653
18965
  onClose: () => setIsImportModalOpen(false),
18654
18966
  onConfirm: handleConfirmImport,
18655
18967
  session,
18656
- parentDbs: ((_m = sceneDataRef.current) == null ? void 0 : _m.parent_dbs) || [],
18968
+ parentDbs: ((_l = sceneDataRef.current) == null ? void 0 : _l.parent_dbs) || [],
18657
18969
  onFetchAvailableFiles: import_parent_file_modal_get,
18658
18970
  currentViewName: viewParams == null ? void 0 : viewParams.name,
18659
18971
  currentAncestries: ancestryDataRef.current || []