@lv-x-software-house/x_view 1.2.5-dev.14 → 1.2.5-dev.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +204 -69
  2. package/dist/index.mjs +204 -69
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -646,8 +646,10 @@ function XViewSidebar({
646
646
  const typesSet = /* @__PURE__ */ new Set();
647
647
  (dbNodes || []).forEach((node) => {
648
648
  if (Array.isArray(node.type)) {
649
- node.type.forEach((t) => typesSet.add(t));
650
- } else if (node.type) {
649
+ node.type.forEach((t) => {
650
+ if (t && typeof t === "string" && t.trim() !== "") typesSet.add(t);
651
+ });
652
+ } else if (node.type && typeof node.type === "string" && node.type.trim() !== "") {
651
653
  typesSet.add(node.type);
652
654
  }
653
655
  });
@@ -2881,18 +2883,27 @@ function createNewCustomProperty(existingProps = []) {
2881
2883
  };
2882
2884
  }
2883
2885
  var resolveDescriptionReference = (refString, availableNodes = [], availableAncestries = []) => {
2884
- const match = refString.match(/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/);
2886
+ const match = refString.match(
2887
+ /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/
2888
+ );
2885
2889
  if (!match) return null;
2886
2890
  const [_, type, itemId, sectionId] = match;
2887
2891
  let sourceItem = null;
2888
2892
  if (type === "node") {
2889
2893
  sourceItem = availableNodes.find((n) => String(n.id) === String(itemId));
2890
2894
  } else {
2891
- sourceItem = availableAncestries.find((a) => String(a.ancestry_id) === String(itemId));
2895
+ sourceItem = availableAncestries.find(
2896
+ (a) => String(a.ancestry_id) === String(itemId)
2897
+ );
2892
2898
  }
2893
2899
  if (!sourceItem) return null;
2894
- const sections = parseDescriptionSections(sourceItem.description, sourceItem.description_sections);
2895
- const targetSection = sections.find((s) => String(s.id) === String(sectionId));
2900
+ const sections = parseDescriptionSections(
2901
+ sourceItem.description,
2902
+ sourceItem.description_sections
2903
+ );
2904
+ const targetSection = sections.find(
2905
+ (s) => String(s.id) === String(sectionId)
2906
+ );
2896
2907
  if (!targetSection) return null;
2897
2908
  return {
2898
2909
  content: targetSection.content,
@@ -2906,21 +2917,34 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
2906
2917
  let text = rawText;
2907
2918
  const allNodes = parentData ? Object.values(parentData).flatMap((f) => f.nodes || []) : [];
2908
2919
  const allAncestries = ancestryData || [];
2909
- text = text.replace(/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g, (match, type, itemId, sectionId) => {
2910
- const resolved = resolveDescriptionReference(match, allNodes, allAncestries);
2911
- if (resolved && !resolved.error) {
2912
- return resolved.content;
2920
+ text = text.replace(
2921
+ /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g,
2922
+ (match, type, itemId, sectionId) => {
2923
+ const resolved = resolveDescriptionReference(
2924
+ match,
2925
+ allNodes,
2926
+ allAncestries
2927
+ );
2928
+ if (resolved && !resolved.error) {
2929
+ return resolved.content;
2930
+ }
2931
+ return "[Refer\xEAncia indispon\xEDvel]";
2913
2932
  }
2914
- return "[Refer\xEAncia indispon\xEDvel]";
2915
- });
2933
+ );
2916
2934
  text = text.replace(/\*\/\s*\d+(?::[a-zA-Z0-9-]+)?\s*\//g, "");
2917
- text = text.replace(/\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g, (match, nodeId) => {
2918
- const node = allNodes.find((n) => String(n.id) === String(nodeId));
2919
- return node ? `@${node.name}` : `@Men\xE7\xE3o`;
2920
- });
2921
- text = text.replace(/\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g, (match, url, name) => {
2922
- return name ? `[Imagem: ${name}]` : `[Imagem]`;
2923
- });
2935
+ text = text.replace(
2936
+ /\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g,
2937
+ (match, nodeId) => {
2938
+ const node = allNodes.find((n) => String(n.id) === String(nodeId));
2939
+ return node ? `@${node.name}` : `@Men\xE7\xE3o`;
2940
+ }
2941
+ );
2942
+ text = text.replace(
2943
+ /\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g,
2944
+ (match, url, name) => {
2945
+ return name ? `[Imagem: ${name}]` : `[Imagem]`;
2946
+ }
2947
+ );
2924
2948
  text = text.replace(/^#+\s*/gm, "");
2925
2949
  text = text.replace(/```[\s\S]*?```/g, "[C\xF3digo]");
2926
2950
  text = text.replace(/-\s\[[xX ]\]\s*/g, "\u2022 ");
@@ -2928,7 +2952,14 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
2928
2952
  return text.trim();
2929
2953
  }
2930
2954
  function generateTooltipHtml(data, parentData, ancestryData) {
2931
- const ignoredKeys = ["id", "name", "type", "color", "_baseEmissiveIntensity", "description"];
2955
+ const ignoredKeys = [
2956
+ "id",
2957
+ "name",
2958
+ "type",
2959
+ "color",
2960
+ "_baseEmissiveIntensity",
2961
+ "description"
2962
+ ];
2932
2963
  const customKeys = Object.keys(data).filter((k) => !ignoredKeys.includes(k));
2933
2964
  const extras = customKeys.length;
2934
2965
  let typeDisplay = "Node";
@@ -2943,7 +2974,11 @@ function generateTooltipHtml(data, parentData, ancestryData) {
2943
2974
  <div style="font-weight:600; font-size: 14px; color: #fff;">${typeDisplay}</div>
2944
2975
  <div style="margin-bottom: 2px; color: #e2e8f0;">${data.name || ""}</div>`;
2945
2976
  if (data.description) {
2946
- const cleanDesc = formatDescriptionForTooltip(data.description, parentData, ancestryData);
2977
+ const cleanDesc = formatDescriptionForTooltip(
2978
+ data.description,
2979
+ parentData,
2980
+ ancestryData
2981
+ );
2947
2982
  if (cleanDesc) {
2948
2983
  html += `<div style="
2949
2984
  margin-top: 6px;
@@ -2981,7 +3016,11 @@ function generateLinkTooltipHtml(data, parentData, ancestryData) {
2981
3016
  html += `<div style="font-weight:600; font-size: 14px; color: #a5f3fc; margin-bottom: 4px;">${data.name}</div>`;
2982
3017
  }
2983
3018
  if (hasDescription) {
2984
- const cleanDesc = formatDescriptionForTooltip(data.description, parentData, ancestryData);
3019
+ const cleanDesc = formatDescriptionForTooltip(
3020
+ data.description,
3021
+ parentData,
3022
+ ancestryData
3023
+ );
2985
3024
  if (cleanDesc) {
2986
3025
  html += `<div style="
2987
3026
  display: -webkit-box;
@@ -3063,7 +3102,8 @@ var IGNORED_KEYS = [
3063
3102
  "isAddingAbstractionNodes",
3064
3103
  "status",
3065
3104
  "is_quest",
3066
- "raw_title"
3105
+ "raw_title",
3106
+ "assignee_id"
3067
3107
  ];
3068
3108
  function extractCustomPropsFromNode(node) {
3069
3109
  const customPropTypes = node._customPropTypes || {};
@@ -3073,16 +3113,40 @@ function extractCustomPropsFromNode(node) {
3073
3113
  if (t === "date") {
3074
3114
  if (val && typeof val === "object") {
3075
3115
  if ("date_interval" in val) {
3076
- return { id: v4_default(), key, type: "date", value: { type: "Date Interval", start: val.date_interval.start || "", end: val.date_interval.end || "" } };
3116
+ return {
3117
+ id: v4_default(),
3118
+ key,
3119
+ type: "date",
3120
+ value: {
3121
+ type: "Date Interval",
3122
+ start: val.date_interval.start || "",
3123
+ end: val.date_interval.end || ""
3124
+ }
3125
+ };
3077
3126
  }
3078
3127
  if ("year" in val) {
3079
- return { id: v4_default(), key, type: "date", value: { type: "Ano", value: val.year || "" } };
3128
+ return {
3129
+ id: v4_default(),
3130
+ key,
3131
+ type: "date",
3132
+ value: { type: "Ano", value: val.year || "" }
3133
+ };
3080
3134
  }
3081
3135
  if ("date" in val) {
3082
- return { id: v4_default(), key, type: "date", value: { type: "Data", value: val.date || "" } };
3136
+ return {
3137
+ id: v4_default(),
3138
+ key,
3139
+ type: "date",
3140
+ value: { type: "Data", value: val.date || "" }
3141
+ };
3083
3142
  }
3084
3143
  }
3085
- return { id: v4_default(), key, type: "date", value: { type: "Data", value: "" } };
3144
+ return {
3145
+ id: v4_default(),
3146
+ key,
3147
+ type: "date",
3148
+ value: { type: "Data", value: "" }
3149
+ };
3086
3150
  }
3087
3151
  if (t === "list" || t === "links" || t === "images" || t === "documents") {
3088
3152
  const safeList = (val || []).map((item) => ({
@@ -3092,7 +3156,12 @@ function extractCustomPropsFromNode(node) {
3092
3156
  }));
3093
3157
  return { id: v4_default(), key, type: t, value: safeList };
3094
3158
  }
3095
- return { id: v4_default(), key, type: t, value: t === "number" ? Number(val) || 0 : String(val ?? "") };
3159
+ return {
3160
+ id: v4_default(),
3161
+ key,
3162
+ type: t,
3163
+ value: t === "number" ? Number(val) || 0 : String(val ?? "")
3164
+ };
3096
3165
  });
3097
3166
  }
3098
3167
  function toObjectFromCustomProps(customProps) {
@@ -3107,7 +3176,12 @@ function toObjectFromCustomProps(customProps) {
3107
3176
  const { type: uiDateType, ...dateValues } = p.value || {};
3108
3177
  if (uiDateType) {
3109
3178
  if (uiDateType === "Date Interval") {
3110
- out[key] = { date_interval: { start: dateValues.start || "", end: dateValues.end || "" } };
3179
+ out[key] = {
3180
+ date_interval: {
3181
+ start: dateValues.start || "",
3182
+ end: dateValues.end || ""
3183
+ }
3184
+ };
3111
3185
  } else if (uiDateType === "Ano") {
3112
3186
  out[key] = { year: dateValues.value || "" };
3113
3187
  } else {
@@ -3143,10 +3217,20 @@ function createTextSprite(text, fontSize = 64) {
3143
3217
  context.fillText(effectiveText, canvas.width / 2, canvas.height / 2);
3144
3218
  const texture = new THREE2.CanvasTexture(canvas);
3145
3219
  texture.colorSpace = THREE2.SRGBColorSpace;
3146
- const spriteMaterial = new THREE2.SpriteMaterial({ map: texture, sizeAttenuation: true, depthWrite: false, transparent: true, toneMapped: false });
3220
+ const spriteMaterial = new THREE2.SpriteMaterial({
3221
+ map: texture,
3222
+ sizeAttenuation: true,
3223
+ depthWrite: false,
3224
+ transparent: true,
3225
+ toneMapped: false
3226
+ });
3147
3227
  const sprite = new THREE2.Sprite(spriteMaterial);
3148
3228
  const scaleFactor = 0.05;
3149
- sprite.scale.set(canvas.width * scaleFactor, canvas.height * scaleFactor, 1);
3229
+ sprite.scale.set(
3230
+ canvas.width * scaleFactor,
3231
+ canvas.height * scaleFactor,
3232
+ 1
3233
+ );
3150
3234
  sprite.layers.set(DEFAULT_LAYER);
3151
3235
  return sprite;
3152
3236
  }
@@ -3162,7 +3246,14 @@ function makeGlowTexture() {
3162
3246
  const canvas = document.createElement("canvas");
3163
3247
  canvas.width = canvas.height = size;
3164
3248
  const ctx = canvas.getContext("2d");
3165
- const grd = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
3249
+ const grd = ctx.createRadialGradient(
3250
+ size / 2,
3251
+ size / 2,
3252
+ 0,
3253
+ size / 2,
3254
+ size / 2,
3255
+ size / 2
3256
+ );
3166
3257
  grd.addColorStop(0, "rgba(255,255,255,1)");
3167
3258
  grd.addColorStop(0.4, "rgba(255,255,255,0.45)");
3168
3259
  grd.addColorStop(1, "rgba(255,255,255,0)");
@@ -3178,8 +3269,19 @@ function addGlowAura(mesh, hexColor, glowTexture, auraScale) {
3178
3269
  let midBoost = 1 - Math.abs(L - 0.5) * 2;
3179
3270
  midBoost = THREE2.MathUtils.clamp(midBoost, 0, 1);
3180
3271
  const auraOpacity = THREE2.MathUtils.lerp(0.25, 0.8, midBoost);
3181
- const auraColor = new THREE2.Color(hexColor).lerp(new THREE2.Color("#ffffff"), 0.25);
3182
- const auraMat = new THREE2.SpriteMaterial({ map: glowTexture, color: auraColor, opacity: auraOpacity, blending: THREE2.AdditiveBlending, transparent: true, depthWrite: false, toneMapped: false });
3272
+ const auraColor = new THREE2.Color(hexColor).lerp(
3273
+ new THREE2.Color("#ffffff"),
3274
+ 0.25
3275
+ );
3276
+ const auraMat = new THREE2.SpriteMaterial({
3277
+ map: glowTexture,
3278
+ color: auraColor,
3279
+ opacity: auraOpacity,
3280
+ blending: THREE2.AdditiveBlending,
3281
+ transparent: true,
3282
+ depthWrite: false,
3283
+ toneMapped: false
3284
+ });
3183
3285
  const aura = new THREE2.Sprite(auraMat);
3184
3286
  aura.scale.setScalar(auraScale);
3185
3287
  aura.name = "aura";
@@ -3212,7 +3314,16 @@ function calculateNodePositions(nodes) {
3212
3314
  }
3213
3315
  return positions;
3214
3316
  }
3215
- function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, isSceneBusy, parentData, ancestryData }) {
3317
+ function updateTooltip({
3318
+ tooltipEl,
3319
+ hoveredNode,
3320
+ hoveredLink,
3321
+ camera,
3322
+ mountEl,
3323
+ isSceneBusy,
3324
+ parentData,
3325
+ ancestryData
3326
+ }) {
3216
3327
  var _a, _b;
3217
3328
  if (!tooltipEl || !camera || !mountEl) return;
3218
3329
  let content = "";
@@ -3223,7 +3334,11 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3223
3334
  currentId = `node_${hoveredNode.userData.id}`;
3224
3335
  positionTarget = hoveredNode;
3225
3336
  if (tooltipEl.dataset.currentId !== currentId) {
3226
- content = generateTooltipHtml(hoveredNode.userData, parentData, ancestryData);
3337
+ content = generateTooltipHtml(
3338
+ hoveredNode.userData,
3339
+ parentData,
3340
+ ancestryData
3341
+ );
3227
3342
  }
3228
3343
  } else if (hoveredLink && !isSceneBusy) {
3229
3344
  const linkData = hoveredLink.userData.isAncestryLink ? hoveredLink.userData.relationship || {} : hoveredLink.userData;
@@ -3233,9 +3348,16 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3233
3348
  if (hoveredLink.userData.isCurved) {
3234
3349
  const positions = hoveredLink.geometry.attributes.position.array;
3235
3350
  const midIndex = Math.floor(positions.length / 2 / 3) * 3;
3236
- positionTarget = new THREE2.Vector3(positions[midIndex], positions[midIndex + 1], positions[midIndex + 2]);
3351
+ positionTarget = new THREE2.Vector3(
3352
+ positions[midIndex],
3353
+ positions[midIndex + 1],
3354
+ positions[midIndex + 2]
3355
+ );
3237
3356
  } else {
3238
- positionTarget = new THREE2.Vector3().addVectors(hoveredLink.userData.sourceNode.position, hoveredLink.userData.targetNode.position).multiplyScalar(0.5);
3357
+ positionTarget = new THREE2.Vector3().addVectors(
3358
+ hoveredLink.userData.sourceNode.position,
3359
+ hoveredLink.userData.targetNode.position
3360
+ ).multiplyScalar(0.5);
3239
3361
  }
3240
3362
  isLink = true;
3241
3363
  if (tooltipEl.dataset.currentId !== currentId) {
@@ -3264,9 +3386,11 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3264
3386
  const tooltipRect = tooltipEl.getBoundingClientRect();
3265
3387
  const offsetX = 20;
3266
3388
  const offsetY = 20;
3267
- if (x + tooltipRect.width + offsetX > clientWidth) x = x - tooltipRect.width - offsetX;
3389
+ if (x + tooltipRect.width + offsetX > clientWidth)
3390
+ x = x - tooltipRect.width - offsetX;
3268
3391
  else x = x + offsetX;
3269
- if (y + tooltipRect.height + offsetY > clientHeight) y = y - tooltipRect.height - offsetY;
3392
+ if (y + tooltipRect.height + offsetY > clientHeight)
3393
+ y = y - tooltipRect.height - offsetY;
3270
3394
  else y = y + offsetY;
3271
3395
  tooltipEl.style.display = "block";
3272
3396
  tooltipEl.style.left = `${x}px`;
@@ -3300,7 +3424,9 @@ var processDescriptionForSave = (text, existingSections = []) => {
3300
3424
  const content = parts[i + 2] || "";
3301
3425
  let finalUuid = null;
3302
3426
  if (suffix) {
3303
- const existingMatch = existingSections.find((s) => s.id && s.id.includes(suffix));
3427
+ const existingMatch = existingSections.find(
3428
+ (s) => s.id && s.id.includes(suffix)
3429
+ );
3304
3430
  if (existingMatch) {
3305
3431
  finalUuid = existingMatch.id;
3306
3432
  } else {
@@ -3391,28 +3517,34 @@ var extractFileUrlsFromProperties = (dataObject) => {
3391
3517
  function useResizablePanel({ initialWidth, minWidth, maxWidth }) {
3392
3518
  const [width, setWidth] = (0, import_react3.useState)(initialWidth);
3393
3519
  const [isResizing, setIsResizing] = (0, import_react3.useState)(false);
3394
- const handlePointerDown = (0, import_react3.useCallback)((e) => {
3395
- e.preventDefault();
3396
- e.stopPropagation();
3397
- setIsResizing(true);
3398
- const startX = e.clientX;
3399
- const startWidth = width;
3400
- const originalUserSelect = document.body.style.userSelect;
3401
- document.body.style.userSelect = "none";
3402
- const handlePointerMove = (moveEvent) => {
3403
- const deltaX = startX - moveEvent.clientX;
3404
- const newWidth = Math.min(Math.max(startWidth + deltaX, minWidth), maxWidth);
3405
- setWidth(newWidth);
3406
- };
3407
- const handlePointerUp = () => {
3408
- setIsResizing(false);
3409
- document.body.style.userSelect = originalUserSelect;
3410
- document.removeEventListener("pointermove", handlePointerMove);
3411
- document.removeEventListener("pointerup", handlePointerUp);
3412
- };
3413
- document.addEventListener("pointermove", handlePointerMove);
3414
- document.addEventListener("pointerup", handlePointerUp);
3415
- }, [width, minWidth, maxWidth]);
3520
+ const handlePointerDown = (0, import_react3.useCallback)(
3521
+ (e) => {
3522
+ e.preventDefault();
3523
+ e.stopPropagation();
3524
+ setIsResizing(true);
3525
+ const startX = e.clientX;
3526
+ const startWidth = width;
3527
+ const originalUserSelect = document.body.style.userSelect;
3528
+ document.body.style.userSelect = "none";
3529
+ const handlePointerMove = (moveEvent) => {
3530
+ const deltaX = startX - moveEvent.clientX;
3531
+ const newWidth = Math.min(
3532
+ Math.max(startWidth + deltaX, minWidth),
3533
+ maxWidth
3534
+ );
3535
+ setWidth(newWidth);
3536
+ };
3537
+ const handlePointerUp = () => {
3538
+ setIsResizing(false);
3539
+ document.body.style.userSelect = originalUserSelect;
3540
+ document.removeEventListener("pointermove", handlePointerMove);
3541
+ document.removeEventListener("pointerup", handlePointerUp);
3542
+ };
3543
+ document.addEventListener("pointermove", handlePointerMove);
3544
+ document.addEventListener("pointerup", handlePointerUp);
3545
+ },
3546
+ [width, minWidth, maxWidth]
3547
+ );
3416
3548
  return { width, isResizing, handlePointerDown, setWidth };
3417
3549
  }
3418
3550
 
@@ -7625,11 +7757,12 @@ function InSceneCreationForm({
7625
7757
  }, [hasImages, useImageAsTexture, onImageChange]);
7626
7758
  (0, import_react14.useEffect)(() => {
7627
7759
  let result = [];
7760
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
7628
7761
  if (typeInput.trim() === "") {
7629
- result = existingTypes.filter((t) => !types.includes(t));
7762
+ result = validExistingTypes.filter((t) => !types.includes(t));
7630
7763
  } else {
7631
7764
  const lowercasedInput = typeInput.toLowerCase();
7632
- result = existingTypes.filter(
7765
+ result = validExistingTypes.filter(
7633
7766
  (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
7634
7767
  );
7635
7768
  }
@@ -8477,12 +8610,13 @@ function NodeDetailsPanel({
8477
8610
  }
8478
8611
  }, [hasImages, useImageAsTexture]);
8479
8612
  (0, import_react17.useEffect)(() => {
8613
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
8480
8614
  if (typeInput.trim() === "") {
8481
- setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
8615
+ setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
8482
8616
  } else {
8483
8617
  const lowercasedInput = typeInput.toLowerCase();
8484
8618
  setFilteredTypes(
8485
- existingTypes.filter(
8619
+ validExistingTypes.filter(
8486
8620
  (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
8487
8621
  )
8488
8622
  );
@@ -8978,12 +9112,13 @@ function QuestDetailsPanel({
8978
9112
  }
8979
9113
  }, [node]);
8980
9114
  (0, import_react18.useEffect)(() => {
9115
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
8981
9116
  if (typeInput.trim() === "") {
8982
- setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
9117
+ setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
8983
9118
  } else {
8984
9119
  const lowercasedInput = typeInput.toLowerCase();
8985
9120
  setFilteredTypes(
8986
- existingTypes.filter(
9121
+ validExistingTypes.filter(
8987
9122
  (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
8988
9123
  )
8989
9124
  );
package/dist/index.mjs CHANGED
@@ -607,8 +607,10 @@ function XViewSidebar({
607
607
  const typesSet = /* @__PURE__ */ new Set();
608
608
  (dbNodes || []).forEach((node) => {
609
609
  if (Array.isArray(node.type)) {
610
- node.type.forEach((t) => typesSet.add(t));
611
- } else if (node.type) {
610
+ node.type.forEach((t) => {
611
+ if (t && typeof t === "string" && t.trim() !== "") typesSet.add(t);
612
+ });
613
+ } else if (node.type && typeof node.type === "string" && node.type.trim() !== "") {
612
614
  typesSet.add(node.type);
613
615
  }
614
616
  });
@@ -2842,18 +2844,27 @@ function createNewCustomProperty(existingProps = []) {
2842
2844
  };
2843
2845
  }
2844
2846
  var resolveDescriptionReference = (refString, availableNodes = [], availableAncestries = []) => {
2845
- const match = refString.match(/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/);
2847
+ const match = refString.match(
2848
+ /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/
2849
+ );
2846
2850
  if (!match) return null;
2847
2851
  const [_, type, itemId, sectionId] = match;
2848
2852
  let sourceItem = null;
2849
2853
  if (type === "node") {
2850
2854
  sourceItem = availableNodes.find((n) => String(n.id) === String(itemId));
2851
2855
  } else {
2852
- sourceItem = availableAncestries.find((a) => String(a.ancestry_id) === String(itemId));
2856
+ sourceItem = availableAncestries.find(
2857
+ (a) => String(a.ancestry_id) === String(itemId)
2858
+ );
2853
2859
  }
2854
2860
  if (!sourceItem) return null;
2855
- const sections = parseDescriptionSections(sourceItem.description, sourceItem.description_sections);
2856
- const targetSection = sections.find((s) => String(s.id) === String(sectionId));
2861
+ const sections = parseDescriptionSections(
2862
+ sourceItem.description,
2863
+ sourceItem.description_sections
2864
+ );
2865
+ const targetSection = sections.find(
2866
+ (s) => String(s.id) === String(sectionId)
2867
+ );
2857
2868
  if (!targetSection) return null;
2858
2869
  return {
2859
2870
  content: targetSection.content,
@@ -2867,21 +2878,34 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
2867
2878
  let text = rawText;
2868
2879
  const allNodes = parentData ? Object.values(parentData).flatMap((f) => f.nodes || []) : [];
2869
2880
  const allAncestries = ancestryData || [];
2870
- text = text.replace(/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g, (match, type, itemId, sectionId) => {
2871
- const resolved = resolveDescriptionReference(match, allNodes, allAncestries);
2872
- if (resolved && !resolved.error) {
2873
- return resolved.content;
2881
+ text = text.replace(
2882
+ /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g,
2883
+ (match, type, itemId, sectionId) => {
2884
+ const resolved = resolveDescriptionReference(
2885
+ match,
2886
+ allNodes,
2887
+ allAncestries
2888
+ );
2889
+ if (resolved && !resolved.error) {
2890
+ return resolved.content;
2891
+ }
2892
+ return "[Refer\xEAncia indispon\xEDvel]";
2874
2893
  }
2875
- return "[Refer\xEAncia indispon\xEDvel]";
2876
- });
2894
+ );
2877
2895
  text = text.replace(/\*\/\s*\d+(?::[a-zA-Z0-9-]+)?\s*\//g, "");
2878
- text = text.replace(/\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g, (match, nodeId) => {
2879
- const node = allNodes.find((n) => String(n.id) === String(nodeId));
2880
- return node ? `@${node.name}` : `@Men\xE7\xE3o`;
2881
- });
2882
- text = text.replace(/\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g, (match, url, name) => {
2883
- return name ? `[Imagem: ${name}]` : `[Imagem]`;
2884
- });
2896
+ text = text.replace(
2897
+ /\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g,
2898
+ (match, nodeId) => {
2899
+ const node = allNodes.find((n) => String(n.id) === String(nodeId));
2900
+ return node ? `@${node.name}` : `@Men\xE7\xE3o`;
2901
+ }
2902
+ );
2903
+ text = text.replace(
2904
+ /\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g,
2905
+ (match, url, name) => {
2906
+ return name ? `[Imagem: ${name}]` : `[Imagem]`;
2907
+ }
2908
+ );
2885
2909
  text = text.replace(/^#+\s*/gm, "");
2886
2910
  text = text.replace(/```[\s\S]*?```/g, "[C\xF3digo]");
2887
2911
  text = text.replace(/-\s\[[xX ]\]\s*/g, "\u2022 ");
@@ -2889,7 +2913,14 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
2889
2913
  return text.trim();
2890
2914
  }
2891
2915
  function generateTooltipHtml(data, parentData, ancestryData) {
2892
- const ignoredKeys = ["id", "name", "type", "color", "_baseEmissiveIntensity", "description"];
2916
+ const ignoredKeys = [
2917
+ "id",
2918
+ "name",
2919
+ "type",
2920
+ "color",
2921
+ "_baseEmissiveIntensity",
2922
+ "description"
2923
+ ];
2893
2924
  const customKeys = Object.keys(data).filter((k) => !ignoredKeys.includes(k));
2894
2925
  const extras = customKeys.length;
2895
2926
  let typeDisplay = "Node";
@@ -2904,7 +2935,11 @@ function generateTooltipHtml(data, parentData, ancestryData) {
2904
2935
  <div style="font-weight:600; font-size: 14px; color: #fff;">${typeDisplay}</div>
2905
2936
  <div style="margin-bottom: 2px; color: #e2e8f0;">${data.name || ""}</div>`;
2906
2937
  if (data.description) {
2907
- const cleanDesc = formatDescriptionForTooltip(data.description, parentData, ancestryData);
2938
+ const cleanDesc = formatDescriptionForTooltip(
2939
+ data.description,
2940
+ parentData,
2941
+ ancestryData
2942
+ );
2908
2943
  if (cleanDesc) {
2909
2944
  html += `<div style="
2910
2945
  margin-top: 6px;
@@ -2942,7 +2977,11 @@ function generateLinkTooltipHtml(data, parentData, ancestryData) {
2942
2977
  html += `<div style="font-weight:600; font-size: 14px; color: #a5f3fc; margin-bottom: 4px;">${data.name}</div>`;
2943
2978
  }
2944
2979
  if (hasDescription) {
2945
- const cleanDesc = formatDescriptionForTooltip(data.description, parentData, ancestryData);
2980
+ const cleanDesc = formatDescriptionForTooltip(
2981
+ data.description,
2982
+ parentData,
2983
+ ancestryData
2984
+ );
2946
2985
  if (cleanDesc) {
2947
2986
  html += `<div style="
2948
2987
  display: -webkit-box;
@@ -3024,7 +3063,8 @@ var IGNORED_KEYS = [
3024
3063
  "isAddingAbstractionNodes",
3025
3064
  "status",
3026
3065
  "is_quest",
3027
- "raw_title"
3066
+ "raw_title",
3067
+ "assignee_id"
3028
3068
  ];
3029
3069
  function extractCustomPropsFromNode(node) {
3030
3070
  const customPropTypes = node._customPropTypes || {};
@@ -3034,16 +3074,40 @@ function extractCustomPropsFromNode(node) {
3034
3074
  if (t === "date") {
3035
3075
  if (val && typeof val === "object") {
3036
3076
  if ("date_interval" in val) {
3037
- return { id: v4_default(), key, type: "date", value: { type: "Date Interval", start: val.date_interval.start || "", end: val.date_interval.end || "" } };
3077
+ return {
3078
+ id: v4_default(),
3079
+ key,
3080
+ type: "date",
3081
+ value: {
3082
+ type: "Date Interval",
3083
+ start: val.date_interval.start || "",
3084
+ end: val.date_interval.end || ""
3085
+ }
3086
+ };
3038
3087
  }
3039
3088
  if ("year" in val) {
3040
- return { id: v4_default(), key, type: "date", value: { type: "Ano", value: val.year || "" } };
3089
+ return {
3090
+ id: v4_default(),
3091
+ key,
3092
+ type: "date",
3093
+ value: { type: "Ano", value: val.year || "" }
3094
+ };
3041
3095
  }
3042
3096
  if ("date" in val) {
3043
- return { id: v4_default(), key, type: "date", value: { type: "Data", value: val.date || "" } };
3097
+ return {
3098
+ id: v4_default(),
3099
+ key,
3100
+ type: "date",
3101
+ value: { type: "Data", value: val.date || "" }
3102
+ };
3044
3103
  }
3045
3104
  }
3046
- return { id: v4_default(), key, type: "date", value: { type: "Data", value: "" } };
3105
+ return {
3106
+ id: v4_default(),
3107
+ key,
3108
+ type: "date",
3109
+ value: { type: "Data", value: "" }
3110
+ };
3047
3111
  }
3048
3112
  if (t === "list" || t === "links" || t === "images" || t === "documents") {
3049
3113
  const safeList = (val || []).map((item) => ({
@@ -3053,7 +3117,12 @@ function extractCustomPropsFromNode(node) {
3053
3117
  }));
3054
3118
  return { id: v4_default(), key, type: t, value: safeList };
3055
3119
  }
3056
- return { id: v4_default(), key, type: t, value: t === "number" ? Number(val) || 0 : String(val ?? "") };
3120
+ return {
3121
+ id: v4_default(),
3122
+ key,
3123
+ type: t,
3124
+ value: t === "number" ? Number(val) || 0 : String(val ?? "")
3125
+ };
3057
3126
  });
3058
3127
  }
3059
3128
  function toObjectFromCustomProps(customProps) {
@@ -3068,7 +3137,12 @@ function toObjectFromCustomProps(customProps) {
3068
3137
  const { type: uiDateType, ...dateValues } = p.value || {};
3069
3138
  if (uiDateType) {
3070
3139
  if (uiDateType === "Date Interval") {
3071
- out[key] = { date_interval: { start: dateValues.start || "", end: dateValues.end || "" } };
3140
+ out[key] = {
3141
+ date_interval: {
3142
+ start: dateValues.start || "",
3143
+ end: dateValues.end || ""
3144
+ }
3145
+ };
3072
3146
  } else if (uiDateType === "Ano") {
3073
3147
  out[key] = { year: dateValues.value || "" };
3074
3148
  } else {
@@ -3104,10 +3178,20 @@ function createTextSprite(text, fontSize = 64) {
3104
3178
  context.fillText(effectiveText, canvas.width / 2, canvas.height / 2);
3105
3179
  const texture = new THREE2.CanvasTexture(canvas);
3106
3180
  texture.colorSpace = THREE2.SRGBColorSpace;
3107
- const spriteMaterial = new THREE2.SpriteMaterial({ map: texture, sizeAttenuation: true, depthWrite: false, transparent: true, toneMapped: false });
3181
+ const spriteMaterial = new THREE2.SpriteMaterial({
3182
+ map: texture,
3183
+ sizeAttenuation: true,
3184
+ depthWrite: false,
3185
+ transparent: true,
3186
+ toneMapped: false
3187
+ });
3108
3188
  const sprite = new THREE2.Sprite(spriteMaterial);
3109
3189
  const scaleFactor = 0.05;
3110
- sprite.scale.set(canvas.width * scaleFactor, canvas.height * scaleFactor, 1);
3190
+ sprite.scale.set(
3191
+ canvas.width * scaleFactor,
3192
+ canvas.height * scaleFactor,
3193
+ 1
3194
+ );
3111
3195
  sprite.layers.set(DEFAULT_LAYER);
3112
3196
  return sprite;
3113
3197
  }
@@ -3123,7 +3207,14 @@ function makeGlowTexture() {
3123
3207
  const canvas = document.createElement("canvas");
3124
3208
  canvas.width = canvas.height = size;
3125
3209
  const ctx = canvas.getContext("2d");
3126
- const grd = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
3210
+ const grd = ctx.createRadialGradient(
3211
+ size / 2,
3212
+ size / 2,
3213
+ 0,
3214
+ size / 2,
3215
+ size / 2,
3216
+ size / 2
3217
+ );
3127
3218
  grd.addColorStop(0, "rgba(255,255,255,1)");
3128
3219
  grd.addColorStop(0.4, "rgba(255,255,255,0.45)");
3129
3220
  grd.addColorStop(1, "rgba(255,255,255,0)");
@@ -3139,8 +3230,19 @@ function addGlowAura(mesh, hexColor, glowTexture, auraScale) {
3139
3230
  let midBoost = 1 - Math.abs(L - 0.5) * 2;
3140
3231
  midBoost = THREE2.MathUtils.clamp(midBoost, 0, 1);
3141
3232
  const auraOpacity = THREE2.MathUtils.lerp(0.25, 0.8, midBoost);
3142
- const auraColor = new THREE2.Color(hexColor).lerp(new THREE2.Color("#ffffff"), 0.25);
3143
- const auraMat = new THREE2.SpriteMaterial({ map: glowTexture, color: auraColor, opacity: auraOpacity, blending: THREE2.AdditiveBlending, transparent: true, depthWrite: false, toneMapped: false });
3233
+ const auraColor = new THREE2.Color(hexColor).lerp(
3234
+ new THREE2.Color("#ffffff"),
3235
+ 0.25
3236
+ );
3237
+ const auraMat = new THREE2.SpriteMaterial({
3238
+ map: glowTexture,
3239
+ color: auraColor,
3240
+ opacity: auraOpacity,
3241
+ blending: THREE2.AdditiveBlending,
3242
+ transparent: true,
3243
+ depthWrite: false,
3244
+ toneMapped: false
3245
+ });
3144
3246
  const aura = new THREE2.Sprite(auraMat);
3145
3247
  aura.scale.setScalar(auraScale);
3146
3248
  aura.name = "aura";
@@ -3173,7 +3275,16 @@ function calculateNodePositions(nodes) {
3173
3275
  }
3174
3276
  return positions;
3175
3277
  }
3176
- function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, isSceneBusy, parentData, ancestryData }) {
3278
+ function updateTooltip({
3279
+ tooltipEl,
3280
+ hoveredNode,
3281
+ hoveredLink,
3282
+ camera,
3283
+ mountEl,
3284
+ isSceneBusy,
3285
+ parentData,
3286
+ ancestryData
3287
+ }) {
3177
3288
  var _a, _b;
3178
3289
  if (!tooltipEl || !camera || !mountEl) return;
3179
3290
  let content = "";
@@ -3184,7 +3295,11 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3184
3295
  currentId = `node_${hoveredNode.userData.id}`;
3185
3296
  positionTarget = hoveredNode;
3186
3297
  if (tooltipEl.dataset.currentId !== currentId) {
3187
- content = generateTooltipHtml(hoveredNode.userData, parentData, ancestryData);
3298
+ content = generateTooltipHtml(
3299
+ hoveredNode.userData,
3300
+ parentData,
3301
+ ancestryData
3302
+ );
3188
3303
  }
3189
3304
  } else if (hoveredLink && !isSceneBusy) {
3190
3305
  const linkData = hoveredLink.userData.isAncestryLink ? hoveredLink.userData.relationship || {} : hoveredLink.userData;
@@ -3194,9 +3309,16 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3194
3309
  if (hoveredLink.userData.isCurved) {
3195
3310
  const positions = hoveredLink.geometry.attributes.position.array;
3196
3311
  const midIndex = Math.floor(positions.length / 2 / 3) * 3;
3197
- positionTarget = new THREE2.Vector3(positions[midIndex], positions[midIndex + 1], positions[midIndex + 2]);
3312
+ positionTarget = new THREE2.Vector3(
3313
+ positions[midIndex],
3314
+ positions[midIndex + 1],
3315
+ positions[midIndex + 2]
3316
+ );
3198
3317
  } else {
3199
- positionTarget = new THREE2.Vector3().addVectors(hoveredLink.userData.sourceNode.position, hoveredLink.userData.targetNode.position).multiplyScalar(0.5);
3318
+ positionTarget = new THREE2.Vector3().addVectors(
3319
+ hoveredLink.userData.sourceNode.position,
3320
+ hoveredLink.userData.targetNode.position
3321
+ ).multiplyScalar(0.5);
3200
3322
  }
3201
3323
  isLink = true;
3202
3324
  if (tooltipEl.dataset.currentId !== currentId) {
@@ -3225,9 +3347,11 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3225
3347
  const tooltipRect = tooltipEl.getBoundingClientRect();
3226
3348
  const offsetX = 20;
3227
3349
  const offsetY = 20;
3228
- if (x + tooltipRect.width + offsetX > clientWidth) x = x - tooltipRect.width - offsetX;
3350
+ if (x + tooltipRect.width + offsetX > clientWidth)
3351
+ x = x - tooltipRect.width - offsetX;
3229
3352
  else x = x + offsetX;
3230
- if (y + tooltipRect.height + offsetY > clientHeight) y = y - tooltipRect.height - offsetY;
3353
+ if (y + tooltipRect.height + offsetY > clientHeight)
3354
+ y = y - tooltipRect.height - offsetY;
3231
3355
  else y = y + offsetY;
3232
3356
  tooltipEl.style.display = "block";
3233
3357
  tooltipEl.style.left = `${x}px`;
@@ -3261,7 +3385,9 @@ var processDescriptionForSave = (text, existingSections = []) => {
3261
3385
  const content = parts[i + 2] || "";
3262
3386
  let finalUuid = null;
3263
3387
  if (suffix) {
3264
- const existingMatch = existingSections.find((s) => s.id && s.id.includes(suffix));
3388
+ const existingMatch = existingSections.find(
3389
+ (s) => s.id && s.id.includes(suffix)
3390
+ );
3265
3391
  if (existingMatch) {
3266
3392
  finalUuid = existingMatch.id;
3267
3393
  } else {
@@ -3352,28 +3478,34 @@ var extractFileUrlsFromProperties = (dataObject) => {
3352
3478
  function useResizablePanel({ initialWidth, minWidth, maxWidth }) {
3353
3479
  const [width, setWidth] = useState3(initialWidth);
3354
3480
  const [isResizing, setIsResizing] = useState3(false);
3355
- const handlePointerDown = useCallback((e) => {
3356
- e.preventDefault();
3357
- e.stopPropagation();
3358
- setIsResizing(true);
3359
- const startX = e.clientX;
3360
- const startWidth = width;
3361
- const originalUserSelect = document.body.style.userSelect;
3362
- document.body.style.userSelect = "none";
3363
- const handlePointerMove = (moveEvent) => {
3364
- const deltaX = startX - moveEvent.clientX;
3365
- const newWidth = Math.min(Math.max(startWidth + deltaX, minWidth), maxWidth);
3366
- setWidth(newWidth);
3367
- };
3368
- const handlePointerUp = () => {
3369
- setIsResizing(false);
3370
- document.body.style.userSelect = originalUserSelect;
3371
- document.removeEventListener("pointermove", handlePointerMove);
3372
- document.removeEventListener("pointerup", handlePointerUp);
3373
- };
3374
- document.addEventListener("pointermove", handlePointerMove);
3375
- document.addEventListener("pointerup", handlePointerUp);
3376
- }, [width, minWidth, maxWidth]);
3481
+ const handlePointerDown = useCallback(
3482
+ (e) => {
3483
+ e.preventDefault();
3484
+ e.stopPropagation();
3485
+ setIsResizing(true);
3486
+ const startX = e.clientX;
3487
+ const startWidth = width;
3488
+ const originalUserSelect = document.body.style.userSelect;
3489
+ document.body.style.userSelect = "none";
3490
+ const handlePointerMove = (moveEvent) => {
3491
+ const deltaX = startX - moveEvent.clientX;
3492
+ const newWidth = Math.min(
3493
+ Math.max(startWidth + deltaX, minWidth),
3494
+ maxWidth
3495
+ );
3496
+ setWidth(newWidth);
3497
+ };
3498
+ const handlePointerUp = () => {
3499
+ setIsResizing(false);
3500
+ document.body.style.userSelect = originalUserSelect;
3501
+ document.removeEventListener("pointermove", handlePointerMove);
3502
+ document.removeEventListener("pointerup", handlePointerUp);
3503
+ };
3504
+ document.addEventListener("pointermove", handlePointerMove);
3505
+ document.addEventListener("pointerup", handlePointerUp);
3506
+ },
3507
+ [width, minWidth, maxWidth]
3508
+ );
3377
3509
  return { width, isResizing, handlePointerDown, setWidth };
3378
3510
  }
3379
3511
 
@@ -7617,11 +7749,12 @@ function InSceneCreationForm({
7617
7749
  }, [hasImages, useImageAsTexture, onImageChange]);
7618
7750
  useEffect13(() => {
7619
7751
  let result = [];
7752
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
7620
7753
  if (typeInput.trim() === "") {
7621
- result = existingTypes.filter((t) => !types.includes(t));
7754
+ result = validExistingTypes.filter((t) => !types.includes(t));
7622
7755
  } else {
7623
7756
  const lowercasedInput = typeInput.toLowerCase();
7624
- result = existingTypes.filter(
7757
+ result = validExistingTypes.filter(
7625
7758
  (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
7626
7759
  );
7627
7760
  }
@@ -8469,12 +8602,13 @@ function NodeDetailsPanel({
8469
8602
  }
8470
8603
  }, [hasImages, useImageAsTexture]);
8471
8604
  useEffect15(() => {
8605
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
8472
8606
  if (typeInput.trim() === "") {
8473
- setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
8607
+ setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
8474
8608
  } else {
8475
8609
  const lowercasedInput = typeInput.toLowerCase();
8476
8610
  setFilteredTypes(
8477
- existingTypes.filter(
8611
+ validExistingTypes.filter(
8478
8612
  (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
8479
8613
  )
8480
8614
  );
@@ -8970,12 +9104,13 @@ function QuestDetailsPanel({
8970
9104
  }
8971
9105
  }, [node]);
8972
9106
  useEffect16(() => {
9107
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
8973
9108
  if (typeInput.trim() === "") {
8974
- setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
9109
+ setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
8975
9110
  } else {
8976
9111
  const lowercasedInput = typeInput.toLowerCase();
8977
9112
  setFilteredTypes(
8978
- existingTypes.filter(
9113
+ validExistingTypes.filter(
8979
9114
  (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
8980
9115
  )
8981
9116
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lv-x-software-house/x_view",
3
- "version": "1.2.5-dev.14",
3
+ "version": "1.2.5-dev.16",
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",