@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.
@@ -28176,6 +28176,78 @@ var init_GameOverScreen = __esm({
28176
28176
  GameOverScreen.displayName = "GameOverScreen";
28177
28177
  }
28178
28178
  });
28179
+ function useRenderInterpolation(options = {}) {
28180
+ const tickIntervalMs = options.tickIntervalMs ?? 1e3 / 30;
28181
+ const prevRef = React82.useRef(null);
28182
+ const currRef = React82.useRef(null);
28183
+ const rafRef = React82.useRef(null);
28184
+ const onSnapshot = React82.useCallback((entities) => {
28185
+ prevRef.current = currRef.current;
28186
+ currRef.current = { entities, arrivedAt: performance.now() };
28187
+ }, []);
28188
+ const getInterpolated = React82.useCallback((now2) => {
28189
+ const out = /* @__PURE__ */ new Map();
28190
+ const curr = currRef.current;
28191
+ if (!curr) return out;
28192
+ const prev = prevRef.current;
28193
+ if (!prev) {
28194
+ for (const e of curr.entities) out.set(e.id, { x: e.x, y: e.y });
28195
+ return out;
28196
+ }
28197
+ const rawAlpha = (now2 - curr.arrivedAt) / tickIntervalMs;
28198
+ const alpha = rawAlpha < 0 ? 0 : rawAlpha > 1 ? 1 : rawAlpha;
28199
+ const prevMap = /* @__PURE__ */ new Map();
28200
+ for (const e of prev.entities) prevMap.set(e.id, e);
28201
+ for (const c of curr.entities) {
28202
+ const p2 = prevMap.get(c.id);
28203
+ if (!p2) {
28204
+ out.set(c.id, { x: c.x, y: c.y });
28205
+ } else {
28206
+ out.set(c.id, {
28207
+ x: p2.x + (c.x - p2.x) * alpha,
28208
+ y: p2.y + (c.y - p2.y) * alpha
28209
+ });
28210
+ }
28211
+ }
28212
+ return out;
28213
+ }, [tickIntervalMs]);
28214
+ const startLoop = React82.useCallback(
28215
+ (draw) => {
28216
+ let active = true;
28217
+ const loop = () => {
28218
+ if (!active) return;
28219
+ try {
28220
+ draw(getInterpolated(performance.now()));
28221
+ } catch {
28222
+ }
28223
+ rafRef.current = requestAnimationFrame(loop);
28224
+ };
28225
+ rafRef.current = requestAnimationFrame(loop);
28226
+ return () => {
28227
+ active = false;
28228
+ if (rafRef.current !== null) {
28229
+ cancelAnimationFrame(rafRef.current);
28230
+ rafRef.current = null;
28231
+ }
28232
+ };
28233
+ },
28234
+ [getInterpolated]
28235
+ );
28236
+ React82.useEffect(() => {
28237
+ return () => {
28238
+ if (rafRef.current !== null) {
28239
+ cancelAnimationFrame(rafRef.current);
28240
+ rafRef.current = null;
28241
+ }
28242
+ };
28243
+ }, []);
28244
+ return { onSnapshot, getInterpolated, startLoop };
28245
+ }
28246
+ var init_useRenderInterpolation = __esm({
28247
+ "hooks/useRenderInterpolation.ts"() {
28248
+ "use client";
28249
+ }
28250
+ });
28179
28251
  function PlatformerCanvas({
28180
28252
  player,
28181
28253
  platforms = [
@@ -28244,8 +28316,43 @@ function PlatformerCanvas({
28244
28316
  y: 336,
28245
28317
  width: 32,
28246
28318
  height: 48,
28319
+ vx: 0,
28320
+ vy: 0,
28321
+ grounded: true,
28247
28322
  facingRight: true
28248
28323
  };
28324
+ const playerRef = React82.useRef(resolvedPlayer);
28325
+ playerRef.current = resolvedPlayer;
28326
+ const interp = useRenderInterpolation();
28327
+ React82.useEffect(() => {
28328
+ interp.onSnapshot([{ id: "player", x: resolvedPlayer.x, y: resolvedPlayer.y }]);
28329
+ }, [resolvedPlayer.x, resolvedPlayer.y]);
28330
+ const propsRef = React82.useRef({
28331
+ platforms,
28332
+ worldWidth,
28333
+ worldHeight,
28334
+ canvasWidth,
28335
+ canvasHeight,
28336
+ followCamera,
28337
+ bgColor,
28338
+ playerSprite,
28339
+ tileSprites,
28340
+ backgroundImage,
28341
+ assetBaseUrl
28342
+ });
28343
+ propsRef.current = {
28344
+ platforms,
28345
+ worldWidth,
28346
+ worldHeight,
28347
+ canvasWidth,
28348
+ canvasHeight,
28349
+ followCamera,
28350
+ bgColor,
28351
+ playerSprite,
28352
+ tileSprites,
28353
+ backgroundImage,
28354
+ assetBaseUrl
28355
+ };
28249
28356
  const handleKeyDown = React82.useCallback((e) => {
28250
28357
  if (keysRef.current.has(e.code)) return;
28251
28358
  keysRef.current.add(e.code);
@@ -28294,124 +28401,149 @@ function PlatformerCanvas({
28294
28401
  canvas.width = canvasWidth * dpr;
28295
28402
  canvas.height = canvasHeight * dpr;
28296
28403
  ctx.scale(dpr, dpr);
28297
- let camX = 0;
28298
- let camY = 0;
28299
- if (followCamera) {
28300
- camX = Math.max(0, Math.min(resolvedPlayer.x - canvasWidth / 2, worldWidth - canvasWidth));
28301
- camY = Math.max(0, Math.min(resolvedPlayer.y - canvasHeight / 2 - 50, worldHeight - canvasHeight));
28302
- }
28303
- const bgImg = backgroundImage ? loadImage(backgroundImage) : null;
28304
- if (bgImg) {
28305
- ctx.drawImage(bgImg, 0, 0, canvasWidth, canvasHeight);
28306
- } else if (bgColor) {
28307
- ctx.fillStyle = bgColor;
28308
- ctx.fillRect(0, 0, canvasWidth, canvasHeight);
28309
- } else {
28310
- const grad = ctx.createLinearGradient(0, 0, 0, canvasHeight);
28311
- grad.addColorStop(0, SKY_GRADIENT_TOP);
28312
- grad.addColorStop(1, SKY_GRADIENT_BOTTOM);
28313
- ctx.fillStyle = grad;
28314
- ctx.fillRect(0, 0, canvasWidth, canvasHeight);
28315
- }
28316
- ctx.strokeStyle = GRID_COLOR;
28317
- ctx.lineWidth = 1;
28318
- const gridSize = 32;
28319
- for (let gx = -camX % gridSize; gx < canvasWidth; gx += gridSize) {
28320
- ctx.beginPath();
28321
- ctx.moveTo(gx, 0);
28322
- ctx.lineTo(gx, canvasHeight);
28323
- ctx.stroke();
28324
- }
28325
- for (let gy = -camY % gridSize; gy < canvasHeight; gy += gridSize) {
28326
- ctx.beginPath();
28327
- ctx.moveTo(0, gy);
28328
- ctx.lineTo(canvasWidth, gy);
28329
- ctx.stroke();
28330
- }
28331
- for (const plat of platforms) {
28332
- const px = plat.x - camX;
28333
- const py = plat.y - camY;
28334
- const platType = plat.type ?? "ground";
28335
- const spriteUrl = tileSprites?.[platType];
28336
- const tileImg = spriteUrl ? loadImage(spriteUrl) : null;
28337
- if (tileImg) {
28338
- const tileW = tileImg.naturalWidth;
28339
- const tileH = tileImg.naturalHeight;
28340
- const scaleH = plat.height / tileH;
28341
- const scaledW = tileW * scaleH;
28342
- for (let tx = 0; tx < plat.width; tx += scaledW) {
28343
- const drawW = Math.min(scaledW, plat.width - tx);
28344
- const srcW = drawW / scaleH;
28345
- ctx.drawImage(tileImg, 0, 0, srcW, tileH, px + tx, py, drawW, plat.height);
28346
- }
28404
+ }, [canvasWidth, canvasHeight]);
28405
+ React82.useEffect(() => {
28406
+ const drawFrame = (positions) => {
28407
+ const canvas = canvasRef.current;
28408
+ if (!canvas) return;
28409
+ const ctx = canvas.getContext("2d");
28410
+ if (!ctx) return;
28411
+ const {
28412
+ platforms: plats,
28413
+ worldWidth: ww,
28414
+ worldHeight: wh,
28415
+ canvasWidth: cw,
28416
+ canvasHeight: ch,
28417
+ followCamera: fc,
28418
+ bgColor: bg,
28419
+ playerSprite: pSprite,
28420
+ tileSprites: tSprites,
28421
+ backgroundImage: bgImg
28422
+ } = propsRef.current;
28423
+ const auth = playerRef.current;
28424
+ const interped = positions.get("player");
28425
+ const px = interped?.x ?? auth.x;
28426
+ const py = interped?.y ?? auth.y;
28427
+ let camX = 0;
28428
+ let camY = 0;
28429
+ if (fc) {
28430
+ camX = Math.max(0, Math.min(px - cw / 2, ww - cw));
28431
+ camY = Math.max(0, Math.min(py - ch / 2 - 50, wh - ch));
28432
+ }
28433
+ const bgImage = bgImg ? loadImage(bgImg) : null;
28434
+ if (bgImage) {
28435
+ ctx.drawImage(bgImage, 0, 0, cw, ch);
28436
+ } else if (bg) {
28437
+ ctx.fillStyle = bg;
28438
+ ctx.fillRect(0, 0, cw, ch);
28347
28439
  } else {
28348
- const color = PLATFORM_COLORS[platType] ?? PLATFORM_COLORS.ground;
28349
- ctx.fillStyle = color;
28350
- ctx.fillRect(px, py, plat.width, plat.height);
28351
- ctx.fillStyle = "rgba(255, 255, 255, 0.15)";
28352
- ctx.fillRect(px, py, plat.width, 3);
28353
- ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
28354
- ctx.fillRect(px, py + plat.height - 2, plat.width, 2);
28355
- if (platType === "hazard") {
28356
- ctx.strokeStyle = "#e74c3c";
28357
- ctx.lineWidth = 2;
28358
- for (let sx = px; sx < px + plat.width; sx += 12) {
28440
+ const grad = ctx.createLinearGradient(0, 0, 0, ch);
28441
+ grad.addColorStop(0, SKY_GRADIENT_TOP);
28442
+ grad.addColorStop(1, SKY_GRADIENT_BOTTOM);
28443
+ ctx.fillStyle = grad;
28444
+ ctx.fillRect(0, 0, cw, ch);
28445
+ }
28446
+ ctx.strokeStyle = GRID_COLOR;
28447
+ ctx.lineWidth = 1;
28448
+ const gridSize = 32;
28449
+ for (let gx = -camX % gridSize; gx < cw; gx += gridSize) {
28450
+ ctx.beginPath();
28451
+ ctx.moveTo(gx, 0);
28452
+ ctx.lineTo(gx, ch);
28453
+ ctx.stroke();
28454
+ }
28455
+ for (let gy = -camY % gridSize; gy < ch; gy += gridSize) {
28456
+ ctx.beginPath();
28457
+ ctx.moveTo(0, gy);
28458
+ ctx.lineTo(cw, gy);
28459
+ ctx.stroke();
28460
+ }
28461
+ for (const plat of plats) {
28462
+ const platX = plat.x - camX;
28463
+ const platY = plat.y - camY;
28464
+ const platType = plat.type ?? "ground";
28465
+ const spriteUrl = tSprites?.[platType];
28466
+ const tileImg = spriteUrl ? loadImage(spriteUrl) : null;
28467
+ if (tileImg) {
28468
+ const tileW = tileImg.naturalWidth;
28469
+ const tileH = tileImg.naturalHeight;
28470
+ const scaleH = plat.height / tileH;
28471
+ const scaledW = tileW * scaleH;
28472
+ for (let tx = 0; tx < plat.width; tx += scaledW) {
28473
+ const drawW = Math.min(scaledW, plat.width - tx);
28474
+ const srcW = drawW / scaleH;
28475
+ ctx.drawImage(tileImg, 0, 0, srcW, tileH, platX + tx, platY, drawW, plat.height);
28476
+ }
28477
+ } else {
28478
+ const color = PLATFORM_COLORS[platType] ?? PLATFORM_COLORS.ground;
28479
+ ctx.fillStyle = color;
28480
+ ctx.fillRect(platX, platY, plat.width, plat.height);
28481
+ ctx.fillStyle = "rgba(255, 255, 255, 0.15)";
28482
+ ctx.fillRect(platX, platY, plat.width, 3);
28483
+ ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
28484
+ ctx.fillRect(platX, platY + plat.height - 2, plat.width, 2);
28485
+ if (platType === "hazard") {
28486
+ ctx.strokeStyle = "#e74c3c";
28487
+ ctx.lineWidth = 2;
28488
+ for (let sx = platX; sx < platX + plat.width; sx += 12) {
28489
+ ctx.beginPath();
28490
+ ctx.moveTo(sx, platY);
28491
+ ctx.lineTo(sx + 6, platY + plat.height);
28492
+ ctx.stroke();
28493
+ }
28494
+ }
28495
+ if (platType === "goal") {
28496
+ ctx.fillStyle = "rgba(241, 196, 15, 0.5)";
28359
28497
  ctx.beginPath();
28360
- ctx.moveTo(sx, py);
28361
- ctx.lineTo(sx + 6, py + plat.height);
28362
- ctx.stroke();
28498
+ ctx.arc(platX + plat.width / 2, platY - 10, 8, 0, Math.PI * 2);
28499
+ ctx.fill();
28363
28500
  }
28364
28501
  }
28365
- if (platType === "goal") {
28366
- ctx.fillStyle = "rgba(241, 196, 15, 0.5)";
28367
- ctx.beginPath();
28368
- ctx.arc(px + plat.width / 2, py - 10, 8, 0, Math.PI * 2);
28369
- ctx.fill();
28370
- }
28371
28502
  }
28372
- }
28373
- const pw = resolvedPlayer.width ?? 24;
28374
- const ph = resolvedPlayer.height ?? 32;
28375
- const ppx = resolvedPlayer.x - camX;
28376
- const ppy = resolvedPlayer.y - camY;
28377
- const facingRight = resolvedPlayer.facingRight ?? true;
28378
- const playerImg = playerSprite ? loadImage(playerSprite) : null;
28379
- if (playerImg) {
28380
- ctx.save();
28381
- if (!facingRight) {
28382
- ctx.translate(ppx + pw, ppy);
28383
- ctx.scale(-1, 1);
28384
- ctx.drawImage(playerImg, 0, 0, pw, ph);
28503
+ const pw = auth.width ?? 24;
28504
+ const ph = auth.height ?? 32;
28505
+ const ppx = px - camX;
28506
+ const ppy = py - camY;
28507
+ const facingRight = auth.facingRight ?? true;
28508
+ const playerImg = pSprite ? loadImage(pSprite) : null;
28509
+ if (playerImg) {
28510
+ ctx.save();
28511
+ if (!facingRight) {
28512
+ ctx.translate(ppx + pw, ppy);
28513
+ ctx.scale(-1, 1);
28514
+ ctx.drawImage(playerImg, 0, 0, pw, ph);
28515
+ } else {
28516
+ ctx.drawImage(playerImg, ppx, ppy, pw, ph);
28517
+ }
28518
+ ctx.restore();
28385
28519
  } else {
28386
- ctx.drawImage(playerImg, ppx, ppy, pw, ph);
28520
+ ctx.fillStyle = PLAYER_COLOR;
28521
+ const radius = Math.min(pw, ph) * 0.25;
28522
+ ctx.beginPath();
28523
+ ctx.moveTo(ppx + radius, ppy);
28524
+ ctx.lineTo(ppx + pw - radius, ppy);
28525
+ ctx.quadraticCurveTo(ppx + pw, ppy, ppx + pw, ppy + radius);
28526
+ ctx.lineTo(ppx + pw, ppy + ph - radius);
28527
+ ctx.quadraticCurveTo(ppx + pw, ppy + ph, ppx + pw - radius, ppy + ph);
28528
+ ctx.lineTo(ppx + radius, ppy + ph);
28529
+ ctx.quadraticCurveTo(ppx, ppy + ph, ppx, ppy + ph - radius);
28530
+ ctx.lineTo(ppx, ppy + radius);
28531
+ ctx.quadraticCurveTo(ppx, ppy, ppx + radius, ppy);
28532
+ ctx.fill();
28533
+ const eyeY = ppy + ph * 0.3;
28534
+ const eyeSize = 3;
28535
+ const eyeOffsetX = facingRight ? pw * 0.55 : pw * 0.2;
28536
+ ctx.fillStyle = PLAYER_EYE_COLOR;
28537
+ ctx.beginPath();
28538
+ ctx.arc(ppx + eyeOffsetX, eyeY, eyeSize, 0, Math.PI * 2);
28539
+ ctx.fill();
28540
+ ctx.beginPath();
28541
+ ctx.arc(ppx + eyeOffsetX + 7, eyeY, eyeSize, 0, Math.PI * 2);
28542
+ ctx.fill();
28387
28543
  }
28388
- ctx.restore();
28389
- } else {
28390
- ctx.fillStyle = PLAYER_COLOR;
28391
- const radius = Math.min(pw, ph) * 0.25;
28392
- ctx.beginPath();
28393
- ctx.moveTo(ppx + radius, ppy);
28394
- ctx.lineTo(ppx + pw - radius, ppy);
28395
- ctx.quadraticCurveTo(ppx + pw, ppy, ppx + pw, ppy + radius);
28396
- ctx.lineTo(ppx + pw, ppy + ph - radius);
28397
- ctx.quadraticCurveTo(ppx + pw, ppy + ph, ppx + pw - radius, ppy + ph);
28398
- ctx.lineTo(ppx + radius, ppy + ph);
28399
- ctx.quadraticCurveTo(ppx, ppy + ph, ppx, ppy + ph - radius);
28400
- ctx.lineTo(ppx, ppy + radius);
28401
- ctx.quadraticCurveTo(ppx, ppy, ppx + radius, ppy);
28402
- ctx.fill();
28403
- const eyeY = ppy + ph * 0.3;
28404
- const eyeSize = 3;
28405
- const eyeOffsetX = facingRight ? pw * 0.55 : pw * 0.2;
28406
- ctx.fillStyle = PLAYER_EYE_COLOR;
28407
- ctx.beginPath();
28408
- ctx.arc(ppx + eyeOffsetX, eyeY, eyeSize, 0, Math.PI * 2);
28409
- ctx.fill();
28410
- ctx.beginPath();
28411
- ctx.arc(ppx + eyeOffsetX + 7, eyeY, eyeSize, 0, Math.PI * 2);
28412
- ctx.fill();
28413
- }
28414
- }, [player, platforms, worldWidth, worldHeight, canvasWidth, canvasHeight, followCamera, bgColor, playerSprite, tileSprites, backgroundImage, assetBaseUrl, loadedImages]);
28544
+ };
28545
+ return interp.startLoop(drawFrame);
28546
+ }, [interp.startLoop, loadImage]);
28415
28547
  return /* @__PURE__ */ jsxRuntime.jsx(
28416
28548
  "canvas",
28417
28549
  {
@@ -28429,6 +28561,7 @@ var init_PlatformerCanvas = __esm({
28429
28561
  init_cn();
28430
28562
  init_useEventBus();
28431
28563
  init_verificationRegistry();
28564
+ init_useRenderInterpolation();
28432
28565
  PLATFORM_COLORS = {
28433
28566
  ground: "#4a7c59",
28434
28567
  platform: "#7c6b4a",
@@ -28811,7 +28944,7 @@ var init_MapView = __esm({
28811
28944
  shadowSize: [41, 41]
28812
28945
  });
28813
28946
  L.Marker.prototype.options.icon = defaultIcon;
28814
- const { useEffect: useEffect74, useRef: useRef68, useCallback: useCallback111, useState: useState107 } = React82__namespace.default;
28947
+ const { useEffect: useEffect74, useRef: useRef68, useCallback: useCallback112, useState: useState107 } = React82__namespace.default;
28815
28948
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
28816
28949
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
28817
28950
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -28857,7 +28990,7 @@ var init_MapView = __esm({
28857
28990
  }) {
28858
28991
  const eventBus = useEventBus3();
28859
28992
  const [clickedPosition, setClickedPosition] = useState107(null);
28860
- const handleMapClick = useCallback111((lat, lng) => {
28993
+ const handleMapClick = useCallback112((lat, lng) => {
28861
28994
  if (showClickedPin) {
28862
28995
  setClickedPosition({ lat, lng });
28863
28996
  }
@@ -28866,7 +28999,7 @@ var init_MapView = __esm({
28866
28999
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
28867
29000
  }
28868
29001
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
28869
- const handleMarkerClick = useCallback111((marker) => {
29002
+ const handleMarkerClick = useCallback112((marker) => {
28870
29003
  onMarkerClick?.(marker);
28871
29004
  if (markerClickEvent) {
28872
29005
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -33865,7 +33998,7 @@ var init_RichBlockEditor = __esm({
33865
33998
  "border-b border-border bg-muted/30 px-2 py-2"
33866
33999
  ),
33867
34000
  children: TOOLBAR_ENTRIES.map((entry) => {
33868
- const Icon3 = entry.icon;
34001
+ const Icon2 = entry.icon;
33869
34002
  const entryLabel = t(entry.labelKey);
33870
34003
  return /* @__PURE__ */ jsxRuntime.jsxs(
33871
34004
  Button,
@@ -33877,7 +34010,7 @@ var init_RichBlockEditor = __esm({
33877
34010
  title: entryLabel,
33878
34011
  onClick: () => handleAppend(entry.type),
33879
34012
  children: [
33880
- /* @__PURE__ */ jsxRuntime.jsx(Icon3, { size: 14 }),
34013
+ /* @__PURE__ */ jsxRuntime.jsx(Icon2, { size: 14 }),
33881
34014
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", variant: "caption", className: "ml-1 hidden text-xs sm:inline", children: entryLabel })
33882
34015
  ]
33883
34016
  },
@@ -44738,11 +44871,13 @@ function SimulationCanvas({
44738
44871
  height = 400,
44739
44872
  running,
44740
44873
  speed = 1,
44874
+ bodies: externalBodies,
44741
44875
  className
44742
44876
  }) {
44743
44877
  const preset = React82.useMemo(() => resolvePreset(presetProp), [presetProp]);
44744
44878
  const canvasRef = React82.useRef(null);
44745
44879
  const bodiesRef = React82.useRef(structuredClone(preset.bodies));
44880
+ const interp = useRenderInterpolation();
44746
44881
  React82.useEffect(() => {
44747
44882
  bodiesRef.current = structuredClone(preset.bodies);
44748
44883
  }, [preset]);
@@ -44840,6 +44975,67 @@ function SimulationCanvas({
44840
44975
  }
44841
44976
  }, [width, height, preset]);
44842
44977
  React82.useEffect(() => {
44978
+ if (!externalBodies) return;
44979
+ interp.onSnapshot(externalBodies.map((b) => ({ id: b.id, x: b.x, y: b.y })));
44980
+ }, [externalBodies]);
44981
+ const presetRef = React82.useRef(preset);
44982
+ presetRef.current = preset;
44983
+ const widthRef = React82.useRef(width);
44984
+ widthRef.current = width;
44985
+ const heightRef = React82.useRef(height);
44986
+ heightRef.current = height;
44987
+ React82.useEffect(() => {
44988
+ if (!externalBodies) return;
44989
+ const drawInterpolated = (positions) => {
44990
+ const canvas = canvasRef.current;
44991
+ if (!canvas) return;
44992
+ const ctx = canvas.getContext("2d");
44993
+ if (!ctx) return;
44994
+ const bodies = bodiesRef.current;
44995
+ const p2 = presetRef.current;
44996
+ const w = widthRef.current;
44997
+ const h = heightRef.current;
44998
+ ctx.clearRect(0, 0, w, h);
44999
+ ctx.fillStyle = p2.backgroundColor ?? "#1a1a2e";
45000
+ ctx.fillRect(0, 0, w, h);
45001
+ if (p2.constraints) {
45002
+ for (const c of p2.constraints) {
45003
+ const a = bodies[c.bodyA];
45004
+ const b = bodies[c.bodyB];
45005
+ if (a && b) {
45006
+ const aPos = positions.get(a.id) ?? a;
45007
+ const bPos = positions.get(b.id) ?? b;
45008
+ ctx.beginPath();
45009
+ ctx.moveTo(aPos.x, aPos.y);
45010
+ ctx.lineTo(bPos.x, bPos.y);
45011
+ ctx.strokeStyle = "#533483";
45012
+ ctx.lineWidth = 1;
45013
+ ctx.setLineDash([4, 4]);
45014
+ ctx.stroke();
45015
+ ctx.setLineDash([]);
45016
+ }
45017
+ }
45018
+ }
45019
+ for (const body of bodies) {
45020
+ const pos = positions.get(body.id) ?? body;
45021
+ ctx.beginPath();
45022
+ ctx.arc(pos.x, pos.y, body.radius, 0, Math.PI * 2);
45023
+ ctx.fillStyle = body.color ?? "#e94560";
45024
+ ctx.fill();
45025
+ if (p2.showVelocity) {
45026
+ ctx.beginPath();
45027
+ ctx.moveTo(pos.x, pos.y);
45028
+ ctx.lineTo(pos.x + body.vx * 0.1, pos.y + body.vy * 0.1);
45029
+ ctx.strokeStyle = "#16213e";
45030
+ ctx.lineWidth = 2;
45031
+ ctx.stroke();
45032
+ }
45033
+ }
45034
+ };
45035
+ return interp.startLoop(drawInterpolated);
45036
+ }, [externalBodies !== void 0, interp.startLoop]);
45037
+ React82.useEffect(() => {
45038
+ if (externalBodies !== void 0) return;
44843
45039
  if (!running) return;
44844
45040
  let raf;
44845
45041
  const loop = () => {
@@ -44849,10 +45045,11 @@ function SimulationCanvas({
44849
45045
  };
44850
45046
  raf = requestAnimationFrame(loop);
44851
45047
  return () => cancelAnimationFrame(raf);
44852
- }, [running, step, draw]);
45048
+ }, [running, step, draw, externalBodies]);
44853
45049
  React82.useEffect(() => {
45050
+ if (externalBodies !== void 0) return;
44854
45051
  draw();
44855
- }, [draw]);
45052
+ }, [draw, externalBodies]);
44856
45053
  React82.useEffect(() => {
44857
45054
  if (typeof window === "undefined") return;
44858
45055
  const canvas = canvasRef.current;
@@ -44878,136 +45075,10 @@ var init_SimulationCanvas = __esm({
44878
45075
  init_atoms2();
44879
45076
  init_verificationRegistry();
44880
45077
  init_presets();
45078
+ init_useRenderInterpolation();
44881
45079
  SimulationCanvas.displayName = "SimulationCanvas";
44882
45080
  }
44883
45081
  });
44884
- function SimulationControls({
44885
- running,
44886
- speed,
44887
- parameters,
44888
- onPlay,
44889
- onPause,
44890
- onStep,
44891
- onReset,
44892
- onSpeedChange,
44893
- onParameterChange,
44894
- className
44895
- }) {
44896
- return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", className, children: [
44897
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", children: [
44898
- 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" }),
44899
- /* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", variant: "ghost", onClick: onStep, icon: LucideIcons2.SkipForward, disabled: running, children: "Step" }),
44900
- /* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", variant: "ghost", onClick: onReset, icon: LucideIcons2.RotateCcw, children: "Reset" })
44901
- ] }),
44902
- /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
44903
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", color: "muted", children: [
44904
- "Speed: ",
44905
- speed.toFixed(1),
44906
- "x"
44907
- ] }),
44908
- /* @__PURE__ */ jsxRuntime.jsx(
44909
- "input",
44910
- {
44911
- type: "range",
44912
- min: 0.1,
44913
- max: 5,
44914
- step: 0.1,
44915
- value: speed,
44916
- onChange: (e) => onSpeedChange(parseFloat(e.target.value)),
44917
- className: "w-full"
44918
- }
44919
- )
44920
- ] }),
44921
- Object.entries(parameters).map(([name, param]) => /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
44922
- /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", color: "muted", children: [
44923
- param.label,
44924
- ": ",
44925
- param.value.toFixed(2)
44926
- ] }),
44927
- /* @__PURE__ */ jsxRuntime.jsx(
44928
- "input",
44929
- {
44930
- type: "range",
44931
- min: param.min,
44932
- max: param.max,
44933
- step: param.step,
44934
- value: param.value,
44935
- onChange: (e) => onParameterChange(name, parseFloat(e.target.value)),
44936
- className: "w-full"
44937
- }
44938
- )
44939
- ] }, name))
44940
- ] });
44941
- }
44942
- var init_SimulationControls = __esm({
44943
- "components/game/organisms/physics-sim/SimulationControls.tsx"() {
44944
- init_atoms2();
44945
- SimulationControls.displayName = "SimulationControls";
44946
- }
44947
- });
44948
- function SimulationGraph({
44949
- label,
44950
- unit,
44951
- data,
44952
- maxPoints = 200,
44953
- width = 300,
44954
- height = 120,
44955
- color = "#e94560",
44956
- className
44957
- }) {
44958
- const canvasRef = React82.useRef(null);
44959
- const visibleData = data.slice(-maxPoints);
44960
- React82.useEffect(() => {
44961
- const canvas = canvasRef.current;
44962
- if (!canvas || visibleData.length < 2) return;
44963
- const ctx = canvas.getContext("2d");
44964
- if (!ctx) return;
44965
- ctx.clearRect(0, 0, width, height);
44966
- ctx.fillStyle = "#0f0f23";
44967
- ctx.fillRect(0, 0, width, height);
44968
- ctx.strokeStyle = "#1a1a3e";
44969
- ctx.lineWidth = 0.5;
44970
- for (let i = 0; i < 5; i++) {
44971
- const y = height / 5 * i;
44972
- ctx.beginPath();
44973
- ctx.moveTo(0, y);
44974
- ctx.lineTo(width, y);
44975
- ctx.stroke();
44976
- }
44977
- let minVal = Infinity;
44978
- let maxVal = -Infinity;
44979
- for (const pt of visibleData) {
44980
- if (pt.value < minVal) minVal = pt.value;
44981
- if (pt.value > maxVal) maxVal = pt.value;
44982
- }
44983
- const range = maxVal - minVal || 1;
44984
- const pad = height * 0.1;
44985
- ctx.beginPath();
44986
- ctx.strokeStyle = color;
44987
- ctx.lineWidth = 2;
44988
- for (let i = 0; i < visibleData.length; i++) {
44989
- const x = i / (maxPoints - 1) * width;
44990
- const y = pad + (maxVal - visibleData[i].value) / range * (height - 2 * pad);
44991
- if (i === 0) ctx.moveTo(x, y);
44992
- else ctx.lineTo(x, y);
44993
- }
44994
- ctx.stroke();
44995
- const last = visibleData[visibleData.length - 1];
44996
- ctx.fillStyle = color;
44997
- ctx.font = "12px monospace";
44998
- ctx.fillText(`${last.value.toFixed(2)} ${unit}`, width - 80, 16);
44999
- }, [visibleData, width, height, color, unit, maxPoints]);
45000
- return /* @__PURE__ */ jsxRuntime.jsx(Card, { padding: "sm", className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
45001
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", weight: "bold", children: label }),
45002
- /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, width, height, className: "rounded" })
45003
- ] }) });
45004
- }
45005
- var init_SimulationGraph = __esm({
45006
- "components/game/organisms/physics-sim/SimulationGraph.tsx"() {
45007
- init_atoms2();
45008
- SimulationGraph.displayName = "SimulationGraph";
45009
- }
45010
- });
45011
45082
  function SimulatorBoard({
45012
45083
  entity,
45013
45084
  completeEvent = "PUZZLE_COMPLETE",
@@ -45278,7 +45349,7 @@ var init_StatCard = __esm({
45278
45349
  isLoading: externalLoading,
45279
45350
  error: externalError
45280
45351
  }) => {
45281
- const Icon3 = typeof iconProp === "string" ? resolveIcon(iconProp) ?? void 0 : iconProp;
45352
+ const Icon2 = typeof iconProp === "string" ? resolveIcon(iconProp) ?? void 0 : iconProp;
45282
45353
  const labelToUse = propLabel ?? propTitle;
45283
45354
  const eventBus = useEventBus();
45284
45355
  const { t } = hooks.useTranslate();
@@ -45421,7 +45492,7 @@ var init_StatCard = __esm({
45421
45492
  subtitle && !calculatedTrend && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: subtitle })
45422
45493
  ] }),
45423
45494
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", align: "end", children: [
45424
- Icon3 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("p-3", iconBg), children: /* @__PURE__ */ jsxRuntime.jsx(Icon3, { className: cn("h-6 w-6", iconColor) }) }),
45495
+ Icon2 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("p-3", iconBg), children: /* @__PURE__ */ jsxRuntime.jsx(Icon2, { className: cn("h-6 w-6", iconColor) }) }),
45425
45496
  sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(Sparkline, { data: sparklineData, color: "auto" })
45426
45497
  ] })
45427
45498
  ] }),
@@ -47224,7 +47295,6 @@ var init_component_registry_generated = __esm({
47224
47295
  init_Navigation();
47225
47296
  init_NegotiatorBoard();
47226
47297
  init_NumberStepper();
47227
- init_ObjectRulePanel();
47228
47298
  init_OptionConstraintGroup();
47229
47299
  init_OrbitalVisualization();
47230
47300
  init_Overlay();
@@ -47255,7 +47325,6 @@ var init_component_registry_generated = __esm({
47255
47325
  init_ResourceBar();
47256
47326
  init_ResourceCounter();
47257
47327
  init_RichBlockEditor();
47258
- init_RuleEditor();
47259
47328
  init_RuntimeDebugger2();
47260
47329
  init_ScaledDiagram();
47261
47330
  init_ScoreBoard();
@@ -47275,8 +47344,6 @@ var init_component_registry_generated = __esm({
47275
47344
  init_SignaturePad();
47276
47345
  init_SimpleGrid();
47277
47346
  init_SimulationCanvas();
47278
- init_SimulationControls();
47279
- init_SimulationGraph();
47280
47347
  init_SimulatorBoard();
47281
47348
  init_Skeleton();
47282
47349
  init_SocialProof();
@@ -47294,7 +47361,6 @@ var init_component_registry_generated = __esm({
47294
47361
  init_StateArchitectBoard();
47295
47362
  init_StateIndicator();
47296
47363
  init_StateMachineView();
47297
- init_StateNode();
47298
47364
  init_StatsGrid();
47299
47365
  init_StatsOrganism();
47300
47366
  init_StatusDot();
@@ -47333,8 +47399,6 @@ var init_component_registry_generated = __esm({
47333
47399
  init_Tooltip();
47334
47400
  init_TraitFrame();
47335
47401
  init_TraitSlot();
47336
- init_TraitStateViewer();
47337
- init_TransitionArrow();
47338
47402
  init_TrendIndicator();
47339
47403
  init_TurnIndicator();
47340
47404
  init_TurnPanel();
@@ -47344,7 +47408,6 @@ var init_component_registry_generated = __esm({
47344
47408
  init_UncontrolledBattleBoard();
47345
47409
  init_UnitCommandBar();
47346
47410
  init_UploadDropZone();
47347
- init_VariablePanel();
47348
47411
  init_VersionDiff();
47349
47412
  init_ViolationAlert();
47350
47413
  init_VoteStack();
@@ -47554,7 +47617,6 @@ var init_component_registry_generated = __esm({
47554
47617
  "Navigation": Navigation,
47555
47618
  "NegotiatorBoard": NegotiatorBoard,
47556
47619
  "NumberStepper": NumberStepper,
47557
- "ObjectRulePanel": ObjectRulePanel,
47558
47620
  "OptionConstraintGroup": OptionConstraintGroup,
47559
47621
  "OrbitalVisualization": OrbitalVisualization,
47560
47622
  "Overlay": Overlay,
@@ -47585,7 +47647,6 @@ var init_component_registry_generated = __esm({
47585
47647
  "ResourceBar": ResourceBar,
47586
47648
  "ResourceCounter": ResourceCounter,
47587
47649
  "RichBlockEditor": RichBlockEditor,
47588
- "RuleEditor": RuleEditor,
47589
47650
  "RuntimeDebugger": RuntimeDebugger,
47590
47651
  "ScaledDiagram": ScaledDiagram,
47591
47652
  "ScoreBoard": ScoreBoard,
@@ -47605,8 +47666,6 @@ var init_component_registry_generated = __esm({
47605
47666
  "SignaturePad": SignaturePad,
47606
47667
  "SimpleGrid": SimpleGrid,
47607
47668
  "SimulationCanvas": SimulationCanvas,
47608
- "SimulationControls": SimulationControls,
47609
- "SimulationGraph": SimulationGraph,
47610
47669
  "SimulatorBoard": SimulatorBoard,
47611
47670
  "Skeleton": Skeleton,
47612
47671
  "SocialProof": SocialProof,
@@ -47627,7 +47686,6 @@ var init_component_registry_generated = __esm({
47627
47686
  "StateArchitectBoard": StateArchitectBoard,
47628
47687
  "StateIndicator": StateIndicator,
47629
47688
  "StateMachineView": StateMachineView,
47630
- "StateNode": StateNode2,
47631
47689
  "StatsGrid": StatsGrid,
47632
47690
  "StatsOrganism": StatsOrganism,
47633
47691
  "StatusDot": StatusDot,
@@ -47666,8 +47724,6 @@ var init_component_registry_generated = __esm({
47666
47724
  "Tooltip": Tooltip,
47667
47725
  "TraitFrame": TraitFrame,
47668
47726
  "TraitSlot": TraitSlot,
47669
- "TraitStateViewer": TraitStateViewer,
47670
- "TransitionArrow": TransitionArrow,
47671
47727
  "TrendIndicator": TrendIndicator,
47672
47728
  "TurnIndicator": TurnIndicator,
47673
47729
  "TurnPanel": TurnPanel,
@@ -47678,7 +47734,6 @@ var init_component_registry_generated = __esm({
47678
47734
  "UnitCommandBar": UnitCommandBar,
47679
47735
  "UploadDropZone": UploadDropZone,
47680
47736
  "VStack": VStack,
47681
- "VariablePanel": VariablePanel,
47682
47737
  "VersionDiff": VersionDiff,
47683
47738
  "ViolationAlert": ViolationAlert,
47684
47739
  "VoteStack": VoteStack,