@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.cjs
CHANGED
|
@@ -10729,9 +10729,10 @@ function Canvas({
|
|
|
10729
10729
|
drawables: [...drawables ?? [], ...childDrawables],
|
|
10730
10730
|
isLoading,
|
|
10731
10731
|
cameraMode: to3DCameraMode(camera?.mode),
|
|
10732
|
-
...zoom !== void 0 ? {
|
|
10732
|
+
...zoom !== void 0 ? { zoom } : {},
|
|
10733
10733
|
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
10734
10734
|
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
10735
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
10735
10736
|
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
10736
10737
|
unitScale,
|
|
10737
10738
|
backgroundColor,
|
|
@@ -11940,7 +11941,10 @@ var init_LearningCanvas = __esm({
|
|
|
11940
11941
|
ctx.fillRect(0, 0, width, height);
|
|
11941
11942
|
}
|
|
11942
11943
|
for (const shape of shapes) {
|
|
11943
|
-
drawShape(ctx, shape, width, height);
|
|
11944
|
+
if (shape.type !== "text") drawShape(ctx, shape, width, height);
|
|
11945
|
+
}
|
|
11946
|
+
for (const shape of shapes) {
|
|
11947
|
+
if (shape.type === "text") drawShape(ctx, shape, width, height);
|
|
11944
11948
|
}
|
|
11945
11949
|
}, [width, height, backgroundColor, shapes]);
|
|
11946
11950
|
React85.useEffect(() => {
|
|
@@ -13503,7 +13507,7 @@ var init_AlgorithmCanvas = __esm({
|
|
|
13503
13507
|
DEFAULT_CELL_COLOR = "#e5e7eb";
|
|
13504
13508
|
DEFAULT_POINTER_COLOR = "#dc2626";
|
|
13505
13509
|
POINTER_BAND = 34;
|
|
13506
|
-
TOP_PAD =
|
|
13510
|
+
TOP_PAD = 26;
|
|
13507
13511
|
AlgorithmCanvas = ({
|
|
13508
13512
|
className,
|
|
13509
13513
|
width = 600,
|
|
@@ -14607,23 +14611,188 @@ var init_BehaviorView = __esm({
|
|
|
14607
14611
|
BehaviorView.displayName = "BehaviorView";
|
|
14608
14612
|
}
|
|
14609
14613
|
});
|
|
14610
|
-
|
|
14614
|
+
function LearningScene3D({
|
|
14615
|
+
className,
|
|
14616
|
+
width = 600,
|
|
14617
|
+
height = 400,
|
|
14618
|
+
title,
|
|
14619
|
+
backgroundColor,
|
|
14620
|
+
drawables,
|
|
14621
|
+
camera,
|
|
14622
|
+
lighting,
|
|
14623
|
+
post,
|
|
14624
|
+
showGrid = false,
|
|
14625
|
+
shadows,
|
|
14626
|
+
interactive,
|
|
14627
|
+
isLoading,
|
|
14628
|
+
error,
|
|
14629
|
+
onItemClick
|
|
14630
|
+
}) {
|
|
14631
|
+
const instanceId = React85.useId().replace(/[^a-zA-Z0-9]/g, "");
|
|
14632
|
+
const clickEvent = onItemClick ? `LEARNING_SCENE_3D.ITEM_CLICK.${instanceId}` : void 0;
|
|
14633
|
+
const onItemClickRef = React85.useRef(onItemClick);
|
|
14634
|
+
onItemClickRef.current = onItemClick;
|
|
14635
|
+
useEventListener(`UI:${clickEvent ?? "LEARNING_SCENE_3D.ITEM_CLICK.__disabled"}`, (event) => {
|
|
14636
|
+
const unitId = event.payload?.unitId;
|
|
14637
|
+
if (typeof unitId === "string") onItemClickRef.current?.(unitId);
|
|
14638
|
+
});
|
|
14639
|
+
const props3d = {
|
|
14640
|
+
drawables,
|
|
14641
|
+
isLoading,
|
|
14642
|
+
error: error ? error.message : null,
|
|
14643
|
+
cameraMode: camera?.mode ?? "perspective",
|
|
14644
|
+
...camera?.zoom !== void 0 ? { zoom: camera.zoom } : {},
|
|
14645
|
+
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
14646
|
+
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
14647
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
14648
|
+
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
14649
|
+
backgroundColor,
|
|
14650
|
+
showGrid,
|
|
14651
|
+
...shadows !== void 0 ? { shadows } : {},
|
|
14652
|
+
...interactive !== void 0 ? { controlsEnabled: interactive } : {},
|
|
14653
|
+
lighting,
|
|
14654
|
+
post,
|
|
14655
|
+
...clickEvent !== void 0 ? { unitClickEvent: clickEvent } : {}
|
|
14656
|
+
};
|
|
14657
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
14658
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
14659
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { width, height, display: "flex" }, children: /* @__PURE__ */ jsxRuntime.jsx(React85.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx(Canvas3DHost2, { ...props3d }) }) })
|
|
14660
|
+
] }) });
|
|
14661
|
+
}
|
|
14662
|
+
function meshSphere(id, x, y, z, radius, color, opts) {
|
|
14663
|
+
return {
|
|
14664
|
+
type: "draw-mesh",
|
|
14665
|
+
...id !== void 0 ? { id } : {},
|
|
14666
|
+
shape: opts?.shape ?? "sphere",
|
|
14667
|
+
position: { x, y, z },
|
|
14668
|
+
radius,
|
|
14669
|
+
pivot: "center",
|
|
14670
|
+
segments: opts?.segments ?? 24,
|
|
14671
|
+
material: { color, ...opts?.material },
|
|
14672
|
+
...opts?.opacity !== void 0 ? { opacity: opts.opacity } : {}
|
|
14673
|
+
};
|
|
14674
|
+
}
|
|
14675
|
+
function axisRotation(from, to) {
|
|
14676
|
+
const wx = to[0] - from[0];
|
|
14677
|
+
const wy = to[2] - from[2];
|
|
14678
|
+
const wz = to[1] - from[1];
|
|
14679
|
+
const len = Math.sqrt(wx * wx + wy * wy + wz * wz);
|
|
14680
|
+
const tilt = Math.acos(Math.min(1, Math.max(-1, len > 0 ? wy / len : 1)));
|
|
14681
|
+
return [0, Math.atan2(wz, -wx), tilt];
|
|
14682
|
+
}
|
|
14683
|
+
function segmentLength(from, to) {
|
|
14684
|
+
const dx = to[0] - from[0];
|
|
14685
|
+
const dy = to[1] - from[1];
|
|
14686
|
+
const dz = to[2] - from[2];
|
|
14687
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
14688
|
+
}
|
|
14689
|
+
function cylinderBetween(from, to, radius, color) {
|
|
14690
|
+
const len = segmentLength(from, to);
|
|
14691
|
+
if (len < 1e-6) return null;
|
|
14692
|
+
return {
|
|
14693
|
+
type: "draw-mesh",
|
|
14694
|
+
shape: "cylinder",
|
|
14695
|
+
position: { x: (from[0] + to[0]) / 2, y: (from[1] + to[1]) / 2, z: (from[2] + to[2]) / 2 },
|
|
14696
|
+
radius,
|
|
14697
|
+
height: len,
|
|
14698
|
+
rotation: axisRotation(from, to),
|
|
14699
|
+
pivot: "center",
|
|
14700
|
+
segments: 12,
|
|
14701
|
+
material: { color }
|
|
14702
|
+
};
|
|
14703
|
+
}
|
|
14704
|
+
function arrowBetween(from, to, color, shaftRadius = 0.08) {
|
|
14705
|
+
const len = segmentLength(from, to);
|
|
14706
|
+
if (len < 1e-6) return null;
|
|
14707
|
+
const tipLen = Math.min(shaftRadius * 8, len * 0.35);
|
|
14708
|
+
const k = (len - tipLen) / len;
|
|
14709
|
+
const delta = [to[0] - from[0], to[1] - from[1], to[2] - from[2]];
|
|
14710
|
+
const shaftEnd = [delta[0] * k, delta[1] * k, delta[2] * k];
|
|
14711
|
+
const tipStart = shaftEnd;
|
|
14712
|
+
const tipEnd = delta;
|
|
14713
|
+
const shaft = cylinderBetween([0, 0, 0], shaftEnd, shaftRadius, color);
|
|
14714
|
+
const tipLenActual = segmentLength(tipStart, tipEnd);
|
|
14715
|
+
const tip = {
|
|
14716
|
+
type: "draw-mesh",
|
|
14717
|
+
shape: "cone",
|
|
14718
|
+
position: {
|
|
14719
|
+
x: (tipStart[0] + tipEnd[0]) / 2,
|
|
14720
|
+
y: (tipStart[1] + tipEnd[1]) / 2,
|
|
14721
|
+
z: (tipStart[2] + tipEnd[2]) / 2
|
|
14722
|
+
},
|
|
14723
|
+
radius: shaftRadius * 3,
|
|
14724
|
+
height: tipLenActual,
|
|
14725
|
+
rotation: axisRotation(tipStart, tipEnd),
|
|
14726
|
+
pivot: "center",
|
|
14727
|
+
segments: 12,
|
|
14728
|
+
material: { color }
|
|
14729
|
+
};
|
|
14730
|
+
return {
|
|
14731
|
+
type: "draw-group",
|
|
14732
|
+
position: { x: from[0], y: from[1], z: from[2] },
|
|
14733
|
+
items: tipLenActual < 1e-6 ? shaft ? [shaft] : [] : shaft ? [shaft, tip] : [tip]
|
|
14734
|
+
};
|
|
14735
|
+
}
|
|
14736
|
+
function billboardLabel(text, x, y, z, opts) {
|
|
14737
|
+
return {
|
|
14738
|
+
type: "draw-text",
|
|
14739
|
+
text,
|
|
14740
|
+
position: { x, y, z },
|
|
14741
|
+
color: opts?.color ?? "#111827"
|
|
14742
|
+
};
|
|
14743
|
+
}
|
|
14744
|
+
function labelColorForBackground(backgroundColor) {
|
|
14745
|
+
const m = /^#(?:([0-9a-f]{3})|([0-9a-f]{6}))$/i.exec(backgroundColor ?? "");
|
|
14746
|
+
if (!m) return "#111827";
|
|
14747
|
+
const hex = m[1] !== void 0 ? m[1].split("").map((c) => c + c).join("") : m[2];
|
|
14748
|
+
const r = parseInt(hex.slice(0, 2), 16) / 255;
|
|
14749
|
+
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
14750
|
+
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
14751
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
14752
|
+
return luminance < 0.5 ? "#e5e7eb" : "#111827";
|
|
14753
|
+
}
|
|
14754
|
+
function get3DClickPayload(onShapeClick, idToIndex) {
|
|
14755
|
+
if (!onShapeClick) return void 0;
|
|
14756
|
+
return (id) => onShapeClick({ id, index: idToIndex.get(id) ?? -1 });
|
|
14757
|
+
}
|
|
14758
|
+
var Canvas3DHost2;
|
|
14759
|
+
var init_learningScene3D = __esm({
|
|
14760
|
+
"components/learning/molecules/learningScene3D.tsx"() {
|
|
14761
|
+
"use client";
|
|
14762
|
+
init_atoms();
|
|
14763
|
+
init_Stack();
|
|
14764
|
+
init_useEventBus();
|
|
14765
|
+
Canvas3DHost2 = React85.lazy(
|
|
14766
|
+
() => import('@almadar/ui/components/molecules/game/three').then((m) => ({ default: m.Canvas3DHost }))
|
|
14767
|
+
);
|
|
14768
|
+
LearningScene3D.displayName = "LearningScene3D";
|
|
14769
|
+
}
|
|
14770
|
+
});
|
|
14771
|
+
var biologyLog, BiologyCanvas;
|
|
14611
14772
|
var init_BiologyCanvas = __esm({
|
|
14612
14773
|
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
14613
14774
|
"use client";
|
|
14614
14775
|
init_atoms();
|
|
14615
14776
|
init_Stack();
|
|
14616
14777
|
init_LearningCanvas();
|
|
14778
|
+
init_learningScene3D();
|
|
14779
|
+
biologyLog = logger.createLogger("almadar:ui:biology-canvas");
|
|
14617
14780
|
BiologyCanvas = ({
|
|
14618
14781
|
className,
|
|
14619
14782
|
width = 600,
|
|
14620
14783
|
height = 400,
|
|
14621
14784
|
title,
|
|
14622
14785
|
backgroundColor,
|
|
14786
|
+
mode = "2d",
|
|
14787
|
+
camera,
|
|
14788
|
+
lighting,
|
|
14789
|
+
post,
|
|
14623
14790
|
nodes = [],
|
|
14624
14791
|
edges = [],
|
|
14625
14792
|
shapes = [],
|
|
14626
|
-
|
|
14793
|
+
showGrid,
|
|
14794
|
+
shadows,
|
|
14795
|
+
interactive,
|
|
14627
14796
|
animate = false,
|
|
14628
14797
|
onShapeClick,
|
|
14629
14798
|
isLoading,
|
|
@@ -14684,6 +14853,75 @@ var init_BiologyCanvas = __esm({
|
|
|
14684
14853
|
out.push(...shapes);
|
|
14685
14854
|
return out;
|
|
14686
14855
|
}, [nodes, edges, shapes]);
|
|
14856
|
+
const drawables3D = React85.useMemo(() => {
|
|
14857
|
+
if (mode !== "3d") return [];
|
|
14858
|
+
if (shapes.length > 0) {
|
|
14859
|
+
biologyLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
14860
|
+
}
|
|
14861
|
+
const out = [];
|
|
14862
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
14863
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
14864
|
+
for (const n of nodes) {
|
|
14865
|
+
if (n.id) nodeById.set(n.id, n);
|
|
14866
|
+
}
|
|
14867
|
+
for (const e of edges) {
|
|
14868
|
+
const a = nodeById.get(e.from);
|
|
14869
|
+
const b = nodeById.get(e.to);
|
|
14870
|
+
if (!a || !b) continue;
|
|
14871
|
+
const edgeRadius = Math.max(0.04, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.12);
|
|
14872
|
+
const edge = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], edgeRadius, e.color ?? "#9ca3af");
|
|
14873
|
+
if (edge) out.push(edge);
|
|
14874
|
+
if (e.label) {
|
|
14875
|
+
out.push(
|
|
14876
|
+
billboardLabel(
|
|
14877
|
+
e.label,
|
|
14878
|
+
(a.x + b.x) / 2,
|
|
14879
|
+
(a.y + b.y) / 2,
|
|
14880
|
+
((a.z ?? 0) + (b.z ?? 0)) / 2,
|
|
14881
|
+
{ color: labelColor }
|
|
14882
|
+
)
|
|
14883
|
+
);
|
|
14884
|
+
}
|
|
14885
|
+
}
|
|
14886
|
+
for (const n of nodes) {
|
|
14887
|
+
const radius = n.radius ?? 0.5;
|
|
14888
|
+
const nz = n.z ?? 0;
|
|
14889
|
+
out.push(meshSphere(n.id, n.x, n.y, nz, radius, n.color ?? "#16a34a", { shape: n.shape, ...n.opacity !== void 0 ? { opacity: n.opacity } : {} }));
|
|
14890
|
+
if (n.label) {
|
|
14891
|
+
out.push(billboardLabel(n.label, n.x, n.y, nz + radius, { color: labelColor }));
|
|
14892
|
+
}
|
|
14893
|
+
}
|
|
14894
|
+
return out;
|
|
14895
|
+
}, [mode, nodes, edges, shapes, backgroundColor]);
|
|
14896
|
+
const nodeIndexById = React85.useMemo(() => {
|
|
14897
|
+
const m = /* @__PURE__ */ new Map();
|
|
14898
|
+
nodes.forEach((n, i) => {
|
|
14899
|
+
if (n.id) m.set(n.id, i);
|
|
14900
|
+
});
|
|
14901
|
+
return m;
|
|
14902
|
+
}, [nodes]);
|
|
14903
|
+
if (mode === "3d") {
|
|
14904
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14905
|
+
LearningScene3D,
|
|
14906
|
+
{
|
|
14907
|
+
className,
|
|
14908
|
+
width,
|
|
14909
|
+
height,
|
|
14910
|
+
title,
|
|
14911
|
+
backgroundColor,
|
|
14912
|
+
drawables: drawables3D,
|
|
14913
|
+
camera,
|
|
14914
|
+
lighting,
|
|
14915
|
+
post,
|
|
14916
|
+
showGrid,
|
|
14917
|
+
shadows,
|
|
14918
|
+
interactive,
|
|
14919
|
+
isLoading,
|
|
14920
|
+
error,
|
|
14921
|
+
onItemClick: get3DClickPayload(onShapeClick, nodeIndexById)
|
|
14922
|
+
}
|
|
14923
|
+
);
|
|
14924
|
+
}
|
|
14687
14925
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
14688
14926
|
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
14689
14927
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -14693,7 +14931,7 @@ var init_BiologyCanvas = __esm({
|
|
|
14693
14931
|
height,
|
|
14694
14932
|
backgroundColor,
|
|
14695
14933
|
shapes: derivedShapes,
|
|
14696
|
-
interactive,
|
|
14934
|
+
interactive: interactive ?? false,
|
|
14697
14935
|
animate,
|
|
14698
14936
|
onShapeClick,
|
|
14699
14937
|
isLoading,
|
|
@@ -21416,24 +21654,41 @@ var init_ChatBar = __esm({
|
|
|
21416
21654
|
ChatBar.displayName = "ChatBar";
|
|
21417
21655
|
}
|
|
21418
21656
|
});
|
|
21419
|
-
|
|
21657
|
+
function bondPerpendicular(a, b) {
|
|
21658
|
+
const dx = b[0] - a[0];
|
|
21659
|
+
const dy = b[1] - a[1];
|
|
21660
|
+
const px = dy;
|
|
21661
|
+
const py = -dx;
|
|
21662
|
+
const len = Math.sqrt(px * px + py * py);
|
|
21663
|
+
if (len < 1e-6) return [1, 0, 0];
|
|
21664
|
+
return [px / len, py / len, 0];
|
|
21665
|
+
}
|
|
21666
|
+
var chemistryLog, ChemistryCanvas;
|
|
21420
21667
|
var init_ChemistryCanvas = __esm({
|
|
21421
21668
|
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
21422
21669
|
"use client";
|
|
21423
21670
|
init_atoms();
|
|
21424
21671
|
init_Stack();
|
|
21425
21672
|
init_LearningCanvas();
|
|
21673
|
+
init_learningScene3D();
|
|
21674
|
+
chemistryLog = logger.createLogger("almadar:ui:chemistry-canvas");
|
|
21426
21675
|
ChemistryCanvas = ({
|
|
21427
21676
|
className,
|
|
21428
21677
|
width = 600,
|
|
21429
21678
|
height = 400,
|
|
21430
21679
|
title,
|
|
21431
21680
|
backgroundColor,
|
|
21681
|
+
mode = "2d",
|
|
21682
|
+
camera,
|
|
21683
|
+
lighting,
|
|
21684
|
+
post,
|
|
21432
21685
|
atoms = [],
|
|
21433
21686
|
bonds = [],
|
|
21434
21687
|
arrows = [],
|
|
21435
21688
|
shapes = [],
|
|
21436
|
-
|
|
21689
|
+
showGrid,
|
|
21690
|
+
shadows,
|
|
21691
|
+
interactive,
|
|
21437
21692
|
animate = false,
|
|
21438
21693
|
onShapeClick,
|
|
21439
21694
|
isLoading,
|
|
@@ -21512,6 +21767,88 @@ var init_ChemistryCanvas = __esm({
|
|
|
21512
21767
|
out.push(...shapes);
|
|
21513
21768
|
return out;
|
|
21514
21769
|
}, [atoms, bonds, arrows, shapes]);
|
|
21770
|
+
const drawables3D = React85.useMemo(() => {
|
|
21771
|
+
if (mode !== "3d") return [];
|
|
21772
|
+
if (shapes.length > 0) {
|
|
21773
|
+
chemistryLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
21774
|
+
}
|
|
21775
|
+
const out = [];
|
|
21776
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
21777
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
21778
|
+
for (const a of atoms) {
|
|
21779
|
+
if (a.id) atomById.set(a.id, a);
|
|
21780
|
+
}
|
|
21781
|
+
for (const b of bonds) {
|
|
21782
|
+
const a = atomById.get(b.from);
|
|
21783
|
+
const c = atomById.get(b.to);
|
|
21784
|
+
if (!a || !c) continue;
|
|
21785
|
+
const color = b.color ?? "#6b7280";
|
|
21786
|
+
const from = [a.x, a.y, a.z ?? 0];
|
|
21787
|
+
const to = [c.x, c.y, c.z ?? 0];
|
|
21788
|
+
const perp = bondPerpendicular(from, to);
|
|
21789
|
+
const bondRadius = Math.max(0.06, Math.min(a.radius ?? 0.45, c.radius ?? 0.45) * 0.22);
|
|
21790
|
+
const step = bondRadius * 2.2;
|
|
21791
|
+
const offsets = b.type === "double" ? [-step, step] : b.type === "triple" ? [-step, 0, step] : [0];
|
|
21792
|
+
for (const off of offsets) {
|
|
21793
|
+
const bond = cylinderBetween(
|
|
21794
|
+
[from[0] + perp[0] * off, from[1] + perp[1] * off, from[2] + perp[2] * off],
|
|
21795
|
+
[to[0] + perp[0] * off, to[1] + perp[1] * off, to[2] + perp[2] * off],
|
|
21796
|
+
bondRadius,
|
|
21797
|
+
color
|
|
21798
|
+
);
|
|
21799
|
+
if (bond) out.push(bond);
|
|
21800
|
+
}
|
|
21801
|
+
}
|
|
21802
|
+
for (const a of arrows) {
|
|
21803
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
21804
|
+
const len = a.length ?? 60;
|
|
21805
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
21806
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
21807
|
+
const arrow = arrowBetween([a.x, a.y, 0], [x2, y2, 0], a.color ?? "#dc2626");
|
|
21808
|
+
if (arrow) out.push(arrow);
|
|
21809
|
+
if (a.label) {
|
|
21810
|
+
out.push(billboardLabel(a.label, (a.x + x2) / 2, (a.y + y2) / 2, 0, { color: labelColor }));
|
|
21811
|
+
}
|
|
21812
|
+
}
|
|
21813
|
+
for (const a of atoms) {
|
|
21814
|
+
const radius = a.radius ?? 0.45;
|
|
21815
|
+
const az = a.z ?? 0;
|
|
21816
|
+
out.push(meshSphere(a.id, a.x, a.y, az, radius, a.color ?? "#2563eb"));
|
|
21817
|
+
if (a.element) {
|
|
21818
|
+
out.push(billboardLabel(a.element, a.x, a.y, az + radius, { color: labelColor }));
|
|
21819
|
+
}
|
|
21820
|
+
}
|
|
21821
|
+
return out;
|
|
21822
|
+
}, [mode, atoms, bonds, arrows, shapes, backgroundColor]);
|
|
21823
|
+
const atomIndexById = React85.useMemo(() => {
|
|
21824
|
+
const m = /* @__PURE__ */ new Map();
|
|
21825
|
+
atoms.forEach((a, i) => {
|
|
21826
|
+
if (a.id) m.set(a.id, i);
|
|
21827
|
+
});
|
|
21828
|
+
return m;
|
|
21829
|
+
}, [atoms]);
|
|
21830
|
+
if (mode === "3d") {
|
|
21831
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
21832
|
+
LearningScene3D,
|
|
21833
|
+
{
|
|
21834
|
+
className,
|
|
21835
|
+
width,
|
|
21836
|
+
height,
|
|
21837
|
+
title,
|
|
21838
|
+
backgroundColor,
|
|
21839
|
+
drawables: drawables3D,
|
|
21840
|
+
camera,
|
|
21841
|
+
lighting,
|
|
21842
|
+
post,
|
|
21843
|
+
showGrid,
|
|
21844
|
+
shadows,
|
|
21845
|
+
interactive,
|
|
21846
|
+
isLoading,
|
|
21847
|
+
error,
|
|
21848
|
+
onItemClick: get3DClickPayload(onShapeClick, atomIndexById)
|
|
21849
|
+
}
|
|
21850
|
+
);
|
|
21851
|
+
}
|
|
21515
21852
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
21516
21853
|
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
21517
21854
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -21521,7 +21858,7 @@ var init_ChemistryCanvas = __esm({
|
|
|
21521
21858
|
height,
|
|
21522
21859
|
backgroundColor,
|
|
21523
21860
|
shapes: derivedShapes,
|
|
21524
|
-
interactive,
|
|
21861
|
+
interactive: interactive ?? false,
|
|
21525
21862
|
animate,
|
|
21526
21863
|
onShapeClick,
|
|
21527
21864
|
isLoading,
|
|
@@ -24914,6 +25251,7 @@ var init_FilterGroup = __esm({
|
|
|
24914
25251
|
init_Badge();
|
|
24915
25252
|
init_Stack();
|
|
24916
25253
|
init_Icon();
|
|
25254
|
+
init_RangeSlider();
|
|
24917
25255
|
init_useEventBus();
|
|
24918
25256
|
init_useQuerySingleton();
|
|
24919
25257
|
resolveFilterType = (filter) => filter.filterType ?? filter.type;
|
|
@@ -25108,6 +25446,35 @@ var init_FilterGroup = __esm({
|
|
|
25108
25446
|
onClear: () => handleFilterSelect(`${filter.field}_to`, null)
|
|
25109
25447
|
}
|
|
25110
25448
|
)
|
|
25449
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
25450
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
25451
|
+
RangeSlider,
|
|
25452
|
+
{
|
|
25453
|
+
min: filter.min ?? 0,
|
|
25454
|
+
max: filter.max ?? 100,
|
|
25455
|
+
step: filter.step ?? 1,
|
|
25456
|
+
value: Number(
|
|
25457
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
25458
|
+
),
|
|
25459
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
25460
|
+
showTooltip: true,
|
|
25461
|
+
"aria-label": t("filterGroup.from")
|
|
25462
|
+
}
|
|
25463
|
+
),
|
|
25464
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
25465
|
+
RangeSlider,
|
|
25466
|
+
{
|
|
25467
|
+
min: filter.min ?? 0,
|
|
25468
|
+
max: filter.max ?? 100,
|
|
25469
|
+
step: filter.step ?? 1,
|
|
25470
|
+
value: Number(
|
|
25471
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
25472
|
+
),
|
|
25473
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
25474
|
+
showTooltip: true,
|
|
25475
|
+
"aria-label": t("filterGroup.to")
|
|
25476
|
+
}
|
|
25477
|
+
)
|
|
25111
25478
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
25112
25479
|
Input,
|
|
25113
25480
|
{
|
|
@@ -25190,6 +25557,36 @@ var init_FilterGroup = __esm({
|
|
|
25190
25557
|
className: "text-sm min-w-[100px]"
|
|
25191
25558
|
}
|
|
25192
25559
|
)
|
|
25560
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
25561
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
25562
|
+
RangeSlider,
|
|
25563
|
+
{
|
|
25564
|
+
min: filter.min ?? 0,
|
|
25565
|
+
max: filter.max ?? 100,
|
|
25566
|
+
step: filter.step ?? 1,
|
|
25567
|
+
value: Number(
|
|
25568
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
25569
|
+
),
|
|
25570
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
25571
|
+
className: "min-w-[100px]",
|
|
25572
|
+
"aria-label": t("filterGroup.from")
|
|
25573
|
+
}
|
|
25574
|
+
),
|
|
25575
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "-" }),
|
|
25576
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
25577
|
+
RangeSlider,
|
|
25578
|
+
{
|
|
25579
|
+
min: filter.min ?? 0,
|
|
25580
|
+
max: filter.max ?? 100,
|
|
25581
|
+
step: filter.step ?? 1,
|
|
25582
|
+
value: Number(
|
|
25583
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
25584
|
+
),
|
|
25585
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
25586
|
+
className: "min-w-[100px]",
|
|
25587
|
+
"aria-label": t("filterGroup.to")
|
|
25588
|
+
}
|
|
25589
|
+
)
|
|
25193
25590
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
25194
25591
|
Input,
|
|
25195
25592
|
{
|
|
@@ -25308,6 +25705,38 @@ var init_FilterGroup = __esm({
|
|
|
25308
25705
|
className: "min-w-[130px]"
|
|
25309
25706
|
}
|
|
25310
25707
|
)
|
|
25708
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
25709
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
25710
|
+
RangeSlider,
|
|
25711
|
+
{
|
|
25712
|
+
min: filter.min ?? 0,
|
|
25713
|
+
max: filter.max ?? 100,
|
|
25714
|
+
step: filter.step ?? 1,
|
|
25715
|
+
value: Number(
|
|
25716
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
25717
|
+
),
|
|
25718
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
25719
|
+
showTooltip: true,
|
|
25720
|
+
className: "min-w-[130px]",
|
|
25721
|
+
"aria-label": t("filterGroup.from")
|
|
25722
|
+
}
|
|
25723
|
+
),
|
|
25724
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "-" }),
|
|
25725
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
25726
|
+
RangeSlider,
|
|
25727
|
+
{
|
|
25728
|
+
min: filter.min ?? 0,
|
|
25729
|
+
max: filter.max ?? 100,
|
|
25730
|
+
step: filter.step ?? 1,
|
|
25731
|
+
value: Number(
|
|
25732
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
25733
|
+
),
|
|
25734
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
25735
|
+
showTooltip: true,
|
|
25736
|
+
className: "min-w-[130px]",
|
|
25737
|
+
"aria-label": t("filterGroup.to")
|
|
25738
|
+
}
|
|
25739
|
+
)
|
|
25311
25740
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
25312
25741
|
Input,
|
|
25313
25742
|
{
|
|
@@ -27656,19 +28085,25 @@ var init_MathCanvas = __esm({
|
|
|
27656
28085
|
};
|
|
27657
28086
|
}
|
|
27658
28087
|
});
|
|
27659
|
-
var PhysicsCanvas;
|
|
28088
|
+
var physicsLog2, PhysicsCanvas;
|
|
27660
28089
|
var init_PhysicsCanvas = __esm({
|
|
27661
28090
|
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
27662
28091
|
"use client";
|
|
27663
28092
|
init_atoms();
|
|
27664
28093
|
init_Stack();
|
|
27665
28094
|
init_LearningCanvas();
|
|
28095
|
+
init_learningScene3D();
|
|
28096
|
+
physicsLog2 = logger.createLogger("almadar:ui:physics-canvas");
|
|
27666
28097
|
PhysicsCanvas = ({
|
|
27667
28098
|
className,
|
|
27668
28099
|
width = 600,
|
|
27669
28100
|
height = 400,
|
|
27670
28101
|
title,
|
|
27671
28102
|
backgroundColor,
|
|
28103
|
+
mode = "2d",
|
|
28104
|
+
camera,
|
|
28105
|
+
lighting,
|
|
28106
|
+
post,
|
|
27672
28107
|
bodies = [],
|
|
27673
28108
|
constraints = [],
|
|
27674
28109
|
showVelocity = true,
|
|
@@ -27676,7 +28111,9 @@ var init_PhysicsCanvas = __esm({
|
|
|
27676
28111
|
velocityScale = 20,
|
|
27677
28112
|
forceScale = 20,
|
|
27678
28113
|
shapes = [],
|
|
27679
|
-
|
|
28114
|
+
showGrid,
|
|
28115
|
+
shadows,
|
|
28116
|
+
interactive,
|
|
27680
28117
|
animate = false,
|
|
27681
28118
|
onShapeClick,
|
|
27682
28119
|
isLoading,
|
|
@@ -27748,6 +28185,89 @@ var init_PhysicsCanvas = __esm({
|
|
|
27748
28185
|
out.push(...shapes);
|
|
27749
28186
|
return out;
|
|
27750
28187
|
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
28188
|
+
const drawables3D = React85.useMemo(() => {
|
|
28189
|
+
if (mode !== "3d") return [];
|
|
28190
|
+
if (shapes.length > 0) {
|
|
28191
|
+
physicsLog2.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
28192
|
+
}
|
|
28193
|
+
const out = [];
|
|
28194
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
28195
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
28196
|
+
for (const b of bodies) {
|
|
28197
|
+
if (b.id) bodyById.set(b.id, b);
|
|
28198
|
+
}
|
|
28199
|
+
for (const c of constraints) {
|
|
28200
|
+
const a = bodyById.get(c.from);
|
|
28201
|
+
const b = bodyById.get(c.to);
|
|
28202
|
+
if (!a || !b) continue;
|
|
28203
|
+
const rodRadius = Math.max(0.05, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.15);
|
|
28204
|
+
const rod = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], rodRadius, c.color ?? "#9ca3af");
|
|
28205
|
+
if (rod) out.push(rod);
|
|
28206
|
+
}
|
|
28207
|
+
for (const b of bodies) {
|
|
28208
|
+
const radius = b.radius ?? 0.5;
|
|
28209
|
+
const bz = b.z ?? 0;
|
|
28210
|
+
out.push(meshSphere(b.id, b.x, b.y, bz, radius, b.color ?? "#2563eb"));
|
|
28211
|
+
if (b.label) {
|
|
28212
|
+
out.push(billboardLabel(b.label, b.x, b.y, bz + radius, { color: labelColor }));
|
|
28213
|
+
}
|
|
28214
|
+
const vx = b.vx ?? 0;
|
|
28215
|
+
const vy = b.vy ?? 0;
|
|
28216
|
+
const vz = b.vz ?? 0;
|
|
28217
|
+
const arrowRadius = Math.max(0.05, radius * 0.15);
|
|
28218
|
+
if (showVelocity && (vx !== 0 || vy !== 0 || vz !== 0)) {
|
|
28219
|
+
const arrow = arrowBetween(
|
|
28220
|
+
[b.x, b.y, bz],
|
|
28221
|
+
[b.x + vx * velocityScale, b.y + vy * velocityScale, bz + vz * velocityScale],
|
|
28222
|
+
"#16a34a",
|
|
28223
|
+
arrowRadius
|
|
28224
|
+
);
|
|
28225
|
+
if (arrow) out.push(arrow);
|
|
28226
|
+
}
|
|
28227
|
+
const fx = b.fx ?? 0;
|
|
28228
|
+
const fy = b.fy ?? 0;
|
|
28229
|
+
const fz = b.fz ?? 0;
|
|
28230
|
+
if (showForces && (fx !== 0 || fy !== 0 || fz !== 0)) {
|
|
28231
|
+
const arrow = arrowBetween(
|
|
28232
|
+
[b.x, b.y, bz],
|
|
28233
|
+
[b.x + fx * forceScale, b.y + fy * forceScale, bz + fz * forceScale],
|
|
28234
|
+
"#dc2626",
|
|
28235
|
+
arrowRadius
|
|
28236
|
+
);
|
|
28237
|
+
if (arrow) out.push(arrow);
|
|
28238
|
+
}
|
|
28239
|
+
}
|
|
28240
|
+
return out;
|
|
28241
|
+
}, [mode, bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes, backgroundColor]);
|
|
28242
|
+
const bodyIndexById = React85.useMemo(() => {
|
|
28243
|
+
const m = /* @__PURE__ */ new Map();
|
|
28244
|
+
bodies.forEach((b, i) => {
|
|
28245
|
+
if (b.id) m.set(b.id, i);
|
|
28246
|
+
});
|
|
28247
|
+
return m;
|
|
28248
|
+
}, [bodies]);
|
|
28249
|
+
if (mode === "3d") {
|
|
28250
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
28251
|
+
LearningScene3D,
|
|
28252
|
+
{
|
|
28253
|
+
className,
|
|
28254
|
+
width,
|
|
28255
|
+
height,
|
|
28256
|
+
title,
|
|
28257
|
+
backgroundColor,
|
|
28258
|
+
drawables: drawables3D,
|
|
28259
|
+
camera,
|
|
28260
|
+
lighting,
|
|
28261
|
+
post,
|
|
28262
|
+
showGrid,
|
|
28263
|
+
shadows,
|
|
28264
|
+
interactive,
|
|
28265
|
+
isLoading,
|
|
28266
|
+
error,
|
|
28267
|
+
onItemClick: get3DClickPayload(onShapeClick, bodyIndexById)
|
|
28268
|
+
}
|
|
28269
|
+
);
|
|
28270
|
+
}
|
|
27751
28271
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
27752
28272
|
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
27753
28273
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -27757,7 +28277,7 @@ var init_PhysicsCanvas = __esm({
|
|
|
27757
28277
|
height,
|
|
27758
28278
|
backgroundColor,
|
|
27759
28279
|
shapes: derivedShapes,
|
|
27760
|
-
interactive,
|
|
28280
|
+
interactive: interactive ?? false,
|
|
27761
28281
|
animate,
|
|
27762
28282
|
onShapeClick,
|
|
27763
28283
|
isLoading,
|
|
@@ -28375,12 +28895,12 @@ var init_MapView = __esm({
|
|
|
28375
28895
|
shadowSize: [41, 41]
|
|
28376
28896
|
});
|
|
28377
28897
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
28378
|
-
const { useEffect: useEffect60, useRef:
|
|
28898
|
+
const { useEffect: useEffect60, useRef: useRef63, useCallback: useCallback90, useState: useState92 } = React85__namespace.default;
|
|
28379
28899
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
28380
28900
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
28381
28901
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
28382
28902
|
const map = useMap();
|
|
28383
|
-
const prevRef =
|
|
28903
|
+
const prevRef = useRef63({ centerLat, centerLng, zoom });
|
|
28384
28904
|
useEffect60(() => {
|
|
28385
28905
|
const prev = prevRef.current;
|
|
28386
28906
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
@@ -39045,7 +39565,7 @@ function getEnumOptions(field) {
|
|
|
39045
39565
|
}));
|
|
39046
39566
|
}
|
|
39047
39567
|
const validation = field.validation;
|
|
39048
|
-
if (validation?.enum &&
|
|
39568
|
+
if (validation?.enum && validation.enum.length > 0) {
|
|
39049
39569
|
return validation.enum.map((v) => ({
|
|
39050
39570
|
value: v,
|
|
39051
39571
|
label: v.charAt(0).toUpperCase() + v.slice(1).replace(/_/g, " ")
|