@almadar/ui 5.63.1 → 5.64.0

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.
@@ -43,6 +43,7 @@ var THREE3 = require('three');
43
43
  var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
44
44
  var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
45
45
  var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
46
+ var SkeletonUtils = require('three/examples/jsm/utils/SkeletonUtils');
46
47
  var fiber = require('@react-three/fiber');
47
48
  var drei = require('@react-three/drei');
48
49
  var patterns = require('@almadar/patterns');
@@ -41203,6 +41204,13 @@ var init_DocumentViewer = __esm({
41203
41204
  DocumentViewer.displayName = "DocumentViewer";
41204
41205
  }
41205
41206
  });
41207
+ function measureLabelWidth(text) {
41208
+ if (typeof document === "undefined") return text.length * 6;
41209
+ if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
41210
+ if (!labelMeasureCtx) return text.length * 6;
41211
+ labelMeasureCtx.font = "10px system-ui";
41212
+ return labelMeasureCtx.measureText(text).width;
41213
+ }
41206
41214
  function getGroupColor(group, groups) {
41207
41215
  if (!group) return GROUP_COLORS2[0];
41208
41216
  const idx = groups.indexOf(group);
@@ -41214,7 +41222,7 @@ function resolveColor2(color, el) {
41214
41222
  const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
41215
41223
  return resolved || (m[2]?.trim() ?? "#888888");
41216
41224
  }
41217
- var GROUP_COLORS2, GraphCanvas;
41225
+ var GROUP_COLORS2, labelMeasureCtx, GraphCanvas;
41218
41226
  var init_GraphCanvas = __esm({
41219
41227
  "components/core/molecules/GraphCanvas.tsx"() {
41220
41228
  "use client";
@@ -41234,6 +41242,7 @@ var init_GraphCanvas = __esm({
41234
41242
  "var(--color-info)",
41235
41243
  "var(--color-accent)"
41236
41244
  ];
41245
+ labelMeasureCtx = null;
41237
41246
  GraphCanvas = ({
41238
41247
  title,
41239
41248
  nodes: propNodes = [],
@@ -41414,22 +41423,36 @@ var init_GraphCanvas = __esm({
41414
41423
  node.x = Math.max(30, Math.min(w - 30, node.x));
41415
41424
  node.y = Math.max(30, Math.min(h - 30, node.y));
41416
41425
  }
41426
+ const LABEL_GAP = 12;
41427
+ const LABEL_H = 12;
41428
+ const pad = nodeSpacing / 2;
41417
41429
  for (let i = 0; i < nodes.length; i++) {
41418
41430
  for (let j = i + 1; j < nodes.length; j++) {
41419
41431
  const a = nodes[i];
41420
41432
  const b = nodes[j];
41421
- const dx = b.x - a.x;
41422
- const dy = b.y - a.y;
41423
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
41424
- const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
41425
- if (dist < minDist) {
41426
- const push2 = (minDist - dist) / 2;
41427
- const ux = dx / dist;
41428
- const uy = dy / dist;
41429
- a.x = Math.max(30, Math.min(w - 30, a.x - ux * push2));
41430
- a.y = Math.max(30, Math.min(h - 30, a.y - uy * push2));
41431
- b.x = Math.max(30, Math.min(w - 30, b.x + ux * push2));
41432
- b.y = Math.max(30, Math.min(h - 30, b.y + uy * push2));
41433
+ const ar = a.size || 8;
41434
+ const br = b.size || 8;
41435
+ if (showLabels) {
41436
+ if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
41437
+ if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
41438
+ }
41439
+ const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
41440
+ const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
41441
+ const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
41442
+ const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
41443
+ const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
41444
+ const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
41445
+ const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
41446
+ if (overlapX > 0 && overlapY > 0) {
41447
+ if (overlapX <= overlapY) {
41448
+ const push2 = overlapX / 2 * (b.x >= a.x ? 1 : -1);
41449
+ a.x = Math.max(30, Math.min(w - 30, a.x - push2));
41450
+ b.x = Math.max(30, Math.min(w - 30, b.x + push2));
41451
+ } else {
41452
+ const push2 = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
41453
+ a.y = Math.max(30, Math.min(h - 30, a.y - push2));
41454
+ b.y = Math.max(30, Math.min(h - 30, b.y + push2));
41455
+ }
41433
41456
  }
41434
41457
  }
41435
41458
  }
@@ -41446,7 +41469,7 @@ var init_GraphCanvas = __esm({
41446
41469
  return () => {
41447
41470
  cancelAnimationFrame(animRef.current);
41448
41471
  };
41449
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
41472
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels, logicalW, height]);
41450
41473
  React93.useEffect(() => {
41451
41474
  const canvas = canvasRef.current;
41452
41475
  if (!canvas) return;
@@ -45438,7 +45461,8 @@ function ModelLoader({
45438
45461
  const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
45439
45462
  const model = React93.useMemo(() => {
45440
45463
  if (!loadedModel) return null;
45441
- const cloned = loadedModel.clone();
45464
+ const cloned = SkeletonUtils.clone(loadedModel);
45465
+ cloned.updateMatrixWorld(true);
45442
45466
  cloned.traverse((child) => {
45443
45467
  if (child instanceof THREE3__namespace.Mesh) {
45444
45468
  child.castShadow = castShadow;
@@ -45453,7 +45477,8 @@ function ModelLoader({
45453
45477
  const size = new THREE3__namespace.Vector3();
45454
45478
  box.getSize(size);
45455
45479
  const maxDim = Math.max(size.x, size.y, size.z);
45456
- return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
45480
+ if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
45481
+ return 1 / maxDim;
45457
45482
  }, [model]);
45458
45483
  const scaleArray = React93.useMemo(() => {
45459
45484
  const base = typeof scale === "number" ? [scale, scale, scale] : scale;
package/dist/avl/index.js CHANGED
@@ -43,6 +43,7 @@ import * as THREE3 from 'three';
43
43
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
44
44
  import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
45
45
  import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
46
+ import { clone } from 'three/examples/jsm/utils/SkeletonUtils';
46
47
  import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
47
48
  import { Billboard, Grid as Grid$1, OrbitControls } from '@react-three/drei';
48
49
  import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1, isEntityAwarePattern } from '@almadar/patterns';
@@ -41156,6 +41157,13 @@ var init_DocumentViewer = __esm({
41156
41157
  DocumentViewer.displayName = "DocumentViewer";
41157
41158
  }
41158
41159
  });
41160
+ function measureLabelWidth(text) {
41161
+ if (typeof document === "undefined") return text.length * 6;
41162
+ if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
41163
+ if (!labelMeasureCtx) return text.length * 6;
41164
+ labelMeasureCtx.font = "10px system-ui";
41165
+ return labelMeasureCtx.measureText(text).width;
41166
+ }
41159
41167
  function getGroupColor(group, groups) {
41160
41168
  if (!group) return GROUP_COLORS2[0];
41161
41169
  const idx = groups.indexOf(group);
@@ -41167,7 +41175,7 @@ function resolveColor2(color, el) {
41167
41175
  const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
41168
41176
  return resolved || (m[2]?.trim() ?? "#888888");
41169
41177
  }
41170
- var GROUP_COLORS2, GraphCanvas;
41178
+ var GROUP_COLORS2, labelMeasureCtx, GraphCanvas;
41171
41179
  var init_GraphCanvas = __esm({
41172
41180
  "components/core/molecules/GraphCanvas.tsx"() {
41173
41181
  "use client";
@@ -41187,6 +41195,7 @@ var init_GraphCanvas = __esm({
41187
41195
  "var(--color-info)",
41188
41196
  "var(--color-accent)"
41189
41197
  ];
41198
+ labelMeasureCtx = null;
41190
41199
  GraphCanvas = ({
41191
41200
  title,
41192
41201
  nodes: propNodes = [],
@@ -41367,22 +41376,36 @@ var init_GraphCanvas = __esm({
41367
41376
  node.x = Math.max(30, Math.min(w - 30, node.x));
41368
41377
  node.y = Math.max(30, Math.min(h - 30, node.y));
41369
41378
  }
41379
+ const LABEL_GAP = 12;
41380
+ const LABEL_H = 12;
41381
+ const pad = nodeSpacing / 2;
41370
41382
  for (let i = 0; i < nodes.length; i++) {
41371
41383
  for (let j = i + 1; j < nodes.length; j++) {
41372
41384
  const a = nodes[i];
41373
41385
  const b = nodes[j];
41374
- const dx = b.x - a.x;
41375
- const dy = b.y - a.y;
41376
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
41377
- const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
41378
- if (dist < minDist) {
41379
- const push2 = (minDist - dist) / 2;
41380
- const ux = dx / dist;
41381
- const uy = dy / dist;
41382
- a.x = Math.max(30, Math.min(w - 30, a.x - ux * push2));
41383
- a.y = Math.max(30, Math.min(h - 30, a.y - uy * push2));
41384
- b.x = Math.max(30, Math.min(w - 30, b.x + ux * push2));
41385
- b.y = Math.max(30, Math.min(h - 30, b.y + uy * push2));
41386
+ const ar = a.size || 8;
41387
+ const br = b.size || 8;
41388
+ if (showLabels) {
41389
+ if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
41390
+ if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
41391
+ }
41392
+ const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
41393
+ const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
41394
+ const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
41395
+ const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
41396
+ const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
41397
+ const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
41398
+ const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
41399
+ if (overlapX > 0 && overlapY > 0) {
41400
+ if (overlapX <= overlapY) {
41401
+ const push2 = overlapX / 2 * (b.x >= a.x ? 1 : -1);
41402
+ a.x = Math.max(30, Math.min(w - 30, a.x - push2));
41403
+ b.x = Math.max(30, Math.min(w - 30, b.x + push2));
41404
+ } else {
41405
+ const push2 = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
41406
+ a.y = Math.max(30, Math.min(h - 30, a.y - push2));
41407
+ b.y = Math.max(30, Math.min(h - 30, b.y + push2));
41408
+ }
41386
41409
  }
41387
41410
  }
41388
41411
  }
@@ -41399,7 +41422,7 @@ var init_GraphCanvas = __esm({
41399
41422
  return () => {
41400
41423
  cancelAnimationFrame(animRef.current);
41401
41424
  };
41402
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
41425
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels, logicalW, height]);
41403
41426
  useEffect(() => {
41404
41427
  const canvas = canvasRef.current;
41405
41428
  if (!canvas) return;
@@ -45391,7 +45414,8 @@ function ModelLoader({
45391
45414
  const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
45392
45415
  const model = useMemo(() => {
45393
45416
  if (!loadedModel) return null;
45394
- const cloned = loadedModel.clone();
45417
+ const cloned = clone(loadedModel);
45418
+ cloned.updateMatrixWorld(true);
45395
45419
  cloned.traverse((child) => {
45396
45420
  if (child instanceof THREE3.Mesh) {
45397
45421
  child.castShadow = castShadow;
@@ -45406,7 +45430,8 @@ function ModelLoader({
45406
45430
  const size = new THREE3.Vector3();
45407
45431
  box.getSize(size);
45408
45432
  const maxDim = Math.max(size.x, size.y, size.z);
45409
- return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
45433
+ if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
45434
+ return 1 / maxDim;
45410
45435
  }, [model]);
45411
45436
  const scaleArray = useMemo(() => {
45412
45437
  const base = typeof scale === "number" ? [scale, scale, scale] : scale;
@@ -7,6 +7,7 @@ var jsxRuntime = require('react/jsx-runtime');
7
7
  var drei = require('@react-three/drei');
8
8
  var logger = require('@almadar/logger');
9
9
  var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
10
+ var SkeletonUtils = require('three/examples/jsm/utils/SkeletonUtils');
10
11
  var OrbitControls_js = require('three/examples/jsm/controls/OrbitControls.js');
11
12
  var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
12
13
  var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
@@ -392,7 +393,8 @@ function ModelLoader({
392
393
  const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
393
394
  const model = React11.useMemo(() => {
394
395
  if (!loadedModel) return null;
395
- const cloned = loadedModel.clone();
396
+ const cloned = SkeletonUtils.clone(loadedModel);
397
+ cloned.updateMatrixWorld(true);
396
398
  cloned.traverse((child) => {
397
399
  if (child instanceof THREE6__namespace.Mesh) {
398
400
  child.castShadow = castShadow;
@@ -407,7 +409,8 @@ function ModelLoader({
407
409
  const size = new THREE6__namespace.Vector3();
408
410
  box.getSize(size);
409
411
  const maxDim = Math.max(size.x, size.y, size.z);
410
- return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
412
+ if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
413
+ return 1 / maxDim;
411
414
  }, [model]);
412
415
  const scaleArray = React11.useMemo(() => {
413
416
  const base = typeof scale === "number" ? [scale, scale, scale] : scale;
@@ -6,6 +6,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
  import { OrbitControls, Billboard, Grid, Stars, Sparkles, Html, RoundedBox } from '@react-three/drei';
7
7
  import { createLogger } from '@almadar/logger';
8
8
  import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
9
+ import { clone } from 'three/examples/jsm/utils/SkeletonUtils';
9
10
  import { OrbitControls as OrbitControls$1 } from 'three/examples/jsm/controls/OrbitControls.js';
10
11
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
11
12
  import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
@@ -368,7 +369,8 @@ function ModelLoader({
368
369
  const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
369
370
  const model = useMemo(() => {
370
371
  if (!loadedModel) return null;
371
- const cloned = loadedModel.clone();
372
+ const cloned = clone(loadedModel);
373
+ cloned.updateMatrixWorld(true);
372
374
  cloned.traverse((child) => {
373
375
  if (child instanceof THREE6.Mesh) {
374
376
  child.castShadow = castShadow;
@@ -383,7 +385,8 @@ function ModelLoader({
383
385
  const size = new THREE6.Vector3();
384
386
  box.getSize(size);
385
387
  const maxDim = Math.max(size.x, size.y, size.z);
386
- return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
388
+ if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
389
+ return 1 / maxDim;
387
390
  }, [model]);
388
391
  const scaleArray = useMemo(() => {
389
392
  const base = typeof scale === "number" ? [scale, scale, scale] : scale;
@@ -45,6 +45,7 @@ var THREE3 = require('three');
45
45
  var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
46
46
  var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
47
47
  var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
48
+ var SkeletonUtils = require('three/examples/jsm/utils/SkeletonUtils');
48
49
  var fiber = require('@react-three/fiber');
49
50
  var drei = require('@react-three/drei');
50
51
  var patterns = require('@almadar/patterns');
@@ -39430,6 +39431,13 @@ var init_DocumentViewer = __esm({
39430
39431
  exports.DocumentViewer.displayName = "DocumentViewer";
39431
39432
  }
39432
39433
  });
39434
+ function measureLabelWidth(text) {
39435
+ if (typeof document === "undefined") return text.length * 6;
39436
+ if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
39437
+ if (!labelMeasureCtx) return text.length * 6;
39438
+ labelMeasureCtx.font = "10px system-ui";
39439
+ return labelMeasureCtx.measureText(text).width;
39440
+ }
39433
39441
  function getGroupColor(group, groups) {
39434
39442
  if (!group) return GROUP_COLORS2[0];
39435
39443
  const idx = groups.indexOf(group);
@@ -39441,7 +39449,7 @@ function resolveColor2(color, el) {
39441
39449
  const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
39442
39450
  return resolved || (m[2]?.trim() ?? "#888888");
39443
39451
  }
39444
- var GROUP_COLORS2; exports.GraphCanvas = void 0;
39452
+ var GROUP_COLORS2, labelMeasureCtx; exports.GraphCanvas = void 0;
39445
39453
  var init_GraphCanvas = __esm({
39446
39454
  "components/core/molecules/GraphCanvas.tsx"() {
39447
39455
  "use client";
@@ -39461,6 +39469,7 @@ var init_GraphCanvas = __esm({
39461
39469
  "var(--color-info)",
39462
39470
  "var(--color-accent)"
39463
39471
  ];
39472
+ labelMeasureCtx = null;
39464
39473
  exports.GraphCanvas = ({
39465
39474
  title,
39466
39475
  nodes: propNodes = [],
@@ -39641,22 +39650,36 @@ var init_GraphCanvas = __esm({
39641
39650
  node.x = Math.max(30, Math.min(w - 30, node.x));
39642
39651
  node.y = Math.max(30, Math.min(h - 30, node.y));
39643
39652
  }
39653
+ const LABEL_GAP = 12;
39654
+ const LABEL_H = 12;
39655
+ const pad = nodeSpacing / 2;
39644
39656
  for (let i = 0; i < nodes.length; i++) {
39645
39657
  for (let j = i + 1; j < nodes.length; j++) {
39646
39658
  const a = nodes[i];
39647
39659
  const b = nodes[j];
39648
- const dx = b.x - a.x;
39649
- const dy = b.y - a.y;
39650
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
39651
- const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
39652
- if (dist < minDist) {
39653
- const push = (minDist - dist) / 2;
39654
- const ux = dx / dist;
39655
- const uy = dy / dist;
39656
- a.x = Math.max(30, Math.min(w - 30, a.x - ux * push));
39657
- a.y = Math.max(30, Math.min(h - 30, a.y - uy * push));
39658
- b.x = Math.max(30, Math.min(w - 30, b.x + ux * push));
39659
- b.y = Math.max(30, Math.min(h - 30, b.y + uy * push));
39660
+ const ar = a.size || 8;
39661
+ const br = b.size || 8;
39662
+ if (showLabels) {
39663
+ if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
39664
+ if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
39665
+ }
39666
+ const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
39667
+ const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
39668
+ const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
39669
+ const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
39670
+ const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
39671
+ const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
39672
+ const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
39673
+ if (overlapX > 0 && overlapY > 0) {
39674
+ if (overlapX <= overlapY) {
39675
+ const push = overlapX / 2 * (b.x >= a.x ? 1 : -1);
39676
+ a.x = Math.max(30, Math.min(w - 30, a.x - push));
39677
+ b.x = Math.max(30, Math.min(w - 30, b.x + push));
39678
+ } else {
39679
+ const push = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
39680
+ a.y = Math.max(30, Math.min(h - 30, a.y - push));
39681
+ b.y = Math.max(30, Math.min(h - 30, b.y + push));
39682
+ }
39660
39683
  }
39661
39684
  }
39662
39685
  }
@@ -39673,7 +39696,7 @@ var init_GraphCanvas = __esm({
39673
39696
  return () => {
39674
39697
  cancelAnimationFrame(animRef.current);
39675
39698
  };
39676
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
39699
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels, logicalW, height]);
39677
39700
  React79.useEffect(() => {
39678
39701
  const canvas = canvasRef.current;
39679
39702
  if (!canvas) return;
@@ -43992,7 +44015,8 @@ function ModelLoader({
43992
44015
  const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
43993
44016
  const model = React79.useMemo(() => {
43994
44017
  if (!loadedModel) return null;
43995
- const cloned = loadedModel.clone();
44018
+ const cloned = SkeletonUtils.clone(loadedModel);
44019
+ cloned.updateMatrixWorld(true);
43996
44020
  cloned.traverse((child) => {
43997
44021
  if (child instanceof THREE3__namespace.Mesh) {
43998
44022
  child.castShadow = castShadow;
@@ -44007,7 +44031,8 @@ function ModelLoader({
44007
44031
  const size = new THREE3__namespace.Vector3();
44008
44032
  box.getSize(size);
44009
44033
  const maxDim = Math.max(size.x, size.y, size.z);
44010
- return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
44034
+ if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
44035
+ return 1 / maxDim;
44011
44036
  }, [model]);
44012
44037
  const scaleArray = React79.useMemo(() => {
44013
44038
  const base = typeof scale === "number" ? [scale, scale, scale] : scale;
@@ -46,6 +46,7 @@ import * as THREE3 from 'three';
46
46
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
47
47
  import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
48
48
  import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
49
+ import { clone } from 'three/examples/jsm/utils/SkeletonUtils';
49
50
  import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
50
51
  import { Billboard, Grid as Grid$1, OrbitControls } from '@react-three/drei';
51
52
  import { getPatternDefinition as getPatternDefinition$1, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
@@ -39384,6 +39385,13 @@ var init_DocumentViewer = __esm({
39384
39385
  DocumentViewer.displayName = "DocumentViewer";
39385
39386
  }
39386
39387
  });
39388
+ function measureLabelWidth(text) {
39389
+ if (typeof document === "undefined") return text.length * 6;
39390
+ if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
39391
+ if (!labelMeasureCtx) return text.length * 6;
39392
+ labelMeasureCtx.font = "10px system-ui";
39393
+ return labelMeasureCtx.measureText(text).width;
39394
+ }
39387
39395
  function getGroupColor(group, groups) {
39388
39396
  if (!group) return GROUP_COLORS2[0];
39389
39397
  const idx = groups.indexOf(group);
@@ -39395,7 +39403,7 @@ function resolveColor2(color, el) {
39395
39403
  const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
39396
39404
  return resolved || (m[2]?.trim() ?? "#888888");
39397
39405
  }
39398
- var GROUP_COLORS2, GraphCanvas;
39406
+ var GROUP_COLORS2, labelMeasureCtx, GraphCanvas;
39399
39407
  var init_GraphCanvas = __esm({
39400
39408
  "components/core/molecules/GraphCanvas.tsx"() {
39401
39409
  "use client";
@@ -39415,6 +39423,7 @@ var init_GraphCanvas = __esm({
39415
39423
  "var(--color-info)",
39416
39424
  "var(--color-accent)"
39417
39425
  ];
39426
+ labelMeasureCtx = null;
39418
39427
  GraphCanvas = ({
39419
39428
  title,
39420
39429
  nodes: propNodes = [],
@@ -39595,22 +39604,36 @@ var init_GraphCanvas = __esm({
39595
39604
  node.x = Math.max(30, Math.min(w - 30, node.x));
39596
39605
  node.y = Math.max(30, Math.min(h - 30, node.y));
39597
39606
  }
39607
+ const LABEL_GAP = 12;
39608
+ const LABEL_H = 12;
39609
+ const pad = nodeSpacing / 2;
39598
39610
  for (let i = 0; i < nodes.length; i++) {
39599
39611
  for (let j = i + 1; j < nodes.length; j++) {
39600
39612
  const a = nodes[i];
39601
39613
  const b = nodes[j];
39602
- const dx = b.x - a.x;
39603
- const dy = b.y - a.y;
39604
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
39605
- const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
39606
- if (dist < minDist) {
39607
- const push = (minDist - dist) / 2;
39608
- const ux = dx / dist;
39609
- const uy = dy / dist;
39610
- a.x = Math.max(30, Math.min(w - 30, a.x - ux * push));
39611
- a.y = Math.max(30, Math.min(h - 30, a.y - uy * push));
39612
- b.x = Math.max(30, Math.min(w - 30, b.x + ux * push));
39613
- b.y = Math.max(30, Math.min(h - 30, b.y + uy * push));
39614
+ const ar = a.size || 8;
39615
+ const br = b.size || 8;
39616
+ if (showLabels) {
39617
+ if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
39618
+ if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
39619
+ }
39620
+ const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
39621
+ const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
39622
+ const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
39623
+ const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
39624
+ const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
39625
+ const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
39626
+ const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
39627
+ if (overlapX > 0 && overlapY > 0) {
39628
+ if (overlapX <= overlapY) {
39629
+ const push = overlapX / 2 * (b.x >= a.x ? 1 : -1);
39630
+ a.x = Math.max(30, Math.min(w - 30, a.x - push));
39631
+ b.x = Math.max(30, Math.min(w - 30, b.x + push));
39632
+ } else {
39633
+ const push = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
39634
+ a.y = Math.max(30, Math.min(h - 30, a.y - push));
39635
+ b.y = Math.max(30, Math.min(h - 30, b.y + push));
39636
+ }
39614
39637
  }
39615
39638
  }
39616
39639
  }
@@ -39627,7 +39650,7 @@ var init_GraphCanvas = __esm({
39627
39650
  return () => {
39628
39651
  cancelAnimationFrame(animRef.current);
39629
39652
  };
39630
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
39653
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels, logicalW, height]);
39631
39654
  useEffect(() => {
39632
39655
  const canvas = canvasRef.current;
39633
39656
  if (!canvas) return;
@@ -43946,7 +43969,8 @@ function ModelLoader({
43946
43969
  const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
43947
43970
  const model = useMemo(() => {
43948
43971
  if (!loadedModel) return null;
43949
- const cloned = loadedModel.clone();
43972
+ const cloned = clone(loadedModel);
43973
+ cloned.updateMatrixWorld(true);
43950
43974
  cloned.traverse((child) => {
43951
43975
  if (child instanceof THREE3.Mesh) {
43952
43976
  child.castShadow = castShadow;
@@ -43961,7 +43985,8 @@ function ModelLoader({
43961
43985
  const size = new THREE3.Vector3();
43962
43986
  box.getSize(size);
43963
43987
  const maxDim = Math.max(size.x, size.y, size.z);
43964
- return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
43988
+ if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
43989
+ return 1 / maxDim;
43965
43990
  }, [model]);
43966
43991
  const scaleArray = useMemo(() => {
43967
43992
  const base = typeof scale === "number" ? [scale, scale, scale] : scale;
@@ -10,7 +10,7 @@
10
10
  * - Never listens to events
11
11
  */
12
12
  import React from 'react';
13
- import type { EntityWith } from '@almadar/core';
13
+ import type { EntityWith, AssetUrl } from '@almadar/core';
14
14
  import type { DisplayStateProps } from '../../core/organisms/types';
15
15
  /** A hero CTA: a plain `{ label, href }` object (FieldValue-compatible — a `type`,
16
16
  * not `interface`, so it's assignable to the entity-row `FieldValue` index sig). */
@@ -29,7 +29,7 @@ export interface HeroRow {
29
29
  secondaryAction?: HeroAction;
30
30
  installCommand?: string;
31
31
  image?: {
32
- src?: string;
32
+ src?: AssetUrl;
33
33
  alt?: string;
34
34
  };
35
35
  imagePosition?: 'below' | 'right' | 'background';
@@ -10,7 +10,7 @@
10
10
  * - Never listens to events
11
11
  */
12
12
  import React from 'react';
13
- import type { EntityWith } from '@almadar/core';
13
+ import type { EntityWith, AssetUrl } from '@almadar/core';
14
14
  import type { DisplayStateProps } from '../../core/organisms/types';
15
15
  /** The per-showcase entity fields this organism reads (FieldValue-compatible; `image`
16
16
  * is a plain `{ src, alt }` object, `accentColor` a string). */
@@ -18,7 +18,7 @@ export interface ShowcaseRow {
18
18
  title: string;
19
19
  description?: string;
20
20
  image?: {
21
- src?: string;
21
+ src?: AssetUrl;
22
22
  alt?: string;
23
23
  };
24
24
  href?: string;
@@ -9,7 +9,7 @@
9
9
  * - No events (display-only)
10
10
  */
11
11
  import React from 'react';
12
- import type { EntityWith } from '@almadar/core';
12
+ import type { EntityWith, AssetUrl } from '@almadar/core';
13
13
  import type { DisplayStateProps } from '../../core/organisms/types';
14
14
  /** The per-member entity fields this organism reads (FieldValue-compatible;
15
15
  * `avatar` is a string url/initials). */
@@ -18,7 +18,7 @@ export interface TeamMemberRow {
18
18
  nameAr?: string;
19
19
  role?: string;
20
20
  bio?: string;
21
- avatar?: string;
21
+ avatar?: AssetUrl;
22
22
  }
23
23
  export interface TeamOrganismProps extends DisplayStateProps {
24
24
  entity?: EntityWith<TeamMemberRow> | readonly EntityWith<TeamMemberRow>[];
@@ -45,6 +45,7 @@ var THREE3 = require('three');
45
45
  var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
46
46
  var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
47
47
  var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
48
+ var SkeletonUtils = require('three/examples/jsm/utils/SkeletonUtils');
48
49
  var fiber = require('@react-three/fiber');
49
50
  var drei = require('@react-three/drei');
50
51
  var patterns = require('@almadar/patterns');
@@ -39004,6 +39005,13 @@ var init_DocumentViewer = __esm({
39004
39005
  DocumentViewer.displayName = "DocumentViewer";
39005
39006
  }
39006
39007
  });
39008
+ function measureLabelWidth(text) {
39009
+ if (typeof document === "undefined") return text.length * 6;
39010
+ if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
39011
+ if (!labelMeasureCtx) return text.length * 6;
39012
+ labelMeasureCtx.font = "10px system-ui";
39013
+ return labelMeasureCtx.measureText(text).width;
39014
+ }
39007
39015
  function getGroupColor(group, groups) {
39008
39016
  if (!group) return GROUP_COLORS2[0];
39009
39017
  const idx = groups.indexOf(group);
@@ -39015,7 +39023,7 @@ function resolveColor2(color, el) {
39015
39023
  const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
39016
39024
  return resolved || (m[2]?.trim() ?? "#888888");
39017
39025
  }
39018
- var GROUP_COLORS2, GraphCanvas;
39026
+ var GROUP_COLORS2, labelMeasureCtx, GraphCanvas;
39019
39027
  var init_GraphCanvas = __esm({
39020
39028
  "components/core/molecules/GraphCanvas.tsx"() {
39021
39029
  "use client";
@@ -39035,6 +39043,7 @@ var init_GraphCanvas = __esm({
39035
39043
  "var(--color-info)",
39036
39044
  "var(--color-accent)"
39037
39045
  ];
39046
+ labelMeasureCtx = null;
39038
39047
  GraphCanvas = ({
39039
39048
  title,
39040
39049
  nodes: propNodes = [],
@@ -39215,22 +39224,36 @@ var init_GraphCanvas = __esm({
39215
39224
  node.x = Math.max(30, Math.min(w - 30, node.x));
39216
39225
  node.y = Math.max(30, Math.min(h - 30, node.y));
39217
39226
  }
39227
+ const LABEL_GAP = 12;
39228
+ const LABEL_H = 12;
39229
+ const pad = nodeSpacing / 2;
39218
39230
  for (let i = 0; i < nodes.length; i++) {
39219
39231
  for (let j = i + 1; j < nodes.length; j++) {
39220
39232
  const a = nodes[i];
39221
39233
  const b = nodes[j];
39222
- const dx = b.x - a.x;
39223
- const dy = b.y - a.y;
39224
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
39225
- const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
39226
- if (dist < minDist) {
39227
- const push = (minDist - dist) / 2;
39228
- const ux = dx / dist;
39229
- const uy = dy / dist;
39230
- a.x = Math.max(30, Math.min(w - 30, a.x - ux * push));
39231
- a.y = Math.max(30, Math.min(h - 30, a.y - uy * push));
39232
- b.x = Math.max(30, Math.min(w - 30, b.x + ux * push));
39233
- b.y = Math.max(30, Math.min(h - 30, b.y + uy * push));
39234
+ const ar = a.size || 8;
39235
+ const br = b.size || 8;
39236
+ if (showLabels) {
39237
+ if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
39238
+ if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
39239
+ }
39240
+ const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
39241
+ const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
39242
+ const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
39243
+ const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
39244
+ const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
39245
+ const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
39246
+ const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
39247
+ if (overlapX > 0 && overlapY > 0) {
39248
+ if (overlapX <= overlapY) {
39249
+ const push = overlapX / 2 * (b.x >= a.x ? 1 : -1);
39250
+ a.x = Math.max(30, Math.min(w - 30, a.x - push));
39251
+ b.x = Math.max(30, Math.min(w - 30, b.x + push));
39252
+ } else {
39253
+ const push = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
39254
+ a.y = Math.max(30, Math.min(h - 30, a.y - push));
39255
+ b.y = Math.max(30, Math.min(h - 30, b.y + push));
39256
+ }
39234
39257
  }
39235
39258
  }
39236
39259
  }
@@ -39247,7 +39270,7 @@ var init_GraphCanvas = __esm({
39247
39270
  return () => {
39248
39271
  cancelAnimationFrame(animRef.current);
39249
39272
  };
39250
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
39273
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels, logicalW, height]);
39251
39274
  React85.useEffect(() => {
39252
39275
  const canvas = canvasRef.current;
39253
39276
  if (!canvas) return;
@@ -43239,7 +43262,8 @@ function ModelLoader({
43239
43262
  const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
43240
43263
  const model = React85.useMemo(() => {
43241
43264
  if (!loadedModel) return null;
43242
- const cloned = loadedModel.clone();
43265
+ const cloned = SkeletonUtils.clone(loadedModel);
43266
+ cloned.updateMatrixWorld(true);
43243
43267
  cloned.traverse((child) => {
43244
43268
  if (child instanceof THREE3__namespace.Mesh) {
43245
43269
  child.castShadow = castShadow;
@@ -43254,7 +43278,8 @@ function ModelLoader({
43254
43278
  const size = new THREE3__namespace.Vector3();
43255
43279
  box.getSize(size);
43256
43280
  const maxDim = Math.max(size.x, size.y, size.z);
43257
- return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
43281
+ if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
43282
+ return 1 / maxDim;
43258
43283
  }, [model]);
43259
43284
  const scaleArray = React85.useMemo(() => {
43260
43285
  const base = typeof scale === "number" ? [scale, scale, scale] : scale;
@@ -45,6 +45,7 @@ import * as THREE3 from 'three';
45
45
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
46
46
  import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
47
47
  import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
48
+ import { clone } from 'three/examples/jsm/utils/SkeletonUtils';
48
49
  import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
49
50
  import { Billboard, Grid as Grid$1, OrbitControls } from '@react-three/drei';
50
51
  import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
@@ -38957,6 +38958,13 @@ var init_DocumentViewer = __esm({
38957
38958
  DocumentViewer.displayName = "DocumentViewer";
38958
38959
  }
38959
38960
  });
38961
+ function measureLabelWidth(text) {
38962
+ if (typeof document === "undefined") return text.length * 6;
38963
+ if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
38964
+ if (!labelMeasureCtx) return text.length * 6;
38965
+ labelMeasureCtx.font = "10px system-ui";
38966
+ return labelMeasureCtx.measureText(text).width;
38967
+ }
38960
38968
  function getGroupColor(group, groups) {
38961
38969
  if (!group) return GROUP_COLORS2[0];
38962
38970
  const idx = groups.indexOf(group);
@@ -38968,7 +38976,7 @@ function resolveColor2(color, el) {
38968
38976
  const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
38969
38977
  return resolved || (m[2]?.trim() ?? "#888888");
38970
38978
  }
38971
- var GROUP_COLORS2, GraphCanvas;
38979
+ var GROUP_COLORS2, labelMeasureCtx, GraphCanvas;
38972
38980
  var init_GraphCanvas = __esm({
38973
38981
  "components/core/molecules/GraphCanvas.tsx"() {
38974
38982
  "use client";
@@ -38988,6 +38996,7 @@ var init_GraphCanvas = __esm({
38988
38996
  "var(--color-info)",
38989
38997
  "var(--color-accent)"
38990
38998
  ];
38999
+ labelMeasureCtx = null;
38991
39000
  GraphCanvas = ({
38992
39001
  title,
38993
39002
  nodes: propNodes = [],
@@ -39168,22 +39177,36 @@ var init_GraphCanvas = __esm({
39168
39177
  node.x = Math.max(30, Math.min(w - 30, node.x));
39169
39178
  node.y = Math.max(30, Math.min(h - 30, node.y));
39170
39179
  }
39180
+ const LABEL_GAP = 12;
39181
+ const LABEL_H = 12;
39182
+ const pad = nodeSpacing / 2;
39171
39183
  for (let i = 0; i < nodes.length; i++) {
39172
39184
  for (let j = i + 1; j < nodes.length; j++) {
39173
39185
  const a = nodes[i];
39174
39186
  const b = nodes[j];
39175
- const dx = b.x - a.x;
39176
- const dy = b.y - a.y;
39177
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
39178
- const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
39179
- if (dist < minDist) {
39180
- const push = (minDist - dist) / 2;
39181
- const ux = dx / dist;
39182
- const uy = dy / dist;
39183
- a.x = Math.max(30, Math.min(w - 30, a.x - ux * push));
39184
- a.y = Math.max(30, Math.min(h - 30, a.y - uy * push));
39185
- b.x = Math.max(30, Math.min(w - 30, b.x + ux * push));
39186
- b.y = Math.max(30, Math.min(h - 30, b.y + uy * push));
39187
+ const ar = a.size || 8;
39188
+ const br = b.size || 8;
39189
+ if (showLabels) {
39190
+ if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
39191
+ if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
39192
+ }
39193
+ const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
39194
+ const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
39195
+ const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
39196
+ const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
39197
+ const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
39198
+ const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
39199
+ const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
39200
+ if (overlapX > 0 && overlapY > 0) {
39201
+ if (overlapX <= overlapY) {
39202
+ const push = overlapX / 2 * (b.x >= a.x ? 1 : -1);
39203
+ a.x = Math.max(30, Math.min(w - 30, a.x - push));
39204
+ b.x = Math.max(30, Math.min(w - 30, b.x + push));
39205
+ } else {
39206
+ const push = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
39207
+ a.y = Math.max(30, Math.min(h - 30, a.y - push));
39208
+ b.y = Math.max(30, Math.min(h - 30, b.y + push));
39209
+ }
39187
39210
  }
39188
39211
  }
39189
39212
  }
@@ -39200,7 +39223,7 @@ var init_GraphCanvas = __esm({
39200
39223
  return () => {
39201
39224
  cancelAnimationFrame(animRef.current);
39202
39225
  };
39203
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
39226
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels, logicalW, height]);
39204
39227
  useEffect(() => {
39205
39228
  const canvas = canvasRef.current;
39206
39229
  if (!canvas) return;
@@ -43192,7 +43215,8 @@ function ModelLoader({
43192
43215
  const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
43193
43216
  const model = useMemo(() => {
43194
43217
  if (!loadedModel) return null;
43195
- const cloned = loadedModel.clone();
43218
+ const cloned = clone(loadedModel);
43219
+ cloned.updateMatrixWorld(true);
43196
43220
  cloned.traverse((child) => {
43197
43221
  if (child instanceof THREE3.Mesh) {
43198
43222
  child.castShadow = castShadow;
@@ -43207,7 +43231,8 @@ function ModelLoader({
43207
43231
  const size = new THREE3.Vector3();
43208
43232
  box.getSize(size);
43209
43233
  const maxDim = Math.max(size.x, size.y, size.z);
43210
- return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
43234
+ if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
43235
+ return 1 / maxDim;
43211
43236
  }, [model]);
43212
43237
  const scaleArray = useMemo(() => {
43213
43238
  const base = typeof scale === "number" ? [scale, scale, scale] : scale;
@@ -45,6 +45,7 @@ var THREE3 = require('three');
45
45
  var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
46
46
  var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
47
47
  var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
48
+ var SkeletonUtils = require('three/examples/jsm/utils/SkeletonUtils');
48
49
  var fiber = require('@react-three/fiber');
49
50
  var drei = require('@react-three/drei');
50
51
  var patterns = require('@almadar/patterns');
@@ -38702,6 +38703,13 @@ var init_DocumentViewer = __esm({
38702
38703
  DocumentViewer.displayName = "DocumentViewer";
38703
38704
  }
38704
38705
  });
38706
+ function measureLabelWidth(text) {
38707
+ if (typeof document === "undefined") return text.length * 6;
38708
+ if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
38709
+ if (!labelMeasureCtx) return text.length * 6;
38710
+ labelMeasureCtx.font = "10px system-ui";
38711
+ return labelMeasureCtx.measureText(text).width;
38712
+ }
38705
38713
  function getGroupColor(group, groups) {
38706
38714
  if (!group) return GROUP_COLORS2[0];
38707
38715
  const idx = groups.indexOf(group);
@@ -38713,7 +38721,7 @@ function resolveColor2(color, el) {
38713
38721
  const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
38714
38722
  return resolved || (m[2]?.trim() ?? "#888888");
38715
38723
  }
38716
- var GROUP_COLORS2, GraphCanvas;
38724
+ var GROUP_COLORS2, labelMeasureCtx, GraphCanvas;
38717
38725
  var init_GraphCanvas = __esm({
38718
38726
  "components/core/molecules/GraphCanvas.tsx"() {
38719
38727
  "use client";
@@ -38733,6 +38741,7 @@ var init_GraphCanvas = __esm({
38733
38741
  "var(--color-info)",
38734
38742
  "var(--color-accent)"
38735
38743
  ];
38744
+ labelMeasureCtx = null;
38736
38745
  GraphCanvas = ({
38737
38746
  title,
38738
38747
  nodes: propNodes = [],
@@ -38913,22 +38922,36 @@ var init_GraphCanvas = __esm({
38913
38922
  node.x = Math.max(30, Math.min(w - 30, node.x));
38914
38923
  node.y = Math.max(30, Math.min(h - 30, node.y));
38915
38924
  }
38925
+ const LABEL_GAP = 12;
38926
+ const LABEL_H = 12;
38927
+ const pad = nodeSpacing / 2;
38916
38928
  for (let i = 0; i < nodes.length; i++) {
38917
38929
  for (let j = i + 1; j < nodes.length; j++) {
38918
38930
  const a = nodes[i];
38919
38931
  const b = nodes[j];
38920
- const dx = b.x - a.x;
38921
- const dy = b.y - a.y;
38922
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
38923
- const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
38924
- if (dist < minDist) {
38925
- const push2 = (minDist - dist) / 2;
38926
- const ux = dx / dist;
38927
- const uy = dy / dist;
38928
- a.x = Math.max(30, Math.min(w - 30, a.x - ux * push2));
38929
- a.y = Math.max(30, Math.min(h - 30, a.y - uy * push2));
38930
- b.x = Math.max(30, Math.min(w - 30, b.x + ux * push2));
38931
- b.y = Math.max(30, Math.min(h - 30, b.y + uy * push2));
38932
+ const ar = a.size || 8;
38933
+ const br = b.size || 8;
38934
+ if (showLabels) {
38935
+ if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
38936
+ if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
38937
+ }
38938
+ const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
38939
+ const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
38940
+ const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
38941
+ const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
38942
+ const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
38943
+ const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
38944
+ const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
38945
+ if (overlapX > 0 && overlapY > 0) {
38946
+ if (overlapX <= overlapY) {
38947
+ const push2 = overlapX / 2 * (b.x >= a.x ? 1 : -1);
38948
+ a.x = Math.max(30, Math.min(w - 30, a.x - push2));
38949
+ b.x = Math.max(30, Math.min(w - 30, b.x + push2));
38950
+ } else {
38951
+ const push2 = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
38952
+ a.y = Math.max(30, Math.min(h - 30, a.y - push2));
38953
+ b.y = Math.max(30, Math.min(h - 30, b.y + push2));
38954
+ }
38932
38955
  }
38933
38956
  }
38934
38957
  }
@@ -38945,7 +38968,7 @@ var init_GraphCanvas = __esm({
38945
38968
  return () => {
38946
38969
  cancelAnimationFrame(animRef.current);
38947
38970
  };
38948
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
38971
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels, logicalW, height]);
38949
38972
  React84.useEffect(() => {
38950
38973
  const canvas = canvasRef.current;
38951
38974
  if (!canvas) return;
@@ -42937,7 +42960,8 @@ function ModelLoader({
42937
42960
  const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
42938
42961
  const model = React84.useMemo(() => {
42939
42962
  if (!loadedModel) return null;
42940
- const cloned = loadedModel.clone();
42963
+ const cloned = SkeletonUtils.clone(loadedModel);
42964
+ cloned.updateMatrixWorld(true);
42941
42965
  cloned.traverse((child) => {
42942
42966
  if (child instanceof THREE3__namespace.Mesh) {
42943
42967
  child.castShadow = castShadow;
@@ -42952,7 +42976,8 @@ function ModelLoader({
42952
42976
  const size = new THREE3__namespace.Vector3();
42953
42977
  box.getSize(size);
42954
42978
  const maxDim = Math.max(size.x, size.y, size.z);
42955
- return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
42979
+ if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
42980
+ return 1 / maxDim;
42956
42981
  }, [model]);
42957
42982
  const scaleArray = React84.useMemo(() => {
42958
42983
  const base = typeof scale === "number" ? [scale, scale, scale] : scale;
@@ -45,6 +45,7 @@ import * as THREE3 from 'three';
45
45
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
46
46
  import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
47
47
  import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
48
+ import { clone } from 'three/examples/jsm/utils/SkeletonUtils';
48
49
  import { Canvas, useLoader, useFrame, useThree } from '@react-three/fiber';
49
50
  import { Billboard, Grid as Grid$1, OrbitControls } from '@react-three/drei';
50
51
  import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
@@ -38655,6 +38656,13 @@ var init_DocumentViewer = __esm({
38655
38656
  DocumentViewer.displayName = "DocumentViewer";
38656
38657
  }
38657
38658
  });
38659
+ function measureLabelWidth(text) {
38660
+ if (typeof document === "undefined") return text.length * 6;
38661
+ if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
38662
+ if (!labelMeasureCtx) return text.length * 6;
38663
+ labelMeasureCtx.font = "10px system-ui";
38664
+ return labelMeasureCtx.measureText(text).width;
38665
+ }
38658
38666
  function getGroupColor(group, groups) {
38659
38667
  if (!group) return GROUP_COLORS2[0];
38660
38668
  const idx = groups.indexOf(group);
@@ -38666,7 +38674,7 @@ function resolveColor2(color, el) {
38666
38674
  const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
38667
38675
  return resolved || (m[2]?.trim() ?? "#888888");
38668
38676
  }
38669
- var GROUP_COLORS2, GraphCanvas;
38677
+ var GROUP_COLORS2, labelMeasureCtx, GraphCanvas;
38670
38678
  var init_GraphCanvas = __esm({
38671
38679
  "components/core/molecules/GraphCanvas.tsx"() {
38672
38680
  "use client";
@@ -38686,6 +38694,7 @@ var init_GraphCanvas = __esm({
38686
38694
  "var(--color-info)",
38687
38695
  "var(--color-accent)"
38688
38696
  ];
38697
+ labelMeasureCtx = null;
38689
38698
  GraphCanvas = ({
38690
38699
  title,
38691
38700
  nodes: propNodes = [],
@@ -38866,22 +38875,36 @@ var init_GraphCanvas = __esm({
38866
38875
  node.x = Math.max(30, Math.min(w - 30, node.x));
38867
38876
  node.y = Math.max(30, Math.min(h - 30, node.y));
38868
38877
  }
38878
+ const LABEL_GAP = 12;
38879
+ const LABEL_H = 12;
38880
+ const pad = nodeSpacing / 2;
38869
38881
  for (let i = 0; i < nodes.length; i++) {
38870
38882
  for (let j = i + 1; j < nodes.length; j++) {
38871
38883
  const a = nodes[i];
38872
38884
  const b = nodes[j];
38873
- const dx = b.x - a.x;
38874
- const dy = b.y - a.y;
38875
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
38876
- const minDist = (a.size || 8) + (b.size || 8) + nodeSpacing;
38877
- if (dist < minDist) {
38878
- const push2 = (minDist - dist) / 2;
38879
- const ux = dx / dist;
38880
- const uy = dy / dist;
38881
- a.x = Math.max(30, Math.min(w - 30, a.x - ux * push2));
38882
- a.y = Math.max(30, Math.min(h - 30, a.y - uy * push2));
38883
- b.x = Math.max(30, Math.min(w - 30, b.x + ux * push2));
38884
- b.y = Math.max(30, Math.min(h - 30, b.y + uy * push2));
38885
+ const ar = a.size || 8;
38886
+ const br = b.size || 8;
38887
+ if (showLabels) {
38888
+ if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
38889
+ if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
38890
+ }
38891
+ const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
38892
+ const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
38893
+ const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
38894
+ const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
38895
+ const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
38896
+ const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
38897
+ const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
38898
+ if (overlapX > 0 && overlapY > 0) {
38899
+ if (overlapX <= overlapY) {
38900
+ const push2 = overlapX / 2 * (b.x >= a.x ? 1 : -1);
38901
+ a.x = Math.max(30, Math.min(w - 30, a.x - push2));
38902
+ b.x = Math.max(30, Math.min(w - 30, b.x + push2));
38903
+ } else {
38904
+ const push2 = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
38905
+ a.y = Math.max(30, Math.min(h - 30, a.y - push2));
38906
+ b.y = Math.max(30, Math.min(h - 30, b.y + push2));
38907
+ }
38885
38908
  }
38886
38909
  }
38887
38910
  }
@@ -38898,7 +38921,7 @@ var init_GraphCanvas = __esm({
38898
38921
  return () => {
38899
38922
  cancelAnimationFrame(animRef.current);
38900
38923
  };
38901
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, logicalW, height]);
38924
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels, logicalW, height]);
38902
38925
  useEffect(() => {
38903
38926
  const canvas = canvasRef.current;
38904
38927
  if (!canvas) return;
@@ -42890,7 +42913,8 @@ function ModelLoader({
42890
42913
  const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
42891
42914
  const model = useMemo(() => {
42892
42915
  if (!loadedModel) return null;
42893
- const cloned = loadedModel.clone();
42916
+ const cloned = clone(loadedModel);
42917
+ cloned.updateMatrixWorld(true);
42894
42918
  cloned.traverse((child) => {
42895
42919
  if (child instanceof THREE3.Mesh) {
42896
42920
  child.castShadow = castShadow;
@@ -42905,7 +42929,8 @@ function ModelLoader({
42905
42929
  const size = new THREE3.Vector3();
42906
42930
  box.getSize(size);
42907
42931
  const maxDim = Math.max(size.x, size.y, size.z);
42908
- return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
42932
+ if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
42933
+ return 1 / maxDim;
42909
42934
  }, [model]);
42910
42935
  const scaleArray = useMemo(() => {
42911
42936
  const base = typeof scale === "number" ? [scale, scale, scale] : scale;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "5.63.1",
3
+ "version": "5.64.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [