@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 +83 -14
- package/dist/index.mjs +86 -16
- package/package.json +1 -1
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
|
|
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
|
|
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(
|
|
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)
|
|
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
|
-
|
|
12575
|
-
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
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)
|
|
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
|
-
|
|
12606
|
-
|
|
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(
|
|
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.
|
|
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",
|