@almadar/ui 5.148.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 +438 -13
- package/dist/avl/index.js +438 -13
- 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 +448 -16
- package/dist/components/index.d.cts +269 -95
- package/dist/components/index.d.ts +269 -95
- package/dist/components/index.js +444 -18
- 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 +438 -13
- package/dist/providers/index.d.cts +2 -1
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/index.js +438 -13
- package/dist/runtime/index.cjs +438 -13
- package/dist/runtime/index.js +438 -13
- package/dist/types-B3nHgnMG.d.cts +51 -0
- package/dist/types-B3nHgnMG.d.ts +51 -0
- package/package.json +3 -3
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,
|
|
@@ -14536,23 +14537,188 @@ var init_BehaviorView = __esm({
|
|
|
14536
14537
|
BehaviorView.displayName = "BehaviorView";
|
|
14537
14538
|
}
|
|
14538
14539
|
});
|
|
14539
|
-
|
|
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;
|
|
14540
14698
|
var init_BiologyCanvas = __esm({
|
|
14541
14699
|
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
14542
14700
|
"use client";
|
|
14543
14701
|
init_atoms();
|
|
14544
14702
|
init_Stack();
|
|
14545
14703
|
init_LearningCanvas();
|
|
14704
|
+
init_learningScene3D();
|
|
14705
|
+
biologyLog = createLogger("almadar:ui:biology-canvas");
|
|
14546
14706
|
BiologyCanvas = ({
|
|
14547
14707
|
className,
|
|
14548
14708
|
width = 600,
|
|
14549
14709
|
height = 400,
|
|
14550
14710
|
title,
|
|
14551
14711
|
backgroundColor,
|
|
14712
|
+
mode = "2d",
|
|
14713
|
+
camera,
|
|
14714
|
+
lighting,
|
|
14715
|
+
post,
|
|
14552
14716
|
nodes = [],
|
|
14553
14717
|
edges = [],
|
|
14554
14718
|
shapes = [],
|
|
14555
|
-
|
|
14719
|
+
showGrid,
|
|
14720
|
+
shadows,
|
|
14721
|
+
interactive,
|
|
14556
14722
|
animate = false,
|
|
14557
14723
|
onShapeClick,
|
|
14558
14724
|
isLoading,
|
|
@@ -14613,6 +14779,75 @@ var init_BiologyCanvas = __esm({
|
|
|
14613
14779
|
out.push(...shapes);
|
|
14614
14780
|
return out;
|
|
14615
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
|
+
}
|
|
14616
14851
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
14617
14852
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
14618
14853
|
/* @__PURE__ */ jsx(
|
|
@@ -14622,7 +14857,7 @@ var init_BiologyCanvas = __esm({
|
|
|
14622
14857
|
height,
|
|
14623
14858
|
backgroundColor,
|
|
14624
14859
|
shapes: derivedShapes,
|
|
14625
|
-
interactive,
|
|
14860
|
+
interactive: interactive ?? false,
|
|
14626
14861
|
animate,
|
|
14627
14862
|
onShapeClick,
|
|
14628
14863
|
isLoading,
|
|
@@ -21345,24 +21580,41 @@ var init_ChatBar = __esm({
|
|
|
21345
21580
|
ChatBar.displayName = "ChatBar";
|
|
21346
21581
|
}
|
|
21347
21582
|
});
|
|
21348
|
-
|
|
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;
|
|
21349
21593
|
var init_ChemistryCanvas = __esm({
|
|
21350
21594
|
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
21351
21595
|
"use client";
|
|
21352
21596
|
init_atoms();
|
|
21353
21597
|
init_Stack();
|
|
21354
21598
|
init_LearningCanvas();
|
|
21599
|
+
init_learningScene3D();
|
|
21600
|
+
chemistryLog = createLogger("almadar:ui:chemistry-canvas");
|
|
21355
21601
|
ChemistryCanvas = ({
|
|
21356
21602
|
className,
|
|
21357
21603
|
width = 600,
|
|
21358
21604
|
height = 400,
|
|
21359
21605
|
title,
|
|
21360
21606
|
backgroundColor,
|
|
21607
|
+
mode = "2d",
|
|
21608
|
+
camera,
|
|
21609
|
+
lighting,
|
|
21610
|
+
post,
|
|
21361
21611
|
atoms = [],
|
|
21362
21612
|
bonds = [],
|
|
21363
21613
|
arrows = [],
|
|
21364
21614
|
shapes = [],
|
|
21365
|
-
|
|
21615
|
+
showGrid,
|
|
21616
|
+
shadows,
|
|
21617
|
+
interactive,
|
|
21366
21618
|
animate = false,
|
|
21367
21619
|
onShapeClick,
|
|
21368
21620
|
isLoading,
|
|
@@ -21441,6 +21693,88 @@ var init_ChemistryCanvas = __esm({
|
|
|
21441
21693
|
out.push(...shapes);
|
|
21442
21694
|
return out;
|
|
21443
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
|
+
}
|
|
21444
21778
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
21445
21779
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
21446
21780
|
/* @__PURE__ */ jsx(
|
|
@@ -21450,7 +21784,7 @@ var init_ChemistryCanvas = __esm({
|
|
|
21450
21784
|
height,
|
|
21451
21785
|
backgroundColor,
|
|
21452
21786
|
shapes: derivedShapes,
|
|
21453
|
-
interactive,
|
|
21787
|
+
interactive: interactive ?? false,
|
|
21454
21788
|
animate,
|
|
21455
21789
|
onShapeClick,
|
|
21456
21790
|
isLoading,
|
|
@@ -27677,19 +28011,25 @@ var init_MathCanvas = __esm({
|
|
|
27677
28011
|
};
|
|
27678
28012
|
}
|
|
27679
28013
|
});
|
|
27680
|
-
var PhysicsCanvas;
|
|
28014
|
+
var physicsLog2, PhysicsCanvas;
|
|
27681
28015
|
var init_PhysicsCanvas = __esm({
|
|
27682
28016
|
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
27683
28017
|
"use client";
|
|
27684
28018
|
init_atoms();
|
|
27685
28019
|
init_Stack();
|
|
27686
28020
|
init_LearningCanvas();
|
|
28021
|
+
init_learningScene3D();
|
|
28022
|
+
physicsLog2 = createLogger("almadar:ui:physics-canvas");
|
|
27687
28023
|
PhysicsCanvas = ({
|
|
27688
28024
|
className,
|
|
27689
28025
|
width = 600,
|
|
27690
28026
|
height = 400,
|
|
27691
28027
|
title,
|
|
27692
28028
|
backgroundColor,
|
|
28029
|
+
mode = "2d",
|
|
28030
|
+
camera,
|
|
28031
|
+
lighting,
|
|
28032
|
+
post,
|
|
27693
28033
|
bodies = [],
|
|
27694
28034
|
constraints = [],
|
|
27695
28035
|
showVelocity = true,
|
|
@@ -27697,7 +28037,9 @@ var init_PhysicsCanvas = __esm({
|
|
|
27697
28037
|
velocityScale = 20,
|
|
27698
28038
|
forceScale = 20,
|
|
27699
28039
|
shapes = [],
|
|
27700
|
-
|
|
28040
|
+
showGrid,
|
|
28041
|
+
shadows,
|
|
28042
|
+
interactive,
|
|
27701
28043
|
animate = false,
|
|
27702
28044
|
onShapeClick,
|
|
27703
28045
|
isLoading,
|
|
@@ -27769,6 +28111,89 @@ var init_PhysicsCanvas = __esm({
|
|
|
27769
28111
|
out.push(...shapes);
|
|
27770
28112
|
return out;
|
|
27771
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
|
+
}
|
|
27772
28197
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
27773
28198
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
27774
28199
|
/* @__PURE__ */ jsx(
|
|
@@ -27778,7 +28203,7 @@ var init_PhysicsCanvas = __esm({
|
|
|
27778
28203
|
height,
|
|
27779
28204
|
backgroundColor,
|
|
27780
28205
|
shapes: derivedShapes,
|
|
27781
|
-
interactive,
|
|
28206
|
+
interactive: interactive ?? false,
|
|
27782
28207
|
animate,
|
|
27783
28208
|
onShapeClick,
|
|
27784
28209
|
isLoading,
|
|
@@ -28396,12 +28821,12 @@ var init_MapView = __esm({
|
|
|
28396
28821
|
shadowSize: [41, 41]
|
|
28397
28822
|
});
|
|
28398
28823
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
28399
|
-
const { useEffect: useEffect60, useRef:
|
|
28824
|
+
const { useEffect: useEffect60, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__default;
|
|
28400
28825
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
28401
28826
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
28402
28827
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
28403
28828
|
const map = useMap();
|
|
28404
|
-
const prevRef =
|
|
28829
|
+
const prevRef = useRef63({ centerLat, centerLng, zoom });
|
|
28405
28830
|
useEffect60(() => {
|
|
28406
28831
|
const prev = prevRef.current;
|
|
28407
28832
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
@@ -39066,7 +39491,7 @@ function getEnumOptions(field) {
|
|
|
39066
39491
|
}));
|
|
39067
39492
|
}
|
|
39068
39493
|
const validation = field.validation;
|
|
39069
|
-
if (validation?.enum &&
|
|
39494
|
+
if (validation?.enum && validation.enum.length > 0) {
|
|
39070
39495
|
return validation.enum.map((v) => ({
|
|
39071
39496
|
value: v,
|
|
39072
39497
|
label: v.charAt(0).toUpperCase() + v.slice(1).replace(/_/g, " ")
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { AssetUrl } from '@almadar/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cross-cutting atom-level prop shapes shared across the design system.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Canonical semantic color palette. Values are the Tailwind / CSS-var token
|
|
9
|
+
* names that every component in the design system understands. Prefer this
|
|
10
|
+
* over a bare `string` for any `color` or `variant` prop.
|
|
11
|
+
*/
|
|
12
|
+
type ColorToken = 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'muted';
|
|
13
|
+
/**
|
|
14
|
+
* Concrete error-state shape read by display components (`error.message`,
|
|
15
|
+
* occasionally `error.stack`). Structurally assignable from the global `Error`,
|
|
16
|
+
* so existing call sites passing an `Error` keep working — this just gives the
|
|
17
|
+
* pattern extractor a readable field schema instead of the opaque `Error` global.
|
|
18
|
+
*/
|
|
19
|
+
type UiError = {
|
|
20
|
+
message: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
code?: string;
|
|
23
|
+
stack?: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A labelled link/CTA used by marketing molecules (HeroSection, CTABanner,
|
|
27
|
+
* PricingCard) for their primary/secondary call-to-action props.
|
|
28
|
+
*/
|
|
29
|
+
type LinkAction = {
|
|
30
|
+
label: string;
|
|
31
|
+
href: string;
|
|
32
|
+
};
|
|
33
|
+
/** An image with its required alt text. */
|
|
34
|
+
type ImageSource = {
|
|
35
|
+
src: AssetUrl;
|
|
36
|
+
alt: string;
|
|
37
|
+
};
|
|
38
|
+
/** A 2D point in canvas/layout coordinates. */
|
|
39
|
+
type Point = {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
};
|
|
43
|
+
/** A 2D rectangle in canvas coordinates (`w`/`h` = width/height). */
|
|
44
|
+
type Rect = {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
w: number;
|
|
48
|
+
h: number;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type { ColorToken as C, ImageSource as I, LinkAction as L, Point as P, Rect as R, UiError as U };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { AssetUrl } from '@almadar/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cross-cutting atom-level prop shapes shared across the design system.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Canonical semantic color palette. Values are the Tailwind / CSS-var token
|
|
9
|
+
* names that every component in the design system understands. Prefer this
|
|
10
|
+
* over a bare `string` for any `color` or `variant` prop.
|
|
11
|
+
*/
|
|
12
|
+
type ColorToken = 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'muted';
|
|
13
|
+
/**
|
|
14
|
+
* Concrete error-state shape read by display components (`error.message`,
|
|
15
|
+
* occasionally `error.stack`). Structurally assignable from the global `Error`,
|
|
16
|
+
* so existing call sites passing an `Error` keep working — this just gives the
|
|
17
|
+
* pattern extractor a readable field schema instead of the opaque `Error` global.
|
|
18
|
+
*/
|
|
19
|
+
type UiError = {
|
|
20
|
+
message: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
code?: string;
|
|
23
|
+
stack?: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A labelled link/CTA used by marketing molecules (HeroSection, CTABanner,
|
|
27
|
+
* PricingCard) for their primary/secondary call-to-action props.
|
|
28
|
+
*/
|
|
29
|
+
type LinkAction = {
|
|
30
|
+
label: string;
|
|
31
|
+
href: string;
|
|
32
|
+
};
|
|
33
|
+
/** An image with its required alt text. */
|
|
34
|
+
type ImageSource = {
|
|
35
|
+
src: AssetUrl;
|
|
36
|
+
alt: string;
|
|
37
|
+
};
|
|
38
|
+
/** A 2D point in canvas/layout coordinates. */
|
|
39
|
+
type Point = {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
};
|
|
43
|
+
/** A 2D rectangle in canvas coordinates (`w`/`h` = width/height). */
|
|
44
|
+
type Rect = {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
w: number;
|
|
48
|
+
h: number;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type { ColorToken as C, ImageSource as I, LinkAction as L, Point as P, Rect as R, UiError as U };
|