@almadar/ui 5.119.0 → 5.120.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.
@@ -35530,8 +35530,11 @@ var init_GraphCanvas = __esm({
35530
35530
  React83.useEffect(() => {
35531
35531
  const canvas = canvasRef.current;
35532
35532
  if (!canvas || propNodes.length === 0) return;
35533
- const w = logicalW;
35534
- const h = height;
35533
+ const viewW = logicalW;
35534
+ const viewH = height;
35535
+ const layoutScale = Math.max(1, Math.min(3, Math.sqrt(propNodes.length / 12)));
35536
+ const w = viewW * layoutScale;
35537
+ const h = viewH * layoutScale;
35535
35538
  const labelFont = resolveColor3("var(--font-family)", canvas) || "system-ui";
35536
35539
  const prevPos = /* @__PURE__ */ new Map();
35537
35540
  for (const pn of nodesRef.current) if (pn.x != null && pn.y != null) prevPos.set(pn.id, { x: pn.x, y: pn.y });
@@ -35571,93 +35574,122 @@ var init_GraphCanvas = __esm({
35571
35574
  const COLLIDE_PASSES = 6;
35572
35575
  const LABEL_GAP = 12;
35573
35576
  const LABEL_H = 16;
35574
- const tick = (iter) => {
35577
+ const SIMILARITY_NEIGHBORS = 5;
35578
+ const SIMILARITY_MIN_WEIGHT = 0.25;
35579
+ const drawnPairs = /* @__PURE__ */ new Set();
35580
+ for (const edge of propEdges) drawnPairs.add(edgeKeyOf(edge.source, edge.target));
35581
+ const similarityNeighbors = /* @__PURE__ */ new Map();
35582
+ {
35583
+ const bySource = /* @__PURE__ */ new Map();
35584
+ for (const pair of propSimilarity) {
35585
+ if (pair.weight < SIMILARITY_MIN_WEIGHT) continue;
35586
+ if (drawnPairs.has(edgeKeyOf(pair.source, pair.target))) continue;
35587
+ const arr = bySource.get(pair.source) ?? [];
35588
+ arr.push(pair);
35589
+ bySource.set(pair.source, arr);
35590
+ }
35591
+ for (const [id, arr] of bySource) {
35592
+ arr.sort((a, b) => b.weight - a.weight);
35593
+ const keep = new Set(arr.slice(0, SIMILARITY_NEIGHBORS).map((p) => p.target));
35594
+ similarityNeighbors.set(id, keep);
35595
+ }
35596
+ }
35597
+ const activeSimilarity = propSimilarity.filter((pair) => {
35598
+ if (drawnPairs.has(edgeKeyOf(pair.source, pair.target))) return false;
35599
+ const a = similarityNeighbors.get(pair.source);
35600
+ const b = similarityNeighbors.get(pair.target);
35601
+ return (a?.has(pair.target) ?? false) || (b?.has(pair.source) ?? false);
35602
+ });
35603
+ const tick = (iter, options) => {
35604
+ const skipForces = options?.skipForces ?? false;
35575
35605
  const nodes = nodesRef.current;
35576
35606
  const centerX = w / 2;
35577
35607
  const centerY = h / 2;
35578
- const temp = Math.max(COOL, 1 - iter / maxIterations);
35608
+ const temp = skipForces ? 0 : Math.max(COOL, 1 - iter / maxIterations);
35579
35609
  for (const node of nodes) {
35580
35610
  node.fx = 0;
35581
35611
  node.fy = 0;
35582
35612
  }
35583
- for (let i = 0; i < nodes.length; i++) {
35584
- for (let j = i + 1; j < nodes.length; j++) {
35585
- const dx = nodes[j].x - nodes[i].x;
35586
- const dy = nodes[j].y - nodes[i].y;
35613
+ if (!skipForces) {
35614
+ for (let i = 0; i < nodes.length; i++) {
35615
+ for (let j = i + 1; j < nodes.length; j++) {
35616
+ const dx = nodes[j].x - nodes[i].x;
35617
+ const dy = nodes[j].y - nodes[i].y;
35618
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
35619
+ const force = repulsion / (dist * dist) * temp;
35620
+ const fx = dx / dist * force;
35621
+ const fy = dy / dist * force;
35622
+ nodes[i].fx -= fx;
35623
+ nodes[i].fy -= fy;
35624
+ nodes[j].fx += fx;
35625
+ nodes[j].fy += fy;
35626
+ }
35627
+ }
35628
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
35629
+ const spring = (a, b, rest, k) => {
35630
+ const dx = b.x - a.x;
35631
+ const dy = b.y - a.y;
35587
35632
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
35588
- const force = repulsion / (dist * dist) * temp;
35633
+ const force = (dist - rest) * k * temp;
35589
35634
  const fx = dx / dist * force;
35590
35635
  const fy = dy / dist * force;
35591
- nodes[i].fx -= fx;
35592
- nodes[i].fy -= fy;
35593
- nodes[j].fx += fx;
35594
- nodes[j].fy += fy;
35636
+ a.fx += fx;
35637
+ a.fy += fy;
35638
+ b.fx -= fx;
35639
+ b.fy -= fy;
35640
+ };
35641
+ for (const edge of propEdges) {
35642
+ const source = nodeById.get(edge.source);
35643
+ const target = nodeById.get(edge.target);
35644
+ if (!source || !target) continue;
35645
+ drawnPairs.add(edgeKeyOf(edge.source, edge.target));
35646
+ const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
35647
+ spring(source, target, linkDistance * (0.35 + (1 - w2) * 0.3), 0.14);
35595
35648
  }
35596
- }
35597
- const nodeById = new Map(nodes.map((n) => [n.id, n]));
35598
- const spring = (a, b, rest, k) => {
35599
- const dx = b.x - a.x;
35600
- const dy = b.y - a.y;
35601
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
35602
- const force = (dist - rest) * k * temp;
35603
- const fx = dx / dist * force;
35604
- const fy = dy / dist * force;
35605
- a.fx += fx;
35606
- a.fy += fy;
35607
- b.fx -= fx;
35608
- b.fy -= fy;
35609
- };
35610
- const drawnPairs = /* @__PURE__ */ new Set();
35611
- for (const edge of propEdges) {
35612
- const source = nodeById.get(edge.source);
35613
- const target = nodeById.get(edge.target);
35614
- if (!source || !target) continue;
35615
- drawnPairs.add(edgeKeyOf(edge.source, edge.target));
35616
- const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
35617
- spring(source, target, linkDistance * (0.35 + (1 - w2) * 0.3), 0.14);
35618
- }
35619
- for (const pair of propSimilarity) {
35620
- if (drawnPairs.has(edgeKeyOf(pair.source, pair.target))) continue;
35621
- const source = nodeById.get(pair.source);
35622
- const target = nodeById.get(pair.target);
35623
- if (!source || !target) continue;
35624
- const w2 = Math.min(1, Math.max(0, pair.weight));
35625
- spring(source, target, linkDistance * (1.35 - 0.55 * w2), 0.015);
35626
- }
35627
- const centroids = /* @__PURE__ */ new Map();
35628
- for (const node of nodes) {
35629
- const g = node.group ?? "__none__";
35630
- let c = centroids.get(g);
35631
- if (!c) {
35632
- c = { x: 0, y: 0, n: 0 };
35633
- centroids.set(g, c);
35649
+ for (const pair of activeSimilarity) {
35650
+ const source = nodeById.get(pair.source);
35651
+ const target = nodeById.get(pair.target);
35652
+ if (!source || !target) continue;
35653
+ const w2 = Math.min(1, Math.max(0, pair.weight));
35654
+ spring(source, target, linkDistance * (1.35 - 0.55 * w2), 0.015);
35634
35655
  }
35635
- c.x += node.x;
35636
- c.y += node.y;
35637
- c.n += 1;
35638
- }
35639
- const multiCluster = centroids.size > 1;
35640
- for (const node of nodes) {
35641
- if (multiCluster) {
35642
- const c = centroids.get(node.group ?? "__none__");
35643
- if (c && c.n > 1) {
35644
- node.fx += (c.x / c.n - node.x) * 0.04 * temp;
35645
- node.fy += (c.y / c.n - node.y) * 0.04 * temp;
35656
+ const centroids = /* @__PURE__ */ new Map();
35657
+ for (const node of nodes) {
35658
+ const g = node.group ?? "__none__";
35659
+ let c = centroids.get(g);
35660
+ if (!c) {
35661
+ c = { x: 0, y: 0, n: 0 };
35662
+ centroids.set(g, c);
35663
+ }
35664
+ c.x += node.x;
35665
+ c.y += node.y;
35666
+ c.n += 1;
35667
+ }
35668
+ const multiCluster = centroids.size > 1;
35669
+ for (const node of nodes) {
35670
+ if (multiCluster) {
35671
+ const c = centroids.get(node.group ?? "__none__");
35672
+ if (c && c.n > 1) {
35673
+ node.fx += (c.x / c.n - node.x) * 0.06 * temp;
35674
+ node.fy += (c.y / c.n - node.y) * 0.06 * temp;
35675
+ } else {
35676
+ node.fx += (centerX - node.x) * 0.01 * temp;
35677
+ node.fy += (centerY - node.y) * 0.01 * temp;
35678
+ }
35646
35679
  } else {
35647
- node.fx += (centerX - node.x) * 0.01 * temp;
35648
- node.fy += (centerY - node.y) * 0.01 * temp;
35680
+ node.fx += (centerX - node.x) * 8e-3 * temp;
35681
+ node.fy += (centerY - node.y) * 8e-3 * temp;
35649
35682
  }
35650
- } else {
35651
- node.fx += (centerX - node.x) * 8e-3 * temp;
35652
- node.fy += (centerY - node.y) * 8e-3 * temp;
35683
+ }
35684
+ const damping = 0.9;
35685
+ for (const node of nodes) {
35686
+ node.vx = (node.vx + node.fx) * damping;
35687
+ node.vy = (node.vy + node.fy) * damping;
35688
+ node.x += node.vx;
35689
+ node.y += node.vy;
35653
35690
  }
35654
35691
  }
35655
- const damping = 0.9;
35656
35692
  for (const node of nodes) {
35657
- node.vx = (node.vx + node.fx) * damping;
35658
- node.vy = (node.vy + node.fy) * damping;
35659
- node.x += node.vx;
35660
- node.y += node.vy;
35661
35693
  node.x = Math.max(30, Math.min(w - 30, node.x));
35662
35694
  node.y = Math.max(30, Math.min(h - 30, node.y));
35663
35695
  }
@@ -35708,6 +35740,22 @@ var init_GraphCanvas = __esm({
35708
35740
  };
35709
35741
  if (fullRelayout) {
35710
35742
  for (let i = 0; i < maxIterations; i++) tick(i);
35743
+ for (let i = 0; i < 4; i++) tick(maxIterations, { skipForces: true });
35744
+ const fitPad = 40;
35745
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
35746
+ for (const node of nodesRef.current) {
35747
+ const r = node.size || 8;
35748
+ const lw = showLabels ? node.labelW ?? (node.labelW = node.label ? measureLabelWidth(node.label, labelFont) : 0) : 0;
35749
+ minX = Math.min(minX, node.x - r, node.x - lw / 2);
35750
+ minY = Math.min(minY, node.y - r);
35751
+ maxX = Math.max(maxX, node.x + r, node.x + lw / 2);
35752
+ maxY = Math.max(maxY, node.y + r + LABEL_GAP + LABEL_H);
35753
+ }
35754
+ const bboxW = Math.max(1, maxX - minX + fitPad * 2);
35755
+ const bboxH = Math.max(1, maxY - minY + fitPad * 2);
35756
+ const nextZoom = Math.min(1, viewW / bboxW, viewH / bboxH);
35757
+ setZoom(nextZoom);
35758
+ setOffset({ x: fitPad - minX * nextZoom, y: fitPad - minY * nextZoom });
35711
35759
  laidOutRef.current = true;
35712
35760
  } else {
35713
35761
  const centroids = /* @__PURE__ */ new Map();
@@ -35485,8 +35485,11 @@ var init_GraphCanvas = __esm({
35485
35485
  useEffect(() => {
35486
35486
  const canvas = canvasRef.current;
35487
35487
  if (!canvas || propNodes.length === 0) return;
35488
- const w = logicalW;
35489
- const h = height;
35488
+ const viewW = logicalW;
35489
+ const viewH = height;
35490
+ const layoutScale = Math.max(1, Math.min(3, Math.sqrt(propNodes.length / 12)));
35491
+ const w = viewW * layoutScale;
35492
+ const h = viewH * layoutScale;
35490
35493
  const labelFont = resolveColor3("var(--font-family)", canvas) || "system-ui";
35491
35494
  const prevPos = /* @__PURE__ */ new Map();
35492
35495
  for (const pn of nodesRef.current) if (pn.x != null && pn.y != null) prevPos.set(pn.id, { x: pn.x, y: pn.y });
@@ -35526,93 +35529,122 @@ var init_GraphCanvas = __esm({
35526
35529
  const COLLIDE_PASSES = 6;
35527
35530
  const LABEL_GAP = 12;
35528
35531
  const LABEL_H = 16;
35529
- const tick = (iter) => {
35532
+ const SIMILARITY_NEIGHBORS = 5;
35533
+ const SIMILARITY_MIN_WEIGHT = 0.25;
35534
+ const drawnPairs = /* @__PURE__ */ new Set();
35535
+ for (const edge of propEdges) drawnPairs.add(edgeKeyOf(edge.source, edge.target));
35536
+ const similarityNeighbors = /* @__PURE__ */ new Map();
35537
+ {
35538
+ const bySource = /* @__PURE__ */ new Map();
35539
+ for (const pair of propSimilarity) {
35540
+ if (pair.weight < SIMILARITY_MIN_WEIGHT) continue;
35541
+ if (drawnPairs.has(edgeKeyOf(pair.source, pair.target))) continue;
35542
+ const arr = bySource.get(pair.source) ?? [];
35543
+ arr.push(pair);
35544
+ bySource.set(pair.source, arr);
35545
+ }
35546
+ for (const [id, arr] of bySource) {
35547
+ arr.sort((a, b) => b.weight - a.weight);
35548
+ const keep = new Set(arr.slice(0, SIMILARITY_NEIGHBORS).map((p) => p.target));
35549
+ similarityNeighbors.set(id, keep);
35550
+ }
35551
+ }
35552
+ const activeSimilarity = propSimilarity.filter((pair) => {
35553
+ if (drawnPairs.has(edgeKeyOf(pair.source, pair.target))) return false;
35554
+ const a = similarityNeighbors.get(pair.source);
35555
+ const b = similarityNeighbors.get(pair.target);
35556
+ return (a?.has(pair.target) ?? false) || (b?.has(pair.source) ?? false);
35557
+ });
35558
+ const tick = (iter, options) => {
35559
+ const skipForces = options?.skipForces ?? false;
35530
35560
  const nodes = nodesRef.current;
35531
35561
  const centerX = w / 2;
35532
35562
  const centerY = h / 2;
35533
- const temp = Math.max(COOL, 1 - iter / maxIterations);
35563
+ const temp = skipForces ? 0 : Math.max(COOL, 1 - iter / maxIterations);
35534
35564
  for (const node of nodes) {
35535
35565
  node.fx = 0;
35536
35566
  node.fy = 0;
35537
35567
  }
35538
- for (let i = 0; i < nodes.length; i++) {
35539
- for (let j = i + 1; j < nodes.length; j++) {
35540
- const dx = nodes[j].x - nodes[i].x;
35541
- const dy = nodes[j].y - nodes[i].y;
35568
+ if (!skipForces) {
35569
+ for (let i = 0; i < nodes.length; i++) {
35570
+ for (let j = i + 1; j < nodes.length; j++) {
35571
+ const dx = nodes[j].x - nodes[i].x;
35572
+ const dy = nodes[j].y - nodes[i].y;
35573
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
35574
+ const force = repulsion / (dist * dist) * temp;
35575
+ const fx = dx / dist * force;
35576
+ const fy = dy / dist * force;
35577
+ nodes[i].fx -= fx;
35578
+ nodes[i].fy -= fy;
35579
+ nodes[j].fx += fx;
35580
+ nodes[j].fy += fy;
35581
+ }
35582
+ }
35583
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
35584
+ const spring = (a, b, rest, k) => {
35585
+ const dx = b.x - a.x;
35586
+ const dy = b.y - a.y;
35542
35587
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
35543
- const force = repulsion / (dist * dist) * temp;
35588
+ const force = (dist - rest) * k * temp;
35544
35589
  const fx = dx / dist * force;
35545
35590
  const fy = dy / dist * force;
35546
- nodes[i].fx -= fx;
35547
- nodes[i].fy -= fy;
35548
- nodes[j].fx += fx;
35549
- nodes[j].fy += fy;
35591
+ a.fx += fx;
35592
+ a.fy += fy;
35593
+ b.fx -= fx;
35594
+ b.fy -= fy;
35595
+ };
35596
+ for (const edge of propEdges) {
35597
+ const source = nodeById.get(edge.source);
35598
+ const target = nodeById.get(edge.target);
35599
+ if (!source || !target) continue;
35600
+ drawnPairs.add(edgeKeyOf(edge.source, edge.target));
35601
+ const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
35602
+ spring(source, target, linkDistance * (0.35 + (1 - w2) * 0.3), 0.14);
35550
35603
  }
35551
- }
35552
- const nodeById = new Map(nodes.map((n) => [n.id, n]));
35553
- const spring = (a, b, rest, k) => {
35554
- const dx = b.x - a.x;
35555
- const dy = b.y - a.y;
35556
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
35557
- const force = (dist - rest) * k * temp;
35558
- const fx = dx / dist * force;
35559
- const fy = dy / dist * force;
35560
- a.fx += fx;
35561
- a.fy += fy;
35562
- b.fx -= fx;
35563
- b.fy -= fy;
35564
- };
35565
- const drawnPairs = /* @__PURE__ */ new Set();
35566
- for (const edge of propEdges) {
35567
- const source = nodeById.get(edge.source);
35568
- const target = nodeById.get(edge.target);
35569
- if (!source || !target) continue;
35570
- drawnPairs.add(edgeKeyOf(edge.source, edge.target));
35571
- const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
35572
- spring(source, target, linkDistance * (0.35 + (1 - w2) * 0.3), 0.14);
35573
- }
35574
- for (const pair of propSimilarity) {
35575
- if (drawnPairs.has(edgeKeyOf(pair.source, pair.target))) continue;
35576
- const source = nodeById.get(pair.source);
35577
- const target = nodeById.get(pair.target);
35578
- if (!source || !target) continue;
35579
- const w2 = Math.min(1, Math.max(0, pair.weight));
35580
- spring(source, target, linkDistance * (1.35 - 0.55 * w2), 0.015);
35581
- }
35582
- const centroids = /* @__PURE__ */ new Map();
35583
- for (const node of nodes) {
35584
- const g = node.group ?? "__none__";
35585
- let c = centroids.get(g);
35586
- if (!c) {
35587
- c = { x: 0, y: 0, n: 0 };
35588
- centroids.set(g, c);
35604
+ for (const pair of activeSimilarity) {
35605
+ const source = nodeById.get(pair.source);
35606
+ const target = nodeById.get(pair.target);
35607
+ if (!source || !target) continue;
35608
+ const w2 = Math.min(1, Math.max(0, pair.weight));
35609
+ spring(source, target, linkDistance * (1.35 - 0.55 * w2), 0.015);
35589
35610
  }
35590
- c.x += node.x;
35591
- c.y += node.y;
35592
- c.n += 1;
35593
- }
35594
- const multiCluster = centroids.size > 1;
35595
- for (const node of nodes) {
35596
- if (multiCluster) {
35597
- const c = centroids.get(node.group ?? "__none__");
35598
- if (c && c.n > 1) {
35599
- node.fx += (c.x / c.n - node.x) * 0.04 * temp;
35600
- node.fy += (c.y / c.n - node.y) * 0.04 * temp;
35611
+ const centroids = /* @__PURE__ */ new Map();
35612
+ for (const node of nodes) {
35613
+ const g = node.group ?? "__none__";
35614
+ let c = centroids.get(g);
35615
+ if (!c) {
35616
+ c = { x: 0, y: 0, n: 0 };
35617
+ centroids.set(g, c);
35618
+ }
35619
+ c.x += node.x;
35620
+ c.y += node.y;
35621
+ c.n += 1;
35622
+ }
35623
+ const multiCluster = centroids.size > 1;
35624
+ for (const node of nodes) {
35625
+ if (multiCluster) {
35626
+ const c = centroids.get(node.group ?? "__none__");
35627
+ if (c && c.n > 1) {
35628
+ node.fx += (c.x / c.n - node.x) * 0.06 * temp;
35629
+ node.fy += (c.y / c.n - node.y) * 0.06 * temp;
35630
+ } else {
35631
+ node.fx += (centerX - node.x) * 0.01 * temp;
35632
+ node.fy += (centerY - node.y) * 0.01 * temp;
35633
+ }
35601
35634
  } else {
35602
- node.fx += (centerX - node.x) * 0.01 * temp;
35603
- node.fy += (centerY - node.y) * 0.01 * temp;
35635
+ node.fx += (centerX - node.x) * 8e-3 * temp;
35636
+ node.fy += (centerY - node.y) * 8e-3 * temp;
35604
35637
  }
35605
- } else {
35606
- node.fx += (centerX - node.x) * 8e-3 * temp;
35607
- node.fy += (centerY - node.y) * 8e-3 * temp;
35638
+ }
35639
+ const damping = 0.9;
35640
+ for (const node of nodes) {
35641
+ node.vx = (node.vx + node.fx) * damping;
35642
+ node.vy = (node.vy + node.fy) * damping;
35643
+ node.x += node.vx;
35644
+ node.y += node.vy;
35608
35645
  }
35609
35646
  }
35610
- const damping = 0.9;
35611
35647
  for (const node of nodes) {
35612
- node.vx = (node.vx + node.fx) * damping;
35613
- node.vy = (node.vy + node.fy) * damping;
35614
- node.x += node.vx;
35615
- node.y += node.vy;
35616
35648
  node.x = Math.max(30, Math.min(w - 30, node.x));
35617
35649
  node.y = Math.max(30, Math.min(h - 30, node.y));
35618
35650
  }
@@ -35663,6 +35695,22 @@ var init_GraphCanvas = __esm({
35663
35695
  };
35664
35696
  if (fullRelayout) {
35665
35697
  for (let i = 0; i < maxIterations; i++) tick(i);
35698
+ for (let i = 0; i < 4; i++) tick(maxIterations, { skipForces: true });
35699
+ const fitPad = 40;
35700
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
35701
+ for (const node of nodesRef.current) {
35702
+ const r = node.size || 8;
35703
+ const lw = showLabels ? node.labelW ?? (node.labelW = node.label ? measureLabelWidth(node.label, labelFont) : 0) : 0;
35704
+ minX = Math.min(minX, node.x - r, node.x - lw / 2);
35705
+ minY = Math.min(minY, node.y - r);
35706
+ maxX = Math.max(maxX, node.x + r, node.x + lw / 2);
35707
+ maxY = Math.max(maxY, node.y + r + LABEL_GAP + LABEL_H);
35708
+ }
35709
+ const bboxW = Math.max(1, maxX - minX + fitPad * 2);
35710
+ const bboxH = Math.max(1, maxY - minY + fitPad * 2);
35711
+ const nextZoom = Math.min(1, viewW / bboxW, viewH / bboxH);
35712
+ setZoom(nextZoom);
35713
+ setOffset({ x: fitPad - minX * nextZoom, y: fitPad - minY * nextZoom });
35666
35714
  laidOutRef.current = true;
35667
35715
  } else {
35668
35716
  const centroids = /* @__PURE__ */ new Map();