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

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,10 +1,12 @@
1
+ "use client";
2
+
1
3
  // src/XViewScene.jsx
2
- import React28, {
4
+ import React30, {
3
5
  useCallback as useCallback4,
4
- useEffect as useEffect22,
6
+ useEffect as useEffect24,
5
7
  useRef as useRef19,
6
- useState as useState27,
7
- useMemo as useMemo12
8
+ useState as useState28,
9
+ useMemo as useMemo13
8
10
  } from "react";
9
11
  import { useRouter, useSearchParams } from "next/navigation";
10
12
  import { useSession } from "next-auth/react";
@@ -79,55 +81,7 @@ function defineAbilityFor(role) {
79
81
 
80
82
  // src/logic/x_view_utils.js
81
83
  import { useState, useCallback } from "react";
82
-
83
- // node_modules/uuid/dist/esm-node/rng.js
84
- import crypto from "crypto";
85
- var rnds8Pool = new Uint8Array(256);
86
- var poolPtr = rnds8Pool.length;
87
- function rng() {
88
- if (poolPtr > rnds8Pool.length - 16) {
89
- crypto.randomFillSync(rnds8Pool);
90
- poolPtr = 0;
91
- }
92
- return rnds8Pool.slice(poolPtr, poolPtr += 16);
93
- }
94
-
95
- // node_modules/uuid/dist/esm-node/stringify.js
96
- var byteToHex = [];
97
- for (let i = 0; i < 256; ++i) {
98
- byteToHex.push((i + 256).toString(16).slice(1));
99
- }
100
- function unsafeStringify(arr, offset = 0) {
101
- return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
102
- }
103
-
104
- // node_modules/uuid/dist/esm-node/native.js
105
- import crypto2 from "crypto";
106
- var native_default = {
107
- randomUUID: crypto2.randomUUID
108
- };
109
-
110
- // node_modules/uuid/dist/esm-node/v4.js
111
- function v4(options, buf, offset) {
112
- if (native_default.randomUUID && !buf && !options) {
113
- return native_default.randomUUID();
114
- }
115
- options = options || {};
116
- const rnds = options.random || (options.rng || rng)();
117
- rnds[6] = rnds[6] & 15 | 64;
118
- rnds[8] = rnds[8] & 63 | 128;
119
- if (buf) {
120
- offset = offset || 0;
121
- for (let i = 0; i < 16; ++i) {
122
- buf[offset + i] = rnds[i];
123
- }
124
- return buf;
125
- }
126
- return unsafeStringify(rnds);
127
- }
128
- var v4_default = v4;
129
-
130
- // src/logic/x_view_utils.js
84
+ import { v4 as uuidv4 } from "uuid";
131
85
  import * as THREE2 from "three";
132
86
 
133
87
  // src/logic/x_view_config.js
@@ -2480,15 +2434,15 @@ function createNewCustomProperty(existingProps = []) {
2480
2434
  switch (nextType) {
2481
2435
  case "links":
2482
2436
  nextKey = "Links";
2483
- nextValue = [{ id: v4_default(), name: "", value: "" }];
2437
+ nextValue = [{ id: uuidv4(), name: "", value: "" }];
2484
2438
  break;
2485
2439
  case "images":
2486
2440
  nextKey = "Images";
2487
- nextValue = [{ id: v4_default(), name: "1", value: "" }];
2441
+ nextValue = [{ id: uuidv4(), name: "1", value: "" }];
2488
2442
  break;
2489
2443
  case "documents":
2490
2444
  nextKey = "Documents";
2491
- nextValue = [{ id: v4_default(), name: "1", value: "" }];
2445
+ nextValue = [{ id: uuidv4(), name: "1", value: "" }];
2492
2446
  break;
2493
2447
  case "date":
2494
2448
  nextKey = "Date";
@@ -2498,14 +2452,14 @@ function createNewCustomProperty(existingProps = []) {
2498
2452
  nextValue = 0;
2499
2453
  break;
2500
2454
  case "list":
2501
- nextValue = [{ id: v4_default(), name: "", value: "" }];
2455
+ nextValue = [{ id: uuidv4(), name: "", value: "" }];
2502
2456
  break;
2503
2457
  default:
2504
2458
  nextValue = "";
2505
2459
  break;
2506
2460
  }
2507
2461
  return {
2508
- id: v4_default(),
2462
+ id: uuidv4(),
2509
2463
  key: nextKey,
2510
2464
  type: nextType,
2511
2465
  value: nextValue,
@@ -2750,7 +2704,7 @@ function extractCustomPropsFromNode(node) {
2750
2704
  if (val && typeof val === "object") {
2751
2705
  if ("date_interval" in val) {
2752
2706
  return {
2753
- id: v4_default(),
2707
+ id: uuidv4(),
2754
2708
  key,
2755
2709
  type: "date",
2756
2710
  value: {
@@ -2762,7 +2716,7 @@ function extractCustomPropsFromNode(node) {
2762
2716
  }
2763
2717
  if ("year" in val) {
2764
2718
  return {
2765
- id: v4_default(),
2719
+ id: uuidv4(),
2766
2720
  key,
2767
2721
  type: "date",
2768
2722
  value: { type: "Ano", value: val.year || "" }
@@ -2770,7 +2724,7 @@ function extractCustomPropsFromNode(node) {
2770
2724
  }
2771
2725
  if ("date" in val) {
2772
2726
  return {
2773
- id: v4_default(),
2727
+ id: uuidv4(),
2774
2728
  key,
2775
2729
  type: "date",
2776
2730
  value: { type: "Data", value: val.date || "" }
@@ -2778,7 +2732,7 @@ function extractCustomPropsFromNode(node) {
2778
2732
  }
2779
2733
  }
2780
2734
  return {
2781
- id: v4_default(),
2735
+ id: uuidv4(),
2782
2736
  key,
2783
2737
  type: "date",
2784
2738
  value: { type: "Data", value: "" }
@@ -2786,14 +2740,14 @@ function extractCustomPropsFromNode(node) {
2786
2740
  }
2787
2741
  if (t === "list" || t === "links" || t === "images" || t === "documents") {
2788
2742
  const safeList = (val || []).map((item) => ({
2789
- id: item.id || v4_default(),
2743
+ id: item.id || uuidv4(),
2790
2744
  name: item.name ?? "",
2791
2745
  value: item.value ?? ""
2792
2746
  }));
2793
- return { id: v4_default(), key, type: t, value: safeList };
2747
+ return { id: uuidv4(), key, type: t, value: safeList };
2794
2748
  }
2795
2749
  return {
2796
- id: v4_default(),
2750
+ id: uuidv4(),
2797
2751
  key,
2798
2752
  type: t,
2799
2753
  value: t === "number" ? Number(val) || 0 : String(val ?? "")
@@ -3066,16 +3020,16 @@ var processDescriptionForSave = (text, existingSections = []) => {
3066
3020
  if (existingMatch) {
3067
3021
  finalUuid = existingMatch.id;
3068
3022
  } else {
3069
- finalUuid = v4_default();
3023
+ finalUuid = uuidv4();
3070
3024
  }
3071
3025
  } else {
3072
3026
  finalUuid = numericMap.get(numStr);
3073
3027
  if (!finalUuid) {
3074
- finalUuid = v4_default();
3028
+ finalUuid = uuidv4();
3075
3029
  }
3076
3030
  }
3077
3031
  if (usedIds.has(finalUuid)) {
3078
- finalUuid = v4_default();
3032
+ finalUuid = uuidv4();
3079
3033
  }
3080
3034
  usedIds.add(finalUuid);
3081
3035
  processedSections.push({
@@ -3186,18 +3140,18 @@ function useResizablePanel({ initialWidth, minWidth, maxWidth }) {
3186
3140
  function createTimelineEntry(actorId, description, type = "comment", extras = {}) {
3187
3141
  const { links = [], images = [], metadata = {} } = extras;
3188
3142
  return {
3189
- id: v4_default(),
3143
+ id: uuidv4(),
3190
3144
  type,
3191
3145
  timestamp: Date.now(),
3192
3146
  actorId,
3193
3147
  description,
3194
3148
  links: links.map((l) => ({
3195
- id: l.id || v4_default(),
3149
+ id: l.id || uuidv4(),
3196
3150
  name: l.name || "",
3197
3151
  value: l.value || ""
3198
3152
  })),
3199
3153
  images: images.map((img) => ({
3200
- id: img.id || v4_default(),
3154
+ id: img.id || uuidv4(),
3201
3155
  name: img.name || "",
3202
3156
  value: img.value || ""
3203
3157
  }))
@@ -4424,6 +4378,7 @@ function XViewSidebar({
4424
4378
 
4425
4379
  // src/components/AncestryRelationshipPanel.jsx
4426
4380
  import React8, { useState as useState9, useEffect as useEffect8, useRef as useRef7 } from "react";
4381
+ import { v4 as uuidv43 } from "uuid";
4427
4382
 
4428
4383
  // src/components/CustomPropertyDisplay.jsx
4429
4384
  import React3, { useState as useState4, useRef as useRef3, useEffect as useEffect3 } from "react";
@@ -5183,6 +5138,7 @@ function SectionImportModal({ isOpen, onClose, onSelect, availableNodes = [], av
5183
5138
  }
5184
5139
 
5185
5140
  // src/components/DescriptionEditModal.jsx
5141
+ import { v4 as uuidv42 } from "uuid";
5186
5142
  var getNextSectionNumber = (currentText) => {
5187
5143
  const regex = /\*\/\s*(\d+)(?::[a-zA-Z0-9-]+)?\s*\//g;
5188
5144
  let max = 0;
@@ -5314,7 +5270,7 @@ function DescriptionEditModal({
5314
5270
  const el = textareaRef.current;
5315
5271
  if (!el) return;
5316
5272
  const nextNum = getNextSectionNumber(text);
5317
- const uniqueSuffix = v4_default().slice(0, 4);
5273
+ const uniqueSuffix = uuidv42().slice(0, 4);
5318
5274
  insertAtCursor(`*/${nextNum}:${uniqueSuffix}/ `);
5319
5275
  };
5320
5276
  const handleImportSelect = (tags) => {
@@ -5325,7 +5281,7 @@ function DescriptionEditModal({
5325
5281
  let currentNum = getNextSectionNumber(currentText);
5326
5282
  let blockToInsert = "";
5327
5283
  tagsArray.forEach((tag) => {
5328
- const uniqueSuffix = v4_default().slice(0, 4);
5284
+ const uniqueSuffix = uuidv42().slice(0, 4);
5329
5285
  blockToInsert += `
5330
5286
  */${currentNum}:${uniqueSuffix}/ ${tag}
5331
5287
  `;
@@ -6685,6 +6641,7 @@ import {
6685
6641
  FiLink as FiLink4,
6686
6642
  FiTrash2 as FiTrash22
6687
6643
  } from "react-icons/fi";
6644
+ import { v4 as uuidv44 } from "uuid";
6688
6645
 
6689
6646
  // src/components/AncestryPickerModal.jsx
6690
6647
  import React9, { useState as useState10, useMemo as useMemo7, useEffect as useEffect9 } from "react";
@@ -7721,7 +7678,7 @@ function CreateAncestryPanel({
7721
7678
  const nodeData = availableNodes.find((n) => String(n.id) === String(nodeId));
7722
7679
  const dirSuffix = direction === "left" ? "(Esq)" : "(Dir)";
7723
7680
  branch = {
7724
- id: v4_default(),
7681
+ id: uuidv44(),
7725
7682
  name: `Ramifica\xE7\xE3o ${dirSuffix} de ${(nodeData == null ? void 0 : nodeData.name) || "Node"}`,
7726
7683
  description: "",
7727
7684
  description_sections: [],
@@ -8500,6 +8457,7 @@ function ImageViewer({ data, onClose }) {
8500
8457
 
8501
8458
  // src/components/InSceneCreationForm.jsx
8502
8459
  import React13, { useState as useState14, useEffect as useEffect13, useRef as useRef10 } from "react";
8460
+ import { v4 as uuidv45 } from "uuid";
8503
8461
 
8504
8462
  // src/components/ColorPicker.jsx
8505
8463
  import React12, { useState as useState13, useEffect as useEffect12, useRef as useRef9 } from "react";
@@ -9084,6 +9042,7 @@ function InSceneCreationForm({
9084
9042
 
9085
9043
  // src/components/InSceneVersionForm.jsx
9086
9044
  import React14, { useState as useState15, useEffect as useEffect14, useRef as useRef11 } from "react";
9045
+ import { v4 as uuidv46 } from "uuid";
9087
9046
  import { FiPlus as FiPlus4, FiMaximize2 as FiMaximize22, FiCheck as FiCheck8, FiEdit2 as FiEdit25 } from "react-icons/fi";
9088
9047
  function InSceneVersionForm({
9089
9048
  onSave,
@@ -9106,7 +9065,7 @@ function InSceneVersionForm({
9106
9065
  const [description, setDescription] = useState15("");
9107
9066
  const [customProps, setCustomProps] = useState15([
9108
9067
  {
9109
- id: v4_default(),
9068
+ id: uuidv46(),
9110
9069
  key: "Date",
9111
9070
  type: "date",
9112
9071
  value: { type: "Data", value: "" },
@@ -9399,6 +9358,7 @@ function InSceneVersionForm({
9399
9358
 
9400
9359
  // src/components/InSceneQuestForm.jsx
9401
9360
  import React16, { useState as useState17, useRef as useRef12 } from "react";
9361
+ import { v4 as uuidv47 } from "uuid";
9402
9362
  import {
9403
9363
  FiPlus as FiPlus6,
9404
9364
  FiCheck as FiCheck9,
@@ -10323,6 +10283,7 @@ function InSceneQuestForm({
10323
10283
 
10324
10284
  // src/components/NodeDetailsPanel.jsx
10325
10285
  import React17, { useState as useState18, useEffect as useEffect15, useRef as useRef13 } from "react";
10286
+ import { v4 as uuidv48 } from "uuid";
10326
10287
  import { FiPlus as FiPlus7, FiMaximize2 as FiMaximize23, FiX as FiX6, FiCheck as FiCheck10, FiImage as FiImage3, FiEdit2 as FiEdit27, FiLoader as FiLoader2, FiBookOpen as FiBookOpen3, FiSun as FiSun2, FiLink as FiLink5, FiDatabase } from "react-icons/fi";
10327
10288
  function NodeDetailsPanel({
10328
10289
  node,
@@ -10837,6 +10798,7 @@ function NodeDetailsPanel({
10837
10798
 
10838
10799
  // src/components/QuestDetailsPanel.jsx
10839
10800
  import React19, { useState as useState19, useEffect as useEffect16, useRef as useRef14 } from "react";
10801
+ import { v4 as uuidv49 } from "uuid";
10840
10802
  import { FiPlus as FiPlus8, FiX as FiX7, FiCheck as FiCheck11, FiEdit2 as FiEdit28, FiLoader as FiLoader3, FiBookOpen as FiBookOpen4, FiLink as FiLink7, FiTarget as FiTarget3, FiChevronDown as FiChevronDown7, FiUser as FiUser3, FiSearch as FiSearch7, FiRepeat as FiRepeat2, FiCalendar as FiCalendar2, FiImage as FiImage4 } from "react-icons/fi";
10841
10803
 
10842
10804
  // src/components/QuestTimeline.jsx
@@ -11287,7 +11249,7 @@ ${noteText}` : noteText : entry.notes,
11287
11249
  }, 100);
11288
11250
  };
11289
11251
  const addLinkToNote = () => {
11290
- setNoteLinks([...noteLinks, { id: v4_default(), name: "", value: "" }]);
11252
+ setNoteLinks([...noteLinks, { id: uuidv49(), name: "", value: "" }]);
11291
11253
  };
11292
11254
  const removeLinkFromNote = (id) => {
11293
11255
  setNoteLinks(noteLinks.filter((l) => l.id !== id));
@@ -11296,7 +11258,7 @@ ${noteText}` : noteText : entry.notes,
11296
11258
  setNoteLinks(noteLinks.map((l) => l.id === id ? { ...l, [field]: val } : l));
11297
11259
  };
11298
11260
  const addImageToNote = () => {
11299
- setNoteImages([...noteImages, { id: v4_default(), name: "Image", value: "" }]);
11261
+ setNoteImages([...noteImages, { id: uuidv49(), name: "Image", value: "" }]);
11300
11262
  };
11301
11263
  const removeImageFromNote = (id) => {
11302
11264
  setNoteImages(noteImages.filter((img) => img.id !== id));
@@ -12173,6 +12135,7 @@ function MultiNodeContextMenu({
12173
12135
 
12174
12136
  // src/components/RelationshipDetailsPanel.jsx
12175
12137
  import React21, { useState as useState21, useEffect as useEffect18, useRef as useRef16, useMemo as useMemo9 } from "react";
12138
+ import { v4 as uuidv410 } from "uuid";
12176
12139
  import { FiPlus as FiPlus9, FiEdit2 as FiEdit29, FiLoader as FiLoader4, FiBookOpen as FiBookOpen5 } from "react-icons/fi";
12177
12140
  function RelationshipDetailsPanel({
12178
12141
  link,
@@ -12982,10 +12945,10 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
12982
12945
  }
12983
12946
 
12984
12947
  // src/components/AncestryBoard.jsx
12985
- import React26, { useState as useState25, useMemo as useMemo11, useEffect as useEffect21, useRef as useRef18 } from "react";
12948
+ import React28, { useState as useState26, useMemo as useMemo12, useEffect as useEffect23, useRef as useRef18 } from "react";
12986
12949
  import {
12987
- FiSearch as FiSearch8,
12988
- FiLayers as FiLayers6,
12950
+ FiSearch as FiSearch9,
12951
+ FiLayers as FiLayers7,
12989
12952
  FiCornerUpRight as FiCornerUpRight4,
12990
12953
  FiPlay,
12991
12954
  FiPlus as FiPlus10,
@@ -12996,151 +12959,384 @@ import {
12996
12959
  FiLoader as FiLoader5,
12997
12960
  FiX as FiX8,
12998
12961
  FiAlertTriangle,
12962
+ FiCheckSquare as FiCheckSquare4,
12963
+ FiBox as FiBox2,
12964
+ FiGitBranch as FiGitBranch4
12965
+ } from "react-icons/fi";
12966
+ import { v4 as uuidv411 } from "uuid";
12967
+
12968
+ // src/components/MentionEditor.jsx
12969
+ import React27, { useMemo as useMemo11, useEffect as useEffect22 } from "react";
12970
+ import { useEditor, EditorContent, ReactRenderer } from "@tiptap/react";
12971
+ import StarterKit from "@tiptap/starter-kit";
12972
+ import Mention from "@tiptap/extension-mention";
12973
+ import tippy from "tippy.js";
12974
+
12975
+ // src/components/MentionList.jsx
12976
+ import React26, {
12977
+ forwardRef,
12978
+ useEffect as useEffect21,
12979
+ useImperativeHandle,
12980
+ useState as useState25
12981
+ } from "react";
12982
+ import {
12983
+ FiLayers as FiLayers6,
12999
12984
  FiCheckSquare as FiCheckSquare3,
13000
12985
  FiBox,
13001
- FiGitBranch as FiGitBranch3
12986
+ FiGitBranch as FiGitBranch3,
12987
+ FiSearch as FiSearch8
13002
12988
  } from "react-icons/fi";
12989
+ var MentionList = forwardRef((props, ref) => {
12990
+ const [selectedIndex, setSelectedIndex] = useState25(0);
12991
+ const selectItem = (index) => {
12992
+ const item = props.items[index];
12993
+ if (item) {
12994
+ props.command({ id: item.id, label: item.name, type: item.type });
12995
+ }
12996
+ };
12997
+ const upHandler = () => {
12998
+ setSelectedIndex(
12999
+ (selectedIndex + props.items.length - 1) % props.items.length
13000
+ );
13001
+ };
13002
+ const downHandler = () => {
13003
+ setSelectedIndex((selectedIndex + 1) % props.items.length);
13004
+ };
13005
+ const enterHandler = () => {
13006
+ selectItem(selectedIndex);
13007
+ };
13008
+ useEffect21(() => setSelectedIndex(0), [props.items]);
13009
+ useImperativeHandle(ref, () => ({
13010
+ onKeyDown: ({ event }) => {
13011
+ if (event.key === "ArrowUp") {
13012
+ upHandler();
13013
+ return true;
13014
+ }
13015
+ if (event.key === "ArrowDown") {
13016
+ downHandler();
13017
+ return true;
13018
+ }
13019
+ if (event.key === "Enter") {
13020
+ enterHandler();
13021
+ return true;
13022
+ }
13023
+ return false;
13024
+ }
13025
+ }));
13026
+ 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) => {
13027
+ const isQuest = item.type === "quest";
13028
+ const isVersion = item.type === "version";
13029
+ const isNode = item.type === "node";
13030
+ let Icon = FiLayers6;
13031
+ let colorClass = "text-indigo-400";
13032
+ let badgeText = "Ancestralidade";
13033
+ if (isQuest) {
13034
+ Icon = FiCheckSquare3;
13035
+ colorClass = "text-amber-400";
13036
+ badgeText = "Quest";
13037
+ } else if (isVersion) {
13038
+ Icon = FiGitBranch3;
13039
+ colorClass = "text-purple-400";
13040
+ badgeText = "Vers\xE3o";
13041
+ } else if (isNode) {
13042
+ Icon = FiBox;
13043
+ colorClass = "text-emerald-400";
13044
+ badgeText = "Node";
13045
+ }
13046
+ return /* @__PURE__ */ React26.createElement(
13047
+ "button",
13048
+ {
13049
+ className: `
13050
+ w-full flex items-center gap-3 px-3 py-2 text-left rounded-md transition-all
13051
+ ${index === selectedIndex ? "bg-indigo-500/20 border border-indigo-500/30 text-white" : "text-slate-400 hover:bg-white/5 border border-transparent"}
13052
+ `,
13053
+ key: item.id,
13054
+ onClick: () => selectItem(index)
13055
+ },
13056
+ /* @__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 })),
13057
+ /* @__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)))
13058
+ );
13059
+ }) : /* @__PURE__ */ React26.createElement("div", { className: "p-4 text-center text-slate-500 text-sm italic" }, "Nenhum resultado encontrado...")));
13060
+ });
13061
+ MentionList.displayName = "MentionList";
13062
+ var MentionList_default = MentionList;
13063
+
13064
+ // src/components/MentionEditor.jsx
13065
+ var CustomMention = Mention.extend({
13066
+ addAttributes() {
13067
+ var _a;
13068
+ return {
13069
+ ...(_a = this.parent) == null ? void 0 : _a.call(this),
13070
+ type: {
13071
+ default: null,
13072
+ parseHTML: (element) => element.getAttribute("data-mention-type"),
13073
+ renderHTML: (attributes) => {
13074
+ if (!attributes.type) {
13075
+ return {};
13076
+ }
13077
+ return {
13078
+ "data-mention-type": attributes.type
13079
+ };
13080
+ }
13081
+ },
13082
+ label: {
13083
+ default: null,
13084
+ parseHTML: (element) => element.getAttribute("data-label"),
13085
+ renderHTML: (attributes) => {
13086
+ if (!attributes.label) {
13087
+ return {};
13088
+ }
13089
+ return {
13090
+ "data-label": attributes.label
13091
+ };
13092
+ }
13093
+ }
13094
+ };
13095
+ }
13096
+ });
13097
+ var MentionEditor = ({
13098
+ initialValue = "",
13099
+ onChange,
13100
+ onPlay,
13101
+ availableItems = [],
13102
+ canEdit = true,
13103
+ placeholder = "Escreva algo... use @ para mencionar"
13104
+ }) => {
13105
+ const parseInitialContent = (text) => {
13106
+ if (!text) return "<p></p>";
13107
+ const parts = text.split(/(\[\[MENTION:[^\]]+\]\])/g);
13108
+ return parts.map((part) => {
13109
+ const match = part.match(/\[\[MENTION:([^:]+):([^:]+):([^\]]+)\]\]/);
13110
+ if (match) {
13111
+ const [, type, id, label] = match;
13112
+ return `<span data-type="mention" data-id="${id}" data-mention-type="${type}" data-label="${label}">@${label}</span>`;
13113
+ }
13114
+ return part;
13115
+ }).join("");
13116
+ };
13117
+ const content = useMemo11(() => parseInitialContent(initialValue), [initialValue]);
13118
+ const editor = useEditor({
13119
+ extensions: [
13120
+ StarterKit.configure({
13121
+ heading: false,
13122
+ bulletList: false,
13123
+ orderedList: false,
13124
+ codeBlock: false,
13125
+ blockquote: false
13126
+ }),
13127
+ CustomMention.configure({
13128
+ HTMLAttributes: {
13129
+ class: "mention"
13130
+ },
13131
+ suggestion: {
13132
+ items: ({ query }) => {
13133
+ return availableItems.filter(
13134
+ (item) => item.name.toLowerCase().includes(query.toLowerCase())
13135
+ ).slice(0, 10);
13136
+ },
13137
+ render: () => {
13138
+ let component;
13139
+ let popup;
13140
+ return {
13141
+ onStart: (props) => {
13142
+ component = new ReactRenderer(MentionList_default, {
13143
+ props,
13144
+ editor: props.editor
13145
+ });
13146
+ if (!props.clientRect) {
13147
+ return;
13148
+ }
13149
+ popup = tippy("body", {
13150
+ getReferenceClientRect: props.clientRect,
13151
+ appendTo: () => document.body,
13152
+ content: component.element,
13153
+ showOnCreate: true,
13154
+ interactive: true,
13155
+ trigger: "manual",
13156
+ placement: "bottom-start",
13157
+ theme: "x-view-dark"
13158
+ });
13159
+ },
13160
+ onUpdate(props) {
13161
+ component.updateProps(props);
13162
+ if (!props.clientRect) {
13163
+ return;
13164
+ }
13165
+ popup[0].setProps({
13166
+ getReferenceClientRect: props.clientRect
13167
+ });
13168
+ },
13169
+ onKeyDown(props) {
13170
+ var _a;
13171
+ if (props.event.key === "Escape") {
13172
+ popup[0].hide();
13173
+ return true;
13174
+ }
13175
+ return (_a = component == null ? void 0 : component.ref) == null ? void 0 : _a.onKeyDown(props);
13176
+ },
13177
+ onExit() {
13178
+ if (popup && popup[0]) popup[0].destroy();
13179
+ if (component) component.destroy();
13180
+ }
13181
+ };
13182
+ }
13183
+ }
13184
+ })
13185
+ ],
13186
+ content,
13187
+ editable: canEdit,
13188
+ onUpdate: ({ editor: editor2 }) => {
13189
+ const serialize = (json) => {
13190
+ if (!json.content) return "";
13191
+ return json.content.map((block) => {
13192
+ if (block.type === "paragraph") {
13193
+ const inner = block.content ? block.content.map((node) => {
13194
+ if (node.type === "text") return node.text;
13195
+ if (node.type === "mention") {
13196
+ return `[[MENTION:${node.attrs.type}:${node.attrs.id}:${node.attrs.label}]]`;
13197
+ }
13198
+ return "";
13199
+ }).join("") : "";
13200
+ return inner;
13201
+ }
13202
+ return "";
13203
+ }).join("\n").trim();
13204
+ };
13205
+ const serialized = serialize(editor2.getJSON());
13206
+ onChange(serialized);
13207
+ }
13208
+ });
13209
+ if (!editor) {
13210
+ return null;
13211
+ }
13212
+ return /* @__PURE__ */ React27.createElement("div", { className: "relative group/editor min-h-[1.5rem] w-full" }, /* @__PURE__ */ React27.createElement(
13213
+ EditorContent,
13214
+ {
13215
+ editor,
13216
+ className: "text-sm text-slate-200 placeholder-slate-600 focus:outline-none"
13217
+ }
13218
+ ), /* @__PURE__ */ React27.createElement("style", null, `
13219
+ .tiptap p {
13220
+ margin: 0;
13221
+ min-height: 1.25rem;
13222
+ line-height: 1.5;
13223
+ }
13224
+ .tiptap .mention {
13225
+ display: inline-flex;
13226
+ align-items: center;
13227
+ background: rgba(99, 102, 241, 0.12);
13228
+ border: 1px solid rgba(99, 102, 241, 0.25);
13229
+ border-radius: 6px;
13230
+ padding: 1px 6px;
13231
+ color: #a5b4fc;
13232
+ font-weight: 600;
13233
+ cursor: pointer;
13234
+ margin: 0 1px;
13235
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
13236
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
13237
+ font-size: 0.85rem;
13238
+ text-decoration: none;
13239
+ vertical-align: baseline;
13240
+ }
13241
+ .tiptap .mention:hover {
13242
+ background: rgba(99, 102, 241, 0.22);
13243
+ border-color: rgba(99, 102, 241, 0.5);
13244
+ color: #fff;
13245
+ transform: translateY(-1px);
13246
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.2);
13247
+ }
13248
+ .tiptap .mention::before {
13249
+ content: "";
13250
+ display: inline-block;
13251
+ width: 12px;
13252
+ height: 12px;
13253
+ margin-right: 4px;
13254
+ background-size: contain;
13255
+ background-repeat: no-repeat;
13256
+ opacity: 0.8;
13257
+ filter: brightness(1.2);
13258
+ }
13259
+
13260
+ /* Type specific styles */
13261
+ .tiptap .mention[data-mention-type="ancestry"] {
13262
+ background: rgba(139, 92, 246, 0.15);
13263
+ border-color: rgba(139, 92, 246, 0.3);
13264
+ color: #c4b5fd;
13265
+ }
13266
+ .tiptap .mention[data-mention-type="quest"] {
13267
+ background: rgba(16, 185, 129, 0.15);
13268
+ border-color: rgba(16, 185, 129, 0.3);
13269
+ color: #6ee7b7;
13270
+ }
13271
+ .tiptap .mention[data-mention-type="node"] {
13272
+ background: rgba(59, 130, 246, 0.15);
13273
+ border-color: rgba(59, 130, 246, 0.3);
13274
+ color: #93c5fd;
13275
+ }
13276
+ .tiptap .mention[data-mention-type="version"] {
13277
+ background: rgba(245, 158, 11, 0.15);
13278
+ border-color: rgba(245, 158, 11, 0.3);
13279
+ color: #fcd34d;
13280
+ }
13281
+
13282
+ .tiptap:focus {
13283
+ outline: none;
13284
+ }
13285
+
13286
+ .ProseMirror-placeholder:first-child::before {
13287
+ content: attr(data-placeholder);
13288
+ float: left;
13289
+ color: #475569;
13290
+ pointer-events: none;
13291
+ height: 0;
13292
+ font-style: italic;
13293
+ }
13294
+ `));
13295
+ };
13296
+ var MentionEditor_default = MentionEditor;
13297
+
13298
+ // src/components/AncestryBoard.jsx
13003
13299
  var GroupItem = ({
13004
13300
  group,
13005
13301
  index,
13006
- siblings,
13007
13302
  onUpdate,
13008
13303
  onDelete,
13009
13304
  onAddSubgroup,
13010
13305
  onIndent,
13011
13306
  onOutdent,
13012
- onRemoveAncestry,
13013
- onRequestPickAncestry,
13014
- pickingGroupId,
13015
13307
  onPlayAncestry,
13016
- availableIds,
13308
+ availableMentionItems,
13017
13309
  canEdit
13018
13310
  }) => {
13019
13311
  const canIndent = index > 0;
13020
- const isPickingForThisGroup = pickingGroupId === group.id;
13021
- const textareaRef = useRef18(null);
13022
- const adjustHeight = () => {
13023
- const textarea = textareaRef.current;
13024
- if (textarea) {
13025
- textarea.style.height = "auto";
13026
- textarea.style.height = `${textarea.scrollHeight}px`;
13027
- }
13028
- };
13029
- useEffect21(() => {
13030
- adjustHeight();
13031
- }, [group.text]);
13032
- return /* @__PURE__ */ React26.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__ */ React26.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ React26.createElement(
13312
+ 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(
13033
13313
  "div",
13034
13314
  {
13035
- className: `
13036
- flex flex-col gap-2 py-2 px-3 transition-all duration-200
13037
- ${isPickingForThisGroup ? "bg-indigo-500/10 border-l-2 border-indigo-500" : "hover:bg-white/5 border-l-2 border-transparent hover:border-white/20"}
13038
- `
13315
+ 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"
13039
13316
  },
13040
- /* @__PURE__ */ React26.createElement(
13041
- "textarea",
13317
+ /* @__PURE__ */ React28.createElement(
13318
+ MentionEditor_default,
13042
13319
  {
13043
- ref: textareaRef,
13044
- className: `w-full bg-transparent text-sm text-slate-200 placeholder-slate-600 resize-none focus:outline-none focus:placeholder-slate-500 overflow-y-auto ${!canEdit ? "cursor-default" : ""}`,
13045
- rows: 1,
13046
- style: {
13047
- minHeight: "1.5rem",
13048
- maxHeight: "250px"
13320
+ initialValue: group.text,
13321
+ onChange: (newText) => {
13322
+ if (canEdit) onUpdate(group.id, { ...group, text: newText });
13049
13323
  },
13050
- placeholder: canEdit ? "Escreva sobre este grupo..." : "",
13051
- value: group.text,
13052
- readOnly: !canEdit,
13053
- onChange: (e) => {
13054
- if (canEdit) onUpdate(group.id, { ...group, text: e.target.value });
13055
- }
13324
+ onPlay: onPlayAncestry,
13325
+ availableItems: availableMentionItems,
13326
+ canEdit,
13327
+ placeholder: "Escreva sobre este grupo... use @ para mencionar"
13056
13328
  }
13057
13329
  ),
13058
- group.ancestries && group.ancestries.length > 0 && /* @__PURE__ */ React26.createElement("div", { className: "flex flex-wrap gap-2 mt-1" }, group.ancestries.map((anc) => {
13059
- const isValid = anc.type === "ancestry" || !anc.type ? availableIds.has(String(anc.ancestry_id || anc.id)) : true;
13060
- const isAncestry = anc.type === "ancestry" || !anc.type;
13061
- const itemId = isAncestry ? anc.ancestry_id : anc.id;
13062
- let Icon = FiLayers6;
13063
- let colorClass = "text-indigo-400";
13064
- if (anc.type === "quest") {
13065
- Icon = FiCheckSquare3;
13066
- colorClass = "text-amber-400";
13067
- } else if (anc.type === "version") {
13068
- Icon = FiGitBranch3;
13069
- colorClass = "text-purple-400";
13070
- } else if (anc.type === "node") {
13071
- Icon = FiBox;
13072
- colorClass = "text-emerald-400";
13073
- }
13074
- return /* @__PURE__ */ React26.createElement(
13075
- "div",
13076
- {
13077
- key: `${anc.type || "ancestry"}-${itemId}`,
13078
- className: `
13079
- flex items-center gap-2 rounded-md px-3 py-1.5 text-xs transition-all group/card border
13080
- ${isValid ? "bg-slate-800/60 border-white/10 hover:border-indigo-500/30 text-slate-200" : "bg-red-900/10 border-red-500/30 text-red-300/70 hover:bg-red-900/20"}
13081
- `,
13082
- title: isValid ? "" : "Esta informa\xE7\xE3o n\xE3o est\xE1 mais dispon\xEDvel neste contexto."
13083
- },
13084
- isValid ? /* @__PURE__ */ React26.createElement(
13085
- "button",
13086
- {
13087
- onClick: () => onPlayAncestry(anc),
13088
- className: `${colorClass} hover:text-white hover:bg-current/20 p-1 rounded transition-colors flex items-center justify-center`,
13089
- title: "Focar no cen\xE1rio"
13090
- },
13091
- /* @__PURE__ */ React26.createElement(Icon, { size: 12 }),
13092
- /* @__PURE__ */ React26.createElement(
13093
- FiPlay,
13094
- {
13095
- size: 8,
13096
- className: "ml-0.5 fill-current opacity-0 group-hover/card:opacity-100 transition-opacity"
13097
- }
13098
- )
13099
- ) : /* @__PURE__ */ React26.createElement("div", { className: "p-1 text-red-500 cursor-not-allowed" }, /* @__PURE__ */ React26.createElement(FiAlertTriangle, { size: 10 })),
13100
- /* @__PURE__ */ React26.createElement(
13101
- "span",
13102
- {
13103
- className: `font-medium truncate max-w-[150px] ${!isValid && "line-through decoration-red-500/50"}`
13104
- },
13105
- anc.name
13106
- ),
13107
- canEdit && /* @__PURE__ */ React26.createElement(React26.Fragment, null, /* @__PURE__ */ React26.createElement(
13108
- "div",
13109
- {
13110
- className: `w-px h-3 mx-0.5 ${isValid ? "bg-white/10" : "bg-red-500/20"}`
13111
- }
13112
- ), /* @__PURE__ */ React26.createElement(
13113
- "button",
13114
- {
13115
- onClick: () => onRemoveAncestry(group.id, itemId),
13116
- className: `${isValid ? "text-slate-500 hover:text-red-400" : "text-red-400 hover:text-red-200"} p-0.5 rounded transition-colors`,
13117
- title: "Remover men\xE7\xE3o"
13118
- },
13119
- /* @__PURE__ */ React26.createElement(FiX8, { size: 12 })
13120
- ))
13121
- );
13122
- })),
13123
- canEdit && /* @__PURE__ */ React26.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__ */ React26.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React26.createElement(
13124
- "button",
13125
- {
13126
- onClick: () => onRequestPickAncestry(group.id),
13127
- className: `
13128
- flex items-center gap-1.5 px-2 py-1 rounded text-xs font-medium transition-colors
13129
- ${isPickingForThisGroup ? "text-indigo-300 animate-pulse" : "text-slate-500 hover:text-indigo-300 hover:bg-indigo-500/10"}
13130
- `,
13131
- title: "Adicionar Men\xE7\xE3o a este grupo"
13132
- },
13133
- isPickingForThisGroup ? /* @__PURE__ */ React26.createElement(FiCheckCircle, { size: 12 }) : /* @__PURE__ */ React26.createElement(FiSearch8, { size: 12 }),
13134
- isPickingForThisGroup ? "Selecionando..." : "Adicionar"
13135
- ), /* @__PURE__ */ React26.createElement(
13330
+ 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(
13136
13331
  "button",
13137
13332
  {
13138
13333
  onClick: () => onAddSubgroup(group.id),
13139
- className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
13334
+ 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",
13140
13335
  title: "Criar Subgrupo"
13141
13336
  },
13142
- /* @__PURE__ */ React26.createElement(FiPlus10, { size: 14 })
13143
- )), /* @__PURE__ */ React26.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React26.createElement(
13337
+ /* @__PURE__ */ React28.createElement(FiPlus10, { size: 14 }),
13338
+ /* @__PURE__ */ React28.createElement("span", null, "Adicionar Subgrupo")
13339
+ )), /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React28.createElement(
13144
13340
  "button",
13145
13341
  {
13146
13342
  onClick: () => onIndent(group.id),
@@ -13148,25 +13344,25 @@ var GroupItem = ({
13148
13344
  className: `p-1.5 rounded transition-colors ${!canIndent ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`,
13149
13345
  title: "Aninhar no grupo acima"
13150
13346
  },
13151
- /* @__PURE__ */ React26.createElement(FiArrowRight2, { size: 14 })
13152
- ), /* @__PURE__ */ React26.createElement(
13347
+ /* @__PURE__ */ React28.createElement(FiArrowRight2, { size: 14 })
13348
+ ), /* @__PURE__ */ React28.createElement(
13153
13349
  "button",
13154
13350
  {
13155
13351
  onClick: () => onOutdent(group.id),
13156
13352
  className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
13157
13353
  title: "Desaninhar"
13158
13354
  },
13159
- /* @__PURE__ */ React26.createElement(FiArrowLeft3, { size: 14 })
13160
- ), /* @__PURE__ */ React26.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), /* @__PURE__ */ React26.createElement(
13355
+ /* @__PURE__ */ React28.createElement(FiArrowLeft3, { size: 14 })
13356
+ ), /* @__PURE__ */ React28.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), /* @__PURE__ */ React28.createElement(
13161
13357
  "button",
13162
13358
  {
13163
13359
  onClick: () => onDelete(group.id),
13164
13360
  className: "p-1.5 text-slate-600 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors",
13165
13361
  title: "Remover Grupo"
13166
13362
  },
13167
- /* @__PURE__ */ React26.createElement(FiTrash23, { size: 14 })
13363
+ /* @__PURE__ */ React28.createElement(FiTrash23, { size: 14 })
13168
13364
  )))
13169
- ), group.children && group.children.length > 0 && /* @__PURE__ */ React26.createElement("div", { className: "ml-2" }, group.children.map((childGroup, idx) => /* @__PURE__ */ React26.createElement(
13365
+ ), group.children && group.children.length > 0 && /* @__PURE__ */ React28.createElement("div", { className: "ml-2" }, group.children.map((childGroup, idx) => /* @__PURE__ */ React28.createElement(
13170
13366
  GroupItem,
13171
13367
  {
13172
13368
  key: childGroup.id,
@@ -13178,11 +13374,8 @@ var GroupItem = ({
13178
13374
  onAddSubgroup,
13179
13375
  onIndent,
13180
13376
  onOutdent,
13181
- onRemoveAncestry,
13182
- onRequestPickAncestry,
13183
- pickingGroupId,
13184
13377
  onPlayAncestry,
13185
- availableIds,
13378
+ availableMentionItems,
13186
13379
  canEdit
13187
13380
  }
13188
13381
  ))));
@@ -13197,21 +13390,19 @@ function AncestryBoard({
13197
13390
  onSave,
13198
13391
  userRole
13199
13392
  }) {
13200
- const [searchTerm, setSearchTerm] = useState25("");
13201
- const [groups, setGroups] = useState25([]);
13202
- const [isLoaded, setIsLoaded] = useState25(false);
13203
- const [pickingGroupId, setPickingGroupId] = useState25(null);
13204
- const [saveStatus, setSaveStatus] = useState25("idle");
13205
- const canEdit = useMemo11(() => {
13393
+ const [groups, setGroups] = useState26([]);
13394
+ const [isLoaded, setIsLoaded] = useState26(false);
13395
+ const [saveStatus, setSaveStatus] = useState26("idle");
13396
+ const canEdit = useMemo12(() => {
13206
13397
  return userRole !== "viewer";
13207
13398
  }, [userRole]);
13208
- useEffect21(() => {
13399
+ useEffect23(() => {
13209
13400
  if (initialGroups && !isLoaded) {
13210
13401
  setGroups(initialGroups);
13211
13402
  setIsLoaded(true);
13212
13403
  }
13213
13404
  }, [initialGroups, isLoaded]);
13214
- const nodeNamesMap = useMemo11(() => {
13405
+ const nodeNamesMap = useMemo12(() => {
13215
13406
  const map = /* @__PURE__ */ new Map();
13216
13407
  if (availableNodes && Array.isArray(availableNodes)) {
13217
13408
  availableNodes.forEach((node) => {
@@ -13222,10 +13413,10 @@ function AncestryBoard({
13222
13413
  }
13223
13414
  return map;
13224
13415
  }, [availableNodes]);
13225
- const availableIds = useMemo11(() => {
13416
+ const availableIds = useMemo12(() => {
13226
13417
  return new Set(availableAncestries.map((a) => String(a.ancestry_id)));
13227
13418
  }, [availableAncestries]);
13228
- const allMentionableItems = useMemo11(() => {
13419
+ const availableMentionItems = useMemo12(() => {
13229
13420
  const ancestries = availableAncestries.map((a) => ({
13230
13421
  id: String(a.ancestry_id),
13231
13422
  ancestry_id: a.ancestry_id,
@@ -13253,16 +13444,10 @@ function AncestryBoard({
13253
13444
  return groupList.map((g) => ({
13254
13445
  id: g.id,
13255
13446
  text: g.text || "",
13256
- ancestries: (g.ancestries || []).map((a) => ({
13257
- id: a.id || a.ancestry_id,
13258
- ancestry_id: a.ancestry_id,
13259
- name: a.name,
13260
- type: a.type || "ancestry"
13261
- })),
13262
13447
  children: sanitizeGroups(g.children || [])
13263
13448
  }));
13264
13449
  };
13265
- useEffect21(() => {
13450
+ useEffect23(() => {
13266
13451
  if (!isLoaded || !onSave) return;
13267
13452
  const timeoutId = setTimeout(async () => {
13268
13453
  setSaveStatus("saving");
@@ -13280,50 +13465,30 @@ function AncestryBoard({
13280
13465
  }, 3e3);
13281
13466
  return () => clearTimeout(timeoutId);
13282
13467
  }, [groups, isLoaded, onSave]);
13283
- useEffect21(() => {
13468
+ useEffect23(() => {
13284
13469
  if (!isOpen) return;
13285
13470
  const handleKeyDown = (e) => {
13286
13471
  if (e.key === "Escape") {
13287
- if (pickingGroupId) {
13288
- setPickingGroupId(null);
13289
- } else {
13290
- onClose();
13291
- }
13472
+ onClose();
13292
13473
  e.preventDefault();
13293
13474
  e.stopPropagation();
13294
13475
  }
13295
13476
  };
13296
13477
  window.addEventListener("keydown", handleKeyDown);
13297
13478
  return () => window.removeEventListener("keydown", handleKeyDown);
13298
- }, [isOpen, onClose, pickingGroupId]);
13299
- const filtered = useMemo11(() => {
13300
- const term = searchTerm.toLowerCase().trim();
13301
- return allMentionableItems.filter((item) => {
13302
- if (!term) return true;
13303
- const nameMatch = item.name && item.name.toLowerCase().includes(term);
13304
- const descMatch = item.description && item.description.toLowerCase().includes(term);
13305
- let parentMatch = false;
13306
- if (item.type === "ancestry") {
13307
- const parentName = nodeNamesMap.get(String(item.ancestral_node)) || "";
13308
- parentMatch = parentName.toLowerCase().includes(term);
13309
- }
13310
- return nameMatch || descMatch || parentMatch;
13311
- });
13312
- }, [allMentionableItems, searchTerm, nodeNamesMap]);
13479
+ }, [isOpen, onClose]);
13313
13480
  const handleAddRootGroup = () => {
13314
13481
  const newGroup = {
13315
- id: v4_default(),
13482
+ id: uuidv411(),
13316
13483
  text: "",
13317
- ancestries: [],
13318
13484
  children: []
13319
13485
  };
13320
13486
  setGroups((prev) => [newGroup, ...prev]);
13321
13487
  };
13322
13488
  const handleAddSubgroup = (parentId) => {
13323
13489
  const newGroup = {
13324
- id: v4_default(),
13490
+ id: uuidv411(),
13325
13491
  text: "",
13326
- ancestries: [],
13327
13492
  children: []
13328
13493
  };
13329
13494
  const addRecursive = (list) => {
@@ -13394,42 +13559,6 @@ function AncestryBoard({
13394
13559
  return newList;
13395
13560
  });
13396
13561
  };
13397
- const handleRequestPick = (groupId) => {
13398
- setPickingGroupId((prev) => prev === groupId ? null : groupId);
13399
- };
13400
- const handleSelectAncestry = (item) => {
13401
- if (pickingGroupId) {
13402
- setGroups((prev) => {
13403
- const addRecursive = (list) => {
13404
- return list.map((g) => {
13405
- if (g.id === pickingGroupId) {
13406
- const currentId = item.id;
13407
- const exists = g.ancestries.some(
13408
- (a) => (a.id || a.ancestry_id) === currentId && a.type === item.type
13409
- );
13410
- if (exists) return g;
13411
- return { ...g, ancestries: [...g.ancestries, item] };
13412
- }
13413
- return { ...g, children: addRecursive(g.children) };
13414
- });
13415
- };
13416
- return addRecursive(prev);
13417
- });
13418
- setPickingGroupId(null);
13419
- } else {
13420
- let fullItem = item;
13421
- if (item.type === "ancestry" || !item.type) {
13422
- fullItem = availableAncestries.find(
13423
- (a) => String(a.ancestry_id) === String(item.id || item.ancestry_id)
13424
- ) || item;
13425
- } else {
13426
- fullItem = availableNodes.find(
13427
- (n) => String(n.id) === String(item.id)
13428
- ) || item;
13429
- }
13430
- onSelect(fullItem);
13431
- }
13432
- };
13433
13562
  const handlePlayFromGroup = (item) => {
13434
13563
  let fullItem = item;
13435
13564
  if (item.type === "ancestry" || !item.type) {
@@ -13445,52 +13574,34 @@ function AncestryBoard({
13445
13574
  onSelect(fullItem);
13446
13575
  }
13447
13576
  };
13448
- const handleRemoveAncestryFromGroup = (groupId, itemId) => {
13449
- setGroups((prev) => {
13450
- const removeRecursive = (list) => {
13451
- return list.map((g) => {
13452
- if (g.id === groupId) {
13453
- return {
13454
- ...g,
13455
- ancestries: g.ancestries.filter(
13456
- (a) => (a.id || a.ancestry_id) !== itemId
13457
- )
13458
- };
13459
- }
13460
- return { ...g, children: removeRecursive(g.children) };
13461
- });
13462
- };
13463
- return removeRecursive(prev);
13464
- });
13465
- };
13466
13577
  if (!isOpen) return null;
13467
- return /* @__PURE__ */ React26.createElement(
13578
+ return /* @__PURE__ */ React28.createElement(
13468
13579
  "div",
13469
13580
  {
13470
13581
  className: "fixed inset-0 z-[2200] bg-black/80 backdrop-blur-sm flex items-center justify-center p-2",
13471
13582
  onClick: onClose
13472
13583
  },
13473
- /* @__PURE__ */ React26.createElement(
13584
+ /* @__PURE__ */ React28.createElement(
13474
13585
  "div",
13475
13586
  {
13476
- 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",
13587
+ 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",
13477
13588
  onClick: (e) => e.stopPropagation()
13478
13589
  },
13479
- /* @__PURE__ */ React26.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ React26.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React26.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ React26.createElement(FiLayers6, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ React26.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React26.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ React26.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__ */ React26.createElement(React26.Fragment, null, /* @__PURE__ */ React26.createElement(
13590
+ /* @__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(
13480
13591
  FiLoader5,
13481
13592
  {
13482
13593
  className: "animate-spin text-indigo-400",
13483
13594
  size: 12
13484
13595
  }
13485
- ), /* @__PURE__ */ React26.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ React26.createElement(React26.Fragment, null, /* @__PURE__ */ React26.createElement(FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ React26.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ React26.createElement(React26.Fragment, null, /* @__PURE__ */ React26.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ React26.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ React26.createElement("div", { className: "flex items-center gap-3" }, pickingGroupId && /* @__PURE__ */ React26.createElement("span", { className: "text-xs text-indigo-300 font-medium animate-pulse hidden sm:inline-block mr-2" }, "Selecione na lateral..."), canEdit && /* @__PURE__ */ React26.createElement(
13596
+ ), /* @__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(
13486
13597
  "button",
13487
13598
  {
13488
13599
  onClick: handleAddRootGroup,
13489
13600
  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 "
13490
13601
  },
13491
- /* @__PURE__ */ React26.createElement(FiPlus10, { size: 14, className: "text-indigo-400" }),
13492
- /* @__PURE__ */ React26.createElement("span", { className: "hidden sm:inline" }, "Novo Grupo")
13493
- ), /* @__PURE__ */ React26.createElement(
13602
+ /* @__PURE__ */ React28.createElement(FiPlus10, { size: 14, className: "text-indigo-400" }),
13603
+ /* @__PURE__ */ React28.createElement("span", { className: "hidden sm:inline" }, "Novo Grupo")
13604
+ ), /* @__PURE__ */ React28.createElement(
13494
13605
  "button",
13495
13606
  {
13496
13607
  onClick: onClose,
@@ -13498,127 +13609,36 @@ function AncestryBoard({
13498
13609
  },
13499
13610
  "\xD7"
13500
13611
  ))),
13501
- /* @__PURE__ */ React26.createElement("div", { className: "flex flex-1 overflow-hidden" }, /* @__PURE__ */ React26.createElement(
13502
- "div",
13612
+ /* @__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(
13613
+ "button",
13503
13614
  {
13504
- className: `
13505
- flex flex-col border-r border-white/10 transition-all duration-300 flex-none
13506
- ${pickingGroupId ? "w-[25%] border-indigo-500/30" : "w-[20%]"}
13507
- min-w-[280px] max-w-[500px] bg-slate-900
13508
- `
13615
+ onClick: handleAddRootGroup,
13616
+ 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"
13509
13617
  },
13510
- /* @__PURE__ */ React26.createElement("div", { className: "p-3 border-b border-white/5 bg-slate-900/50" }, /* @__PURE__ */ React26.createElement("div", { className: "relative group" }, /* @__PURE__ */ React26.createElement(
13511
- FiSearch8,
13512
- {
13513
- className: `absolute left-3 top-1/2 -translate-y-1/2 transition-colors ${pickingGroupId ? "text-indigo-400" : "text-slate-500 group-focus-within:text-indigo-400"}`
13514
- }
13515
- ), /* @__PURE__ */ React26.createElement(
13516
- "input",
13517
- {
13518
- type: "text",
13519
- placeholder: pickingGroupId ? "Pesquise para adicionar..." : "Pesquisar (Ancestralidades e Nodes)",
13520
- className: `
13521
- w-full rounded-md pl-9 pr-4 py-2 text-sm transition-all focus:outline-none focus:ring-1 focus:ring-indigo-500 border
13522
- ${pickingGroupId ? "bg-indigo-950/30 border-indigo-500/30 text-white placeholder-indigo-300/50" : "bg-slate-950 border-white/10 text-slate-200 placeholder-slate-600"}
13523
- `,
13524
- value: searchTerm,
13525
- onChange: (e) => setSearchTerm(e.target.value),
13526
- autoFocus: !pickingGroupId
13527
- }
13528
- ))),
13529
- /* @__PURE__ */ React26.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-3 space-y-2" }, filtered.map((item) => {
13530
- const isPicking = !!pickingGroupId;
13531
- const isQuest = item.type === "quest";
13532
- const isVersion = item.type === "version";
13533
- const isNode = item.type === "node";
13534
- let Icon = FiLayers6;
13535
- let colorClass = "bg-slate-800 text-indigo-400 border-white/5";
13536
- let pickingColorClass = "bg-indigo-500 text-white border-indigo-400";
13537
- let hoverClass = "hover:bg-indigo-600/10 hover:border-indigo-500/30";
13538
- let badge = null;
13539
- if (isQuest) {
13540
- Icon = FiCheckSquare3;
13541
- colorClass = "bg-slate-800 text-amber-400 border-white/5";
13542
- pickingColorClass = "bg-amber-500 text-white border-amber-400";
13543
- hoverClass = "hover:bg-amber-500/10 hover:border-amber-500/30";
13544
- badge = "Quest";
13545
- } else if (isVersion) {
13546
- Icon = FiGitBranch3;
13547
- colorClass = "bg-slate-800 text-purple-400 border-white/5";
13548
- pickingColorClass = "bg-purple-500 text-white border-purple-400";
13549
- hoverClass = "hover:bg-purple-500/10 hover:border-purple-500/30";
13550
- badge = "Ver";
13551
- } else if (isNode) {
13552
- Icon = FiBox;
13553
- colorClass = "bg-slate-800 text-emerald-400 border-white/5";
13554
- pickingColorClass = "bg-emerald-500 text-white border-emerald-400";
13555
- hoverClass = "hover:bg-emerald-500/10 hover:border-emerald-500/30";
13556
- badge = "Node";
13557
- }
13558
- return /* @__PURE__ */ React26.createElement(
13559
- "div",
13560
- {
13561
- key: `${item.type}-${item.id}`,
13562
- onClick: () => {
13563
- if (isPicking) handleSelectAncestry(item);
13564
- },
13565
- className: `
13566
- group relative flex items-start gap-3 p-3 text-left rounded-lg border transition-all duration-200
13567
- ${isPicking ? `border-indigo-500/30 bg-indigo-500/5 hover:bg-indigo-500/20 hover:border-indigo-400 cursor-pointer` : `border-white/5 bg-slate-800/40 ${hoverClass} cursor-default`}
13568
- `
13569
- },
13570
- /* @__PURE__ */ React26.createElement(
13571
- "div",
13572
- {
13573
- className: `
13574
- mt-0.5 w-8 h-8 rounded-md grid place-content-center shrink-0 border transition-all shadow-lg
13575
- ${isPicking ? pickingColorClass : `${colorClass} group-hover:bg-opacity-80`}
13576
- `
13577
- },
13578
- isPicking ? /* @__PURE__ */ React26.createElement(FiPlus10, { size: 16 }) : /* @__PURE__ */ React26.createElement(Icon, { size: 14 })
13579
- ),
13580
- /* @__PURE__ */ React26.createElement("div", { className: "flex-1 min-w-0 pb-2" }, /* @__PURE__ */ React26.createElement("div", { className: "flex items-center justify-between gap-2" }, /* @__PURE__ */ React26.createElement("h4", { className: "text-sm font-medium text-slate-200 group-hover:text-white truncate transition-colors" }, item.name || "Sem Nome"), badge && /* @__PURE__ */ React26.createElement("span", { className: `text-[9px] px-1 py-0.5 rounded ${isQuest ? "bg-amber-500/10 text-amber-400" : isVersion ? "bg-purple-500/10 text-purple-400" : "bg-emerald-500/10 text-emerald-400"} border border-current opacity-50` }, badge), item.is_private && /* @__PURE__ */ React26.createElement("span", { className: "text-[9px] px-1 py-0.5 rounded bg-amber-500/10 text-amber-300 border border-amber-500/20" }, "Priv")), item.type === "ancestry" && /* @__PURE__ */ React26.createElement("div", { className: "flex items-center gap-1.5 mt-0.5 text-[11px] text-slate-500 group-hover:text-indigo-200/70 transition-colors" }, /* @__PURE__ */ React26.createElement(FiCornerUpRight4, { size: 10 }), /* @__PURE__ */ React26.createElement("span", { className: "truncate max-w-[120px]" }, nodeNamesMap.get(String(item.ancestral_node)) || "Node Desconhecido")), item.description && /* @__PURE__ */ React26.createElement("p", { className: "mt-1.5 text-[11px] text-slate-400 line-clamp-2 leading-relaxed opacity-80" }, item.description)),
13581
- !isPicking && /* @__PURE__ */ React26.createElement(
13582
- "button",
13583
- {
13584
- onClick: (e) => {
13585
- e.stopPropagation();
13586
- handleSelectAncestry(item);
13587
- },
13588
- className: "absolute right-2 bottom-2 opacity-0 group-hover:opacity-100 transition-all duration-300 transform translate-y-2 group-hover:translate-y-0 z-10",
13589
- title: "Renderizar / Focar"
13590
- },
13591
- /* @__PURE__ */ React26.createElement("div", { className: "bg-indigo-500 text-white p-2 rounded-full shadow-lg hover:bg-indigo-400 hover:scale-110 transition-all" }, /* @__PURE__ */ React26.createElement(FiPlay, { size: 14, className: "ml-0.5" }))
13592
- )
13593
- );
13594
- }))
13595
- ), /* @__PURE__ */ React26.createElement("div", { className: "flex flex-col flex-1 bg-slate-950/30" }, /* @__PURE__ */ React26.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-6 space-y-4" }, groups.length === 0 ? /* @__PURE__ */ React26.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 m-4 bg-slate-900/20" }, /* @__PURE__ */ React26.createElement(FiLayers6, { size: 24, className: "opacity-20" }), /* @__PURE__ */ React26.createElement("p", { className: "text-xs text-center px-4" }, canEdit ? /* @__PURE__ */ React26.createElement(React26.Fragment, null, "Nenhum grupo criado.", /* @__PURE__ */ React26.createElement("br", null), 'Use o bot\xE3o "Novo Grupo" acima.') : /* @__PURE__ */ React26.createElement(React26.Fragment, null, "Nenhum grupo dispon\xEDvel para visualiza\xE7\xE3o."))) : groups.map((group, index) => /* @__PURE__ */ React26.createElement(
13618
+ "Criar Primeiro Grupo"
13619
+ )) : groups.map((group, index) => /* @__PURE__ */ React28.createElement(
13596
13620
  GroupItem,
13597
13621
  {
13598
13622
  key: group.id,
13599
13623
  index,
13600
13624
  group,
13601
- siblings: groups,
13602
13625
  onUpdate: handleUpdateGroup,
13603
13626
  onDelete: handleDeleteGroup,
13604
13627
  onAddSubgroup: handleAddSubgroup,
13605
13628
  onIndent: handleIndent,
13606
13629
  onOutdent: handleOutdent,
13607
- onRemoveAncestry: handleRemoveAncestryFromGroup,
13608
- onRequestPickAncestry: handleRequestPick,
13609
- pickingGroupId,
13610
13630
  onPlayAncestry: handlePlayFromGroup,
13611
- availableIds,
13631
+ availableMentionItems,
13612
13632
  canEdit
13613
13633
  }
13614
13634
  ))))),
13615
- /* @__PURE__ */ React26.createElement("div", { className: "px-5 py-2 border-t border-white/10 bg-slate-950/50 text-xs text-slate-500 flex justify-between flex-shrink-0" }, /* @__PURE__ */ React26.createElement("span", null, filtered.length, " itens encontrados"), /* @__PURE__ */ React26.createElement("span", null, groups.length, " grupos raiz"))
13635
+ /* @__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"))
13616
13636
  )
13617
13637
  );
13618
13638
  }
13619
13639
 
13620
13640
  // src/components/MilestoneManager.jsx
13621
- import React27, { useState as useState26 } from "react";
13641
+ import React29, { useState as useState27 } from "react";
13622
13642
  import { FiTarget as FiTarget4, FiPlus as FiPlus11, FiTrash2 as FiTrash24, FiX as FiX9, FiCheck as FiCheck12, FiPauseCircle, FiPlayCircle } from "react-icons/fi";
13623
13643
  function MilestoneManager({
13624
13644
  isOpen,
@@ -13627,7 +13647,7 @@ function MilestoneManager({
13627
13647
  onUpdateMilestones = () => {
13628
13648
  }
13629
13649
  }) {
13630
- const [newMilestoneName, setNewMilestoneName] = useState26("");
13650
+ const [newMilestoneName, setNewMilestoneName] = useState27("");
13631
13651
  const handleAddMilestone = () => {
13632
13652
  if (!newMilestoneName.trim()) return;
13633
13653
  const newMilestone = {
@@ -13652,27 +13672,27 @@ function MilestoneManager({
13652
13672
  };
13653
13673
  if (!isOpen) return null;
13654
13674
  const swallow = (e) => e.stopPropagation();
13655
- return /* @__PURE__ */ React27.createElement(
13675
+ return /* @__PURE__ */ React29.createElement(
13656
13676
  "div",
13657
13677
  {
13658
13678
  className: "fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/40 backdrop-blur-sm animate-in fade-in duration-300",
13659
13679
  onClick: onClose
13660
13680
  },
13661
- /* @__PURE__ */ React27.createElement(
13681
+ /* @__PURE__ */ React29.createElement(
13662
13682
  "div",
13663
13683
  {
13664
13684
  className: "w-full max-w-lg bg-slate-900/90 backdrop-blur-xl border border-white/10 rounded-2xl shadow-2xl flex flex-col max-h-[85vh] animate-in zoom-in-95 duration-300",
13665
13685
  onClick: swallow
13666
13686
  },
13667
- /* @__PURE__ */ React27.createElement("div", { className: "px-6 py-4 border-b border-white/10 flex items-center justify-between" }, /* @__PURE__ */ React27.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React27.createElement("div", { className: "p-2 rounded-lg bg-indigo-500/20 text-indigo-400" }, /* @__PURE__ */ React27.createElement(FiTarget4, { size: 20 })), /* @__PURE__ */ React27.createElement("div", null, /* @__PURE__ */ React27.createElement("h2", { className: "text-lg font-semibold text-slate-100" }, "Milestones"), /* @__PURE__ */ React27.createElement("p", { className: "text-xs text-slate-400" }, "Gerencie os marcos do seu cen\xE1rio"))), /* @__PURE__ */ React27.createElement(
13687
+ /* @__PURE__ */ React29.createElement("div", { className: "px-6 py-4 border-b border-white/10 flex items-center justify-between" }, /* @__PURE__ */ React29.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React29.createElement("div", { className: "p-2 rounded-lg bg-indigo-500/20 text-indigo-400" }, /* @__PURE__ */ React29.createElement(FiTarget4, { size: 20 })), /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("h2", { className: "text-lg font-semibold text-slate-100" }, "Milestones"), /* @__PURE__ */ React29.createElement("p", { className: "text-xs text-slate-400" }, "Gerencie os marcos do seu cen\xE1rio"))), /* @__PURE__ */ React29.createElement(
13668
13688
  "button",
13669
13689
  {
13670
13690
  onClick: onClose,
13671
13691
  className: "p-2 rounded-full hover:bg-white/10 text-slate-400 hover:text-white transition-colors"
13672
13692
  },
13673
- /* @__PURE__ */ React27.createElement(FiX9, { size: 20 })
13693
+ /* @__PURE__ */ React29.createElement(FiX9, { size: 20 })
13674
13694
  )),
13675
- /* @__PURE__ */ React27.createElement("div", { className: "px-6 py-5 bg-white/5 border-b border-white/5" }, /* @__PURE__ */ React27.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React27.createElement(
13695
+ /* @__PURE__ */ React29.createElement("div", { className: "px-6 py-5 bg-white/5 border-b border-white/5" }, /* @__PURE__ */ React29.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React29.createElement(
13676
13696
  "input",
13677
13697
  {
13678
13698
  type: "text",
@@ -13682,47 +13702,47 @@ function MilestoneManager({
13682
13702
  placeholder: "Adicionar novo milestone...",
13683
13703
  className: "flex-1 bg-slate-800/50 border border-white/10 rounded-xl px-4 py-2.5 text-sm text-slate-100 placeholder:text-slate-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/40 focus:border-indigo-500/50 transition-all shadow-inner"
13684
13704
  }
13685
- ), /* @__PURE__ */ React27.createElement(
13705
+ ), /* @__PURE__ */ React29.createElement(
13686
13706
  "button",
13687
13707
  {
13688
13708
  onClick: handleAddMilestone,
13689
13709
  disabled: !newMilestoneName.trim(),
13690
13710
  className: "px-4 py-2.5 rounded-xl bg-indigo-600 hover:bg-indigo-500 disabled:opacity-30 disabled:cursor-not-allowed text-white text-sm font-medium transition-all flex items-center gap-2 shadow-lg shadow-indigo-500/20 active:scale-95"
13691
13711
  },
13692
- /* @__PURE__ */ React27.createElement(FiPlus11, { size: 18 }),
13712
+ /* @__PURE__ */ React29.createElement(FiPlus11, { size: 18 }),
13693
13713
  "Adicionar"
13694
13714
  ))),
13695
- /* @__PURE__ */ React27.createElement("div", { className: "flex-1 overflow-y-auto px-6 py-4 custom-scrollbar" }, milestones.length > 0 ? /* @__PURE__ */ React27.createElement("div", { className: "space-y-3" }, milestones.map((ms) => /* @__PURE__ */ React27.createElement(
13715
+ /* @__PURE__ */ React29.createElement("div", { className: "flex-1 overflow-y-auto px-6 py-4 custom-scrollbar" }, milestones.length > 0 ? /* @__PURE__ */ React29.createElement("div", { className: "space-y-3" }, milestones.map((ms) => /* @__PURE__ */ React29.createElement(
13696
13716
  "div",
13697
13717
  {
13698
13718
  key: ms.id,
13699
13719
  className: `group flex items-center justify-between p-4 rounded-xl border transition-all duration-200 ${ms.status === "active" ? "bg-slate-800/40 border-indigo-500/20 hover:border-indigo-500/40" : "bg-slate-900/40 border-white/5 opacity-60 grayscale-[0.3]"}`
13700
13720
  },
13701
- /* @__PURE__ */ React27.createElement("div", { className: "flex items-center gap-3 min-w-0" }, /* @__PURE__ */ React27.createElement(
13721
+ /* @__PURE__ */ React29.createElement("div", { className: "flex items-center gap-3 min-w-0" }, /* @__PURE__ */ React29.createElement(
13702
13722
  "div",
13703
13723
  {
13704
13724
  className: `w-2 h-2 rounded-full shrink-0 ${ms.status === "active" ? "bg-indigo-400 shadow-[0_0_8px_rgba(129,140,248,0.6)]" : "bg-slate-600"}`
13705
13725
  }
13706
- ), /* @__PURE__ */ React27.createElement("div", { className: "flex flex-col min-w-0" }, /* @__PURE__ */ React27.createElement("span", { className: `text-sm font-medium truncate ${ms.status === "active" ? "text-slate-100" : "text-slate-400 line-through"}` }, ms.name), /* @__PURE__ */ React27.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-wider" }, ms.status === "active" ? "Ativo" : "Inativo"))),
13707
- /* @__PURE__ */ React27.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React27.createElement(
13726
+ ), /* @__PURE__ */ React29.createElement("div", { className: "flex flex-col min-w-0" }, /* @__PURE__ */ React29.createElement("span", { className: `text-sm font-medium truncate ${ms.status === "active" ? "text-slate-100" : "text-slate-400 line-through"}` }, ms.name), /* @__PURE__ */ React29.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-wider" }, ms.status === "active" ? "Ativo" : "Inativo"))),
13727
+ /* @__PURE__ */ React29.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React29.createElement(
13708
13728
  "button",
13709
13729
  {
13710
13730
  onClick: () => handleToggleMilestoneStatus(ms.id),
13711
13731
  className: `p-2 rounded-lg transition-all ${ms.status === "active" ? "hover:bg-slate-700 text-slate-400" : "hover:bg-indigo-500/20 text-indigo-400"}`,
13712
13732
  title: ms.status === "active" ? "Inativar" : "Ativar"
13713
13733
  },
13714
- ms.status === "active" ? /* @__PURE__ */ React27.createElement(FiPauseCircle, { size: 18 }) : /* @__PURE__ */ React27.createElement(FiPlayCircle, { size: 18 })
13715
- ), /* @__PURE__ */ React27.createElement(
13734
+ ms.status === "active" ? /* @__PURE__ */ React29.createElement(FiPauseCircle, { size: 18 }) : /* @__PURE__ */ React29.createElement(FiPlayCircle, { size: 18 })
13735
+ ), /* @__PURE__ */ React29.createElement(
13716
13736
  "button",
13717
13737
  {
13718
13738
  onClick: () => handleRemoveMilestone(ms.id),
13719
13739
  className: "p-2 rounded-lg hover:bg-red-500/20 text-slate-500 hover:text-red-400 transition-all",
13720
13740
  title: "Remover"
13721
13741
  },
13722
- /* @__PURE__ */ React27.createElement(FiTrash24, { size: 18 })
13742
+ /* @__PURE__ */ React29.createElement(FiTrash24, { size: 18 })
13723
13743
  ))
13724
- ))) : /* @__PURE__ */ React27.createElement("div", { className: "flex flex-col items-center justify-center py-12 text-center" }, /* @__PURE__ */ React27.createElement("div", { className: "p-4 rounded-full bg-slate-800/30 text-slate-600 mb-4" }, /* @__PURE__ */ React27.createElement(FiTarget4, { size: 40 })), /* @__PURE__ */ React27.createElement("h3", { className: "text-slate-400 font-medium" }, "Nenhum milestone definido"), /* @__PURE__ */ React27.createElement("p", { className: "text-xs text-slate-600 max-w-[200px] mt-1 italic" }, "Defina marcos importantes para orientar o progresso do projeto."))),
13725
- /* @__PURE__ */ React27.createElement("div", { className: "px-6 py-4 border-t border-white/5 bg-slate-900/50 flex items-center justify-between rounded-b-2xl" }, /* @__PURE__ */ React27.createElement("div", { className: "flex gap-4" }, /* @__PURE__ */ React27.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ React27.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-widest" }, "Total:"), /* @__PURE__ */ React27.createElement("span", { className: "text-xs font-bold text-slate-300" }, milestones.length)), /* @__PURE__ */ React27.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ React27.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-widest" }, "Ativos:"), /* @__PURE__ */ React27.createElement("span", { className: "text-xs font-bold text-indigo-400" }, milestones.filter((m) => m.status === "active").length))))
13744
+ ))) : /* @__PURE__ */ React29.createElement("div", { className: "flex flex-col items-center justify-center py-12 text-center" }, /* @__PURE__ */ React29.createElement("div", { className: "p-4 rounded-full bg-slate-800/30 text-slate-600 mb-4" }, /* @__PURE__ */ React29.createElement(FiTarget4, { size: 40 })), /* @__PURE__ */ React29.createElement("h3", { className: "text-slate-400 font-medium" }, "Nenhum milestone definido"), /* @__PURE__ */ React29.createElement("p", { className: "text-xs text-slate-600 max-w-[200px] mt-1 italic" }, "Defina marcos importantes para orientar o progresso do projeto."))),
13745
+ /* @__PURE__ */ React29.createElement("div", { className: "px-6 py-4 border-t border-white/5 bg-slate-900/50 flex items-center justify-between rounded-b-2xl" }, /* @__PURE__ */ React29.createElement("div", { className: "flex gap-4" }, /* @__PURE__ */ React29.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ React29.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-widest" }, "Total:"), /* @__PURE__ */ React29.createElement("span", { className: "text-xs font-bold text-slate-300" }, milestones.length)), /* @__PURE__ */ React29.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ React29.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-widest" }, "Ativos:"), /* @__PURE__ */ React29.createElement("span", { className: "text-xs font-bold text-indigo-400" }, milestones.filter((m) => m.status === "active").length))))
13726
13746
  )
13727
13747
  );
13728
13748
  }
@@ -13814,7 +13834,7 @@ function XViewScene({
13814
13834
  const searchParams = useSearchParams();
13815
13835
  const focusNodeId = searchParams == null ? void 0 : searchParams.get("focus");
13816
13836
  const focusAncestryId = searchParams == null ? void 0 : searchParams.get("ancestry");
13817
- const viewParams = useMemo12(() => {
13837
+ const viewParams = useMemo13(() => {
13818
13838
  if (encryptedConfig) {
13819
13839
  const data = decryptData(encryptedConfig);
13820
13840
  if (data) {
@@ -13823,7 +13843,7 @@ function XViewScene({
13823
13843
  }
13824
13844
  return null;
13825
13845
  }, [encryptedConfig, session]);
13826
- useEffect22(() => {
13846
+ useEffect24(() => {
13827
13847
  async function verifyPermission() {
13828
13848
  if (!viewParams || !session || !check_user_permission) return;
13829
13849
  const { id, type, owner_id } = viewParams;
@@ -13860,21 +13880,21 @@ function XViewScene({
13860
13880
  setIsLoading(false);
13861
13881
  }
13862
13882
  }, [viewParams, session, status, check_user_permission]);
13863
- const sceneConfigId = useMemo12(() => (viewParams == null ? void 0 : viewParams.id) || null, [viewParams]);
13864
- const ownerId = useMemo12(() => (viewParams == null ? void 0 : viewParams.owner_id) || null, [viewParams]);
13865
- const dbSaveUrl = useMemo12(() => {
13883
+ const sceneConfigId = useMemo13(() => (viewParams == null ? void 0 : viewParams.id) || null, [viewParams]);
13884
+ const ownerId = useMemo13(() => (viewParams == null ? void 0 : viewParams.owner_id) || null, [viewParams]);
13885
+ const dbSaveUrl = useMemo13(() => {
13866
13886
  if (ownerId && sceneConfigId) {
13867
13887
  return `x_view_dbs/${ownerId}/${sceneConfigId}`;
13868
13888
  }
13869
13889
  return null;
13870
13890
  }, [ownerId, sceneConfigId]);
13871
- const sceneSaveUrl = useMemo12(() => {
13891
+ const sceneSaveUrl = useMemo13(() => {
13872
13892
  if (ownerId && sceneConfigId) {
13873
13893
  return `x_view_scenes/${ownerId}/${sceneConfigId}`;
13874
13894
  }
13875
13895
  return null;
13876
13896
  }, [ownerId, sceneConfigId]);
13877
- const ancestry_save_url = useMemo12(() => {
13897
+ const ancestry_save_url = useMemo13(() => {
13878
13898
  if (ownerId && sceneConfigId) {
13879
13899
  return `x_view_ancestry/${ownerId}/${sceneConfigId}`;
13880
13900
  }
@@ -13883,42 +13903,42 @@ function XViewScene({
13883
13903
  const sceneDataRef = useRef19(null);
13884
13904
  const parentDataRef = useRef19(null);
13885
13905
  const ancestryDataRef = useRef19(null);
13886
- const [isLoading, setIsLoading] = useState27(true);
13887
- const [permissionStatus, setPermissionStatus] = useState27("loading");
13888
- const [userPermissionRole, setUserPermissionRole] = useState27(null);
13889
- const [viewMembers, setViewMembers] = useState27([]);
13890
- const [isInitialized, setIsInitialized] = useState27(false);
13891
- const [sceneVersion, setSceneVersion] = useState27(0);
13892
- const [contextMenu, setContextMenu] = useState27({
13906
+ const [isLoading, setIsLoading] = useState28(true);
13907
+ const [permissionStatus, setPermissionStatus] = useState28("loading");
13908
+ const [userPermissionRole, setUserPermissionRole] = useState28(null);
13909
+ const [viewMembers, setViewMembers] = useState28([]);
13910
+ const [isInitialized, setIsInitialized] = useState28(false);
13911
+ const [sceneVersion, setSceneVersion] = useState28(0);
13912
+ const [contextMenu, setContextMenu] = useState28({
13893
13913
  visible: false,
13894
13914
  x: 0,
13895
13915
  y: 0,
13896
13916
  nodeData: null
13897
13917
  });
13898
- const [multiContextMenu, setMultiContextMenu] = useState27({
13918
+ const [multiContextMenu, setMultiContextMenu] = useState28({
13899
13919
  visible: false,
13900
13920
  x: 0,
13901
13921
  y: 0,
13902
13922
  nodeIds: null
13903
13923
  });
13904
- const [relationshipMenu, setRelationshipMenu] = useState27({
13924
+ const [relationshipMenu, setRelationshipMenu] = useState28({
13905
13925
  visible: false,
13906
13926
  x: 0,
13907
13927
  y: 0,
13908
13928
  linkObject: null
13909
13929
  });
13910
- const [creationMode, setCreationMode] = useState27({
13930
+ const [creationMode, setCreationMode] = useState28({
13911
13931
  isActive: false,
13912
13932
  sourceNodeData: null
13913
13933
  });
13914
- const [versionMode, setVersionMode] = useState27({
13934
+ const [versionMode, setVersionMode] = useState28({
13915
13935
  isActive: false,
13916
13936
  sourceNodeData: null
13917
13937
  });
13918
- const [questMode, setQuestMode] = useState27({ isActive: false });
13919
- const [hasFocusedInitial, setHasFocusedInitial] = useState27(false);
13920
- const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = useState27(false);
13921
- const [ancestryMode, setAncestryMode] = useState27({
13938
+ const [questMode, setQuestMode] = useState28({ isActive: false });
13939
+ const [hasFocusedInitial, setHasFocusedInitial] = useState28(false);
13940
+ const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = useState28(false);
13941
+ const [ancestryMode, setAncestryMode] = useState28({
13922
13942
  isActive: false,
13923
13943
  tree: null,
13924
13944
  selectedParentId: null,
@@ -13929,38 +13949,38 @@ function XViewScene({
13929
13949
  ancestryDescriptionSections: [],
13930
13950
  isAddingNodes: false
13931
13951
  });
13932
- const [readingMode, setReadingMode] = useState27({
13952
+ const [readingMode, setReadingMode] = useState28({
13933
13953
  isActive: false,
13934
13954
  ancestry: null,
13935
13955
  branchStack: [],
13936
13956
  autoAbstraction: false
13937
13957
  });
13938
- const [formPosition, setFormPosition] = useState27({
13958
+ const [formPosition, setFormPosition] = useState28({
13939
13959
  left: 16,
13940
13960
  top: 16,
13941
13961
  opacity: 0
13942
13962
  });
13943
- const [detailsNode, setDetailsNode] = useState27(null);
13944
- const [detailsLink, setDetailsLink] = useState27(null);
13945
- const [ancestryLinkDetails, setAncestryLinkDetails] = useState27(null);
13946
- const [imageViewer, setImageViewer] = useState27({
13963
+ const [detailsNode, setDetailsNode] = useState28(null);
13964
+ const [detailsLink, setDetailsLink] = useState28(null);
13965
+ const [ancestryLinkDetails, setAncestryLinkDetails] = useState28(null);
13966
+ const [imageViewer, setImageViewer] = useState28({
13947
13967
  visible: false,
13948
13968
  images: [],
13949
13969
  startIndex: 0
13950
13970
  });
13951
- const [editingAncestryRel, setEditingAncestryRel] = useState27({
13971
+ const [editingAncestryRel, setEditingAncestryRel] = useState28({
13952
13972
  visible: false,
13953
13973
  data: null,
13954
13974
  path: null
13955
13975
  });
13956
- const [isImportModalOpen, setIsImportModalOpen] = useState27(false);
13957
- const [importSuccessMessage, setImportSuccessMessage] = useState27("");
13958
- const [invalidTargetError, setInvalidTargetError] = useState27(null);
13959
- const [highlightedNodeId, setHighlightedNodeId] = useState27(null);
13960
- const [isAncestryBoardOpen, setIsAncestryBoardOpen] = useState27(false);
13961
- const [ancestryBoardData, setAncestryBoardData] = useState27([]);
13962
- const [isSidebarOpen, setIsSidebarOpen] = useState27(false);
13963
- const [isMilestoneManagerOpen, setIsMilestoneManagerOpen] = useState27(false);
13976
+ const [isImportModalOpen, setIsImportModalOpen] = useState28(false);
13977
+ const [importSuccessMessage, setImportSuccessMessage] = useState28("");
13978
+ const [invalidTargetError, setInvalidTargetError] = useState28(null);
13979
+ const [highlightedNodeId, setHighlightedNodeId] = useState28(null);
13980
+ const [isAncestryBoardOpen, setIsAncestryBoardOpen] = useState28(false);
13981
+ const [ancestryBoardData, setAncestryBoardData] = useState28([]);
13982
+ const [isSidebarOpen, setIsSidebarOpen] = useState28(false);
13983
+ const [isMilestoneManagerOpen, setIsMilestoneManagerOpen] = useState28(false);
13964
13984
  const mountRef = useRef19(null);
13965
13985
  const tooltipRef = useRef19(null);
13966
13986
  const formRef = useRef19(null);
@@ -14028,10 +14048,10 @@ function XViewScene({
14028
14048
  minWidth: 320,
14029
14049
  maxWidth: maxReadPanelW
14030
14050
  });
14031
- useEffect22(() => {
14051
+ useEffect24(() => {
14032
14052
  stateRef.current.ancestry = ancestryMode;
14033
14053
  }, [ancestryMode]);
14034
- useEffect22(() => {
14054
+ useEffect24(() => {
14035
14055
  var _a2;
14036
14056
  if (!isInitialized) return;
14037
14057
  const map = /* @__PURE__ */ new Map();
@@ -14593,7 +14613,7 @@ function XViewScene({
14593
14613
  }
14594
14614
  });
14595
14615
  }, []);
14596
- useEffect22(() => {
14616
+ useEffect24(() => {
14597
14617
  async function fetchAllData(configPath, ownerId2) {
14598
14618
  var _a2, _b2;
14599
14619
  if (!get_scene_view_data) {
@@ -14958,7 +14978,7 @@ function XViewScene({
14958
14978
  camTween.start();
14959
14979
  }
14960
14980
  }, [addOrUpdateNodeMesh]);
14961
- useEffect22(() => {
14981
+ useEffect24(() => {
14962
14982
  if (!isInitialized || !sceneDataRef.current) return;
14963
14983
  const currentMount = mountRef.current;
14964
14984
  if (!currentMount) return;
@@ -15962,7 +15982,7 @@ function XViewScene({
15962
15982
  };
15963
15983
  });
15964
15984
  }, []);
15965
- const actionHandlerContext = useMemo12(
15985
+ const actionHandlerContext = useMemo13(
15966
15986
  () => {
15967
15987
  var _a2;
15968
15988
  return {
@@ -17071,7 +17091,7 @@ function XViewScene({
17071
17091
  }
17072
17092
  setHighlightedNodeId(nodeId);
17073
17093
  }, []);
17074
- const activeNodeBranches = useMemo12(() => {
17094
+ const activeNodeBranches = useMemo13(() => {
17075
17095
  if (!highlightedNodeId || !readingMode.ancestry || !readingMode.ancestry.tree)
17076
17096
  return null;
17077
17097
  const fullTree = buildFullAncestryTree(
@@ -17115,7 +17135,7 @@ function XViewScene({
17115
17135
  readingMode.branchStack,
17116
17136
  ancestryDataRef.current
17117
17137
  ]);
17118
- const backNavigationInfo = useMemo12(() => {
17138
+ const backNavigationInfo = useMemo13(() => {
17119
17139
  const { branchStack } = readingMode;
17120
17140
  if (!branchStack || branchStack.length === 0) return null;
17121
17141
  const lastStep = branchStack[branchStack.length - 1];
@@ -17126,7 +17146,7 @@ function XViewScene({
17126
17146
  name: "Voltar para anterior"
17127
17147
  };
17128
17148
  }, [readingMode.branchStack]);
17129
- const getReadModeDisplayContext = useMemo12(() => {
17149
+ const getReadModeDisplayContext = useMemo13(() => {
17130
17150
  const { ancestry, branchStack } = readingMode;
17131
17151
  if (!ancestry) return null;
17132
17152
  if (branchStack.length === 0) {
@@ -17169,7 +17189,7 @@ function XViewScene({
17169
17189
  customProperties: branchProps
17170
17190
  };
17171
17191
  }, [readingMode, buildFullAncestryTree, ancestryDataRef.current]);
17172
- const readModeAbstractionTree = useMemo12(() => {
17192
+ const readModeAbstractionTree = useMemo13(() => {
17173
17193
  if (!readingMode.isActive || !readingMode.ancestry || !readingMode.ancestry.abstraction_tree) {
17174
17194
  return null;
17175
17195
  }
@@ -17814,7 +17834,7 @@ function XViewScene({
17814
17834
  },
17815
17835
  [sceneConfigId, viewParams, session, save_ancestry_board_action, ownerId]
17816
17836
  );
17817
- const existingNodeTypes = useMemo12(() => {
17837
+ const existingNodeTypes = useMemo13(() => {
17818
17838
  if (!parentDataRef.current) {
17819
17839
  return [];
17820
17840
  }
@@ -17826,7 +17846,7 @@ function XViewScene({
17826
17846
  ).filter((t) => Boolean(t) && String(t).toLowerCase() !== "quest");
17827
17847
  return [...new Set(allTypes)];
17828
17848
  }, [parentDataRef.current, sceneVersion]);
17829
- const searchableDbNodes = useMemo12(() => {
17849
+ const searchableDbNodes = useMemo13(() => {
17830
17850
  var _a2;
17831
17851
  if (!parentDataRef.current) {
17832
17852
  return [];
@@ -17945,13 +17965,13 @@ function XViewScene({
17945
17965
  console.error("Erro na chamada de save_view_data:", error);
17946
17966
  }
17947
17967
  }, [sceneSaveUrl, save_view_data, sceneConfigId, viewParams == null ? void 0 : viewParams.type]);
17948
- const allAvailableNodes = useMemo12(() => {
17968
+ const allAvailableNodes = useMemo13(() => {
17949
17969
  if (!parentDataRef.current) return [];
17950
17970
  return Object.values(parentDataRef.current).flatMap(
17951
17971
  (fileData) => fileData.nodes || []
17952
17972
  );
17953
17973
  }, [sceneVersion, isInitialized]);
17954
- const allAvailableAncestries = useMemo12(() => {
17974
+ const allAvailableAncestries = useMemo13(() => {
17955
17975
  return ancestryDataRef.current || [];
17956
17976
  }, [sceneVersion, isInitialized]);
17957
17977
  const handleOpenReference = useCallback4(
@@ -18015,7 +18035,7 @@ function XViewScene({
18015
18035
  },
18016
18036
  [tweenToTarget]
18017
18037
  );
18018
- const availableDatasets = useMemo12(() => {
18038
+ const availableDatasets = useMemo13(() => {
18019
18039
  if (!sceneDataRef.current || !parentDataRef.current) return [];
18020
18040
  return sceneDataRef.current.parent_dbs.map((db) => {
18021
18041
  var _a2;
@@ -18029,7 +18049,7 @@ function XViewScene({
18029
18049
  String(creationMode.sourceNodeData.id)
18030
18050
  )) == null ? void 0 : _c.parentFileId : null;
18031
18051
  const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
18032
- useEffect22(() => {
18052
+ useEffect24(() => {
18033
18053
  if (isInitialized && focusNodeId && !hasFocusedInitial) {
18034
18054
  const nodeObjects = stateRef.current.nodeObjects || {};
18035
18055
  const targetMesh = nodeObjects[String(focusNodeId)];
@@ -18052,7 +18072,7 @@ function XViewScene({
18052
18072
  hasFocusedInitial,
18053
18073
  tweenToTarget
18054
18074
  ]);
18055
- useEffect22(() => {
18075
+ useEffect24(() => {
18056
18076
  if (isInitialized && focusAncestryId && !hasOpenedInitialAncestry) {
18057
18077
  const ancestries = ancestryDataRef.current || [];
18058
18078
  const targetAncestry = ancestries.find(
@@ -18077,7 +18097,7 @@ function XViewScene({
18077
18097
  hasOpenedInitialAncestry,
18078
18098
  handleStartReadingAncestry
18079
18099
  ]);
18080
- useEffect22(() => {
18100
+ useEffect24(() => {
18081
18101
  function handleKeyDown(event) {
18082
18102
  var _a2, _b2, _c2;
18083
18103
  const context = actionHandlerContext;
@@ -18200,10 +18220,10 @@ function XViewScene({
18200
18220
  // <-- handleCancelQuest adicionado aqui
18201
18221
  ]);
18202
18222
  if (isLoading || status === "loading" || permissionStatus === "loading") {
18203
- return /* @__PURE__ */ React28.createElement(LoadingScreen, null);
18223
+ return /* @__PURE__ */ React30.createElement(LoadingScreen, null);
18204
18224
  }
18205
18225
  if (permissionStatus === "denied") {
18206
- return /* @__PURE__ */ React28.createElement("div", { className: "flex flex-col items-center justify-center min-h-screen w-full bg-slate-950 text-white" }, /* @__PURE__ */ React28.createElement("div", { className: "bg-slate-900/50 p-8 rounded-2xl border border-slate-800 shadow-2xl text-center max-w-md" }, /* @__PURE__ */ React28.createElement("div", { className: "mb-4 text-red-500" }, /* @__PURE__ */ React28.createElement(
18226
+ return /* @__PURE__ */ React30.createElement("div", { className: "flex flex-col items-center justify-center min-h-screen w-full bg-slate-950 text-white" }, /* @__PURE__ */ React30.createElement("div", { className: "bg-slate-900/50 p-8 rounded-2xl border border-slate-800 shadow-2xl text-center max-w-md" }, /* @__PURE__ */ React30.createElement("div", { className: "mb-4 text-red-500" }, /* @__PURE__ */ React30.createElement(
18207
18227
  "svg",
18208
18228
  {
18209
18229
  xmlns: "http://www.w3.org/2000/svg",
@@ -18213,7 +18233,7 @@ function XViewScene({
18213
18233
  stroke: "currentColor",
18214
18234
  className: "w-16 h-16 mx-auto"
18215
18235
  },
18216
- /* @__PURE__ */ React28.createElement(
18236
+ /* @__PURE__ */ React30.createElement(
18217
18237
  "path",
18218
18238
  {
18219
18239
  strokeLinecap: "round",
@@ -18221,13 +18241,13 @@ function XViewScene({
18221
18241
  d: "M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
18222
18242
  }
18223
18243
  )
18224
- )), /* @__PURE__ */ React28.createElement("h2", { className: "text-2xl font-bold mb-2" }, "Acesso Negado"), /* @__PURE__ */ React28.createElement("p", { className: "text-slate-400 mb-6" }, "Voc\xEA n\xE3o tem permiss\xE3o para acessar este conte\xFAdo. Solicite acesso ao propriet\xE1rio ou verifique se est\xE1 na conta correta."), /* @__PURE__ */ React28.createElement(
18244
+ )), /* @__PURE__ */ React30.createElement("h2", { className: "text-2xl font-bold mb-2" }, "Acesso Negado"), /* @__PURE__ */ React30.createElement("p", { className: "text-slate-400 mb-6" }, "Voc\xEA n\xE3o tem permiss\xE3o para acessar este conte\xFAdo. Solicite acesso ao propriet\xE1rio ou verifique se est\xE1 na conta correta."), /* @__PURE__ */ React30.createElement(
18225
18245
  "button",
18226
18246
  {
18227
18247
  onClick: () => router.push("/dashboard/scenes"),
18228
18248
  className: "flex items-center justify-center gap-2 w-full py-3 px-4 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors font-medium"
18229
18249
  },
18230
- /* @__PURE__ */ React28.createElement(
18250
+ /* @__PURE__ */ React30.createElement(
18231
18251
  "svg",
18232
18252
  {
18233
18253
  xmlns: "http://www.w3.org/2000/svg",
@@ -18237,7 +18257,7 @@ function XViewScene({
18237
18257
  stroke: "currentColor",
18238
18258
  className: "w-5 h-5"
18239
18259
  },
18240
- /* @__PURE__ */ React28.createElement(
18260
+ /* @__PURE__ */ React30.createElement(
18241
18261
  "path",
18242
18262
  {
18243
18263
  strokeLinecap: "round",
@@ -18249,7 +18269,7 @@ function XViewScene({
18249
18269
  "Voltar para Scenes"
18250
18270
  )));
18251
18271
  }
18252
- return /* @__PURE__ */ React28.createElement(
18272
+ return /* @__PURE__ */ React30.createElement(
18253
18273
  "div",
18254
18274
  {
18255
18275
  ref: mountRef,
@@ -18261,7 +18281,7 @@ function XViewScene({
18261
18281
  cursor: stateRef.current.connection.isActive || stateRef.current.relink.isActive || ancestryMode.isActive ? "crosshair" : creationMode.isActive ? "default" : "grab"
18262
18282
  }
18263
18283
  },
18264
- userPermissionRole !== "link_viewer" && /* @__PURE__ */ React28.createElement(
18284
+ userPermissionRole !== "link_viewer" && /* @__PURE__ */ React30.createElement(
18265
18285
  XViewSidebar,
18266
18286
  {
18267
18287
  dbNodes: searchableDbNodes,
@@ -18285,7 +18305,7 @@ function XViewScene({
18285
18305
  onOpenMilestoneManager: () => setIsMilestoneManagerOpen(true)
18286
18306
  }
18287
18307
  ),
18288
- creationMode.isActive && /* @__PURE__ */ React28.createElement(
18308
+ creationMode.isActive && /* @__PURE__ */ React30.createElement(
18289
18309
  InSceneCreationForm,
18290
18310
  {
18291
18311
  onSave: (data) => userActionHandlers.handleSaveNode(actionHandlerContext, data),
@@ -18317,7 +18337,7 @@ function XViewScene({
18317
18337
  availableAncestries: allAvailableAncestries
18318
18338
  }
18319
18339
  ),
18320
- versionMode.isActive && /* @__PURE__ */ React28.createElement(
18340
+ versionMode.isActive && /* @__PURE__ */ React30.createElement(
18321
18341
  InSceneVersionForm,
18322
18342
  {
18323
18343
  onSave: (data) => userActionHandlers.handleSaveVersionNode(actionHandlerContext, data),
@@ -18343,7 +18363,7 @@ function XViewScene({
18343
18363
  availableAncestries: allAvailableAncestries
18344
18364
  }
18345
18365
  ),
18346
- questMode.isActive && /* @__PURE__ */ React28.createElement(
18366
+ questMode.isActive && /* @__PURE__ */ React30.createElement(
18347
18367
  InSceneQuestForm,
18348
18368
  {
18349
18369
  onSave: (data) => handleSaveQuestNode(actionHandlerContext, data),
@@ -18371,7 +18391,7 @@ function XViewScene({
18371
18391
  onUpdateMilestones: handleUpdateMilestones
18372
18392
  }
18373
18393
  ),
18374
- readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ React28.createElement(
18394
+ readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ React30.createElement(
18375
18395
  "div",
18376
18396
  {
18377
18397
  className: `ui-overlay absolute group rounded-2xl border border-white/10 bg-slate-950/70 backdrop-blur-xl shadow-[0_20px_80px_rgba(0,0,0,0.6)] ring-1 ring-white/10 text-white overflow-hidden flex flex-col ${isReadModeResizing ? "transition-none" : "transition-all duration-300 ease-out"}`,
@@ -18384,7 +18404,7 @@ function XViewScene({
18384
18404
  maxWidth: "92vw"
18385
18405
  }
18386
18406
  },
18387
- /* @__PURE__ */ React28.createElement(
18407
+ /* @__PURE__ */ React30.createElement(
18388
18408
  "div",
18389
18409
  {
18390
18410
  onPointerDown: (e) => {
@@ -18395,7 +18415,7 @@ function XViewScene({
18395
18415
  title: "Arraste para redimensionar"
18396
18416
  }
18397
18417
  ),
18398
- /* @__PURE__ */ React28.createElement(
18418
+ /* @__PURE__ */ React30.createElement(
18399
18419
  DescriptionReadModePanel,
18400
18420
  {
18401
18421
  key: readingMode.branchStack.length > 0 ? readingMode.branchStack[readingMode.branchStack.length - 1].branchId : readingMode.ancestry.ancestry_id,
@@ -18430,7 +18450,7 @@ function XViewScene({
18430
18450
  }
18431
18451
  )
18432
18452
  ),
18433
- ancestryMode.isActive && ancestryMode.tree && /* @__PURE__ */ React28.createElement(
18453
+ ancestryMode.isActive && ancestryMode.tree && /* @__PURE__ */ React30.createElement(
18434
18454
  CreateAncestryPanel,
18435
18455
  {
18436
18456
  ancestryMode,
@@ -18466,7 +18486,7 @@ function XViewScene({
18466
18486
  onRenderAbstractionTree: (data, targetId) => handleRenderAbstractionTree(data, targetId)
18467
18487
  }
18468
18488
  ),
18469
- editingAncestryRel.visible && /* @__PURE__ */ React28.createElement(
18489
+ editingAncestryRel.visible && /* @__PURE__ */ React30.createElement(
18470
18490
  AncestryRelationshipPanel,
18471
18491
  {
18472
18492
  data: editingAncestryRel.data,
@@ -18480,7 +18500,7 @@ function XViewScene({
18480
18500
  onUploadFile: upload_file_action
18481
18501
  }
18482
18502
  ),
18483
- detailsNode && detailsNode.is_quest && /* @__PURE__ */ React28.createElement(
18503
+ detailsNode && detailsNode.is_quest && /* @__PURE__ */ React30.createElement(
18484
18504
  QuestDetailsPanel,
18485
18505
  {
18486
18506
  node: detailsNode,
@@ -18506,7 +18526,7 @@ function XViewScene({
18506
18526
  onUpdateMilestones: handleUpdateMilestones
18507
18527
  }
18508
18528
  ),
18509
- detailsNode && !detailsNode.is_quest && /* @__PURE__ */ React28.createElement(
18529
+ detailsNode && !detailsNode.is_quest && /* @__PURE__ */ React30.createElement(
18510
18530
  NodeDetailsPanel,
18511
18531
  {
18512
18532
  node: detailsNode,
@@ -18538,7 +18558,7 @@ function XViewScene({
18538
18558
  currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
18539
18559
  }
18540
18560
  ),
18541
- detailsLink && /* @__PURE__ */ React28.createElement(
18561
+ detailsLink && /* @__PURE__ */ React30.createElement(
18542
18562
  RelationshipDetailsPanel,
18543
18563
  {
18544
18564
  link: detailsLink,
@@ -18552,7 +18572,7 @@ function XViewScene({
18552
18572
  userRole: userPermissionRole
18553
18573
  }
18554
18574
  ),
18555
- ancestryLinkDetails && /* @__PURE__ */ React28.createElement(
18575
+ ancestryLinkDetails && /* @__PURE__ */ React30.createElement(
18556
18576
  AncestryLinkDetailsPanel,
18557
18577
  {
18558
18578
  data: ancestryLinkDetails,
@@ -18563,7 +18583,7 @@ function XViewScene({
18563
18583
  onUploadFile: upload_file_action
18564
18584
  }
18565
18585
  ),
18566
- /* @__PURE__ */ React28.createElement(
18586
+ /* @__PURE__ */ React30.createElement(
18567
18587
  "div",
18568
18588
  {
18569
18589
  ref: tooltipRef,
@@ -18590,7 +18610,7 @@ function XViewScene({
18590
18610
  }
18591
18611
  }
18592
18612
  ),
18593
- /* @__PURE__ */ React28.createElement(
18613
+ /* @__PURE__ */ React30.createElement(
18594
18614
  ContextMenu,
18595
18615
  {
18596
18616
  data: contextMenu,
@@ -18623,7 +18643,7 @@ function XViewScene({
18623
18643
  onFinishQuest: handleFinishQuestQuick
18624
18644
  }
18625
18645
  ),
18626
- /* @__PURE__ */ React28.createElement(
18646
+ /* @__PURE__ */ React30.createElement(
18627
18647
  MultiNodeContextMenu,
18628
18648
  {
18629
18649
  data: multiContextMenu,
@@ -18643,7 +18663,7 @@ function XViewScene({
18643
18663
  )
18644
18664
  }
18645
18665
  ),
18646
- /* @__PURE__ */ React28.createElement(
18666
+ /* @__PURE__ */ React30.createElement(
18647
18667
  RelationshipContextMenu,
18648
18668
  {
18649
18669
  data: relationshipMenu,
@@ -18661,14 +18681,14 @@ function XViewScene({
18661
18681
  onDelete: (data) => userActionHandlers.handleDeleteLink(actionHandlerContext, data)
18662
18682
  }
18663
18683
  ),
18664
- /* @__PURE__ */ React28.createElement(
18684
+ /* @__PURE__ */ React30.createElement(
18665
18685
  ImageViewer,
18666
18686
  {
18667
18687
  data: imageViewer,
18668
18688
  onClose: () => setImageViewer({ ...imageViewer, visible: false })
18669
18689
  }
18670
18690
  ),
18671
- /* @__PURE__ */ React28.createElement(
18691
+ /* @__PURE__ */ React30.createElement(
18672
18692
  AncestryBoard,
18673
18693
  {
18674
18694
  isOpen: isAncestryBoardOpen,
@@ -18681,7 +18701,7 @@ function XViewScene({
18681
18701
  userRole: userPermissionRole
18682
18702
  }
18683
18703
  ),
18684
- /* @__PURE__ */ React28.createElement(
18704
+ /* @__PURE__ */ React30.createElement(
18685
18705
  MilestoneManager,
18686
18706
  {
18687
18707
  isOpen: isMilestoneManagerOpen,
@@ -18690,7 +18710,7 @@ function XViewScene({
18690
18710
  onUpdateMilestones: handleUpdateMilestones
18691
18711
  }
18692
18712
  ),
18693
- /* @__PURE__ */ React28.createElement(
18713
+ /* @__PURE__ */ React30.createElement(
18694
18714
  ImportParentFileModal,
18695
18715
  {
18696
18716
  isOpen: isImportModalOpen,
@@ -18703,7 +18723,7 @@ function XViewScene({
18703
18723
  currentAncestries: ancestryDataRef.current || []
18704
18724
  }
18705
18725
  ),
18706
- invalidTargetError && /* @__PURE__ */ React28.createElement(
18726
+ invalidTargetError && /* @__PURE__ */ React30.createElement(
18707
18727
  "div",
18708
18728
  {
18709
18729
  className: "ui-overlay",
@@ -18728,13 +18748,13 @@ function XViewScene({
18728
18748
  animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
18729
18749
  }
18730
18750
  },
18731
- /* @__PURE__ */ React28.createElement("style", null, `
18751
+ /* @__PURE__ */ React30.createElement("style", null, `
18732
18752
  @keyframes fadeInDown {
18733
18753
  from { opacity: 0; transform: translate(-50%, -20px); }
18734
18754
  to { opacity: 1; transform: translate(-50%, 0); }
18735
18755
  }
18736
18756
  `),
18737
- /* @__PURE__ */ React28.createElement(
18757
+ /* @__PURE__ */ React30.createElement(
18738
18758
  "svg",
18739
18759
  {
18740
18760
  width: "20",
@@ -18746,12 +18766,12 @@ function XViewScene({
18746
18766
  strokeLinecap: "round",
18747
18767
  strokeLinejoin: "round"
18748
18768
  },
18749
- /* @__PURE__ */ React28.createElement("circle", { cx: "12", cy: "12", r: "10" }),
18750
- /* @__PURE__ */ React28.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
18751
- /* @__PURE__ */ React28.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
18769
+ /* @__PURE__ */ React30.createElement("circle", { cx: "12", cy: "12", r: "10" }),
18770
+ /* @__PURE__ */ React30.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
18771
+ /* @__PURE__ */ React30.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
18752
18772
  ),
18753
- /* @__PURE__ */ React28.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
18754
- /* @__PURE__ */ React28.createElement(
18773
+ /* @__PURE__ */ React30.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
18774
+ /* @__PURE__ */ React30.createElement(
18755
18775
  "button",
18756
18776
  {
18757
18777
  onClick: () => setInvalidTargetError(null),
@@ -18781,450 +18801,6 @@ function XViewScene({
18781
18801
  )
18782
18802
  );
18783
18803
  }
18784
-
18785
- // src/x_view_server_logic.js
18786
- async function save_view_data_logic(db_services, filename, data) {
18787
- try {
18788
- const saveResult = await db_services.save_file(filename, data);
18789
- return saveResult;
18790
- } catch (error) {
18791
- console.error("catch (error) em save_view_data_logic:", error);
18792
- return { success: false, error: error.message };
18793
- }
18794
- }
18795
- async function get_scene_view_data_logic(db_services, scene_config, owner_id, type, session, focusNodeId = null, focusAncestryId = null) {
18796
- var _a, _b;
18797
- try {
18798
- if (!session || !session.user) {
18799
- return { success: false, error: "Usu\xE1rio n\xE3o autenticado." };
18800
- }
18801
- const membershipCheck = await db_services.check_membership(owner_id, type, scene_config, session.user.id);
18802
- const isFullMember = membershipCheck.success && membershipCheck.isValidMember;
18803
- if (!isFullMember && !focusNodeId && !focusAncestryId) {
18804
- return { success: false, error: "Acesso Negado: Voc\xEA n\xE3o possui permiss\xE3o para acessar este cen\xE1rio." };
18805
- }
18806
- const sceneResponse = await db_services.get_file(`x_view_scenes/${owner_id}/${scene_config}`);
18807
- if (!sceneResponse || !sceneResponse.success) {
18808
- console.error("Falha ao buscar dados da cena:", sceneResponse == null ? void 0 : sceneResponse.error);
18809
- return { success: false, error: `N\xE3o foi poss\xEDvel carregar o arquivo da cena: ${scene_config}` };
18810
- }
18811
- const sceneData = sceneResponse.data;
18812
- const parentDbObjects = sceneData.parent_dbs || [];
18813
- if (type && type.toLowerCase().includes("database")) {
18814
- const selfExists = parentDbObjects.some((db) => String(db.db_id) === String(scene_config));
18815
- if (!selfExists) {
18816
- parentDbObjects.push({ db_id: scene_config, owner_id });
18817
- }
18818
- }
18819
- const parentResponsesPromises = parentDbObjects.map(
18820
- (db_info) => db_services.get_file(`x_view_dbs/${db_info.owner_id}/${db_info.db_id}`)
18821
- );
18822
- const parentResponsesResults = await Promise.allSettled(parentResponsesPromises);
18823
- const parentData = {};
18824
- for (let i = 0; i < parentResponsesResults.length; i++) {
18825
- const result = parentResponsesResults[i];
18826
- const db_info = parentDbObjects[i];
18827
- if (result.status === "fulfilled" && ((_a = result.value) == null ? void 0 : _a.success)) {
18828
- parentData[db_info.db_id] = result.value.data;
18829
- } else {
18830
- console.warn(
18831
- `Arquivo pai n\xE3o carregado: ${db_info.db_id}. Pode ter sido deletado.`,
18832
- result.status === "rejected" ? result.reason : (_b = result.value) == null ? void 0 : _b.error
18833
- );
18834
- }
18835
- }
18836
- if (type && type.toLowerCase() === "view") {
18837
- parentData[scene_config] = {
18838
- dataset_name: "Quests Internas (View)",
18839
- nodes: sceneData.quest_nodes || [],
18840
- links: sceneData.quest_links || []
18841
- };
18842
- }
18843
- const allNodes = Object.values(parentData).flatMap((db) => db.nodes || []);
18844
- const allLinks = Object.values(parentData).flatMap((db) => db.links || []);
18845
- const parentNodeMap = new Map(allNodes.map((node) => [String(node.id), node]));
18846
- const parentLinkMap = new Map(allLinks.map((link) => [`${link.source}-${link.target}`, link]));
18847
- const validatedNodes = (sceneData.nodes || []).map((sceneNode) => {
18848
- var _a2, _b2;
18849
- const nodeTypes = Array.isArray(sceneNode.type) ? sceneNode.type : [sceneNode.type];
18850
- if (nodeTypes.includes("quest")) {
18851
- const dbQuestNode = (_b2 = (_a2 = parentData[scene_config]) == null ? void 0 : _a2.nodes) == null ? void 0 : _b2.find((qn) => String(qn.id) === String(sceneNode.id));
18852
- if (dbQuestNode) {
18853
- return { ...sceneNode, ...dbQuestNode };
18854
- }
18855
- return sceneNode;
18856
- }
18857
- const dbNode = parentNodeMap.get(String(sceneNode.id));
18858
- if (dbNode) {
18859
- return { ...sceneNode, ...dbNode };
18860
- }
18861
- return null;
18862
- }).filter(Boolean);
18863
- const validNodeIdsInScene = new Set(validatedNodes.map((node) => String(node.id)));
18864
- const validatedLinks = (sceneData.links || []).filter((sceneLink) => {
18865
- const linkExistsInParent = parentLinkMap.has(`${sceneLink.source}-${sceneLink.target}`);
18866
- const nodesAreValidForScene = validNodeIdsInScene.has(String(sceneLink.source)) && validNodeIdsInScene.has(String(sceneLink.target));
18867
- return linkExistsInParent && nodesAreValidForScene;
18868
- }).map((sceneLink) => parentLinkMap.get(`${sceneLink.source}-${sceneLink.target}`));
18869
- const validatedSceneData = {
18870
- ...sceneData,
18871
- nodes: validatedNodes,
18872
- links: validatedLinks
18873
- };
18874
- const ancestryFilePaths = [];
18875
- ancestryFilePaths.push({
18876
- path: `x_view_ancestry/${owner_id}/${scene_config}`,
18877
- source_id: scene_config,
18878
- owner_id,
18879
- isSceneFile: true
18880
- });
18881
- parentDbObjects.forEach((db_info) => {
18882
- ancestryFilePaths.push({
18883
- path: `x_view_ancestry/${db_info.owner_id}/${db_info.db_id}`,
18884
- source_id: db_info.db_id,
18885
- owner_id: db_info.owner_id,
18886
- isSceneFile: false
18887
- });
18888
- });
18889
- const ancestryPromises = ancestryFilePaths.map((file) => db_services.get_file(file.path));
18890
- const ancestryResults = await Promise.allSettled(ancestryPromises);
18891
- const ancestriesFromSceneFile = [];
18892
- const ancestriesFromDbFiles = [];
18893
- ancestryResults.forEach((result, index) => {
18894
- var _a2;
18895
- const fileInfo = ancestryFilePaths[index];
18896
- if (result.status === "fulfilled" && ((_a2 = result.value) == null ? void 0 : _a2.success) && Array.isArray(result.value.data)) {
18897
- const ancestriesFromFile = result.value.data.map((ancestry) => {
18898
- const existingSource = ancestry._source_file_id;
18899
- const existingOwner = ancestry._source_owner_id;
18900
- const isImportedItem = existingSource && existingSource !== fileInfo.source_id;
18901
- return {
18902
- ...ancestry,
18903
- _source_file_id: isImportedItem ? existingSource : fileInfo.source_id,
18904
- _source_owner_id: isImportedItem ? existingOwner : fileInfo.owner_id
18905
- };
18906
- });
18907
- if (fileInfo.isSceneFile) {
18908
- ancestriesFromSceneFile.push(...ancestriesFromFile);
18909
- } else {
18910
- ancestriesFromDbFiles.push(...ancestriesFromFile);
18911
- }
18912
- }
18913
- });
18914
- const ancestryMap = /* @__PURE__ */ new Map();
18915
- ancestriesFromDbFiles.forEach((anc) => ancestryMap.set(anc.ancestry_id, anc));
18916
- ancestriesFromSceneFile.forEach((anc) => ancestryMap.set(anc.ancestry_id, anc));
18917
- let ancestryData = Array.from(ancestryMap.values());
18918
- const externalSourcesToRefresh = /* @__PURE__ */ new Map();
18919
- ancestryData.forEach((anc) => {
18920
- const targetSourceId = anc._source_file_id || anc._imported_from_view_id;
18921
- const targetOwnerId = anc._source_owner_id || anc._imported_from_view_owner_id;
18922
- if (targetSourceId && targetSourceId !== scene_config && targetOwnerId) {
18923
- const isAlreadyLoadedAsParent = parentDbObjects.some((db) => String(db.db_id) === String(targetSourceId));
18924
- if (!isAlreadyLoadedAsParent) {
18925
- const key = `${targetOwnerId}/${targetSourceId}`;
18926
- externalSourcesToRefresh.set(key, {
18927
- owner_id: targetOwnerId,
18928
- source_id: targetSourceId,
18929
- key
18930
- });
18931
- }
18932
- }
18933
- });
18934
- const successfullyFetchedSources = /* @__PURE__ */ new Set();
18935
- const failedSources = /* @__PURE__ */ new Set();
18936
- if (externalSourcesToRefresh.size > 0) {
18937
- const refreshPromises = Array.from(externalSourcesToRefresh.values()).map(
18938
- (src) => db_services.get_file(`x_view_ancestry/${src.owner_id}/${src.source_id}`).then((res) => ({ ...res, _meta_key: src.key }))
18939
- );
18940
- const refreshResults = await Promise.allSettled(refreshPromises);
18941
- const freshAncestryMap = /* @__PURE__ */ new Map();
18942
- refreshResults.forEach((result) => {
18943
- var _a2, _b2;
18944
- if (result.status === "fulfilled" && ((_a2 = result.value) == null ? void 0 : _a2.success) && Array.isArray(result.value.data)) {
18945
- successfullyFetchedSources.add(result.value._meta_key);
18946
- result.value.data.forEach((freshAnc) => {
18947
- if (!freshAnc.is_private) {
18948
- freshAncestryMap.set(String(freshAnc.ancestry_id), freshAnc);
18949
- }
18950
- });
18951
- } else if (result.status === "fulfilled" && !((_b2 = result.value) == null ? void 0 : _b2.success)) {
18952
- failedSources.add(result.value._meta_key);
18953
- }
18954
- });
18955
- ancestryData = ancestryData.filter((localAnc) => {
18956
- const targetSourceId = localAnc._source_file_id || localAnc._imported_from_view_id;
18957
- const targetOwnerId = localAnc._source_owner_id || localAnc._imported_from_view_owner_id;
18958
- if (!targetSourceId || targetSourceId === scene_config) return true;
18959
- const key = `${targetOwnerId}/${targetSourceId}`;
18960
- if (failedSources.has(key)) {
18961
- return false;
18962
- }
18963
- if (externalSourcesToRefresh.has(key) && successfullyFetchedSources.has(key)) {
18964
- const stillExists = freshAncestryMap.has(String(localAnc.ancestry_id));
18965
- return stillExists;
18966
- }
18967
- return true;
18968
- }).map((localAnc) => {
18969
- const freshVersion = freshAncestryMap.get(String(localAnc.ancestry_id));
18970
- if (freshVersion) {
18971
- return {
18972
- ...freshVersion,
18973
- _imported_from_view_id: localAnc._imported_from_view_id,
18974
- _imported_from_view_owner_id: localAnc._imported_from_view_owner_id,
18975
- _origin_db_ids: localAnc._origin_db_ids,
18976
- _source_file_id: localAnc._source_file_id,
18977
- _source_owner_id: localAnc._source_owner_id
18978
- };
18979
- }
18980
- return localAnc;
18981
- });
18982
- }
18983
- const ancestryFilePath = `x_view_ancestry/${owner_id}/${scene_config}`;
18984
- const parentNodeIds = new Set(allNodes.map((node) => String(node.id)));
18985
- const pruneTree = (node) => {
18986
- if (!node) return null;
18987
- if (node.is_section) {
18988
- const validChildren2 = (node.children || []).map((child) => pruneTree(child)).filter(Boolean);
18989
- return { ...node, children: validChildren2 };
18990
- }
18991
- if (!parentNodeIds.has(String(node.node_id))) {
18992
- return null;
18993
- }
18994
- const validChildren = (node.children || []).map((child) => pruneTree(child)).filter(Boolean);
18995
- let validBranches = [];
18996
- if (node.parallel_branches) {
18997
- validBranches = node.parallel_branches.map((branch) => ({
18998
- ...branch,
18999
- tree: pruneTree(branch.tree)
19000
- })).filter((b) => b.tree !== null);
19001
- }
19002
- return {
19003
- ...node,
19004
- children: validChildren,
19005
- parallel_branches: validBranches
19006
- };
19007
- };
19008
- const originalAncestryDataString = JSON.stringify(Array.from(ancestryMap.values()));
19009
- const cleanedAncestryData = ancestryData.map((ancestry) => {
19010
- if (!parentNodeIds.has(String(ancestry.ancestral_node))) return null;
19011
- const cleanedTree = pruneTree(ancestry.tree);
19012
- if (!cleanedTree) return null;
19013
- let cleanedAbstraction = null;
19014
- if (ancestry.abstraction_tree) {
19015
- cleanedAbstraction = pruneTree(ancestry.abstraction_tree);
19016
- }
19017
- return {
19018
- ...ancestry,
19019
- tree: cleanedTree,
19020
- abstraction_tree: cleanedAbstraction
19021
- };
19022
- }).filter(Boolean);
19023
- if (JSON.stringify(cleanedAncestryData) !== originalAncestryDataString) {
19024
- ancestryData = cleanedAncestryData;
19025
- try {
19026
- await db_services.save_file(ancestryFilePath, ancestryData);
19027
- } catch (saveError) {
19028
- console.error("Falha ao salvar o arquivo de ancestralidade da cena atualizado:", saveError);
19029
- }
19030
- }
19031
- if (!isFullMember) {
19032
- const requiredNodeIds = /* @__PURE__ */ new Set();
19033
- const requiredAncestryIds = /* @__PURE__ */ new Set();
19034
- if (focusNodeId) requiredNodeIds.add(String(focusNodeId));
19035
- if (focusAncestryId) requiredAncestryIds.add(String(focusAncestryId));
19036
- const allNodesFromDb = Object.values(parentData).flatMap((db) => db.nodes || []);
19037
- const extractDependenciesFromText = (text) => {
19038
- if (!text) return;
19039
- const mentionRegex = /\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g;
19040
- const refRegex = /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g;
19041
- let match;
19042
- while ((match = mentionRegex.exec(text)) !== null) {
19043
- requiredNodeIds.add(match[1]);
19044
- }
19045
- while ((match = refRegex.exec(text)) !== null) {
19046
- if (match[1] === "node") requiredNodeIds.add(match[2]);
19047
- if (match[1] === "ancestry") requiredAncestryIds.add(match[2]);
19048
- }
19049
- };
19050
- const extractTreeDependencies = (treeItem) => {
19051
- var _a2;
19052
- if (!treeItem) return;
19053
- if (treeItem.node_id) requiredNodeIds.add(String(treeItem.node_id));
19054
- if ((_a2 = treeItem.node) == null ? void 0 : _a2.id) requiredNodeIds.add(String(treeItem.node.id));
19055
- if (treeItem.children) {
19056
- treeItem.children.forEach(extractTreeDependencies);
19057
- }
19058
- if (treeItem.parallel_branches) {
19059
- treeItem.parallel_branches.forEach((branch) => {
19060
- if (branch.linked_ancestry_id) {
19061
- requiredAncestryIds.add(String(branch.linked_ancestry_id));
19062
- }
19063
- if (branch.description) extractDependenciesFromText(branch.description);
19064
- if (branch.description_sections) {
19065
- branch.description_sections.forEach((s) => extractDependenciesFromText(s.content));
19066
- }
19067
- extractTreeDependencies(branch.tree);
19068
- });
19069
- }
19070
- };
19071
- let previousSize;
19072
- do {
19073
- previousSize = requiredNodeIds.size + requiredAncestryIds.size;
19074
- requiredAncestryIds.forEach((ancId) => {
19075
- const anc = ancestryData.find((a) => String(a.ancestry_id) === ancId);
19076
- if (anc && !anc.is_private) {
19077
- if (anc.description) extractDependenciesFromText(anc.description);
19078
- if (anc.description_sections) {
19079
- anc.description_sections.forEach((s) => extractDependenciesFromText(s.content));
19080
- }
19081
- extractTreeDependencies(anc.tree);
19082
- extractTreeDependencies(anc.abstraction_tree);
19083
- }
19084
- });
19085
- requiredNodeIds.forEach((nodeId) => {
19086
- const node = allNodesFromDb.find((n) => String(n.id) === nodeId);
19087
- if (node) {
19088
- if (node.description) extractDependenciesFromText(node.description);
19089
- if (node.description_sections) {
19090
- node.description_sections.forEach((s) => extractDependenciesFromText(s.content));
19091
- }
19092
- }
19093
- });
19094
- } while (requiredNodeIds.size + requiredAncestryIds.size > previousSize);
19095
- const restrictedSceneData = { ...validatedSceneData, nodes: [], links: [] };
19096
- const restrictedParentData = {};
19097
- const restrictedAncestryData = ancestryData.filter((a) => requiredAncestryIds.has(String(a.ancestry_id)) && !a.is_private);
19098
- for (const [dbId, dbContent] of Object.entries(parentData)) {
19099
- const filteredNodes = (dbContent.nodes || []).filter((n) => requiredNodeIds.has(String(n.id)));
19100
- if (filteredNodes.length > 0) {
19101
- restrictedParentData[dbId] = { ...dbContent, nodes: filteredNodes, links: [] };
19102
- }
19103
- }
19104
- if (focusNodeId) {
19105
- const mainNode = allNodesFromDb.find((n) => String(n.id) === String(focusNodeId));
19106
- if (mainNode) {
19107
- restrictedSceneData.nodes = [mainNode];
19108
- }
19109
- }
19110
- return {
19111
- success: true,
19112
- data: {
19113
- scene: restrictedSceneData,
19114
- parent: restrictedParentData,
19115
- ancestry: restrictedAncestryData
19116
- }
19117
- };
19118
- }
19119
- return {
19120
- success: true,
19121
- data: { scene: validatedSceneData, parent: parentData, ancestry: ancestryData }
19122
- };
19123
- } catch (error) {
19124
- console.error("Erro em get_scene_view_data_logic:", error);
19125
- return { success: false, error: error.message };
19126
- }
19127
- }
19128
- async function import_parent_file_modal_get_logic(db_services, session) {
19129
- try {
19130
- const call_firestore_get_x_view_data = await db_services.get_firestore_data(session);
19131
- const x_view_databases = call_firestore_get_x_view_data.data.sub_collections.x_view_databases || [];
19132
- const x_views = call_firestore_get_x_view_data.data.sub_collections.x_views || [];
19133
- return {
19134
- success: true,
19135
- data: {
19136
- databases: x_view_databases,
19137
- views: x_views
19138
- }
19139
- };
19140
- } catch (error) {
19141
- console.error("catch (error) em import_parent_file_modal_get_logic:", error);
19142
- return { success: false, error: error.message };
19143
- }
19144
- }
19145
- async function get_single_parent_file_logic(db_services, file_id, session) {
19146
- try {
19147
- const call_GCP_cloud_storage_get_file = await db_services.get_file(`x_view_dbs/${session.user.id}/${file_id}`);
19148
- return call_GCP_cloud_storage_get_file;
19149
- } catch (error) {
19150
- console.error("catch (error) em get_single_parent_file_logic:", error);
19151
- return { success: false, error: error.message };
19152
- }
19153
- }
19154
- async function x_view_add_new_parent_file_to_scene_at_firebase(file_id, session, new_parent, db_services) {
19155
- try {
19156
- const update_data = {
19157
- scene_id: file_id,
19158
- new_parent,
19159
- session
19160
- };
19161
- const call_firestore_add_new_parent_file_to_scene_at_firebase = await db_services.add_new_parent_file_to_scene_at_firebase(update_data);
19162
- return call_firestore_add_new_parent_file_to_scene_at_firebase;
19163
- } catch (error) {
19164
- console.error("catch (error) em get_single_parent_file_logic:", error);
19165
- return { success: false, error: error.message };
19166
- }
19167
- }
19168
- async function get_ancestry_file_logic(db_services, file_id, owner_id) {
19169
- try {
19170
- const path = `x_view_ancestry/${owner_id}/${file_id}`;
19171
- const call_GCP_cloud_storage_get_file = await db_services.get_file(path);
19172
- return call_GCP_cloud_storage_get_file;
19173
- } catch (error) {
19174
- console.error("catch (error) em get_ancestry_file_logic:", error);
19175
- return { success: false, error: error.message };
19176
- }
19177
- }
19178
- async function save_ancestry_board_logic(db_services, scene_id, type, groups, session, owner_id) {
19179
- try {
19180
- const targetOwnerId = owner_id || session.user.id;
19181
- return await db_services.save_ancestry_board(session, scene_id, type, groups, targetOwnerId);
19182
- } catch (error) {
19183
- console.error("Erro em save_ancestry_board_logic:", error);
19184
- return { success: false, error: error.message };
19185
- }
19186
- }
19187
- async function get_ancestry_board_logic(db_services, scene_id, type, session, owner_id) {
19188
- try {
19189
- const targetOwnerId = owner_id || session.user.id;
19190
- return await db_services.get_ancestry_board(session, scene_id, type, targetOwnerId);
19191
- } catch (error) {
19192
- console.error("Erro em get_ancestry_board_logic:", error);
19193
- return { success: false, error: error.message };
19194
- }
19195
- }
19196
- async function delete_uploaded_file_logic(db_services, fileUrl) {
19197
- try {
19198
- if (db_services.delete_public_file) {
19199
- return await db_services.delete_public_file(fileUrl);
19200
- }
19201
- return { success: false, error: "Servi\xE7o de dele\xE7\xE3o n\xE3o configurado." };
19202
- } catch (error) {
19203
- console.error("Erro em delete_uploaded_file_logic:", error);
19204
- return { success: false, error: error.message };
19205
- }
19206
- }
19207
- async function get_view_members_logic(db_services, ownerId, type, itemId) {
19208
- try {
19209
- if (!db_services.get_view_members) {
19210
- return { success: false, error: "Servi\xE7o de busca de membros n\xE3o configurado." };
19211
- }
19212
- return await db_services.get_view_members(ownerId, type, itemId);
19213
- } catch (error) {
19214
- console.error("Erro em get_view_members_logic:", error);
19215
- return { success: false, error: error.message };
19216
- }
19217
- }
19218
18804
  export {
19219
- XViewScene,
19220
- delete_uploaded_file_logic,
19221
- get_ancestry_board_logic,
19222
- get_ancestry_file_logic,
19223
- get_scene_view_data_logic,
19224
- get_single_parent_file_logic,
19225
- get_view_members_logic,
19226
- import_parent_file_modal_get_logic,
19227
- save_ancestry_board_logic,
19228
- save_view_data_logic,
19229
- x_view_add_new_parent_file_to_scene_at_firebase
18805
+ XViewScene
19230
18806
  };