@almadar/ui 5.148.0 → 5.149.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/{GameAudioProvider-CPGwD49P.d.cts → GameAudioProvider-B48iXwz3.d.ts} +2 -50
  2. package/dist/{GameAudioProvider-CPGwD49P.d.ts → GameAudioProvider-CQAdPreB.d.cts} +2 -50
  3. package/dist/avl/index.cjs +438 -13
  4. package/dist/avl/index.js +438 -13
  5. package/dist/{avl-schema-parser-DVmrgdwc.d.cts → avl-schema-parser-CVzkNzg7.d.cts} +18 -9
  6. package/dist/{avl-schema-parser-BRJ77Yze.d.ts → avl-schema-parser-Cx_4SVg9.d.ts} +18 -9
  7. package/dist/{cn-BAn68sNO.d.cts → cn-BnAJZcNb.d.cts} +2 -8
  8. package/dist/{cn-CWxxLkri.d.ts → cn-DJTUjk1M.d.ts} +2 -8
  9. package/dist/components/index.cjs +448 -16
  10. package/dist/components/index.d.cts +269 -95
  11. package/dist/components/index.d.ts +269 -95
  12. package/dist/components/index.js +444 -18
  13. package/dist/lib/drawable/three/index.cjs +56 -29
  14. package/dist/lib/drawable/three/index.d.cts +4 -3
  15. package/dist/lib/drawable/three/index.d.ts +4 -3
  16. package/dist/lib/drawable/three/index.js +56 -29
  17. package/dist/lib/index.d.cts +3 -2
  18. package/dist/lib/index.d.ts +3 -2
  19. package/dist/{paintDispatch-Cb_hQj4Y.d.ts → paintDispatch-BjZjUbcb.d.cts} +63 -69
  20. package/dist/{paintDispatch-Cb_hQj4Y.d.cts → paintDispatch-CxdLjHQq.d.ts} +63 -69
  21. package/dist/providers/index.cjs +438 -13
  22. package/dist/providers/index.d.cts +2 -1
  23. package/dist/providers/index.d.ts +2 -1
  24. package/dist/providers/index.js +438 -13
  25. package/dist/runtime/index.cjs +438 -13
  26. package/dist/runtime/index.js +438 -13
  27. package/dist/types-B3nHgnMG.d.cts +51 -0
  28. package/dist/types-B3nHgnMG.d.ts +51 -0
  29. package/package.json +3 -3
