@almadar/ui 5.148.0 → 5.150.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.
- package/dist/{GameAudioProvider-CPGwD49P.d.cts → GameAudioProvider-B48iXwz3.d.ts} +2 -50
- package/dist/{GameAudioProvider-CPGwD49P.d.ts → GameAudioProvider-CQAdPreB.d.cts} +2 -50
- package/dist/avl/index.cjs +2741 -158
- package/dist/avl/index.js +2741 -158
- package/dist/{avl-schema-parser-DVmrgdwc.d.cts → avl-schema-parser-CVzkNzg7.d.cts} +18 -9
- package/dist/{avl-schema-parser-BRJ77Yze.d.ts → avl-schema-parser-Cx_4SVg9.d.ts} +18 -9
- package/dist/{cn-BAn68sNO.d.cts → cn-BnAJZcNb.d.cts} +2 -8
- package/dist/{cn-CWxxLkri.d.ts → cn-DJTUjk1M.d.ts} +2 -8
- package/dist/components/index.cjs +2764 -158
- package/dist/components/index.d.cts +1077 -107
- package/dist/components/index.d.ts +1077 -107
- package/dist/components/index.js +2760 -160
- package/dist/hooks/index.cjs +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/lib/drawable/three/index.cjs +57 -30
- package/dist/lib/drawable/three/index.d.cts +4 -3
- package/dist/lib/drawable/three/index.d.ts +4 -3
- package/dist/lib/drawable/three/index.js +57 -30
- package/dist/lib/index.d.cts +3 -2
- package/dist/lib/index.d.ts +3 -2
- package/dist/marketing/index.cjs +1 -1
- package/dist/marketing/index.js +1 -1
- package/dist/{paintDispatch-Cb_hQj4Y.d.ts → paintDispatch-BjZjUbcb.d.cts} +63 -69
- package/dist/{paintDispatch-Cb_hQj4Y.d.cts → paintDispatch-CxdLjHQq.d.ts} +63 -69
- package/dist/providers/index.cjs +2734 -152
- package/dist/providers/index.d.cts +2 -1
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/index.js +2734 -152
- package/dist/runtime/index.cjs +2741 -158
- package/dist/runtime/index.d.cts +2 -2
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +2741 -158
- package/dist/types-B3nHgnMG.d.cts +51 -0
- package/dist/types-B3nHgnMG.d.ts +51 -0
- package/package.json +3 -3
package/dist/components/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import * as React77 from 'react';
|
|
3
|
-
import React77__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, useState, useLayoutEffect,
|
|
3
|
+
import React77__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, useState, useLayoutEffect, useId, Suspense, lazy, useSyncExternalStore } from 'react';
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
6
6
|
import { EventBusContext, useTraitScopeChain, useCurrentPagePath, useGameAudioContextOptional, useEntitySchemaOptional, useEntityBindingSnapshot, useTraitScope, TraitScopeProvider } from '@almadar/ui/providers';
|
|
@@ -1081,7 +1081,7 @@ function useEventBus() {
|
|
|
1081
1081
|
return {
|
|
1082
1082
|
...baseBus,
|
|
1083
1083
|
emit: (type, payload, source) => {
|
|
1084
|
-
if (typeof type === "string" && type.startsWith("UI:") && !type.slice(3).includes(".")) {
|
|
1084
|
+
if (typeof type === "string" && type.startsWith("UI:") && !type.slice(3).includes(".") && !source?.trait) {
|
|
1085
1085
|
scopeLog.warn("emit:bare-key-no-scope", { type });
|
|
1086
1086
|
}
|
|
1087
1087
|
baseBus.emit(type, payload, source);
|
|
@@ -8407,6 +8407,14 @@ function shapeBounds(shape) {
|
|
|
8407
8407
|
w: shape.radius * 2 + 8,
|
|
8408
8408
|
h: shape.radius * 2 + 8
|
|
8409
8409
|
};
|
|
8410
|
+
case "ellipse":
|
|
8411
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) return null;
|
|
8412
|
+
return {
|
|
8413
|
+
x: shape.x - shape.width / 2 - 4,
|
|
8414
|
+
y: shape.y - shape.height / 2 - 4,
|
|
8415
|
+
w: shape.width + 8,
|
|
8416
|
+
h: shape.height + 8
|
|
8417
|
+
};
|
|
8410
8418
|
case "rect":
|
|
8411
8419
|
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) return null;
|
|
8412
8420
|
return { x: shape.x - 4, y: shape.y - 4, w: shape.width + 8, h: shape.height + 8 };
|
|
@@ -8440,13 +8448,14 @@ function drawArrowHead(ctx, x1, y1, x2, y2, size) {
|
|
|
8440
8448
|
ctx.closePath();
|
|
8441
8449
|
ctx.fill();
|
|
8442
8450
|
}
|
|
8443
|
-
function drawShape(ctx, shape, width, height) {
|
|
8451
|
+
function drawShape(ctx, shape, width, height, allShapes) {
|
|
8444
8452
|
ctx.save();
|
|
8445
8453
|
const opacity = shape.opacity ?? 1;
|
|
8446
8454
|
ctx.globalAlpha = opacity;
|
|
8447
8455
|
const stroke = resolveColor2(shape.color, ctx, "#333333");
|
|
8448
8456
|
const fill = shape.fill ? resolveColor2(shape.fill, ctx, "#cccccc") : void 0;
|
|
8449
8457
|
ctx.lineWidth = shape.lineWidth ?? 2;
|
|
8458
|
+
if (shape.dash) ctx.setLineDash([...DASH_PATTERNS[shape.dash]]);
|
|
8450
8459
|
switch (shape.type) {
|
|
8451
8460
|
case "grid": {
|
|
8452
8461
|
const step = shape.step ?? 40;
|
|
@@ -8512,6 +8521,20 @@ function drawShape(ctx, shape, width, height) {
|
|
|
8512
8521
|
ctx.stroke();
|
|
8513
8522
|
break;
|
|
8514
8523
|
}
|
|
8524
|
+
case "ellipse": {
|
|
8525
|
+
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) break;
|
|
8526
|
+
const startAngle = (shape.startAngle ?? 0) * Math.PI / 180;
|
|
8527
|
+
const endAngle = (shape.endAngle ?? 360) * Math.PI / 180;
|
|
8528
|
+
ctx.beginPath();
|
|
8529
|
+
ctx.ellipse(shape.x, shape.y, shape.width / 2, shape.height / 2, 0, startAngle, endAngle);
|
|
8530
|
+
if (fill) {
|
|
8531
|
+
ctx.fillStyle = fill;
|
|
8532
|
+
ctx.fill();
|
|
8533
|
+
}
|
|
8534
|
+
ctx.strokeStyle = stroke;
|
|
8535
|
+
ctx.stroke();
|
|
8536
|
+
break;
|
|
8537
|
+
}
|
|
8515
8538
|
case "rect": {
|
|
8516
8539
|
if (shape.x == null || shape.y == null || shape.width == null || shape.height == null) break;
|
|
8517
8540
|
if (fill) {
|
|
@@ -8558,21 +8581,153 @@ function drawShape(ctx, shape, width, height) {
|
|
|
8558
8581
|
ctx.fillText(shape.text, shape.x, shape.y);
|
|
8559
8582
|
break;
|
|
8560
8583
|
}
|
|
8584
|
+
case "venn-region": {
|
|
8585
|
+
const resolveCircles = (ids) => (ids ?? []).flatMap((id) => {
|
|
8586
|
+
const c = allShapes.find((s) => s.type === "circle" && s.id === id);
|
|
8587
|
+
return c && c.x != null && c.y != null && c.radius != null ? [{ x: c.x, y: c.y, radius: c.radius }] : [];
|
|
8588
|
+
});
|
|
8589
|
+
const inside = resolveCircles(shape.inside);
|
|
8590
|
+
if (inside.length === 0) break;
|
|
8591
|
+
const outside = resolveCircles(shape.outside);
|
|
8592
|
+
const off = document.createElement("canvas");
|
|
8593
|
+
off.width = ctx.canvas.width;
|
|
8594
|
+
off.height = ctx.canvas.height;
|
|
8595
|
+
const octx = off.getContext("2d");
|
|
8596
|
+
if (!octx) break;
|
|
8597
|
+
octx.setTransform(ctx.getTransform());
|
|
8598
|
+
for (const c of inside) {
|
|
8599
|
+
const p = new Path2D();
|
|
8600
|
+
p.arc(c.x, c.y, c.radius, 0, Math.PI * 2);
|
|
8601
|
+
octx.clip(p);
|
|
8602
|
+
}
|
|
8603
|
+
octx.fillStyle = fill ?? stroke;
|
|
8604
|
+
octx.fillRect(0, 0, width, height);
|
|
8605
|
+
octx.globalCompositeOperation = "destination-out";
|
|
8606
|
+
for (const c of outside) {
|
|
8607
|
+
const p = new Path2D();
|
|
8608
|
+
p.arc(c.x, c.y, c.radius, 0, Math.PI * 2);
|
|
8609
|
+
octx.fill(p);
|
|
8610
|
+
}
|
|
8611
|
+
ctx.save();
|
|
8612
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
8613
|
+
ctx.drawImage(off, 0, 0);
|
|
8614
|
+
ctx.restore();
|
|
8615
|
+
break;
|
|
8616
|
+
}
|
|
8561
8617
|
}
|
|
8562
8618
|
ctx.restore();
|
|
8563
8619
|
}
|
|
8564
|
-
|
|
8620
|
+
function readoutShapes(readouts, width) {
|
|
8621
|
+
const out = [];
|
|
8622
|
+
const chipH = 18;
|
|
8623
|
+
const gap = 6;
|
|
8624
|
+
let rightEdge = width - 6;
|
|
8625
|
+
let rowY = 6;
|
|
8626
|
+
for (const readout of readouts) {
|
|
8627
|
+
const text = `${readout.label}: ${String(readout.value)}`;
|
|
8628
|
+
const chipW = Math.min(170, Math.max(34, text.length * 6 + 12));
|
|
8629
|
+
let chipX = rightEdge - chipW;
|
|
8630
|
+
if (chipX < 4) {
|
|
8631
|
+
rowY += chipH + 4;
|
|
8632
|
+
rightEdge = width - 6;
|
|
8633
|
+
chipX = rightEdge - chipW;
|
|
8634
|
+
}
|
|
8635
|
+
const color = readout.color ?? "#334155";
|
|
8636
|
+
out.push({ type: "rect", x: chipX, y: rowY, width: chipW, height: chipH, color, fill: color });
|
|
8637
|
+
out.push({
|
|
8638
|
+
type: "text",
|
|
8639
|
+
x: chipX + chipW / 2,
|
|
8640
|
+
y: rowY + chipH / 2,
|
|
8641
|
+
text,
|
|
8642
|
+
color: "#ffffff",
|
|
8643
|
+
fontSize: 10,
|
|
8644
|
+
align: "center"
|
|
8645
|
+
});
|
|
8646
|
+
rightEdge = chipX - gap;
|
|
8647
|
+
}
|
|
8648
|
+
return out;
|
|
8649
|
+
}
|
|
8650
|
+
function traceShapes(panel, k, width, height) {
|
|
8651
|
+
const w = panel.width ?? Math.round(width * 0.32);
|
|
8652
|
+
const h = panel.height ?? Math.round(height * 0.28);
|
|
8653
|
+
const x = panel.x ?? width - w - 8;
|
|
8654
|
+
const y = panel.y ?? height - h - 8 - k * (h + 8);
|
|
8655
|
+
const allSamples = panel.series.flatMap((series) => series.samples);
|
|
8656
|
+
let xLo = Math.min(...allSamples.map((p) => p.x));
|
|
8657
|
+
let xHi = Math.max(...allSamples.map((p) => p.x));
|
|
8658
|
+
let yLo = Math.min(...allSamples.map((p) => p.y));
|
|
8659
|
+
let yHi = Math.max(...allSamples.map((p) => p.y));
|
|
8660
|
+
if (xLo === xHi) {
|
|
8661
|
+
xLo -= 1;
|
|
8662
|
+
xHi += 1;
|
|
8663
|
+
}
|
|
8664
|
+
if (yLo === yHi) {
|
|
8665
|
+
yLo -= 1;
|
|
8666
|
+
yHi += 1;
|
|
8667
|
+
}
|
|
8668
|
+
const backgroundColor = panel.backgroundColor ?? "#ffffff";
|
|
8669
|
+
const frameColor = panel.frameColor ?? "#94a3b8";
|
|
8670
|
+
const out = [];
|
|
8671
|
+
out.push({
|
|
8672
|
+
type: "rect",
|
|
8673
|
+
x,
|
|
8674
|
+
y,
|
|
8675
|
+
width: w,
|
|
8676
|
+
height: h,
|
|
8677
|
+
color: backgroundColor,
|
|
8678
|
+
fill: backgroundColor,
|
|
8679
|
+
opacity: panel.backgroundOpacity ?? 0.85
|
|
8680
|
+
});
|
|
8681
|
+
out.push({ type: "rect", x, y, width: w, height: h, color: frameColor, lineWidth: 1 });
|
|
8682
|
+
panel.series.forEach((series, j) => {
|
|
8683
|
+
const color = series.color ?? TRACE_SERIES_COLORS[j % TRACE_SERIES_COLORS.length];
|
|
8684
|
+
const mapped = series.samples.map((p) => ({
|
|
8685
|
+
x: x + 4 + (p.x - xLo) / (xHi - xLo) * (w - 8),
|
|
8686
|
+
y: y + h - 4 - (p.y - yLo) / (yHi - yLo) * (h - 8)
|
|
8687
|
+
}));
|
|
8688
|
+
for (let i = 1; i < mapped.length; i++) {
|
|
8689
|
+
out.push({
|
|
8690
|
+
type: "line",
|
|
8691
|
+
x1: mapped[i - 1].x,
|
|
8692
|
+
y1: mapped[i - 1].y,
|
|
8693
|
+
x2: mapped[i].x,
|
|
8694
|
+
y2: mapped[i].y,
|
|
8695
|
+
color,
|
|
8696
|
+
lineWidth: 1.5
|
|
8697
|
+
});
|
|
8698
|
+
}
|
|
8699
|
+
if (mapped.length > 0) {
|
|
8700
|
+
const last = mapped[mapped.length - 1];
|
|
8701
|
+
out.push({ type: "circle", x: last.x, y: last.y, radius: 2, color, fill: color });
|
|
8702
|
+
}
|
|
8703
|
+
if (series.label) {
|
|
8704
|
+
out.push({ type: "text", x: x + 6, y: y + 10 + 11 * j, text: series.label, color, fontSize: 9 });
|
|
8705
|
+
}
|
|
8706
|
+
});
|
|
8707
|
+
if (panel.yLabel) {
|
|
8708
|
+
out.push({ type: "text", x: x + w - 6, y: y + 10, text: panel.yLabel, color: "#6b7280", fontSize: 9, align: "right" });
|
|
8709
|
+
}
|
|
8710
|
+
if (panel.xLabel) {
|
|
8711
|
+
out.push({ type: "text", x: x + w - 6, y: y + h - 6, text: panel.xLabel, color: "#6b7280", fontSize: 9, align: "right" });
|
|
8712
|
+
}
|
|
8713
|
+
return out;
|
|
8714
|
+
}
|
|
8715
|
+
var DASH_PATTERNS, TRACE_SERIES_COLORS, LearningCanvas;
|
|
8565
8716
|
var init_LearningCanvas = __esm({
|
|
8566
8717
|
"components/learning/atoms/LearningCanvas.tsx"() {
|
|
8567
8718
|
"use client";
|
|
8568
8719
|
init_cn();
|
|
8569
8720
|
init_useEventBus();
|
|
8721
|
+
DASH_PATTERNS = { dashed: [6, 4], dotted: [2, 3] };
|
|
8722
|
+
TRACE_SERIES_COLORS = ["#2563eb", "#dc2626", "#16a34a", "#f59e0b"];
|
|
8570
8723
|
LearningCanvas = ({
|
|
8571
8724
|
className,
|
|
8572
8725
|
width = 600,
|
|
8573
8726
|
height = 400,
|
|
8574
8727
|
backgroundColor,
|
|
8575
8728
|
shapes = [],
|
|
8729
|
+
readouts,
|
|
8730
|
+
traces,
|
|
8576
8731
|
interactive = false,
|
|
8577
8732
|
animate = false,
|
|
8578
8733
|
onShapeClick,
|
|
@@ -8598,6 +8753,12 @@ var init_LearningCanvas = __esm({
|
|
|
8598
8753
|
}
|
|
8599
8754
|
return -1;
|
|
8600
8755
|
}, [shapes]);
|
|
8756
|
+
const derivedShapes = useMemo(() => {
|
|
8757
|
+
if (!traces?.length && !readouts?.length) return shapes;
|
|
8758
|
+
const traceOut = (traces ?? []).flatMap((panel, k) => traceShapes(panel, k, width, height));
|
|
8759
|
+
const readoutOut = readouts?.length ? readoutShapes(readouts, width) : [];
|
|
8760
|
+
return [...shapes, ...traceOut, ...readoutOut];
|
|
8761
|
+
}, [shapes, traces, readouts, width, height]);
|
|
8601
8762
|
const draw = useCallback(() => {
|
|
8602
8763
|
const canvas = canvasRef.current;
|
|
8603
8764
|
if (!canvas) return;
|
|
@@ -8614,13 +8775,13 @@ var init_LearningCanvas = __esm({
|
|
|
8614
8775
|
ctx.fillStyle = backgroundColor;
|
|
8615
8776
|
ctx.fillRect(0, 0, width, height);
|
|
8616
8777
|
}
|
|
8617
|
-
for (const shape of
|
|
8618
|
-
if (shape.type !== "text") drawShape(ctx, shape, width, height);
|
|
8778
|
+
for (const shape of derivedShapes) {
|
|
8779
|
+
if (shape.type !== "text") drawShape(ctx, shape, width, height, derivedShapes);
|
|
8619
8780
|
}
|
|
8620
|
-
for (const shape of
|
|
8621
|
-
if (shape.type === "text") drawShape(ctx, shape, width, height);
|
|
8781
|
+
for (const shape of derivedShapes) {
|
|
8782
|
+
if (shape.type === "text") drawShape(ctx, shape, width, height, derivedShapes);
|
|
8622
8783
|
}
|
|
8623
|
-
}, [width, height, backgroundColor,
|
|
8784
|
+
}, [width, height, backgroundColor, derivedShapes]);
|
|
8624
8785
|
useEffect(() => {
|
|
8625
8786
|
draw();
|
|
8626
8787
|
}, [draw]);
|
|
@@ -8688,7 +8849,363 @@ var init_LearningCanvas = __esm({
|
|
|
8688
8849
|
};
|
|
8689
8850
|
}
|
|
8690
8851
|
});
|
|
8691
|
-
|
|
8852
|
+
function layoutCircle(nodes, width, height) {
|
|
8853
|
+
const cx = width / 2;
|
|
8854
|
+
const cy = height / 2;
|
|
8855
|
+
const radius = Math.max(10, Math.min(cx, cy) - 40);
|
|
8856
|
+
const positions = /* @__PURE__ */ new Map();
|
|
8857
|
+
const n = nodes.length;
|
|
8858
|
+
nodes.forEach((node, i) => {
|
|
8859
|
+
const angle = 2 * Math.PI * i / Math.max(n, 1) - Math.PI / 2;
|
|
8860
|
+
positions.set(node.id, { x: cx + radius * Math.cos(angle), y: cy + radius * Math.sin(angle) });
|
|
8861
|
+
});
|
|
8862
|
+
return positions;
|
|
8863
|
+
}
|
|
8864
|
+
function layoutTree(nodes, edges, root, width, height) {
|
|
8865
|
+
const nodeIds = nodes.map((n) => n.id);
|
|
8866
|
+
const idSet = new Set(nodeIds);
|
|
8867
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
8868
|
+
const hasIncoming = /* @__PURE__ */ new Set();
|
|
8869
|
+
for (const e of edges) {
|
|
8870
|
+
if (!idSet.has(e.from) || !idSet.has(e.to)) continue;
|
|
8871
|
+
const list = childrenOf.get(e.from) ?? [];
|
|
8872
|
+
list.push(e.to);
|
|
8873
|
+
childrenOf.set(e.from, list);
|
|
8874
|
+
hasIncoming.add(e.to);
|
|
8875
|
+
}
|
|
8876
|
+
const depth = /* @__PURE__ */ new Map();
|
|
8877
|
+
const treeChildren = /* @__PURE__ */ new Map();
|
|
8878
|
+
const visited = /* @__PURE__ */ new Set();
|
|
8879
|
+
const bfsFrom = (start) => {
|
|
8880
|
+
if (visited.has(start)) return;
|
|
8881
|
+
visited.add(start);
|
|
8882
|
+
depth.set(start, 0);
|
|
8883
|
+
const queue = [start];
|
|
8884
|
+
while (queue.length > 0) {
|
|
8885
|
+
const u = queue.shift();
|
|
8886
|
+
for (const v of childrenOf.get(u) ?? []) {
|
|
8887
|
+
if (visited.has(v)) continue;
|
|
8888
|
+
visited.add(v);
|
|
8889
|
+
depth.set(v, (depth.get(u) ?? 0) + 1);
|
|
8890
|
+
const list = treeChildren.get(u) ?? [];
|
|
8891
|
+
list.push(v);
|
|
8892
|
+
treeChildren.set(u, list);
|
|
8893
|
+
queue.push(v);
|
|
8894
|
+
}
|
|
8895
|
+
}
|
|
8896
|
+
};
|
|
8897
|
+
const primaryRoot = root && idSet.has(root) ? root : nodeIds.find((id) => !hasIncoming.has(id)) ?? nodeIds[0];
|
|
8898
|
+
const rootsOrder = [];
|
|
8899
|
+
if (primaryRoot !== void 0) {
|
|
8900
|
+
bfsFrom(primaryRoot);
|
|
8901
|
+
rootsOrder.push(primaryRoot);
|
|
8902
|
+
}
|
|
8903
|
+
for (const id of nodeIds) {
|
|
8904
|
+
if (!visited.has(id)) {
|
|
8905
|
+
bfsFrom(id);
|
|
8906
|
+
rootsOrder.push(id);
|
|
8907
|
+
}
|
|
8908
|
+
}
|
|
8909
|
+
let leafCounter = 0;
|
|
8910
|
+
const xSlot = /* @__PURE__ */ new Map();
|
|
8911
|
+
const assignXSlot = (u) => {
|
|
8912
|
+
const children = treeChildren.get(u) ?? [];
|
|
8913
|
+
if (children.length === 0) {
|
|
8914
|
+
const slot = leafCounter++;
|
|
8915
|
+
xSlot.set(u, slot);
|
|
8916
|
+
return slot;
|
|
8917
|
+
}
|
|
8918
|
+
const childSlots = children.map(assignXSlot);
|
|
8919
|
+
const avg = childSlots.reduce((a, b) => a + b, 0) / childSlots.length;
|
|
8920
|
+
xSlot.set(u, avg);
|
|
8921
|
+
return avg;
|
|
8922
|
+
};
|
|
8923
|
+
for (const r of rootsOrder) assignXSlot(r);
|
|
8924
|
+
let maxDepth = 0;
|
|
8925
|
+
for (const d of depth.values()) maxDepth = Math.max(maxDepth, d);
|
|
8926
|
+
const colWidth = width / Math.max(1, leafCounter);
|
|
8927
|
+
const rowHeight = height / (maxDepth + 1);
|
|
8928
|
+
const positions = /* @__PURE__ */ new Map();
|
|
8929
|
+
for (const id of nodeIds) {
|
|
8930
|
+
const slot = xSlot.get(id) ?? 0;
|
|
8931
|
+
const d = depth.get(id) ?? 0;
|
|
8932
|
+
positions.set(id, { x: slot * colWidth + colWidth / 2, y: d * rowHeight + rowHeight / 2 });
|
|
8933
|
+
}
|
|
8934
|
+
return positions;
|
|
8935
|
+
}
|
|
8936
|
+
function layoutLayered(nodes, edges, width, height) {
|
|
8937
|
+
const nodeIds = nodes.map((n) => n.id);
|
|
8938
|
+
const idSet = new Set(nodeIds);
|
|
8939
|
+
const adj = /* @__PURE__ */ new Map();
|
|
8940
|
+
const remainingIndegree = /* @__PURE__ */ new Map();
|
|
8941
|
+
for (const id of nodeIds) remainingIndegree.set(id, 0);
|
|
8942
|
+
for (const e of edges) {
|
|
8943
|
+
if (!idSet.has(e.from) || !idSet.has(e.to)) continue;
|
|
8944
|
+
const list = adj.get(e.from) ?? [];
|
|
8945
|
+
list.push(e.to);
|
|
8946
|
+
adj.set(e.from, list);
|
|
8947
|
+
remainingIndegree.set(e.to, (remainingIndegree.get(e.to) ?? 0) + 1);
|
|
8948
|
+
}
|
|
8949
|
+
const layer = /* @__PURE__ */ new Map();
|
|
8950
|
+
const dequeued = /* @__PURE__ */ new Set();
|
|
8951
|
+
const queue = [];
|
|
8952
|
+
for (const id of nodeIds) {
|
|
8953
|
+
if ((remainingIndegree.get(id) ?? 0) === 0) {
|
|
8954
|
+
layer.set(id, 0);
|
|
8955
|
+
queue.push(id);
|
|
8956
|
+
}
|
|
8957
|
+
}
|
|
8958
|
+
while (queue.length > 0) {
|
|
8959
|
+
const u = queue.shift();
|
|
8960
|
+
dequeued.add(u);
|
|
8961
|
+
for (const v of adj.get(u) ?? []) {
|
|
8962
|
+
const candidate = (layer.get(u) ?? 0) + 1;
|
|
8963
|
+
layer.set(v, Math.max(layer.get(v) ?? 0, candidate));
|
|
8964
|
+
remainingIndegree.set(v, (remainingIndegree.get(v) ?? 0) - 1);
|
|
8965
|
+
if ((remainingIndegree.get(v) ?? 0) === 0 && !dequeued.has(v)) {
|
|
8966
|
+
queue.push(v);
|
|
8967
|
+
}
|
|
8968
|
+
}
|
|
8969
|
+
}
|
|
8970
|
+
let baseMaxLayer = 0;
|
|
8971
|
+
for (const id of nodeIds) {
|
|
8972
|
+
if (dequeued.has(id)) baseMaxLayer = Math.max(baseMaxLayer, layer.get(id) ?? 0);
|
|
8973
|
+
}
|
|
8974
|
+
const cycleLayer = baseMaxLayer + 1;
|
|
8975
|
+
let maxLayer = baseMaxLayer;
|
|
8976
|
+
for (const id of nodeIds) {
|
|
8977
|
+
if (!dequeued.has(id)) {
|
|
8978
|
+
layer.set(id, cycleLayer);
|
|
8979
|
+
maxLayer = cycleLayer;
|
|
8980
|
+
}
|
|
8981
|
+
}
|
|
8982
|
+
const colWidth = width / Math.max(1, maxLayer + 1);
|
|
8983
|
+
const byLayer = /* @__PURE__ */ new Map();
|
|
8984
|
+
for (const id of nodeIds) {
|
|
8985
|
+
const l = layer.get(id) ?? 0;
|
|
8986
|
+
const list = byLayer.get(l) ?? [];
|
|
8987
|
+
list.push(id);
|
|
8988
|
+
byLayer.set(l, list);
|
|
8989
|
+
}
|
|
8990
|
+
const positions = /* @__PURE__ */ new Map();
|
|
8991
|
+
for (const [l, ids] of byLayer) {
|
|
8992
|
+
const rowHeight = height / ids.length;
|
|
8993
|
+
ids.forEach((id, i) => {
|
|
8994
|
+
positions.set(id, { x: l * colWidth + colWidth / 2, y: i * rowHeight + rowHeight / 2 });
|
|
8995
|
+
});
|
|
8996
|
+
}
|
|
8997
|
+
return positions;
|
|
8998
|
+
}
|
|
8999
|
+
function computePositions(nodes, edges, layout, root, width, height) {
|
|
9000
|
+
switch (layout) {
|
|
9001
|
+
case "circle":
|
|
9002
|
+
return layoutCircle(nodes, width, height);
|
|
9003
|
+
case "tree":
|
|
9004
|
+
return layoutTree(nodes, edges, root, width, height);
|
|
9005
|
+
case "layered":
|
|
9006
|
+
return layoutLayered(nodes, edges, width, height);
|
|
9007
|
+
case "manual":
|
|
9008
|
+
default: {
|
|
9009
|
+
const positions = /* @__PURE__ */ new Map();
|
|
9010
|
+
for (const n of nodes) positions.set(n.id, { x: n.x ?? 0, y: n.y ?? 0 });
|
|
9011
|
+
return positions;
|
|
9012
|
+
}
|
|
9013
|
+
}
|
|
9014
|
+
}
|
|
9015
|
+
var NODE_STATE_COLOR, EDGE_STATE_COLOR, DEFAULT_NODE_RADIUS, AlgoGraphCanvas;
|
|
9016
|
+
var init_AlgoGraphCanvas = __esm({
|
|
9017
|
+
"components/learning/molecules/AlgoGraphCanvas.tsx"() {
|
|
9018
|
+
"use client";
|
|
9019
|
+
init_atoms();
|
|
9020
|
+
init_Stack();
|
|
9021
|
+
init_LearningCanvas();
|
|
9022
|
+
NODE_STATE_COLOR = {
|
|
9023
|
+
unvisited: "#cbd5e1",
|
|
9024
|
+
frontier: "#f59e0b",
|
|
9025
|
+
current: "#ef4444",
|
|
9026
|
+
visited: "#22c55e",
|
|
9027
|
+
goal: "#8b5cf6",
|
|
9028
|
+
path: "#0ea5e9"
|
|
9029
|
+
};
|
|
9030
|
+
EDGE_STATE_COLOR = {
|
|
9031
|
+
default: "#9ca3af",
|
|
9032
|
+
tree: "#16a34a",
|
|
9033
|
+
relaxed: "#f59e0b",
|
|
9034
|
+
candidate: "#38bdf8",
|
|
9035
|
+
path: "#dc2626"
|
|
9036
|
+
};
|
|
9037
|
+
DEFAULT_NODE_RADIUS = 18;
|
|
9038
|
+
AlgoGraphCanvas = ({
|
|
9039
|
+
className,
|
|
9040
|
+
width = 600,
|
|
9041
|
+
height = 400,
|
|
9042
|
+
title,
|
|
9043
|
+
backgroundColor,
|
|
9044
|
+
nodes = [],
|
|
9045
|
+
edges = [],
|
|
9046
|
+
layout = "manual",
|
|
9047
|
+
root,
|
|
9048
|
+
shapes = [],
|
|
9049
|
+
interactive = false,
|
|
9050
|
+
animate = false,
|
|
9051
|
+
onShapeClick,
|
|
9052
|
+
onNodeClick,
|
|
9053
|
+
isLoading,
|
|
9054
|
+
error
|
|
9055
|
+
}) => {
|
|
9056
|
+
const nodeById = useMemo(() => {
|
|
9057
|
+
const m = /* @__PURE__ */ new Map();
|
|
9058
|
+
for (const n of nodes) m.set(n.id, n);
|
|
9059
|
+
return m;
|
|
9060
|
+
}, [nodes]);
|
|
9061
|
+
const nodeIndexById = useMemo(() => {
|
|
9062
|
+
const m = /* @__PURE__ */ new Map();
|
|
9063
|
+
nodes.forEach((n, i) => m.set(n.id, i));
|
|
9064
|
+
return m;
|
|
9065
|
+
}, [nodes]);
|
|
9066
|
+
const derivedShapes = useMemo(() => {
|
|
9067
|
+
const out = [];
|
|
9068
|
+
const positions = computePositions(nodes, edges, layout, root, width, height);
|
|
9069
|
+
const edgeGeoms = [];
|
|
9070
|
+
for (const e of edges) {
|
|
9071
|
+
const a = nodeById.get(e.from);
|
|
9072
|
+
const b = nodeById.get(e.to);
|
|
9073
|
+
const posA = positions.get(e.from);
|
|
9074
|
+
const posB = positions.get(e.to);
|
|
9075
|
+
if (!a || !b || !posA || !posB) continue;
|
|
9076
|
+
const rA = a.radius ?? DEFAULT_NODE_RADIUS;
|
|
9077
|
+
const rB = b.radius ?? DEFAULT_NODE_RADIUS;
|
|
9078
|
+
const dx = posB.x - posA.x;
|
|
9079
|
+
const dy = posB.y - posA.y;
|
|
9080
|
+
const dist = Math.max(1e-6, Math.hypot(dx, dy));
|
|
9081
|
+
const ux = dx / dist;
|
|
9082
|
+
const uy = dy / dist;
|
|
9083
|
+
edgeGeoms.push({
|
|
9084
|
+
directed: e.directed ?? false,
|
|
9085
|
+
x1: posA.x + ux * rA,
|
|
9086
|
+
y1: posA.y + uy * rA,
|
|
9087
|
+
x2: posB.x - ux * rB,
|
|
9088
|
+
y2: posB.y - uy * rB,
|
|
9089
|
+
color: e.color ?? EDGE_STATE_COLOR[e.state ?? "default"],
|
|
9090
|
+
label: e.label ?? (e.weight != null ? String(e.weight) : void 0)
|
|
9091
|
+
});
|
|
9092
|
+
}
|
|
9093
|
+
for (const g of edgeGeoms) {
|
|
9094
|
+
out.push({
|
|
9095
|
+
type: g.directed ? "arrow" : "line",
|
|
9096
|
+
x1: g.x1,
|
|
9097
|
+
y1: g.y1,
|
|
9098
|
+
x2: g.x2,
|
|
9099
|
+
y2: g.y2,
|
|
9100
|
+
color: g.color,
|
|
9101
|
+
lineWidth: 2
|
|
9102
|
+
});
|
|
9103
|
+
}
|
|
9104
|
+
for (const g of edgeGeoms) {
|
|
9105
|
+
if (g.label === void 0) continue;
|
|
9106
|
+
const midX = (g.x1 + g.x2) / 2;
|
|
9107
|
+
const midY = (g.y1 + g.y2) / 2;
|
|
9108
|
+
const dx = g.x2 - g.x1;
|
|
9109
|
+
const dy = g.y2 - g.y1;
|
|
9110
|
+
const dist = Math.max(1e-6, Math.hypot(dx, dy));
|
|
9111
|
+
const perpX = -(dy / dist);
|
|
9112
|
+
const perpY = dx / dist;
|
|
9113
|
+
out.push({
|
|
9114
|
+
type: "text",
|
|
9115
|
+
x: midX + perpX * 10,
|
|
9116
|
+
y: midY + perpY * 10,
|
|
9117
|
+
text: g.label,
|
|
9118
|
+
fontSize: 11,
|
|
9119
|
+
align: "center",
|
|
9120
|
+
color: "#374151"
|
|
9121
|
+
});
|
|
9122
|
+
}
|
|
9123
|
+
const nodeGeoms = [];
|
|
9124
|
+
for (const n of nodes) {
|
|
9125
|
+
const pos = positions.get(n.id);
|
|
9126
|
+
if (!pos) continue;
|
|
9127
|
+
nodeGeoms.push({
|
|
9128
|
+
id: n.id,
|
|
9129
|
+
x: pos.x,
|
|
9130
|
+
y: pos.y,
|
|
9131
|
+
radius: n.radius ?? DEFAULT_NODE_RADIUS,
|
|
9132
|
+
color: n.color ?? NODE_STATE_COLOR[n.state ?? "unvisited"],
|
|
9133
|
+
label: n.label,
|
|
9134
|
+
badge: n.badge
|
|
9135
|
+
});
|
|
9136
|
+
}
|
|
9137
|
+
for (const g of nodeGeoms) {
|
|
9138
|
+
out.push({ type: "circle", id: g.id, x: g.x, y: g.y, radius: g.radius, color: g.color, fill: `${g.color}33` });
|
|
9139
|
+
}
|
|
9140
|
+
const badgeGeoms = [];
|
|
9141
|
+
for (const g of nodeGeoms) {
|
|
9142
|
+
if (!g.badge) continue;
|
|
9143
|
+
const w = Math.min(42, Math.max(18, g.badge.text.length * 6 + 10));
|
|
9144
|
+
badgeGeoms.push({
|
|
9145
|
+
cx: g.x + g.radius * 0.75,
|
|
9146
|
+
cy: g.y - g.radius * 0.75,
|
|
9147
|
+
w,
|
|
9148
|
+
h: 14,
|
|
9149
|
+
// Borderless pill: same color drives both stroke and fill.
|
|
9150
|
+
color: g.badge.color ?? "#1e293b",
|
|
9151
|
+
text: g.badge.text
|
|
9152
|
+
});
|
|
9153
|
+
}
|
|
9154
|
+
for (const b of badgeGeoms) {
|
|
9155
|
+
out.push({ type: "rect", x: b.cx - b.w / 2, y: b.cy - b.h / 2, width: b.w, height: b.h, color: b.color, fill: b.color });
|
|
9156
|
+
}
|
|
9157
|
+
for (const b of badgeGeoms) {
|
|
9158
|
+
out.push({ type: "text", x: b.cx, y: b.cy, text: b.text, fontSize: 9, align: "center", color: "#ffffff" });
|
|
9159
|
+
}
|
|
9160
|
+
for (const g of nodeGeoms) {
|
|
9161
|
+
if (g.label === void 0) continue;
|
|
9162
|
+
out.push({
|
|
9163
|
+
type: "text",
|
|
9164
|
+
x: g.x,
|
|
9165
|
+
y: g.y + g.radius + 14,
|
|
9166
|
+
text: g.label,
|
|
9167
|
+
fontSize: 12,
|
|
9168
|
+
align: "center",
|
|
9169
|
+
color: "#111827"
|
|
9170
|
+
});
|
|
9171
|
+
}
|
|
9172
|
+
out.push(...shapes);
|
|
9173
|
+
return out;
|
|
9174
|
+
}, [nodes, edges, layout, root, width, height, nodeById, shapes]);
|
|
9175
|
+
const handleShapeClick = useCallback(
|
|
9176
|
+
(payload) => {
|
|
9177
|
+
if (payload.type === "circle" && payload.id) {
|
|
9178
|
+
const node = nodeById.get(payload.id);
|
|
9179
|
+
const idx = nodeIndexById.get(payload.id);
|
|
9180
|
+
if (node && idx !== void 0) {
|
|
9181
|
+
onNodeClick?.({ id: node.id, label: node.label, index: idx });
|
|
9182
|
+
}
|
|
9183
|
+
}
|
|
9184
|
+
onShapeClick?.(payload);
|
|
9185
|
+
},
|
|
9186
|
+
[nodeById, nodeIndexById, onNodeClick, onShapeClick]
|
|
9187
|
+
);
|
|
9188
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
9189
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
9190
|
+
/* @__PURE__ */ jsx(
|
|
9191
|
+
LearningCanvas,
|
|
9192
|
+
{
|
|
9193
|
+
width,
|
|
9194
|
+
height,
|
|
9195
|
+
backgroundColor,
|
|
9196
|
+
shapes: derivedShapes,
|
|
9197
|
+
interactive,
|
|
9198
|
+
animate,
|
|
9199
|
+
onShapeClick: onShapeClick || onNodeClick ? handleShapeClick : void 0,
|
|
9200
|
+
isLoading,
|
|
9201
|
+
error
|
|
9202
|
+
}
|
|
9203
|
+
)
|
|
9204
|
+
] }) });
|
|
9205
|
+
};
|
|
9206
|
+
}
|
|
9207
|
+
});
|
|
9208
|
+
var DEFAULT_BAR_COLOR, DEFAULT_CELL_COLOR, DEFAULT_POINTER_COLOR, POINTER_BAND, TOP_PAD, PANEL_FAMILY_ORDER, RANGE_COLOR_DEFAULT, RANGE_FILL_OPACITY, BRACKET_TOP_OFFSET, BRACKET_ROW_H, BRACKET_TICK_H, BRACKET_LABEL_OFFSET, SLOT_EMPTY_FILL, SLOT_EMPTY_STROKE, SLOT_FILLED_STROKE, SLOT_HIGHLIGHT_DEFAULT, SLOT_VALUE_TEXT_COLOR, FRAME_ACTIVE_COLOR, FRAME_RETURNING_COLOR, FRAME_DONE_COLOR, FRAME_LABEL_COLOR, FRAME_DETAIL_COLOR, FRAME_TWO_LINE_MIN_H, BUCKET_INDEX_FILL, BUCKET_INDEX_STROKE, BUCKET_INDEX_TEXT, BUCKET_ENTRY_TEXT, BUCKET_ENTRY_DEFAULT, BUCKET_ENTRY_HIGHLIGHT, BUCKET_ENTRY_PROBING, BUCKET_ENTRY_MIN_W, BUCKET_ENTRY_MAX_W, AXIS_LABEL_COLOR, AXIS_LABEL_FONT_SIZE, CORNER_TEXT_COLOR, CORNER_FONT_SIZE, CORNER_MIN_CELL, CORNER_INSET_X, CORNER_INSET_Y, AUX_PRIMARY_RATIO, AUX_LABEL_BAND, AUX_BASELINE_PAD, AlgorithmCanvas;
|
|
8692
9209
|
var init_AlgorithmCanvas = __esm({
|
|
8693
9210
|
"components/learning/molecules/AlgorithmCanvas.tsx"() {
|
|
8694
9211
|
"use client";
|
|
@@ -8700,6 +9217,43 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8700
9217
|
DEFAULT_POINTER_COLOR = "#dc2626";
|
|
8701
9218
|
POINTER_BAND = 34;
|
|
8702
9219
|
TOP_PAD = 26;
|
|
9220
|
+
PANEL_FAMILY_ORDER = ["bars", "slots", "cells", "buckets", "frames"];
|
|
9221
|
+
RANGE_COLOR_DEFAULT = "#3b82f6";
|
|
9222
|
+
RANGE_FILL_OPACITY = 0.15;
|
|
9223
|
+
BRACKET_TOP_OFFSET = 16;
|
|
9224
|
+
BRACKET_ROW_H = 14;
|
|
9225
|
+
BRACKET_TICK_H = 6;
|
|
9226
|
+
BRACKET_LABEL_OFFSET = 6;
|
|
9227
|
+
SLOT_EMPTY_FILL = "#f1f5f9";
|
|
9228
|
+
SLOT_EMPTY_STROKE = "#cbd5e1";
|
|
9229
|
+
SLOT_FILLED_STROKE = "#9ca3af";
|
|
9230
|
+
SLOT_HIGHLIGHT_DEFAULT = "#f59e0b";
|
|
9231
|
+
SLOT_VALUE_TEXT_COLOR = "#ffffff";
|
|
9232
|
+
FRAME_ACTIVE_COLOR = "#3b82f6";
|
|
9233
|
+
FRAME_RETURNING_COLOR = "#f59e0b";
|
|
9234
|
+
FRAME_DONE_COLOR = "#94a3b8";
|
|
9235
|
+
FRAME_LABEL_COLOR = "#ffffff";
|
|
9236
|
+
FRAME_DETAIL_COLOR = "#e2e8f0";
|
|
9237
|
+
FRAME_TWO_LINE_MIN_H = 22;
|
|
9238
|
+
BUCKET_INDEX_FILL = "#e2e8f0";
|
|
9239
|
+
BUCKET_INDEX_STROKE = "#9ca3af";
|
|
9240
|
+
BUCKET_INDEX_TEXT = "#374151";
|
|
9241
|
+
BUCKET_ENTRY_TEXT = "#ffffff";
|
|
9242
|
+
BUCKET_ENTRY_DEFAULT = "#3b82f6";
|
|
9243
|
+
BUCKET_ENTRY_HIGHLIGHT = "#f59e0b";
|
|
9244
|
+
BUCKET_ENTRY_PROBING = "#38bdf8";
|
|
9245
|
+
BUCKET_ENTRY_MIN_W = 24;
|
|
9246
|
+
BUCKET_ENTRY_MAX_W = 64;
|
|
9247
|
+
AXIS_LABEL_COLOR = "#6b7280";
|
|
9248
|
+
AXIS_LABEL_FONT_SIZE = 10;
|
|
9249
|
+
CORNER_TEXT_COLOR = "#111827";
|
|
9250
|
+
CORNER_FONT_SIZE = 7;
|
|
9251
|
+
CORNER_MIN_CELL = 28;
|
|
9252
|
+
CORNER_INSET_X = 3;
|
|
9253
|
+
CORNER_INSET_Y = 6;
|
|
9254
|
+
AUX_PRIMARY_RATIO = 0.6;
|
|
9255
|
+
AUX_LABEL_BAND = 18;
|
|
9256
|
+
AUX_BASELINE_PAD = 8;
|
|
8703
9257
|
AlgorithmCanvas = ({
|
|
8704
9258
|
className,
|
|
8705
9259
|
width = 600,
|
|
@@ -8709,6 +9263,14 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8709
9263
|
bars = [],
|
|
8710
9264
|
cells = [],
|
|
8711
9265
|
pointers = [],
|
|
9266
|
+
ranges = [],
|
|
9267
|
+
slots = [],
|
|
9268
|
+
slotOrientation = "horizontal",
|
|
9269
|
+
frames = [],
|
|
9270
|
+
buckets = [],
|
|
9271
|
+
auxBars = [],
|
|
9272
|
+
rowLabels = [],
|
|
9273
|
+
colLabels = [],
|
|
8712
9274
|
shapes = [],
|
|
8713
9275
|
interactive = false,
|
|
8714
9276
|
animate = false,
|
|
@@ -8718,12 +9280,35 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8718
9280
|
}) => {
|
|
8719
9281
|
const derivedShapes = useMemo(() => {
|
|
8720
9282
|
const out = [];
|
|
9283
|
+
const presence = {
|
|
9284
|
+
bars: bars.length > 0,
|
|
9285
|
+
slots: slots.length > 0,
|
|
9286
|
+
cells: cells.length > 0,
|
|
9287
|
+
buckets: buckets.length > 0,
|
|
9288
|
+
frames: frames.length > 0
|
|
9289
|
+
};
|
|
9290
|
+
const panelCount = PANEL_FAMILY_ORDER.filter((f3) => presence[f3]).length;
|
|
9291
|
+
const panelHeight = height / Math.max(1, panelCount);
|
|
9292
|
+
const panelY = { bars: 0, slots: 0, cells: 0, buckets: 0, frames: 0 };
|
|
9293
|
+
let compactIndex = 0;
|
|
9294
|
+
PANEL_FAMILY_ORDER.forEach((f3) => {
|
|
9295
|
+
if (presence[f3]) {
|
|
9296
|
+
panelY[f3] = compactIndex * panelHeight;
|
|
9297
|
+
compactIndex += 1;
|
|
9298
|
+
}
|
|
9299
|
+
});
|
|
8721
9300
|
if (bars.length > 0) {
|
|
9301
|
+
const panelYBars = panelY.bars;
|
|
8722
9302
|
const slot = width / bars.length;
|
|
8723
9303
|
const barW = slot * 0.8;
|
|
8724
9304
|
const gap = slot * 0.1;
|
|
8725
|
-
const
|
|
8726
|
-
const
|
|
9305
|
+
const bracketRanges = ranges.filter((r) => r.kind === "bracket");
|
|
9306
|
+
const bracketCount = bracketRanges.length;
|
|
9307
|
+
const bracketHeadroom = bracketCount > 0 ? BRACKET_TOP_OFFSET + bracketCount * BRACKET_ROW_H : 0;
|
|
9308
|
+
const hasAux = auxBars.length > 0;
|
|
9309
|
+
const primaryH = hasAux ? panelHeight * AUX_PRIMARY_RATIO : panelHeight;
|
|
9310
|
+
const baseline = panelYBars + primaryH - POINTER_BAND;
|
|
9311
|
+
const usableH = baseline - (panelYBars + TOP_PAD + bracketHeadroom);
|
|
8727
9312
|
const maxV = Math.max(1, ...bars.map((b) => Number.isFinite(b.value) ? b.value : 0));
|
|
8728
9313
|
bars.forEach((bar, i) => {
|
|
8729
9314
|
const v = Number.isFinite(bar.value) ? bar.value : 0;
|
|
@@ -8753,6 +9338,89 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8753
9338
|
});
|
|
8754
9339
|
}
|
|
8755
9340
|
});
|
|
9341
|
+
ranges.forEach((r) => {
|
|
9342
|
+
const kind = r.kind ?? "fill";
|
|
9343
|
+
if (kind !== "fill") return;
|
|
9344
|
+
const color = r.color ?? RANGE_COLOR_DEFAULT;
|
|
9345
|
+
out.push({
|
|
9346
|
+
type: "rect",
|
|
9347
|
+
x: r.from * slot,
|
|
9348
|
+
y: panelYBars,
|
|
9349
|
+
width: (r.to - r.from + 1) * slot,
|
|
9350
|
+
height: primaryH,
|
|
9351
|
+
color,
|
|
9352
|
+
fill: color,
|
|
9353
|
+
opacity: RANGE_FILL_OPACITY
|
|
9354
|
+
});
|
|
9355
|
+
if (r.label) {
|
|
9356
|
+
out.push({
|
|
9357
|
+
type: "text",
|
|
9358
|
+
x: r.from * slot + 4,
|
|
9359
|
+
// Sits below the bracket block (if any) so fill and bracket labels never collide.
|
|
9360
|
+
y: panelYBars + 10 + bracketHeadroom,
|
|
9361
|
+
text: r.label,
|
|
9362
|
+
color,
|
|
9363
|
+
fontSize: 10,
|
|
9364
|
+
align: "left"
|
|
9365
|
+
});
|
|
9366
|
+
}
|
|
9367
|
+
});
|
|
9368
|
+
bracketRanges.forEach((r, i) => {
|
|
9369
|
+
const bracketY = panelYBars + BRACKET_TOP_OFFSET + i * BRACKET_ROW_H;
|
|
9370
|
+
const x1 = r.from * slot + slot * 0.1;
|
|
9371
|
+
const x2 = (r.to + 1) * slot - slot * 0.1;
|
|
9372
|
+
const color = r.color ?? RANGE_COLOR_DEFAULT;
|
|
9373
|
+
out.push({ type: "line", x1, y1: bracketY, x2, y2: bracketY, color, lineWidth: 2 });
|
|
9374
|
+
out.push({ type: "line", x1, y1: bracketY, x2: x1, y2: bracketY + BRACKET_TICK_H, color, lineWidth: 2 });
|
|
9375
|
+
out.push({ type: "line", x1: x2, y1: bracketY, x2, y2: bracketY + BRACKET_TICK_H, color, lineWidth: 2 });
|
|
9376
|
+
if (r.label) {
|
|
9377
|
+
out.push({
|
|
9378
|
+
type: "text",
|
|
9379
|
+
x: (x1 + x2) / 2,
|
|
9380
|
+
y: bracketY - BRACKET_LABEL_OFFSET,
|
|
9381
|
+
text: r.label,
|
|
9382
|
+
color,
|
|
9383
|
+
fontSize: 10,
|
|
9384
|
+
align: "center"
|
|
9385
|
+
});
|
|
9386
|
+
}
|
|
9387
|
+
});
|
|
9388
|
+
if (hasAux) {
|
|
9389
|
+
const auxH = panelHeight - primaryH;
|
|
9390
|
+
const slot2 = width / auxBars.length;
|
|
9391
|
+
const auxBaseline = panelYBars + primaryH + auxH - AUX_BASELINE_PAD;
|
|
9392
|
+
const auxUsableH = auxBaseline - (panelYBars + primaryH + AUX_LABEL_BAND);
|
|
9393
|
+
const maxAuxV = Math.max(1, ...auxBars.map((b) => Number.isFinite(b.value) ? b.value : 0));
|
|
9394
|
+
auxBars.forEach((bar, i) => {
|
|
9395
|
+
const v = Number.isFinite(bar.value) ? bar.value : 0;
|
|
9396
|
+
const bh = Math.max(0, v / maxAuxV * auxUsableH);
|
|
9397
|
+
const x = i * slot2 + slot2 * 0.1;
|
|
9398
|
+
const w = slot2 * 0.8;
|
|
9399
|
+
const color = bar.color ?? DEFAULT_BAR_COLOR;
|
|
9400
|
+
out.push({
|
|
9401
|
+
type: "rect",
|
|
9402
|
+
id: `auxbar-${i}`,
|
|
9403
|
+
x,
|
|
9404
|
+
y: auxBaseline - bh,
|
|
9405
|
+
width: w,
|
|
9406
|
+
height: bh,
|
|
9407
|
+
color,
|
|
9408
|
+
fill: color
|
|
9409
|
+
});
|
|
9410
|
+
const label = bar.label ?? (auxBars.length <= 24 ? String(v) : void 0);
|
|
9411
|
+
if (label) {
|
|
9412
|
+
out.push({
|
|
9413
|
+
type: "text",
|
|
9414
|
+
x: x + w / 2,
|
|
9415
|
+
y: auxBaseline - bh - 8,
|
|
9416
|
+
text: label,
|
|
9417
|
+
color: "#374151",
|
|
9418
|
+
fontSize: 11,
|
|
9419
|
+
align: "center"
|
|
9420
|
+
});
|
|
9421
|
+
}
|
|
9422
|
+
});
|
|
9423
|
+
}
|
|
8756
9424
|
pointers.forEach((p) => {
|
|
8757
9425
|
if (p.index < 0 || p.index >= bars.length) return;
|
|
8758
9426
|
const cx = p.index * slot + slot / 2;
|
|
@@ -8760,7 +9428,7 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8760
9428
|
out.push({
|
|
8761
9429
|
type: "arrow",
|
|
8762
9430
|
x1: cx,
|
|
8763
|
-
y1:
|
|
9431
|
+
y1: panelYBars + primaryH - 18,
|
|
8764
9432
|
x2: cx,
|
|
8765
9433
|
y2: baseline + 4,
|
|
8766
9434
|
color,
|
|
@@ -8770,7 +9438,7 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8770
9438
|
out.push({
|
|
8771
9439
|
type: "text",
|
|
8772
9440
|
x: cx,
|
|
8773
|
-
y:
|
|
9441
|
+
y: panelYBars + primaryH - 8,
|
|
8774
9442
|
text: p.label,
|
|
8775
9443
|
color,
|
|
8776
9444
|
fontSize: 11,
|
|
@@ -8779,14 +9447,111 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8779
9447
|
}
|
|
8780
9448
|
});
|
|
8781
9449
|
}
|
|
9450
|
+
if (slots.length > 0) {
|
|
9451
|
+
const panelYSlots = panelY.slots;
|
|
9452
|
+
const n = slots.length;
|
|
9453
|
+
const vertical = slotOrientation === "vertical";
|
|
9454
|
+
const vBoxH = panelHeight / n;
|
|
9455
|
+
const vBoxW = Math.min(width * 0.5, 120);
|
|
9456
|
+
const vBoxX = (width - vBoxW) / 2;
|
|
9457
|
+
const hCellW = width / n;
|
|
9458
|
+
const hBoxW = hCellW * 0.82;
|
|
9459
|
+
const hBoxH = Math.min(panelHeight * 0.6, 48);
|
|
9460
|
+
const hBoxY = panelYSlots + (panelHeight - hBoxH) / 2;
|
|
9461
|
+
const slotBox = (i) => vertical ? { x: vBoxX, y: panelYSlots + panelHeight - (i + 1) * vBoxH, width: vBoxW, height: vBoxH } : { x: i * hCellW + (hCellW - hBoxW) / 2, y: hBoxY, width: hBoxW, height: hBoxH };
|
|
9462
|
+
slots.forEach((s, i) => {
|
|
9463
|
+
const box = slotBox(i);
|
|
9464
|
+
const state = s.state ?? "filled";
|
|
9465
|
+
const fill = state === "empty" ? SLOT_EMPTY_FILL : state === "highlight" ? s.color ?? SLOT_HIGHLIGHT_DEFAULT : s.color ?? DEFAULT_BAR_COLOR;
|
|
9466
|
+
const stroke = state === "empty" ? SLOT_EMPTY_STROKE : SLOT_FILLED_STROKE;
|
|
9467
|
+
out.push({
|
|
9468
|
+
type: "rect",
|
|
9469
|
+
id: `slot-${i}`,
|
|
9470
|
+
x: box.x,
|
|
9471
|
+
y: box.y,
|
|
9472
|
+
width: box.width,
|
|
9473
|
+
height: box.height,
|
|
9474
|
+
color: stroke,
|
|
9475
|
+
fill
|
|
9476
|
+
});
|
|
9477
|
+
if (s.value != null && state !== "empty") {
|
|
9478
|
+
out.push({
|
|
9479
|
+
type: "text",
|
|
9480
|
+
x: box.x + box.width / 2,
|
|
9481
|
+
y: box.y + box.height / 2,
|
|
9482
|
+
text: String(s.value),
|
|
9483
|
+
color: SLOT_VALUE_TEXT_COLOR,
|
|
9484
|
+
fontSize: 12,
|
|
9485
|
+
align: "center"
|
|
9486
|
+
});
|
|
9487
|
+
}
|
|
9488
|
+
});
|
|
9489
|
+
if (bars.length === 0) {
|
|
9490
|
+
pointers.forEach((p) => {
|
|
9491
|
+
if (p.index < 0 || p.index >= slots.length) return;
|
|
9492
|
+
const box = slotBox(p.index);
|
|
9493
|
+
const color = p.color ?? DEFAULT_POINTER_COLOR;
|
|
9494
|
+
if (vertical) {
|
|
9495
|
+
const cy = box.y + box.height / 2;
|
|
9496
|
+
out.push({
|
|
9497
|
+
type: "arrow",
|
|
9498
|
+
x1: box.x + box.width + 34,
|
|
9499
|
+
y1: cy,
|
|
9500
|
+
x2: box.x + box.width + 4,
|
|
9501
|
+
y2: cy,
|
|
9502
|
+
color,
|
|
9503
|
+
lineWidth: 2
|
|
9504
|
+
});
|
|
9505
|
+
if (p.label) {
|
|
9506
|
+
out.push({
|
|
9507
|
+
type: "text",
|
|
9508
|
+
x: box.x + box.width + 38,
|
|
9509
|
+
y: cy,
|
|
9510
|
+
text: p.label,
|
|
9511
|
+
color,
|
|
9512
|
+
fontSize: 11,
|
|
9513
|
+
align: "left"
|
|
9514
|
+
});
|
|
9515
|
+
}
|
|
9516
|
+
} else {
|
|
9517
|
+
const cx = box.x + box.width / 2;
|
|
9518
|
+
out.push({
|
|
9519
|
+
type: "arrow",
|
|
9520
|
+
x1: cx,
|
|
9521
|
+
y1: panelYSlots + panelHeight - 18,
|
|
9522
|
+
x2: cx,
|
|
9523
|
+
y2: box.y + box.height + 4,
|
|
9524
|
+
color,
|
|
9525
|
+
lineWidth: 2
|
|
9526
|
+
});
|
|
9527
|
+
if (p.label) {
|
|
9528
|
+
out.push({
|
|
9529
|
+
type: "text",
|
|
9530
|
+
x: cx,
|
|
9531
|
+
y: panelYSlots + panelHeight - 8,
|
|
9532
|
+
text: p.label,
|
|
9533
|
+
color,
|
|
9534
|
+
fontSize: 11,
|
|
9535
|
+
align: "center"
|
|
9536
|
+
});
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9539
|
+
});
|
|
9540
|
+
}
|
|
9541
|
+
}
|
|
8782
9542
|
if (cells.length > 0) {
|
|
9543
|
+
const panelYCells = panelY.cells;
|
|
8783
9544
|
const maxCol = Math.max(0, ...cells.map((c) => c.col)) + 1;
|
|
8784
9545
|
const maxRow = Math.max(0, ...cells.map((c) => c.row)) + 1;
|
|
8785
|
-
const
|
|
8786
|
-
const
|
|
9546
|
+
const colLabelH = colLabels.length > 0 ? 16 : 0;
|
|
9547
|
+
const rowLabelW = rowLabels.length > 0 ? 20 : 0;
|
|
9548
|
+
const gridX0 = rowLabelW;
|
|
9549
|
+
const gridY0 = panelYCells + colLabelH;
|
|
9550
|
+
const cw = (width - rowLabelW) / maxCol;
|
|
9551
|
+
const ch = (panelHeight - colLabelH) / maxRow;
|
|
8787
9552
|
cells.forEach((c, i) => {
|
|
8788
|
-
const x = c.col * cw;
|
|
8789
|
-
const y = c.row * ch;
|
|
9553
|
+
const x = gridX0 + c.col * cw;
|
|
9554
|
+
const y = gridY0 + c.row * ch;
|
|
8790
9555
|
const color = c.color ?? DEFAULT_CELL_COLOR;
|
|
8791
9556
|
out.push({
|
|
8792
9557
|
type: "rect",
|
|
@@ -8810,11 +9575,207 @@ var init_AlgorithmCanvas = __esm({
|
|
|
8810
9575
|
align: "center"
|
|
8811
9576
|
});
|
|
8812
9577
|
}
|
|
9578
|
+
if (c.corner && cw >= CORNER_MIN_CELL && ch >= CORNER_MIN_CELL) {
|
|
9579
|
+
const { tl, tr, bl, br } = c.corner;
|
|
9580
|
+
if (tl) {
|
|
9581
|
+
out.push({
|
|
9582
|
+
type: "text",
|
|
9583
|
+
x: x + CORNER_INSET_X,
|
|
9584
|
+
y: y + CORNER_INSET_Y,
|
|
9585
|
+
text: tl,
|
|
9586
|
+
color: CORNER_TEXT_COLOR,
|
|
9587
|
+
fontSize: CORNER_FONT_SIZE,
|
|
9588
|
+
align: "left"
|
|
9589
|
+
});
|
|
9590
|
+
}
|
|
9591
|
+
if (tr) {
|
|
9592
|
+
out.push({
|
|
9593
|
+
type: "text",
|
|
9594
|
+
x: x + cw - CORNER_INSET_X,
|
|
9595
|
+
y: y + CORNER_INSET_Y,
|
|
9596
|
+
text: tr,
|
|
9597
|
+
color: CORNER_TEXT_COLOR,
|
|
9598
|
+
fontSize: CORNER_FONT_SIZE,
|
|
9599
|
+
align: "right"
|
|
9600
|
+
});
|
|
9601
|
+
}
|
|
9602
|
+
if (bl) {
|
|
9603
|
+
out.push({
|
|
9604
|
+
type: "text",
|
|
9605
|
+
x: x + CORNER_INSET_X,
|
|
9606
|
+
y: y + ch - CORNER_INSET_Y,
|
|
9607
|
+
text: bl,
|
|
9608
|
+
color: CORNER_TEXT_COLOR,
|
|
9609
|
+
fontSize: CORNER_FONT_SIZE,
|
|
9610
|
+
align: "left"
|
|
9611
|
+
});
|
|
9612
|
+
}
|
|
9613
|
+
if (br) {
|
|
9614
|
+
out.push({
|
|
9615
|
+
type: "text",
|
|
9616
|
+
x: x + cw - CORNER_INSET_X,
|
|
9617
|
+
y: y + ch - CORNER_INSET_Y,
|
|
9618
|
+
text: br,
|
|
9619
|
+
color: CORNER_TEXT_COLOR,
|
|
9620
|
+
fontSize: CORNER_FONT_SIZE,
|
|
9621
|
+
align: "right"
|
|
9622
|
+
});
|
|
9623
|
+
}
|
|
9624
|
+
}
|
|
9625
|
+
});
|
|
9626
|
+
colLabels.forEach((l) => {
|
|
9627
|
+
out.push({
|
|
9628
|
+
type: "text",
|
|
9629
|
+
x: gridX0 + l.index * cw + cw / 2,
|
|
9630
|
+
y: panelYCells + colLabelH / 2,
|
|
9631
|
+
text: l.text,
|
|
9632
|
+
color: l.color ?? AXIS_LABEL_COLOR,
|
|
9633
|
+
fontSize: AXIS_LABEL_FONT_SIZE,
|
|
9634
|
+
align: "center"
|
|
9635
|
+
});
|
|
9636
|
+
});
|
|
9637
|
+
rowLabels.forEach((l) => {
|
|
9638
|
+
out.push({
|
|
9639
|
+
type: "text",
|
|
9640
|
+
x: rowLabelW - 6,
|
|
9641
|
+
y: gridY0 + l.index * ch + ch / 2,
|
|
9642
|
+
text: l.text,
|
|
9643
|
+
color: l.color ?? AXIS_LABEL_COLOR,
|
|
9644
|
+
fontSize: AXIS_LABEL_FONT_SIZE,
|
|
9645
|
+
align: "right"
|
|
9646
|
+
});
|
|
9647
|
+
});
|
|
9648
|
+
}
|
|
9649
|
+
if (buckets.length > 0) {
|
|
9650
|
+
const panelYBuckets = panelY.buckets;
|
|
9651
|
+
const bucketCount = Math.max(0, ...buckets.map((b) => b.index)) + 1;
|
|
9652
|
+
const rowH = panelHeight / bucketCount;
|
|
9653
|
+
const indexColW = Math.min(width * 0.12, 40);
|
|
9654
|
+
const maxChainLen = Math.max(1, ...buckets.map((b) => b.entries.length));
|
|
9655
|
+
const entryW = Math.min(BUCKET_ENTRY_MAX_W, Math.max(BUCKET_ENTRY_MIN_W, (width - indexColW - 8) / maxChainLen));
|
|
9656
|
+
const maxVisible = Math.floor((width - indexColW - 4) / entryW);
|
|
9657
|
+
buckets.forEach((b) => {
|
|
9658
|
+
const rowY = panelYBuckets + b.index * rowH;
|
|
9659
|
+
out.push({
|
|
9660
|
+
type: "rect",
|
|
9661
|
+
id: `bucket-index-${b.index}`,
|
|
9662
|
+
x: 2,
|
|
9663
|
+
y: rowY + 2,
|
|
9664
|
+
width: indexColW - 4,
|
|
9665
|
+
height: rowH - 4,
|
|
9666
|
+
color: BUCKET_INDEX_STROKE,
|
|
9667
|
+
fill: BUCKET_INDEX_FILL
|
|
9668
|
+
});
|
|
9669
|
+
out.push({
|
|
9670
|
+
type: "text",
|
|
9671
|
+
x: 2 + (indexColW - 4) / 2,
|
|
9672
|
+
y: rowY + rowH / 2,
|
|
9673
|
+
text: String(b.index),
|
|
9674
|
+
color: BUCKET_INDEX_TEXT,
|
|
9675
|
+
fontSize: 10,
|
|
9676
|
+
align: "center"
|
|
9677
|
+
});
|
|
9678
|
+
const overflow = b.entries.length > maxVisible;
|
|
9679
|
+
const visibleCount = overflow ? Math.max(0, maxVisible - 1) : b.entries.length;
|
|
9680
|
+
for (let j = 0; j < visibleCount; j++) {
|
|
9681
|
+
const entry = b.entries[j];
|
|
9682
|
+
const ex = indexColW + 4 + j * entryW;
|
|
9683
|
+
const state = entry.state ?? "default";
|
|
9684
|
+
const fill = state === "highlight" ? entry.color ?? BUCKET_ENTRY_HIGHLIGHT : state === "probing" ? entry.color ?? BUCKET_ENTRY_PROBING : entry.color ?? BUCKET_ENTRY_DEFAULT;
|
|
9685
|
+
out.push({
|
|
9686
|
+
type: "rect",
|
|
9687
|
+
id: `bucket-${b.index}-${j}`,
|
|
9688
|
+
x: ex,
|
|
9689
|
+
y: rowY + 2,
|
|
9690
|
+
width: entryW - 2,
|
|
9691
|
+
height: rowH - 4,
|
|
9692
|
+
color: fill,
|
|
9693
|
+
fill
|
|
9694
|
+
});
|
|
9695
|
+
if (entryW >= 20 && rowH >= 16) {
|
|
9696
|
+
out.push({
|
|
9697
|
+
type: "text",
|
|
9698
|
+
x: ex + (entryW - 2) / 2,
|
|
9699
|
+
y: rowY + rowH / 2,
|
|
9700
|
+
text: entry.label,
|
|
9701
|
+
color: BUCKET_ENTRY_TEXT,
|
|
9702
|
+
fontSize: 10,
|
|
9703
|
+
align: "center"
|
|
9704
|
+
});
|
|
9705
|
+
}
|
|
9706
|
+
}
|
|
9707
|
+
if (overflow) {
|
|
9708
|
+
const ex = indexColW + 4 + visibleCount * entryW;
|
|
9709
|
+
out.push({
|
|
9710
|
+
type: "rect",
|
|
9711
|
+
id: `bucket-${b.index}-overflow`,
|
|
9712
|
+
x: ex,
|
|
9713
|
+
y: rowY + 2,
|
|
9714
|
+
width: entryW - 2,
|
|
9715
|
+
height: rowH - 4,
|
|
9716
|
+
color: BUCKET_ENTRY_DEFAULT,
|
|
9717
|
+
fill: BUCKET_ENTRY_DEFAULT
|
|
9718
|
+
});
|
|
9719
|
+
out.push({
|
|
9720
|
+
type: "text",
|
|
9721
|
+
x: ex + (entryW - 2) / 2,
|
|
9722
|
+
y: rowY + rowH / 2,
|
|
9723
|
+
text: `+${b.entries.length - visibleCount}`,
|
|
9724
|
+
color: BUCKET_ENTRY_TEXT,
|
|
9725
|
+
fontSize: 10,
|
|
9726
|
+
align: "center"
|
|
9727
|
+
});
|
|
9728
|
+
}
|
|
9729
|
+
});
|
|
9730
|
+
}
|
|
9731
|
+
if (frames.length > 0) {
|
|
9732
|
+
const panelYFrames = panelY.frames;
|
|
9733
|
+
const n = frames.length;
|
|
9734
|
+
const frameH = panelHeight / n;
|
|
9735
|
+
const x = 8;
|
|
9736
|
+
const w = width - 16;
|
|
9737
|
+
frames.forEach((f3, i) => {
|
|
9738
|
+
const y = panelYFrames + panelHeight - (i + 1) * frameH;
|
|
9739
|
+
const state = f3.state ?? "active";
|
|
9740
|
+
const fill = state === "returning" ? f3.color ?? FRAME_RETURNING_COLOR : state === "done" ? f3.color ?? FRAME_DONE_COLOR : f3.color ?? FRAME_ACTIVE_COLOR;
|
|
9741
|
+
out.push({ type: "rect", id: `frame-${i}`, x, y, width: w, height: frameH, color: fill, fill });
|
|
9742
|
+
if (frameH >= FRAME_TWO_LINE_MIN_H) {
|
|
9743
|
+
out.push({
|
|
9744
|
+
type: "text",
|
|
9745
|
+
x: 16,
|
|
9746
|
+
y: y + frameH * 0.35,
|
|
9747
|
+
text: f3.label,
|
|
9748
|
+
color: FRAME_LABEL_COLOR,
|
|
9749
|
+
fontSize: 10,
|
|
9750
|
+
align: "left"
|
|
9751
|
+
});
|
|
9752
|
+
if (f3.detail) {
|
|
9753
|
+
out.push({
|
|
9754
|
+
type: "text",
|
|
9755
|
+
x: 16,
|
|
9756
|
+
y: y + frameH * 0.7,
|
|
9757
|
+
text: f3.detail,
|
|
9758
|
+
color: FRAME_DETAIL_COLOR,
|
|
9759
|
+
fontSize: 10,
|
|
9760
|
+
align: "left"
|
|
9761
|
+
});
|
|
9762
|
+
}
|
|
9763
|
+
} else {
|
|
9764
|
+
out.push({
|
|
9765
|
+
type: "text",
|
|
9766
|
+
x: 16,
|
|
9767
|
+
y: y + frameH / 2,
|
|
9768
|
+
text: f3.label,
|
|
9769
|
+
color: FRAME_LABEL_COLOR,
|
|
9770
|
+
fontSize: 10,
|
|
9771
|
+
align: "left"
|
|
9772
|
+
});
|
|
9773
|
+
}
|
|
8813
9774
|
});
|
|
8814
9775
|
}
|
|
8815
9776
|
out.push(...shapes);
|
|
8816
9777
|
return out;
|
|
8817
|
-
}, [bars, cells, pointers, shapes, width, height]);
|
|
9778
|
+
}, [bars, cells, pointers, ranges, slots, slotOrientation, frames, buckets, auxBars, rowLabels, colLabels, shapes, width, height]);
|
|
8818
9779
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
8819
9780
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
8820
9781
|
/* @__PURE__ */ jsx(
|
|
@@ -9803,23 +10764,422 @@ var init_BehaviorView = __esm({
|
|
|
9803
10764
|
BehaviorView.displayName = "BehaviorView";
|
|
9804
10765
|
}
|
|
9805
10766
|
});
|
|
9806
|
-
|
|
10767
|
+
function LearningScene3D({
|
|
10768
|
+
className,
|
|
10769
|
+
width = 600,
|
|
10770
|
+
height = 400,
|
|
10771
|
+
title,
|
|
10772
|
+
backgroundColor,
|
|
10773
|
+
drawables,
|
|
10774
|
+
camera,
|
|
10775
|
+
lighting,
|
|
10776
|
+
post,
|
|
10777
|
+
showGrid = false,
|
|
10778
|
+
shadows,
|
|
10779
|
+
interactive,
|
|
10780
|
+
isLoading,
|
|
10781
|
+
error,
|
|
10782
|
+
onItemClick
|
|
10783
|
+
}) {
|
|
10784
|
+
const instanceId = useId().replace(/[^a-zA-Z0-9]/g, "");
|
|
10785
|
+
const clickEvent = onItemClick ? `LEARNING_SCENE_3D.ITEM_CLICK.${instanceId}` : void 0;
|
|
10786
|
+
const onItemClickRef = useRef(onItemClick);
|
|
10787
|
+
onItemClickRef.current = onItemClick;
|
|
10788
|
+
useEventListener(`UI:${clickEvent ?? "LEARNING_SCENE_3D.ITEM_CLICK.__disabled"}`, (event) => {
|
|
10789
|
+
const unitId = event.payload?.unitId;
|
|
10790
|
+
if (typeof unitId === "string") onItemClickRef.current?.(unitId);
|
|
10791
|
+
});
|
|
10792
|
+
if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && post?.bloom) {
|
|
10793
|
+
const unknownKeys = Object.keys(post.bloom).filter((k) => !KNOWN_BLOOM_KEYS.has(k));
|
|
10794
|
+
if (unknownKeys.length > 0) {
|
|
10795
|
+
sceneLog.debug("post.bloom has unrecognized keys \u2014 only intensity/threshold/smoothing are read", { unknownKeys });
|
|
10796
|
+
}
|
|
10797
|
+
}
|
|
10798
|
+
const props3d = {
|
|
10799
|
+
drawables,
|
|
10800
|
+
isLoading,
|
|
10801
|
+
error: error ? error.message : null,
|
|
10802
|
+
cameraMode: camera?.mode ?? "perspective",
|
|
10803
|
+
...camera?.zoom !== void 0 ? { zoom: camera.zoom } : {},
|
|
10804
|
+
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
10805
|
+
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
10806
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
10807
|
+
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
10808
|
+
backgroundColor,
|
|
10809
|
+
showGrid,
|
|
10810
|
+
...shadows !== void 0 ? { shadows } : {},
|
|
10811
|
+
...interactive !== void 0 ? { controlsEnabled: interactive } : {},
|
|
10812
|
+
lighting,
|
|
10813
|
+
post,
|
|
10814
|
+
...clickEvent !== void 0 ? { unitClickEvent: clickEvent } : {}
|
|
10815
|
+
};
|
|
10816
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
10817
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
10818
|
+
/* @__PURE__ */ jsx("div", { style: { width, height, display: "flex" }, children: /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(Canvas3DHost, { ...props3d }) }) })
|
|
10819
|
+
] }) });
|
|
10820
|
+
}
|
|
10821
|
+
function meshSphere(id, x, y, z, radius, color, opts) {
|
|
10822
|
+
return {
|
|
10823
|
+
type: "draw-mesh",
|
|
10824
|
+
...id !== void 0 ? { id } : {},
|
|
10825
|
+
shape: opts?.shape ?? "sphere",
|
|
10826
|
+
position: { x, y, z },
|
|
10827
|
+
radius,
|
|
10828
|
+
pivot: "center",
|
|
10829
|
+
segments: opts?.segments ?? 24,
|
|
10830
|
+
material: { color, ...opts?.material },
|
|
10831
|
+
...opts?.opacity !== void 0 ? { opacity: opts.opacity } : {}
|
|
10832
|
+
};
|
|
10833
|
+
}
|
|
10834
|
+
function axisRotation(from, to) {
|
|
10835
|
+
const wx = to[0] - from[0];
|
|
10836
|
+
const wy = to[2] - from[2];
|
|
10837
|
+
const wz = to[1] - from[1];
|
|
10838
|
+
const len = Math.sqrt(wx * wx + wy * wy + wz * wz);
|
|
10839
|
+
const tilt = Math.acos(Math.min(1, Math.max(-1, len > 0 ? wy / len : 1)));
|
|
10840
|
+
return [0, Math.atan2(wz, -wx), tilt];
|
|
10841
|
+
}
|
|
10842
|
+
function segmentLength(from, to) {
|
|
10843
|
+
const dx = to[0] - from[0];
|
|
10844
|
+
const dy = to[1] - from[1];
|
|
10845
|
+
const dz = to[2] - from[2];
|
|
10846
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
10847
|
+
}
|
|
10848
|
+
function cylinderBetween(from, to, radius, color) {
|
|
10849
|
+
const len = segmentLength(from, to);
|
|
10850
|
+
if (len < 1e-6) return null;
|
|
10851
|
+
return {
|
|
10852
|
+
type: "draw-mesh",
|
|
10853
|
+
shape: "cylinder",
|
|
10854
|
+
position: { x: (from[0] + to[0]) / 2, y: (from[1] + to[1]) / 2, z: (from[2] + to[2]) / 2 },
|
|
10855
|
+
radius,
|
|
10856
|
+
height: len,
|
|
10857
|
+
rotation: axisRotation(from, to),
|
|
10858
|
+
pivot: "center",
|
|
10859
|
+
segments: 12,
|
|
10860
|
+
material: { color }
|
|
10861
|
+
};
|
|
10862
|
+
}
|
|
10863
|
+
function arrowBetween(from, to, color, shaftRadius = 0.08, id) {
|
|
10864
|
+
const len = segmentLength(from, to);
|
|
10865
|
+
if (len < 1e-6) return null;
|
|
10866
|
+
const tipLen = Math.min(shaftRadius * 8, len * 0.35);
|
|
10867
|
+
const k = (len - tipLen) / len;
|
|
10868
|
+
const delta = [to[0] - from[0], to[1] - from[1], to[2] - from[2]];
|
|
10869
|
+
const shaftEnd = [delta[0] * k, delta[1] * k, delta[2] * k];
|
|
10870
|
+
const tipStart = shaftEnd;
|
|
10871
|
+
const tipEnd = delta;
|
|
10872
|
+
const shaft = cylinderBetween([0, 0, 0], shaftEnd, shaftRadius, color);
|
|
10873
|
+
const tipLenActual = segmentLength(tipStart, tipEnd);
|
|
10874
|
+
const tip = {
|
|
10875
|
+
type: "draw-mesh",
|
|
10876
|
+
shape: "cone",
|
|
10877
|
+
position: {
|
|
10878
|
+
x: (tipStart[0] + tipEnd[0]) / 2,
|
|
10879
|
+
y: (tipStart[1] + tipEnd[1]) / 2,
|
|
10880
|
+
z: (tipStart[2] + tipEnd[2]) / 2
|
|
10881
|
+
},
|
|
10882
|
+
radius: shaftRadius * 3,
|
|
10883
|
+
height: tipLenActual,
|
|
10884
|
+
rotation: axisRotation(tipStart, tipEnd),
|
|
10885
|
+
pivot: "center",
|
|
10886
|
+
segments: 12,
|
|
10887
|
+
material: { color }
|
|
10888
|
+
};
|
|
10889
|
+
return {
|
|
10890
|
+
type: "draw-group",
|
|
10891
|
+
...id !== void 0 ? { id } : {},
|
|
10892
|
+
position: { x: from[0], y: from[1], z: from[2] },
|
|
10893
|
+
items: tipLenActual < 1e-6 ? shaft ? [shaft] : [] : shaft ? [shaft, tip] : [tip]
|
|
10894
|
+
};
|
|
10895
|
+
}
|
|
10896
|
+
function billboardLabel(text, x, y, z, opts) {
|
|
10897
|
+
return {
|
|
10898
|
+
type: "draw-text",
|
|
10899
|
+
text,
|
|
10900
|
+
position: { x, y, z },
|
|
10901
|
+
color: opts?.color ?? "#111827"
|
|
10902
|
+
};
|
|
10903
|
+
}
|
|
10904
|
+
function labelColorForBackground(backgroundColor) {
|
|
10905
|
+
const m = /^#(?:([0-9a-f]{3})|([0-9a-f]{6}))$/i.exec(backgroundColor ?? "");
|
|
10906
|
+
if (!m) return "#111827";
|
|
10907
|
+
const hex = m[1] !== void 0 ? m[1].split("").map((c) => c + c).join("") : m[2];
|
|
10908
|
+
const r = parseInt(hex.slice(0, 2), 16) / 255;
|
|
10909
|
+
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
10910
|
+
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
10911
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
10912
|
+
return luminance < 0.5 ? "#e5e7eb" : "#111827";
|
|
10913
|
+
}
|
|
10914
|
+
function get3DClickPayload(onShapeClick, idToIndex) {
|
|
10915
|
+
if (!onShapeClick) return void 0;
|
|
10916
|
+
return (id) => onShapeClick({ id, index: idToIndex.get(id) ?? -1 });
|
|
10917
|
+
}
|
|
10918
|
+
function polylineTube(points, radius, color, opts) {
|
|
10919
|
+
const maxSegments = opts?.maxSegments ?? 128;
|
|
10920
|
+
let pts = points;
|
|
10921
|
+
if (pts.length - 1 > maxSegments) {
|
|
10922
|
+
const step = (pts.length - 1) / maxSegments;
|
|
10923
|
+
const kept = [pts[0]];
|
|
10924
|
+
for (let s = 1; s < maxSegments; s++) kept.push(pts[Math.round(s * step)]);
|
|
10925
|
+
kept.push(pts[pts.length - 1]);
|
|
10926
|
+
pts = kept;
|
|
10927
|
+
}
|
|
10928
|
+
const out = [];
|
|
10929
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
10930
|
+
const seg = cylinderBetween(pts[i], pts[i + 1], radius, color);
|
|
10931
|
+
if (seg) out.push(opts?.opacity !== void 0 ? { ...seg, opacity: opts.opacity } : seg);
|
|
10932
|
+
}
|
|
10933
|
+
return out;
|
|
10934
|
+
}
|
|
10935
|
+
function heightFieldMesh(spec) {
|
|
10936
|
+
const { nx, ny, heights, spacing = 1, x = 0, y = 0 } = spec;
|
|
10937
|
+
const flatShading = spec.flatShading ?? true;
|
|
10938
|
+
const vertices = [];
|
|
10939
|
+
for (let iy = 0; iy < ny; iy++) {
|
|
10940
|
+
for (let ix = 0; ix < nx; ix++) {
|
|
10941
|
+
vertices.push([
|
|
10942
|
+
x + (ix - (nx - 1) / 2) * spacing,
|
|
10943
|
+
y + (iy - (ny - 1) / 2) * spacing,
|
|
10944
|
+
heights[iy * nx + ix] ?? 0
|
|
10945
|
+
]);
|
|
10946
|
+
}
|
|
10947
|
+
}
|
|
10948
|
+
const bands = [...spec.bands ?? []].sort((a, b) => (a.min ?? -Infinity) - (b.min ?? -Infinity));
|
|
10949
|
+
const facesByBand = /* @__PURE__ */ new Map();
|
|
10950
|
+
for (let iy = 0; iy < ny - 1; iy++) {
|
|
10951
|
+
for (let ix = 0; ix < nx - 1; ix++) {
|
|
10952
|
+
const v00 = iy * nx + ix;
|
|
10953
|
+
const v10 = iy * nx + ix + 1;
|
|
10954
|
+
const v01 = (iy + 1) * nx + ix;
|
|
10955
|
+
const v11 = (iy + 1) * nx + ix + 1;
|
|
10956
|
+
for (const face of [[v00, v10, v01], [v10, v11, v01]]) {
|
|
10957
|
+
const centroid = (vertices[face[0]][2] + vertices[face[1]][2] + vertices[face[2]][2]) / 3;
|
|
10958
|
+
let band = null;
|
|
10959
|
+
for (const b of bands) {
|
|
10960
|
+
if ((b.min ?? -Infinity) <= centroid) band = b;
|
|
10961
|
+
}
|
|
10962
|
+
const key = bands.length > 0 ? band : null;
|
|
10963
|
+
const list = facesByBand.get(key) ?? [];
|
|
10964
|
+
list.push(face);
|
|
10965
|
+
facesByBand.set(key, list);
|
|
10966
|
+
}
|
|
10967
|
+
}
|
|
10968
|
+
}
|
|
10969
|
+
const out = [];
|
|
10970
|
+
for (const [band, faces] of facesByBand) {
|
|
10971
|
+
if (faces.length === 0) continue;
|
|
10972
|
+
out.push({
|
|
10973
|
+
type: "draw-mesh",
|
|
10974
|
+
shape: "polyhedron",
|
|
10975
|
+
position: { x: 0, y: 0, z: 0 },
|
|
10976
|
+
vertices,
|
|
10977
|
+
faces,
|
|
10978
|
+
pivot: "center",
|
|
10979
|
+
material: { color: band?.color ?? spec.color ?? "#64748b", flatShading, side: "double" },
|
|
10980
|
+
...spec.opacity !== void 0 ? { opacity: spec.opacity } : {}
|
|
10981
|
+
});
|
|
10982
|
+
}
|
|
10983
|
+
return out;
|
|
10984
|
+
}
|
|
10985
|
+
function arrowField(vectors, opts) {
|
|
10986
|
+
const scale = opts?.scale ?? 1;
|
|
10987
|
+
const out = [];
|
|
10988
|
+
for (const v of vectors) {
|
|
10989
|
+
const to = [
|
|
10990
|
+
v.from[0] + v.delta[0] * scale,
|
|
10991
|
+
v.from[1] + v.delta[1] * scale,
|
|
10992
|
+
v.from[2] + v.delta[2] * scale
|
|
10993
|
+
];
|
|
10994
|
+
const arrow = arrowBetween(v.from, to, v.color ?? "#dc2626", v.width, v.id);
|
|
10995
|
+
if (arrow) out.push(arrow);
|
|
10996
|
+
if (v.label) out.push(billboardLabel(v.label, to[0], to[1], to[2], { color: opts?.labelColor }));
|
|
10997
|
+
}
|
|
10998
|
+
return out;
|
|
10999
|
+
}
|
|
11000
|
+
function helixDrawables(spec, opts) {
|
|
11001
|
+
const count = spec.count ?? spec.rungs?.length ?? 0;
|
|
11002
|
+
const rungs = Array.from({ length: count }, (_, i) => spec.rungs?.[i] ?? {});
|
|
11003
|
+
const radius = spec.radius ?? 1;
|
|
11004
|
+
const rise = spec.rise ?? 0.34;
|
|
11005
|
+
const twistRad = (spec.twistDeg ?? 36) * (Math.PI / 180);
|
|
11006
|
+
const strandAColor = spec.strandAColor ?? "#38bdf8";
|
|
11007
|
+
const strandBColor = spec.strandBColor ?? "#fb923c";
|
|
11008
|
+
const backboneRadius = spec.backboneRadius ?? 0.16;
|
|
11009
|
+
const rungRadius = spec.rungRadius ?? 0.12;
|
|
11010
|
+
const cx = spec.x ?? 0;
|
|
11011
|
+
const cy = spec.y ?? 0;
|
|
11012
|
+
const cz = spec.z ?? 0;
|
|
11013
|
+
const unwoundCount = spec.unwoundCount ?? 0;
|
|
11014
|
+
const unwindSpread = spec.unwindSpread ?? 1.8;
|
|
11015
|
+
const strandA = [];
|
|
11016
|
+
const strandB = [];
|
|
11017
|
+
for (let i = 0; i < count; i++) {
|
|
11018
|
+
const yi = cy + (i - (count - 1) / 2) * rise;
|
|
11019
|
+
const theta = i * twistRad;
|
|
11020
|
+
const s = i < unwoundCount ? unwindSpread : 1;
|
|
11021
|
+
strandA.push([cx + s * radius * Math.cos(theta), yi, cz + s * radius * Math.sin(theta)]);
|
|
11022
|
+
strandB.push([cx + s * radius * Math.cos(theta + Math.PI), yi, cz + s * radius * Math.sin(theta + Math.PI)]);
|
|
11023
|
+
}
|
|
11024
|
+
const out = [];
|
|
11025
|
+
for (let i = 0; i < count; i++) {
|
|
11026
|
+
out.push(meshSphere(`hx-a-${i}`, strandA[i][0], strandA[i][1], strandA[i][2], backboneRadius, strandAColor));
|
|
11027
|
+
out.push(meshSphere(`hx-b-${i}`, strandB[i][0], strandB[i][1], strandB[i][2], backboneRadius, strandBColor));
|
|
11028
|
+
if (i > 0) {
|
|
11029
|
+
const segA = cylinderBetween(strandA[i - 1], strandA[i], backboneRadius, strandAColor);
|
|
11030
|
+
if (segA) out.push(segA);
|
|
11031
|
+
const segB = cylinderBetween(strandB[i - 1], strandB[i], backboneRadius, strandBColor);
|
|
11032
|
+
if (segB) out.push(segB);
|
|
11033
|
+
}
|
|
11034
|
+
const rung = rungs[i];
|
|
11035
|
+
const rungColor = rung.color ?? "#94a3b8";
|
|
11036
|
+
const rod = cylinderBetween(strandA[i], strandB[i], rungRadius, rungColor);
|
|
11037
|
+
if (rod) out.push(rod);
|
|
11038
|
+
const mid = [
|
|
11039
|
+
(strandA[i][0] + strandB[i][0]) / 2,
|
|
11040
|
+
(strandA[i][1] + strandB[i][1]) / 2,
|
|
11041
|
+
(strandA[i][2] + strandB[i][2]) / 2
|
|
11042
|
+
];
|
|
11043
|
+
const markerRadius = rung.radius ?? rungRadius;
|
|
11044
|
+
out.push(meshSphere(rung.id, mid[0], mid[1], mid[2], markerRadius, rungColor));
|
|
11045
|
+
if (rung.label) out.push(billboardLabel(rung.label, mid[0], mid[1], mid[2] + markerRadius, { color: opts?.labelColor }));
|
|
11046
|
+
}
|
|
11047
|
+
return out;
|
|
11048
|
+
}
|
|
11049
|
+
function latticeDrawables(spec, opts) {
|
|
11050
|
+
const nx = spec.nx ?? 2;
|
|
11051
|
+
const ny = spec.ny ?? 2;
|
|
11052
|
+
const nz = spec.nz ?? 2;
|
|
11053
|
+
const latticeConstant = spec.latticeConstant ?? 2;
|
|
11054
|
+
const bondRadius = spec.bondRadius ?? 0.06;
|
|
11055
|
+
const highlightCell = spec.highlightCell ?? false;
|
|
11056
|
+
const dimColor = spec.dimColor ?? "#475569";
|
|
11057
|
+
const showLabels = spec.showLabels ?? false;
|
|
11058
|
+
const selectedColor = spec.selectedColor ?? "#f59e0b";
|
|
11059
|
+
const posByKey = /* @__PURE__ */ new Map();
|
|
11060
|
+
const inCellByKey = /* @__PURE__ */ new Map();
|
|
11061
|
+
const out = [];
|
|
11062
|
+
for (const site of spec.basis) {
|
|
11063
|
+
const snx = site.xEdge ? nx + 1 : nx;
|
|
11064
|
+
const sny = site.yEdge ? ny + 1 : ny;
|
|
11065
|
+
const snz = site.zEdge ? nz + 1 : nz;
|
|
11066
|
+
for (let i = 0; i < snx; i++) {
|
|
11067
|
+
for (let j = 0; j < sny; j++) {
|
|
11068
|
+
for (let k = 0; k < snz; k++) {
|
|
11069
|
+
const key = `${site.key}-${i}-${j}-${k}`;
|
|
11070
|
+
const inCell = i + site.dx <= 1 && j + site.dy <= 1 && k + site.dz <= 1;
|
|
11071
|
+
const pos = [
|
|
11072
|
+
(i + site.dx) * latticeConstant - nx * latticeConstant / 2,
|
|
11073
|
+
(j + site.dy) * latticeConstant - ny * latticeConstant / 2,
|
|
11074
|
+
(k + site.dz) * latticeConstant - nz * latticeConstant / 2
|
|
11075
|
+
];
|
|
11076
|
+
posByKey.set(key, pos);
|
|
11077
|
+
inCellByKey.set(key, inCell);
|
|
11078
|
+
const isSelected = spec.selectedId === `lat-${key}`;
|
|
11079
|
+
const color = isSelected ? selectedColor : highlightCell && !inCell ? dimColor : site.color ?? "#2563eb";
|
|
11080
|
+
const radius = (site.radius ?? 0.3) * (isSelected ? 1.4 : 1);
|
|
11081
|
+
out.push(meshSphere(`lat-${key}`, pos[0], pos[1], pos[2], radius, color));
|
|
11082
|
+
if (showLabels && site.element) {
|
|
11083
|
+
out.push(billboardLabel(site.element, pos[0], pos[1], pos[2] + radius, { color: opts?.labelColor }));
|
|
11084
|
+
}
|
|
11085
|
+
}
|
|
11086
|
+
}
|
|
11087
|
+
}
|
|
11088
|
+
}
|
|
11089
|
+
const basisByKey = new Map(spec.basis.map((s) => [s.key, s]));
|
|
11090
|
+
for (const bond of spec.bonds ?? []) {
|
|
11091
|
+
const fromSite = basisByKey.get(bond.from);
|
|
11092
|
+
const toSite = basisByKey.get(bond.to);
|
|
11093
|
+
if (!fromSite || !toSite) continue;
|
|
11094
|
+
const fnx = fromSite.xEdge ? nx + 1 : nx;
|
|
11095
|
+
const fny = fromSite.yEdge ? ny + 1 : ny;
|
|
11096
|
+
const fnz = fromSite.zEdge ? nz + 1 : nz;
|
|
11097
|
+
const tnx = toSite.xEdge ? nx + 1 : nx;
|
|
11098
|
+
const tny = toSite.yEdge ? ny + 1 : ny;
|
|
11099
|
+
const tnz = toSite.zEdge ? nz + 1 : nz;
|
|
11100
|
+
const bdx = bond.dx ?? 0;
|
|
11101
|
+
const bdy = bond.dy ?? 0;
|
|
11102
|
+
const bdz = bond.dz ?? 0;
|
|
11103
|
+
for (let i = 0; i < fnx; i++) {
|
|
11104
|
+
for (let j = 0; j < fny; j++) {
|
|
11105
|
+
for (let k = 0; k < fnz; k++) {
|
|
11106
|
+
const ti = i + bdx;
|
|
11107
|
+
const tj = j + bdy;
|
|
11108
|
+
const tk = k + bdz;
|
|
11109
|
+
if (ti < 0 || ti >= tnx || tj < 0 || tj >= tny || tk < 0 || tk >= tnz) continue;
|
|
11110
|
+
const fromKey = `${fromSite.key}-${i}-${j}-${k}`;
|
|
11111
|
+
const toKey = `${toSite.key}-${ti}-${tj}-${tk}`;
|
|
11112
|
+
const fromPos = posByKey.get(fromKey);
|
|
11113
|
+
const toPos = posByKey.get(toKey);
|
|
11114
|
+
if (!fromPos || !toPos) continue;
|
|
11115
|
+
const dimmed = highlightCell && !(inCellByKey.get(fromKey) && inCellByKey.get(toKey));
|
|
11116
|
+
const seg = cylinderBetween(fromPos, toPos, bondRadius, dimmed ? dimColor : bond.color ?? "#6b7280");
|
|
11117
|
+
if (seg) out.push(seg);
|
|
11118
|
+
}
|
|
11119
|
+
}
|
|
11120
|
+
}
|
|
11121
|
+
}
|
|
11122
|
+
return out;
|
|
11123
|
+
}
|
|
11124
|
+
var sceneLog, KNOWN_BLOOM_KEYS, Canvas3DHost;
|
|
11125
|
+
var init_learningScene3D = __esm({
|
|
11126
|
+
"components/learning/molecules/learningScene3D.tsx"() {
|
|
11127
|
+
"use client";
|
|
11128
|
+
init_atoms();
|
|
11129
|
+
init_Stack();
|
|
11130
|
+
init_useEventBus();
|
|
11131
|
+
sceneLog = createLogger("almadar:ui:learning-scene-3d");
|
|
11132
|
+
KNOWN_BLOOM_KEYS = /* @__PURE__ */ new Set(["intensity", "threshold", "smoothing"]);
|
|
11133
|
+
Canvas3DHost = lazy(
|
|
11134
|
+
() => import('@almadar/ui/components/molecules/game/three').then((m) => ({ default: m.Canvas3DHost }))
|
|
11135
|
+
);
|
|
11136
|
+
LearningScene3D.displayName = "LearningScene3D";
|
|
11137
|
+
}
|
|
11138
|
+
});
|
|
11139
|
+
var biologyLog, BIO_BAND_COLORS, BIO_STAGE_FILL, BIO_STAGE_TEXT, BiologyCanvas;
|
|
9807
11140
|
var init_BiologyCanvas = __esm({
|
|
9808
11141
|
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
9809
11142
|
"use client";
|
|
9810
11143
|
init_atoms();
|
|
9811
11144
|
init_Stack();
|
|
9812
11145
|
init_LearningCanvas();
|
|
11146
|
+
init_learningScene3D();
|
|
11147
|
+
biologyLog = createLogger("almadar:ui:biology-canvas");
|
|
11148
|
+
BIO_BAND_COLORS = ["#dcfce7", "#fef9c3", "#fee2e2", "#e0e7ff"];
|
|
11149
|
+
BIO_STAGE_FILL = {
|
|
11150
|
+
pending: "#e2e8f0",
|
|
11151
|
+
active: "#3b82f6",
|
|
11152
|
+
done: "#94a3b8"
|
|
11153
|
+
};
|
|
11154
|
+
BIO_STAGE_TEXT = {
|
|
11155
|
+
pending: "#64748b",
|
|
11156
|
+
active: "#ffffff",
|
|
11157
|
+
done: "#ffffff"
|
|
11158
|
+
};
|
|
9813
11159
|
BiologyCanvas = ({
|
|
9814
11160
|
className,
|
|
9815
11161
|
width = 600,
|
|
9816
11162
|
height = 400,
|
|
9817
11163
|
title,
|
|
9818
11164
|
backgroundColor,
|
|
11165
|
+
mode = "2d",
|
|
11166
|
+
camera,
|
|
11167
|
+
lighting,
|
|
11168
|
+
post,
|
|
9819
11169
|
nodes = [],
|
|
9820
11170
|
edges = [],
|
|
11171
|
+
compartments = [],
|
|
11172
|
+
bands = [],
|
|
11173
|
+
stages = [],
|
|
11174
|
+
stageStyle = "timeline",
|
|
11175
|
+
helix,
|
|
11176
|
+
helix3d,
|
|
9821
11177
|
shapes = [],
|
|
9822
|
-
|
|
11178
|
+
readouts,
|
|
11179
|
+
traces,
|
|
11180
|
+
showGrid,
|
|
11181
|
+
shadows,
|
|
11182
|
+
interactive,
|
|
9823
11183
|
animate = false,
|
|
9824
11184
|
onShapeClick,
|
|
9825
11185
|
isLoading,
|
|
@@ -9831,19 +11191,148 @@ var init_BiologyCanvas = __esm({
|
|
|
9831
11191
|
for (const n of nodes) {
|
|
9832
11192
|
if (n.id) nodeById.set(n.id, n);
|
|
9833
11193
|
}
|
|
11194
|
+
const bandCount = bands.length;
|
|
11195
|
+
for (let i = 0; i < bandCount; i++) {
|
|
11196
|
+
const band = bands[i];
|
|
11197
|
+
const bandColor = band.color ?? BIO_BAND_COLORS[i % BIO_BAND_COLORS.length];
|
|
11198
|
+
const bandY = i * height / bandCount;
|
|
11199
|
+
const bandH = height / bandCount;
|
|
11200
|
+
out.push({
|
|
11201
|
+
type: "rect",
|
|
11202
|
+
x: 0,
|
|
11203
|
+
y: bandY,
|
|
11204
|
+
width,
|
|
11205
|
+
height: bandH,
|
|
11206
|
+
color: bandColor,
|
|
11207
|
+
fill: bandColor,
|
|
11208
|
+
opacity: 0.45
|
|
11209
|
+
});
|
|
11210
|
+
if (band.label) {
|
|
11211
|
+
out.push({
|
|
11212
|
+
type: "text",
|
|
11213
|
+
x: 8,
|
|
11214
|
+
y: bandY + 14,
|
|
11215
|
+
text: band.label,
|
|
11216
|
+
color: "#6b7280",
|
|
11217
|
+
fontSize: 10
|
|
11218
|
+
});
|
|
11219
|
+
}
|
|
11220
|
+
}
|
|
11221
|
+
for (const c of compartments) {
|
|
11222
|
+
const color = c.color ?? "#16a34a";
|
|
11223
|
+
out.push({
|
|
11224
|
+
type: "ellipse",
|
|
11225
|
+
x: c.x,
|
|
11226
|
+
y: c.y,
|
|
11227
|
+
width: c.width,
|
|
11228
|
+
height: c.height,
|
|
11229
|
+
color,
|
|
11230
|
+
fill: c.fill ?? `${color}1A`,
|
|
11231
|
+
lineWidth: c.lineWidth ?? 2,
|
|
11232
|
+
...c.dash ? { dash: c.dash } : {}
|
|
11233
|
+
});
|
|
11234
|
+
if (c.label) {
|
|
11235
|
+
out.push({
|
|
11236
|
+
type: "text",
|
|
11237
|
+
x: c.x,
|
|
11238
|
+
y: c.y - c.height / 2 + 14,
|
|
11239
|
+
text: c.label,
|
|
11240
|
+
color: "#111827",
|
|
11241
|
+
fontSize: 11,
|
|
11242
|
+
align: "center"
|
|
11243
|
+
});
|
|
11244
|
+
}
|
|
11245
|
+
}
|
|
11246
|
+
if (helix) {
|
|
11247
|
+
const hx = helix.x ?? 24;
|
|
11248
|
+
const hy = helix.y ?? height * 0.25;
|
|
11249
|
+
const hw = helix.width ?? width - 48;
|
|
11250
|
+
const hh = helix.height ?? height * 0.5;
|
|
11251
|
+
const rungs = helix.rungs;
|
|
11252
|
+
const n = rungs.length;
|
|
11253
|
+
const cy = hy + hh / 2;
|
|
11254
|
+
const colorA = helix.colorA ?? "#2563eb";
|
|
11255
|
+
const colorB = helix.colorB ?? "#dc2626";
|
|
11256
|
+
const rungColor = helix.rungColor ?? "#94a3b8";
|
|
11257
|
+
const fork = helix.fork ?? 0;
|
|
11258
|
+
const maxSep = Math.min(hh - 8, 96);
|
|
11259
|
+
const strandA = [];
|
|
11260
|
+
const strandB = [];
|
|
11261
|
+
const rungGeoms = [];
|
|
11262
|
+
for (let i = 0; i < n; i++) {
|
|
11263
|
+
const rx = hx + (i + 0.5) * hw / n;
|
|
11264
|
+
const t = (i + 0.5) / n;
|
|
11265
|
+
const paired = t >= fork;
|
|
11266
|
+
const sep = paired ? 28 : 28 + (maxSep - 28) * ((fork - t) / fork);
|
|
11267
|
+
strandA.push({ x: rx, y: cy - sep / 2 });
|
|
11268
|
+
strandB.push({ x: rx, y: cy + sep / 2 });
|
|
11269
|
+
rungGeoms.push({ rx, sep, rung: rungs[i], paired });
|
|
11270
|
+
}
|
|
11271
|
+
for (let i = 1; i < n; i++) {
|
|
11272
|
+
out.push({ type: "line", x1: strandA[i - 1].x, y1: strandA[i - 1].y, x2: strandA[i].x, y2: strandA[i].y, color: colorA, lineWidth: 3 });
|
|
11273
|
+
}
|
|
11274
|
+
for (let i = 1; i < n; i++) {
|
|
11275
|
+
out.push({ type: "line", x1: strandB[i - 1].x, y1: strandB[i - 1].y, x2: strandB[i].x, y2: strandB[i].y, color: colorB, lineWidth: 3 });
|
|
11276
|
+
}
|
|
11277
|
+
for (const g of rungGeoms) {
|
|
11278
|
+
const rColor = g.rung.color ?? (g.rung.state === "new" ? "#16a34a" : rungColor);
|
|
11279
|
+
const topY = cy - g.sep / 2;
|
|
11280
|
+
const bottomY = cy + g.sep / 2;
|
|
11281
|
+
if (g.paired) {
|
|
11282
|
+
out.push({ type: "line", x1: g.rx, y1: topY, x2: g.rx, y2: bottomY, color: rColor });
|
|
11283
|
+
if (g.rung.a) {
|
|
11284
|
+
out.push({ type: "text", x: g.rx, y: cy - g.sep / 4, text: g.rung.a, fontSize: 9, align: "center", color: "#374151" });
|
|
11285
|
+
}
|
|
11286
|
+
if (g.rung.b) {
|
|
11287
|
+
out.push({ type: "text", x: g.rx, y: cy + g.sep / 4, text: g.rung.b, fontSize: 9, align: "center", color: "#374151" });
|
|
11288
|
+
}
|
|
11289
|
+
} else {
|
|
11290
|
+
const stubTopY = topY + 8;
|
|
11291
|
+
const stubBottomY = bottomY - 8;
|
|
11292
|
+
out.push({ type: "line", x1: g.rx, y1: topY, x2: g.rx, y2: stubTopY, color: rColor });
|
|
11293
|
+
out.push({ type: "line", x1: g.rx, y1: bottomY, x2: g.rx, y2: stubBottomY, color: rColor });
|
|
11294
|
+
if (g.rung.a) {
|
|
11295
|
+
out.push({ type: "text", x: g.rx, y: stubTopY + 6, text: g.rung.a, fontSize: 9, align: "center", color: "#374151" });
|
|
11296
|
+
}
|
|
11297
|
+
if (g.rung.b) {
|
|
11298
|
+
out.push({ type: "text", x: g.rx, y: stubBottomY - 6, text: g.rung.b, fontSize: 9, align: "center", color: "#374151" });
|
|
11299
|
+
}
|
|
11300
|
+
}
|
|
11301
|
+
}
|
|
11302
|
+
}
|
|
9834
11303
|
for (const e of edges) {
|
|
9835
11304
|
const a = nodeById.get(e.from);
|
|
9836
11305
|
const b = nodeById.get(e.to);
|
|
9837
11306
|
if (!a || !b) continue;
|
|
9838
|
-
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
11307
|
+
const color = e.color ?? "#9ca3af";
|
|
11308
|
+
if (e.directed) {
|
|
11309
|
+
const rA = a.radius ?? 16;
|
|
11310
|
+
const rB = b.radius ?? 16;
|
|
11311
|
+
const dx = b.x - a.x;
|
|
11312
|
+
const dy = b.y - a.y;
|
|
11313
|
+
const dist = Math.max(1e-6, Math.hypot(dx, dy));
|
|
11314
|
+
const ux = dx / dist;
|
|
11315
|
+
const uy = dy / dist;
|
|
11316
|
+
out.push({
|
|
11317
|
+
type: "arrow",
|
|
11318
|
+
x1: a.x + ux * rA,
|
|
11319
|
+
y1: a.y + uy * rA,
|
|
11320
|
+
x2: b.x - ux * rB,
|
|
11321
|
+
y2: b.y - uy * rB,
|
|
11322
|
+
color,
|
|
11323
|
+
lineWidth: 2
|
|
11324
|
+
});
|
|
11325
|
+
} else {
|
|
11326
|
+
out.push({
|
|
11327
|
+
type: "line",
|
|
11328
|
+
x1: a.x,
|
|
11329
|
+
y1: a.y,
|
|
11330
|
+
x2: b.x,
|
|
11331
|
+
y2: b.y,
|
|
11332
|
+
color,
|
|
11333
|
+
lineWidth: 2
|
|
11334
|
+
});
|
|
11335
|
+
}
|
|
9847
11336
|
if (e.label) {
|
|
9848
11337
|
out.push({
|
|
9849
11338
|
type: "text",
|
|
@@ -9856,6 +11345,8 @@ var init_BiologyCanvas = __esm({
|
|
|
9856
11345
|
}
|
|
9857
11346
|
}
|
|
9858
11347
|
for (const n of nodes) {
|
|
11348
|
+
const state = n.state ?? "default";
|
|
11349
|
+
const muted = state === "muted";
|
|
9859
11350
|
out.push({
|
|
9860
11351
|
type: "circle",
|
|
9861
11352
|
x: n.x,
|
|
@@ -9863,23 +11354,197 @@ var init_BiologyCanvas = __esm({
|
|
|
9863
11354
|
radius: n.radius ?? 16,
|
|
9864
11355
|
color: n.color ?? "#16a34a",
|
|
9865
11356
|
fill: `${n.color ?? "#16a34a"}33`,
|
|
9866
|
-
id: n.id
|
|
11357
|
+
id: n.id,
|
|
11358
|
+
...muted ? { opacity: 0.35 } : {}
|
|
9867
11359
|
});
|
|
11360
|
+
if (state === "highlight") {
|
|
11361
|
+
out.push({
|
|
11362
|
+
type: "circle",
|
|
11363
|
+
x: n.x,
|
|
11364
|
+
y: n.y,
|
|
11365
|
+
radius: (n.radius ?? 16) + 4,
|
|
11366
|
+
color: "#f59e0b",
|
|
11367
|
+
lineWidth: 2
|
|
11368
|
+
});
|
|
11369
|
+
}
|
|
9868
11370
|
if (n.label) {
|
|
9869
11371
|
out.push({
|
|
9870
11372
|
type: "text",
|
|
9871
11373
|
x: n.x,
|
|
9872
11374
|
y: n.y + (n.radius ?? 16) + 14,
|
|
9873
11375
|
text: n.label,
|
|
11376
|
+
...muted ? { opacity: 0.35 } : {},
|
|
9874
11377
|
color: "#111827",
|
|
9875
11378
|
fontSize: 12,
|
|
9876
11379
|
align: "center"
|
|
9877
11380
|
});
|
|
9878
11381
|
}
|
|
9879
11382
|
}
|
|
11383
|
+
const stageCount = stages.length;
|
|
11384
|
+
if (stageCount > 0) {
|
|
11385
|
+
if (stageStyle === "ring") {
|
|
11386
|
+
const cx = width / 2;
|
|
11387
|
+
const cy = height / 2;
|
|
11388
|
+
const R = Math.min(width, height) / 2 - 48;
|
|
11389
|
+
const ringPoints = [];
|
|
11390
|
+
for (let i = 0; i < stageCount; i++) {
|
|
11391
|
+
const angleRad = (-90 + 360 * i / stageCount) * Math.PI / 180;
|
|
11392
|
+
ringPoints.push({ x: cx + R * Math.cos(angleRad), y: cy + R * Math.sin(angleRad) });
|
|
11393
|
+
}
|
|
11394
|
+
if (stageCount >= 2) {
|
|
11395
|
+
for (let i = 0; i < stageCount - 1; i++) {
|
|
11396
|
+
const p1 = ringPoints[i];
|
|
11397
|
+
const p2 = ringPoints[i + 1];
|
|
11398
|
+
const dx = p2.x - p1.x;
|
|
11399
|
+
const dy = p2.y - p1.y;
|
|
11400
|
+
const dist = Math.max(1e-6, Math.hypot(dx, dy));
|
|
11401
|
+
const ux = dx / dist;
|
|
11402
|
+
const uy = dy / dist;
|
|
11403
|
+
out.push({
|
|
11404
|
+
type: "arrow",
|
|
11405
|
+
x1: p1.x + ux * 46,
|
|
11406
|
+
y1: p1.y + uy * 46,
|
|
11407
|
+
x2: p2.x - ux * 46,
|
|
11408
|
+
y2: p2.y - uy * 46,
|
|
11409
|
+
color: "#94a3b8"
|
|
11410
|
+
});
|
|
11411
|
+
}
|
|
11412
|
+
}
|
|
11413
|
+
for (let i = 0; i < stageCount; i++) {
|
|
11414
|
+
const stage = stages[i];
|
|
11415
|
+
const state = stage.state ?? "pending";
|
|
11416
|
+
const fill = stage.color ?? BIO_STAGE_FILL[state];
|
|
11417
|
+
const w = Math.max(26, Math.min(84, stage.label.length * 6 + 10));
|
|
11418
|
+
const h = 18;
|
|
11419
|
+
const p = ringPoints[i];
|
|
11420
|
+
out.push({ type: "rect", x: p.x - w / 2, y: p.y - h / 2, width: w, height: h, color: fill, fill });
|
|
11421
|
+
out.push({ type: "text", x: p.x, y: p.y, text: stage.label, color: BIO_STAGE_TEXT[state], fontSize: 10, align: "center" });
|
|
11422
|
+
}
|
|
11423
|
+
} else {
|
|
11424
|
+
const stripY = height - 32;
|
|
11425
|
+
const slotW = (width - 16) / stageCount;
|
|
11426
|
+
const chipGeoms = [];
|
|
11427
|
+
for (let i = 0; i < stageCount; i++) {
|
|
11428
|
+
chipGeoms.push({ x: 8 + i * slotW + 5, w: slotW - 10 });
|
|
11429
|
+
}
|
|
11430
|
+
for (let i = 0; i < stageCount - 1; i++) {
|
|
11431
|
+
const midY = stripY + 13;
|
|
11432
|
+
out.push({
|
|
11433
|
+
type: "arrow",
|
|
11434
|
+
x1: chipGeoms[i].x + chipGeoms[i].w,
|
|
11435
|
+
y1: midY,
|
|
11436
|
+
x2: chipGeoms[i + 1].x,
|
|
11437
|
+
y2: midY,
|
|
11438
|
+
color: "#94a3b8"
|
|
11439
|
+
});
|
|
11440
|
+
}
|
|
11441
|
+
for (let i = 0; i < stageCount; i++) {
|
|
11442
|
+
const stage = stages[i];
|
|
11443
|
+
const state = stage.state ?? "pending";
|
|
11444
|
+
const fill = stage.color ?? BIO_STAGE_FILL[state];
|
|
11445
|
+
const g = chipGeoms[i];
|
|
11446
|
+
out.push({ type: "rect", x: g.x, y: stripY, width: g.w, height: 26, color: fill, fill });
|
|
11447
|
+
out.push({
|
|
11448
|
+
type: "text",
|
|
11449
|
+
x: g.x + g.w / 2,
|
|
11450
|
+
y: stripY + 13,
|
|
11451
|
+
text: stage.label,
|
|
11452
|
+
color: BIO_STAGE_TEXT[state],
|
|
11453
|
+
fontSize: 10,
|
|
11454
|
+
align: "center"
|
|
11455
|
+
});
|
|
11456
|
+
}
|
|
11457
|
+
}
|
|
11458
|
+
}
|
|
9880
11459
|
out.push(...shapes);
|
|
9881
11460
|
return out;
|
|
9882
|
-
}, [nodes, edges, shapes]);
|
|
11461
|
+
}, [nodes, edges, compartments, bands, stages, stageStyle, helix, shapes, width, height]);
|
|
11462
|
+
const drawables3D = useMemo(() => {
|
|
11463
|
+
if (mode !== "3d") return [];
|
|
11464
|
+
if (shapes.length > 0) {
|
|
11465
|
+
biologyLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
11466
|
+
}
|
|
11467
|
+
if (compartments.length > 0 || bands.length > 0 || stages.length > 0 || helix) {
|
|
11468
|
+
biologyLog.debug("2D-only families ignored in 3D mode (pixel-authored 2D vocabulary)", {
|
|
11469
|
+
compartments: compartments.length,
|
|
11470
|
+
bands: bands.length,
|
|
11471
|
+
stages: stages.length,
|
|
11472
|
+
helix: helix != null
|
|
11473
|
+
});
|
|
11474
|
+
}
|
|
11475
|
+
if (animate) {
|
|
11476
|
+
biologyLog.debug("animate ignored in 3D mode (motion is entity-state driven)");
|
|
11477
|
+
}
|
|
11478
|
+
const out = [];
|
|
11479
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
11480
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
11481
|
+
for (const n of nodes) {
|
|
11482
|
+
if (n.id) nodeById.set(n.id, n);
|
|
11483
|
+
}
|
|
11484
|
+
for (const e of edges) {
|
|
11485
|
+
const a = nodeById.get(e.from);
|
|
11486
|
+
const b = nodeById.get(e.to);
|
|
11487
|
+
if (!a || !b) continue;
|
|
11488
|
+
const edgeRadius = Math.max(0.04, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.12);
|
|
11489
|
+
const edge = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], edgeRadius, e.color ?? "#9ca3af");
|
|
11490
|
+
if (edge) out.push(edge);
|
|
11491
|
+
if (e.label) {
|
|
11492
|
+
out.push(
|
|
11493
|
+
billboardLabel(
|
|
11494
|
+
e.label,
|
|
11495
|
+
(a.x + b.x) / 2,
|
|
11496
|
+
(a.y + b.y) / 2,
|
|
11497
|
+
((a.z ?? 0) + (b.z ?? 0)) / 2,
|
|
11498
|
+
{ color: labelColor }
|
|
11499
|
+
)
|
|
11500
|
+
);
|
|
11501
|
+
}
|
|
11502
|
+
}
|
|
11503
|
+
for (const n of nodes) {
|
|
11504
|
+
const radius = n.radius ?? 0.5;
|
|
11505
|
+
const nz = n.z ?? 0;
|
|
11506
|
+
out.push(meshSphere(n.id, n.x, n.y, nz, radius, n.color ?? "#16a34a", { shape: n.shape, ...n.opacity !== void 0 ? { opacity: n.opacity } : {} }));
|
|
11507
|
+
if (n.label) {
|
|
11508
|
+
out.push(billboardLabel(n.label, n.x, n.y, nz + radius, { color: labelColor }));
|
|
11509
|
+
}
|
|
11510
|
+
}
|
|
11511
|
+
if (helix3d) {
|
|
11512
|
+
out.push(...helixDrawables(helix3d, { labelColor }));
|
|
11513
|
+
}
|
|
11514
|
+
return out;
|
|
11515
|
+
}, [mode, nodes, edges, shapes, compartments, bands, stages, helix, helix3d, animate, backgroundColor]);
|
|
11516
|
+
const nodeIndexById = useMemo(() => {
|
|
11517
|
+
const m = /* @__PURE__ */ new Map();
|
|
11518
|
+
(helix3d?.rungs ?? []).forEach((rung, i) => {
|
|
11519
|
+
if (rung.id) m.set(rung.id, i);
|
|
11520
|
+
});
|
|
11521
|
+
nodes.forEach((n, i) => {
|
|
11522
|
+
if (n.id) m.set(n.id, i);
|
|
11523
|
+
});
|
|
11524
|
+
return m;
|
|
11525
|
+
}, [nodes, helix3d]);
|
|
11526
|
+
if (mode === "3d") {
|
|
11527
|
+
return /* @__PURE__ */ jsx(
|
|
11528
|
+
LearningScene3D,
|
|
11529
|
+
{
|
|
11530
|
+
className,
|
|
11531
|
+
width,
|
|
11532
|
+
height,
|
|
11533
|
+
title,
|
|
11534
|
+
backgroundColor,
|
|
11535
|
+
drawables: drawables3D,
|
|
11536
|
+
camera,
|
|
11537
|
+
lighting,
|
|
11538
|
+
post,
|
|
11539
|
+
showGrid,
|
|
11540
|
+
shadows,
|
|
11541
|
+
interactive,
|
|
11542
|
+
isLoading,
|
|
11543
|
+
error,
|
|
11544
|
+
onItemClick: get3DClickPayload(onShapeClick, nodeIndexById)
|
|
11545
|
+
}
|
|
11546
|
+
);
|
|
11547
|
+
}
|
|
9883
11548
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
9884
11549
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
9885
11550
|
/* @__PURE__ */ jsx(
|
|
@@ -9889,7 +11554,9 @@ var init_BiologyCanvas = __esm({
|
|
|
9889
11554
|
height,
|
|
9890
11555
|
backgroundColor,
|
|
9891
11556
|
shapes: derivedShapes,
|
|
9892
|
-
|
|
11557
|
+
readouts,
|
|
11558
|
+
traces,
|
|
11559
|
+
interactive: interactive ?? false,
|
|
9893
11560
|
animate,
|
|
9894
11561
|
onShapeClick,
|
|
9895
11562
|
isLoading,
|
|
@@ -17281,9 +18948,10 @@ function Canvas({
|
|
|
17281
18948
|
drawables: [...drawables ?? [], ...childDrawables],
|
|
17282
18949
|
isLoading,
|
|
17283
18950
|
cameraMode: to3DCameraMode(camera?.mode),
|
|
17284
|
-
...zoom !== void 0 ? {
|
|
18951
|
+
...zoom !== void 0 ? { zoom } : {},
|
|
17285
18952
|
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
17286
18953
|
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
18954
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
17287
18955
|
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
17288
18956
|
unitScale,
|
|
17289
18957
|
backgroundColor,
|
|
@@ -17306,7 +18974,7 @@ function Canvas({
|
|
|
17306
18974
|
keyUpMap
|
|
17307
18975
|
};
|
|
17308
18976
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
17309
|
-
/* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
|
|
18977
|
+
/* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(Canvas3DHost2, { ...props3d }) }),
|
|
17310
18978
|
React77.Children.count(children) > 0 && /* @__PURE__ */ jsx(DrawableRegistryContext.Provider, { value: registerChildDrawable, children: /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: { position: "absolute", width: 0, height: 0, overflow: "hidden" }, children }) })
|
|
17311
18979
|
] });
|
|
17312
18980
|
}
|
|
@@ -17336,13 +19004,13 @@ function Canvas({
|
|
|
17336
19004
|
}
|
|
17337
19005
|
);
|
|
17338
19006
|
}
|
|
17339
|
-
var
|
|
19007
|
+
var Canvas3DHost2, canvasLog;
|
|
17340
19008
|
var init_Canvas = __esm({
|
|
17341
19009
|
"components/game/molecules/Canvas.tsx"() {
|
|
17342
19010
|
"use client";
|
|
17343
19011
|
init_registry();
|
|
17344
19012
|
init_Canvas2D();
|
|
17345
|
-
|
|
19013
|
+
Canvas3DHost2 = lazy(
|
|
17346
19014
|
() => import('@almadar/ui/components/molecules/game/three').then((m) => ({ default: m.Canvas3DHost }))
|
|
17347
19015
|
);
|
|
17348
19016
|
canvasLog = createLogger("almadar:ui:game-canvas");
|
|
@@ -19069,24 +20737,55 @@ var init_ChatBar = __esm({
|
|
|
19069
20737
|
ChatBar.displayName = "ChatBar";
|
|
19070
20738
|
}
|
|
19071
20739
|
});
|
|
19072
|
-
|
|
20740
|
+
function bondPerpendicular(a, b) {
|
|
20741
|
+
const dx = b[0] - a[0];
|
|
20742
|
+
const dy = b[1] - a[1];
|
|
20743
|
+
const px = dy;
|
|
20744
|
+
const py = -dx;
|
|
20745
|
+
const len = Math.sqrt(px * px + py * py);
|
|
20746
|
+
if (len < 1e-6) return [1, 0, 0];
|
|
20747
|
+
return [px / len, py / len, 0];
|
|
20748
|
+
}
|
|
20749
|
+
var chemistryLog, CHEM_BOND_STATE_COLOR, LONE_PAIR_ANGLES, ChemistryCanvas;
|
|
19073
20750
|
var init_ChemistryCanvas = __esm({
|
|
19074
20751
|
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
19075
20752
|
"use client";
|
|
19076
20753
|
init_atoms();
|
|
19077
20754
|
init_Stack();
|
|
19078
20755
|
init_LearningCanvas();
|
|
20756
|
+
init_learningScene3D();
|
|
20757
|
+
chemistryLog = createLogger("almadar:ui:chemistry-canvas");
|
|
20758
|
+
CHEM_BOND_STATE_COLOR = {
|
|
20759
|
+
default: "#6b7280",
|
|
20760
|
+
forming: "#16a34a",
|
|
20761
|
+
breaking: "#dc2626",
|
|
20762
|
+
highlight: "#f59e0b"
|
|
20763
|
+
};
|
|
20764
|
+
LONE_PAIR_ANGLES = [-90, 0, 90, 180];
|
|
19079
20765
|
ChemistryCanvas = ({
|
|
19080
20766
|
className,
|
|
19081
20767
|
width = 600,
|
|
19082
20768
|
height = 400,
|
|
19083
20769
|
title,
|
|
19084
20770
|
backgroundColor,
|
|
20771
|
+
mode = "2d",
|
|
20772
|
+
camera,
|
|
20773
|
+
lighting,
|
|
20774
|
+
post,
|
|
19085
20775
|
atoms = [],
|
|
19086
20776
|
bonds = [],
|
|
19087
20777
|
arrows = [],
|
|
20778
|
+
bondStyle = "thick",
|
|
20779
|
+
containers = [],
|
|
20780
|
+
equation,
|
|
20781
|
+
equationColor,
|
|
20782
|
+
lattice3d,
|
|
19088
20783
|
shapes = [],
|
|
19089
|
-
|
|
20784
|
+
readouts,
|
|
20785
|
+
traces,
|
|
20786
|
+
showGrid,
|
|
20787
|
+
shadows,
|
|
20788
|
+
interactive,
|
|
19090
20789
|
animate = false,
|
|
19091
20790
|
onShapeClick,
|
|
19092
20791
|
isLoading,
|
|
@@ -19098,21 +20797,118 @@ var init_ChemistryCanvas = __esm({
|
|
|
19098
20797
|
for (const a of atoms) {
|
|
19099
20798
|
if (a.id) atomById.set(a.id, a);
|
|
19100
20799
|
}
|
|
20800
|
+
for (const c of containers) {
|
|
20801
|
+
const color = c.color ?? "#64748b";
|
|
20802
|
+
if (c.level != null) {
|
|
20803
|
+
const lv = c.level;
|
|
20804
|
+
out.push({
|
|
20805
|
+
type: "rect",
|
|
20806
|
+
x: c.x + 1,
|
|
20807
|
+
y: c.y + c.height * (1 - lv),
|
|
20808
|
+
width: c.width - 2,
|
|
20809
|
+
height: c.height * lv - 1,
|
|
20810
|
+
color: c.levelColor ?? "#60a5fa",
|
|
20811
|
+
fill: c.levelColor ?? "#60a5fa",
|
|
20812
|
+
opacity: 0.5
|
|
20813
|
+
});
|
|
20814
|
+
}
|
|
20815
|
+
out.push({
|
|
20816
|
+
type: "rect",
|
|
20817
|
+
x: c.x,
|
|
20818
|
+
y: c.y,
|
|
20819
|
+
width: c.width,
|
|
20820
|
+
height: c.height,
|
|
20821
|
+
color,
|
|
20822
|
+
fill: c.fill,
|
|
20823
|
+
lineWidth: c.lineWidth ?? 2
|
|
20824
|
+
});
|
|
20825
|
+
const divider = c.divider ?? "none";
|
|
20826
|
+
if (divider !== "none") {
|
|
20827
|
+
out.push({
|
|
20828
|
+
type: "line",
|
|
20829
|
+
x1: c.x + c.width / 2,
|
|
20830
|
+
y1: c.y,
|
|
20831
|
+
x2: c.x + c.width / 2,
|
|
20832
|
+
y2: c.y + c.height,
|
|
20833
|
+
color: c.dividerColor ?? color,
|
|
20834
|
+
...divider === "dashed" || divider === "dotted" ? { dash: divider } : {}
|
|
20835
|
+
});
|
|
20836
|
+
}
|
|
20837
|
+
if (c.leftLabel) {
|
|
20838
|
+
out.push({
|
|
20839
|
+
type: "text",
|
|
20840
|
+
x: c.x + c.width * 0.25,
|
|
20841
|
+
y: c.y + 12,
|
|
20842
|
+
text: c.leftLabel,
|
|
20843
|
+
color: "#374151",
|
|
20844
|
+
fontSize: 11,
|
|
20845
|
+
align: "center"
|
|
20846
|
+
});
|
|
20847
|
+
}
|
|
20848
|
+
if (c.rightLabel) {
|
|
20849
|
+
out.push({
|
|
20850
|
+
type: "text",
|
|
20851
|
+
x: c.x + c.width * 0.75,
|
|
20852
|
+
y: c.y + 12,
|
|
20853
|
+
text: c.rightLabel,
|
|
20854
|
+
color: "#374151",
|
|
20855
|
+
fontSize: 11,
|
|
20856
|
+
align: "center"
|
|
20857
|
+
});
|
|
20858
|
+
}
|
|
20859
|
+
if (c.label) {
|
|
20860
|
+
out.push({
|
|
20861
|
+
type: "text",
|
|
20862
|
+
x: c.x + c.width / 2,
|
|
20863
|
+
y: c.y + c.height + 12,
|
|
20864
|
+
text: c.label,
|
|
20865
|
+
color: "#111827",
|
|
20866
|
+
fontSize: 12,
|
|
20867
|
+
align: "center"
|
|
20868
|
+
});
|
|
20869
|
+
}
|
|
20870
|
+
}
|
|
19101
20871
|
for (const b of bonds) {
|
|
19102
20872
|
const a = atomById.get(b.from);
|
|
19103
20873
|
const c = atomById.get(b.to);
|
|
19104
20874
|
if (!a || !c) continue;
|
|
19105
|
-
const
|
|
19106
|
-
const
|
|
19107
|
-
|
|
19108
|
-
|
|
19109
|
-
|
|
19110
|
-
|
|
19111
|
-
|
|
19112
|
-
|
|
19113
|
-
|
|
19114
|
-
|
|
19115
|
-
|
|
20875
|
+
const state = b.state ?? "default";
|
|
20876
|
+
const color = b.color ?? CHEM_BOND_STATE_COLOR[state];
|
|
20877
|
+
const dash = state === "forming" || state === "breaking" ? "dashed" : void 0;
|
|
20878
|
+
if (bondStyle === "parallel") {
|
|
20879
|
+
const dx = c.x - a.x;
|
|
20880
|
+
const dy = c.y - a.y;
|
|
20881
|
+
const dist = Math.max(1e-6, Math.hypot(dx, dy));
|
|
20882
|
+
const ux = dx / dist;
|
|
20883
|
+
const uy = dy / dist;
|
|
20884
|
+
const px = -uy;
|
|
20885
|
+
const py = ux;
|
|
20886
|
+
const offsets = b.type === "double" ? [-3, 3] : b.type === "triple" ? [-4, 0, 4] : [0];
|
|
20887
|
+
for (const off of offsets) {
|
|
20888
|
+
out.push({
|
|
20889
|
+
type: "line",
|
|
20890
|
+
x1: a.x + px * off,
|
|
20891
|
+
y1: a.y + py * off,
|
|
20892
|
+
x2: c.x + px * off,
|
|
20893
|
+
y2: c.y + py * off,
|
|
20894
|
+
color,
|
|
20895
|
+
lineWidth: 2,
|
|
20896
|
+
...dash ? { dash } : {}
|
|
20897
|
+
});
|
|
20898
|
+
}
|
|
20899
|
+
} else {
|
|
20900
|
+
const strokeWidth = b.type === "double" ? 4 : b.type === "triple" ? 6 : 2;
|
|
20901
|
+
out.push({
|
|
20902
|
+
type: "line",
|
|
20903
|
+
x1: a.x,
|
|
20904
|
+
y1: a.y,
|
|
20905
|
+
x2: c.x,
|
|
20906
|
+
y2: c.y,
|
|
20907
|
+
color,
|
|
20908
|
+
lineWidth: strokeWidth,
|
|
20909
|
+
...dash ? { dash } : {}
|
|
20910
|
+
});
|
|
20911
|
+
}
|
|
19116
20912
|
}
|
|
19117
20913
|
for (const a of arrows) {
|
|
19118
20914
|
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
@@ -19161,10 +20957,142 @@ var init_ChemistryCanvas = __esm({
|
|
|
19161
20957
|
align: "center"
|
|
19162
20958
|
});
|
|
19163
20959
|
}
|
|
20960
|
+
const r = a.radius ?? 14;
|
|
20961
|
+
if (a.charge) {
|
|
20962
|
+
out.push({
|
|
20963
|
+
type: "text",
|
|
20964
|
+
x: a.x + r * 0.85,
|
|
20965
|
+
y: a.y - r * 0.85,
|
|
20966
|
+
text: a.charge,
|
|
20967
|
+
color: "#111827",
|
|
20968
|
+
fontSize: 9,
|
|
20969
|
+
align: "left"
|
|
20970
|
+
});
|
|
20971
|
+
}
|
|
20972
|
+
const lonePairs = Math.max(0, Math.min(4, a.lonePairs ?? 0));
|
|
20973
|
+
for (let k = 0; k < lonePairs; k++) {
|
|
20974
|
+
const angleRad = LONE_PAIR_ANGLES[k] * Math.PI / 180;
|
|
20975
|
+
const cx = a.x + (r + 6) * Math.cos(angleRad);
|
|
20976
|
+
const cy = a.y + (r + 6) * Math.sin(angleRad);
|
|
20977
|
+
const perpX = -Math.sin(angleRad);
|
|
20978
|
+
const perpY = Math.cos(angleRad);
|
|
20979
|
+
for (const sign of [1, -1]) {
|
|
20980
|
+
out.push({
|
|
20981
|
+
type: "circle",
|
|
20982
|
+
x: cx + perpX * 2.5 * sign,
|
|
20983
|
+
y: cy + perpY * 2.5 * sign,
|
|
20984
|
+
radius: 1.5,
|
|
20985
|
+
color: "#374151",
|
|
20986
|
+
fill: "#374151"
|
|
20987
|
+
});
|
|
20988
|
+
}
|
|
20989
|
+
}
|
|
20990
|
+
}
|
|
20991
|
+
if (equation) {
|
|
20992
|
+
out.push({
|
|
20993
|
+
type: "text",
|
|
20994
|
+
x: width / 2,
|
|
20995
|
+
y: 14,
|
|
20996
|
+
text: equation,
|
|
20997
|
+
color: equationColor ?? "#111827",
|
|
20998
|
+
fontSize: 13,
|
|
20999
|
+
align: "center"
|
|
21000
|
+
});
|
|
19164
21001
|
}
|
|
19165
21002
|
out.push(...shapes);
|
|
19166
21003
|
return out;
|
|
19167
|
-
}, [atoms, bonds, arrows, shapes]);
|
|
21004
|
+
}, [atoms, bonds, arrows, bondStyle, containers, equation, equationColor, shapes, width]);
|
|
21005
|
+
const drawables3D = useMemo(() => {
|
|
21006
|
+
if (mode !== "3d") return [];
|
|
21007
|
+
if (shapes.length > 0) {
|
|
21008
|
+
chemistryLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
21009
|
+
}
|
|
21010
|
+
if (containers.length > 0) {
|
|
21011
|
+
chemistryLog.debug("containers ignored in 3D mode (pixel-authored 2D vocabulary)", { count: containers.length });
|
|
21012
|
+
}
|
|
21013
|
+
if (animate) {
|
|
21014
|
+
chemistryLog.debug("animate ignored in 3D mode (motion is entity-state driven)");
|
|
21015
|
+
}
|
|
21016
|
+
const out = [];
|
|
21017
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
21018
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
21019
|
+
for (const a of atoms) {
|
|
21020
|
+
if (a.id) atomById.set(a.id, a);
|
|
21021
|
+
}
|
|
21022
|
+
for (const b of bonds) {
|
|
21023
|
+
const a = atomById.get(b.from);
|
|
21024
|
+
const c = atomById.get(b.to);
|
|
21025
|
+
if (!a || !c) continue;
|
|
21026
|
+
const color = b.color ?? "#6b7280";
|
|
21027
|
+
const from = [a.x, a.y, a.z ?? 0];
|
|
21028
|
+
const to = [c.x, c.y, c.z ?? 0];
|
|
21029
|
+
const perp = bondPerpendicular(from, to);
|
|
21030
|
+
const bondRadius = Math.max(0.06, Math.min(a.radius ?? 0.45, c.radius ?? 0.45) * 0.22);
|
|
21031
|
+
const step = bondRadius * 2.2;
|
|
21032
|
+
const offsets = b.type === "double" ? [-step, step] : b.type === "triple" ? [-step, 0, step] : [0];
|
|
21033
|
+
for (const off of offsets) {
|
|
21034
|
+
const bond = cylinderBetween(
|
|
21035
|
+
[from[0] + perp[0] * off, from[1] + perp[1] * off, from[2] + perp[2] * off],
|
|
21036
|
+
[to[0] + perp[0] * off, to[1] + perp[1] * off, to[2] + perp[2] * off],
|
|
21037
|
+
bondRadius,
|
|
21038
|
+
color
|
|
21039
|
+
);
|
|
21040
|
+
if (bond) out.push(bond);
|
|
21041
|
+
}
|
|
21042
|
+
}
|
|
21043
|
+
for (const a of arrows) {
|
|
21044
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
21045
|
+
const len = a.length ?? 60;
|
|
21046
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
21047
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
21048
|
+
const arrow = arrowBetween([a.x, a.y, 0], [x2, y2, 0], a.color ?? "#dc2626");
|
|
21049
|
+
if (arrow) out.push(arrow);
|
|
21050
|
+
if (a.label) {
|
|
21051
|
+
out.push(billboardLabel(a.label, (a.x + x2) / 2, (a.y + y2) / 2, 0, { color: labelColor }));
|
|
21052
|
+
}
|
|
21053
|
+
}
|
|
21054
|
+
for (const a of atoms) {
|
|
21055
|
+
const radius = a.radius ?? 0.45;
|
|
21056
|
+
const az = a.z ?? 0;
|
|
21057
|
+
out.push(meshSphere(a.id, a.x, a.y, az, radius, a.color ?? "#2563eb"));
|
|
21058
|
+
if (a.element) {
|
|
21059
|
+
out.push(billboardLabel(a.element, a.x, a.y, az + radius, { color: labelColor }));
|
|
21060
|
+
}
|
|
21061
|
+
}
|
|
21062
|
+
if (lattice3d) {
|
|
21063
|
+
out.push(...latticeDrawables(lattice3d, { labelColor }));
|
|
21064
|
+
}
|
|
21065
|
+
return out;
|
|
21066
|
+
}, [mode, atoms, bonds, arrows, shapes, containers, lattice3d, animate, backgroundColor]);
|
|
21067
|
+
const atomIndexById = useMemo(() => {
|
|
21068
|
+
const m = /* @__PURE__ */ new Map();
|
|
21069
|
+
atoms.forEach((a, i) => {
|
|
21070
|
+
if (a.id) m.set(a.id, i);
|
|
21071
|
+
});
|
|
21072
|
+
return m;
|
|
21073
|
+
}, [atoms]);
|
|
21074
|
+
if (mode === "3d") {
|
|
21075
|
+
return /* @__PURE__ */ jsx(
|
|
21076
|
+
LearningScene3D,
|
|
21077
|
+
{
|
|
21078
|
+
className,
|
|
21079
|
+
width,
|
|
21080
|
+
height,
|
|
21081
|
+
title,
|
|
21082
|
+
backgroundColor,
|
|
21083
|
+
drawables: drawables3D,
|
|
21084
|
+
camera,
|
|
21085
|
+
lighting,
|
|
21086
|
+
post,
|
|
21087
|
+
showGrid,
|
|
21088
|
+
shadows,
|
|
21089
|
+
interactive,
|
|
21090
|
+
isLoading,
|
|
21091
|
+
error,
|
|
21092
|
+
onItemClick: get3DClickPayload(onShapeClick, atomIndexById)
|
|
21093
|
+
}
|
|
21094
|
+
);
|
|
21095
|
+
}
|
|
19168
21096
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
19169
21097
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
19170
21098
|
/* @__PURE__ */ jsx(
|
|
@@ -19174,7 +21102,9 @@ var init_ChemistryCanvas = __esm({
|
|
|
19174
21102
|
height,
|
|
19175
21103
|
backgroundColor,
|
|
19176
21104
|
shapes: derivedShapes,
|
|
19177
|
-
|
|
21105
|
+
readouts,
|
|
21106
|
+
traces,
|
|
21107
|
+
interactive: interactive ?? false,
|
|
19178
21108
|
animate,
|
|
19179
21109
|
onShapeClick,
|
|
19180
21110
|
isLoading,
|
|
@@ -29086,6 +31016,10 @@ var init_molecules = __esm({
|
|
|
29086
31016
|
init_GameShell();
|
|
29087
31017
|
}
|
|
29088
31018
|
});
|
|
31019
|
+
function formatTick(v) {
|
|
31020
|
+
if (Number.isInteger(v)) return String(v);
|
|
31021
|
+
return v.toFixed(2).replace(/0+$/, "").replace(/\.$/, "");
|
|
31022
|
+
}
|
|
29089
31023
|
var MathCanvas;
|
|
29090
31024
|
var init_MathCanvas = __esm({
|
|
29091
31025
|
"components/learning/molecules/MathCanvas.tsx"() {
|
|
@@ -29105,10 +31039,19 @@ var init_MathCanvas = __esm({
|
|
|
29105
31039
|
showAxes = true,
|
|
29106
31040
|
showGrid = true,
|
|
29107
31041
|
gridStep = 1,
|
|
31042
|
+
showTickLabels = false,
|
|
31043
|
+
showCurveLabels = false,
|
|
29108
31044
|
curves = [],
|
|
29109
31045
|
points = [],
|
|
29110
31046
|
vectors = [],
|
|
31047
|
+
regions = [],
|
|
31048
|
+
bars = [],
|
|
31049
|
+
guides = [],
|
|
31050
|
+
angles = [],
|
|
31051
|
+
hops = [],
|
|
29111
31052
|
shapes = [],
|
|
31053
|
+
readouts,
|
|
31054
|
+
traces,
|
|
29112
31055
|
interactive = false,
|
|
29113
31056
|
animate = false,
|
|
29114
31057
|
onShapeClick,
|
|
@@ -29122,6 +31065,8 @@ var init_MathCanvas = __esm({
|
|
|
29122
31065
|
const plotH = height - margin * 2;
|
|
29123
31066
|
const mapX = (x) => margin + (x - xMin) / (xMax - xMin) * plotW;
|
|
29124
31067
|
const mapY = (y) => height - (margin + (y - yMin) / (yMax - yMin) * plotH);
|
|
31068
|
+
const xAxisY = Math.max(margin, Math.min(height - margin, mapY(0)));
|
|
31069
|
+
const yAxisX = Math.max(margin, Math.min(width - margin, mapX(0)));
|
|
29125
31070
|
if (showGrid) {
|
|
29126
31071
|
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep) {
|
|
29127
31072
|
const px = mapX(x);
|
|
@@ -29132,14 +31077,99 @@ var init_MathCanvas = __esm({
|
|
|
29132
31077
|
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color: "#e5e7eb", lineWidth: 1 });
|
|
29133
31078
|
}
|
|
29134
31079
|
}
|
|
31080
|
+
if (showTickLabels) {
|
|
31081
|
+
const labelEveryX = Math.max(1, Math.ceil((xMax - xMin) / gridStep / Math.floor(plotW / 40)));
|
|
31082
|
+
let kx = 0;
|
|
31083
|
+
for (let x = Math.ceil(xMin / gridStep) * gridStep; x <= xMax; x += gridStep, kx++) {
|
|
31084
|
+
if (kx % labelEveryX === 0 && x !== 0) {
|
|
31085
|
+
out.push({ type: "text", x: mapX(x), y: xAxisY + 12, text: formatTick(x), color: "#6b7280", fontSize: 10, align: "center" });
|
|
31086
|
+
}
|
|
31087
|
+
}
|
|
31088
|
+
const labelEveryY = Math.max(1, Math.ceil((yMax - yMin) / gridStep / Math.floor(plotH / 28)));
|
|
31089
|
+
let ky = 0;
|
|
31090
|
+
for (let y = Math.ceil(yMin / gridStep) * gridStep; y <= yMax; y += gridStep, ky++) {
|
|
31091
|
+
if (ky % labelEveryY === 0 && y !== 0) {
|
|
31092
|
+
out.push({ type: "text", x: yAxisX - 6, y: mapY(y), text: formatTick(y), color: "#6b7280", fontSize: 10, align: "right" });
|
|
31093
|
+
}
|
|
31094
|
+
}
|
|
31095
|
+
if (xMin <= 0 && xMax >= 0 && yMin <= 0 && yMax >= 0) {
|
|
31096
|
+
out.push({ type: "text", x: yAxisX - 6, y: xAxisY + 12, text: "0", color: "#6b7280", fontSize: 10, align: "right" });
|
|
31097
|
+
}
|
|
31098
|
+
}
|
|
31099
|
+
for (const region of regions) {
|
|
31100
|
+
if (!region.samples || region.samples.length === 0) continue;
|
|
31101
|
+
const baseline = region.baseline ?? 0;
|
|
31102
|
+
const clampedPoint = (p) => ({
|
|
31103
|
+
x: mapX(Math.min(xMax, Math.max(xMin, p.x))),
|
|
31104
|
+
y: mapY(Math.min(yMax, Math.max(yMin, p.y)))
|
|
31105
|
+
});
|
|
31106
|
+
const upper = region.samples.map(clampedPoint);
|
|
31107
|
+
const first = region.samples[0];
|
|
31108
|
+
const last = region.samples[region.samples.length - 1];
|
|
31109
|
+
const closing = region.samples2 && region.samples2.length > 0 ? [...region.samples2].reverse().map(clampedPoint) : [clampedPoint({ x: last.x, y: baseline }), clampedPoint({ x: first.x, y: baseline })];
|
|
31110
|
+
const color = region.color ?? "#2563eb";
|
|
31111
|
+
out.push({
|
|
31112
|
+
type: "polygon",
|
|
31113
|
+
points: [...upper, ...closing],
|
|
31114
|
+
fill: color,
|
|
31115
|
+
color,
|
|
31116
|
+
opacity: region.opacity ?? 0.2,
|
|
31117
|
+
lineWidth: 1
|
|
31118
|
+
});
|
|
31119
|
+
if (region.label) {
|
|
31120
|
+
const mid = Math.floor(region.samples.length / 2);
|
|
31121
|
+
out.push({
|
|
31122
|
+
type: "text",
|
|
31123
|
+
x: mapX((first.x + last.x) / 2),
|
|
31124
|
+
y: (mapY(region.samples[mid].y) + mapY(baseline)) / 2,
|
|
31125
|
+
text: region.label,
|
|
31126
|
+
color: "#111827",
|
|
31127
|
+
fontSize: 11
|
|
31128
|
+
});
|
|
31129
|
+
}
|
|
31130
|
+
}
|
|
31131
|
+
for (const bar of bars) {
|
|
31132
|
+
if (bar.x + bar.width < xMin || bar.x > xMax) continue;
|
|
31133
|
+
const y0 = bar.y0 ?? 0;
|
|
31134
|
+
const color = bar.color ?? "#93c5fd";
|
|
31135
|
+
out.push({
|
|
31136
|
+
type: "rect",
|
|
31137
|
+
x: mapX(bar.x),
|
|
31138
|
+
y: mapY(Math.max(y0, bar.y1)),
|
|
31139
|
+
width: mapX(bar.x + bar.width) - mapX(bar.x),
|
|
31140
|
+
height: Math.abs(mapY(bar.y1) - mapY(y0)),
|
|
31141
|
+
color,
|
|
31142
|
+
fill: color,
|
|
31143
|
+
opacity: bar.opacity ?? 0.5,
|
|
31144
|
+
lineWidth: 1
|
|
31145
|
+
});
|
|
31146
|
+
}
|
|
29135
31147
|
if (showAxes) {
|
|
29136
|
-
const xAxisY = Math.max(margin, Math.min(height - margin, mapY(0)));
|
|
29137
|
-
const yAxisX = Math.max(margin, Math.min(width - margin, mapX(0)));
|
|
29138
31148
|
out.push({ type: "line", x1: margin, y1: xAxisY, x2: width - margin, y2: xAxisY, color: "#374151", lineWidth: 2 });
|
|
29139
31149
|
out.push({ type: "line", x1: yAxisX, y1: margin, x2: yAxisX, y2: height - margin, color: "#374151", lineWidth: 2 });
|
|
29140
31150
|
}
|
|
31151
|
+
for (const guide of guides) {
|
|
31152
|
+
const color = guide.color ?? "#9ca3af";
|
|
31153
|
+
const dash = guide.dash ?? "dashed";
|
|
31154
|
+
if (guide.kind === "vline") {
|
|
31155
|
+
if (guide.at < xMin || guide.at > xMax) continue;
|
|
31156
|
+
const px = mapX(guide.at);
|
|
31157
|
+
out.push({ type: "line", x1: px, y1: margin, x2: px, y2: height - margin, color, dash });
|
|
31158
|
+
if (guide.label) {
|
|
31159
|
+
out.push({ type: "text", x: px + 4, y: margin + 10, text: guide.label, color: "#111827", fontSize: 11 });
|
|
31160
|
+
}
|
|
31161
|
+
} else {
|
|
31162
|
+
if (guide.at < yMin || guide.at > yMax) continue;
|
|
31163
|
+
const py = mapY(guide.at);
|
|
31164
|
+
out.push({ type: "line", x1: margin, y1: py, x2: width - margin, y2: py, color, dash });
|
|
31165
|
+
if (guide.label) {
|
|
31166
|
+
out.push({ type: "text", x: width - margin - 4, y: py - 8, text: guide.label, color: "#111827", fontSize: 11, align: "right" });
|
|
31167
|
+
}
|
|
31168
|
+
}
|
|
31169
|
+
}
|
|
29141
31170
|
for (const curve of curves) {
|
|
29142
31171
|
if (!curve.samples || curve.samples.length < 2) continue;
|
|
31172
|
+
let lastInRange;
|
|
29143
31173
|
for (let i = 1; i < curve.samples.length; i++) {
|
|
29144
31174
|
const a = curve.samples[i - 1];
|
|
29145
31175
|
const b = curve.samples[i];
|
|
@@ -29151,19 +31181,97 @@ var init_MathCanvas = __esm({
|
|
|
29151
31181
|
x2: mapX(b.x),
|
|
29152
31182
|
y2: mapY(b.y),
|
|
29153
31183
|
color: curve.color ?? "#2563eb",
|
|
29154
|
-
lineWidth: 2
|
|
31184
|
+
lineWidth: 2,
|
|
31185
|
+
dash: curve.dash
|
|
31186
|
+
});
|
|
31187
|
+
lastInRange = b;
|
|
31188
|
+
}
|
|
31189
|
+
if (showCurveLabels && curve.label && lastInRange) {
|
|
31190
|
+
out.push({
|
|
31191
|
+
type: "text",
|
|
31192
|
+
x: mapX(lastInRange.x) + 6,
|
|
31193
|
+
y: mapY(lastInRange.y) - 6,
|
|
31194
|
+
text: curve.label,
|
|
31195
|
+
color: curve.color ?? "#2563eb",
|
|
31196
|
+
fontSize: 11
|
|
31197
|
+
});
|
|
31198
|
+
}
|
|
31199
|
+
}
|
|
31200
|
+
for (const hop of hops) {
|
|
31201
|
+
const x1 = mapX(hop.from);
|
|
31202
|
+
const x2 = mapX(hop.to);
|
|
31203
|
+
const peak = Math.min(36, plotH * 0.3);
|
|
31204
|
+
const color = hop.color ?? "#7c3aed";
|
|
31205
|
+
out.push({
|
|
31206
|
+
type: "ellipse",
|
|
31207
|
+
x: (x1 + x2) / 2,
|
|
31208
|
+
y: xAxisY,
|
|
31209
|
+
width: Math.abs(x2 - x1),
|
|
31210
|
+
height: 2 * peak,
|
|
31211
|
+
startAngle: 180,
|
|
31212
|
+
endAngle: 360,
|
|
31213
|
+
color
|
|
31214
|
+
});
|
|
31215
|
+
const s = Math.sign(hop.to - hop.from);
|
|
31216
|
+
out.push({
|
|
31217
|
+
type: "polygon",
|
|
31218
|
+
points: [
|
|
31219
|
+
{ x: x2, y: xAxisY },
|
|
31220
|
+
{ x: x2 - 4 * s, y: xAxisY - 7 },
|
|
31221
|
+
{ x: x2 + 2 * s, y: xAxisY - 7 }
|
|
31222
|
+
],
|
|
31223
|
+
fill: color,
|
|
31224
|
+
color
|
|
31225
|
+
});
|
|
31226
|
+
if (hop.label) {
|
|
31227
|
+
out.push({
|
|
31228
|
+
type: "text",
|
|
31229
|
+
x: (x1 + x2) / 2,
|
|
31230
|
+
y: xAxisY - peak - 8,
|
|
31231
|
+
text: hop.label,
|
|
31232
|
+
color: "#111827",
|
|
31233
|
+
fontSize: 10,
|
|
31234
|
+
align: "center"
|
|
31235
|
+
});
|
|
31236
|
+
}
|
|
31237
|
+
}
|
|
31238
|
+
for (const angle of angles) {
|
|
31239
|
+
const radius = angle.radius ?? 0.8;
|
|
31240
|
+
const color = angle.color ?? "#0ea5e9";
|
|
31241
|
+
out.push({
|
|
31242
|
+
type: "ellipse",
|
|
31243
|
+
x: mapX(angle.x),
|
|
31244
|
+
y: mapY(angle.y),
|
|
31245
|
+
width: 2 * radius * plotW / (xMax - xMin),
|
|
31246
|
+
height: 2 * radius * plotH / (yMax - yMin),
|
|
31247
|
+
startAngle: -angle.to,
|
|
31248
|
+
endAngle: -angle.from,
|
|
31249
|
+
color
|
|
31250
|
+
});
|
|
31251
|
+
if (angle.label) {
|
|
31252
|
+
const mid = (angle.from + angle.to) / 2;
|
|
31253
|
+
const rad = mid * Math.PI / 180;
|
|
31254
|
+
out.push({
|
|
31255
|
+
type: "text",
|
|
31256
|
+
x: mapX(angle.x + 1.35 * radius * Math.cos(rad)),
|
|
31257
|
+
y: mapY(angle.y + 1.35 * radius * Math.sin(rad)),
|
|
31258
|
+
text: angle.label,
|
|
31259
|
+
color: "#111827",
|
|
31260
|
+
fontSize: 11,
|
|
31261
|
+
align: "center"
|
|
29155
31262
|
});
|
|
29156
31263
|
}
|
|
29157
31264
|
}
|
|
29158
31265
|
for (const p of points) {
|
|
29159
31266
|
if (p.x < xMin || p.x > xMax || p.y < yMin || p.y > yMax) continue;
|
|
31267
|
+
const isOpen = p.style === "open";
|
|
29160
31268
|
out.push({
|
|
29161
31269
|
type: "circle",
|
|
29162
31270
|
x: mapX(p.x),
|
|
29163
31271
|
y: mapY(p.y),
|
|
29164
31272
|
radius: p.radius ?? 4,
|
|
29165
31273
|
color: p.color ?? "#dc2626",
|
|
29166
|
-
fill: p.color ?? "#dc2626"
|
|
31274
|
+
fill: isOpen ? "#ffffff" : p.color ?? "#dc2626"
|
|
29167
31275
|
});
|
|
29168
31276
|
if (p.label) {
|
|
29169
31277
|
out.push({ type: "text", x: mapX(p.x) + 8, y: mapY(p.y) - 8, text: p.label, color: "#111827", fontSize: 12 });
|
|
@@ -29182,7 +31290,28 @@ var init_MathCanvas = __esm({
|
|
|
29182
31290
|
}
|
|
29183
31291
|
out.push(...shapes);
|
|
29184
31292
|
return out;
|
|
29185
|
-
}, [
|
|
31293
|
+
}, [
|
|
31294
|
+
width,
|
|
31295
|
+
height,
|
|
31296
|
+
xMin,
|
|
31297
|
+
xMax,
|
|
31298
|
+
yMin,
|
|
31299
|
+
yMax,
|
|
31300
|
+
showAxes,
|
|
31301
|
+
showGrid,
|
|
31302
|
+
gridStep,
|
|
31303
|
+
showTickLabels,
|
|
31304
|
+
showCurveLabels,
|
|
31305
|
+
curves,
|
|
31306
|
+
points,
|
|
31307
|
+
vectors,
|
|
31308
|
+
regions,
|
|
31309
|
+
bars,
|
|
31310
|
+
guides,
|
|
31311
|
+
angles,
|
|
31312
|
+
hops,
|
|
31313
|
+
shapes
|
|
31314
|
+
]);
|
|
29186
31315
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
29187
31316
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
29188
31317
|
/* @__PURE__ */ jsx(
|
|
@@ -29191,6 +31320,8 @@ var init_MathCanvas = __esm({
|
|
|
29191
31320
|
width,
|
|
29192
31321
|
height,
|
|
29193
31322
|
shapes: derivedShapes,
|
|
31323
|
+
readouts,
|
|
31324
|
+
traces,
|
|
29194
31325
|
interactive,
|
|
29195
31326
|
animate,
|
|
29196
31327
|
onShapeClick,
|
|
@@ -29202,27 +31333,355 @@ var init_MathCanvas = __esm({
|
|
|
29202
31333
|
};
|
|
29203
31334
|
}
|
|
29204
31335
|
});
|
|
29205
|
-
|
|
31336
|
+
function formatMeterValue(v) {
|
|
31337
|
+
return Number.isInteger(v) ? String(v) : String(Number(v.toFixed(2)));
|
|
31338
|
+
}
|
|
31339
|
+
function sceneObjectShapes(obj, canvasWidth, canvasHeight) {
|
|
31340
|
+
const out = [];
|
|
31341
|
+
const color = obj.color ?? "#334155";
|
|
31342
|
+
switch (obj.kind) {
|
|
31343
|
+
case "ground": {
|
|
31344
|
+
const xStart = obj.x1 ?? 0;
|
|
31345
|
+
const xEnd = obj.x2 ?? canvasWidth;
|
|
31346
|
+
const y = obj.y ?? 0;
|
|
31347
|
+
out.push({ type: "line", x1: xStart, y1: y, x2: xEnd, y2: y, color, lineWidth: 2 });
|
|
31348
|
+
for (let hx = xStart + 7; hx <= xEnd; hx += 14) {
|
|
31349
|
+
out.push({ type: "line", x1: hx, y1: y, x2: hx - 7, y2: y + 7, color, lineWidth: 1 });
|
|
31350
|
+
}
|
|
31351
|
+
if (obj.label) {
|
|
31352
|
+
out.push({
|
|
31353
|
+
type: "text",
|
|
31354
|
+
x: (xStart + xEnd) / 2,
|
|
31355
|
+
y: y - 10,
|
|
31356
|
+
text: obj.label,
|
|
31357
|
+
color: PHYSICS_LABEL_COLOR,
|
|
31358
|
+
fontSize: 11,
|
|
31359
|
+
align: "center"
|
|
31360
|
+
});
|
|
31361
|
+
}
|
|
31362
|
+
break;
|
|
31363
|
+
}
|
|
31364
|
+
case "wall": {
|
|
31365
|
+
const yStart = obj.y1 ?? 0;
|
|
31366
|
+
const yEnd = obj.y2 ?? canvasHeight;
|
|
31367
|
+
const x = obj.x ?? 0;
|
|
31368
|
+
out.push({ type: "line", x1: x, y1: yStart, x2: x, y2: yEnd, color, lineWidth: 2 });
|
|
31369
|
+
for (let hy = yStart + 7; hy <= yEnd; hy += 14) {
|
|
31370
|
+
out.push({ type: "line", x1: x, y1: hy, x2: x - 7, y2: hy + 7, color, lineWidth: 1 });
|
|
31371
|
+
}
|
|
31372
|
+
if (obj.label) {
|
|
31373
|
+
out.push({
|
|
31374
|
+
type: "text",
|
|
31375
|
+
x: x + 12,
|
|
31376
|
+
y: (yStart + yEnd) / 2,
|
|
31377
|
+
text: obj.label,
|
|
31378
|
+
color: PHYSICS_LABEL_COLOR,
|
|
31379
|
+
fontSize: 11,
|
|
31380
|
+
align: "left"
|
|
31381
|
+
});
|
|
31382
|
+
}
|
|
31383
|
+
break;
|
|
31384
|
+
}
|
|
31385
|
+
case "ramp": {
|
|
31386
|
+
const x1 = obj.x1 ?? 0;
|
|
31387
|
+
const y1 = obj.y1 ?? 0;
|
|
31388
|
+
const x2 = obj.x2 ?? canvasWidth;
|
|
31389
|
+
const y2 = obj.y2 ?? canvasHeight;
|
|
31390
|
+
out.push({
|
|
31391
|
+
type: "polygon",
|
|
31392
|
+
points: [
|
|
31393
|
+
{ x: x1, y: y1 },
|
|
31394
|
+
{ x: x2, y: y2 },
|
|
31395
|
+
{ x: x1, y: y2 }
|
|
31396
|
+
],
|
|
31397
|
+
color,
|
|
31398
|
+
fill: obj.fill ?? "#e2e8f0",
|
|
31399
|
+
lineWidth: 2
|
|
31400
|
+
});
|
|
31401
|
+
if (obj.label) {
|
|
31402
|
+
out.push({
|
|
31403
|
+
type: "text",
|
|
31404
|
+
x: (2 * x1 + x2) / 3,
|
|
31405
|
+
y: (y1 + 2 * y2) / 3,
|
|
31406
|
+
text: obj.label,
|
|
31407
|
+
color: PHYSICS_LABEL_COLOR,
|
|
31408
|
+
fontSize: 11,
|
|
31409
|
+
align: "center"
|
|
31410
|
+
});
|
|
31411
|
+
}
|
|
31412
|
+
break;
|
|
31413
|
+
}
|
|
31414
|
+
case "box": {
|
|
31415
|
+
const x = obj.x ?? 0;
|
|
31416
|
+
const y = obj.y ?? 0;
|
|
31417
|
+
const w = obj.width ?? 40;
|
|
31418
|
+
const h = obj.height ?? 40;
|
|
31419
|
+
out.push({ type: "rect", x, y, width: w, height: h, color, fill: obj.fill, lineWidth: 2 });
|
|
31420
|
+
if (obj.label) {
|
|
31421
|
+
out.push({
|
|
31422
|
+
type: "text",
|
|
31423
|
+
x: x + w / 2,
|
|
31424
|
+
y: y + h / 2,
|
|
31425
|
+
text: obj.label,
|
|
31426
|
+
color: PHYSICS_LABEL_COLOR,
|
|
31427
|
+
fontSize: 11,
|
|
31428
|
+
align: "center"
|
|
31429
|
+
});
|
|
31430
|
+
}
|
|
31431
|
+
break;
|
|
31432
|
+
}
|
|
31433
|
+
case "pivot": {
|
|
31434
|
+
const x = obj.x ?? 0;
|
|
31435
|
+
const y = obj.y ?? 0;
|
|
31436
|
+
out.push({ type: "circle", x, y, radius: 5, color, fill: color });
|
|
31437
|
+
out.push({ type: "line", x1: x - 14, y1: y - 8, x2: x + 14, y2: y - 8, color, lineWidth: 1 });
|
|
31438
|
+
for (let k = 0; k < 5; k++) {
|
|
31439
|
+
const hx = x - 14 + 7 * k;
|
|
31440
|
+
out.push({ type: "line", x1: hx, y1: y - 8, x2: hx - 6, y2: y - 14, color, lineWidth: 1 });
|
|
31441
|
+
}
|
|
31442
|
+
if (obj.label) {
|
|
31443
|
+
out.push({
|
|
31444
|
+
type: "text",
|
|
31445
|
+
x,
|
|
31446
|
+
y: y - 20,
|
|
31447
|
+
text: obj.label,
|
|
31448
|
+
color: PHYSICS_LABEL_COLOR,
|
|
31449
|
+
fontSize: 11,
|
|
31450
|
+
align: "center"
|
|
31451
|
+
});
|
|
31452
|
+
}
|
|
31453
|
+
break;
|
|
31454
|
+
}
|
|
31455
|
+
}
|
|
31456
|
+
return out;
|
|
31457
|
+
}
|
|
31458
|
+
function trailShapes(trail) {
|
|
31459
|
+
const n = trail.points.length;
|
|
31460
|
+
if (n < 2) return [];
|
|
31461
|
+
const color = trail.color ?? "#94a3b8";
|
|
31462
|
+
const lineWidth = trail.width ?? 2;
|
|
31463
|
+
const fade = trail.fade ?? true;
|
|
31464
|
+
const globalOpacity = trail.opacity ?? 1;
|
|
31465
|
+
const out = [];
|
|
31466
|
+
for (let i = 0; i < n - 1; i++) {
|
|
31467
|
+
const a = trail.points[i];
|
|
31468
|
+
const b = trail.points[i + 1];
|
|
31469
|
+
const segmentOpacity = fade ? 0.12 + 0.68 * i / (n - 1) : 0.6;
|
|
31470
|
+
out.push({
|
|
31471
|
+
type: "line",
|
|
31472
|
+
x1: a.x,
|
|
31473
|
+
y1: a.y,
|
|
31474
|
+
x2: b.x,
|
|
31475
|
+
y2: b.y,
|
|
31476
|
+
color,
|
|
31477
|
+
lineWidth,
|
|
31478
|
+
opacity: segmentOpacity * globalOpacity
|
|
31479
|
+
});
|
|
31480
|
+
}
|
|
31481
|
+
return out;
|
|
31482
|
+
}
|
|
31483
|
+
function constraintShapes(c, a, b) {
|
|
31484
|
+
const color = c.color ?? "#9ca3af";
|
|
31485
|
+
const kind = c.kind ?? "rod";
|
|
31486
|
+
if (kind === "rod") {
|
|
31487
|
+
return [{ type: "line", x1: a.x, y1: a.y, x2: b.x, y2: b.y, color, lineWidth: 2 }];
|
|
31488
|
+
}
|
|
31489
|
+
if (kind === "string") {
|
|
31490
|
+
return [{ type: "line", x1: a.x, y1: a.y, x2: b.x, y2: b.y, color, lineWidth: 2, dash: "dashed" }];
|
|
31491
|
+
}
|
|
31492
|
+
const COILS = 8;
|
|
31493
|
+
const AMP = 7;
|
|
31494
|
+
const LEAD = 10;
|
|
31495
|
+
const dx = b.x - a.x;
|
|
31496
|
+
const dy = b.y - a.y;
|
|
31497
|
+
const dist = Math.max(1e-6, Math.hypot(dx, dy));
|
|
31498
|
+
const ux = dx / dist;
|
|
31499
|
+
const uy = dy / dist;
|
|
31500
|
+
const perpX = -uy;
|
|
31501
|
+
const perpY = ux;
|
|
31502
|
+
const aPrime = { x: a.x + LEAD * ux, y: a.y + LEAD * uy };
|
|
31503
|
+
const bPrime = { x: b.x - LEAD * ux, y: b.y - LEAD * uy };
|
|
31504
|
+
const m = 2 * COILS;
|
|
31505
|
+
const polyline = [{ x: a.x, y: a.y }, aPrime];
|
|
31506
|
+
for (let j = 1; j <= m; j++) {
|
|
31507
|
+
const t = j / (m + 1);
|
|
31508
|
+
const baseX = aPrime.x + t * (bPrime.x - aPrime.x);
|
|
31509
|
+
const baseY = aPrime.y + t * (bPrime.y - aPrime.y);
|
|
31510
|
+
const sign = j % 2 === 0 ? 1 : -1;
|
|
31511
|
+
polyline.push({ x: baseX + sign * AMP * perpX, y: baseY + sign * AMP * perpY });
|
|
31512
|
+
}
|
|
31513
|
+
polyline.push(bPrime, { x: b.x, y: b.y });
|
|
31514
|
+
const out = [];
|
|
31515
|
+
for (let i = 1; i < polyline.length; i++) {
|
|
31516
|
+
out.push({
|
|
31517
|
+
type: "line",
|
|
31518
|
+
x1: polyline[i - 1].x,
|
|
31519
|
+
y1: polyline[i - 1].y,
|
|
31520
|
+
x2: polyline[i].x,
|
|
31521
|
+
y2: polyline[i].y,
|
|
31522
|
+
color,
|
|
31523
|
+
lineWidth: 2
|
|
31524
|
+
});
|
|
31525
|
+
}
|
|
31526
|
+
return out;
|
|
31527
|
+
}
|
|
31528
|
+
function vectorShapes(v, bodyById) {
|
|
31529
|
+
let ax;
|
|
31530
|
+
let ay;
|
|
31531
|
+
if (v.body) {
|
|
31532
|
+
const anchor = bodyById.get(v.body);
|
|
31533
|
+
if (!anchor) return [];
|
|
31534
|
+
ax = anchor.x;
|
|
31535
|
+
ay = anchor.y;
|
|
31536
|
+
} else {
|
|
31537
|
+
ax = v.x ?? 0;
|
|
31538
|
+
ay = v.y ?? 0;
|
|
31539
|
+
}
|
|
31540
|
+
const scale = v.scale ?? 1;
|
|
31541
|
+
const color = v.color ?? "#dc2626";
|
|
31542
|
+
const tx = ax + v.dx * scale;
|
|
31543
|
+
const ty = ay + v.dy * scale;
|
|
31544
|
+
const out = [{ type: "arrow", x1: ax, y1: ay, x2: tx, y2: ty, color, lineWidth: 2, dash: v.dash }];
|
|
31545
|
+
if (v.label) {
|
|
31546
|
+
const dist = Math.max(1e-6, Math.hypot(tx - ax, ty - ay));
|
|
31547
|
+
const ux = (tx - ax) / dist;
|
|
31548
|
+
const uy = (ty - ay) / dist;
|
|
31549
|
+
out.push({
|
|
31550
|
+
type: "text",
|
|
31551
|
+
x: tx + 8 * ux,
|
|
31552
|
+
y: ty + 8 * uy,
|
|
31553
|
+
text: v.label,
|
|
31554
|
+
color,
|
|
31555
|
+
fontSize: 11,
|
|
31556
|
+
align: "center"
|
|
31557
|
+
});
|
|
31558
|
+
}
|
|
31559
|
+
return out;
|
|
31560
|
+
}
|
|
31561
|
+
function angleMarkerShapes(a) {
|
|
31562
|
+
const radius = a.radius ?? 26;
|
|
31563
|
+
const color = a.color ?? "#0ea5e9";
|
|
31564
|
+
const out = [
|
|
31565
|
+
{
|
|
31566
|
+
type: "ellipse",
|
|
31567
|
+
x: a.x,
|
|
31568
|
+
y: a.y,
|
|
31569
|
+
width: radius * 2,
|
|
31570
|
+
height: radius * 2,
|
|
31571
|
+
startAngle: a.from,
|
|
31572
|
+
endAngle: a.to,
|
|
31573
|
+
color,
|
|
31574
|
+
lineWidth: 2
|
|
31575
|
+
}
|
|
31576
|
+
];
|
|
31577
|
+
if (a.label) {
|
|
31578
|
+
const mid = (a.from + a.to) / 2 * (Math.PI / 180);
|
|
31579
|
+
out.push({
|
|
31580
|
+
type: "text",
|
|
31581
|
+
x: a.x + (radius + 13) * Math.cos(mid),
|
|
31582
|
+
y: a.y + (radius + 13) * Math.sin(mid),
|
|
31583
|
+
text: a.label,
|
|
31584
|
+
color,
|
|
31585
|
+
fontSize: 11,
|
|
31586
|
+
align: "center"
|
|
31587
|
+
});
|
|
31588
|
+
}
|
|
31589
|
+
return out;
|
|
31590
|
+
}
|
|
31591
|
+
function fieldShapes(field, canvasWidth, canvasHeight) {
|
|
31592
|
+
const spacing = field.spacing ?? 48;
|
|
31593
|
+
const size = field.size ?? 14;
|
|
31594
|
+
const color = field.color ?? "#94a3b8";
|
|
31595
|
+
const regionX = field.x ?? 0;
|
|
31596
|
+
const regionY = field.y ?? 0;
|
|
31597
|
+
const regionW = field.width ?? canvasWidth;
|
|
31598
|
+
const regionH = field.height ?? canvasHeight;
|
|
31599
|
+
const out = [];
|
|
31600
|
+
for (let gx = regionX + spacing / 2; gx < regionX + regionW; gx += spacing) {
|
|
31601
|
+
for (let gy = regionY + spacing / 2; gy < regionY + regionH; gy += spacing) {
|
|
31602
|
+
if (field.kind === "arrows") {
|
|
31603
|
+
const rad = (field.angle ?? 0) * Math.PI / 180;
|
|
31604
|
+
const hx = Math.cos(rad) * size / 2;
|
|
31605
|
+
const hy = Math.sin(rad) * size / 2;
|
|
31606
|
+
out.push({ type: "arrow", x1: gx - hx, y1: gy - hy, x2: gx + hx, y2: gy + hy, color, lineWidth: 2 });
|
|
31607
|
+
} else if (field.kind === "into") {
|
|
31608
|
+
const r = size / 3;
|
|
31609
|
+
const d = 0.6 * r * Math.SQRT1_2;
|
|
31610
|
+
out.push({ type: "circle", x: gx, y: gy, radius: r, color });
|
|
31611
|
+
out.push({ type: "line", x1: gx - d, y1: gy - d, x2: gx + d, y2: gy + d, color, lineWidth: 1 });
|
|
31612
|
+
out.push({ type: "line", x1: gx - d, y1: gy + d, x2: gx + d, y2: gy - d, color, lineWidth: 1 });
|
|
31613
|
+
} else {
|
|
31614
|
+
const r = size / 3;
|
|
31615
|
+
out.push({ type: "circle", x: gx, y: gy, radius: r, color });
|
|
31616
|
+
out.push({ type: "circle", x: gx, y: gy, radius: 1.5, color, fill: color });
|
|
31617
|
+
}
|
|
31618
|
+
}
|
|
31619
|
+
}
|
|
31620
|
+
return out;
|
|
31621
|
+
}
|
|
31622
|
+
function meterShapes(meters, canvasHeight) {
|
|
31623
|
+
const n = meters.length;
|
|
31624
|
+
const out = [];
|
|
31625
|
+
const sharedMax = Math.max(1e-6, ...meters.map((m) => m.value));
|
|
31626
|
+
meters.forEach((meter, i) => {
|
|
31627
|
+
const rowY = canvasHeight - 10 - 16 * (n - i);
|
|
31628
|
+
const color = meter.color ?? "#3b82f6";
|
|
31629
|
+
const M = meter.max ?? sharedMax;
|
|
31630
|
+
const w = Math.round(Math.min(1, Math.max(0, meter.value / M)) * 110);
|
|
31631
|
+
out.push({ type: "text", x: 8, y: rowY + 8, text: meter.label, color: PHYSICS_LABEL_COLOR, fontSize: 10 });
|
|
31632
|
+
out.push({ type: "rect", x: 52, y: rowY, width: w, height: 10, color, fill: color });
|
|
31633
|
+
out.push({
|
|
31634
|
+
type: "text",
|
|
31635
|
+
x: 166,
|
|
31636
|
+
y: rowY + 8,
|
|
31637
|
+
text: formatMeterValue(meter.value),
|
|
31638
|
+
color: "#6b7280",
|
|
31639
|
+
fontSize: 9
|
|
31640
|
+
});
|
|
31641
|
+
});
|
|
31642
|
+
return out;
|
|
31643
|
+
}
|
|
31644
|
+
var physicsLog2, PHYSICS_LABEL_COLOR, PhysicsCanvas;
|
|
29206
31645
|
var init_PhysicsCanvas = __esm({
|
|
29207
31646
|
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
29208
31647
|
"use client";
|
|
29209
31648
|
init_atoms();
|
|
29210
31649
|
init_Stack();
|
|
29211
31650
|
init_LearningCanvas();
|
|
31651
|
+
init_learningScene3D();
|
|
31652
|
+
physicsLog2 = createLogger("almadar:ui:physics-canvas");
|
|
31653
|
+
PHYSICS_LABEL_COLOR = "#374151";
|
|
29212
31654
|
PhysicsCanvas = ({
|
|
29213
31655
|
className,
|
|
29214
31656
|
width = 600,
|
|
29215
31657
|
height = 400,
|
|
29216
31658
|
title,
|
|
29217
31659
|
backgroundColor,
|
|
31660
|
+
mode = "2d",
|
|
31661
|
+
camera,
|
|
31662
|
+
lighting,
|
|
31663
|
+
post,
|
|
29218
31664
|
bodies = [],
|
|
29219
31665
|
constraints = [],
|
|
29220
31666
|
showVelocity = true,
|
|
29221
31667
|
showForces = false,
|
|
29222
31668
|
velocityScale = 20,
|
|
29223
31669
|
forceScale = 20,
|
|
31670
|
+
sceneObjects = [],
|
|
31671
|
+
trails = [],
|
|
31672
|
+
vectors = [],
|
|
31673
|
+
surface3d,
|
|
31674
|
+
vectors3d = [],
|
|
31675
|
+
vectorScale = 1,
|
|
31676
|
+
angles = [],
|
|
31677
|
+
field,
|
|
31678
|
+
meters = [],
|
|
29224
31679
|
shapes = [],
|
|
29225
|
-
|
|
31680
|
+
readouts,
|
|
31681
|
+
traces,
|
|
31682
|
+
showGrid,
|
|
31683
|
+
shadows,
|
|
31684
|
+
interactive,
|
|
29226
31685
|
animate = false,
|
|
29227
31686
|
onShapeClick,
|
|
29228
31687
|
isLoading,
|
|
@@ -29234,19 +31693,14 @@ var init_PhysicsCanvas = __esm({
|
|
|
29234
31693
|
for (const b of bodies) {
|
|
29235
31694
|
if (b.id) bodyById.set(b.id, b);
|
|
29236
31695
|
}
|
|
31696
|
+
if (field) out.push(...fieldShapes(field, width, height));
|
|
31697
|
+
for (const obj of sceneObjects) out.push(...sceneObjectShapes(obj, width, height));
|
|
31698
|
+
for (const trail of trails) out.push(...trailShapes(trail));
|
|
29237
31699
|
for (const c of constraints) {
|
|
29238
31700
|
const a = bodyById.get(c.from);
|
|
29239
31701
|
const b = bodyById.get(c.to);
|
|
29240
31702
|
if (!a || !b) continue;
|
|
29241
|
-
out.push(
|
|
29242
|
-
type: "line",
|
|
29243
|
-
x1: a.x,
|
|
29244
|
-
y1: a.y,
|
|
29245
|
-
x2: b.x,
|
|
29246
|
-
y2: b.y,
|
|
29247
|
-
color: c.color ?? "#9ca3af",
|
|
29248
|
-
lineWidth: 2
|
|
29249
|
-
});
|
|
31703
|
+
out.push(...constraintShapes(c, a, b));
|
|
29250
31704
|
}
|
|
29251
31705
|
for (const b of bodies) {
|
|
29252
31706
|
out.push({
|
|
@@ -29291,9 +31745,181 @@ var init_PhysicsCanvas = __esm({
|
|
|
29291
31745
|
});
|
|
29292
31746
|
}
|
|
29293
31747
|
}
|
|
31748
|
+
for (const v of vectors) out.push(...vectorShapes(v, bodyById));
|
|
31749
|
+
for (const a of angles) out.push(...angleMarkerShapes(a));
|
|
31750
|
+
if (meters.length > 0) out.push(...meterShapes(meters, height));
|
|
29294
31751
|
out.push(...shapes);
|
|
29295
31752
|
return out;
|
|
29296
|
-
}, [
|
|
31753
|
+
}, [
|
|
31754
|
+
bodies,
|
|
31755
|
+
constraints,
|
|
31756
|
+
showVelocity,
|
|
31757
|
+
showForces,
|
|
31758
|
+
velocityScale,
|
|
31759
|
+
forceScale,
|
|
31760
|
+
sceneObjects,
|
|
31761
|
+
trails,
|
|
31762
|
+
vectors,
|
|
31763
|
+
angles,
|
|
31764
|
+
field,
|
|
31765
|
+
meters,
|
|
31766
|
+
shapes,
|
|
31767
|
+
width,
|
|
31768
|
+
height
|
|
31769
|
+
]);
|
|
31770
|
+
const drawables3D = useMemo(() => {
|
|
31771
|
+
if (mode !== "3d") return [];
|
|
31772
|
+
if (shapes.length > 0) {
|
|
31773
|
+
physicsLog2.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
31774
|
+
}
|
|
31775
|
+
if (sceneObjects.length > 0) {
|
|
31776
|
+
physicsLog2.debug("sceneObjects ignored in 3D mode (pixel-authored 2D vocabulary)", { count: sceneObjects.length });
|
|
31777
|
+
}
|
|
31778
|
+
if (vectors.length > 0) {
|
|
31779
|
+
physicsLog2.debug("vectors ignored in 3D mode (pixel-authored 2D vocabulary)", { count: vectors.length });
|
|
31780
|
+
}
|
|
31781
|
+
if (angles.length > 0) {
|
|
31782
|
+
physicsLog2.debug("angles ignored in 3D mode (pixel-authored 2D vocabulary)", { count: angles.length });
|
|
31783
|
+
}
|
|
31784
|
+
if (field) {
|
|
31785
|
+
physicsLog2.debug("field ignored in 3D mode (pixel-authored 2D vocabulary)");
|
|
31786
|
+
}
|
|
31787
|
+
if (meters.length > 0) {
|
|
31788
|
+
physicsLog2.debug("meters ignored in 3D mode (pixel-authored 2D vocabulary)", { count: meters.length });
|
|
31789
|
+
}
|
|
31790
|
+
if (animate) {
|
|
31791
|
+
physicsLog2.debug("animate ignored in 3D mode (motion is entity-state driven)");
|
|
31792
|
+
}
|
|
31793
|
+
const out = [];
|
|
31794
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
31795
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
31796
|
+
for (const b of bodies) {
|
|
31797
|
+
if (b.id) bodyById.set(b.id, b);
|
|
31798
|
+
}
|
|
31799
|
+
for (const c of constraints) {
|
|
31800
|
+
const a = bodyById.get(c.from);
|
|
31801
|
+
const b = bodyById.get(c.to);
|
|
31802
|
+
if (!a || !b) continue;
|
|
31803
|
+
const rodRadius = Math.max(0.05, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.15);
|
|
31804
|
+
const rod = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], rodRadius, c.color ?? "#9ca3af");
|
|
31805
|
+
if (rod) out.push(rod);
|
|
31806
|
+
}
|
|
31807
|
+
for (const b of bodies) {
|
|
31808
|
+
const radius = b.radius ?? 0.5;
|
|
31809
|
+
const bz = b.z ?? 0;
|
|
31810
|
+
out.push(meshSphere(b.id, b.x, b.y, bz, radius, b.color ?? "#2563eb"));
|
|
31811
|
+
if (b.label) {
|
|
31812
|
+
out.push(billboardLabel(b.label, b.x, b.y, bz + radius, { color: labelColor }));
|
|
31813
|
+
}
|
|
31814
|
+
const vx = b.vx ?? 0;
|
|
31815
|
+
const vy = b.vy ?? 0;
|
|
31816
|
+
const vz = b.vz ?? 0;
|
|
31817
|
+
const arrowRadius = Math.max(0.05, radius * 0.15);
|
|
31818
|
+
if (showVelocity && (vx !== 0 || vy !== 0 || vz !== 0)) {
|
|
31819
|
+
const arrow = arrowBetween(
|
|
31820
|
+
[b.x, b.y, bz],
|
|
31821
|
+
[b.x + vx * velocityScale, b.y + vy * velocityScale, bz + vz * velocityScale],
|
|
31822
|
+
"#16a34a",
|
|
31823
|
+
arrowRadius
|
|
31824
|
+
);
|
|
31825
|
+
if (arrow) out.push(arrow);
|
|
31826
|
+
}
|
|
31827
|
+
const fx = b.fx ?? 0;
|
|
31828
|
+
const fy = b.fy ?? 0;
|
|
31829
|
+
const fz = b.fz ?? 0;
|
|
31830
|
+
if (showForces && (fx !== 0 || fy !== 0 || fz !== 0)) {
|
|
31831
|
+
const arrow = arrowBetween(
|
|
31832
|
+
[b.x, b.y, bz],
|
|
31833
|
+
[b.x + fx * forceScale, b.y + fy * forceScale, bz + fz * forceScale],
|
|
31834
|
+
"#dc2626",
|
|
31835
|
+
arrowRadius
|
|
31836
|
+
);
|
|
31837
|
+
if (arrow) out.push(arrow);
|
|
31838
|
+
}
|
|
31839
|
+
}
|
|
31840
|
+
for (const trail of trails) {
|
|
31841
|
+
if (trail.fade !== void 0) {
|
|
31842
|
+
physicsLog2.debug("trail.fade ignored in 3D mode (2D-only fade curve \u2014 3D draws an opaque tube)", { id: trail.id });
|
|
31843
|
+
}
|
|
31844
|
+
const points = trail.points.map((p) => [p.x, p.y, p.z ?? 0]);
|
|
31845
|
+
out.push(
|
|
31846
|
+
...polylineTube(points, trail.width ?? 0.05, trail.color ?? "#94a3b8", {
|
|
31847
|
+
...trail.opacity !== void 0 ? { opacity: trail.opacity } : {}
|
|
31848
|
+
})
|
|
31849
|
+
);
|
|
31850
|
+
}
|
|
31851
|
+
if (surface3d) {
|
|
31852
|
+
out.push(...heightFieldMesh(surface3d));
|
|
31853
|
+
}
|
|
31854
|
+
if (vectors3d.length > 0) {
|
|
31855
|
+
out.push(
|
|
31856
|
+
...arrowField(
|
|
31857
|
+
vectors3d.map((v) => ({
|
|
31858
|
+
id: v.id,
|
|
31859
|
+
from: [v.x, v.y, v.z ?? 0],
|
|
31860
|
+
delta: [v.dx, v.dy, v.dz ?? 0],
|
|
31861
|
+
color: v.color,
|
|
31862
|
+
label: v.label,
|
|
31863
|
+
width: v.width
|
|
31864
|
+
})),
|
|
31865
|
+
{ scale: vectorScale, labelColor }
|
|
31866
|
+
)
|
|
31867
|
+
);
|
|
31868
|
+
}
|
|
31869
|
+
return out;
|
|
31870
|
+
}, [
|
|
31871
|
+
mode,
|
|
31872
|
+
bodies,
|
|
31873
|
+
constraints,
|
|
31874
|
+
showVelocity,
|
|
31875
|
+
showForces,
|
|
31876
|
+
velocityScale,
|
|
31877
|
+
forceScale,
|
|
31878
|
+
shapes,
|
|
31879
|
+
sceneObjects,
|
|
31880
|
+
trails,
|
|
31881
|
+
vectors,
|
|
31882
|
+
surface3d,
|
|
31883
|
+
vectors3d,
|
|
31884
|
+
vectorScale,
|
|
31885
|
+
angles,
|
|
31886
|
+
field,
|
|
31887
|
+
meters,
|
|
31888
|
+
animate,
|
|
31889
|
+
backgroundColor
|
|
31890
|
+
]);
|
|
31891
|
+
const bodyIndexById = useMemo(() => {
|
|
31892
|
+
const m = /* @__PURE__ */ new Map();
|
|
31893
|
+
vectors3d.forEach((v, i) => {
|
|
31894
|
+
if (v.id) m.set(v.id, i);
|
|
31895
|
+
});
|
|
31896
|
+
bodies.forEach((b, i) => {
|
|
31897
|
+
if (b.id) m.set(b.id, i);
|
|
31898
|
+
});
|
|
31899
|
+
return m;
|
|
31900
|
+
}, [bodies, vectors3d]);
|
|
31901
|
+
if (mode === "3d") {
|
|
31902
|
+
return /* @__PURE__ */ jsx(
|
|
31903
|
+
LearningScene3D,
|
|
31904
|
+
{
|
|
31905
|
+
className,
|
|
31906
|
+
width,
|
|
31907
|
+
height,
|
|
31908
|
+
title,
|
|
31909
|
+
backgroundColor,
|
|
31910
|
+
drawables: drawables3D,
|
|
31911
|
+
camera,
|
|
31912
|
+
lighting,
|
|
31913
|
+
post,
|
|
31914
|
+
showGrid,
|
|
31915
|
+
shadows,
|
|
31916
|
+
interactive,
|
|
31917
|
+
isLoading,
|
|
31918
|
+
error,
|
|
31919
|
+
onItemClick: get3DClickPayload(onShapeClick, bodyIndexById)
|
|
31920
|
+
}
|
|
31921
|
+
);
|
|
31922
|
+
}
|
|
29297
31923
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
29298
31924
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
29299
31925
|
/* @__PURE__ */ jsx(
|
|
@@ -29303,7 +31929,9 @@ var init_PhysicsCanvas = __esm({
|
|
|
29303
31929
|
height,
|
|
29304
31930
|
backgroundColor,
|
|
29305
31931
|
shapes: derivedShapes,
|
|
29306
|
-
|
|
31932
|
+
readouts,
|
|
31933
|
+
traces,
|
|
31934
|
+
interactive: interactive ?? false,
|
|
29307
31935
|
animate,
|
|
29308
31936
|
onShapeClick,
|
|
29309
31937
|
isLoading,
|
|
@@ -29453,7 +32081,7 @@ function layoutFlow(nodeIds, adjacency, roots, width, height, margin) {
|
|
|
29453
32081
|
}
|
|
29454
32082
|
return nodeIds.map((id) => positions.get(id));
|
|
29455
32083
|
}
|
|
29456
|
-
function
|
|
32084
|
+
function layoutTree2(nodeIds, adjacency, roots, width, height, margin) {
|
|
29457
32085
|
const effectiveRoots = roots.length > 0 ? roots : [nodeIds[0]];
|
|
29458
32086
|
const layers = assignLayers(nodeIds, adjacency, effectiveRoots);
|
|
29459
32087
|
const maxLayer = Math.max(...Array.from(layers.values()));
|
|
@@ -29509,7 +32137,7 @@ function computeStaticLayout(mode, input) {
|
|
|
29509
32137
|
const adjacency = buildAdjacency(nodeIds, edges);
|
|
29510
32138
|
const roots = findRoots(nodeIds, adjacency);
|
|
29511
32139
|
if (mode === "flow") return layoutFlow(nodeIds, adjacency, roots, width, height, margin);
|
|
29512
|
-
if (mode === "tree") return
|
|
32140
|
+
if (mode === "tree") return layoutTree2(nodeIds, adjacency, roots, width, height, margin);
|
|
29513
32141
|
return layoutRadial(nodeIds, adjacency, roots, width, height, margin);
|
|
29514
32142
|
}
|
|
29515
32143
|
var init_graphViewLayouts = __esm({
|
|
@@ -29921,12 +32549,12 @@ var init_MapView = __esm({
|
|
|
29921
32549
|
shadowSize: [41, 41]
|
|
29922
32550
|
});
|
|
29923
32551
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
29924
|
-
const { useEffect: useEffect66, useRef:
|
|
32552
|
+
const { useEffect: useEffect66, useRef: useRef65, useCallback: useCallback108, useState: useState104 } = React77__default;
|
|
29925
32553
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
29926
32554
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
29927
32555
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
29928
32556
|
const map = useMap();
|
|
29929
|
-
const prevRef =
|
|
32557
|
+
const prevRef = useRef65({ centerLat, centerLng, zoom });
|
|
29930
32558
|
useEffect66(() => {
|
|
29931
32559
|
const prev = prevRef.current;
|
|
29932
32560
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
@@ -29967,7 +32595,7 @@ var init_MapView = __esm({
|
|
|
29967
32595
|
}) {
|
|
29968
32596
|
const eventBus = useEventBus2();
|
|
29969
32597
|
const [clickedPosition, setClickedPosition] = useState104(null);
|
|
29970
|
-
const handleMapClick =
|
|
32598
|
+
const handleMapClick = useCallback108((lat, lng) => {
|
|
29971
32599
|
if (showClickedPin) {
|
|
29972
32600
|
setClickedPosition({ lat, lng });
|
|
29973
32601
|
}
|
|
@@ -29976,7 +32604,7 @@ var init_MapView = __esm({
|
|
|
29976
32604
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
29977
32605
|
}
|
|
29978
32606
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
29979
|
-
const handleMarkerClick =
|
|
32607
|
+
const handleMarkerClick = useCallback108((marker) => {
|
|
29980
32608
|
onMarkerClick?.(marker);
|
|
29981
32609
|
if (markerClickEvent) {
|
|
29982
32610
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -30752,26 +33380,6 @@ var init_Lightbox = __esm({
|
|
|
30752
33380
|
Lightbox.displayName = "Lightbox";
|
|
30753
33381
|
}
|
|
30754
33382
|
});
|
|
30755
|
-
function useMediaQuery(query) {
|
|
30756
|
-
const subscribe = useCallback(
|
|
30757
|
-
(onChange) => {
|
|
30758
|
-
const mql = window.matchMedia(query);
|
|
30759
|
-
mql.addEventListener("change", onChange);
|
|
30760
|
-
return () => mql.removeEventListener("change", onChange);
|
|
30761
|
-
},
|
|
30762
|
-
[query]
|
|
30763
|
-
);
|
|
30764
|
-
return useSyncExternalStore(
|
|
30765
|
-
subscribe,
|
|
30766
|
-
() => window.matchMedia(query).matches,
|
|
30767
|
-
() => false
|
|
30768
|
-
);
|
|
30769
|
-
}
|
|
30770
|
-
var init_useMediaQuery = __esm({
|
|
30771
|
-
"hooks/useMediaQuery.ts"() {
|
|
30772
|
-
"use client";
|
|
30773
|
-
}
|
|
30774
|
-
});
|
|
30775
33383
|
function renderIconInput3(icon, props) {
|
|
30776
33384
|
return typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsx(Icon, { icon, ...props });
|
|
30777
33385
|
}
|
|
@@ -30810,8 +33418,8 @@ function TableView({
|
|
|
30810
33418
|
columns,
|
|
30811
33419
|
fields,
|
|
30812
33420
|
itemActions,
|
|
30813
|
-
maxInlineActions,
|
|
30814
|
-
itemClickEvent,
|
|
33421
|
+
maxInlineActions: _maxInlineActions,
|
|
33422
|
+
itemClickEvent = "",
|
|
30815
33423
|
selectable = false,
|
|
30816
33424
|
selectEvent,
|
|
30817
33425
|
selectedIds,
|
|
@@ -30861,7 +33469,6 @@ function TableView({
|
|
|
30861
33469
|
const hasMore = pageSize > 0 && visibleCount < ordered2.length;
|
|
30862
33470
|
const hasRenderProp = typeof children === "function";
|
|
30863
33471
|
const idField = dndItemIdField ?? "id";
|
|
30864
|
-
const isCoarsePointer = useMediaQuery("(pointer: coarse)");
|
|
30865
33472
|
React77__default.useEffect(() => {
|
|
30866
33473
|
tableViewLog.debug("render", {
|
|
30867
33474
|
rowCount: data.length,
|
|
@@ -30903,21 +33510,14 @@ function TableView({
|
|
|
30903
33510
|
const dir = sortColumn === (col.field ?? col.key) && sortDirection === "asc" ? "desc" : "asc";
|
|
30904
33511
|
eventBus.emit(`UI:${sortEvent}`, { column: col.field ?? col.key, direction: dir });
|
|
30905
33512
|
};
|
|
30906
|
-
const
|
|
30907
|
-
e.stopPropagation();
|
|
30908
|
-
const payload = {
|
|
30909
|
-
id: row.id,
|
|
30910
|
-
row
|
|
30911
|
-
};
|
|
30912
|
-
eventBus.emit(`UI:${action.event}`, payload);
|
|
30913
|
-
};
|
|
33513
|
+
const rowClickEvent = itemClickEvent || actionDefs.find((a) => a.variant !== "danger")?.event;
|
|
30914
33514
|
const handleRowClick = (row) => () => {
|
|
30915
|
-
if (!
|
|
33515
|
+
if (!rowClickEvent) return;
|
|
30916
33516
|
const payload = {
|
|
30917
33517
|
id: row.id,
|
|
30918
33518
|
row
|
|
30919
33519
|
};
|
|
30920
|
-
eventBus.emit(`UI:${
|
|
33520
|
+
eventBus.emit(`UI:${rowClickEvent}`, payload);
|
|
30921
33521
|
};
|
|
30922
33522
|
const colFloors = React77__default.useMemo(
|
|
30923
33523
|
() => colDefs.map((col) => {
|
|
@@ -30933,10 +33533,7 @@ function TableView({
|
|
|
30933
33533
|
const statusNode = isLoading ? /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") }) }) : error ? /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) }) : data.length === 0 ? /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") }) }) : null;
|
|
30934
33534
|
const lk = LOOKS[look];
|
|
30935
33535
|
const hasActions = actionDefs.length > 0;
|
|
30936
|
-
const
|
|
30937
|
-
const inlineActionCount = hasActions ? effectiveMaxInline != null ? Math.min(actionDefs.length, effectiveMaxInline) : actionDefs.length : 0;
|
|
30938
|
-
const hasOverflowActions = hasActions && effectiveMaxInline != null && actionDefs.length > effectiveMaxInline;
|
|
30939
|
-
const actionsTrack = hasActions ? `${inlineActionCount * 6 + (hasOverflowActions ? 3 : 0)}rem` : null;
|
|
33536
|
+
const actionsTrack = hasActions ? "3rem" : null;
|
|
30940
33537
|
const gridTemplateColumns = [
|
|
30941
33538
|
selectable ? "auto" : null,
|
|
30942
33539
|
...colDefs.map((c, i) => c.width ?? `minmax(${colFloors[i]}ch, 1fr)`),
|
|
@@ -30983,7 +33580,7 @@ function TableView({
|
|
|
30983
33580
|
col.key
|
|
30984
33581
|
);
|
|
30985
33582
|
}),
|
|
30986
|
-
hasActions && /* @__PURE__ */ jsx(Box, { "aria-hidden": true, className: "sticky right-0 bg-[var(--color-surface-subtle)]" })
|
|
33583
|
+
hasActions && /* @__PURE__ */ jsx(Box, { "aria-hidden": true, className: "sticky right-0 bg-[var(--color-surface-subtle)] border-l border-[var(--color-border)] h-full" })
|
|
30987
33584
|
]
|
|
30988
33585
|
}
|
|
30989
33586
|
);
|
|
@@ -30995,12 +33592,12 @@ function TableView({
|
|
|
30995
33592
|
role: "row",
|
|
30996
33593
|
"data-entity-row": true,
|
|
30997
33594
|
"data-entity-id": id,
|
|
30998
|
-
onClick:
|
|
33595
|
+
onClick: rowClickEvent ? handleRowClick(row) : void 0,
|
|
30999
33596
|
style: !hasRenderProp ? { gridTemplateColumns } : void 0,
|
|
31000
33597
|
className: cn(
|
|
31001
33598
|
"group items-center gap-3 transition-colors duration-fast",
|
|
31002
33599
|
hasRenderProp ? "flex" : "grid",
|
|
31003
|
-
|
|
33600
|
+
rowClickEvent && "cursor-pointer",
|
|
31004
33601
|
lk.rowPad,
|
|
31005
33602
|
lk.divider && "border-b border-[var(--color-border)]",
|
|
31006
33603
|
lk.striped && index % 2 === 1 && "bg-[var(--color-surface-subtle)]",
|
|
@@ -31008,7 +33605,7 @@ function TableView({
|
|
|
31008
33605
|
look === "bordered" && "[&>*]:border-r [&>*]:border-[var(--color-border)] [&>*:last-child]:border-r-0"
|
|
31009
33606
|
),
|
|
31010
33607
|
children: [
|
|
31011
|
-
selectable && /* @__PURE__ */ jsx(Box, { className: "flex items-center", onClick:
|
|
33608
|
+
selectable && /* @__PURE__ */ jsx(Box, { className: "flex items-center", onClick: rowClickEvent ? (e) => e.stopPropagation() : void 0, children: /* @__PURE__ */ jsx(
|
|
31012
33609
|
Checkbox,
|
|
31013
33610
|
{
|
|
31014
33611
|
checked: selected.has(id),
|
|
@@ -31029,53 +33626,37 @@ function TableView({
|
|
|
31029
33626
|
}
|
|
31030
33627
|
return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
|
|
31031
33628
|
}),
|
|
31032
|
-
hasActions && /* @__PURE__ */
|
|
33629
|
+
hasActions && /* @__PURE__ */ jsx(
|
|
31033
33630
|
HStack,
|
|
31034
33631
|
{
|
|
31035
33632
|
gap: "xs",
|
|
31036
|
-
onClick:
|
|
33633
|
+
onClick: rowClickEvent ? (e) => e.stopPropagation() : void 0,
|
|
31037
33634
|
className: cn(
|
|
31038
33635
|
// Pinned: the fixed column tracks routinely overflow the caller's
|
|
31039
|
-
// scroll container, which
|
|
31040
|
-
// Opaque so
|
|
33636
|
+
// scroll container, which would leave the kebab off-screen.
|
|
33637
|
+
// Opaque + hairline edge so it reads as a pinned column, not a
|
|
33638
|
+
// floating control, while scrolled cells pass underneath.
|
|
31041
33639
|
"justify-end flex-shrink-0 sticky right-0 z-[1] transition-colors",
|
|
33640
|
+
"border-l border-[var(--color-border)]",
|
|
31042
33641
|
lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
|
|
31043
33642
|
),
|
|
31044
|
-
children:
|
|
31045
|
-
|
|
31046
|
-
|
|
31047
|
-
|
|
31048
|
-
|
|
31049
|
-
|
|
31050
|
-
|
|
31051
|
-
|
|
31052
|
-
|
|
31053
|
-
|
|
31054
|
-
|
|
31055
|
-
|
|
31056
|
-
|
|
31057
|
-
|
|
31058
|
-
}
|
|
31059
|
-
|
|
31060
|
-
|
|
31061
|
-
effectiveMaxInline != null && actionDefs.length > effectiveMaxInline && /* @__PURE__ */ jsx(
|
|
31062
|
-
Menu,
|
|
31063
|
-
{
|
|
31064
|
-
position: "bottom-end",
|
|
31065
|
-
trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
31066
|
-
items: actionDefs.slice(effectiveMaxInline).map((action) => ({
|
|
31067
|
-
label: action.label,
|
|
31068
|
-
icon: action.icon,
|
|
31069
|
-
event: action.event,
|
|
31070
|
-
variant: action.variant === "danger" ? "danger" : "default",
|
|
31071
|
-
onClick: () => eventBus.emit(`UI:${action.event}`, {
|
|
31072
|
-
id: row.id,
|
|
31073
|
-
row
|
|
31074
|
-
})
|
|
31075
|
-
}))
|
|
31076
|
-
}
|
|
31077
|
-
)
|
|
31078
|
-
]
|
|
33643
|
+
children: /* @__PURE__ */ jsx(
|
|
33644
|
+
Menu,
|
|
33645
|
+
{
|
|
33646
|
+
position: "bottom-end",
|
|
33647
|
+
trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", "data-row-id": String(row.id), children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
|
|
33648
|
+
items: actionDefs.map((action) => ({
|
|
33649
|
+
label: action.label,
|
|
33650
|
+
icon: action.icon,
|
|
33651
|
+
event: action.event,
|
|
33652
|
+
variant: action.variant === "danger" ? "danger" : "default",
|
|
33653
|
+
onClick: () => eventBus.emit(`UI:${action.event}`, {
|
|
33654
|
+
id: row.id,
|
|
33655
|
+
row
|
|
33656
|
+
})
|
|
33657
|
+
}))
|
|
33658
|
+
}
|
|
33659
|
+
)
|
|
31079
33660
|
}
|
|
31080
33661
|
)
|
|
31081
33662
|
]
|
|
@@ -31118,7 +33699,6 @@ var init_TableView = __esm({
|
|
|
31118
33699
|
init_format();
|
|
31119
33700
|
init_getNestedValue();
|
|
31120
33701
|
init_useEventBus();
|
|
31121
|
-
init_useMediaQuery();
|
|
31122
33702
|
init_Box();
|
|
31123
33703
|
init_Stack();
|
|
31124
33704
|
init_Typography();
|
|
@@ -39718,6 +42298,8 @@ var init_molecules2 = __esm({
|
|
|
39718
42298
|
init_BiologyCanvas();
|
|
39719
42299
|
init_ChemistryCanvas();
|
|
39720
42300
|
init_AlgorithmCanvas();
|
|
42301
|
+
init_AlgoGraphCanvas();
|
|
42302
|
+
init_learningScene3D();
|
|
39721
42303
|
init_GraphView();
|
|
39722
42304
|
init_MapView();
|
|
39723
42305
|
init_NumberStepper();
|
|
@@ -40971,7 +43553,7 @@ function getEnumOptions(field) {
|
|
|
40971
43553
|
}));
|
|
40972
43554
|
}
|
|
40973
43555
|
const validation = field.validation;
|
|
40974
|
-
if (validation?.enum &&
|
|
43556
|
+
if (validation?.enum && validation.enum.length > 0) {
|
|
40975
43557
|
return validation.enum.map((v) => ({
|
|
40976
43558
|
value: v,
|
|
40977
43559
|
label: v.charAt(0).toUpperCase() + v.slice(1).replace(/_/g, " ")
|
|
@@ -46399,6 +48981,7 @@ var init_component_registry_generated = __esm({
|
|
|
46399
48981
|
init_ActionTile();
|
|
46400
48982
|
init_ActivationBlock();
|
|
46401
48983
|
init_ComponentPatterns();
|
|
48984
|
+
init_AlgoGraphCanvas();
|
|
46402
48985
|
init_AlgorithmCanvas();
|
|
46403
48986
|
init_AnimatedCounter();
|
|
46404
48987
|
init_AnimatedGraphic();
|
|
@@ -46662,6 +49245,7 @@ var init_component_registry_generated = __esm({
|
|
|
46662
49245
|
"ActivationBlock": ActivationBlock,
|
|
46663
49246
|
"Alert": AlertPattern,
|
|
46664
49247
|
"AlertPattern": AlertPattern,
|
|
49248
|
+
"AlgoGraphCanvas": AlgoGraphCanvas,
|
|
46665
49249
|
"AlgorithmCanvas": AlgorithmCanvas,
|
|
46666
49250
|
"AnimatedCounter": AnimatedCounter,
|
|
46667
49251
|
"AnimatedGraphic": AnimatedGraphic,
|
|
@@ -50134,7 +52718,23 @@ init_useAuthContext();
|
|
|
50134
52718
|
init_useSwipeGesture();
|
|
50135
52719
|
init_useLongPress();
|
|
50136
52720
|
init_useDragReorder();
|
|
50137
|
-
|
|
52721
|
+
function useMediaQuery(query) {
|
|
52722
|
+
const subscribe = useCallback(
|
|
52723
|
+
(onChange) => {
|
|
52724
|
+
const mql = window.matchMedia(query);
|
|
52725
|
+
mql.addEventListener("change", onChange);
|
|
52726
|
+
return () => mql.removeEventListener("change", onChange);
|
|
52727
|
+
},
|
|
52728
|
+
[query]
|
|
52729
|
+
);
|
|
52730
|
+
return useSyncExternalStore(
|
|
52731
|
+
subscribe,
|
|
52732
|
+
() => window.matchMedia(query).matches,
|
|
52733
|
+
() => false
|
|
52734
|
+
);
|
|
52735
|
+
}
|
|
52736
|
+
|
|
52737
|
+
// hooks/index.ts
|
|
50138
52738
|
init_useInfiniteScroll();
|
|
50139
52739
|
init_usePullToRefresh();
|
|
50140
52740
|
init_useCanvasGestures();
|
|
@@ -50380,4 +52980,4 @@ function useGitHubBranches(owner, repo, enabled = true) {
|
|
|
50380
52980
|
});
|
|
50381
52981
|
}
|
|
50382
52982
|
|
|
50383
|
-
export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AlgorithmCanvas, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AtlasImage, AtlasPanel, AuthLayout, Avatar, Badge, BehaviorView, BiologyCanvas, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, Button, ButtonGroup, CTABanner, CalendarGrid, Canvas, Canvas2D, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, Center, Chart, ChartLegend, ChatBar, Checkbox, ChemistryCanvas, ChoiceButton, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, ControlGrid, CounterTemplate, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DEFAULT_SLOTS, DIAMOND_TOP_Y, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DetailPanel, Dialog, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, ELEMENT_SELECTED_EVENT, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmojiPicker, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioToggle, GameHud, GameIcon, GameMenu, GameShell, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, IconPicker, ImportPreviewTree, ImportProgress, ImportSourcePicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, LearningCanvas, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MathCanvas, MatrixQuestion, MediaGallery, Menu, Meter, Modal, ModalSlot, ModuleCard, Navigation, NodeSlotEditor, NotifyListener, NumberStepper, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, PageTransition, Pagination, PatternTile, PhysicsCanvas, Popover, PositionedCanvas, Presence, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, RichBlockEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, ServiceCatalog, SharedEntityStoreContext, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateGraph, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StepFlow, StepFlowOrganism, SubagentTracePanel, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, VersionDiff, ViolationAlert, VoteStack, WizardContainer, WizardNavigation, WizardProgress, boardEntity, bool, calculateAttackTargets, calculateValidMoves, cn, createInitialGameState, createSharedEntityStore, createTranslate, createUnitAnimationState, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, makeAsset, makeAssetMap, mapBookData, num, objAvailableActions, objAvailableEvents, objCurrentState, objIcon, objId, objMaxRules, objName, objRules, objStates, parseEditFocus, parseLessonSegments, parseMarkdownWithCodeBlocks, parseQueryBinding, pendulum, projectileMotion, registerCodeLanguageLoader, resolveFieldMap, resolveFrame, resolveSheetDirection, rows, runTickFrame, screenToIso, springOscillator, str, tickAnimationState, toCodeLanguage, transitionAnimation, unitHealth, unitPosition, unitTeam, useAgentChat, useAnchorRect, useAtlasSliceDataUrl, useAuthContext, useCamera, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useLongPress, useMediaQuery, useOrbitalHistory, usePresence, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate114 as useTranslate, useUIEvents, useUISlotManager, useUnitSpriteAtlas, useValidation, vec2 };
|
|
52983
|
+
export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AlgoGraphCanvas, AlgorithmCanvas, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AtlasImage, AtlasPanel, AuthLayout, Avatar, Badge, BehaviorView, BiologyCanvas, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, Button, ButtonGroup, CTABanner, CalendarGrid, Canvas, Canvas2D, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, Center, Chart, ChartLegend, ChatBar, Checkbox, ChemistryCanvas, ChoiceButton, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, ControlGrid, CounterTemplate, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DEFAULT_SLOTS, DIAMOND_TOP_Y, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DetailPanel, Dialog, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, ELEMENT_SELECTED_EVENT, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmojiPicker, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioToggle, GameHud, GameIcon, GameMenu, GameShell, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, IconPicker, ImportPreviewTree, ImportProgress, ImportSourcePicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, LearningCanvas, LearningScene3D, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MathCanvas, MatrixQuestion, MediaGallery, Menu, Meter, Modal, ModalSlot, ModuleCard, Navigation, NodeSlotEditor, NotifyListener, NumberStepper, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, PageTransition, Pagination, PatternTile, PhysicsCanvas, Popover, PositionedCanvas, Presence, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, RichBlockEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, ServiceCatalog, SharedEntityStoreContext, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateGraph, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StepFlow, StepFlowOrganism, SubagentTracePanel, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, VersionDiff, ViolationAlert, VoteStack, WizardContainer, WizardNavigation, WizardProgress, arrowBetween, billboardLabel, boardEntity, bool, calculateAttackTargets, calculateValidMoves, cn, createInitialGameState, createSharedEntityStore, createTranslate, createUnitAnimationState, cylinderBetween, get3DClickPayload, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, makeAsset, makeAssetMap, mapBookData, meshSphere, num, objAvailableActions, objAvailableEvents, objCurrentState, objIcon, objId, objMaxRules, objName, objRules, objStates, parseEditFocus, parseLessonSegments, parseMarkdownWithCodeBlocks, parseQueryBinding, pendulum, projectileMotion, registerCodeLanguageLoader, resolveFieldMap, resolveFrame, resolveSheetDirection, rows, runTickFrame, screenToIso, springOscillator, str, tickAnimationState, toCodeLanguage, transitionAnimation, unitHealth, unitPosition, unitTeam, useAgentChat, useAnchorRect, useAtlasSliceDataUrl, useAuthContext, useCamera, useCanvasGestures, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useLongPress, useMediaQuery, useOrbitalHistory, usePresence, usePreview, usePullToRefresh, useQuerySingleton, useRenderInterpolation, useSharedEntitySnapshot, useSharedEntityStore, useSharedEntityStoreContext, useSwipeGesture, useTapReveal, useTraitListens, useTranslate114 as useTranslate, useUIEvents, useUISlotManager, useUnitSpriteAtlas, useValidation, vec2 };
|