@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/providers/index.cjs
CHANGED
|
@@ -10375,9 +10375,10 @@ function Canvas({
|
|
|
10375
10375
|
drawables: [...drawables ?? [], ...childDrawables],
|
|
10376
10376
|
isLoading,
|
|
10377
10377
|
cameraMode: to3DCameraMode(camera?.mode),
|
|
10378
|
-
...zoom !== void 0 ? {
|
|
10378
|
+
...zoom !== void 0 ? { zoom } : {},
|
|
10379
10379
|
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
10380
10380
|
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
10381
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
10381
10382
|
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
10382
10383
|
unitScale,
|
|
10383
10384
|
backgroundColor,
|
|
@@ -11853,7 +11854,10 @@ var init_LearningCanvas = __esm({
|
|
|
11853
11854
|
ctx.fillRect(0, 0, width, height);
|
|
11854
11855
|
}
|
|
11855
11856
|
for (const shape of shapes) {
|
|
11856
|
-
drawShape(ctx, shape, width, height);
|
|
11857
|
+
if (shape.type !== "text") drawShape(ctx, shape, width, height);
|
|
11858
|
+
}
|
|
11859
|
+
for (const shape of shapes) {
|
|
11860
|
+
if (shape.type === "text") drawShape(ctx, shape, width, height);
|
|
11857
11861
|
}
|
|
11858
11862
|
}, [width, height, backgroundColor, shapes]);
|
|
11859
11863
|
React87.useEffect(() => {
|
|
@@ -13799,7 +13803,7 @@ var init_AlgorithmCanvas = __esm({
|
|
|
13799
13803
|
DEFAULT_CELL_COLOR = "#e5e7eb";
|
|
13800
13804
|
DEFAULT_POINTER_COLOR = "#dc2626";
|
|
13801
13805
|
POINTER_BAND = 34;
|
|
13802
|
-
TOP_PAD =
|
|
13806
|
+
TOP_PAD = 26;
|
|
13803
13807
|
AlgorithmCanvas = ({
|
|
13804
13808
|
className,
|
|
13805
13809
|
width = 600,
|
|
@@ -14903,23 +14907,188 @@ var init_BehaviorView = __esm({
|
|
|
14903
14907
|
BehaviorView.displayName = "BehaviorView";
|
|
14904
14908
|
}
|
|
14905
14909
|
});
|
|
14906
|
-
|
|
14910
|
+
function LearningScene3D({
|
|
14911
|
+
className,
|
|
14912
|
+
width = 600,
|
|
14913
|
+
height = 400,
|
|
14914
|
+
title,
|
|
14915
|
+
backgroundColor,
|
|
14916
|
+
drawables,
|
|
14917
|
+
camera,
|
|
14918
|
+
lighting,
|
|
14919
|
+
post,
|
|
14920
|
+
showGrid = false,
|
|
14921
|
+
shadows,
|
|
14922
|
+
interactive,
|
|
14923
|
+
isLoading,
|
|
14924
|
+
error,
|
|
14925
|
+
onItemClick
|
|
14926
|
+
}) {
|
|
14927
|
+
const instanceId = React87.useId().replace(/[^a-zA-Z0-9]/g, "");
|
|
14928
|
+
const clickEvent = onItemClick ? `LEARNING_SCENE_3D.ITEM_CLICK.${instanceId}` : void 0;
|
|
14929
|
+
const onItemClickRef = React87.useRef(onItemClick);
|
|
14930
|
+
onItemClickRef.current = onItemClick;
|
|
14931
|
+
useEventListener(`UI:${clickEvent ?? "LEARNING_SCENE_3D.ITEM_CLICK.__disabled"}`, (event) => {
|
|
14932
|
+
const unitId = event.payload?.unitId;
|
|
14933
|
+
if (typeof unitId === "string") onItemClickRef.current?.(unitId);
|
|
14934
|
+
});
|
|
14935
|
+
const props3d = {
|
|
14936
|
+
drawables,
|
|
14937
|
+
isLoading,
|
|
14938
|
+
error: error ? error.message : null,
|
|
14939
|
+
cameraMode: camera?.mode ?? "perspective",
|
|
14940
|
+
...camera?.zoom !== void 0 ? { zoom: camera.zoom } : {},
|
|
14941
|
+
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
14942
|
+
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
14943
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
14944
|
+
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
14945
|
+
backgroundColor,
|
|
14946
|
+
showGrid,
|
|
14947
|
+
...shadows !== void 0 ? { shadows } : {},
|
|
14948
|
+
...interactive !== void 0 ? { controlsEnabled: interactive } : {},
|
|
14949
|
+
lighting,
|
|
14950
|
+
post,
|
|
14951
|
+
...clickEvent !== void 0 ? { unitClickEvent: clickEvent } : {}
|
|
14952
|
+
};
|
|
14953
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
14954
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
14955
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { width, height, display: "flex" }, children: /* @__PURE__ */ jsxRuntime.jsx(React87.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx(Canvas3DHost2, { ...props3d }) }) })
|
|
14956
|
+
] }) });
|
|
14957
|
+
}
|
|
14958
|
+
function meshSphere(id, x, y, z, radius, color, opts) {
|
|
14959
|
+
return {
|
|
14960
|
+
type: "draw-mesh",
|
|
14961
|
+
...id !== void 0 ? { id } : {},
|
|
14962
|
+
shape: opts?.shape ?? "sphere",
|
|
14963
|
+
position: { x, y, z },
|
|
14964
|
+
radius,
|
|
14965
|
+
pivot: "center",
|
|
14966
|
+
segments: opts?.segments ?? 24,
|
|
14967
|
+
material: { color, ...opts?.material },
|
|
14968
|
+
...opts?.opacity !== void 0 ? { opacity: opts.opacity } : {}
|
|
14969
|
+
};
|
|
14970
|
+
}
|
|
14971
|
+
function axisRotation(from, to) {
|
|
14972
|
+
const wx = to[0] - from[0];
|
|
14973
|
+
const wy = to[2] - from[2];
|
|
14974
|
+
const wz = to[1] - from[1];
|
|
14975
|
+
const len = Math.sqrt(wx * wx + wy * wy + wz * wz);
|
|
14976
|
+
const tilt = Math.acos(Math.min(1, Math.max(-1, len > 0 ? wy / len : 1)));
|
|
14977
|
+
return [0, Math.atan2(wz, -wx), tilt];
|
|
14978
|
+
}
|
|
14979
|
+
function segmentLength(from, to) {
|
|
14980
|
+
const dx = to[0] - from[0];
|
|
14981
|
+
const dy = to[1] - from[1];
|
|
14982
|
+
const dz = to[2] - from[2];
|
|
14983
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
14984
|
+
}
|
|
14985
|
+
function cylinderBetween(from, to, radius, color) {
|
|
14986
|
+
const len = segmentLength(from, to);
|
|
14987
|
+
if (len < 1e-6) return null;
|
|
14988
|
+
return {
|
|
14989
|
+
type: "draw-mesh",
|
|
14990
|
+
shape: "cylinder",
|
|
14991
|
+
position: { x: (from[0] + to[0]) / 2, y: (from[1] + to[1]) / 2, z: (from[2] + to[2]) / 2 },
|
|
14992
|
+
radius,
|
|
14993
|
+
height: len,
|
|
14994
|
+
rotation: axisRotation(from, to),
|
|
14995
|
+
pivot: "center",
|
|
14996
|
+
segments: 12,
|
|
14997
|
+
material: { color }
|
|
14998
|
+
};
|
|
14999
|
+
}
|
|
15000
|
+
function arrowBetween(from, to, color, shaftRadius = 0.08) {
|
|
15001
|
+
const len = segmentLength(from, to);
|
|
15002
|
+
if (len < 1e-6) return null;
|
|
15003
|
+
const tipLen = Math.min(shaftRadius * 8, len * 0.35);
|
|
15004
|
+
const k = (len - tipLen) / len;
|
|
15005
|
+
const delta = [to[0] - from[0], to[1] - from[1], to[2] - from[2]];
|
|
15006
|
+
const shaftEnd = [delta[0] * k, delta[1] * k, delta[2] * k];
|
|
15007
|
+
const tipStart = shaftEnd;
|
|
15008
|
+
const tipEnd = delta;
|
|
15009
|
+
const shaft = cylinderBetween([0, 0, 0], shaftEnd, shaftRadius, color);
|
|
15010
|
+
const tipLenActual = segmentLength(tipStart, tipEnd);
|
|
15011
|
+
const tip = {
|
|
15012
|
+
type: "draw-mesh",
|
|
15013
|
+
shape: "cone",
|
|
15014
|
+
position: {
|
|
15015
|
+
x: (tipStart[0] + tipEnd[0]) / 2,
|
|
15016
|
+
y: (tipStart[1] + tipEnd[1]) / 2,
|
|
15017
|
+
z: (tipStart[2] + tipEnd[2]) / 2
|
|
15018
|
+
},
|
|
15019
|
+
radius: shaftRadius * 3,
|
|
15020
|
+
height: tipLenActual,
|
|
15021
|
+
rotation: axisRotation(tipStart, tipEnd),
|
|
15022
|
+
pivot: "center",
|
|
15023
|
+
segments: 12,
|
|
15024
|
+
material: { color }
|
|
15025
|
+
};
|
|
15026
|
+
return {
|
|
15027
|
+
type: "draw-group",
|
|
15028
|
+
position: { x: from[0], y: from[1], z: from[2] },
|
|
15029
|
+
items: tipLenActual < 1e-6 ? shaft ? [shaft] : [] : shaft ? [shaft, tip] : [tip]
|
|
15030
|
+
};
|
|
15031
|
+
}
|
|
15032
|
+
function billboardLabel(text, x, y, z, opts) {
|
|
15033
|
+
return {
|
|
15034
|
+
type: "draw-text",
|
|
15035
|
+
text,
|
|
15036
|
+
position: { x, y, z },
|
|
15037
|
+
color: opts?.color ?? "#111827"
|
|
15038
|
+
};
|
|
15039
|
+
}
|
|
15040
|
+
function labelColorForBackground(backgroundColor) {
|
|
15041
|
+
const m = /^#(?:([0-9a-f]{3})|([0-9a-f]{6}))$/i.exec(backgroundColor ?? "");
|
|
15042
|
+
if (!m) return "#111827";
|
|
15043
|
+
const hex = m[1] !== void 0 ? m[1].split("").map((c) => c + c).join("") : m[2];
|
|
15044
|
+
const r = parseInt(hex.slice(0, 2), 16) / 255;
|
|
15045
|
+
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
15046
|
+
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
15047
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
15048
|
+
return luminance < 0.5 ? "#e5e7eb" : "#111827";
|
|
15049
|
+
}
|
|
15050
|
+
function get3DClickPayload(onShapeClick, idToIndex) {
|
|
15051
|
+
if (!onShapeClick) return void 0;
|
|
15052
|
+
return (id) => onShapeClick({ id, index: idToIndex.get(id) ?? -1 });
|
|
15053
|
+
}
|
|
15054
|
+
var Canvas3DHost2;
|
|
15055
|
+
var init_learningScene3D = __esm({
|
|
15056
|
+
"components/learning/molecules/learningScene3D.tsx"() {
|
|
15057
|
+
"use client";
|
|
15058
|
+
init_atoms();
|
|
15059
|
+
init_Stack();
|
|
15060
|
+
init_useEventBus();
|
|
15061
|
+
Canvas3DHost2 = React87.lazy(
|
|
15062
|
+
() => import('@almadar/ui/components/molecules/game/three').then((m) => ({ default: m.Canvas3DHost }))
|
|
15063
|
+
);
|
|
15064
|
+
LearningScene3D.displayName = "LearningScene3D";
|
|
15065
|
+
}
|
|
15066
|
+
});
|
|
15067
|
+
var biologyLog, BiologyCanvas;
|
|
14907
15068
|
var init_BiologyCanvas = __esm({
|
|
14908
15069
|
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
14909
15070
|
"use client";
|
|
14910
15071
|
init_atoms();
|
|
14911
15072
|
init_Stack();
|
|
14912
15073
|
init_LearningCanvas();
|
|
15074
|
+
init_learningScene3D();
|
|
15075
|
+
biologyLog = logger.createLogger("almadar:ui:biology-canvas");
|
|
14913
15076
|
BiologyCanvas = ({
|
|
14914
15077
|
className,
|
|
14915
15078
|
width = 600,
|
|
14916
15079
|
height = 400,
|
|
14917
15080
|
title,
|
|
14918
15081
|
backgroundColor,
|
|
15082
|
+
mode = "2d",
|
|
15083
|
+
camera,
|
|
15084
|
+
lighting,
|
|
15085
|
+
post,
|
|
14919
15086
|
nodes = [],
|
|
14920
15087
|
edges = [],
|
|
14921
15088
|
shapes = [],
|
|
14922
|
-
|
|
15089
|
+
showGrid,
|
|
15090
|
+
shadows,
|
|
15091
|
+
interactive,
|
|
14923
15092
|
animate = false,
|
|
14924
15093
|
onShapeClick,
|
|
14925
15094
|
isLoading,
|
|
@@ -14980,6 +15149,75 @@ var init_BiologyCanvas = __esm({
|
|
|
14980
15149
|
out.push(...shapes);
|
|
14981
15150
|
return out;
|
|
14982
15151
|
}, [nodes, edges, shapes]);
|
|
15152
|
+
const drawables3D = React87.useMemo(() => {
|
|
15153
|
+
if (mode !== "3d") return [];
|
|
15154
|
+
if (shapes.length > 0) {
|
|
15155
|
+
biologyLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
15156
|
+
}
|
|
15157
|
+
const out = [];
|
|
15158
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
15159
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
15160
|
+
for (const n of nodes) {
|
|
15161
|
+
if (n.id) nodeById.set(n.id, n);
|
|
15162
|
+
}
|
|
15163
|
+
for (const e of edges) {
|
|
15164
|
+
const a = nodeById.get(e.from);
|
|
15165
|
+
const b = nodeById.get(e.to);
|
|
15166
|
+
if (!a || !b) continue;
|
|
15167
|
+
const edgeRadius = Math.max(0.04, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.12);
|
|
15168
|
+
const edge = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], edgeRadius, e.color ?? "#9ca3af");
|
|
15169
|
+
if (edge) out.push(edge);
|
|
15170
|
+
if (e.label) {
|
|
15171
|
+
out.push(
|
|
15172
|
+
billboardLabel(
|
|
15173
|
+
e.label,
|
|
15174
|
+
(a.x + b.x) / 2,
|
|
15175
|
+
(a.y + b.y) / 2,
|
|
15176
|
+
((a.z ?? 0) + (b.z ?? 0)) / 2,
|
|
15177
|
+
{ color: labelColor }
|
|
15178
|
+
)
|
|
15179
|
+
);
|
|
15180
|
+
}
|
|
15181
|
+
}
|
|
15182
|
+
for (const n of nodes) {
|
|
15183
|
+
const radius = n.radius ?? 0.5;
|
|
15184
|
+
const nz = n.z ?? 0;
|
|
15185
|
+
out.push(meshSphere(n.id, n.x, n.y, nz, radius, n.color ?? "#16a34a", { shape: n.shape, ...n.opacity !== void 0 ? { opacity: n.opacity } : {} }));
|
|
15186
|
+
if (n.label) {
|
|
15187
|
+
out.push(billboardLabel(n.label, n.x, n.y, nz + radius, { color: labelColor }));
|
|
15188
|
+
}
|
|
15189
|
+
}
|
|
15190
|
+
return out;
|
|
15191
|
+
}, [mode, nodes, edges, shapes, backgroundColor]);
|
|
15192
|
+
const nodeIndexById = React87.useMemo(() => {
|
|
15193
|
+
const m = /* @__PURE__ */ new Map();
|
|
15194
|
+
nodes.forEach((n, i) => {
|
|
15195
|
+
if (n.id) m.set(n.id, i);
|
|
15196
|
+
});
|
|
15197
|
+
return m;
|
|
15198
|
+
}, [nodes]);
|
|
15199
|
+
if (mode === "3d") {
|
|
15200
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
15201
|
+
LearningScene3D,
|
|
15202
|
+
{
|
|
15203
|
+
className,
|
|
15204
|
+
width,
|
|
15205
|
+
height,
|
|
15206
|
+
title,
|
|
15207
|
+
backgroundColor,
|
|
15208
|
+
drawables: drawables3D,
|
|
15209
|
+
camera,
|
|
15210
|
+
lighting,
|
|
15211
|
+
post,
|
|
15212
|
+
showGrid,
|
|
15213
|
+
shadows,
|
|
15214
|
+
interactive,
|
|
15215
|
+
isLoading,
|
|
15216
|
+
error,
|
|
15217
|
+
onItemClick: get3DClickPayload(onShapeClick, nodeIndexById)
|
|
15218
|
+
}
|
|
15219
|
+
);
|
|
15220
|
+
}
|
|
14983
15221
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
14984
15222
|
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
14985
15223
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -14989,7 +15227,7 @@ var init_BiologyCanvas = __esm({
|
|
|
14989
15227
|
height,
|
|
14990
15228
|
backgroundColor,
|
|
14991
15229
|
shapes: derivedShapes,
|
|
14992
|
-
interactive,
|
|
15230
|
+
interactive: interactive ?? false,
|
|
14993
15231
|
animate,
|
|
14994
15232
|
onShapeClick,
|
|
14995
15233
|
isLoading,
|
|
@@ -21829,24 +22067,41 @@ var init_ChatBar = __esm({
|
|
|
21829
22067
|
ChatBar.displayName = "ChatBar";
|
|
21830
22068
|
}
|
|
21831
22069
|
});
|
|
21832
|
-
|
|
22070
|
+
function bondPerpendicular(a, b) {
|
|
22071
|
+
const dx = b[0] - a[0];
|
|
22072
|
+
const dy = b[1] - a[1];
|
|
22073
|
+
const px = dy;
|
|
22074
|
+
const py = -dx;
|
|
22075
|
+
const len = Math.sqrt(px * px + py * py);
|
|
22076
|
+
if (len < 1e-6) return [1, 0, 0];
|
|
22077
|
+
return [px / len, py / len, 0];
|
|
22078
|
+
}
|
|
22079
|
+
var chemistryLog, ChemistryCanvas;
|
|
21833
22080
|
var init_ChemistryCanvas = __esm({
|
|
21834
22081
|
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
21835
22082
|
"use client";
|
|
21836
22083
|
init_atoms();
|
|
21837
22084
|
init_Stack();
|
|
21838
22085
|
init_LearningCanvas();
|
|
22086
|
+
init_learningScene3D();
|
|
22087
|
+
chemistryLog = logger.createLogger("almadar:ui:chemistry-canvas");
|
|
21839
22088
|
ChemistryCanvas = ({
|
|
21840
22089
|
className,
|
|
21841
22090
|
width = 600,
|
|
21842
22091
|
height = 400,
|
|
21843
22092
|
title,
|
|
21844
22093
|
backgroundColor,
|
|
22094
|
+
mode = "2d",
|
|
22095
|
+
camera,
|
|
22096
|
+
lighting,
|
|
22097
|
+
post,
|
|
21845
22098
|
atoms = [],
|
|
21846
22099
|
bonds = [],
|
|
21847
22100
|
arrows = [],
|
|
21848
22101
|
shapes = [],
|
|
21849
|
-
|
|
22102
|
+
showGrid,
|
|
22103
|
+
shadows,
|
|
22104
|
+
interactive,
|
|
21850
22105
|
animate = false,
|
|
21851
22106
|
onShapeClick,
|
|
21852
22107
|
isLoading,
|
|
@@ -21925,6 +22180,88 @@ var init_ChemistryCanvas = __esm({
|
|
|
21925
22180
|
out.push(...shapes);
|
|
21926
22181
|
return out;
|
|
21927
22182
|
}, [atoms, bonds, arrows, shapes]);
|
|
22183
|
+
const drawables3D = React87.useMemo(() => {
|
|
22184
|
+
if (mode !== "3d") return [];
|
|
22185
|
+
if (shapes.length > 0) {
|
|
22186
|
+
chemistryLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
22187
|
+
}
|
|
22188
|
+
const out = [];
|
|
22189
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
22190
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
22191
|
+
for (const a of atoms) {
|
|
22192
|
+
if (a.id) atomById.set(a.id, a);
|
|
22193
|
+
}
|
|
22194
|
+
for (const b of bonds) {
|
|
22195
|
+
const a = atomById.get(b.from);
|
|
22196
|
+
const c = atomById.get(b.to);
|
|
22197
|
+
if (!a || !c) continue;
|
|
22198
|
+
const color = b.color ?? "#6b7280";
|
|
22199
|
+
const from = [a.x, a.y, a.z ?? 0];
|
|
22200
|
+
const to = [c.x, c.y, c.z ?? 0];
|
|
22201
|
+
const perp = bondPerpendicular(from, to);
|
|
22202
|
+
const bondRadius = Math.max(0.06, Math.min(a.radius ?? 0.45, c.radius ?? 0.45) * 0.22);
|
|
22203
|
+
const step = bondRadius * 2.2;
|
|
22204
|
+
const offsets = b.type === "double" ? [-step, step] : b.type === "triple" ? [-step, 0, step] : [0];
|
|
22205
|
+
for (const off of offsets) {
|
|
22206
|
+
const bond = cylinderBetween(
|
|
22207
|
+
[from[0] + perp[0] * off, from[1] + perp[1] * off, from[2] + perp[2] * off],
|
|
22208
|
+
[to[0] + perp[0] * off, to[1] + perp[1] * off, to[2] + perp[2] * off],
|
|
22209
|
+
bondRadius,
|
|
22210
|
+
color
|
|
22211
|
+
);
|
|
22212
|
+
if (bond) out.push(bond);
|
|
22213
|
+
}
|
|
22214
|
+
}
|
|
22215
|
+
for (const a of arrows) {
|
|
22216
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
22217
|
+
const len = a.length ?? 60;
|
|
22218
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
22219
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
22220
|
+
const arrow = arrowBetween([a.x, a.y, 0], [x2, y2, 0], a.color ?? "#dc2626");
|
|
22221
|
+
if (arrow) out.push(arrow);
|
|
22222
|
+
if (a.label) {
|
|
22223
|
+
out.push(billboardLabel(a.label, (a.x + x2) / 2, (a.y + y2) / 2, 0, { color: labelColor }));
|
|
22224
|
+
}
|
|
22225
|
+
}
|
|
22226
|
+
for (const a of atoms) {
|
|
22227
|
+
const radius = a.radius ?? 0.45;
|
|
22228
|
+
const az = a.z ?? 0;
|
|
22229
|
+
out.push(meshSphere(a.id, a.x, a.y, az, radius, a.color ?? "#2563eb"));
|
|
22230
|
+
if (a.element) {
|
|
22231
|
+
out.push(billboardLabel(a.element, a.x, a.y, az + radius, { color: labelColor }));
|
|
22232
|
+
}
|
|
22233
|
+
}
|
|
22234
|
+
return out;
|
|
22235
|
+
}, [mode, atoms, bonds, arrows, shapes, backgroundColor]);
|
|
22236
|
+
const atomIndexById = React87.useMemo(() => {
|
|
22237
|
+
const m = /* @__PURE__ */ new Map();
|
|
22238
|
+
atoms.forEach((a, i) => {
|
|
22239
|
+
if (a.id) m.set(a.id, i);
|
|
22240
|
+
});
|
|
22241
|
+
return m;
|
|
22242
|
+
}, [atoms]);
|
|
22243
|
+
if (mode === "3d") {
|
|
22244
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
22245
|
+
LearningScene3D,
|
|
22246
|
+
{
|
|
22247
|
+
className,
|
|
22248
|
+
width,
|
|
22249
|
+
height,
|
|
22250
|
+
title,
|
|
22251
|
+
backgroundColor,
|
|
22252
|
+
drawables: drawables3D,
|
|
22253
|
+
camera,
|
|
22254
|
+
lighting,
|
|
22255
|
+
post,
|
|
22256
|
+
showGrid,
|
|
22257
|
+
shadows,
|
|
22258
|
+
interactive,
|
|
22259
|
+
isLoading,
|
|
22260
|
+
error,
|
|
22261
|
+
onItemClick: get3DClickPayload(onShapeClick, atomIndexById)
|
|
22262
|
+
}
|
|
22263
|
+
);
|
|
22264
|
+
}
|
|
21928
22265
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
21929
22266
|
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
21930
22267
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -21934,7 +22271,7 @@ var init_ChemistryCanvas = __esm({
|
|
|
21934
22271
|
height,
|
|
21935
22272
|
backgroundColor,
|
|
21936
22273
|
shapes: derivedShapes,
|
|
21937
|
-
interactive,
|
|
22274
|
+
interactive: interactive ?? false,
|
|
21938
22275
|
animate,
|
|
21939
22276
|
onShapeClick,
|
|
21940
22277
|
isLoading,
|
|
@@ -25405,6 +25742,7 @@ var init_FilterGroup = __esm({
|
|
|
25405
25742
|
init_Badge();
|
|
25406
25743
|
init_Stack();
|
|
25407
25744
|
init_Icon();
|
|
25745
|
+
init_RangeSlider();
|
|
25408
25746
|
init_useEventBus();
|
|
25409
25747
|
init_useQuerySingleton();
|
|
25410
25748
|
resolveFilterType = (filter) => filter.filterType ?? filter.type;
|
|
@@ -25599,6 +25937,35 @@ var init_FilterGroup = __esm({
|
|
|
25599
25937
|
onClear: () => handleFilterSelect(`${filter.field}_to`, null)
|
|
25600
25938
|
}
|
|
25601
25939
|
)
|
|
25940
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
25941
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
25942
|
+
RangeSlider,
|
|
25943
|
+
{
|
|
25944
|
+
min: filter.min ?? 0,
|
|
25945
|
+
max: filter.max ?? 100,
|
|
25946
|
+
step: filter.step ?? 1,
|
|
25947
|
+
value: Number(
|
|
25948
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
25949
|
+
),
|
|
25950
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
25951
|
+
showTooltip: true,
|
|
25952
|
+
"aria-label": t("filterGroup.from")
|
|
25953
|
+
}
|
|
25954
|
+
),
|
|
25955
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
25956
|
+
RangeSlider,
|
|
25957
|
+
{
|
|
25958
|
+
min: filter.min ?? 0,
|
|
25959
|
+
max: filter.max ?? 100,
|
|
25960
|
+
step: filter.step ?? 1,
|
|
25961
|
+
value: Number(
|
|
25962
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
25963
|
+
),
|
|
25964
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
25965
|
+
showTooltip: true,
|
|
25966
|
+
"aria-label": t("filterGroup.to")
|
|
25967
|
+
}
|
|
25968
|
+
)
|
|
25602
25969
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
25603
25970
|
Input,
|
|
25604
25971
|
{
|
|
@@ -25681,6 +26048,36 @@ var init_FilterGroup = __esm({
|
|
|
25681
26048
|
className: "text-sm min-w-[100px]"
|
|
25682
26049
|
}
|
|
25683
26050
|
)
|
|
26051
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
26052
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
26053
|
+
RangeSlider,
|
|
26054
|
+
{
|
|
26055
|
+
min: filter.min ?? 0,
|
|
26056
|
+
max: filter.max ?? 100,
|
|
26057
|
+
step: filter.step ?? 1,
|
|
26058
|
+
value: Number(
|
|
26059
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
26060
|
+
),
|
|
26061
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
26062
|
+
className: "min-w-[100px]",
|
|
26063
|
+
"aria-label": t("filterGroup.from")
|
|
26064
|
+
}
|
|
26065
|
+
),
|
|
26066
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "-" }),
|
|
26067
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
26068
|
+
RangeSlider,
|
|
26069
|
+
{
|
|
26070
|
+
min: filter.min ?? 0,
|
|
26071
|
+
max: filter.max ?? 100,
|
|
26072
|
+
step: filter.step ?? 1,
|
|
26073
|
+
value: Number(
|
|
26074
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
26075
|
+
),
|
|
26076
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
26077
|
+
className: "min-w-[100px]",
|
|
26078
|
+
"aria-label": t("filterGroup.to")
|
|
26079
|
+
}
|
|
26080
|
+
)
|
|
25684
26081
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
25685
26082
|
Input,
|
|
25686
26083
|
{
|
|
@@ -25799,6 +26196,38 @@ var init_FilterGroup = __esm({
|
|
|
25799
26196
|
className: "min-w-[130px]"
|
|
25800
26197
|
}
|
|
25801
26198
|
)
|
|
26199
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
26200
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
26201
|
+
RangeSlider,
|
|
26202
|
+
{
|
|
26203
|
+
min: filter.min ?? 0,
|
|
26204
|
+
max: filter.max ?? 100,
|
|
26205
|
+
step: filter.step ?? 1,
|
|
26206
|
+
value: Number(
|
|
26207
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
26208
|
+
),
|
|
26209
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
26210
|
+
showTooltip: true,
|
|
26211
|
+
className: "min-w-[130px]",
|
|
26212
|
+
"aria-label": t("filterGroup.from")
|
|
26213
|
+
}
|
|
26214
|
+
),
|
|
26215
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "-" }),
|
|
26216
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
26217
|
+
RangeSlider,
|
|
26218
|
+
{
|
|
26219
|
+
min: filter.min ?? 0,
|
|
26220
|
+
max: filter.max ?? 100,
|
|
26221
|
+
step: filter.step ?? 1,
|
|
26222
|
+
value: Number(
|
|
26223
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
26224
|
+
),
|
|
26225
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
26226
|
+
showTooltip: true,
|
|
26227
|
+
className: "min-w-[130px]",
|
|
26228
|
+
"aria-label": t("filterGroup.to")
|
|
26229
|
+
}
|
|
26230
|
+
)
|
|
25802
26231
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
25803
26232
|
Input,
|
|
25804
26233
|
{
|
|
@@ -28147,19 +28576,25 @@ var init_MathCanvas = __esm({
|
|
|
28147
28576
|
};
|
|
28148
28577
|
}
|
|
28149
28578
|
});
|
|
28150
|
-
var PhysicsCanvas;
|
|
28579
|
+
var physicsLog2, PhysicsCanvas;
|
|
28151
28580
|
var init_PhysicsCanvas = __esm({
|
|
28152
28581
|
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
28153
28582
|
"use client";
|
|
28154
28583
|
init_atoms();
|
|
28155
28584
|
init_Stack();
|
|
28156
28585
|
init_LearningCanvas();
|
|
28586
|
+
init_learningScene3D();
|
|
28587
|
+
physicsLog2 = logger.createLogger("almadar:ui:physics-canvas");
|
|
28157
28588
|
PhysicsCanvas = ({
|
|
28158
28589
|
className,
|
|
28159
28590
|
width = 600,
|
|
28160
28591
|
height = 400,
|
|
28161
28592
|
title,
|
|
28162
28593
|
backgroundColor,
|
|
28594
|
+
mode = "2d",
|
|
28595
|
+
camera,
|
|
28596
|
+
lighting,
|
|
28597
|
+
post,
|
|
28163
28598
|
bodies = [],
|
|
28164
28599
|
constraints = [],
|
|
28165
28600
|
showVelocity = true,
|
|
@@ -28167,7 +28602,9 @@ var init_PhysicsCanvas = __esm({
|
|
|
28167
28602
|
velocityScale = 20,
|
|
28168
28603
|
forceScale = 20,
|
|
28169
28604
|
shapes = [],
|
|
28170
|
-
|
|
28605
|
+
showGrid,
|
|
28606
|
+
shadows,
|
|
28607
|
+
interactive,
|
|
28171
28608
|
animate = false,
|
|
28172
28609
|
onShapeClick,
|
|
28173
28610
|
isLoading,
|
|
@@ -28239,6 +28676,89 @@ var init_PhysicsCanvas = __esm({
|
|
|
28239
28676
|
out.push(...shapes);
|
|
28240
28677
|
return out;
|
|
28241
28678
|
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
28679
|
+
const drawables3D = React87.useMemo(() => {
|
|
28680
|
+
if (mode !== "3d") return [];
|
|
28681
|
+
if (shapes.length > 0) {
|
|
28682
|
+
physicsLog2.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
28683
|
+
}
|
|
28684
|
+
const out = [];
|
|
28685
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
28686
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
28687
|
+
for (const b of bodies) {
|
|
28688
|
+
if (b.id) bodyById.set(b.id, b);
|
|
28689
|
+
}
|
|
28690
|
+
for (const c of constraints) {
|
|
28691
|
+
const a = bodyById.get(c.from);
|
|
28692
|
+
const b = bodyById.get(c.to);
|
|
28693
|
+
if (!a || !b) continue;
|
|
28694
|
+
const rodRadius = Math.max(0.05, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.15);
|
|
28695
|
+
const rod = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], rodRadius, c.color ?? "#9ca3af");
|
|
28696
|
+
if (rod) out.push(rod);
|
|
28697
|
+
}
|
|
28698
|
+
for (const b of bodies) {
|
|
28699
|
+
const radius = b.radius ?? 0.5;
|
|
28700
|
+
const bz = b.z ?? 0;
|
|
28701
|
+
out.push(meshSphere(b.id, b.x, b.y, bz, radius, b.color ?? "#2563eb"));
|
|
28702
|
+
if (b.label) {
|
|
28703
|
+
out.push(billboardLabel(b.label, b.x, b.y, bz + radius, { color: labelColor }));
|
|
28704
|
+
}
|
|
28705
|
+
const vx = b.vx ?? 0;
|
|
28706
|
+
const vy = b.vy ?? 0;
|
|
28707
|
+
const vz = b.vz ?? 0;
|
|
28708
|
+
const arrowRadius = Math.max(0.05, radius * 0.15);
|
|
28709
|
+
if (showVelocity && (vx !== 0 || vy !== 0 || vz !== 0)) {
|
|
28710
|
+
const arrow = arrowBetween(
|
|
28711
|
+
[b.x, b.y, bz],
|
|
28712
|
+
[b.x + vx * velocityScale, b.y + vy * velocityScale, bz + vz * velocityScale],
|
|
28713
|
+
"#16a34a",
|
|
28714
|
+
arrowRadius
|
|
28715
|
+
);
|
|
28716
|
+
if (arrow) out.push(arrow);
|
|
28717
|
+
}
|
|
28718
|
+
const fx = b.fx ?? 0;
|
|
28719
|
+
const fy = b.fy ?? 0;
|
|
28720
|
+
const fz = b.fz ?? 0;
|
|
28721
|
+
if (showForces && (fx !== 0 || fy !== 0 || fz !== 0)) {
|
|
28722
|
+
const arrow = arrowBetween(
|
|
28723
|
+
[b.x, b.y, bz],
|
|
28724
|
+
[b.x + fx * forceScale, b.y + fy * forceScale, bz + fz * forceScale],
|
|
28725
|
+
"#dc2626",
|
|
28726
|
+
arrowRadius
|
|
28727
|
+
);
|
|
28728
|
+
if (arrow) out.push(arrow);
|
|
28729
|
+
}
|
|
28730
|
+
}
|
|
28731
|
+
return out;
|
|
28732
|
+
}, [mode, bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes, backgroundColor]);
|
|
28733
|
+
const bodyIndexById = React87.useMemo(() => {
|
|
28734
|
+
const m = /* @__PURE__ */ new Map();
|
|
28735
|
+
bodies.forEach((b, i) => {
|
|
28736
|
+
if (b.id) m.set(b.id, i);
|
|
28737
|
+
});
|
|
28738
|
+
return m;
|
|
28739
|
+
}, [bodies]);
|
|
28740
|
+
if (mode === "3d") {
|
|
28741
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
28742
|
+
LearningScene3D,
|
|
28743
|
+
{
|
|
28744
|
+
className,
|
|
28745
|
+
width,
|
|
28746
|
+
height,
|
|
28747
|
+
title,
|
|
28748
|
+
backgroundColor,
|
|
28749
|
+
drawables: drawables3D,
|
|
28750
|
+
camera,
|
|
28751
|
+
lighting,
|
|
28752
|
+
post,
|
|
28753
|
+
showGrid,
|
|
28754
|
+
shadows,
|
|
28755
|
+
interactive,
|
|
28756
|
+
isLoading,
|
|
28757
|
+
error,
|
|
28758
|
+
onItemClick: get3DClickPayload(onShapeClick, bodyIndexById)
|
|
28759
|
+
}
|
|
28760
|
+
);
|
|
28761
|
+
}
|
|
28242
28762
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
28243
28763
|
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
28244
28764
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -28248,7 +28768,7 @@ var init_PhysicsCanvas = __esm({
|
|
|
28248
28768
|
height,
|
|
28249
28769
|
backgroundColor,
|
|
28250
28770
|
shapes: derivedShapes,
|
|
28251
|
-
interactive,
|
|
28771
|
+
interactive: interactive ?? false,
|
|
28252
28772
|
animate,
|
|
28253
28773
|
onShapeClick,
|
|
28254
28774
|
isLoading,
|
|
@@ -28866,12 +29386,12 @@ var init_MapView = __esm({
|
|
|
28866
29386
|
shadowSize: [41, 41]
|
|
28867
29387
|
});
|
|
28868
29388
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
28869
|
-
const { useEffect: useEffect63, useRef:
|
|
29389
|
+
const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback96, useState: useState94 } = React87__namespace.default;
|
|
28870
29390
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
28871
29391
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
28872
29392
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
28873
29393
|
const map = useMap();
|
|
28874
|
-
const prevRef =
|
|
29394
|
+
const prevRef = useRef63({ centerLat, centerLng, zoom });
|
|
28875
29395
|
useEffect63(() => {
|
|
28876
29396
|
const prev = prevRef.current;
|
|
28877
29397
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
@@ -39694,7 +40214,7 @@ function getEnumOptions(field) {
|
|
|
39694
40214
|
}));
|
|
39695
40215
|
}
|
|
39696
40216
|
const validation = field.validation;
|
|
39697
|
-
if (validation?.enum &&
|
|
40217
|
+
if (validation?.enum && validation.enum.length > 0) {
|
|
39698
40218
|
return validation.enum.map((v) => ({
|
|
39699
40219
|
value: v,
|
|
39700
40220
|
label: v.charAt(0).toUpperCase() + v.slice(1).replace(/_/g, " ")
|