@almadar/ui 5.42.0 → 5.44.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.
@@ -31086,6 +31086,78 @@ var init_GameOverScreen = __esm({
31086
31086
  GameOverScreen.displayName = "GameOverScreen";
31087
31087
  }
31088
31088
  });
31089
+ function useRenderInterpolation(options = {}) {
31090
+ const tickIntervalMs = options.tickIntervalMs ?? 1e3 / 30;
31091
+ const prevRef = React91.useRef(null);
31092
+ const currRef = React91.useRef(null);
31093
+ const rafRef = React91.useRef(null);
31094
+ const onSnapshot = React91.useCallback((entities) => {
31095
+ prevRef.current = currRef.current;
31096
+ currRef.current = { entities, arrivedAt: performance.now() };
31097
+ }, []);
31098
+ const getInterpolated = React91.useCallback((now2) => {
31099
+ const out = /* @__PURE__ */ new Map();
31100
+ const curr = currRef.current;
31101
+ if (!curr) return out;
31102
+ const prev = prevRef.current;
31103
+ if (!prev) {
31104
+ for (const e of curr.entities) out.set(e.id, { x: e.x, y: e.y });
31105
+ return out;
31106
+ }
31107
+ const rawAlpha = (now2 - curr.arrivedAt) / tickIntervalMs;
31108
+ const alpha = rawAlpha < 0 ? 0 : rawAlpha > 1 ? 1 : rawAlpha;
31109
+ const prevMap = /* @__PURE__ */ new Map();
31110
+ for (const e of prev.entities) prevMap.set(e.id, e);
31111
+ for (const c of curr.entities) {
31112
+ const p2 = prevMap.get(c.id);
31113
+ if (!p2) {
31114
+ out.set(c.id, { x: c.x, y: c.y });
31115
+ } else {
31116
+ out.set(c.id, {
31117
+ x: p2.x + (c.x - p2.x) * alpha,
31118
+ y: p2.y + (c.y - p2.y) * alpha
31119
+ });
31120
+ }
31121
+ }
31122
+ return out;
31123
+ }, [tickIntervalMs]);
31124
+ const startLoop = React91.useCallback(
31125
+ (draw) => {
31126
+ let active = true;
31127
+ const loop = () => {
31128
+ if (!active) return;
31129
+ try {
31130
+ draw(getInterpolated(performance.now()));
31131
+ } catch {
31132
+ }
31133
+ rafRef.current = requestAnimationFrame(loop);
31134
+ };
31135
+ rafRef.current = requestAnimationFrame(loop);
31136
+ return () => {
31137
+ active = false;
31138
+ if (rafRef.current !== null) {
31139
+ cancelAnimationFrame(rafRef.current);
31140
+ rafRef.current = null;
31141
+ }
31142
+ };
31143
+ },
31144
+ [getInterpolated]
31145
+ );
31146
+ React91.useEffect(() => {
31147
+ return () => {
31148
+ if (rafRef.current !== null) {
31149
+ cancelAnimationFrame(rafRef.current);
31150
+ rafRef.current = null;
31151
+ }
31152
+ };
31153
+ }, []);
31154
+ return { onSnapshot, getInterpolated, startLoop };
31155
+ }
31156
+ var init_useRenderInterpolation = __esm({
31157
+ "hooks/useRenderInterpolation.ts"() {
31158
+ "use client";
31159
+ }
31160
+ });
31089
31161
  function PlatformerCanvas({
31090
31162
  player,
31091
31163
  platforms = [
@@ -31154,8 +31226,43 @@ function PlatformerCanvas({
31154
31226
  y: 336,
31155
31227
  width: 32,
31156
31228
  height: 48,
31229
+ vx: 0,
31230
+ vy: 0,
31231
+ grounded: true,
31157
31232
  facingRight: true
31158
31233
  };
31234
+ const playerRef = React91.useRef(resolvedPlayer);
31235
+ playerRef.current = resolvedPlayer;
31236
+ const interp = useRenderInterpolation();
31237
+ React91.useEffect(() => {
31238
+ interp.onSnapshot([{ id: "player", x: resolvedPlayer.x, y: resolvedPlayer.y }]);
31239
+ }, [resolvedPlayer.x, resolvedPlayer.y]);
31240
+ const propsRef = React91.useRef({
31241
+ platforms,
31242
+ worldWidth,
31243
+ worldHeight,
31244
+ canvasWidth,
31245
+ canvasHeight,
31246
+ followCamera,
31247
+ bgColor,
31248
+ playerSprite,
31249
+ tileSprites,
31250
+ backgroundImage,
31251
+ assetBaseUrl
31252
+ });
31253
+ propsRef.current = {
31254
+ platforms,
31255
+ worldWidth,
31256
+ worldHeight,
31257
+ canvasWidth,
31258
+ canvasHeight,
31259
+ followCamera,
31260
+ bgColor,
31261
+ playerSprite,
31262
+ tileSprites,
31263
+ backgroundImage,
31264
+ assetBaseUrl
31265
+ };
31159
31266
  const handleKeyDown = React91.useCallback((e) => {
31160
31267
  if (keysRef.current.has(e.code)) return;
31161
31268
  keysRef.current.add(e.code);
@@ -31204,124 +31311,149 @@ function PlatformerCanvas({
31204
31311
  canvas.width = canvasWidth * dpr;
31205
31312
  canvas.height = canvasHeight * dpr;
31206
31313
  ctx.scale(dpr, dpr);
31207
- let camX = 0;
31208
- let camY = 0;
31209
- if (followCamera) {
31210
- camX = Math.max(0, Math.min(resolvedPlayer.x - canvasWidth / 2, worldWidth - canvasWidth));
31211
- camY = Math.max(0, Math.min(resolvedPlayer.y - canvasHeight / 2 - 50, worldHeight - canvasHeight));
31212
- }
31213
- const bgImg = backgroundImage ? loadImage(backgroundImage) : null;
31214
- if (bgImg) {
31215
- ctx.drawImage(bgImg, 0, 0, canvasWidth, canvasHeight);
31216
- } else if (bgColor) {
31217
- ctx.fillStyle = bgColor;
31218
- ctx.fillRect(0, 0, canvasWidth, canvasHeight);
31219
- } else {
31220
- const grad = ctx.createLinearGradient(0, 0, 0, canvasHeight);
31221
- grad.addColorStop(0, SKY_GRADIENT_TOP);
31222
- grad.addColorStop(1, SKY_GRADIENT_BOTTOM);
31223
- ctx.fillStyle = grad;
31224
- ctx.fillRect(0, 0, canvasWidth, canvasHeight);
31225
- }
31226
- ctx.strokeStyle = GRID_COLOR;
31227
- ctx.lineWidth = 1;
31228
- const gridSize = 32;
31229
- for (let gx = -camX % gridSize; gx < canvasWidth; gx += gridSize) {
31230
- ctx.beginPath();
31231
- ctx.moveTo(gx, 0);
31232
- ctx.lineTo(gx, canvasHeight);
31233
- ctx.stroke();
31234
- }
31235
- for (let gy = -camY % gridSize; gy < canvasHeight; gy += gridSize) {
31236
- ctx.beginPath();
31237
- ctx.moveTo(0, gy);
31238
- ctx.lineTo(canvasWidth, gy);
31239
- ctx.stroke();
31240
- }
31241
- for (const plat of platforms) {
31242
- const px = plat.x - camX;
31243
- const py = plat.y - camY;
31244
- const platType = plat.type ?? "ground";
31245
- const spriteUrl = tileSprites?.[platType];
31246
- const tileImg = spriteUrl ? loadImage(spriteUrl) : null;
31247
- if (tileImg) {
31248
- const tileW = tileImg.naturalWidth;
31249
- const tileH = tileImg.naturalHeight;
31250
- const scaleH = plat.height / tileH;
31251
- const scaledW = tileW * scaleH;
31252
- for (let tx = 0; tx < plat.width; tx += scaledW) {
31253
- const drawW = Math.min(scaledW, plat.width - tx);
31254
- const srcW = drawW / scaleH;
31255
- ctx.drawImage(tileImg, 0, 0, srcW, tileH, px + tx, py, drawW, plat.height);
31256
- }
31314
+ }, [canvasWidth, canvasHeight]);
31315
+ React91.useEffect(() => {
31316
+ const drawFrame = (positions) => {
31317
+ const canvas = canvasRef.current;
31318
+ if (!canvas) return;
31319
+ const ctx = canvas.getContext("2d");
31320
+ if (!ctx) return;
31321
+ const {
31322
+ platforms: plats,
31323
+ worldWidth: ww,
31324
+ worldHeight: wh,
31325
+ canvasWidth: cw,
31326
+ canvasHeight: ch,
31327
+ followCamera: fc,
31328
+ bgColor: bg,
31329
+ playerSprite: pSprite,
31330
+ tileSprites: tSprites,
31331
+ backgroundImage: bgImg
31332
+ } = propsRef.current;
31333
+ const auth = playerRef.current;
31334
+ const interped = positions.get("player");
31335
+ const px = interped?.x ?? auth.x;
31336
+ const py = interped?.y ?? auth.y;
31337
+ let camX = 0;
31338
+ let camY = 0;
31339
+ if (fc) {
31340
+ camX = Math.max(0, Math.min(px - cw / 2, ww - cw));
31341
+ camY = Math.max(0, Math.min(py - ch / 2 - 50, wh - ch));
31342
+ }
31343
+ const bgImage = bgImg ? loadImage(bgImg) : null;
31344
+ if (bgImage) {
31345
+ ctx.drawImage(bgImage, 0, 0, cw, ch);
31346
+ } else if (bg) {
31347
+ ctx.fillStyle = bg;
31348
+ ctx.fillRect(0, 0, cw, ch);
31257
31349
  } else {
31258
- const color = PLATFORM_COLORS[platType] ?? PLATFORM_COLORS.ground;
31259
- ctx.fillStyle = color;
31260
- ctx.fillRect(px, py, plat.width, plat.height);
31261
- ctx.fillStyle = "rgba(255, 255, 255, 0.15)";
31262
- ctx.fillRect(px, py, plat.width, 3);
31263
- ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
31264
- ctx.fillRect(px, py + plat.height - 2, plat.width, 2);
31265
- if (platType === "hazard") {
31266
- ctx.strokeStyle = "#e74c3c";
31267
- ctx.lineWidth = 2;
31268
- for (let sx = px; sx < px + plat.width; sx += 12) {
31350
+ const grad = ctx.createLinearGradient(0, 0, 0, ch);
31351
+ grad.addColorStop(0, SKY_GRADIENT_TOP);
31352
+ grad.addColorStop(1, SKY_GRADIENT_BOTTOM);
31353
+ ctx.fillStyle = grad;
31354
+ ctx.fillRect(0, 0, cw, ch);
31355
+ }
31356
+ ctx.strokeStyle = GRID_COLOR;
31357
+ ctx.lineWidth = 1;
31358
+ const gridSize = 32;
31359
+ for (let gx = -camX % gridSize; gx < cw; gx += gridSize) {
31360
+ ctx.beginPath();
31361
+ ctx.moveTo(gx, 0);
31362
+ ctx.lineTo(gx, ch);
31363
+ ctx.stroke();
31364
+ }
31365
+ for (let gy = -camY % gridSize; gy < ch; gy += gridSize) {
31366
+ ctx.beginPath();
31367
+ ctx.moveTo(0, gy);
31368
+ ctx.lineTo(cw, gy);
31369
+ ctx.stroke();
31370
+ }
31371
+ for (const plat of plats) {
31372
+ const platX = plat.x - camX;
31373
+ const platY = plat.y - camY;
31374
+ const platType = plat.type ?? "ground";
31375
+ const spriteUrl = tSprites?.[platType];
31376
+ const tileImg = spriteUrl ? loadImage(spriteUrl) : null;
31377
+ if (tileImg) {
31378
+ const tileW = tileImg.naturalWidth;
31379
+ const tileH = tileImg.naturalHeight;
31380
+ const scaleH = plat.height / tileH;
31381
+ const scaledW = tileW * scaleH;
31382
+ for (let tx = 0; tx < plat.width; tx += scaledW) {
31383
+ const drawW = Math.min(scaledW, plat.width - tx);
31384
+ const srcW = drawW / scaleH;
31385
+ ctx.drawImage(tileImg, 0, 0, srcW, tileH, platX + tx, platY, drawW, plat.height);
31386
+ }
31387
+ } else {
31388
+ const color = PLATFORM_COLORS[platType] ?? PLATFORM_COLORS.ground;
31389
+ ctx.fillStyle = color;
31390
+ ctx.fillRect(platX, platY, plat.width, plat.height);
31391
+ ctx.fillStyle = "rgba(255, 255, 255, 0.15)";
31392
+ ctx.fillRect(platX, platY, plat.width, 3);
31393
+ ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
31394
+ ctx.fillRect(platX, platY + plat.height - 2, plat.width, 2);
31395
+ if (platType === "hazard") {
31396
+ ctx.strokeStyle = "#e74c3c";
31397
+ ctx.lineWidth = 2;
31398
+ for (let sx = platX; sx < platX + plat.width; sx += 12) {
31399
+ ctx.beginPath();
31400
+ ctx.moveTo(sx, platY);
31401
+ ctx.lineTo(sx + 6, platY + plat.height);
31402
+ ctx.stroke();
31403
+ }
31404
+ }
31405
+ if (platType === "goal") {
31406
+ ctx.fillStyle = "rgba(241, 196, 15, 0.5)";
31269
31407
  ctx.beginPath();
31270
- ctx.moveTo(sx, py);
31271
- ctx.lineTo(sx + 6, py + plat.height);
31272
- ctx.stroke();
31408
+ ctx.arc(platX + plat.width / 2, platY - 10, 8, 0, Math.PI * 2);
31409
+ ctx.fill();
31273
31410
  }
31274
31411
  }
31275
- if (platType === "goal") {
31276
- ctx.fillStyle = "rgba(241, 196, 15, 0.5)";
31277
- ctx.beginPath();
31278
- ctx.arc(px + plat.width / 2, py - 10, 8, 0, Math.PI * 2);
31279
- ctx.fill();
31280
- }
31281
31412
  }
31282
- }
31283
- const pw = resolvedPlayer.width ?? 24;
31284
- const ph = resolvedPlayer.height ?? 32;
31285
- const ppx = resolvedPlayer.x - camX;
31286
- const ppy = resolvedPlayer.y - camY;
31287
- const facingRight = resolvedPlayer.facingRight ?? true;
31288
- const playerImg = playerSprite ? loadImage(playerSprite) : null;
31289
- if (playerImg) {
31290
- ctx.save();
31291
- if (!facingRight) {
31292
- ctx.translate(ppx + pw, ppy);
31293
- ctx.scale(-1, 1);
31294
- ctx.drawImage(playerImg, 0, 0, pw, ph);
31413
+ const pw = auth.width ?? 24;
31414
+ const ph = auth.height ?? 32;
31415
+ const ppx = px - camX;
31416
+ const ppy = py - camY;
31417
+ const facingRight = auth.facingRight ?? true;
31418
+ const playerImg = pSprite ? loadImage(pSprite) : null;
31419
+ if (playerImg) {
31420
+ ctx.save();
31421
+ if (!facingRight) {
31422
+ ctx.translate(ppx + pw, ppy);
31423
+ ctx.scale(-1, 1);
31424
+ ctx.drawImage(playerImg, 0, 0, pw, ph);
31425
+ } else {
31426
+ ctx.drawImage(playerImg, ppx, ppy, pw, ph);
31427
+ }
31428
+ ctx.restore();
31295
31429
  } else {
31296
- ctx.drawImage(playerImg, ppx, ppy, pw, ph);
31430
+ ctx.fillStyle = PLAYER_COLOR;
31431
+ const radius = Math.min(pw, ph) * 0.25;
31432
+ ctx.beginPath();
31433
+ ctx.moveTo(ppx + radius, ppy);
31434
+ ctx.lineTo(ppx + pw - radius, ppy);
31435
+ ctx.quadraticCurveTo(ppx + pw, ppy, ppx + pw, ppy + radius);
31436
+ ctx.lineTo(ppx + pw, ppy + ph - radius);
31437
+ ctx.quadraticCurveTo(ppx + pw, ppy + ph, ppx + pw - radius, ppy + ph);
31438
+ ctx.lineTo(ppx + radius, ppy + ph);
31439
+ ctx.quadraticCurveTo(ppx, ppy + ph, ppx, ppy + ph - radius);
31440
+ ctx.lineTo(ppx, ppy + radius);
31441
+ ctx.quadraticCurveTo(ppx, ppy, ppx + radius, ppy);
31442
+ ctx.fill();
31443
+ const eyeY = ppy + ph * 0.3;
31444
+ const eyeSize = 3;
31445
+ const eyeOffsetX = facingRight ? pw * 0.55 : pw * 0.2;
31446
+ ctx.fillStyle = PLAYER_EYE_COLOR;
31447
+ ctx.beginPath();
31448
+ ctx.arc(ppx + eyeOffsetX, eyeY, eyeSize, 0, Math.PI * 2);
31449
+ ctx.fill();
31450
+ ctx.beginPath();
31451
+ ctx.arc(ppx + eyeOffsetX + 7, eyeY, eyeSize, 0, Math.PI * 2);
31452
+ ctx.fill();
31297
31453
  }
31298
- ctx.restore();
31299
- } else {
31300
- ctx.fillStyle = PLAYER_COLOR;
31301
- const radius = Math.min(pw, ph) * 0.25;
31302
- ctx.beginPath();
31303
- ctx.moveTo(ppx + radius, ppy);
31304
- ctx.lineTo(ppx + pw - radius, ppy);
31305
- ctx.quadraticCurveTo(ppx + pw, ppy, ppx + pw, ppy + radius);
31306
- ctx.lineTo(ppx + pw, ppy + ph - radius);
31307
- ctx.quadraticCurveTo(ppx + pw, ppy + ph, ppx + pw - radius, ppy + ph);
31308
- ctx.lineTo(ppx + radius, ppy + ph);
31309
- ctx.quadraticCurveTo(ppx, ppy + ph, ppx, ppy + ph - radius);
31310
- ctx.lineTo(ppx, ppy + radius);
31311
- ctx.quadraticCurveTo(ppx, ppy, ppx + radius, ppy);
31312
- ctx.fill();
31313
- const eyeY = ppy + ph * 0.3;
31314
- const eyeSize = 3;
31315
- const eyeOffsetX = facingRight ? pw * 0.55 : pw * 0.2;
31316
- ctx.fillStyle = PLAYER_EYE_COLOR;
31317
- ctx.beginPath();
31318
- ctx.arc(ppx + eyeOffsetX, eyeY, eyeSize, 0, Math.PI * 2);
31319
- ctx.fill();
31320
- ctx.beginPath();
31321
- ctx.arc(ppx + eyeOffsetX + 7, eyeY, eyeSize, 0, Math.PI * 2);
31322
- ctx.fill();
31323
- }
31324
- }, [player, platforms, worldWidth, worldHeight, canvasWidth, canvasHeight, followCamera, bgColor, playerSprite, tileSprites, backgroundImage, assetBaseUrl, loadedImages]);
31454
+ };
31455
+ return interp.startLoop(drawFrame);
31456
+ }, [interp.startLoop, loadImage]);
31325
31457
  return /* @__PURE__ */ jsxRuntime.jsx(
31326
31458
  "canvas",
31327
31459
  {
@@ -31339,6 +31471,7 @@ var init_PlatformerCanvas = __esm({
31339
31471
  init_cn();
31340
31472
  init_useEventBus();
31341
31473
  init_verificationRegistry();
31474
+ init_useRenderInterpolation();
31342
31475
  PLATFORM_COLORS = {
31343
31476
  ground: "#4a7c59",
31344
31477
  platform: "#7c6b4a",
@@ -31721,7 +31854,7 @@ var init_MapView = __esm({
31721
31854
  shadowSize: [41, 41]
31722
31855
  });
31723
31856
  L.Marker.prototype.options.icon = defaultIcon;
31724
- const { useEffect: useEffect78, useRef: useRef70, useCallback: useCallback117, useState: useState115 } = React91__namespace.default;
31857
+ const { useEffect: useEffect78, useRef: useRef70, useCallback: useCallback118, useState: useState115 } = React91__namespace.default;
31725
31858
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
31726
31859
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
31727
31860
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -31767,7 +31900,7 @@ var init_MapView = __esm({
31767
31900
  }) {
31768
31901
  const eventBus = useEventBus3();
31769
31902
  const [clickedPosition, setClickedPosition] = useState115(null);
31770
- const handleMapClick = useCallback117((lat, lng) => {
31903
+ const handleMapClick = useCallback118((lat, lng) => {
31771
31904
  if (showClickedPin) {
31772
31905
  setClickedPosition({ lat, lng });
31773
31906
  }
@@ -31776,7 +31909,7 @@ var init_MapView = __esm({
31776
31909
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
31777
31910
  }
31778
31911
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
31779
- const handleMarkerClick = useCallback117((marker) => {
31912
+ const handleMarkerClick = useCallback118((marker) => {
31780
31913
  onMarkerClick?.(marker);
31781
31914
  if (markerClickEvent) {
31782
31915
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -36775,7 +36908,7 @@ var init_RichBlockEditor = __esm({
36775
36908
  "border-b border-border bg-muted/30 px-2 py-2"
36776
36909
  ),
36777
36910
  children: TOOLBAR_ENTRIES.map((entry) => {
36778
- const Icon3 = entry.icon;
36911
+ const Icon2 = entry.icon;
36779
36912
  const entryLabel = t(entry.labelKey);
36780
36913
  return /* @__PURE__ */ jsxRuntime.jsxs(
36781
36914
  Button,
@@ -36787,7 +36920,7 @@ var init_RichBlockEditor = __esm({
36787
36920
  title: entryLabel,
36788
36921
  onClick: () => handleAppend(entry.type),
36789
36922
  children: [
36790
- /* @__PURE__ */ jsxRuntime.jsx(Icon3, { size: 14 }),
36923
+ /* @__PURE__ */ jsxRuntime.jsx(Icon2, { size: 14 }),
36791
36924
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", className: "ml-1 hidden text-xs sm:inline", children: entryLabel })
36792
36925
  ]
36793
36926
  },
@@ -47239,11 +47372,13 @@ function SimulationCanvas({
47239
47372
  height = 400,
47240
47373
  running,
47241
47374
  speed = 1,
47375
+ bodies: externalBodies,
47242
47376
  className
47243
47377
  }) {
47244
47378
  const preset = React91.useMemo(() => resolvePreset(presetProp), [presetProp]);
47245
47379
  const canvasRef = React91.useRef(null);
47246
47380
  const bodiesRef = React91.useRef(structuredClone(preset.bodies));
47381
+ const interp = useRenderInterpolation();
47247
47382
  React91.useEffect(() => {
47248
47383
  bodiesRef.current = structuredClone(preset.bodies);
47249
47384
  }, [preset]);
@@ -47341,6 +47476,67 @@ function SimulationCanvas({
47341
47476
  }
47342
47477
  }, [width, height, preset]);
47343
47478
  React91.useEffect(() => {
47479
+ if (!externalBodies) return;
47480
+ interp.onSnapshot(externalBodies.map((b) => ({ id: b.id, x: b.x, y: b.y })));
47481
+ }, [externalBodies]);
47482
+ const presetRef = React91.useRef(preset);
47483
+ presetRef.current = preset;
47484
+ const widthRef = React91.useRef(width);
47485
+ widthRef.current = width;
47486
+ const heightRef = React91.useRef(height);
47487
+ heightRef.current = height;
47488
+ React91.useEffect(() => {
47489
+ if (!externalBodies) return;
47490
+ const drawInterpolated = (positions) => {
47491
+ const canvas = canvasRef.current;
47492
+ if (!canvas) return;
47493
+ const ctx = canvas.getContext("2d");
47494
+ if (!ctx) return;
47495
+ const bodies = bodiesRef.current;
47496
+ const p2 = presetRef.current;
47497
+ const w = widthRef.current;
47498
+ const h = heightRef.current;
47499
+ ctx.clearRect(0, 0, w, h);
47500
+ ctx.fillStyle = p2.backgroundColor ?? "#1a1a2e";
47501
+ ctx.fillRect(0, 0, w, h);
47502
+ if (p2.constraints) {
47503
+ for (const c of p2.constraints) {
47504
+ const a = bodies[c.bodyA];
47505
+ const b = bodies[c.bodyB];
47506
+ if (a && b) {
47507
+ const aPos = positions.get(a.id) ?? a;
47508
+ const bPos = positions.get(b.id) ?? b;
47509
+ ctx.beginPath();
47510
+ ctx.moveTo(aPos.x, aPos.y);
47511
+ ctx.lineTo(bPos.x, bPos.y);
47512
+ ctx.strokeStyle = "#533483";
47513
+ ctx.lineWidth = 1;
47514
+ ctx.setLineDash([4, 4]);
47515
+ ctx.stroke();
47516
+ ctx.setLineDash([]);
47517
+ }
47518
+ }
47519
+ }
47520
+ for (const body of bodies) {
47521
+ const pos = positions.get(body.id) ?? body;
47522
+ ctx.beginPath();
47523
+ ctx.arc(pos.x, pos.y, body.radius, 0, Math.PI * 2);
47524
+ ctx.fillStyle = body.color ?? "#e94560";
47525
+ ctx.fill();
47526
+ if (p2.showVelocity) {
47527
+ ctx.beginPath();
47528
+ ctx.moveTo(pos.x, pos.y);
47529
+ ctx.lineTo(pos.x + body.vx * 0.1, pos.y + body.vy * 0.1);
47530
+ ctx.strokeStyle = "#16213e";
47531
+ ctx.lineWidth = 2;
47532
+ ctx.stroke();
47533
+ }
47534
+ }
47535
+ };
47536
+ return interp.startLoop(drawInterpolated);
47537
+ }, [externalBodies !== void 0, interp.startLoop]);
47538
+ React91.useEffect(() => {
47539
+ if (externalBodies !== void 0) return;
47344
47540
  if (!running) return;
47345
47541
  let raf;
47346
47542
  const loop = () => {
@@ -47350,10 +47546,11 @@ function SimulationCanvas({
47350
47546
  };
47351
47547
  raf = requestAnimationFrame(loop);
47352
47548
  return () => cancelAnimationFrame(raf);
47353
- }, [running, step, draw]);
47549
+ }, [running, step, draw, externalBodies]);
47354
47550
  React91.useEffect(() => {
47551
+ if (externalBodies !== void 0) return;
47355
47552
  draw();
47356
- }, [draw]);
47553
+ }, [draw, externalBodies]);
47357
47554
  React91.useEffect(() => {
47358
47555
  if (typeof window === "undefined") return;
47359
47556
  const canvas = canvasRef.current;
@@ -47379,136 +47576,10 @@ var init_SimulationCanvas = __esm({
47379
47576
  init_atoms2();
47380
47577
  init_verificationRegistry();
47381
47578
  init_presets();
47579
+ init_useRenderInterpolation();
47382
47580
  SimulationCanvas.displayName = "SimulationCanvas";
47383
47581
  }
47384
47582
  });
47385
- function SimulationControls({
47386
- running,
47387
- speed,
47388
- parameters,
47389
- onPlay,
47390
- onPause,
47391
- onStep,
47392
- onReset,
47393
- onSpeedChange,
47394
- onParameterChange,
47395
- className
47396
- }) {
47397
- return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className, children: [
47398
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", children: [
47399
- running ? /* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", variant: "secondary", onClick: onPause, icon: LucideIcons2.Pause, children: "Pause" }) : /* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", variant: "primary", onClick: onPlay, icon: LucideIcons2.Play, children: "Play" }),
47400
- /* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", variant: "ghost", onClick: onStep, icon: LucideIcons2.SkipForward, disabled: running, children: "Step" }),
47401
- /* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", variant: "ghost", onClick: onReset, icon: LucideIcons2.RotateCcw, children: "Reset" })
47402
- ] }),
47403
- /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
47404
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", color: "muted", children: [
47405
- "Speed: ",
47406
- speed.toFixed(1),
47407
- "x"
47408
- ] }),
47409
- /* @__PURE__ */ jsxRuntime.jsx(
47410
- "input",
47411
- {
47412
- type: "range",
47413
- min: 0.1,
47414
- max: 5,
47415
- step: 0.1,
47416
- value: speed,
47417
- onChange: (e) => onSpeedChange(parseFloat(e.target.value)),
47418
- className: "w-full"
47419
- }
47420
- )
47421
- ] }),
47422
- Object.entries(parameters).map(([name, param]) => /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
47423
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", color: "muted", children: [
47424
- param.label,
47425
- ": ",
47426
- param.value.toFixed(2)
47427
- ] }),
47428
- /* @__PURE__ */ jsxRuntime.jsx(
47429
- "input",
47430
- {
47431
- type: "range",
47432
- min: param.min,
47433
- max: param.max,
47434
- step: param.step,
47435
- value: param.value,
47436
- onChange: (e) => onParameterChange(name, parseFloat(e.target.value)),
47437
- className: "w-full"
47438
- }
47439
- )
47440
- ] }, name))
47441
- ] });
47442
- }
47443
- var init_SimulationControls = __esm({
47444
- "components/game/organisms/physics-sim/SimulationControls.tsx"() {
47445
- init_atoms2();
47446
- SimulationControls.displayName = "SimulationControls";
47447
- }
47448
- });
47449
- function SimulationGraph({
47450
- label,
47451
- unit,
47452
- data,
47453
- maxPoints = 200,
47454
- width = 300,
47455
- height = 120,
47456
- color = "#e94560",
47457
- className
47458
- }) {
47459
- const canvasRef = React91.useRef(null);
47460
- const visibleData = data.slice(-maxPoints);
47461
- React91.useEffect(() => {
47462
- const canvas = canvasRef.current;
47463
- if (!canvas || visibleData.length < 2) return;
47464
- const ctx = canvas.getContext("2d");
47465
- if (!ctx) return;
47466
- ctx.clearRect(0, 0, width, height);
47467
- ctx.fillStyle = "#0f0f23";
47468
- ctx.fillRect(0, 0, width, height);
47469
- ctx.strokeStyle = "#1a1a3e";
47470
- ctx.lineWidth = 0.5;
47471
- for (let i = 0; i < 5; i++) {
47472
- const y = height / 5 * i;
47473
- ctx.beginPath();
47474
- ctx.moveTo(0, y);
47475
- ctx.lineTo(width, y);
47476
- ctx.stroke();
47477
- }
47478
- let minVal = Infinity;
47479
- let maxVal = -Infinity;
47480
- for (const pt of visibleData) {
47481
- if (pt.value < minVal) minVal = pt.value;
47482
- if (pt.value > maxVal) maxVal = pt.value;
47483
- }
47484
- const range = maxVal - minVal || 1;
47485
- const pad = height * 0.1;
47486
- ctx.beginPath();
47487
- ctx.strokeStyle = color;
47488
- ctx.lineWidth = 2;
47489
- for (let i = 0; i < visibleData.length; i++) {
47490
- const x = i / (maxPoints - 1) * width;
47491
- const y = pad + (maxVal - visibleData[i].value) / range * (height - 2 * pad);
47492
- if (i === 0) ctx.moveTo(x, y);
47493
- else ctx.lineTo(x, y);
47494
- }
47495
- ctx.stroke();
47496
- const last = visibleData[visibleData.length - 1];
47497
- ctx.fillStyle = color;
47498
- ctx.font = "12px monospace";
47499
- ctx.fillText(`${last.value.toFixed(2)} ${unit}`, width - 80, 16);
47500
- }, [visibleData, width, height, color, unit, maxPoints]);
47501
- return /* @__PURE__ */ jsxRuntime.jsx(Card, { padding: "sm", className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
47502
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", weight: "bold", children: label }),
47503
- /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, width, height, className: "rounded" })
47504
- ] }) });
47505
- }
47506
- var init_SimulationGraph = __esm({
47507
- "components/game/organisms/physics-sim/SimulationGraph.tsx"() {
47508
- init_atoms2();
47509
- SimulationGraph.displayName = "SimulationGraph";
47510
- }
47511
- });
47512
47583
  function SimulatorBoard({
47513
47584
  entity,
47514
47585
  completeEvent = "PUZZLE_COMPLETE",
@@ -47779,7 +47850,7 @@ var init_StatCard = __esm({
47779
47850
  isLoading: externalLoading,
47780
47851
  error: externalError
47781
47852
  }) => {
47782
- const Icon3 = typeof iconProp === "string" ? resolveIcon(iconProp) ?? void 0 : iconProp;
47853
+ const Icon2 = typeof iconProp === "string" ? resolveIcon(iconProp) ?? void 0 : iconProp;
47783
47854
  const labelToUse = propLabel ?? propTitle;
47784
47855
  const eventBus = useEventBus();
47785
47856
  const { t } = hooks.useTranslate();
@@ -47922,7 +47993,7 @@ var init_StatCard = __esm({
47922
47993
  subtitle && !calculatedTrend && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: subtitle })
47923
47994
  ] }),
47924
47995
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", align: "end", children: [
47925
- Icon3 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("p-3", iconBg), children: /* @__PURE__ */ jsxRuntime.jsx(Icon3, { className: cn("h-6 w-6", iconColor) }) }),
47996
+ Icon2 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("p-3", iconBg), children: /* @__PURE__ */ jsxRuntime.jsx(Icon2, { className: cn("h-6 w-6", iconColor) }) }),
47926
47997
  sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(Sparkline, { data: sparklineData, color: "auto" })
47927
47998
  ] })
47928
47999
  ] }),
@@ -49725,7 +49796,6 @@ var init_component_registry_generated = __esm({
49725
49796
  init_Navigation();
49726
49797
  init_NegotiatorBoard();
49727
49798
  init_NumberStepper();
49728
- init_ObjectRulePanel();
49729
49799
  init_OptionConstraintGroup();
49730
49800
  init_OrbitalVisualization();
49731
49801
  init_Overlay();
@@ -49756,7 +49826,6 @@ var init_component_registry_generated = __esm({
49756
49826
  init_ResourceBar();
49757
49827
  init_ResourceCounter();
49758
49828
  init_RichBlockEditor();
49759
- init_RuleEditor();
49760
49829
  init_RuntimeDebugger2();
49761
49830
  init_ScaledDiagram();
49762
49831
  init_ScoreBoard();
@@ -49776,8 +49845,6 @@ var init_component_registry_generated = __esm({
49776
49845
  init_SignaturePad();
49777
49846
  init_SimpleGrid();
49778
49847
  init_SimulationCanvas();
49779
- init_SimulationControls();
49780
- init_SimulationGraph();
49781
49848
  init_SimulatorBoard();
49782
49849
  init_Skeleton();
49783
49850
  init_SocialProof();
@@ -49795,7 +49862,6 @@ var init_component_registry_generated = __esm({
49795
49862
  init_StateArchitectBoard();
49796
49863
  init_StateIndicator();
49797
49864
  init_StateMachineView();
49798
- init_StateNode();
49799
49865
  init_StatsGrid();
49800
49866
  init_StatsOrganism();
49801
49867
  init_StatusDot();
@@ -49834,8 +49900,6 @@ var init_component_registry_generated = __esm({
49834
49900
  init_Tooltip();
49835
49901
  init_TraitFrame();
49836
49902
  init_TraitSlot();
49837
- init_TraitStateViewer();
49838
- init_TransitionArrow();
49839
49903
  init_TrendIndicator();
49840
49904
  init_TurnIndicator();
49841
49905
  init_TurnPanel();
@@ -49845,7 +49909,6 @@ var init_component_registry_generated = __esm({
49845
49909
  init_UncontrolledBattleBoard();
49846
49910
  init_UnitCommandBar();
49847
49911
  init_UploadDropZone();
49848
- init_VariablePanel();
49849
49912
  init_VersionDiff();
49850
49913
  init_ViolationAlert();
49851
49914
  init_VoteStack();
@@ -50055,7 +50118,6 @@ var init_component_registry_generated = __esm({
50055
50118
  "Navigation": Navigation,
50056
50119
  "NegotiatorBoard": NegotiatorBoard,
50057
50120
  "NumberStepper": NumberStepper,
50058
- "ObjectRulePanel": ObjectRulePanel,
50059
50121
  "OptionConstraintGroup": OptionConstraintGroup,
50060
50122
  "OrbitalVisualization": OrbitalVisualization,
50061
50123
  "Overlay": Overlay,
@@ -50086,7 +50148,6 @@ var init_component_registry_generated = __esm({
50086
50148
  "ResourceBar": ResourceBar,
50087
50149
  "ResourceCounter": ResourceCounter,
50088
50150
  "RichBlockEditor": RichBlockEditor,
50089
- "RuleEditor": RuleEditor,
50090
50151
  "RuntimeDebugger": RuntimeDebugger,
50091
50152
  "ScaledDiagram": ScaledDiagram,
50092
50153
  "ScoreBoard": ScoreBoard,
@@ -50106,8 +50167,6 @@ var init_component_registry_generated = __esm({
50106
50167
  "SignaturePad": SignaturePad,
50107
50168
  "SimpleGrid": SimpleGrid,
50108
50169
  "SimulationCanvas": SimulationCanvas,
50109
- "SimulationControls": SimulationControls,
50110
- "SimulationGraph": SimulationGraph,
50111
50170
  "SimulatorBoard": SimulatorBoard,
50112
50171
  "Skeleton": Skeleton,
50113
50172
  "SocialProof": SocialProof,
@@ -50128,7 +50187,6 @@ var init_component_registry_generated = __esm({
50128
50187
  "StateArchitectBoard": StateArchitectBoard,
50129
50188
  "StateIndicator": StateIndicator,
50130
50189
  "StateMachineView": StateMachineView,
50131
- "StateNode": StateNode2,
50132
50190
  "StatsGrid": StatsGrid,
50133
50191
  "StatsOrganism": StatsOrganism,
50134
50192
  "StatusDot": StatusDot,
@@ -50167,8 +50225,6 @@ var init_component_registry_generated = __esm({
50167
50225
  "Tooltip": Tooltip,
50168
50226
  "TraitFrame": TraitFrame,
50169
50227
  "TraitSlot": TraitSlot,
50170
- "TraitStateViewer": TraitStateViewer,
50171
- "TransitionArrow": TransitionArrow,
50172
50228
  "TrendIndicator": TrendIndicator,
50173
50229
  "TurnIndicator": TurnIndicator,
50174
50230
  "TurnPanel": TurnPanel,
@@ -50179,7 +50235,6 @@ var init_component_registry_generated = __esm({
50179
50235
  "UnitCommandBar": UnitCommandBar,
50180
50236
  "UploadDropZone": UploadDropZone,
50181
50237
  "VStack": VStack,
50182
- "VariablePanel": VariablePanel,
50183
50238
  "VersionDiff": VersionDiff,
50184
50239
  "ViolationAlert": ViolationAlert,
50185
50240
  "VoteStack": VoteStack,