@almadar/ui 5.147.0 → 5.149.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 +535 -15
- package/dist/avl/index.js +535 -15
- 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 +545 -18
- package/dist/components/index.d.cts +278 -98
- package/dist/components/index.d.ts +278 -98
- package/dist/components/index.js +541 -20
- package/dist/lib/drawable/three/index.cjs +56 -29
- 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 +56 -29
- package/dist/lib/index.d.cts +3 -2
- package/dist/lib/index.d.ts +3 -2
- 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 +535 -15
- package/dist/providers/index.d.cts +2 -1
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/index.js +535 -15
- package/dist/runtime/index.cjs +535 -15
- package/dist/runtime/index.js +535 -15
- package/dist/types-B3nHgnMG.d.cts +51 -0
- package/dist/types-B3nHgnMG.d.ts +51 -0
- package/package.json +4 -4
package/dist/runtime/index.js
CHANGED
|
@@ -10655,9 +10655,10 @@ function Canvas({
|
|
|
10655
10655
|
drawables: [...drawables ?? [], ...childDrawables],
|
|
10656
10656
|
isLoading,
|
|
10657
10657
|
cameraMode: to3DCameraMode(camera?.mode),
|
|
10658
|
-
...zoom !== void 0 ? {
|
|
10658
|
+
...zoom !== void 0 ? { zoom } : {},
|
|
10659
10659
|
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
10660
10660
|
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
10661
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
10661
10662
|
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
10662
10663
|
unitScale,
|
|
10663
10664
|
backgroundColor,
|
|
@@ -11866,7 +11867,10 @@ var init_LearningCanvas = __esm({
|
|
|
11866
11867
|
ctx.fillRect(0, 0, width, height);
|
|
11867
11868
|
}
|
|
11868
11869
|
for (const shape of shapes) {
|
|
11869
|
-
drawShape(ctx, shape, width, height);
|
|
11870
|
+
if (shape.type !== "text") drawShape(ctx, shape, width, height);
|
|
11871
|
+
}
|
|
11872
|
+
for (const shape of shapes) {
|
|
11873
|
+
if (shape.type === "text") drawShape(ctx, shape, width, height);
|
|
11870
11874
|
}
|
|
11871
11875
|
}, [width, height, backgroundColor, shapes]);
|
|
11872
11876
|
useEffect(() => {
|
|
@@ -13429,7 +13433,7 @@ var init_AlgorithmCanvas = __esm({
|
|
|
13429
13433
|
DEFAULT_CELL_COLOR = "#e5e7eb";
|
|
13430
13434
|
DEFAULT_POINTER_COLOR = "#dc2626";
|
|
13431
13435
|
POINTER_BAND = 34;
|
|
13432
|
-
TOP_PAD =
|
|
13436
|
+
TOP_PAD = 26;
|
|
13433
13437
|
AlgorithmCanvas = ({
|
|
13434
13438
|
className,
|
|
13435
13439
|
width = 600,
|
|
@@ -14533,23 +14537,188 @@ var init_BehaviorView = __esm({
|
|
|
14533
14537
|
BehaviorView.displayName = "BehaviorView";
|
|
14534
14538
|
}
|
|
14535
14539
|
});
|
|
14536
|
-
|
|
14540
|
+
function LearningScene3D({
|
|
14541
|
+
className,
|
|
14542
|
+
width = 600,
|
|
14543
|
+
height = 400,
|
|
14544
|
+
title,
|
|
14545
|
+
backgroundColor,
|
|
14546
|
+
drawables,
|
|
14547
|
+
camera,
|
|
14548
|
+
lighting,
|
|
14549
|
+
post,
|
|
14550
|
+
showGrid = false,
|
|
14551
|
+
shadows,
|
|
14552
|
+
interactive,
|
|
14553
|
+
isLoading,
|
|
14554
|
+
error,
|
|
14555
|
+
onItemClick
|
|
14556
|
+
}) {
|
|
14557
|
+
const instanceId = useId().replace(/[^a-zA-Z0-9]/g, "");
|
|
14558
|
+
const clickEvent = onItemClick ? `LEARNING_SCENE_3D.ITEM_CLICK.${instanceId}` : void 0;
|
|
14559
|
+
const onItemClickRef = useRef(onItemClick);
|
|
14560
|
+
onItemClickRef.current = onItemClick;
|
|
14561
|
+
useEventListener(`UI:${clickEvent ?? "LEARNING_SCENE_3D.ITEM_CLICK.__disabled"}`, (event) => {
|
|
14562
|
+
const unitId = event.payload?.unitId;
|
|
14563
|
+
if (typeof unitId === "string") onItemClickRef.current?.(unitId);
|
|
14564
|
+
});
|
|
14565
|
+
const props3d = {
|
|
14566
|
+
drawables,
|
|
14567
|
+
isLoading,
|
|
14568
|
+
error: error ? error.message : null,
|
|
14569
|
+
cameraMode: camera?.mode ?? "perspective",
|
|
14570
|
+
...camera?.zoom !== void 0 ? { zoom: camera.zoom } : {},
|
|
14571
|
+
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
14572
|
+
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
14573
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
14574
|
+
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
14575
|
+
backgroundColor,
|
|
14576
|
+
showGrid,
|
|
14577
|
+
...shadows !== void 0 ? { shadows } : {},
|
|
14578
|
+
...interactive !== void 0 ? { controlsEnabled: interactive } : {},
|
|
14579
|
+
lighting,
|
|
14580
|
+
post,
|
|
14581
|
+
...clickEvent !== void 0 ? { unitClickEvent: clickEvent } : {}
|
|
14582
|
+
};
|
|
14583
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
14584
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
14585
|
+
/* @__PURE__ */ jsx("div", { style: { width, height, display: "flex" }, children: /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(Canvas3DHost2, { ...props3d }) }) })
|
|
14586
|
+
] }) });
|
|
14587
|
+
}
|
|
14588
|
+
function meshSphere(id, x, y, z, radius, color, opts) {
|
|
14589
|
+
return {
|
|
14590
|
+
type: "draw-mesh",
|
|
14591
|
+
...id !== void 0 ? { id } : {},
|
|
14592
|
+
shape: opts?.shape ?? "sphere",
|
|
14593
|
+
position: { x, y, z },
|
|
14594
|
+
radius,
|
|
14595
|
+
pivot: "center",
|
|
14596
|
+
segments: opts?.segments ?? 24,
|
|
14597
|
+
material: { color, ...opts?.material },
|
|
14598
|
+
...opts?.opacity !== void 0 ? { opacity: opts.opacity } : {}
|
|
14599
|
+
};
|
|
14600
|
+
}
|
|
14601
|
+
function axisRotation(from, to) {
|
|
14602
|
+
const wx = to[0] - from[0];
|
|
14603
|
+
const wy = to[2] - from[2];
|
|
14604
|
+
const wz = to[1] - from[1];
|
|
14605
|
+
const len = Math.sqrt(wx * wx + wy * wy + wz * wz);
|
|
14606
|
+
const tilt = Math.acos(Math.min(1, Math.max(-1, len > 0 ? wy / len : 1)));
|
|
14607
|
+
return [0, Math.atan2(wz, -wx), tilt];
|
|
14608
|
+
}
|
|
14609
|
+
function segmentLength(from, to) {
|
|
14610
|
+
const dx = to[0] - from[0];
|
|
14611
|
+
const dy = to[1] - from[1];
|
|
14612
|
+
const dz = to[2] - from[2];
|
|
14613
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
14614
|
+
}
|
|
14615
|
+
function cylinderBetween(from, to, radius, color) {
|
|
14616
|
+
const len = segmentLength(from, to);
|
|
14617
|
+
if (len < 1e-6) return null;
|
|
14618
|
+
return {
|
|
14619
|
+
type: "draw-mesh",
|
|
14620
|
+
shape: "cylinder",
|
|
14621
|
+
position: { x: (from[0] + to[0]) / 2, y: (from[1] + to[1]) / 2, z: (from[2] + to[2]) / 2 },
|
|
14622
|
+
radius,
|
|
14623
|
+
height: len,
|
|
14624
|
+
rotation: axisRotation(from, to),
|
|
14625
|
+
pivot: "center",
|
|
14626
|
+
segments: 12,
|
|
14627
|
+
material: { color }
|
|
14628
|
+
};
|
|
14629
|
+
}
|
|
14630
|
+
function arrowBetween(from, to, color, shaftRadius = 0.08) {
|
|
14631
|
+
const len = segmentLength(from, to);
|
|
14632
|
+
if (len < 1e-6) return null;
|
|
14633
|
+
const tipLen = Math.min(shaftRadius * 8, len * 0.35);
|
|
14634
|
+
const k = (len - tipLen) / len;
|
|
14635
|
+
const delta = [to[0] - from[0], to[1] - from[1], to[2] - from[2]];
|
|
14636
|
+
const shaftEnd = [delta[0] * k, delta[1] * k, delta[2] * k];
|
|
14637
|
+
const tipStart = shaftEnd;
|
|
14638
|
+
const tipEnd = delta;
|
|
14639
|
+
const shaft = cylinderBetween([0, 0, 0], shaftEnd, shaftRadius, color);
|
|
14640
|
+
const tipLenActual = segmentLength(tipStart, tipEnd);
|
|
14641
|
+
const tip = {
|
|
14642
|
+
type: "draw-mesh",
|
|
14643
|
+
shape: "cone",
|
|
14644
|
+
position: {
|
|
14645
|
+
x: (tipStart[0] + tipEnd[0]) / 2,
|
|
14646
|
+
y: (tipStart[1] + tipEnd[1]) / 2,
|
|
14647
|
+
z: (tipStart[2] + tipEnd[2]) / 2
|
|
14648
|
+
},
|
|
14649
|
+
radius: shaftRadius * 3,
|
|
14650
|
+
height: tipLenActual,
|
|
14651
|
+
rotation: axisRotation(tipStart, tipEnd),
|
|
14652
|
+
pivot: "center",
|
|
14653
|
+
segments: 12,
|
|
14654
|
+
material: { color }
|
|
14655
|
+
};
|
|
14656
|
+
return {
|
|
14657
|
+
type: "draw-group",
|
|
14658
|
+
position: { x: from[0], y: from[1], z: from[2] },
|
|
14659
|
+
items: tipLenActual < 1e-6 ? shaft ? [shaft] : [] : shaft ? [shaft, tip] : [tip]
|
|
14660
|
+
};
|
|
14661
|
+
}
|
|
14662
|
+
function billboardLabel(text, x, y, z, opts) {
|
|
14663
|
+
return {
|
|
14664
|
+
type: "draw-text",
|
|
14665
|
+
text,
|
|
14666
|
+
position: { x, y, z },
|
|
14667
|
+
color: opts?.color ?? "#111827"
|
|
14668
|
+
};
|
|
14669
|
+
}
|
|
14670
|
+
function labelColorForBackground(backgroundColor) {
|
|
14671
|
+
const m = /^#(?:([0-9a-f]{3})|([0-9a-f]{6}))$/i.exec(backgroundColor ?? "");
|
|
14672
|
+
if (!m) return "#111827";
|
|
14673
|
+
const hex = m[1] !== void 0 ? m[1].split("").map((c) => c + c).join("") : m[2];
|
|
14674
|
+
const r = parseInt(hex.slice(0, 2), 16) / 255;
|
|
14675
|
+
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
14676
|
+
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
14677
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
14678
|
+
return luminance < 0.5 ? "#e5e7eb" : "#111827";
|
|
14679
|
+
}
|
|
14680
|
+
function get3DClickPayload(onShapeClick, idToIndex) {
|
|
14681
|
+
if (!onShapeClick) return void 0;
|
|
14682
|
+
return (id) => onShapeClick({ id, index: idToIndex.get(id) ?? -1 });
|
|
14683
|
+
}
|
|
14684
|
+
var Canvas3DHost2;
|
|
14685
|
+
var init_learningScene3D = __esm({
|
|
14686
|
+
"components/learning/molecules/learningScene3D.tsx"() {
|
|
14687
|
+
"use client";
|
|
14688
|
+
init_atoms();
|
|
14689
|
+
init_Stack();
|
|
14690
|
+
init_useEventBus();
|
|
14691
|
+
Canvas3DHost2 = lazy(
|
|
14692
|
+
() => import('@almadar/ui/components/molecules/game/three').then((m) => ({ default: m.Canvas3DHost }))
|
|
14693
|
+
);
|
|
14694
|
+
LearningScene3D.displayName = "LearningScene3D";
|
|
14695
|
+
}
|
|
14696
|
+
});
|
|
14697
|
+
var biologyLog, BiologyCanvas;
|
|
14537
14698
|
var init_BiologyCanvas = __esm({
|
|
14538
14699
|
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
14539
14700
|
"use client";
|
|
14540
14701
|
init_atoms();
|
|
14541
14702
|
init_Stack();
|
|
14542
14703
|
init_LearningCanvas();
|
|
14704
|
+
init_learningScene3D();
|
|
14705
|
+
biologyLog = createLogger("almadar:ui:biology-canvas");
|
|
14543
14706
|
BiologyCanvas = ({
|
|
14544
14707
|
className,
|
|
14545
14708
|
width = 600,
|
|
14546
14709
|
height = 400,
|
|
14547
14710
|
title,
|
|
14548
14711
|
backgroundColor,
|
|
14712
|
+
mode = "2d",
|
|
14713
|
+
camera,
|
|
14714
|
+
lighting,
|
|
14715
|
+
post,
|
|
14549
14716
|
nodes = [],
|
|
14550
14717
|
edges = [],
|
|
14551
14718
|
shapes = [],
|
|
14552
|
-
|
|
14719
|
+
showGrid,
|
|
14720
|
+
shadows,
|
|
14721
|
+
interactive,
|
|
14553
14722
|
animate = false,
|
|
14554
14723
|
onShapeClick,
|
|
14555
14724
|
isLoading,
|
|
@@ -14610,6 +14779,75 @@ var init_BiologyCanvas = __esm({
|
|
|
14610
14779
|
out.push(...shapes);
|
|
14611
14780
|
return out;
|
|
14612
14781
|
}, [nodes, edges, shapes]);
|
|
14782
|
+
const drawables3D = useMemo(() => {
|
|
14783
|
+
if (mode !== "3d") return [];
|
|
14784
|
+
if (shapes.length > 0) {
|
|
14785
|
+
biologyLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
14786
|
+
}
|
|
14787
|
+
const out = [];
|
|
14788
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
14789
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
14790
|
+
for (const n of nodes) {
|
|
14791
|
+
if (n.id) nodeById.set(n.id, n);
|
|
14792
|
+
}
|
|
14793
|
+
for (const e of edges) {
|
|
14794
|
+
const a = nodeById.get(e.from);
|
|
14795
|
+
const b = nodeById.get(e.to);
|
|
14796
|
+
if (!a || !b) continue;
|
|
14797
|
+
const edgeRadius = Math.max(0.04, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.12);
|
|
14798
|
+
const edge = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], edgeRadius, e.color ?? "#9ca3af");
|
|
14799
|
+
if (edge) out.push(edge);
|
|
14800
|
+
if (e.label) {
|
|
14801
|
+
out.push(
|
|
14802
|
+
billboardLabel(
|
|
14803
|
+
e.label,
|
|
14804
|
+
(a.x + b.x) / 2,
|
|
14805
|
+
(a.y + b.y) / 2,
|
|
14806
|
+
((a.z ?? 0) + (b.z ?? 0)) / 2,
|
|
14807
|
+
{ color: labelColor }
|
|
14808
|
+
)
|
|
14809
|
+
);
|
|
14810
|
+
}
|
|
14811
|
+
}
|
|
14812
|
+
for (const n of nodes) {
|
|
14813
|
+
const radius = n.radius ?? 0.5;
|
|
14814
|
+
const nz = n.z ?? 0;
|
|
14815
|
+
out.push(meshSphere(n.id, n.x, n.y, nz, radius, n.color ?? "#16a34a", { shape: n.shape, ...n.opacity !== void 0 ? { opacity: n.opacity } : {} }));
|
|
14816
|
+
if (n.label) {
|
|
14817
|
+
out.push(billboardLabel(n.label, n.x, n.y, nz + radius, { color: labelColor }));
|
|
14818
|
+
}
|
|
14819
|
+
}
|
|
14820
|
+
return out;
|
|
14821
|
+
}, [mode, nodes, edges, shapes, backgroundColor]);
|
|
14822
|
+
const nodeIndexById = useMemo(() => {
|
|
14823
|
+
const m = /* @__PURE__ */ new Map();
|
|
14824
|
+
nodes.forEach((n, i) => {
|
|
14825
|
+
if (n.id) m.set(n.id, i);
|
|
14826
|
+
});
|
|
14827
|
+
return m;
|
|
14828
|
+
}, [nodes]);
|
|
14829
|
+
if (mode === "3d") {
|
|
14830
|
+
return /* @__PURE__ */ jsx(
|
|
14831
|
+
LearningScene3D,
|
|
14832
|
+
{
|
|
14833
|
+
className,
|
|
14834
|
+
width,
|
|
14835
|
+
height,
|
|
14836
|
+
title,
|
|
14837
|
+
backgroundColor,
|
|
14838
|
+
drawables: drawables3D,
|
|
14839
|
+
camera,
|
|
14840
|
+
lighting,
|
|
14841
|
+
post,
|
|
14842
|
+
showGrid,
|
|
14843
|
+
shadows,
|
|
14844
|
+
interactive,
|
|
14845
|
+
isLoading,
|
|
14846
|
+
error,
|
|
14847
|
+
onItemClick: get3DClickPayload(onShapeClick, nodeIndexById)
|
|
14848
|
+
}
|
|
14849
|
+
);
|
|
14850
|
+
}
|
|
14613
14851
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
14614
14852
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
14615
14853
|
/* @__PURE__ */ jsx(
|
|
@@ -14619,7 +14857,7 @@ var init_BiologyCanvas = __esm({
|
|
|
14619
14857
|
height,
|
|
14620
14858
|
backgroundColor,
|
|
14621
14859
|
shapes: derivedShapes,
|
|
14622
|
-
interactive,
|
|
14860
|
+
interactive: interactive ?? false,
|
|
14623
14861
|
animate,
|
|
14624
14862
|
onShapeClick,
|
|
14625
14863
|
isLoading,
|
|
@@ -21342,24 +21580,41 @@ var init_ChatBar = __esm({
|
|
|
21342
21580
|
ChatBar.displayName = "ChatBar";
|
|
21343
21581
|
}
|
|
21344
21582
|
});
|
|
21345
|
-
|
|
21583
|
+
function bondPerpendicular(a, b) {
|
|
21584
|
+
const dx = b[0] - a[0];
|
|
21585
|
+
const dy = b[1] - a[1];
|
|
21586
|
+
const px = dy;
|
|
21587
|
+
const py = -dx;
|
|
21588
|
+
const len = Math.sqrt(px * px + py * py);
|
|
21589
|
+
if (len < 1e-6) return [1, 0, 0];
|
|
21590
|
+
return [px / len, py / len, 0];
|
|
21591
|
+
}
|
|
21592
|
+
var chemistryLog, ChemistryCanvas;
|
|
21346
21593
|
var init_ChemistryCanvas = __esm({
|
|
21347
21594
|
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
21348
21595
|
"use client";
|
|
21349
21596
|
init_atoms();
|
|
21350
21597
|
init_Stack();
|
|
21351
21598
|
init_LearningCanvas();
|
|
21599
|
+
init_learningScene3D();
|
|
21600
|
+
chemistryLog = createLogger("almadar:ui:chemistry-canvas");
|
|
21352
21601
|
ChemistryCanvas = ({
|
|
21353
21602
|
className,
|
|
21354
21603
|
width = 600,
|
|
21355
21604
|
height = 400,
|
|
21356
21605
|
title,
|
|
21357
21606
|
backgroundColor,
|
|
21607
|
+
mode = "2d",
|
|
21608
|
+
camera,
|
|
21609
|
+
lighting,
|
|
21610
|
+
post,
|
|
21358
21611
|
atoms = [],
|
|
21359
21612
|
bonds = [],
|
|
21360
21613
|
arrows = [],
|
|
21361
21614
|
shapes = [],
|
|
21362
|
-
|
|
21615
|
+
showGrid,
|
|
21616
|
+
shadows,
|
|
21617
|
+
interactive,
|
|
21363
21618
|
animate = false,
|
|
21364
21619
|
onShapeClick,
|
|
21365
21620
|
isLoading,
|
|
@@ -21438,6 +21693,88 @@ var init_ChemistryCanvas = __esm({
|
|
|
21438
21693
|
out.push(...shapes);
|
|
21439
21694
|
return out;
|
|
21440
21695
|
}, [atoms, bonds, arrows, shapes]);
|
|
21696
|
+
const drawables3D = useMemo(() => {
|
|
21697
|
+
if (mode !== "3d") return [];
|
|
21698
|
+
if (shapes.length > 0) {
|
|
21699
|
+
chemistryLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
21700
|
+
}
|
|
21701
|
+
const out = [];
|
|
21702
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
21703
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
21704
|
+
for (const a of atoms) {
|
|
21705
|
+
if (a.id) atomById.set(a.id, a);
|
|
21706
|
+
}
|
|
21707
|
+
for (const b of bonds) {
|
|
21708
|
+
const a = atomById.get(b.from);
|
|
21709
|
+
const c = atomById.get(b.to);
|
|
21710
|
+
if (!a || !c) continue;
|
|
21711
|
+
const color = b.color ?? "#6b7280";
|
|
21712
|
+
const from = [a.x, a.y, a.z ?? 0];
|
|
21713
|
+
const to = [c.x, c.y, c.z ?? 0];
|
|
21714
|
+
const perp = bondPerpendicular(from, to);
|
|
21715
|
+
const bondRadius = Math.max(0.06, Math.min(a.radius ?? 0.45, c.radius ?? 0.45) * 0.22);
|
|
21716
|
+
const step = bondRadius * 2.2;
|
|
21717
|
+
const offsets = b.type === "double" ? [-step, step] : b.type === "triple" ? [-step, 0, step] : [0];
|
|
21718
|
+
for (const off of offsets) {
|
|
21719
|
+
const bond = cylinderBetween(
|
|
21720
|
+
[from[0] + perp[0] * off, from[1] + perp[1] * off, from[2] + perp[2] * off],
|
|
21721
|
+
[to[0] + perp[0] * off, to[1] + perp[1] * off, to[2] + perp[2] * off],
|
|
21722
|
+
bondRadius,
|
|
21723
|
+
color
|
|
21724
|
+
);
|
|
21725
|
+
if (bond) out.push(bond);
|
|
21726
|
+
}
|
|
21727
|
+
}
|
|
21728
|
+
for (const a of arrows) {
|
|
21729
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
21730
|
+
const len = a.length ?? 60;
|
|
21731
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
21732
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
21733
|
+
const arrow = arrowBetween([a.x, a.y, 0], [x2, y2, 0], a.color ?? "#dc2626");
|
|
21734
|
+
if (arrow) out.push(arrow);
|
|
21735
|
+
if (a.label) {
|
|
21736
|
+
out.push(billboardLabel(a.label, (a.x + x2) / 2, (a.y + y2) / 2, 0, { color: labelColor }));
|
|
21737
|
+
}
|
|
21738
|
+
}
|
|
21739
|
+
for (const a of atoms) {
|
|
21740
|
+
const radius = a.radius ?? 0.45;
|
|
21741
|
+
const az = a.z ?? 0;
|
|
21742
|
+
out.push(meshSphere(a.id, a.x, a.y, az, radius, a.color ?? "#2563eb"));
|
|
21743
|
+
if (a.element) {
|
|
21744
|
+
out.push(billboardLabel(a.element, a.x, a.y, az + radius, { color: labelColor }));
|
|
21745
|
+
}
|
|
21746
|
+
}
|
|
21747
|
+
return out;
|
|
21748
|
+
}, [mode, atoms, bonds, arrows, shapes, backgroundColor]);
|
|
21749
|
+
const atomIndexById = useMemo(() => {
|
|
21750
|
+
const m = /* @__PURE__ */ new Map();
|
|
21751
|
+
atoms.forEach((a, i) => {
|
|
21752
|
+
if (a.id) m.set(a.id, i);
|
|
21753
|
+
});
|
|
21754
|
+
return m;
|
|
21755
|
+
}, [atoms]);
|
|
21756
|
+
if (mode === "3d") {
|
|
21757
|
+
return /* @__PURE__ */ jsx(
|
|
21758
|
+
LearningScene3D,
|
|
21759
|
+
{
|
|
21760
|
+
className,
|
|
21761
|
+
width,
|
|
21762
|
+
height,
|
|
21763
|
+
title,
|
|
21764
|
+
backgroundColor,
|
|
21765
|
+
drawables: drawables3D,
|
|
21766
|
+
camera,
|
|
21767
|
+
lighting,
|
|
21768
|
+
post,
|
|
21769
|
+
showGrid,
|
|
21770
|
+
shadows,
|
|
21771
|
+
interactive,
|
|
21772
|
+
isLoading,
|
|
21773
|
+
error,
|
|
21774
|
+
onItemClick: get3DClickPayload(onShapeClick, atomIndexById)
|
|
21775
|
+
}
|
|
21776
|
+
);
|
|
21777
|
+
}
|
|
21441
21778
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
21442
21779
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
21443
21780
|
/* @__PURE__ */ jsx(
|
|
@@ -21447,7 +21784,7 @@ var init_ChemistryCanvas = __esm({
|
|
|
21447
21784
|
height,
|
|
21448
21785
|
backgroundColor,
|
|
21449
21786
|
shapes: derivedShapes,
|
|
21450
|
-
interactive,
|
|
21787
|
+
interactive: interactive ?? false,
|
|
21451
21788
|
animate,
|
|
21452
21789
|
onShapeClick,
|
|
21453
21790
|
isLoading,
|
|
@@ -24840,6 +25177,7 @@ var init_FilterGroup = __esm({
|
|
|
24840
25177
|
init_Badge();
|
|
24841
25178
|
init_Stack();
|
|
24842
25179
|
init_Icon();
|
|
25180
|
+
init_RangeSlider();
|
|
24843
25181
|
init_useEventBus();
|
|
24844
25182
|
init_useQuerySingleton();
|
|
24845
25183
|
resolveFilterType = (filter) => filter.filterType ?? filter.type;
|
|
@@ -25034,6 +25372,35 @@ var init_FilterGroup = __esm({
|
|
|
25034
25372
|
onClear: () => handleFilterSelect(`${filter.field}_to`, null)
|
|
25035
25373
|
}
|
|
25036
25374
|
)
|
|
25375
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
25376
|
+
/* @__PURE__ */ jsx(
|
|
25377
|
+
RangeSlider,
|
|
25378
|
+
{
|
|
25379
|
+
min: filter.min ?? 0,
|
|
25380
|
+
max: filter.max ?? 100,
|
|
25381
|
+
step: filter.step ?? 1,
|
|
25382
|
+
value: Number(
|
|
25383
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
25384
|
+
),
|
|
25385
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
25386
|
+
showTooltip: true,
|
|
25387
|
+
"aria-label": t("filterGroup.from")
|
|
25388
|
+
}
|
|
25389
|
+
),
|
|
25390
|
+
/* @__PURE__ */ jsx(
|
|
25391
|
+
RangeSlider,
|
|
25392
|
+
{
|
|
25393
|
+
min: filter.min ?? 0,
|
|
25394
|
+
max: filter.max ?? 100,
|
|
25395
|
+
step: filter.step ?? 1,
|
|
25396
|
+
value: Number(
|
|
25397
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
25398
|
+
),
|
|
25399
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
25400
|
+
showTooltip: true,
|
|
25401
|
+
"aria-label": t("filterGroup.to")
|
|
25402
|
+
}
|
|
25403
|
+
)
|
|
25037
25404
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsx(
|
|
25038
25405
|
Input,
|
|
25039
25406
|
{
|
|
@@ -25116,6 +25483,36 @@ var init_FilterGroup = __esm({
|
|
|
25116
25483
|
className: "text-sm min-w-[100px]"
|
|
25117
25484
|
}
|
|
25118
25485
|
)
|
|
25486
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
25487
|
+
/* @__PURE__ */ jsx(
|
|
25488
|
+
RangeSlider,
|
|
25489
|
+
{
|
|
25490
|
+
min: filter.min ?? 0,
|
|
25491
|
+
max: filter.max ?? 100,
|
|
25492
|
+
step: filter.step ?? 1,
|
|
25493
|
+
value: Number(
|
|
25494
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
25495
|
+
),
|
|
25496
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
25497
|
+
className: "min-w-[100px]",
|
|
25498
|
+
"aria-label": t("filterGroup.from")
|
|
25499
|
+
}
|
|
25500
|
+
),
|
|
25501
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "-" }),
|
|
25502
|
+
/* @__PURE__ */ jsx(
|
|
25503
|
+
RangeSlider,
|
|
25504
|
+
{
|
|
25505
|
+
min: filter.min ?? 0,
|
|
25506
|
+
max: filter.max ?? 100,
|
|
25507
|
+
step: filter.step ?? 1,
|
|
25508
|
+
value: Number(
|
|
25509
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
25510
|
+
),
|
|
25511
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
25512
|
+
className: "min-w-[100px]",
|
|
25513
|
+
"aria-label": t("filterGroup.to")
|
|
25514
|
+
}
|
|
25515
|
+
)
|
|
25119
25516
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsx(
|
|
25120
25517
|
Input,
|
|
25121
25518
|
{
|
|
@@ -25234,6 +25631,38 @@ var init_FilterGroup = __esm({
|
|
|
25234
25631
|
className: "min-w-[130px]"
|
|
25235
25632
|
}
|
|
25236
25633
|
)
|
|
25634
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
25635
|
+
/* @__PURE__ */ jsx(
|
|
25636
|
+
RangeSlider,
|
|
25637
|
+
{
|
|
25638
|
+
min: filter.min ?? 0,
|
|
25639
|
+
max: filter.max ?? 100,
|
|
25640
|
+
step: filter.step ?? 1,
|
|
25641
|
+
value: Number(
|
|
25642
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
25643
|
+
),
|
|
25644
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
25645
|
+
showTooltip: true,
|
|
25646
|
+
className: "min-w-[130px]",
|
|
25647
|
+
"aria-label": t("filterGroup.from")
|
|
25648
|
+
}
|
|
25649
|
+
),
|
|
25650
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "-" }),
|
|
25651
|
+
/* @__PURE__ */ jsx(
|
|
25652
|
+
RangeSlider,
|
|
25653
|
+
{
|
|
25654
|
+
min: filter.min ?? 0,
|
|
25655
|
+
max: filter.max ?? 100,
|
|
25656
|
+
step: filter.step ?? 1,
|
|
25657
|
+
value: Number(
|
|
25658
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
25659
|
+
),
|
|
25660
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
25661
|
+
showTooltip: true,
|
|
25662
|
+
className: "min-w-[130px]",
|
|
25663
|
+
"aria-label": t("filterGroup.to")
|
|
25664
|
+
}
|
|
25665
|
+
)
|
|
25237
25666
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsx(
|
|
25238
25667
|
Input,
|
|
25239
25668
|
{
|
|
@@ -27582,19 +28011,25 @@ var init_MathCanvas = __esm({
|
|
|
27582
28011
|
};
|
|
27583
28012
|
}
|
|
27584
28013
|
});
|
|
27585
|
-
var PhysicsCanvas;
|
|
28014
|
+
var physicsLog2, PhysicsCanvas;
|
|
27586
28015
|
var init_PhysicsCanvas = __esm({
|
|
27587
28016
|
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
27588
28017
|
"use client";
|
|
27589
28018
|
init_atoms();
|
|
27590
28019
|
init_Stack();
|
|
27591
28020
|
init_LearningCanvas();
|
|
28021
|
+
init_learningScene3D();
|
|
28022
|
+
physicsLog2 = createLogger("almadar:ui:physics-canvas");
|
|
27592
28023
|
PhysicsCanvas = ({
|
|
27593
28024
|
className,
|
|
27594
28025
|
width = 600,
|
|
27595
28026
|
height = 400,
|
|
27596
28027
|
title,
|
|
27597
28028
|
backgroundColor,
|
|
28029
|
+
mode = "2d",
|
|
28030
|
+
camera,
|
|
28031
|
+
lighting,
|
|
28032
|
+
post,
|
|
27598
28033
|
bodies = [],
|
|
27599
28034
|
constraints = [],
|
|
27600
28035
|
showVelocity = true,
|
|
@@ -27602,7 +28037,9 @@ var init_PhysicsCanvas = __esm({
|
|
|
27602
28037
|
velocityScale = 20,
|
|
27603
28038
|
forceScale = 20,
|
|
27604
28039
|
shapes = [],
|
|
27605
|
-
|
|
28040
|
+
showGrid,
|
|
28041
|
+
shadows,
|
|
28042
|
+
interactive,
|
|
27606
28043
|
animate = false,
|
|
27607
28044
|
onShapeClick,
|
|
27608
28045
|
isLoading,
|
|
@@ -27674,6 +28111,89 @@ var init_PhysicsCanvas = __esm({
|
|
|
27674
28111
|
out.push(...shapes);
|
|
27675
28112
|
return out;
|
|
27676
28113
|
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
28114
|
+
const drawables3D = useMemo(() => {
|
|
28115
|
+
if (mode !== "3d") return [];
|
|
28116
|
+
if (shapes.length > 0) {
|
|
28117
|
+
physicsLog2.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
28118
|
+
}
|
|
28119
|
+
const out = [];
|
|
28120
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
28121
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
28122
|
+
for (const b of bodies) {
|
|
28123
|
+
if (b.id) bodyById.set(b.id, b);
|
|
28124
|
+
}
|
|
28125
|
+
for (const c of constraints) {
|
|
28126
|
+
const a = bodyById.get(c.from);
|
|
28127
|
+
const b = bodyById.get(c.to);
|
|
28128
|
+
if (!a || !b) continue;
|
|
28129
|
+
const rodRadius = Math.max(0.05, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.15);
|
|
28130
|
+
const rod = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], rodRadius, c.color ?? "#9ca3af");
|
|
28131
|
+
if (rod) out.push(rod);
|
|
28132
|
+
}
|
|
28133
|
+
for (const b of bodies) {
|
|
28134
|
+
const radius = b.radius ?? 0.5;
|
|
28135
|
+
const bz = b.z ?? 0;
|
|
28136
|
+
out.push(meshSphere(b.id, b.x, b.y, bz, radius, b.color ?? "#2563eb"));
|
|
28137
|
+
if (b.label) {
|
|
28138
|
+
out.push(billboardLabel(b.label, b.x, b.y, bz + radius, { color: labelColor }));
|
|
28139
|
+
}
|
|
28140
|
+
const vx = b.vx ?? 0;
|
|
28141
|
+
const vy = b.vy ?? 0;
|
|
28142
|
+
const vz = b.vz ?? 0;
|
|
28143
|
+
const arrowRadius = Math.max(0.05, radius * 0.15);
|
|
28144
|
+
if (showVelocity && (vx !== 0 || vy !== 0 || vz !== 0)) {
|
|
28145
|
+
const arrow = arrowBetween(
|
|
28146
|
+
[b.x, b.y, bz],
|
|
28147
|
+
[b.x + vx * velocityScale, b.y + vy * velocityScale, bz + vz * velocityScale],
|
|
28148
|
+
"#16a34a",
|
|
28149
|
+
arrowRadius
|
|
28150
|
+
);
|
|
28151
|
+
if (arrow) out.push(arrow);
|
|
28152
|
+
}
|
|
28153
|
+
const fx = b.fx ?? 0;
|
|
28154
|
+
const fy = b.fy ?? 0;
|
|
28155
|
+
const fz = b.fz ?? 0;
|
|
28156
|
+
if (showForces && (fx !== 0 || fy !== 0 || fz !== 0)) {
|
|
28157
|
+
const arrow = arrowBetween(
|
|
28158
|
+
[b.x, b.y, bz],
|
|
28159
|
+
[b.x + fx * forceScale, b.y + fy * forceScale, bz + fz * forceScale],
|
|
28160
|
+
"#dc2626",
|
|
28161
|
+
arrowRadius
|
|
28162
|
+
);
|
|
28163
|
+
if (arrow) out.push(arrow);
|
|
28164
|
+
}
|
|
28165
|
+
}
|
|
28166
|
+
return out;
|
|
28167
|
+
}, [mode, bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes, backgroundColor]);
|
|
28168
|
+
const bodyIndexById = useMemo(() => {
|
|
28169
|
+
const m = /* @__PURE__ */ new Map();
|
|
28170
|
+
bodies.forEach((b, i) => {
|
|
28171
|
+
if (b.id) m.set(b.id, i);
|
|
28172
|
+
});
|
|
28173
|
+
return m;
|
|
28174
|
+
}, [bodies]);
|
|
28175
|
+
if (mode === "3d") {
|
|
28176
|
+
return /* @__PURE__ */ jsx(
|
|
28177
|
+
LearningScene3D,
|
|
28178
|
+
{
|
|
28179
|
+
className,
|
|
28180
|
+
width,
|
|
28181
|
+
height,
|
|
28182
|
+
title,
|
|
28183
|
+
backgroundColor,
|
|
28184
|
+
drawables: drawables3D,
|
|
28185
|
+
camera,
|
|
28186
|
+
lighting,
|
|
28187
|
+
post,
|
|
28188
|
+
showGrid,
|
|
28189
|
+
shadows,
|
|
28190
|
+
interactive,
|
|
28191
|
+
isLoading,
|
|
28192
|
+
error,
|
|
28193
|
+
onItemClick: get3DClickPayload(onShapeClick, bodyIndexById)
|
|
28194
|
+
}
|
|
28195
|
+
);
|
|
28196
|
+
}
|
|
27677
28197
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
27678
28198
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
27679
28199
|
/* @__PURE__ */ jsx(
|
|
@@ -27683,7 +28203,7 @@ var init_PhysicsCanvas = __esm({
|
|
|
27683
28203
|
height,
|
|
27684
28204
|
backgroundColor,
|
|
27685
28205
|
shapes: derivedShapes,
|
|
27686
|
-
interactive,
|
|
28206
|
+
interactive: interactive ?? false,
|
|
27687
28207
|
animate,
|
|
27688
28208
|
onShapeClick,
|
|
27689
28209
|
isLoading,
|
|
@@ -28301,12 +28821,12 @@ var init_MapView = __esm({
|
|
|
28301
28821
|
shadowSize: [41, 41]
|
|
28302
28822
|
});
|
|
28303
28823
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
28304
|
-
const { useEffect: useEffect60, useRef:
|
|
28824
|
+
const { useEffect: useEffect60, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__default;
|
|
28305
28825
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
28306
28826
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
28307
28827
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
28308
28828
|
const map = useMap();
|
|
28309
|
-
const prevRef =
|
|
28829
|
+
const prevRef = useRef63({ centerLat, centerLng, zoom });
|
|
28310
28830
|
useEffect60(() => {
|
|
28311
28831
|
const prev = prevRef.current;
|
|
28312
28832
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
@@ -38971,7 +39491,7 @@ function getEnumOptions(field) {
|
|
|
38971
39491
|
}));
|
|
38972
39492
|
}
|
|
38973
39493
|
const validation = field.validation;
|
|
38974
|
-
if (validation?.enum &&
|
|
39494
|
+
if (validation?.enum && validation.enum.length > 0) {
|
|
38975
39495
|
return validation.enum.map((v) => ({
|
|
38976
39496
|
value: v,
|
|
38977
39497
|
label: v.charAt(0).toUpperCase() + v.slice(1).replace(/_/g, " ")
|