@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/avl/index.js
CHANGED
|
@@ -14041,9 +14041,10 @@ function Canvas({
|
|
|
14041
14041
|
drawables: [...drawables ?? [], ...childDrawables],
|
|
14042
14042
|
isLoading,
|
|
14043
14043
|
cameraMode: to3DCameraMode(camera?.mode),
|
|
14044
|
-
...zoom !== void 0 ? {
|
|
14044
|
+
...zoom !== void 0 ? { zoom } : {},
|
|
14045
14045
|
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
14046
14046
|
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
14047
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
14047
14048
|
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
14048
14049
|
unitScale,
|
|
14049
14050
|
backgroundColor,
|
|
@@ -15252,7 +15253,10 @@ var init_LearningCanvas = __esm({
|
|
|
15252
15253
|
ctx.fillRect(0, 0, width, height);
|
|
15253
15254
|
}
|
|
15254
15255
|
for (const shape of shapes) {
|
|
15255
|
-
drawShape(ctx, shape, width, height);
|
|
15256
|
+
if (shape.type !== "text") drawShape(ctx, shape, width, height);
|
|
15257
|
+
}
|
|
15258
|
+
for (const shape of shapes) {
|
|
15259
|
+
if (shape.type === "text") drawShape(ctx, shape, width, height);
|
|
15256
15260
|
}
|
|
15257
15261
|
}, [width, height, backgroundColor, shapes]);
|
|
15258
15262
|
useEffect(() => {
|
|
@@ -16815,7 +16819,7 @@ var init_AlgorithmCanvas = __esm({
|
|
|
16815
16819
|
DEFAULT_CELL_COLOR = "#e5e7eb";
|
|
16816
16820
|
DEFAULT_POINTER_COLOR = "#dc2626";
|
|
16817
16821
|
POINTER_BAND = 34;
|
|
16818
|
-
TOP_PAD =
|
|
16822
|
+
TOP_PAD = 26;
|
|
16819
16823
|
AlgorithmCanvas = ({
|
|
16820
16824
|
className,
|
|
16821
16825
|
width = 600,
|
|
@@ -17092,23 +17096,188 @@ var init_AuthLayout = __esm({
|
|
|
17092
17096
|
AuthLayout.displayName = "AuthLayout";
|
|
17093
17097
|
}
|
|
17094
17098
|
});
|
|
17095
|
-
|
|
17099
|
+
function LearningScene3D({
|
|
17100
|
+
className,
|
|
17101
|
+
width = 600,
|
|
17102
|
+
height = 400,
|
|
17103
|
+
title,
|
|
17104
|
+
backgroundColor,
|
|
17105
|
+
drawables,
|
|
17106
|
+
camera,
|
|
17107
|
+
lighting,
|
|
17108
|
+
post,
|
|
17109
|
+
showGrid = false,
|
|
17110
|
+
shadows,
|
|
17111
|
+
interactive,
|
|
17112
|
+
isLoading,
|
|
17113
|
+
error,
|
|
17114
|
+
onItemClick
|
|
17115
|
+
}) {
|
|
17116
|
+
const instanceId = useId().replace(/[^a-zA-Z0-9]/g, "");
|
|
17117
|
+
const clickEvent = onItemClick ? `LEARNING_SCENE_3D.ITEM_CLICK.${instanceId}` : void 0;
|
|
17118
|
+
const onItemClickRef = useRef(onItemClick);
|
|
17119
|
+
onItemClickRef.current = onItemClick;
|
|
17120
|
+
useEventListener(`UI:${clickEvent ?? "LEARNING_SCENE_3D.ITEM_CLICK.__disabled"}`, (event) => {
|
|
17121
|
+
const unitId = event.payload?.unitId;
|
|
17122
|
+
if (typeof unitId === "string") onItemClickRef.current?.(unitId);
|
|
17123
|
+
});
|
|
17124
|
+
const props3d = {
|
|
17125
|
+
drawables,
|
|
17126
|
+
isLoading,
|
|
17127
|
+
error: error ? error.message : null,
|
|
17128
|
+
cameraMode: camera?.mode ?? "perspective",
|
|
17129
|
+
...camera?.zoom !== void 0 ? { zoom: camera.zoom } : {},
|
|
17130
|
+
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
17131
|
+
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
17132
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
17133
|
+
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
17134
|
+
backgroundColor,
|
|
17135
|
+
showGrid,
|
|
17136
|
+
...shadows !== void 0 ? { shadows } : {},
|
|
17137
|
+
...interactive !== void 0 ? { controlsEnabled: interactive } : {},
|
|
17138
|
+
lighting,
|
|
17139
|
+
post,
|
|
17140
|
+
...clickEvent !== void 0 ? { unitClickEvent: clickEvent } : {}
|
|
17141
|
+
};
|
|
17142
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
17143
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
17144
|
+
/* @__PURE__ */ jsx("div", { style: { width, height, display: "flex" }, children: /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(Canvas3DHost2, { ...props3d }) }) })
|
|
17145
|
+
] }) });
|
|
17146
|
+
}
|
|
17147
|
+
function meshSphere(id, x, y, z, radius, color, opts) {
|
|
17148
|
+
return {
|
|
17149
|
+
type: "draw-mesh",
|
|
17150
|
+
...id !== void 0 ? { id } : {},
|
|
17151
|
+
shape: opts?.shape ?? "sphere",
|
|
17152
|
+
position: { x, y, z },
|
|
17153
|
+
radius,
|
|
17154
|
+
pivot: "center",
|
|
17155
|
+
segments: opts?.segments ?? 24,
|
|
17156
|
+
material: { color, ...opts?.material },
|
|
17157
|
+
...opts?.opacity !== void 0 ? { opacity: opts.opacity } : {}
|
|
17158
|
+
};
|
|
17159
|
+
}
|
|
17160
|
+
function axisRotation(from, to) {
|
|
17161
|
+
const wx = to[0] - from[0];
|
|
17162
|
+
const wy = to[2] - from[2];
|
|
17163
|
+
const wz = to[1] - from[1];
|
|
17164
|
+
const len = Math.sqrt(wx * wx + wy * wy + wz * wz);
|
|
17165
|
+
const tilt = Math.acos(Math.min(1, Math.max(-1, len > 0 ? wy / len : 1)));
|
|
17166
|
+
return [0, Math.atan2(wz, -wx), tilt];
|
|
17167
|
+
}
|
|
17168
|
+
function segmentLength(from, to) {
|
|
17169
|
+
const dx = to[0] - from[0];
|
|
17170
|
+
const dy = to[1] - from[1];
|
|
17171
|
+
const dz = to[2] - from[2];
|
|
17172
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
17173
|
+
}
|
|
17174
|
+
function cylinderBetween(from, to, radius, color) {
|
|
17175
|
+
const len = segmentLength(from, to);
|
|
17176
|
+
if (len < 1e-6) return null;
|
|
17177
|
+
return {
|
|
17178
|
+
type: "draw-mesh",
|
|
17179
|
+
shape: "cylinder",
|
|
17180
|
+
position: { x: (from[0] + to[0]) / 2, y: (from[1] + to[1]) / 2, z: (from[2] + to[2]) / 2 },
|
|
17181
|
+
radius,
|
|
17182
|
+
height: len,
|
|
17183
|
+
rotation: axisRotation(from, to),
|
|
17184
|
+
pivot: "center",
|
|
17185
|
+
segments: 12,
|
|
17186
|
+
material: { color }
|
|
17187
|
+
};
|
|
17188
|
+
}
|
|
17189
|
+
function arrowBetween(from, to, color, shaftRadius = 0.08) {
|
|
17190
|
+
const len = segmentLength(from, to);
|
|
17191
|
+
if (len < 1e-6) return null;
|
|
17192
|
+
const tipLen = Math.min(shaftRadius * 8, len * 0.35);
|
|
17193
|
+
const k = (len - tipLen) / len;
|
|
17194
|
+
const delta = [to[0] - from[0], to[1] - from[1], to[2] - from[2]];
|
|
17195
|
+
const shaftEnd = [delta[0] * k, delta[1] * k, delta[2] * k];
|
|
17196
|
+
const tipStart = shaftEnd;
|
|
17197
|
+
const tipEnd = delta;
|
|
17198
|
+
const shaft = cylinderBetween([0, 0, 0], shaftEnd, shaftRadius, color);
|
|
17199
|
+
const tipLenActual = segmentLength(tipStart, tipEnd);
|
|
17200
|
+
const tip = {
|
|
17201
|
+
type: "draw-mesh",
|
|
17202
|
+
shape: "cone",
|
|
17203
|
+
position: {
|
|
17204
|
+
x: (tipStart[0] + tipEnd[0]) / 2,
|
|
17205
|
+
y: (tipStart[1] + tipEnd[1]) / 2,
|
|
17206
|
+
z: (tipStart[2] + tipEnd[2]) / 2
|
|
17207
|
+
},
|
|
17208
|
+
radius: shaftRadius * 3,
|
|
17209
|
+
height: tipLenActual,
|
|
17210
|
+
rotation: axisRotation(tipStart, tipEnd),
|
|
17211
|
+
pivot: "center",
|
|
17212
|
+
segments: 12,
|
|
17213
|
+
material: { color }
|
|
17214
|
+
};
|
|
17215
|
+
return {
|
|
17216
|
+
type: "draw-group",
|
|
17217
|
+
position: { x: from[0], y: from[1], z: from[2] },
|
|
17218
|
+
items: tipLenActual < 1e-6 ? shaft ? [shaft] : [] : shaft ? [shaft, tip] : [tip]
|
|
17219
|
+
};
|
|
17220
|
+
}
|
|
17221
|
+
function billboardLabel(text, x, y, z, opts) {
|
|
17222
|
+
return {
|
|
17223
|
+
type: "draw-text",
|
|
17224
|
+
text,
|
|
17225
|
+
position: { x, y, z },
|
|
17226
|
+
color: opts?.color ?? "#111827"
|
|
17227
|
+
};
|
|
17228
|
+
}
|
|
17229
|
+
function labelColorForBackground(backgroundColor) {
|
|
17230
|
+
const m = /^#(?:([0-9a-f]{3})|([0-9a-f]{6}))$/i.exec(backgroundColor ?? "");
|
|
17231
|
+
if (!m) return "#111827";
|
|
17232
|
+
const hex = m[1] !== void 0 ? m[1].split("").map((c) => c + c).join("") : m[2];
|
|
17233
|
+
const r2 = parseInt(hex.slice(0, 2), 16) / 255;
|
|
17234
|
+
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
17235
|
+
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
17236
|
+
const luminance = 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
|
|
17237
|
+
return luminance < 0.5 ? "#e5e7eb" : "#111827";
|
|
17238
|
+
}
|
|
17239
|
+
function get3DClickPayload(onShapeClick, idToIndex) {
|
|
17240
|
+
if (!onShapeClick) return void 0;
|
|
17241
|
+
return (id) => onShapeClick({ id, index: idToIndex.get(id) ?? -1 });
|
|
17242
|
+
}
|
|
17243
|
+
var Canvas3DHost2;
|
|
17244
|
+
var init_learningScene3D = __esm({
|
|
17245
|
+
"components/learning/molecules/learningScene3D.tsx"() {
|
|
17246
|
+
"use client";
|
|
17247
|
+
init_atoms();
|
|
17248
|
+
init_Stack();
|
|
17249
|
+
init_useEventBus();
|
|
17250
|
+
Canvas3DHost2 = lazy(
|
|
17251
|
+
() => import('@almadar/ui/components/molecules/game/three').then((m) => ({ default: m.Canvas3DHost }))
|
|
17252
|
+
);
|
|
17253
|
+
LearningScene3D.displayName = "LearningScene3D";
|
|
17254
|
+
}
|
|
17255
|
+
});
|
|
17256
|
+
var biologyLog, BiologyCanvas;
|
|
17096
17257
|
var init_BiologyCanvas = __esm({
|
|
17097
17258
|
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
17098
17259
|
"use client";
|
|
17099
17260
|
init_atoms();
|
|
17100
17261
|
init_Stack();
|
|
17101
17262
|
init_LearningCanvas();
|
|
17263
|
+
init_learningScene3D();
|
|
17264
|
+
biologyLog = createLogger("almadar:ui:biology-canvas");
|
|
17102
17265
|
BiologyCanvas = ({
|
|
17103
17266
|
className,
|
|
17104
17267
|
width = 600,
|
|
17105
17268
|
height = 400,
|
|
17106
17269
|
title,
|
|
17107
17270
|
backgroundColor,
|
|
17271
|
+
mode = "2d",
|
|
17272
|
+
camera,
|
|
17273
|
+
lighting,
|
|
17274
|
+
post,
|
|
17108
17275
|
nodes = [],
|
|
17109
17276
|
edges = [],
|
|
17110
17277
|
shapes = [],
|
|
17111
|
-
|
|
17278
|
+
showGrid,
|
|
17279
|
+
shadows,
|
|
17280
|
+
interactive,
|
|
17112
17281
|
animate = false,
|
|
17113
17282
|
onShapeClick,
|
|
17114
17283
|
isLoading,
|
|
@@ -17169,6 +17338,75 @@ var init_BiologyCanvas = __esm({
|
|
|
17169
17338
|
out.push(...shapes);
|
|
17170
17339
|
return out;
|
|
17171
17340
|
}, [nodes, edges, shapes]);
|
|
17341
|
+
const drawables3D = useMemo(() => {
|
|
17342
|
+
if (mode !== "3d") return [];
|
|
17343
|
+
if (shapes.length > 0) {
|
|
17344
|
+
biologyLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
17345
|
+
}
|
|
17346
|
+
const out = [];
|
|
17347
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
17348
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
17349
|
+
for (const n of nodes) {
|
|
17350
|
+
if (n.id) nodeById.set(n.id, n);
|
|
17351
|
+
}
|
|
17352
|
+
for (const e of edges) {
|
|
17353
|
+
const a = nodeById.get(e.from);
|
|
17354
|
+
const b = nodeById.get(e.to);
|
|
17355
|
+
if (!a || !b) continue;
|
|
17356
|
+
const edgeRadius = Math.max(0.04, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.12);
|
|
17357
|
+
const edge = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], edgeRadius, e.color ?? "#9ca3af");
|
|
17358
|
+
if (edge) out.push(edge);
|
|
17359
|
+
if (e.label) {
|
|
17360
|
+
out.push(
|
|
17361
|
+
billboardLabel(
|
|
17362
|
+
e.label,
|
|
17363
|
+
(a.x + b.x) / 2,
|
|
17364
|
+
(a.y + b.y) / 2,
|
|
17365
|
+
((a.z ?? 0) + (b.z ?? 0)) / 2,
|
|
17366
|
+
{ color: labelColor }
|
|
17367
|
+
)
|
|
17368
|
+
);
|
|
17369
|
+
}
|
|
17370
|
+
}
|
|
17371
|
+
for (const n of nodes) {
|
|
17372
|
+
const radius = n.radius ?? 0.5;
|
|
17373
|
+
const nz = n.z ?? 0;
|
|
17374
|
+
out.push(meshSphere(n.id, n.x, n.y, nz, radius, n.color ?? "#16a34a", { shape: n.shape, ...n.opacity !== void 0 ? { opacity: n.opacity } : {} }));
|
|
17375
|
+
if (n.label) {
|
|
17376
|
+
out.push(billboardLabel(n.label, n.x, n.y, nz + radius, { color: labelColor }));
|
|
17377
|
+
}
|
|
17378
|
+
}
|
|
17379
|
+
return out;
|
|
17380
|
+
}, [mode, nodes, edges, shapes, backgroundColor]);
|
|
17381
|
+
const nodeIndexById = useMemo(() => {
|
|
17382
|
+
const m = /* @__PURE__ */ new Map();
|
|
17383
|
+
nodes.forEach((n, i) => {
|
|
17384
|
+
if (n.id) m.set(n.id, i);
|
|
17385
|
+
});
|
|
17386
|
+
return m;
|
|
17387
|
+
}, [nodes]);
|
|
17388
|
+
if (mode === "3d") {
|
|
17389
|
+
return /* @__PURE__ */ jsx(
|
|
17390
|
+
LearningScene3D,
|
|
17391
|
+
{
|
|
17392
|
+
className,
|
|
17393
|
+
width,
|
|
17394
|
+
height,
|
|
17395
|
+
title,
|
|
17396
|
+
backgroundColor,
|
|
17397
|
+
drawables: drawables3D,
|
|
17398
|
+
camera,
|
|
17399
|
+
lighting,
|
|
17400
|
+
post,
|
|
17401
|
+
showGrid,
|
|
17402
|
+
shadows,
|
|
17403
|
+
interactive,
|
|
17404
|
+
isLoading,
|
|
17405
|
+
error,
|
|
17406
|
+
onItemClick: get3DClickPayload(onShapeClick, nodeIndexById)
|
|
17407
|
+
}
|
|
17408
|
+
);
|
|
17409
|
+
}
|
|
17172
17410
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
17173
17411
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
17174
17412
|
/* @__PURE__ */ jsx(
|
|
@@ -17178,7 +17416,7 @@ var init_BiologyCanvas = __esm({
|
|
|
17178
17416
|
height,
|
|
17179
17417
|
backgroundColor,
|
|
17180
17418
|
shapes: derivedShapes,
|
|
17181
|
-
interactive,
|
|
17419
|
+
interactive: interactive ?? false,
|
|
17182
17420
|
animate,
|
|
17183
17421
|
onShapeClick,
|
|
17184
17422
|
isLoading,
|
|
@@ -24018,24 +24256,41 @@ var init_ChatBar = __esm({
|
|
|
24018
24256
|
ChatBar.displayName = "ChatBar";
|
|
24019
24257
|
}
|
|
24020
24258
|
});
|
|
24021
|
-
|
|
24259
|
+
function bondPerpendicular(a, b) {
|
|
24260
|
+
const dx = b[0] - a[0];
|
|
24261
|
+
const dy = b[1] - a[1];
|
|
24262
|
+
const px = dy;
|
|
24263
|
+
const py = -dx;
|
|
24264
|
+
const len = Math.sqrt(px * px + py * py);
|
|
24265
|
+
if (len < 1e-6) return [1, 0, 0];
|
|
24266
|
+
return [px / len, py / len, 0];
|
|
24267
|
+
}
|
|
24268
|
+
var chemistryLog, ChemistryCanvas;
|
|
24022
24269
|
var init_ChemistryCanvas = __esm({
|
|
24023
24270
|
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
24024
24271
|
"use client";
|
|
24025
24272
|
init_atoms();
|
|
24026
24273
|
init_Stack();
|
|
24027
24274
|
init_LearningCanvas();
|
|
24275
|
+
init_learningScene3D();
|
|
24276
|
+
chemistryLog = createLogger("almadar:ui:chemistry-canvas");
|
|
24028
24277
|
ChemistryCanvas = ({
|
|
24029
24278
|
className,
|
|
24030
24279
|
width = 600,
|
|
24031
24280
|
height = 400,
|
|
24032
24281
|
title,
|
|
24033
24282
|
backgroundColor,
|
|
24283
|
+
mode = "2d",
|
|
24284
|
+
camera,
|
|
24285
|
+
lighting,
|
|
24286
|
+
post,
|
|
24034
24287
|
atoms = [],
|
|
24035
24288
|
bonds = [],
|
|
24036
24289
|
arrows = [],
|
|
24037
24290
|
shapes = [],
|
|
24038
|
-
|
|
24291
|
+
showGrid,
|
|
24292
|
+
shadows,
|
|
24293
|
+
interactive,
|
|
24039
24294
|
animate = false,
|
|
24040
24295
|
onShapeClick,
|
|
24041
24296
|
isLoading,
|
|
@@ -24114,6 +24369,88 @@ var init_ChemistryCanvas = __esm({
|
|
|
24114
24369
|
out.push(...shapes);
|
|
24115
24370
|
return out;
|
|
24116
24371
|
}, [atoms, bonds, arrows, shapes]);
|
|
24372
|
+
const drawables3D = useMemo(() => {
|
|
24373
|
+
if (mode !== "3d") return [];
|
|
24374
|
+
if (shapes.length > 0) {
|
|
24375
|
+
chemistryLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
24376
|
+
}
|
|
24377
|
+
const out = [];
|
|
24378
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
24379
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
24380
|
+
for (const a of atoms) {
|
|
24381
|
+
if (a.id) atomById.set(a.id, a);
|
|
24382
|
+
}
|
|
24383
|
+
for (const b of bonds) {
|
|
24384
|
+
const a = atomById.get(b.from);
|
|
24385
|
+
const c = atomById.get(b.to);
|
|
24386
|
+
if (!a || !c) continue;
|
|
24387
|
+
const color = b.color ?? "#6b7280";
|
|
24388
|
+
const from = [a.x, a.y, a.z ?? 0];
|
|
24389
|
+
const to = [c.x, c.y, c.z ?? 0];
|
|
24390
|
+
const perp = bondPerpendicular(from, to);
|
|
24391
|
+
const bondRadius = Math.max(0.06, Math.min(a.radius ?? 0.45, c.radius ?? 0.45) * 0.22);
|
|
24392
|
+
const step = bondRadius * 2.2;
|
|
24393
|
+
const offsets = b.type === "double" ? [-step, step] : b.type === "triple" ? [-step, 0, step] : [0];
|
|
24394
|
+
for (const off of offsets) {
|
|
24395
|
+
const bond = cylinderBetween(
|
|
24396
|
+
[from[0] + perp[0] * off, from[1] + perp[1] * off, from[2] + perp[2] * off],
|
|
24397
|
+
[to[0] + perp[0] * off, to[1] + perp[1] * off, to[2] + perp[2] * off],
|
|
24398
|
+
bondRadius,
|
|
24399
|
+
color
|
|
24400
|
+
);
|
|
24401
|
+
if (bond) out.push(bond);
|
|
24402
|
+
}
|
|
24403
|
+
}
|
|
24404
|
+
for (const a of arrows) {
|
|
24405
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
24406
|
+
const len = a.length ?? 60;
|
|
24407
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
24408
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
24409
|
+
const arrow = arrowBetween([a.x, a.y, 0], [x2, y2, 0], a.color ?? "#dc2626");
|
|
24410
|
+
if (arrow) out.push(arrow);
|
|
24411
|
+
if (a.label) {
|
|
24412
|
+
out.push(billboardLabel(a.label, (a.x + x2) / 2, (a.y + y2) / 2, 0, { color: labelColor }));
|
|
24413
|
+
}
|
|
24414
|
+
}
|
|
24415
|
+
for (const a of atoms) {
|
|
24416
|
+
const radius = a.radius ?? 0.45;
|
|
24417
|
+
const az = a.z ?? 0;
|
|
24418
|
+
out.push(meshSphere(a.id, a.x, a.y, az, radius, a.color ?? "#2563eb"));
|
|
24419
|
+
if (a.element) {
|
|
24420
|
+
out.push(billboardLabel(a.element, a.x, a.y, az + radius, { color: labelColor }));
|
|
24421
|
+
}
|
|
24422
|
+
}
|
|
24423
|
+
return out;
|
|
24424
|
+
}, [mode, atoms, bonds, arrows, shapes, backgroundColor]);
|
|
24425
|
+
const atomIndexById = useMemo(() => {
|
|
24426
|
+
const m = /* @__PURE__ */ new Map();
|
|
24427
|
+
atoms.forEach((a, i) => {
|
|
24428
|
+
if (a.id) m.set(a.id, i);
|
|
24429
|
+
});
|
|
24430
|
+
return m;
|
|
24431
|
+
}, [atoms]);
|
|
24432
|
+
if (mode === "3d") {
|
|
24433
|
+
return /* @__PURE__ */ jsx(
|
|
24434
|
+
LearningScene3D,
|
|
24435
|
+
{
|
|
24436
|
+
className,
|
|
24437
|
+
width,
|
|
24438
|
+
height,
|
|
24439
|
+
title,
|
|
24440
|
+
backgroundColor,
|
|
24441
|
+
drawables: drawables3D,
|
|
24442
|
+
camera,
|
|
24443
|
+
lighting,
|
|
24444
|
+
post,
|
|
24445
|
+
showGrid,
|
|
24446
|
+
shadows,
|
|
24447
|
+
interactive,
|
|
24448
|
+
isLoading,
|
|
24449
|
+
error,
|
|
24450
|
+
onItemClick: get3DClickPayload(onShapeClick, atomIndexById)
|
|
24451
|
+
}
|
|
24452
|
+
);
|
|
24453
|
+
}
|
|
24117
24454
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
24118
24455
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
24119
24456
|
/* @__PURE__ */ jsx(
|
|
@@ -24123,7 +24460,7 @@ var init_ChemistryCanvas = __esm({
|
|
|
24123
24460
|
height,
|
|
24124
24461
|
backgroundColor,
|
|
24125
24462
|
shapes: derivedShapes,
|
|
24126
|
-
interactive,
|
|
24463
|
+
interactive: interactive ?? false,
|
|
24127
24464
|
animate,
|
|
24128
24465
|
onShapeClick,
|
|
24129
24466
|
isLoading,
|
|
@@ -27594,6 +27931,7 @@ var init_FilterGroup = __esm({
|
|
|
27594
27931
|
init_Badge();
|
|
27595
27932
|
init_Stack();
|
|
27596
27933
|
init_Icon();
|
|
27934
|
+
init_RangeSlider();
|
|
27597
27935
|
init_useEventBus();
|
|
27598
27936
|
init_useQuerySingleton();
|
|
27599
27937
|
resolveFilterType = (filter) => filter.filterType ?? filter.type;
|
|
@@ -27788,6 +28126,35 @@ var init_FilterGroup = __esm({
|
|
|
27788
28126
|
onClear: () => handleFilterSelect(`${filter.field}_to`, null)
|
|
27789
28127
|
}
|
|
27790
28128
|
)
|
|
28129
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
28130
|
+
/* @__PURE__ */ jsx(
|
|
28131
|
+
RangeSlider,
|
|
28132
|
+
{
|
|
28133
|
+
min: filter.min ?? 0,
|
|
28134
|
+
max: filter.max ?? 100,
|
|
28135
|
+
step: filter.step ?? 1,
|
|
28136
|
+
value: Number(
|
|
28137
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
28138
|
+
),
|
|
28139
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
28140
|
+
showTooltip: true,
|
|
28141
|
+
"aria-label": t("filterGroup.from")
|
|
28142
|
+
}
|
|
28143
|
+
),
|
|
28144
|
+
/* @__PURE__ */ jsx(
|
|
28145
|
+
RangeSlider,
|
|
28146
|
+
{
|
|
28147
|
+
min: filter.min ?? 0,
|
|
28148
|
+
max: filter.max ?? 100,
|
|
28149
|
+
step: filter.step ?? 1,
|
|
28150
|
+
value: Number(
|
|
28151
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
28152
|
+
),
|
|
28153
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
28154
|
+
showTooltip: true,
|
|
28155
|
+
"aria-label": t("filterGroup.to")
|
|
28156
|
+
}
|
|
28157
|
+
)
|
|
27791
28158
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsx(
|
|
27792
28159
|
Input,
|
|
27793
28160
|
{
|
|
@@ -27870,6 +28237,36 @@ var init_FilterGroup = __esm({
|
|
|
27870
28237
|
className: "text-sm min-w-[100px]"
|
|
27871
28238
|
}
|
|
27872
28239
|
)
|
|
28240
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
28241
|
+
/* @__PURE__ */ jsx(
|
|
28242
|
+
RangeSlider,
|
|
28243
|
+
{
|
|
28244
|
+
min: filter.min ?? 0,
|
|
28245
|
+
max: filter.max ?? 100,
|
|
28246
|
+
step: filter.step ?? 1,
|
|
28247
|
+
value: Number(
|
|
28248
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
28249
|
+
),
|
|
28250
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
28251
|
+
className: "min-w-[100px]",
|
|
28252
|
+
"aria-label": t("filterGroup.from")
|
|
28253
|
+
}
|
|
28254
|
+
),
|
|
28255
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "-" }),
|
|
28256
|
+
/* @__PURE__ */ jsx(
|
|
28257
|
+
RangeSlider,
|
|
28258
|
+
{
|
|
28259
|
+
min: filter.min ?? 0,
|
|
28260
|
+
max: filter.max ?? 100,
|
|
28261
|
+
step: filter.step ?? 1,
|
|
28262
|
+
value: Number(
|
|
28263
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
28264
|
+
),
|
|
28265
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
28266
|
+
className: "min-w-[100px]",
|
|
28267
|
+
"aria-label": t("filterGroup.to")
|
|
28268
|
+
}
|
|
28269
|
+
)
|
|
27873
28270
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsx(
|
|
27874
28271
|
Input,
|
|
27875
28272
|
{
|
|
@@ -27988,6 +28385,38 @@ var init_FilterGroup = __esm({
|
|
|
27988
28385
|
className: "min-w-[130px]"
|
|
27989
28386
|
}
|
|
27990
28387
|
)
|
|
28388
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
28389
|
+
/* @__PURE__ */ jsx(
|
|
28390
|
+
RangeSlider,
|
|
28391
|
+
{
|
|
28392
|
+
min: filter.min ?? 0,
|
|
28393
|
+
max: filter.max ?? 100,
|
|
28394
|
+
step: filter.step ?? 1,
|
|
28395
|
+
value: Number(
|
|
28396
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
28397
|
+
),
|
|
28398
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
28399
|
+
showTooltip: true,
|
|
28400
|
+
className: "min-w-[130px]",
|
|
28401
|
+
"aria-label": t("filterGroup.from")
|
|
28402
|
+
}
|
|
28403
|
+
),
|
|
28404
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "-" }),
|
|
28405
|
+
/* @__PURE__ */ jsx(
|
|
28406
|
+
RangeSlider,
|
|
28407
|
+
{
|
|
28408
|
+
min: filter.min ?? 0,
|
|
28409
|
+
max: filter.max ?? 100,
|
|
28410
|
+
step: filter.step ?? 1,
|
|
28411
|
+
value: Number(
|
|
28412
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
28413
|
+
),
|
|
28414
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
28415
|
+
showTooltip: true,
|
|
28416
|
+
className: "min-w-[130px]",
|
|
28417
|
+
"aria-label": t("filterGroup.to")
|
|
28418
|
+
}
|
|
28419
|
+
)
|
|
27991
28420
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsx(
|
|
27992
28421
|
Input,
|
|
27993
28422
|
{
|
|
@@ -30336,19 +30765,25 @@ var init_MathCanvas = __esm({
|
|
|
30336
30765
|
};
|
|
30337
30766
|
}
|
|
30338
30767
|
});
|
|
30339
|
-
var PhysicsCanvas;
|
|
30768
|
+
var physicsLog2, PhysicsCanvas;
|
|
30340
30769
|
var init_PhysicsCanvas = __esm({
|
|
30341
30770
|
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
30342
30771
|
"use client";
|
|
30343
30772
|
init_atoms();
|
|
30344
30773
|
init_Stack();
|
|
30345
30774
|
init_LearningCanvas();
|
|
30775
|
+
init_learningScene3D();
|
|
30776
|
+
physicsLog2 = createLogger("almadar:ui:physics-canvas");
|
|
30346
30777
|
PhysicsCanvas = ({
|
|
30347
30778
|
className,
|
|
30348
30779
|
width = 600,
|
|
30349
30780
|
height = 400,
|
|
30350
30781
|
title,
|
|
30351
30782
|
backgroundColor,
|
|
30783
|
+
mode = "2d",
|
|
30784
|
+
camera,
|
|
30785
|
+
lighting,
|
|
30786
|
+
post,
|
|
30352
30787
|
bodies = [],
|
|
30353
30788
|
constraints = [],
|
|
30354
30789
|
showVelocity = true,
|
|
@@ -30356,7 +30791,9 @@ var init_PhysicsCanvas = __esm({
|
|
|
30356
30791
|
velocityScale = 20,
|
|
30357
30792
|
forceScale = 20,
|
|
30358
30793
|
shapes = [],
|
|
30359
|
-
|
|
30794
|
+
showGrid,
|
|
30795
|
+
shadows,
|
|
30796
|
+
interactive,
|
|
30360
30797
|
animate = false,
|
|
30361
30798
|
onShapeClick,
|
|
30362
30799
|
isLoading,
|
|
@@ -30428,6 +30865,89 @@ var init_PhysicsCanvas = __esm({
|
|
|
30428
30865
|
out.push(...shapes);
|
|
30429
30866
|
return out;
|
|
30430
30867
|
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
30868
|
+
const drawables3D = useMemo(() => {
|
|
30869
|
+
if (mode !== "3d") return [];
|
|
30870
|
+
if (shapes.length > 0) {
|
|
30871
|
+
physicsLog2.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
30872
|
+
}
|
|
30873
|
+
const out = [];
|
|
30874
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
30875
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
30876
|
+
for (const b of bodies) {
|
|
30877
|
+
if (b.id) bodyById.set(b.id, b);
|
|
30878
|
+
}
|
|
30879
|
+
for (const c of constraints) {
|
|
30880
|
+
const a = bodyById.get(c.from);
|
|
30881
|
+
const b = bodyById.get(c.to);
|
|
30882
|
+
if (!a || !b) continue;
|
|
30883
|
+
const rodRadius = Math.max(0.05, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.15);
|
|
30884
|
+
const rod = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], rodRadius, c.color ?? "#9ca3af");
|
|
30885
|
+
if (rod) out.push(rod);
|
|
30886
|
+
}
|
|
30887
|
+
for (const b of bodies) {
|
|
30888
|
+
const radius = b.radius ?? 0.5;
|
|
30889
|
+
const bz = b.z ?? 0;
|
|
30890
|
+
out.push(meshSphere(b.id, b.x, b.y, bz, radius, b.color ?? "#2563eb"));
|
|
30891
|
+
if (b.label) {
|
|
30892
|
+
out.push(billboardLabel(b.label, b.x, b.y, bz + radius, { color: labelColor }));
|
|
30893
|
+
}
|
|
30894
|
+
const vx = b.vx ?? 0;
|
|
30895
|
+
const vy = b.vy ?? 0;
|
|
30896
|
+
const vz = b.vz ?? 0;
|
|
30897
|
+
const arrowRadius = Math.max(0.05, radius * 0.15);
|
|
30898
|
+
if (showVelocity && (vx !== 0 || vy !== 0 || vz !== 0)) {
|
|
30899
|
+
const arrow = arrowBetween(
|
|
30900
|
+
[b.x, b.y, bz],
|
|
30901
|
+
[b.x + vx * velocityScale, b.y + vy * velocityScale, bz + vz * velocityScale],
|
|
30902
|
+
"#16a34a",
|
|
30903
|
+
arrowRadius
|
|
30904
|
+
);
|
|
30905
|
+
if (arrow) out.push(arrow);
|
|
30906
|
+
}
|
|
30907
|
+
const fx = b.fx ?? 0;
|
|
30908
|
+
const fy = b.fy ?? 0;
|
|
30909
|
+
const fz = b.fz ?? 0;
|
|
30910
|
+
if (showForces && (fx !== 0 || fy !== 0 || fz !== 0)) {
|
|
30911
|
+
const arrow = arrowBetween(
|
|
30912
|
+
[b.x, b.y, bz],
|
|
30913
|
+
[b.x + fx * forceScale, b.y + fy * forceScale, bz + fz * forceScale],
|
|
30914
|
+
"#dc2626",
|
|
30915
|
+
arrowRadius
|
|
30916
|
+
);
|
|
30917
|
+
if (arrow) out.push(arrow);
|
|
30918
|
+
}
|
|
30919
|
+
}
|
|
30920
|
+
return out;
|
|
30921
|
+
}, [mode, bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes, backgroundColor]);
|
|
30922
|
+
const bodyIndexById = useMemo(() => {
|
|
30923
|
+
const m = /* @__PURE__ */ new Map();
|
|
30924
|
+
bodies.forEach((b, i) => {
|
|
30925
|
+
if (b.id) m.set(b.id, i);
|
|
30926
|
+
});
|
|
30927
|
+
return m;
|
|
30928
|
+
}, [bodies]);
|
|
30929
|
+
if (mode === "3d") {
|
|
30930
|
+
return /* @__PURE__ */ jsx(
|
|
30931
|
+
LearningScene3D,
|
|
30932
|
+
{
|
|
30933
|
+
className,
|
|
30934
|
+
width,
|
|
30935
|
+
height,
|
|
30936
|
+
title,
|
|
30937
|
+
backgroundColor,
|
|
30938
|
+
drawables: drawables3D,
|
|
30939
|
+
camera,
|
|
30940
|
+
lighting,
|
|
30941
|
+
post,
|
|
30942
|
+
showGrid,
|
|
30943
|
+
shadows,
|
|
30944
|
+
interactive,
|
|
30945
|
+
isLoading,
|
|
30946
|
+
error,
|
|
30947
|
+
onItemClick: get3DClickPayload(onShapeClick, bodyIndexById)
|
|
30948
|
+
}
|
|
30949
|
+
);
|
|
30950
|
+
}
|
|
30431
30951
|
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
30432
30952
|
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
30433
30953
|
/* @__PURE__ */ jsx(
|
|
@@ -30437,7 +30957,7 @@ var init_PhysicsCanvas = __esm({
|
|
|
30437
30957
|
height,
|
|
30438
30958
|
backgroundColor,
|
|
30439
30959
|
shapes: derivedShapes,
|
|
30440
|
-
interactive,
|
|
30960
|
+
interactive: interactive ?? false,
|
|
30441
30961
|
animate,
|
|
30442
30962
|
onShapeClick,
|
|
30443
30963
|
isLoading,
|
|
@@ -31055,12 +31575,12 @@ var init_MapView = __esm({
|
|
|
31055
31575
|
shadowSize: [41, 41]
|
|
31056
31576
|
});
|
|
31057
31577
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
31058
|
-
const { useEffect: useEffect64, useRef:
|
|
31578
|
+
const { useEffect: useEffect64, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__default;
|
|
31059
31579
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
31060
31580
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
31061
31581
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
31062
31582
|
const map = useMap();
|
|
31063
|
-
const prevRef =
|
|
31583
|
+
const prevRef = useRef65({ centerLat, centerLng, zoom });
|
|
31064
31584
|
useEffect64(() => {
|
|
31065
31585
|
const prev = prevRef.current;
|
|
31066
31586
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
@@ -41474,7 +41994,7 @@ function getEnumOptions(field) {
|
|
|
41474
41994
|
}));
|
|
41475
41995
|
}
|
|
41476
41996
|
const validation = field.validation;
|
|
41477
|
-
if (validation?.enum &&
|
|
41997
|
+
if (validation?.enum && validation.enum.length > 0) {
|
|
41478
41998
|
return validation.enum.map((v) => ({
|
|
41479
41999
|
value: v,
|
|
41480
42000
|
label: v.charAt(0).toUpperCase() + v.slice(1).replace(/_/g, " ")
|