@lv-x-software-house/x_view 1.2.6-dev.58 → 1.2.6-dev.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3445,8 +3445,8 @@ function ContextMenu({
3445
3445
  }, [sceneData == null ? void 0 : sceneData.links, renderedLinkIds]);
3446
3446
  if (!data.visible) return null;
3447
3447
  const swallow = (e) => e.stopPropagation();
3448
- const baseButtonClass = "w-full flex items-center gap-2.5 px-2 py-1.5 text-left text-sm rounded-md hover:bg-indigo-500/20 text-slate-200 hover:text-white transition-colors duration-150 truncate";
3449
- const deleteButtonClass = "w-full flex items-center gap-2.5 px-2 py-1.5 text-left text-sm rounded-md hover:bg-red-500/25 text-red-400 hover:text-red-300 transition-colors duration-150 truncate";
3448
+ const baseButtonClass = "w-full flex items-center gap-2.5 px-2 py-1.5 text-left text-sm rounded-md hover:bg-indigo-500/20 text-slate-200 hover:text-white transition-colors duration-150";
3449
+ const deleteButtonClass = "w-full flex items-center gap-2.5 px-2 py-1.5 text-left text-sm rounded-md hover:bg-red-500/25 text-red-400 hover:text-red-300 transition-colors duration-150";
3450
3450
  const getYearFromDate = (dateObj) => {
3451
3451
  var _a2, _b2;
3452
3452
  if (!dateObj) return -Infinity;
@@ -3859,7 +3859,7 @@ function ContextMenu({
3859
3859
  "div",
3860
3860
  {
3861
3861
  ref: menuRef,
3862
- className: "ui-overlay context-menu-class origin-top-left rounded-xl 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 w-[min(92vw,250px)] overflow-hidden",
3862
+ className: "ui-overlay context-menu-class origin-top-left rounded-xl 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 min-w-[220px] max-w-[min(95vw,420px)] w-max overflow-hidden",
3863
3863
  style: { position: "absolute", left: `${menuPos.left}px`, top: `${menuPos.top}px`, zIndex: 1e3 },
3864
3864
  onPointerDown: swallow,
3865
3865
  onPointerMove: swallow,
@@ -9946,6 +9946,7 @@ function InSceneQuestForm({
9946
9946
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = (0, import_react17.useState)(false);
9947
9947
  const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = (0, import_react17.useState)(false);
9948
9948
  const [assigneeSearchQuery, setAssigneeSearchQuery] = (0, import_react17.useState)("");
9949
+ const [isNameCopied, setIsNameCopied] = (0, import_react17.useState)(false);
9949
9950
  const [customProps, setCustomProps] = (0, import_react17.useState)([]);
9950
9951
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react17.useState)(false);
9951
9952
  const propsEndRef = (0, import_react17.useRef)(null);
@@ -10024,6 +10025,15 @@ function InSceneQuestForm({
10024
10025
  }
10025
10026
  };
10026
10027
  const standardizedName = `${viewName} - ${questCounter} - \xBB ${name || "Nova Quest"}`;
10028
+ const handleCopyName = () => {
10029
+ if (!standardizedName) return;
10030
+ navigator.clipboard.writeText(standardizedName).then(() => {
10031
+ setIsNameCopied(true);
10032
+ setTimeout(() => setIsNameCopied(false), 2e3);
10033
+ }).catch((err) => {
10034
+ console.error("Erro ao copiar nome:", err);
10035
+ });
10036
+ };
10027
10037
  const handleAddProp = () => {
10028
10038
  const newProp = createNewCustomProperty(customProps);
10029
10039
  setCustomProps([...customProps, newProp]);
@@ -10143,13 +10153,22 @@ function InSceneQuestForm({
10143
10153
  },
10144
10154
  className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60"
10145
10155
  }
10146
- ), /* @__PURE__ */ import_react17.default.createElement("div", { className: "pt-1 flex items-center gap-1.5" }, /* @__PURE__ */ import_react17.default.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }), /* @__PURE__ */ import_react17.default.createElement(
10156
+ ), /* @__PURE__ */ import_react17.default.createElement("div", { className: "pt-1 flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ import_react17.default.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }), /* @__PURE__ */ import_react17.default.createElement(
10147
10157
  "p",
10148
10158
  {
10149
10159
  className: "text-xs text-indigo-300 font-medium truncate",
10150
10160
  title: standardizedName
10151
10161
  },
10152
10162
  standardizedName
10163
+ ), /* @__PURE__ */ import_react17.default.createElement(
10164
+ "button",
10165
+ {
10166
+ type: "button",
10167
+ onClick: handleCopyName,
10168
+ className: `p-1 rounded-md transition-all hover:bg-white/5 ${isNameCopied ? "text-green-400 scale-110" : "text-slate-500 hover:text-indigo-400"} flex-shrink-0`,
10169
+ title: isNameCopied ? "Nome Copiado!" : "Copiar nome formatado"
10170
+ },
10171
+ isNameCopied ? /* @__PURE__ */ import_react17.default.createElement(import_fi16.FiCheck, { size: 10 }) : /* @__PURE__ */ import_react17.default.createElement(import_fi16.FiCopy, { size: 10 })
10153
10172
  ))), /* @__PURE__ */ import_react17.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react17.default.createElement("label", { className: "text-xs text-slate-300 font-medium uppercase tracking-wider" }, "Status da Quest"), /* @__PURE__ */ import_react17.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react17.default.createElement(
10154
10173
  "button",
10155
10174
  {
@@ -11293,6 +11312,7 @@ function QuestDetailsPanel({
11293
11312
  const [existingSections, setExistingSections] = (0, import_react20.useState)((node == null ? void 0 : node.description_sections) || []);
11294
11313
  const [isSaving, setIsSaving] = (0, import_react20.useState)(false);
11295
11314
  const [isLinkCopied, setIsLinkCopied] = (0, import_react20.useState)(false);
11315
+ const [isNameCopied, setIsNameCopied] = (0, import_react20.useState)(false);
11296
11316
  const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react20.useState)(false);
11297
11317
  const [questDate, setQuestDate] = (0, import_react20.useState)((node == null ? void 0 : node.Date) || null);
11298
11318
  const [noteText, setNoteText] = (0, import_react20.useState)("");
@@ -11474,6 +11494,15 @@ function QuestDetailsPanel({
11474
11494
  console.error("Erro ao copiar link:", err);
11475
11495
  });
11476
11496
  };
11497
+ const handleCopyName = () => {
11498
+ if (!standardizedName) return;
11499
+ navigator.clipboard.writeText(standardizedName).then(() => {
11500
+ setIsNameCopied(true);
11501
+ setTimeout(() => setIsNameCopied(false), 2e3);
11502
+ }).catch((err) => {
11503
+ console.error("Erro ao copiar nome:", err);
11504
+ });
11505
+ };
11477
11506
  const swallow = (e) => e.stopPropagation();
11478
11507
  const handleNameChange = (val) => {
11479
11508
  setRawTitle(val);
@@ -11811,7 +11840,16 @@ ${noteText}` : noteText : entry.notes,
11811
11840
  readOnly: !canEdit,
11812
11841
  className: `w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none ${canEdit ? "focus:ring-2 focus:ring-indigo-400/60" : "cursor-default text-slate-400"}`
11813
11842
  }
11814
- ), /* @__PURE__ */ import_react20.default.createElement("div", { className: "pt-1 flex items-center gap-1.5" }, /* @__PURE__ */ import_react20.default.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }), /* @__PURE__ */ import_react20.default.createElement("p", { className: "text-xs text-indigo-300 font-medium truncate", title: standardizedName }, standardizedName))), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5 mt-2" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300 font-medium uppercase tracking-wider" }, "Prazo / Data"), canEdit && !questDate && /* @__PURE__ */ import_react20.default.createElement(
11843
+ ), /* @__PURE__ */ import_react20.default.createElement("div", { className: "pt-1 flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ import_react20.default.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }), /* @__PURE__ */ import_react20.default.createElement("p", { className: "text-xs text-indigo-300 font-medium truncate", title: standardizedName }, standardizedName), /* @__PURE__ */ import_react20.default.createElement(
11844
+ "button",
11845
+ {
11846
+ type: "button",
11847
+ onClick: handleCopyName,
11848
+ className: `p-1 rounded-md transition-all hover:bg-white/5 ${isNameCopied ? "text-green-400 scale-110" : "text-slate-500 hover:text-indigo-400"} flex-shrink-0`,
11849
+ title: isNameCopied ? "Nome Copiado!" : "Copiar nome formatado"
11850
+ },
11851
+ isNameCopied ? /* @__PURE__ */ import_react20.default.createElement(import_fi19.FiCheck, { size: 10 }) : /* @__PURE__ */ import_react20.default.createElement(import_fi19.FiCopy, { size: 10 })
11852
+ ))), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5 mt-2" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300 font-medium uppercase tracking-wider" }, "Prazo / Data"), canEdit && !questDate && /* @__PURE__ */ import_react20.default.createElement(
11815
11853
  "button",
11816
11854
  {
11817
11855
  type: "button",
@@ -12495,7 +12533,8 @@ function RelationshipDetailsPanel({
12495
12533
  onOpenReference,
12496
12534
  onMentionClick,
12497
12535
  onUploadFile,
12498
- userRole
12536
+ userRole,
12537
+ onHighlight
12499
12538
  }) {
12500
12539
  const [name, setName] = (0, import_react22.useState)((link == null ? void 0 : link.name) ?? "");
12501
12540
  const [description, setDescription] = (0, import_react22.useState)((link == null ? void 0 : link.description) ?? "");
@@ -12525,6 +12564,11 @@ function RelationshipDetailsPanel({
12525
12564
  setTargetLabel((link == null ? void 0 : link.target_label) ?? "");
12526
12565
  setHasUnsavedChanges(false);
12527
12566
  }, [link]);
12567
+ (0, import_react22.useEffect)(() => {
12568
+ return () => {
12569
+ if (onHighlight) onHighlight(null);
12570
+ };
12571
+ }, [onHighlight]);
12528
12572
  const swallow = (e) => e.stopPropagation();
12529
12573
  const handleAddProp = () => {
12530
12574
  if (!canEdit) return;
@@ -12571,10 +12615,8 @@ function RelationshipDetailsPanel({
12571
12615
  isCurved: link.isCurved,
12572
12616
  curveOffset: link.curveOffset
12573
12617
  };
12574
- if (currentSourceLabel.trim())
12575
- dataToSave.source_label = currentSourceLabel.trim();
12576
- if (currentTargetLabel.trim())
12577
- dataToSave.target_label = currentTargetLabel.trim();
12618
+ dataToSave.source_label = currentSourceLabel.trim();
12619
+ dataToSave.target_label = currentTargetLabel.trim();
12578
12620
  await onSave(dataToSave, keepOpen);
12579
12621
  onDataUpdate(dataToSave);
12580
12622
  setHasUnsavedChanges(false);
@@ -12704,6 +12746,8 @@ function RelationshipDetailsPanel({
12704
12746
  },
12705
12747
  placeholder: "Ex: Conceitos",
12706
12748
  disabled: !canEdit,
12749
+ onFocus: () => onHighlight == null ? void 0 : onHighlight(link.source),
12750
+ onBlur: () => onHighlight == null ? void 0 : onHighlight(null),
12707
12751
  className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
12708
12752
  ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
12709
12753
  `
@@ -12719,6 +12763,8 @@ function RelationshipDetailsPanel({
12719
12763
  },
12720
12764
  placeholder: "Ex: Refer\xEAncias",
12721
12765
  disabled: !canEdit,
12766
+ onFocus: () => onHighlight == null ? void 0 : onHighlight(link.target),
12767
+ onBlur: () => onHighlight == null ? void 0 : onHighlight(null),
12722
12768
  className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
12723
12769
  ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
12724
12770
  `
@@ -13218,7 +13264,7 @@ function ImportParentFileModal({
13218
13264
  // src/components/AncestryLinkDetailsPanel.jsx
13219
13265
  var import_react26 = __toESM(require("react"));
13220
13266
  var import_fi21 = require("react-icons/fi");
13221
- function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenReference, onMentionClick, onUploadFile }) {
13267
+ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenReference, onMentionClick, onUploadFile, onHighlight }) {
13222
13268
  var _a, _b, _c, _d;
13223
13269
  const relationshipData = data.relationship || {};
13224
13270
  const description = relationshipData.description || "";
@@ -13227,6 +13273,11 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
13227
13273
  const sourceName = ((_b = (_a = data.sourceNode) == null ? void 0 : _a.userData) == null ? void 0 : _b.name) || "Origem";
13228
13274
  const targetName = ((_d = (_c = data.targetNode) == null ? void 0 : _c.userData) == null ? void 0 : _d.name) || "Destino";
13229
13275
  const [isReadMode, setIsReadMode] = (0, import_react26.useState)(false);
13276
+ import_react26.default.useEffect(() => {
13277
+ return () => {
13278
+ if (onHighlight) onHighlight(null);
13279
+ };
13280
+ }, [onHighlight]);
13230
13281
  const swallow = (e) => e.stopPropagation();
13231
13282
  const handleImageClickFromText = (url, name) => {
13232
13283
  if (onOpenImageViewer) {
@@ -13260,7 +13311,23 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
13260
13311
  onMentionClick,
13261
13312
  onImageClick: handleImageClickFromText
13262
13313
  }
13263
- ) : /* @__PURE__ */ import_react26.default.createElement(import_react26.default.Fragment, null, /* @__PURE__ */ import_react26.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ import_react26.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react26.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react26.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react26.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ import_react26.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ import_react26.default.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ import_react26.default.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ import_react26.default.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ import_react26.default.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ import_react26.default.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ import_react26.default.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ import_react26.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react26.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react26.default.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react26.default.createElement(
13314
+ ) : /* @__PURE__ */ import_react26.default.createElement(import_react26.default.Fragment, null, /* @__PURE__ */ import_react26.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ import_react26.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react26.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react26.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react26.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ import_react26.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ import_react26.default.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ import_react26.default.createElement(
13315
+ "span",
13316
+ {
13317
+ className: "truncate max-w-[150px] cursor-help hover:text-blue-400 transition-colors",
13318
+ onMouseEnter: () => onHighlight == null ? void 0 : onHighlight(data.source),
13319
+ onMouseLeave: () => onHighlight == null ? void 0 : onHighlight(null)
13320
+ },
13321
+ sourceName
13322
+ ), /* @__PURE__ */ import_react26.default.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ import_react26.default.createElement(
13323
+ "span",
13324
+ {
13325
+ className: "truncate max-w-[150px] cursor-help hover:text-blue-400 transition-colors",
13326
+ onMouseEnter: () => onHighlight == null ? void 0 : onHighlight(data.target),
13327
+ onMouseLeave: () => onHighlight == null ? void 0 : onHighlight(null)
13328
+ },
13329
+ targetName
13330
+ ))), /* @__PURE__ */ import_react26.default.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ import_react26.default.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ import_react26.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react26.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react26.default.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react26.default.createElement(
13264
13331
  "button",
13265
13332
  {
13266
13333
  onClick: () => setIsReadMode(true),
@@ -19444,7 +19511,8 @@ function XViewScene({
19444
19511
  onOpenReference: handleOpenReference,
19445
19512
  onMentionClick: handleAddExistingNode,
19446
19513
  onUploadFile: upload_file_action,
19447
- userRole: userPermissionRole
19514
+ userRole: userPermissionRole,
19515
+ onHighlight: handleReadModeHighlight
19448
19516
  }
19449
19517
  ),
19450
19518
  ancestryLinkDetails && /* @__PURE__ */ import_react31.default.createElement(
@@ -19455,7 +19523,8 @@ function XViewScene({
19455
19523
  onOpenImageViewer: handleOpenImageViewer,
19456
19524
  onOpenReference: handleOpenReference,
19457
19525
  onMentionClick: handleAddExistingNode,
19458
- onUploadFile: upload_file_action
19526
+ onUploadFile: upload_file_action,
19527
+ onHighlight: handleReadModeHighlight
19459
19528
  }
19460
19529
  ),
19461
19530
  /* @__PURE__ */ import_react31.default.createElement(
package/dist/index.mjs CHANGED
@@ -3406,8 +3406,8 @@ function ContextMenu({
3406
3406
  }, [sceneData == null ? void 0 : sceneData.links, renderedLinkIds]);
3407
3407
  if (!data.visible) return null;
3408
3408
  const swallow = (e) => e.stopPropagation();
3409
- const baseButtonClass = "w-full flex items-center gap-2.5 px-2 py-1.5 text-left text-sm rounded-md hover:bg-indigo-500/20 text-slate-200 hover:text-white transition-colors duration-150 truncate";
3410
- const deleteButtonClass = "w-full flex items-center gap-2.5 px-2 py-1.5 text-left text-sm rounded-md hover:bg-red-500/25 text-red-400 hover:text-red-300 transition-colors duration-150 truncate";
3409
+ const baseButtonClass = "w-full flex items-center gap-2.5 px-2 py-1.5 text-left text-sm rounded-md hover:bg-indigo-500/20 text-slate-200 hover:text-white transition-colors duration-150";
3410
+ const deleteButtonClass = "w-full flex items-center gap-2.5 px-2 py-1.5 text-left text-sm rounded-md hover:bg-red-500/25 text-red-400 hover:text-red-300 transition-colors duration-150";
3411
3411
  const getYearFromDate = (dateObj) => {
3412
3412
  var _a2, _b2;
3413
3413
  if (!dateObj) return -Infinity;
@@ -3820,7 +3820,7 @@ function ContextMenu({
3820
3820
  "div",
3821
3821
  {
3822
3822
  ref: menuRef,
3823
- className: "ui-overlay context-menu-class origin-top-left rounded-xl 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 w-[min(92vw,250px)] overflow-hidden",
3823
+ className: "ui-overlay context-menu-class origin-top-left rounded-xl 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 min-w-[220px] max-w-[min(95vw,420px)] w-max overflow-hidden",
3824
3824
  style: { position: "absolute", left: `${menuPos.left}px`, top: `${menuPos.top}px`, zIndex: 1e3 },
3825
3825
  onPointerDown: swallow,
3826
3826
  onPointerMove: swallow,
@@ -9747,7 +9747,8 @@ import {
9747
9747
  FiUser as FiUser2,
9748
9748
  FiSearch as FiSearch5,
9749
9749
  FiCalendar,
9750
- FiRepeat
9750
+ FiRepeat,
9751
+ FiCopy as FiCopy3
9751
9752
  } from "react-icons/fi";
9752
9753
 
9753
9754
  // src/components/QuestReviewStackEditor.jsx
@@ -9965,6 +9966,7 @@ function InSceneQuestForm({
9965
9966
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = useState17(false);
9966
9967
  const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = useState17(false);
9967
9968
  const [assigneeSearchQuery, setAssigneeSearchQuery] = useState17("");
9969
+ const [isNameCopied, setIsNameCopied] = useState17(false);
9968
9970
  const [customProps, setCustomProps] = useState17([]);
9969
9971
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState17(false);
9970
9972
  const propsEndRef = useRef12(null);
@@ -10043,6 +10045,15 @@ function InSceneQuestForm({
10043
10045
  }
10044
10046
  };
10045
10047
  const standardizedName = `${viewName} - ${questCounter} - \xBB ${name || "Nova Quest"}`;
10048
+ const handleCopyName = () => {
10049
+ if (!standardizedName) return;
10050
+ navigator.clipboard.writeText(standardizedName).then(() => {
10051
+ setIsNameCopied(true);
10052
+ setTimeout(() => setIsNameCopied(false), 2e3);
10053
+ }).catch((err) => {
10054
+ console.error("Erro ao copiar nome:", err);
10055
+ });
10056
+ };
10046
10057
  const handleAddProp = () => {
10047
10058
  const newProp = createNewCustomProperty(customProps);
10048
10059
  setCustomProps([...customProps, newProp]);
@@ -10162,13 +10173,22 @@ function InSceneQuestForm({
10162
10173
  },
10163
10174
  className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60"
10164
10175
  }
10165
- ), /* @__PURE__ */ React16.createElement("div", { className: "pt-1 flex items-center gap-1.5" }, /* @__PURE__ */ React16.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }), /* @__PURE__ */ React16.createElement(
10176
+ ), /* @__PURE__ */ React16.createElement("div", { className: "pt-1 flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ React16.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }), /* @__PURE__ */ React16.createElement(
10166
10177
  "p",
10167
10178
  {
10168
10179
  className: "text-xs text-indigo-300 font-medium truncate",
10169
10180
  title: standardizedName
10170
10181
  },
10171
10182
  standardizedName
10183
+ ), /* @__PURE__ */ React16.createElement(
10184
+ "button",
10185
+ {
10186
+ type: "button",
10187
+ onClick: handleCopyName,
10188
+ className: `p-1 rounded-md transition-all hover:bg-white/5 ${isNameCopied ? "text-green-400 scale-110" : "text-slate-500 hover:text-indigo-400"} flex-shrink-0`,
10189
+ title: isNameCopied ? "Nome Copiado!" : "Copiar nome formatado"
10190
+ },
10191
+ isNameCopied ? /* @__PURE__ */ React16.createElement(FiCheck9, { size: 10 }) : /* @__PURE__ */ React16.createElement(FiCopy3, { size: 10 })
10172
10192
  ))), /* @__PURE__ */ React16.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React16.createElement("label", { className: "text-xs text-slate-300 font-medium uppercase tracking-wider" }, "Status da Quest"), /* @__PURE__ */ React16.createElement("div", { className: "relative" }, /* @__PURE__ */ React16.createElement(
10173
10193
  "button",
10174
10194
  {
@@ -11177,7 +11197,7 @@ function NodeDetailsPanel({
11177
11197
  // src/components/QuestDetailsPanel.jsx
11178
11198
  import React19, { useState as useState19, useEffect as useEffect16, useRef as useRef14 } from "react";
11179
11199
  import { v4 as uuidv49 } from "uuid";
11180
- 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";
11200
+ 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, FiCopy as FiCopy4 } from "react-icons/fi";
11181
11201
 
11182
11202
  // src/components/QuestTimeline.jsx
11183
11203
  import React18 from "react";
@@ -11324,6 +11344,7 @@ function QuestDetailsPanel({
11324
11344
  const [existingSections, setExistingSections] = useState19((node == null ? void 0 : node.description_sections) || []);
11325
11345
  const [isSaving, setIsSaving] = useState19(false);
11326
11346
  const [isLinkCopied, setIsLinkCopied] = useState19(false);
11347
+ const [isNameCopied, setIsNameCopied] = useState19(false);
11327
11348
  const [hasUnsavedChanges, setHasUnsavedChanges] = useState19(false);
11328
11349
  const [questDate, setQuestDate] = useState19((node == null ? void 0 : node.Date) || null);
11329
11350
  const [noteText, setNoteText] = useState19("");
@@ -11505,6 +11526,15 @@ function QuestDetailsPanel({
11505
11526
  console.error("Erro ao copiar link:", err);
11506
11527
  });
11507
11528
  };
11529
+ const handleCopyName = () => {
11530
+ if (!standardizedName) return;
11531
+ navigator.clipboard.writeText(standardizedName).then(() => {
11532
+ setIsNameCopied(true);
11533
+ setTimeout(() => setIsNameCopied(false), 2e3);
11534
+ }).catch((err) => {
11535
+ console.error("Erro ao copiar nome:", err);
11536
+ });
11537
+ };
11508
11538
  const swallow = (e) => e.stopPropagation();
11509
11539
  const handleNameChange = (val) => {
11510
11540
  setRawTitle(val);
@@ -11842,7 +11872,16 @@ ${noteText}` : noteText : entry.notes,
11842
11872
  readOnly: !canEdit,
11843
11873
  className: `w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none ${canEdit ? "focus:ring-2 focus:ring-indigo-400/60" : "cursor-default text-slate-400"}`
11844
11874
  }
11845
- ), /* @__PURE__ */ React19.createElement("div", { className: "pt-1 flex items-center gap-1.5" }, /* @__PURE__ */ React19.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }), /* @__PURE__ */ React19.createElement("p", { className: "text-xs text-indigo-300 font-medium truncate", title: standardizedName }, standardizedName))), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5 mt-2" }, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300 font-medium uppercase tracking-wider" }, "Prazo / Data"), canEdit && !questDate && /* @__PURE__ */ React19.createElement(
11875
+ ), /* @__PURE__ */ React19.createElement("div", { className: "pt-1 flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ React19.createElement("span", { className: "text-[10px] uppercase font-bold text-slate-500 tracking-wider" }), /* @__PURE__ */ React19.createElement("p", { className: "text-xs text-indigo-300 font-medium truncate", title: standardizedName }, standardizedName), /* @__PURE__ */ React19.createElement(
11876
+ "button",
11877
+ {
11878
+ type: "button",
11879
+ onClick: handleCopyName,
11880
+ className: `p-1 rounded-md transition-all hover:bg-white/5 ${isNameCopied ? "text-green-400 scale-110" : "text-slate-500 hover:text-indigo-400"} flex-shrink-0`,
11881
+ title: isNameCopied ? "Nome Copiado!" : "Copiar nome formatado"
11882
+ },
11883
+ isNameCopied ? /* @__PURE__ */ React19.createElement(FiCheck11, { size: 10 }) : /* @__PURE__ */ React19.createElement(FiCopy4, { size: 10 })
11884
+ ))), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5 mt-2" }, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300 font-medium uppercase tracking-wider" }, "Prazo / Data"), canEdit && !questDate && /* @__PURE__ */ React19.createElement(
11846
11885
  "button",
11847
11886
  {
11848
11887
  type: "button",
@@ -12526,7 +12565,8 @@ function RelationshipDetailsPanel({
12526
12565
  onOpenReference,
12527
12566
  onMentionClick,
12528
12567
  onUploadFile,
12529
- userRole
12568
+ userRole,
12569
+ onHighlight
12530
12570
  }) {
12531
12571
  const [name, setName] = useState21((link == null ? void 0 : link.name) ?? "");
12532
12572
  const [description, setDescription] = useState21((link == null ? void 0 : link.description) ?? "");
@@ -12556,6 +12596,11 @@ function RelationshipDetailsPanel({
12556
12596
  setTargetLabel((link == null ? void 0 : link.target_label) ?? "");
12557
12597
  setHasUnsavedChanges(false);
12558
12598
  }, [link]);
12599
+ useEffect18(() => {
12600
+ return () => {
12601
+ if (onHighlight) onHighlight(null);
12602
+ };
12603
+ }, [onHighlight]);
12559
12604
  const swallow = (e) => e.stopPropagation();
12560
12605
  const handleAddProp = () => {
12561
12606
  if (!canEdit) return;
@@ -12602,10 +12647,8 @@ function RelationshipDetailsPanel({
12602
12647
  isCurved: link.isCurved,
12603
12648
  curveOffset: link.curveOffset
12604
12649
  };
12605
- if (currentSourceLabel.trim())
12606
- dataToSave.source_label = currentSourceLabel.trim();
12607
- if (currentTargetLabel.trim())
12608
- dataToSave.target_label = currentTargetLabel.trim();
12650
+ dataToSave.source_label = currentSourceLabel.trim();
12651
+ dataToSave.target_label = currentTargetLabel.trim();
12609
12652
  await onSave(dataToSave, keepOpen);
12610
12653
  onDataUpdate(dataToSave);
12611
12654
  setHasUnsavedChanges(false);
@@ -12735,6 +12778,8 @@ function RelationshipDetailsPanel({
12735
12778
  },
12736
12779
  placeholder: "Ex: Conceitos",
12737
12780
  disabled: !canEdit,
12781
+ onFocus: () => onHighlight == null ? void 0 : onHighlight(link.source),
12782
+ onBlur: () => onHighlight == null ? void 0 : onHighlight(null),
12738
12783
  className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
12739
12784
  ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
12740
12785
  `
@@ -12750,6 +12795,8 @@ function RelationshipDetailsPanel({
12750
12795
  },
12751
12796
  placeholder: "Ex: Refer\xEAncias",
12752
12797
  disabled: !canEdit,
12798
+ onFocus: () => onHighlight == null ? void 0 : onHighlight(link.target),
12799
+ onBlur: () => onHighlight == null ? void 0 : onHighlight(null),
12753
12800
  className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
12754
12801
  ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
12755
12802
  `
@@ -13249,7 +13296,7 @@ function ImportParentFileModal({
13249
13296
  // src/components/AncestryLinkDetailsPanel.jsx
13250
13297
  import React25, { useState as useState24 } from "react";
13251
13298
  import { FiBookOpen as FiBookOpen6 } from "react-icons/fi";
13252
- function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenReference, onMentionClick, onUploadFile }) {
13299
+ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenReference, onMentionClick, onUploadFile, onHighlight }) {
13253
13300
  var _a, _b, _c, _d;
13254
13301
  const relationshipData = data.relationship || {};
13255
13302
  const description = relationshipData.description || "";
@@ -13258,6 +13305,11 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
13258
13305
  const sourceName = ((_b = (_a = data.sourceNode) == null ? void 0 : _a.userData) == null ? void 0 : _b.name) || "Origem";
13259
13306
  const targetName = ((_d = (_c = data.targetNode) == null ? void 0 : _c.userData) == null ? void 0 : _d.name) || "Destino";
13260
13307
  const [isReadMode, setIsReadMode] = useState24(false);
13308
+ React25.useEffect(() => {
13309
+ return () => {
13310
+ if (onHighlight) onHighlight(null);
13311
+ };
13312
+ }, [onHighlight]);
13261
13313
  const swallow = (e) => e.stopPropagation();
13262
13314
  const handleImageClickFromText = (url, name) => {
13263
13315
  if (onOpenImageViewer) {
@@ -13291,7 +13343,23 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
13291
13343
  onMentionClick,
13292
13344
  onImageClick: handleImageClickFromText
13293
13345
  }
13294
- ) : /* @__PURE__ */ React25.createElement(React25.Fragment, null, /* @__PURE__ */ React25.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ React25.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React25.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React25.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React25.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ React25.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ React25.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ React25.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ React25.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ React25.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ React25.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React25.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ React25.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React25.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React25.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React25.createElement(
13346
+ ) : /* @__PURE__ */ React25.createElement(React25.Fragment, null, /* @__PURE__ */ React25.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ React25.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React25.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React25.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React25.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ React25.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ React25.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ React25.createElement(
13347
+ "span",
13348
+ {
13349
+ className: "truncate max-w-[150px] cursor-help hover:text-blue-400 transition-colors",
13350
+ onMouseEnter: () => onHighlight == null ? void 0 : onHighlight(data.source),
13351
+ onMouseLeave: () => onHighlight == null ? void 0 : onHighlight(null)
13352
+ },
13353
+ sourceName
13354
+ ), /* @__PURE__ */ React25.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ React25.createElement(
13355
+ "span",
13356
+ {
13357
+ className: "truncate max-w-[150px] cursor-help hover:text-blue-400 transition-colors",
13358
+ onMouseEnter: () => onHighlight == null ? void 0 : onHighlight(data.target),
13359
+ onMouseLeave: () => onHighlight == null ? void 0 : onHighlight(null)
13360
+ },
13361
+ targetName
13362
+ ))), /* @__PURE__ */ React25.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React25.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ React25.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React25.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React25.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React25.createElement(
13295
13363
  "button",
13296
13364
  {
13297
13365
  onClick: () => setIsReadMode(true),
@@ -19500,7 +19568,8 @@ function XViewScene({
19500
19568
  onOpenReference: handleOpenReference,
19501
19569
  onMentionClick: handleAddExistingNode,
19502
19570
  onUploadFile: upload_file_action,
19503
- userRole: userPermissionRole
19571
+ userRole: userPermissionRole,
19572
+ onHighlight: handleReadModeHighlight
19504
19573
  }
19505
19574
  ),
19506
19575
  ancestryLinkDetails && /* @__PURE__ */ React30.createElement(
@@ -19511,7 +19580,8 @@ function XViewScene({
19511
19580
  onOpenImageViewer: handleOpenImageViewer,
19512
19581
  onOpenReference: handleOpenReference,
19513
19582
  onMentionClick: handleAddExistingNode,
19514
- onUploadFile: upload_file_action
19583
+ onUploadFile: upload_file_action,
19584
+ onHighlight: handleReadModeHighlight
19515
19585
  }
19516
19586
  ),
19517
19587
  /* @__PURE__ */ React30.createElement(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lv-x-software-house/x_view",
3
- "version": "1.2.6-dev.58",
3
+ "version": "1.2.6-dev.60",
4
4
  "description": "Pacote privado contendo os componentes e lógica de renderização 3D do X View.",
5
5
  "author": "iv.x - Engenharia de Software - ivxsoftwarehouse@gmail.com",
6
6
  "license": "UNLICENSED",