@@ -10375,9 +10375,10 @@ function Canvas({
10375
10375
  drawables: [...drawables ?? [], ...childDrawables],
10376
10376
  isLoading,
10377
10377
  cameraMode: to3DCameraMode(camera?.mode),
10378
- ...zoom !== void 0 ? { scale: zoom } : {},
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,
@@ -14906,23 +14907,188 @@ var init_BehaviorView = __esm({
14906
14907
  BehaviorView.displayName = "BehaviorView";
14907
14908
  }
14908
14909
  });
14909
- var BiologyCanvas;
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;
14910
15068
  var init_BiologyCanvas = __esm({
14911
15069
  "components/learning/molecules/BiologyCanvas.tsx"() {
14912
15070
  "use client";
14913
15071
  init_atoms();
14914
15072
  init_Stack();
14915
15073
  init_LearningCanvas();
15074
+ init_learningScene3D();
15075
+ biologyLog = logger.createLogger("almadar:ui:biology-canvas");
14916
15076
  BiologyCanvas = ({
14917
15077
  className,
14918
15078
  width = 600,
14919
15079
  height = 400,
14920
15080
  title,
14921
15081
  backgroundColor,
15082
+ mode = "2d",
15083
+ camera,
15084
+ lighting,
15085
+ post,
14922
15086
  nodes = [],
14923
15087
  edges = [],
14924
15088
  shapes = [],
14925
- interactive = false,
15089
+ showGrid,
15090
+ shadows,
15091
+ interactive,
14926
15092
  animate = false,
14927
15093
  onShapeClick,
14928
15094
  isLoading,
@@ -14983,6 +15149,75 @@ var init_BiologyCanvas = __esm({
14983
15149
  out.push(...shapes);
14984
15150
  return out;
14985
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
+ }
14986
15221
  return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
14987
15222
  title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
14988
15223
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -14992,7 +15227,7 @@ var init_BiologyCanvas = __esm({
14992
15227
  height,
14993
15228
  backgroundColor,
14994
15229
  shapes: derivedShapes,
14995
- interactive,
15230
+ interactive: interactive ?? false,
14996
15231
  animate,
14997
15232
  onShapeClick,
14998
15233
  isLoading,
@@ -21832,24 +22067,41 @@ var init_ChatBar = __esm({
21832
22067
  ChatBar.displayName = "ChatBar";
21833
22068
  }
21834
22069
  });
21835
- var ChemistryCanvas;
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;
21836
22080
  var init_ChemistryCanvas = __esm({
21837
22081
  "components/learning/molecules/ChemistryCanvas.tsx"() {
21838
22082
  "use client";
21839
22083
  init_atoms();
21840
22084
  init_Stack();
21841
22085
  init_LearningCanvas();
22086
+ init_learningScene3D();
22087
+ chemistryLog = logger.createLogger("almadar:ui:chemistry-canvas");
21842
22088
  ChemistryCanvas = ({
21843
22089
  className,
21844
22090
  width = 600,
21845
22091
  height = 400,
21846
22092
  title,
21847
22093
  backgroundColor,
22094
+ mode = "2d",
22095
+ camera,
22096
+ lighting,
22097
+ post,
21848
22098
  atoms = [],
21849
22099
  bonds = [],
21850
22100
  arrows = [],
21851
22101
  shapes = [],
21852
- interactive = false,
22102
+ showGrid,
22103
+ shadows,
22104
+ interactive,
21853
22105
  animate = false,
21854
22106
  onShapeClick,
21855
22107
  isLoading,
@@ -21928,6 +22180,88 @@ var init_ChemistryCanvas = __esm({
21928
22180
  out.push(...shapes);
21929
22181
  return out;
21930
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
+ }
21931
22265
  return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
21932
22266
  title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
21933
22267
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -21937,7 +22271,7 @@ var init_ChemistryCanvas = __esm({
21937
22271
  height,
21938
22272
  backgroundColor,
21939
22273
  shapes: derivedShapes,
21940
- interactive,
22274
+ interactive: interactive ?? false,
21941
22275
  animate,
21942
22276
  onShapeClick,
21943
22277
  isLoading,
@@ -28242,19 +28576,25 @@ var init_MathCanvas = __esm({
28242
28576
  };
28243
28577
  }
28244
28578
  });
28245
- var PhysicsCanvas;
28579
+ var physicsLog2, PhysicsCanvas;
28246
28580
  var init_PhysicsCanvas = __esm({
28247
28581
  "components/learning/molecules/PhysicsCanvas.tsx"() {
28248
28582
  "use client";
28249
28583
  init_atoms();
28250
28584
  init_Stack();
28251
28585
  init_LearningCanvas();
28586
+ init_learningScene3D();
28587
+ physicsLog2 = logger.createLogger("almadar:ui:physics-canvas");
28252
28588
  PhysicsCanvas = ({
28253
28589
  className,
28254
28590
  width = 600,
28255
28591
  height = 400,
28256
28592
  title,
28257
28593
  backgroundColor,
28594
+ mode = "2d",
28595
+ camera,
28596
+ lighting,
28597
+ post,
28258
28598
  bodies = [],
28259
28599
  constraints = [],
28260
28600
  showVelocity = true,
@@ -28262,7 +28602,9 @@ var init_PhysicsCanvas = __esm({
28262
28602
  velocityScale = 20,
28263
28603
  forceScale = 20,
28264
28604
  shapes = [],
28265
- interactive = false,
28605
+ showGrid,
28606
+ shadows,
28607
+ interactive,
28266
28608
  animate = false,
28267
28609
  onShapeClick,
28268
28610
  isLoading,
@@ -28334,6 +28676,89 @@ var init_PhysicsCanvas = __esm({
28334
28676
  out.push(...shapes);
28335
28677
  return out;
28336
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
+ }
28337
28762
  return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
28338
28763
  title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
28339
28764
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -28343,7 +28768,7 @@ var init_PhysicsCanvas = __esm({
28343
28768
  height,
28344
28769
  backgroundColor,
28345
28770
  shapes: derivedShapes,
28346
- interactive,
28771
+ interactive: interactive ?? false,
28347
28772
  animate,
28348
28773
  onShapeClick,
28349
28774
  isLoading,
@@ -28961,12 +29386,12 @@ var init_MapView = __esm({
28961
29386
  shadowSize: [41, 41]
28962
29387
  });
28963
29388
  L.Marker.prototype.options.icon = defaultIcon;
28964
- const { useEffect: useEffect63, useRef: useRef62, useCallback: useCallback96, useState: useState94 } = React87__namespace.default;
29389
+ const { useEffect: useEffect63, useRef: useRef63, useCallback: useCallback96, useState: useState94 } = React87__namespace.default;
28965
29390
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28966
29391
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28967
29392
  function MapUpdater({ centerLat, centerLng, zoom }) {
28968
29393
  const map = useMap();
28969
- const prevRef = useRef62({ centerLat, centerLng, zoom });
29394
+ const prevRef = useRef63({ centerLat, centerLng, zoom });
28970
29395
  useEffect63(() => {
28971
29396
  const prev = prevRef.current;
28972
29397
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
@@ -39789,7 +40214,7 @@ function getEnumOptions(field) {
39789
40214
  }));
39790
40215
  }
39791
40216
  const validation = field.validation;
39792
- if (validation?.enum && Array.isArray(validation.enum)) {
40217
+ if (validation?.enum && validation.enum.length > 0) {
39793
40218
  return validation.enum.map((v) => ({
39794
40219
  value: v,
39795
40220
  label: v.charAt(0).toUpperCase() + v.slice(1).replace(/_/g, " ")
@@ -7,8 +7,9 @@ export { E as EntityBindingContext, a as EntityBindingSource, b as EntitySchemaC
7
7
  import { U as UseOfflineExecutorResult, a as UseOfflineExecutorOptions } from '../offline-executor-Qz4b6GpF.cjs';
8
8
  export { N as NavigationContextValue, b as NavigationProvider, c as NavigationProviderProps, d as NavigationState, e as comparePathSpecificity, f as extractRouteParams, g as findPageByName, h as findPageByPath, i as getAllPages, j as getDefaultPage, m as matchPath, k as matchPathAmong, p as pathMatches, u as useActivePage, l as useInitPayload, n as useNavigateTo, o as useNavigation, q as useNavigationId, r as useNavigationState } from '../offline-executor-Qz4b6GpF.cjs';
9
9
  import { E as EventBusContextType } from '../event-bus-types-Bl78kokd.cjs';
10
- export { G as GameAudioContext, a as GameAudioContextValue, b as GameAudioProvider, c as GameAudioProviderProps, u as useGameAudioContext, d as useGameAudioContextOptional } from '../GameAudioProvider-CPGwD49P.cjs';
10
+ export { G as GameAudioContext, a as GameAudioContextValue, b as GameAudioProvider, c as GameAudioProviderProps, u as useGameAudioContext, d as useGameAudioContextOptional } from '../GameAudioProvider-CQAdPreB.cjs';
11
11
  import '@almadar/core/patterns';
12
+ import '../types-B3nHgnMG.cjs';
12
13
 
13
14
  /**
14
15
  * EventBusProvider - React context provider for the event bus
@@ -7,8 +7,9 @@ export { E as EntityBindingContext, a as EntityBindingSource, b as EntitySchemaC
7
7
  import { U as UseOfflineExecutorResult, a as UseOfflineExecutorOptions } from '../offline-executor-Qz4b6GpF.js';
8
8
  export { N as NavigationContextValue, b as NavigationProvider, c as NavigationProviderProps, d as NavigationState, e as comparePathSpecificity, f as extractRouteParams, g as findPageByName, h as findPageByPath, i as getAllPages, j as getDefaultPage, m as matchPath, k as matchPathAmong, p as pathMatches, u as useActivePage, l as useInitPayload, n as useNavigateTo, o as useNavigation, q as useNavigationId, r as useNavigationState } from '../offline-executor-Qz4b6GpF.js';
9
9
  import { E as EventBusContextType } from '../event-bus-types-Bl78kokd.js';
10
- export { G as GameAudioContext, a as GameAudioContextValue, b as GameAudioProvider, c as GameAudioProviderProps, u as useGameAudioContext, d as useGameAudioContextOptional } from '../GameAudioProvider-CPGwD49P.js';
10
+ export { G as GameAudioContext, a as GameAudioContextValue, b as GameAudioProvider, c as GameAudioProviderProps, u as useGameAudioContext, d as useGameAudioContextOptional } from '../GameAudioProvider-B48iXwz3.js';
11
11
  import '@almadar/core/patterns';
12
+ import '../types-B3nHgnMG.js';
12
13
 
13
14
  /**
14
15
  * EventBusProvider - React context provider for the event bus