@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.cjs
CHANGED
|
@@ -14117,9 +14117,10 @@ function Canvas({
|
|
|
14117
14117
|
drawables: [...drawables ?? [], ...childDrawables],
|
|
14118
14118
|
isLoading,
|
|
14119
14119
|
cameraMode: to3DCameraMode(camera?.mode),
|
|
14120
|
-
...zoom !== void 0 ? {
|
|
14120
|
+
...zoom !== void 0 ? { zoom } : {},
|
|
14121
14121
|
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
14122
14122
|
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
14123
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
14123
14124
|
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
14124
14125
|
unitScale,
|
|
14125
14126
|
backgroundColor,
|
|
@@ -15328,7 +15329,10 @@ var init_LearningCanvas = __esm({
|
|
|
15328
15329
|
ctx.fillRect(0, 0, width, height);
|
|
15329
15330
|
}
|
|
15330
15331
|
for (const shape of shapes) {
|
|
15331
|
-
drawShape(ctx, shape, width, height);
|
|
15332
|
+
if (shape.type !== "text") drawShape(ctx, shape, width, height);
|
|
15333
|
+
}
|
|
15334
|
+
for (const shape of shapes) {
|
|
15335
|
+
if (shape.type === "text") drawShape(ctx, shape, width, height);
|
|
15332
15336
|
}
|
|
15333
15337
|
}, [width, height, backgroundColor, shapes]);
|
|
15334
15338
|
React94.useEffect(() => {
|
|
@@ -16891,7 +16895,7 @@ var init_AlgorithmCanvas = __esm({
|
|
|
16891
16895
|
DEFAULT_CELL_COLOR = "#e5e7eb";
|
|
16892
16896
|
DEFAULT_POINTER_COLOR = "#dc2626";
|
|
16893
16897
|
POINTER_BAND = 34;
|
|
16894
|
-
TOP_PAD =
|
|
16898
|
+
TOP_PAD = 26;
|
|
16895
16899
|
AlgorithmCanvas = ({
|
|
16896
16900
|
className,
|
|
16897
16901
|
width = 600,
|
|
@@ -17168,23 +17172,188 @@ var init_AuthLayout = __esm({
|
|
|
17168
17172
|
AuthLayout.displayName = "AuthLayout";
|
|
17169
17173
|
}
|
|
17170
17174
|
});
|
|
17171
|
-
|
|
17175
|
+
function LearningScene3D({
|
|
17176
|
+
className,
|
|
17177
|
+
width = 600,
|
|
17178
|
+
height = 400,
|
|
17179
|
+
title,
|
|
17180
|
+
backgroundColor,
|
|
17181
|
+
drawables,
|
|
17182
|
+
camera,
|
|
17183
|
+
lighting,
|
|
17184
|
+
post,
|
|
17185
|
+
showGrid = false,
|
|
17186
|
+
shadows,
|
|
17187
|
+
interactive,
|
|
17188
|
+
isLoading,
|
|
17189
|
+
error,
|
|
17190
|
+
onItemClick
|
|
17191
|
+
}) {
|
|
17192
|
+
const instanceId = React94.useId().replace(/[^a-zA-Z0-9]/g, "");
|
|
17193
|
+
const clickEvent = onItemClick ? `LEARNING_SCENE_3D.ITEM_CLICK.${instanceId}` : void 0;
|
|
17194
|
+
const onItemClickRef = React94.useRef(onItemClick);
|
|
17195
|
+
onItemClickRef.current = onItemClick;
|
|
17196
|
+
useEventListener(`UI:${clickEvent ?? "LEARNING_SCENE_3D.ITEM_CLICK.__disabled"}`, (event) => {
|
|
17197
|
+
const unitId = event.payload?.unitId;
|
|
17198
|
+
if (typeof unitId === "string") onItemClickRef.current?.(unitId);
|
|
17199
|
+
});
|
|
17200
|
+
const props3d = {
|
|
17201
|
+
drawables,
|
|
17202
|
+
isLoading,
|
|
17203
|
+
error: error ? error.message : null,
|
|
17204
|
+
cameraMode: camera?.mode ?? "perspective",
|
|
17205
|
+
...camera?.zoom !== void 0 ? { zoom: camera.zoom } : {},
|
|
17206
|
+
...camera?.fov !== void 0 ? { fov: camera.fov } : {},
|
|
17207
|
+
...camera?.azimuth !== void 0 ? { azimuth: camera.azimuth } : {},
|
|
17208
|
+
...camera?.elevation !== void 0 ? { elevation: camera.elevation } : {},
|
|
17209
|
+
...camera?.target !== void 0 ? { followTarget: camera.target } : {},
|
|
17210
|
+
backgroundColor,
|
|
17211
|
+
showGrid,
|
|
17212
|
+
...shadows !== void 0 ? { shadows } : {},
|
|
17213
|
+
...interactive !== void 0 ? { controlsEnabled: interactive } : {},
|
|
17214
|
+
lighting,
|
|
17215
|
+
post,
|
|
17216
|
+
...clickEvent !== void 0 ? { unitClickEvent: clickEvent } : {}
|
|
17217
|
+
};
|
|
17218
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
17219
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
17220
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { width, height, display: "flex" }, children: /* @__PURE__ */ jsxRuntime.jsx(React94.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx(Canvas3DHost2, { ...props3d }) }) })
|
|
17221
|
+
] }) });
|
|
17222
|
+
}
|
|
17223
|
+
function meshSphere(id, x, y, z, radius, color, opts) {
|
|
17224
|
+
return {
|
|
17225
|
+
type: "draw-mesh",
|
|
17226
|
+
...id !== void 0 ? { id } : {},
|
|
17227
|
+
shape: opts?.shape ?? "sphere",
|
|
17228
|
+
position: { x, y, z },
|
|
17229
|
+
radius,
|
|
17230
|
+
pivot: "center",
|
|
17231
|
+
segments: opts?.segments ?? 24,
|
|
17232
|
+
material: { color, ...opts?.material },
|
|
17233
|
+
...opts?.opacity !== void 0 ? { opacity: opts.opacity } : {}
|
|
17234
|
+
};
|
|
17235
|
+
}
|
|
17236
|
+
function axisRotation(from, to) {
|
|
17237
|
+
const wx = to[0] - from[0];
|
|
17238
|
+
const wy = to[2] - from[2];
|
|
17239
|
+
const wz = to[1] - from[1];
|
|
17240
|
+
const len = Math.sqrt(wx * wx + wy * wy + wz * wz);
|
|
17241
|
+
const tilt = Math.acos(Math.min(1, Math.max(-1, len > 0 ? wy / len : 1)));
|
|
17242
|
+
return [0, Math.atan2(wz, -wx), tilt];
|
|
17243
|
+
}
|
|
17244
|
+
function segmentLength(from, to) {
|
|
17245
|
+
const dx = to[0] - from[0];
|
|
17246
|
+
const dy = to[1] - from[1];
|
|
17247
|
+
const dz = to[2] - from[2];
|
|
17248
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
17249
|
+
}
|
|
17250
|
+
function cylinderBetween(from, to, radius, color) {
|
|
17251
|
+
const len = segmentLength(from, to);
|
|
17252
|
+
if (len < 1e-6) return null;
|
|
17253
|
+
return {
|
|
17254
|
+
type: "draw-mesh",
|
|
17255
|
+
shape: "cylinder",
|
|
17256
|
+
position: { x: (from[0] + to[0]) / 2, y: (from[1] + to[1]) / 2, z: (from[2] + to[2]) / 2 },
|
|
17257
|
+
radius,
|
|
17258
|
+
height: len,
|
|
17259
|
+
rotation: axisRotation(from, to),
|
|
17260
|
+
pivot: "center",
|
|
17261
|
+
segments: 12,
|
|
17262
|
+
material: { color }
|
|
17263
|
+
};
|
|
17264
|
+
}
|
|
17265
|
+
function arrowBetween(from, to, color, shaftRadius = 0.08) {
|
|
17266
|
+
const len = segmentLength(from, to);
|
|
17267
|
+
if (len < 1e-6) return null;
|
|
17268
|
+
const tipLen = Math.min(shaftRadius * 8, len * 0.35);
|
|
17269
|
+
const k = (len - tipLen) / len;
|
|
17270
|
+
const delta = [to[0] - from[0], to[1] - from[1], to[2] - from[2]];
|
|
17271
|
+
const shaftEnd = [delta[0] * k, delta[1] * k, delta[2] * k];
|
|
17272
|
+
const tipStart = shaftEnd;
|
|
17273
|
+
const tipEnd = delta;
|
|
17274
|
+
const shaft = cylinderBetween([0, 0, 0], shaftEnd, shaftRadius, color);
|
|
17275
|
+
const tipLenActual = segmentLength(tipStart, tipEnd);
|
|
17276
|
+
const tip = {
|
|
17277
|
+
type: "draw-mesh",
|
|
17278
|
+
shape: "cone",
|
|
17279
|
+
position: {
|
|
17280
|
+
x: (tipStart[0] + tipEnd[0]) / 2,
|
|
17281
|
+
y: (tipStart[1] + tipEnd[1]) / 2,
|
|
17282
|
+
z: (tipStart[2] + tipEnd[2]) / 2
|
|
17283
|
+
},
|
|
17284
|
+
radius: shaftRadius * 3,
|
|
17285
|
+
height: tipLenActual,
|
|
17286
|
+
rotation: axisRotation(tipStart, tipEnd),
|
|
17287
|
+
pivot: "center",
|
|
17288
|
+
segments: 12,
|
|
17289
|
+
material: { color }
|
|
17290
|
+
};
|
|
17291
|
+
return {
|
|
17292
|
+
type: "draw-group",
|
|
17293
|
+
position: { x: from[0], y: from[1], z: from[2] },
|
|
17294
|
+
items: tipLenActual < 1e-6 ? shaft ? [shaft] : [] : shaft ? [shaft, tip] : [tip]
|
|
17295
|
+
};
|
|
17296
|
+
}
|
|
17297
|
+
function billboardLabel(text, x, y, z, opts) {
|
|
17298
|
+
return {
|
|
17299
|
+
type: "draw-text",
|
|
17300
|
+
text,
|
|
17301
|
+
position: { x, y, z },
|
|
17302
|
+
color: opts?.color ?? "#111827"
|
|
17303
|
+
};
|
|
17304
|
+
}
|
|
17305
|
+
function labelColorForBackground(backgroundColor) {
|
|
17306
|
+
const m = /^#(?:([0-9a-f]{3})|([0-9a-f]{6}))$/i.exec(backgroundColor ?? "");
|
|
17307
|
+
if (!m) return "#111827";
|
|
17308
|
+
const hex = m[1] !== void 0 ? m[1].split("").map((c) => c + c).join("") : m[2];
|
|
17309
|
+
const r2 = parseInt(hex.slice(0, 2), 16) / 255;
|
|
17310
|
+
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
17311
|
+
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
17312
|
+
const luminance = 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
|
|
17313
|
+
return luminance < 0.5 ? "#e5e7eb" : "#111827";
|
|
17314
|
+
}
|
|
17315
|
+
function get3DClickPayload(onShapeClick, idToIndex) {
|
|
17316
|
+
if (!onShapeClick) return void 0;
|
|
17317
|
+
return (id) => onShapeClick({ id, index: idToIndex.get(id) ?? -1 });
|
|
17318
|
+
}
|
|
17319
|
+
var Canvas3DHost2;
|
|
17320
|
+
var init_learningScene3D = __esm({
|
|
17321
|
+
"components/learning/molecules/learningScene3D.tsx"() {
|
|
17322
|
+
"use client";
|
|
17323
|
+
init_atoms();
|
|
17324
|
+
init_Stack();
|
|
17325
|
+
init_useEventBus();
|
|
17326
|
+
Canvas3DHost2 = React94.lazy(
|
|
17327
|
+
() => import('@almadar/ui/components/molecules/game/three').then((m) => ({ default: m.Canvas3DHost }))
|
|
17328
|
+
);
|
|
17329
|
+
LearningScene3D.displayName = "LearningScene3D";
|
|
17330
|
+
}
|
|
17331
|
+
});
|
|
17332
|
+
var biologyLog, BiologyCanvas;
|
|
17172
17333
|
var init_BiologyCanvas = __esm({
|
|
17173
17334
|
"components/learning/molecules/BiologyCanvas.tsx"() {
|
|
17174
17335
|
"use client";
|
|
17175
17336
|
init_atoms();
|
|
17176
17337
|
init_Stack();
|
|
17177
17338
|
init_LearningCanvas();
|
|
17339
|
+
init_learningScene3D();
|
|
17340
|
+
biologyLog = logger.createLogger("almadar:ui:biology-canvas");
|
|
17178
17341
|
BiologyCanvas = ({
|
|
17179
17342
|
className,
|
|
17180
17343
|
width = 600,
|
|
17181
17344
|
height = 400,
|
|
17182
17345
|
title,
|
|
17183
17346
|
backgroundColor,
|
|
17347
|
+
mode = "2d",
|
|
17348
|
+
camera,
|
|
17349
|
+
lighting,
|
|
17350
|
+
post,
|
|
17184
17351
|
nodes = [],
|
|
17185
17352
|
edges = [],
|
|
17186
17353
|
shapes = [],
|
|
17187
|
-
|
|
17354
|
+
showGrid,
|
|
17355
|
+
shadows,
|
|
17356
|
+
interactive,
|
|
17188
17357
|
animate = false,
|
|
17189
17358
|
onShapeClick,
|
|
17190
17359
|
isLoading,
|
|
@@ -17245,6 +17414,75 @@ var init_BiologyCanvas = __esm({
|
|
|
17245
17414
|
out.push(...shapes);
|
|
17246
17415
|
return out;
|
|
17247
17416
|
}, [nodes, edges, shapes]);
|
|
17417
|
+
const drawables3D = React94.useMemo(() => {
|
|
17418
|
+
if (mode !== "3d") return [];
|
|
17419
|
+
if (shapes.length > 0) {
|
|
17420
|
+
biologyLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
17421
|
+
}
|
|
17422
|
+
const out = [];
|
|
17423
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
17424
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
17425
|
+
for (const n of nodes) {
|
|
17426
|
+
if (n.id) nodeById.set(n.id, n);
|
|
17427
|
+
}
|
|
17428
|
+
for (const e of edges) {
|
|
17429
|
+
const a = nodeById.get(e.from);
|
|
17430
|
+
const b = nodeById.get(e.to);
|
|
17431
|
+
if (!a || !b) continue;
|
|
17432
|
+
const edgeRadius = Math.max(0.04, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.12);
|
|
17433
|
+
const edge = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], edgeRadius, e.color ?? "#9ca3af");
|
|
17434
|
+
if (edge) out.push(edge);
|
|
17435
|
+
if (e.label) {
|
|
17436
|
+
out.push(
|
|
17437
|
+
billboardLabel(
|
|
17438
|
+
e.label,
|
|
17439
|
+
(a.x + b.x) / 2,
|
|
17440
|
+
(a.y + b.y) / 2,
|
|
17441
|
+
((a.z ?? 0) + (b.z ?? 0)) / 2,
|
|
17442
|
+
{ color: labelColor }
|
|
17443
|
+
)
|
|
17444
|
+
);
|
|
17445
|
+
}
|
|
17446
|
+
}
|
|
17447
|
+
for (const n of nodes) {
|
|
17448
|
+
const radius = n.radius ?? 0.5;
|
|
17449
|
+
const nz = n.z ?? 0;
|
|
17450
|
+
out.push(meshSphere(n.id, n.x, n.y, nz, radius, n.color ?? "#16a34a", { shape: n.shape, ...n.opacity !== void 0 ? { opacity: n.opacity } : {} }));
|
|
17451
|
+
if (n.label) {
|
|
17452
|
+
out.push(billboardLabel(n.label, n.x, n.y, nz + radius, { color: labelColor }));
|
|
17453
|
+
}
|
|
17454
|
+
}
|
|
17455
|
+
return out;
|
|
17456
|
+
}, [mode, nodes, edges, shapes, backgroundColor]);
|
|
17457
|
+
const nodeIndexById = React94.useMemo(() => {
|
|
17458
|
+
const m = /* @__PURE__ */ new Map();
|
|
17459
|
+
nodes.forEach((n, i) => {
|
|
17460
|
+
if (n.id) m.set(n.id, i);
|
|
17461
|
+
});
|
|
17462
|
+
return m;
|
|
17463
|
+
}, [nodes]);
|
|
17464
|
+
if (mode === "3d") {
|
|
17465
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
17466
|
+
LearningScene3D,
|
|
17467
|
+
{
|
|
17468
|
+
className,
|
|
17469
|
+
width,
|
|
17470
|
+
height,
|
|
17471
|
+
title,
|
|
17472
|
+
backgroundColor,
|
|
17473
|
+
drawables: drawables3D,
|
|
17474
|
+
camera,
|
|
17475
|
+
lighting,
|
|
17476
|
+
post,
|
|
17477
|
+
showGrid,
|
|
17478
|
+
shadows,
|
|
17479
|
+
interactive,
|
|
17480
|
+
isLoading,
|
|
17481
|
+
error,
|
|
17482
|
+
onItemClick: get3DClickPayload(onShapeClick, nodeIndexById)
|
|
17483
|
+
}
|
|
17484
|
+
);
|
|
17485
|
+
}
|
|
17248
17486
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
17249
17487
|
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
17250
17488
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -17254,7 +17492,7 @@ var init_BiologyCanvas = __esm({
|
|
|
17254
17492
|
height,
|
|
17255
17493
|
backgroundColor,
|
|
17256
17494
|
shapes: derivedShapes,
|
|
17257
|
-
interactive,
|
|
17495
|
+
interactive: interactive ?? false,
|
|
17258
17496
|
animate,
|
|
17259
17497
|
onShapeClick,
|
|
17260
17498
|
isLoading,
|
|
@@ -24094,24 +24332,41 @@ var init_ChatBar = __esm({
|
|
|
24094
24332
|
ChatBar.displayName = "ChatBar";
|
|
24095
24333
|
}
|
|
24096
24334
|
});
|
|
24097
|
-
|
|
24335
|
+
function bondPerpendicular(a, b) {
|
|
24336
|
+
const dx = b[0] - a[0];
|
|
24337
|
+
const dy = b[1] - a[1];
|
|
24338
|
+
const px = dy;
|
|
24339
|
+
const py = -dx;
|
|
24340
|
+
const len = Math.sqrt(px * px + py * py);
|
|
24341
|
+
if (len < 1e-6) return [1, 0, 0];
|
|
24342
|
+
return [px / len, py / len, 0];
|
|
24343
|
+
}
|
|
24344
|
+
var chemistryLog, ChemistryCanvas;
|
|
24098
24345
|
var init_ChemistryCanvas = __esm({
|
|
24099
24346
|
"components/learning/molecules/ChemistryCanvas.tsx"() {
|
|
24100
24347
|
"use client";
|
|
24101
24348
|
init_atoms();
|
|
24102
24349
|
init_Stack();
|
|
24103
24350
|
init_LearningCanvas();
|
|
24351
|
+
init_learningScene3D();
|
|
24352
|
+
chemistryLog = logger.createLogger("almadar:ui:chemistry-canvas");
|
|
24104
24353
|
ChemistryCanvas = ({
|
|
24105
24354
|
className,
|
|
24106
24355
|
width = 600,
|
|
24107
24356
|
height = 400,
|
|
24108
24357
|
title,
|
|
24109
24358
|
backgroundColor,
|
|
24359
|
+
mode = "2d",
|
|
24360
|
+
camera,
|
|
24361
|
+
lighting,
|
|
24362
|
+
post,
|
|
24110
24363
|
atoms = [],
|
|
24111
24364
|
bonds = [],
|
|
24112
24365
|
arrows = [],
|
|
24113
24366
|
shapes = [],
|
|
24114
|
-
|
|
24367
|
+
showGrid,
|
|
24368
|
+
shadows,
|
|
24369
|
+
interactive,
|
|
24115
24370
|
animate = false,
|
|
24116
24371
|
onShapeClick,
|
|
24117
24372
|
isLoading,
|
|
@@ -24190,6 +24445,88 @@ var init_ChemistryCanvas = __esm({
|
|
|
24190
24445
|
out.push(...shapes);
|
|
24191
24446
|
return out;
|
|
24192
24447
|
}, [atoms, bonds, arrows, shapes]);
|
|
24448
|
+
const drawables3D = React94.useMemo(() => {
|
|
24449
|
+
if (mode !== "3d") return [];
|
|
24450
|
+
if (shapes.length > 0) {
|
|
24451
|
+
chemistryLog.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
24452
|
+
}
|
|
24453
|
+
const out = [];
|
|
24454
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
24455
|
+
const atomById = /* @__PURE__ */ new Map();
|
|
24456
|
+
for (const a of atoms) {
|
|
24457
|
+
if (a.id) atomById.set(a.id, a);
|
|
24458
|
+
}
|
|
24459
|
+
for (const b of bonds) {
|
|
24460
|
+
const a = atomById.get(b.from);
|
|
24461
|
+
const c = atomById.get(b.to);
|
|
24462
|
+
if (!a || !c) continue;
|
|
24463
|
+
const color = b.color ?? "#6b7280";
|
|
24464
|
+
const from = [a.x, a.y, a.z ?? 0];
|
|
24465
|
+
const to = [c.x, c.y, c.z ?? 0];
|
|
24466
|
+
const perp = bondPerpendicular(from, to);
|
|
24467
|
+
const bondRadius = Math.max(0.06, Math.min(a.radius ?? 0.45, c.radius ?? 0.45) * 0.22);
|
|
24468
|
+
const step = bondRadius * 2.2;
|
|
24469
|
+
const offsets = b.type === "double" ? [-step, step] : b.type === "triple" ? [-step, 0, step] : [0];
|
|
24470
|
+
for (const off of offsets) {
|
|
24471
|
+
const bond = cylinderBetween(
|
|
24472
|
+
[from[0] + perp[0] * off, from[1] + perp[1] * off, from[2] + perp[2] * off],
|
|
24473
|
+
[to[0] + perp[0] * off, to[1] + perp[1] * off, to[2] + perp[2] * off],
|
|
24474
|
+
bondRadius,
|
|
24475
|
+
color
|
|
24476
|
+
);
|
|
24477
|
+
if (bond) out.push(bond);
|
|
24478
|
+
}
|
|
24479
|
+
}
|
|
24480
|
+
for (const a of arrows) {
|
|
24481
|
+
const angle = (a.angle ?? 0) * (Math.PI / 180);
|
|
24482
|
+
const len = a.length ?? 60;
|
|
24483
|
+
const x2 = a.x + Math.cos(angle) * len;
|
|
24484
|
+
const y2 = a.y + Math.sin(angle) * len;
|
|
24485
|
+
const arrow = arrowBetween([a.x, a.y, 0], [x2, y2, 0], a.color ?? "#dc2626");
|
|
24486
|
+
if (arrow) out.push(arrow);
|
|
24487
|
+
if (a.label) {
|
|
24488
|
+
out.push(billboardLabel(a.label, (a.x + x2) / 2, (a.y + y2) / 2, 0, { color: labelColor }));
|
|
24489
|
+
}
|
|
24490
|
+
}
|
|
24491
|
+
for (const a of atoms) {
|
|
24492
|
+
const radius = a.radius ?? 0.45;
|
|
24493
|
+
const az = a.z ?? 0;
|
|
24494
|
+
out.push(meshSphere(a.id, a.x, a.y, az, radius, a.color ?? "#2563eb"));
|
|
24495
|
+
if (a.element) {
|
|
24496
|
+
out.push(billboardLabel(a.element, a.x, a.y, az + radius, { color: labelColor }));
|
|
24497
|
+
}
|
|
24498
|
+
}
|
|
24499
|
+
return out;
|
|
24500
|
+
}, [mode, atoms, bonds, arrows, shapes, backgroundColor]);
|
|
24501
|
+
const atomIndexById = React94.useMemo(() => {
|
|
24502
|
+
const m = /* @__PURE__ */ new Map();
|
|
24503
|
+
atoms.forEach((a, i) => {
|
|
24504
|
+
if (a.id) m.set(a.id, i);
|
|
24505
|
+
});
|
|
24506
|
+
return m;
|
|
24507
|
+
}, [atoms]);
|
|
24508
|
+
if (mode === "3d") {
|
|
24509
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
24510
|
+
LearningScene3D,
|
|
24511
|
+
{
|
|
24512
|
+
className,
|
|
24513
|
+
width,
|
|
24514
|
+
height,
|
|
24515
|
+
title,
|
|
24516
|
+
backgroundColor,
|
|
24517
|
+
drawables: drawables3D,
|
|
24518
|
+
camera,
|
|
24519
|
+
lighting,
|
|
24520
|
+
post,
|
|
24521
|
+
showGrid,
|
|
24522
|
+
shadows,
|
|
24523
|
+
interactive,
|
|
24524
|
+
isLoading,
|
|
24525
|
+
error,
|
|
24526
|
+
onItemClick: get3DClickPayload(onShapeClick, atomIndexById)
|
|
24527
|
+
}
|
|
24528
|
+
);
|
|
24529
|
+
}
|
|
24193
24530
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
24194
24531
|
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
24195
24532
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -24199,7 +24536,7 @@ var init_ChemistryCanvas = __esm({
|
|
|
24199
24536
|
height,
|
|
24200
24537
|
backgroundColor,
|
|
24201
24538
|
shapes: derivedShapes,
|
|
24202
|
-
interactive,
|
|
24539
|
+
interactive: interactive ?? false,
|
|
24203
24540
|
animate,
|
|
24204
24541
|
onShapeClick,
|
|
24205
24542
|
isLoading,
|
|
@@ -27670,6 +28007,7 @@ var init_FilterGroup = __esm({
|
|
|
27670
28007
|
init_Badge();
|
|
27671
28008
|
init_Stack();
|
|
27672
28009
|
init_Icon();
|
|
28010
|
+
init_RangeSlider();
|
|
27673
28011
|
init_useEventBus();
|
|
27674
28012
|
init_useQuerySingleton();
|
|
27675
28013
|
resolveFilterType = (filter) => filter.filterType ?? filter.type;
|
|
@@ -27864,6 +28202,35 @@ var init_FilterGroup = __esm({
|
|
|
27864
28202
|
onClear: () => handleFilterSelect(`${filter.field}_to`, null)
|
|
27865
28203
|
}
|
|
27866
28204
|
)
|
|
28205
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
28206
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28207
|
+
RangeSlider,
|
|
28208
|
+
{
|
|
28209
|
+
min: filter.min ?? 0,
|
|
28210
|
+
max: filter.max ?? 100,
|
|
28211
|
+
step: filter.step ?? 1,
|
|
28212
|
+
value: Number(
|
|
28213
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
28214
|
+
),
|
|
28215
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
28216
|
+
showTooltip: true,
|
|
28217
|
+
"aria-label": t("filterGroup.from")
|
|
28218
|
+
}
|
|
28219
|
+
),
|
|
28220
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28221
|
+
RangeSlider,
|
|
28222
|
+
{
|
|
28223
|
+
min: filter.min ?? 0,
|
|
28224
|
+
max: filter.max ?? 100,
|
|
28225
|
+
step: filter.step ?? 1,
|
|
28226
|
+
value: Number(
|
|
28227
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
28228
|
+
),
|
|
28229
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
28230
|
+
showTooltip: true,
|
|
28231
|
+
"aria-label": t("filterGroup.to")
|
|
28232
|
+
}
|
|
28233
|
+
)
|
|
27867
28234
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
27868
28235
|
Input,
|
|
27869
28236
|
{
|
|
@@ -27946,6 +28313,36 @@ var init_FilterGroup = __esm({
|
|
|
27946
28313
|
className: "text-sm min-w-[100px]"
|
|
27947
28314
|
}
|
|
27948
28315
|
)
|
|
28316
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
28317
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28318
|
+
RangeSlider,
|
|
28319
|
+
{
|
|
28320
|
+
min: filter.min ?? 0,
|
|
28321
|
+
max: filter.max ?? 100,
|
|
28322
|
+
step: filter.step ?? 1,
|
|
28323
|
+
value: Number(
|
|
28324
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
28325
|
+
),
|
|
28326
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
28327
|
+
className: "min-w-[100px]",
|
|
28328
|
+
"aria-label": t("filterGroup.from")
|
|
28329
|
+
}
|
|
28330
|
+
),
|
|
28331
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "-" }),
|
|
28332
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28333
|
+
RangeSlider,
|
|
28334
|
+
{
|
|
28335
|
+
min: filter.min ?? 0,
|
|
28336
|
+
max: filter.max ?? 100,
|
|
28337
|
+
step: filter.step ?? 1,
|
|
28338
|
+
value: Number(
|
|
28339
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
28340
|
+
),
|
|
28341
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
28342
|
+
className: "min-w-[100px]",
|
|
28343
|
+
"aria-label": t("filterGroup.to")
|
|
28344
|
+
}
|
|
28345
|
+
)
|
|
27949
28346
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
27950
28347
|
Input,
|
|
27951
28348
|
{
|
|
@@ -28064,6 +28461,38 @@ var init_FilterGroup = __esm({
|
|
|
28064
28461
|
className: "min-w-[130px]"
|
|
28065
28462
|
}
|
|
28066
28463
|
)
|
|
28464
|
+
] }) : resolveFilterType(filter) === "numberrange" || resolveFilterType(filter) === "number-range" ? /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
28465
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28466
|
+
RangeSlider,
|
|
28467
|
+
{
|
|
28468
|
+
min: filter.min ?? 0,
|
|
28469
|
+
max: filter.max ?? 100,
|
|
28470
|
+
step: filter.step ?? 1,
|
|
28471
|
+
value: Number(
|
|
28472
|
+
selectedValues[`${filter.field}_min`] ?? filter.min ?? 0
|
|
28473
|
+
),
|
|
28474
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_min`, String(v)),
|
|
28475
|
+
showTooltip: true,
|
|
28476
|
+
className: "min-w-[130px]",
|
|
28477
|
+
"aria-label": t("filterGroup.from")
|
|
28478
|
+
}
|
|
28479
|
+
),
|
|
28480
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "-" }),
|
|
28481
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28482
|
+
RangeSlider,
|
|
28483
|
+
{
|
|
28484
|
+
min: filter.min ?? 0,
|
|
28485
|
+
max: filter.max ?? 100,
|
|
28486
|
+
step: filter.step ?? 1,
|
|
28487
|
+
value: Number(
|
|
28488
|
+
selectedValues[`${filter.field}_max`] ?? filter.max ?? 100
|
|
28489
|
+
),
|
|
28490
|
+
onChange: (v) => handleFilterSelect(`${filter.field}_max`, String(v)),
|
|
28491
|
+
showTooltip: true,
|
|
28492
|
+
className: "min-w-[130px]",
|
|
28493
|
+
"aria-label": t("filterGroup.to")
|
|
28494
|
+
}
|
|
28495
|
+
)
|
|
28067
28496
|
] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
28068
28497
|
Input,
|
|
28069
28498
|
{
|
|
@@ -30412,19 +30841,25 @@ var init_MathCanvas = __esm({
|
|
|
30412
30841
|
};
|
|
30413
30842
|
}
|
|
30414
30843
|
});
|
|
30415
|
-
var PhysicsCanvas;
|
|
30844
|
+
var physicsLog2, PhysicsCanvas;
|
|
30416
30845
|
var init_PhysicsCanvas = __esm({
|
|
30417
30846
|
"components/learning/molecules/PhysicsCanvas.tsx"() {
|
|
30418
30847
|
"use client";
|
|
30419
30848
|
init_atoms();
|
|
30420
30849
|
init_Stack();
|
|
30421
30850
|
init_LearningCanvas();
|
|
30851
|
+
init_learningScene3D();
|
|
30852
|
+
physicsLog2 = logger.createLogger("almadar:ui:physics-canvas");
|
|
30422
30853
|
PhysicsCanvas = ({
|
|
30423
30854
|
className,
|
|
30424
30855
|
width = 600,
|
|
30425
30856
|
height = 400,
|
|
30426
30857
|
title,
|
|
30427
30858
|
backgroundColor,
|
|
30859
|
+
mode = "2d",
|
|
30860
|
+
camera,
|
|
30861
|
+
lighting,
|
|
30862
|
+
post,
|
|
30428
30863
|
bodies = [],
|
|
30429
30864
|
constraints = [],
|
|
30430
30865
|
showVelocity = true,
|
|
@@ -30432,7 +30867,9 @@ var init_PhysicsCanvas = __esm({
|
|
|
30432
30867
|
velocityScale = 20,
|
|
30433
30868
|
forceScale = 20,
|
|
30434
30869
|
shapes = [],
|
|
30435
|
-
|
|
30870
|
+
showGrid,
|
|
30871
|
+
shadows,
|
|
30872
|
+
interactive,
|
|
30436
30873
|
animate = false,
|
|
30437
30874
|
onShapeClick,
|
|
30438
30875
|
isLoading,
|
|
@@ -30504,6 +30941,89 @@ var init_PhysicsCanvas = __esm({
|
|
|
30504
30941
|
out.push(...shapes);
|
|
30505
30942
|
return out;
|
|
30506
30943
|
}, [bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes]);
|
|
30944
|
+
const drawables3D = React94.useMemo(() => {
|
|
30945
|
+
if (mode !== "3d") return [];
|
|
30946
|
+
if (shapes.length > 0) {
|
|
30947
|
+
physicsLog2.debug("shapes ignored in 3D mode (pixel-authored 2D vocabulary)", { count: shapes.length });
|
|
30948
|
+
}
|
|
30949
|
+
const out = [];
|
|
30950
|
+
const labelColor = labelColorForBackground(backgroundColor);
|
|
30951
|
+
const bodyById = /* @__PURE__ */ new Map();
|
|
30952
|
+
for (const b of bodies) {
|
|
30953
|
+
if (b.id) bodyById.set(b.id, b);
|
|
30954
|
+
}
|
|
30955
|
+
for (const c of constraints) {
|
|
30956
|
+
const a = bodyById.get(c.from);
|
|
30957
|
+
const b = bodyById.get(c.to);
|
|
30958
|
+
if (!a || !b) continue;
|
|
30959
|
+
const rodRadius = Math.max(0.05, Math.min(a.radius ?? 0.5, b.radius ?? 0.5) * 0.15);
|
|
30960
|
+
const rod = cylinderBetween([a.x, a.y, a.z ?? 0], [b.x, b.y, b.z ?? 0], rodRadius, c.color ?? "#9ca3af");
|
|
30961
|
+
if (rod) out.push(rod);
|
|
30962
|
+
}
|
|
30963
|
+
for (const b of bodies) {
|
|
30964
|
+
const radius = b.radius ?? 0.5;
|
|
30965
|
+
const bz = b.z ?? 0;
|
|
30966
|
+
out.push(meshSphere(b.id, b.x, b.y, bz, radius, b.color ?? "#2563eb"));
|
|
30967
|
+
if (b.label) {
|
|
30968
|
+
out.push(billboardLabel(b.label, b.x, b.y, bz + radius, { color: labelColor }));
|
|
30969
|
+
}
|
|
30970
|
+
const vx = b.vx ?? 0;
|
|
30971
|
+
const vy = b.vy ?? 0;
|
|
30972
|
+
const vz = b.vz ?? 0;
|
|
30973
|
+
const arrowRadius = Math.max(0.05, radius * 0.15);
|
|
30974
|
+
if (showVelocity && (vx !== 0 || vy !== 0 || vz !== 0)) {
|
|
30975
|
+
const arrow = arrowBetween(
|
|
30976
|
+
[b.x, b.y, bz],
|
|
30977
|
+
[b.x + vx * velocityScale, b.y + vy * velocityScale, bz + vz * velocityScale],
|
|
30978
|
+
"#16a34a",
|
|
30979
|
+
arrowRadius
|
|
30980
|
+
);
|
|
30981
|
+
if (arrow) out.push(arrow);
|
|
30982
|
+
}
|
|
30983
|
+
const fx = b.fx ?? 0;
|
|
30984
|
+
const fy = b.fy ?? 0;
|
|
30985
|
+
const fz = b.fz ?? 0;
|
|
30986
|
+
if (showForces && (fx !== 0 || fy !== 0 || fz !== 0)) {
|
|
30987
|
+
const arrow = arrowBetween(
|
|
30988
|
+
[b.x, b.y, bz],
|
|
30989
|
+
[b.x + fx * forceScale, b.y + fy * forceScale, bz + fz * forceScale],
|
|
30990
|
+
"#dc2626",
|
|
30991
|
+
arrowRadius
|
|
30992
|
+
);
|
|
30993
|
+
if (arrow) out.push(arrow);
|
|
30994
|
+
}
|
|
30995
|
+
}
|
|
30996
|
+
return out;
|
|
30997
|
+
}, [mode, bodies, constraints, showVelocity, showForces, velocityScale, forceScale, shapes, backgroundColor]);
|
|
30998
|
+
const bodyIndexById = React94.useMemo(() => {
|
|
30999
|
+
const m = /* @__PURE__ */ new Map();
|
|
31000
|
+
bodies.forEach((b, i) => {
|
|
31001
|
+
if (b.id) m.set(b.id, i);
|
|
31002
|
+
});
|
|
31003
|
+
return m;
|
|
31004
|
+
}, [bodies]);
|
|
31005
|
+
if (mode === "3d") {
|
|
31006
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
31007
|
+
LearningScene3D,
|
|
31008
|
+
{
|
|
31009
|
+
className,
|
|
31010
|
+
width,
|
|
31011
|
+
height,
|
|
31012
|
+
title,
|
|
31013
|
+
backgroundColor,
|
|
31014
|
+
drawables: drawables3D,
|
|
31015
|
+
camera,
|
|
31016
|
+
lighting,
|
|
31017
|
+
post,
|
|
31018
|
+
showGrid,
|
|
31019
|
+
shadows,
|
|
31020
|
+
interactive,
|
|
31021
|
+
isLoading,
|
|
31022
|
+
error,
|
|
31023
|
+
onItemClick: get3DClickPayload(onShapeClick, bodyIndexById)
|
|
31024
|
+
}
|
|
31025
|
+
);
|
|
31026
|
+
}
|
|
30507
31027
|
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
30508
31028
|
title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
|
|
30509
31029
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -30513,7 +31033,7 @@ var init_PhysicsCanvas = __esm({
|
|
|
30513
31033
|
height,
|
|
30514
31034
|
backgroundColor,
|
|
30515
31035
|
shapes: derivedShapes,
|
|
30516
|
-
interactive,
|
|
31036
|
+
interactive: interactive ?? false,
|
|
30517
31037
|
animate,
|
|
30518
31038
|
onShapeClick,
|
|
30519
31039
|
isLoading,
|
|
@@ -31131,12 +31651,12 @@ var init_MapView = __esm({
|
|
|
31131
31651
|
shadowSize: [41, 41]
|
|
31132
31652
|
});
|
|
31133
31653
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
31134
|
-
const { useEffect: useEffect64, useRef:
|
|
31654
|
+
const { useEffect: useEffect64, useRef: useRef65, useCallback: useCallback96, useState: useState100 } = React94__namespace.default;
|
|
31135
31655
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
31136
31656
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
31137
31657
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
31138
31658
|
const map = useMap();
|
|
31139
|
-
const prevRef =
|
|
31659
|
+
const prevRef = useRef65({ centerLat, centerLng, zoom });
|
|
31140
31660
|
useEffect64(() => {
|
|
31141
31661
|
const prev = prevRef.current;
|
|
31142
31662
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
@@ -41550,7 +42070,7 @@ function getEnumOptions(field) {
|
|
|
41550
42070
|
}));
|
|
41551
42071
|
}
|
|
41552
42072
|
const validation = field.validation;
|
|
41553
|
-
if (validation?.enum &&
|
|
42073
|
+
if (validation?.enum && validation.enum.length > 0) {
|
|
41554
42074
|
return validation.enum.map((v) => ({
|
|
41555
42075
|
value: v,
|
|
41556
42076
|
label: v.charAt(0).toUpperCase() + v.slice(1).replace(/_/g, " ")
|