minimojs 1.0.0-alpha.17 → 1.0.0-alpha.18

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.
@@ -1,12 +1,19 @@
1
1
  /** @internal */
2
2
  export class AnimationSystem {
3
3
  constructor() {
4
+ /** @internal */
4
5
  this._animations = [];
6
+ /** @internal */
5
7
  this._deformAnimations = [];
8
+ /** @internal */
6
9
  this._motionAnimations = [];
10
+ /** @internal */
7
11
  this._shakeAnimations = [];
12
+ /** @internal */
8
13
  this._blinkAnimations = [];
14
+ /** @internal */
9
15
  this._flickerAnimations = [];
16
+ /** @internal */
10
17
  this._renderDataSprites = new Set();
11
18
  }
12
19
  update(dtMs) {
@@ -211,31 +218,37 @@ export class AnimationSystem {
211
218
  this._flickerAnimations = [];
212
219
  this._renderDataSprites.clear();
213
220
  }
221
+ /** @internal */
214
222
  sanitizeScale(value) {
215
223
  if (!Number.isFinite(value))
216
224
  return 1;
217
225
  return Math.max(0, value);
218
226
  }
227
+ /** @internal */
219
228
  sanitizeDuration(value) {
220
229
  if (!Number.isFinite(value))
221
230
  return 1;
222
231
  return Math.max(1, value);
223
232
  }
233
+ /** @internal */
224
234
  sanitizeNonNegative(value) {
225
235
  if (!Number.isFinite(value))
226
236
  return 0;
227
237
  return Math.max(0, value);
228
238
  }
239
+ /** @internal */
229
240
  sanitizePivot(value) {
230
241
  if (!Number.isFinite(value))
231
242
  return 0.5;
232
243
  return Math.max(0, Math.min(1, value));
233
244
  }
245
+ /** @internal */
234
246
  replaceMotionAnimation(entry) {
235
247
  const animations = this._motionAnimations.filter((a) => a.sprite !== entry.sprite);
236
248
  animations.push(entry);
237
249
  this._motionAnimations = animations;
238
250
  }
251
+ /** @internal */
239
252
  rebuildRenderData(dtMs) {
240
253
  const next = new Map();
241
254
  const nextSprites = new Set();
@@ -1,8 +1,11 @@
1
1
  /** @internal */
2
2
  export class AssetSystem {
3
3
  constructor() {
4
+ /** @internal */
4
5
  this._queuedImages = new Map();
6
+ /** @internal */
5
7
  this._loadedImages = new Map();
8
+ /** @internal */
6
9
  this._imageSources = new Map();
7
10
  }
8
11
  queueImage(key, src) {
@@ -51,6 +54,7 @@ export class AssetSystem {
51
54
  }));
52
55
  this._queuedImages.clear();
53
56
  }
57
+ /** @internal */
54
58
  loadImage(src) {
55
59
  return new Promise((resolve, reject) => {
56
60
  const image = new Image();
@@ -1,6 +1,7 @@
1
1
  /** @internal */
2
2
  export class BackgroundSystem {
3
3
  constructor() {
4
+ /** @internal */
4
5
  this._layers = [];
5
6
  }
6
7
  add(layer) {
@@ -1,8 +1,10 @@
1
1
  /** @internal */
2
2
  export class CanvasSystem {
3
- constructor(canvas) {
3
+ constructor(/** @internal */ canvas) {
4
4
  this.canvas = canvas;
5
+ /** @internal */
5
6
  this.onResize = () => this.applyResponsiveCanvasLayout();
7
+ /** @internal */
6
8
  this.onViewportChange = () => this.applyResponsiveCanvasLayout();
7
9
  }
8
10
  initialize() {
@@ -22,6 +24,7 @@ export class CanvasSystem {
22
24
  window.visualViewport?.addEventListener("resize", this.onViewportChange);
23
25
  window.visualViewport?.addEventListener("scroll", this.onViewportChange);
24
26
  }
27
+ /** @internal */
25
28
  applyResponsiveCanvasLayout() {
26
29
  if (!document.body)
27
30
  return;
@@ -45,7 +45,9 @@ const DEFAULT_WIPE_OPTIONS = {
45
45
  /** @internal */
46
46
  export class ExplosionSystem {
47
47
  constructor() {
48
+ /** @internal */
48
49
  this._pieceEffects = [];
50
+ /** @internal */
49
51
  this._wipes = [];
50
52
  }
51
53
  animateExplode(sprite, glyphCanvas, options, onComplete) {
@@ -190,6 +192,7 @@ export class ExplosionSystem {
190
192
  this._pieceEffects = [];
191
193
  this._wipes = [];
192
194
  }
195
+ /** @internal */
193
196
  spawnRadialEffect(mode, sprite, glyphCanvas, context, options, forceHideSprite, restoreVisibility, finalVisible, onComplete) {
194
197
  this.clearSpriteEffects(sprite);
195
198
  if (forceHideSprite || restoreVisibility) {
@@ -241,6 +244,7 @@ export class ExplosionSystem {
241
244
  onComplete,
242
245
  });
243
246
  }
247
+ /** @internal */
244
248
  spawnDirectionalEffect(mode, sprite, glyphCanvas, context, options, forceHideSprite, restoreVisibility, finalVisible, onComplete) {
245
249
  this.clearSpriteEffects(sprite);
246
250
  if (forceHideSprite || restoreVisibility) {
@@ -298,6 +302,7 @@ export class ExplosionSystem {
298
302
  onComplete,
299
303
  });
300
304
  }
305
+ /** @internal */
301
306
  buildPieceGrid(glyphCanvas, context, rows, cols, builder, mode) {
302
307
  const pieces = [];
303
308
  const rotationRad = (context.rotation * Math.PI) / 180;
@@ -362,6 +367,7 @@ export class ExplosionSystem {
362
367
  }
363
368
  return pieces;
364
369
  }
370
+ /** @internal */
365
371
  buildPieceSpriteContext(sprite, glyphCanvas) {
366
372
  const renderData = sprite._renderData;
367
373
  const deformScaleX = this.getSafeScale(renderData?.scaleX);
@@ -392,6 +398,7 @@ export class ExplosionSystem {
392
398
  absScaleY: Math.abs(signedScaleY),
393
399
  };
394
400
  }
401
+ /** @internal */
395
402
  clearSpriteEffects(sprite) {
396
403
  const remainingPieces = [];
397
404
  for (const effect of this._pieceEffects) {
@@ -418,6 +425,7 @@ export class ExplosionSystem {
418
425
  }
419
426
  this._wipes = remainingWipes;
420
427
  }
428
+ /** @internal */
421
429
  getDirectionalProgress(row, col, rows, cols, direction) {
422
430
  const rowProgress = rows <= 1 ? 0 : row / (rows - 1);
423
431
  const colProgress = cols <= 1 ? 0 : col / (cols - 1);
@@ -432,6 +440,7 @@ export class ExplosionSystem {
432
440
  return 1 - rowProgress;
433
441
  }
434
442
  }
443
+ /** @internal */
435
444
  getDirectionVector(direction) {
436
445
  switch (direction) {
437
446
  case "left-to-right":
@@ -444,6 +453,7 @@ export class ExplosionSystem {
444
453
  return { x: 0, y: -1 };
445
454
  }
446
455
  }
456
+ /** @internal */
447
457
  getRadialVelocity(x, y) {
448
458
  const magnitude = Math.hypot(x, y);
449
459
  let dirX = 0;
@@ -460,6 +470,7 @@ export class ExplosionSystem {
460
470
  const jitterAngle = (Math.random() - 0.5) * (Math.PI / 2);
461
471
  return this.rotate(dirX, dirY, jitterAngle);
462
472
  }
473
+ /** @internal */
463
474
  easePieceProgress(mode, t) {
464
475
  const clamped = Math.max(0, Math.min(1, t));
465
476
  switch (mode) {
@@ -471,6 +482,7 @@ export class ExplosionSystem {
471
482
  return clamped * clamped * (3 - 2 * clamped);
472
483
  }
473
484
  }
485
+ /** @internal */
474
486
  rotate(x, y, radians) {
475
487
  const cos = Math.cos(radians);
476
488
  const sin = Math.sin(radians);
@@ -479,39 +491,47 @@ export class ExplosionSystem {
479
491
  y: x * sin + y * cos,
480
492
  };
481
493
  }
494
+ /** @internal */
482
495
  lerp(from, to, t) {
483
496
  return from + (to - from) * t;
484
497
  }
498
+ /** @internal */
485
499
  getSafeScale(value) {
486
500
  if (!Number.isFinite(value))
487
501
  return 1;
488
502
  return Math.max(0, value);
489
503
  }
504
+ /** @internal */
490
505
  getSafePivot(value) {
491
506
  if (!Number.isFinite(value))
492
507
  return 0.5;
493
508
  return Math.max(0, Math.min(1, value));
494
509
  }
510
+ /** @internal */
495
511
  getSafeNumber(value, fallback) {
496
512
  if (!Number.isFinite(value))
497
513
  return fallback;
498
514
  return value;
499
515
  }
516
+ /** @internal */
500
517
  sanitizeCount(value, fallback) {
501
518
  if (!Number.isFinite(value))
502
519
  return fallback;
503
520
  return Math.max(1, Math.round(value));
504
521
  }
522
+ /** @internal */
505
523
  sanitizeDuration(value, fallback) {
506
524
  if (!Number.isFinite(value))
507
525
  return fallback;
508
526
  return Math.max(1, value);
509
527
  }
528
+ /** @internal */
510
529
  sanitizeNonNegative(value, fallback) {
511
530
  if (!Number.isFinite(value))
512
531
  return fallback;
513
532
  return Math.max(0, value);
514
533
  }
534
+ /** @internal */
515
535
  sanitizeNumber(value, fallback) {
516
536
  if (!Number.isFinite(value))
517
537
  return fallback;
@@ -1,19 +1,33 @@
1
1
  /** @internal */
2
2
  export class InputSystem {
3
- constructor(game, canvas) {
3
+ constructor(
4
+ /** @internal */ game,
5
+ /** @internal */ canvas) {
4
6
  this.game = game;
5
7
  this.canvas = canvas;
8
+ /** @internal */
6
9
  this._keysDown = new Set();
10
+ /** @internal */
7
11
  this._keysPressed = new Set();
12
+ /** @internal */
8
13
  this._pointerDown = false;
14
+ /** @internal */
9
15
  this._pointerPressed = false;
16
+ /** @internal */
10
17
  this._pointerX = 0;
18
+ /** @internal */
11
19
  this._pointerY = 0;
20
+ /** @internal */
12
21
  this._mouseDown = false;
22
+ /** @internal */
13
23
  this._mousePressed = false;
24
+ /** @internal */
14
25
  this._mouseX = 0;
26
+ /** @internal */
15
27
  this._mouseY = 0;
28
+ /** @internal */
16
29
  this._touchPointers = new Map();
30
+ /** @internal */
17
31
  this._primaryTouchId = null;
18
32
  }
19
33
  get pointerX() {
@@ -176,6 +190,7 @@ export class InputSystem {
176
190
  resetPressedState() {
177
191
  this.clearFramePressedState();
178
192
  }
193
+ /** @internal */
179
194
  getCanvasPoint(clientX, clientY) {
180
195
  const rect = this.canvas.getBoundingClientRect();
181
196
  const scaleX = this.canvas.width / rect.width;
@@ -185,6 +200,7 @@ export class InputSystem {
185
200
  y: (clientY - rect.top) * scaleY,
186
201
  };
187
202
  }
203
+ /** @internal */
188
204
  isScreenPointOverSprite(x, y, sprite) {
189
205
  const halfWidth = sprite.displayWidth / 2;
190
206
  const halfHeight = sprite.displayHeight / 2;
@@ -192,6 +208,7 @@ export class InputSystem {
192
208
  const drawY = sprite.ignoreScroll ? sprite.renderY : sprite.renderY - this.game.scrollY;
193
209
  return Math.abs(x - drawX) <= halfWidth && Math.abs(y - drawY) <= halfHeight;
194
210
  }
211
+ /** @internal */
195
212
  collectPointers() {
196
213
  const pointers = [];
197
214
  if (this._mouseDown) {
@@ -214,6 +231,7 @@ export class InputSystem {
214
231
  }
215
232
  return pointers;
216
233
  }
234
+ /** @internal */
217
235
  syncPrimaryPointer() {
218
236
  if (this._primaryTouchId !== null) {
219
237
  const primaryTouch = this._touchPointers.get(this._primaryTouchId);
@@ -1,12 +1,18 @@
1
1
  /** @internal */
2
2
  export class LoopSystem {
3
- constructor(onFrame) {
3
+ constructor(/** @internal */ onFrame) {
4
4
  this.onFrame = onFrame;
5
+ /** @internal */
5
6
  this._rafId = null;
7
+ /** @internal */
6
8
  this._lastTimestamp = null;
9
+ /** @internal */
7
10
  this._running = false;
11
+ /** @internal */
8
12
  this._hasCreated = false;
13
+ /** @internal */
9
14
  this._onCreate = null;
15
+ /** @internal */
10
16
  this.loop = (timestamp) => {
11
17
  if (!this._running)
12
18
  return;
@@ -1,8 +1,11 @@
1
1
  /** @internal */
2
2
  export class PhysicsSystem {
3
3
  constructor() {
4
+ /** @internal */
4
5
  this._gravityX = 0;
6
+ /** @internal */
5
7
  this._gravityY = 0;
8
+ /** @internal */
6
9
  this._enabled = false;
7
10
  }
8
11
  get gravityX() {
@@ -94,11 +97,13 @@ export class PhysicsSystem {
94
97
  }
95
98
  return null;
96
99
  }
100
+ /** @internal */
97
101
  assertEnabled() {
98
102
  if (!this._enabled) {
99
103
  throw new Error("MinimoJS: collide() and collideAny() require game.physics = true.");
100
104
  }
101
105
  }
106
+ /** @internal */
102
107
  getCollisionResolution(a, b) {
103
108
  const halfWidthA = a.bodyDisplayWidth / 2;
104
109
  const halfHeightA = a.bodyDisplayHeight / 2;
@@ -1,10 +1,15 @@
1
1
  /** @internal */
2
2
  export class RenderSystem {
3
3
  constructor() {
4
+ /** @internal */
4
5
  this._surfaceCache = new Map();
6
+ /** @internal */
5
7
  this._lastAppliedPageBackground = undefined;
8
+ /** @internal */
6
9
  this._transitionScratchCanvas = null;
10
+ /** @internal */
7
11
  this._transitionScratchContext = null;
12
+ /** @internal */
8
13
  this._dynamicSurfacePassId = 0;
9
14
  }
10
15
  clearSpriteCache() {
@@ -95,6 +100,7 @@ export class RenderSystem {
95
100
  return;
96
101
  }
97
102
  }
103
+ /** @internal */
98
104
  renderScene(options) {
99
105
  const ctx = options.context;
100
106
  const W = options.canvas.width;
@@ -207,6 +213,7 @@ export class RenderSystem {
207
213
  this.drawInputDebugOverlay(ctx, sorted, options.scrollX, options.scrollY);
208
214
  }
209
215
  }
216
+ /** @internal */
210
217
  drawBodyDebugOverlay(ctx, sprites, scrollX, scrollY) {
211
218
  ctx.save();
212
219
  ctx.setLineDash([6, 4]);
@@ -233,6 +240,7 @@ export class RenderSystem {
233
240
  }
234
241
  ctx.restore();
235
242
  }
243
+ /** @internal */
236
244
  drawInputDebugOverlay(ctx, sprites, scrollX, scrollY) {
237
245
  ctx.save();
238
246
  ctx.setLineDash([3, 5]);
@@ -284,6 +292,7 @@ export class RenderSystem {
284
292
  ctx.strokeRect(barX, barY, barWidth, barHeight);
285
293
  ctx.restore();
286
294
  }
295
+ /** @internal */
287
296
  drawFadeTransition(ctx, transition, width, height, progress) {
288
297
  const firstHalf = progress < 0.5;
289
298
  const localT = firstHalf ? progress / 0.5 : (progress - 0.5) / 0.5;
@@ -294,6 +303,7 @@ export class RenderSystem {
294
303
  ctx.fillRect(0, 0, width, height);
295
304
  ctx.restore();
296
305
  }
306
+ /** @internal */
297
307
  drawWipeTransition(ctx, transition, width, height, progress) {
298
308
  ctx.drawImage(transition.fromCanvas, 0, 0, width, height);
299
309
  const clip = this.getScreenWipeClipRect(width, height, transition.direction, progress);
@@ -306,11 +316,13 @@ export class RenderSystem {
306
316
  ctx.drawImage(transition.toCanvas, 0, 0, width, height);
307
317
  ctx.restore();
308
318
  }
319
+ /** @internal */
309
320
  drawSlideTransition(ctx, transition, width, height, progress) {
310
321
  const offset = this.getSlideOffset(transition.direction, width, height, progress);
311
322
  ctx.drawImage(transition.fromCanvas, offset.fromX, offset.fromY, width, height);
312
323
  ctx.drawImage(transition.toCanvas, offset.toX, offset.toY, width, height);
313
324
  }
325
+ /** @internal */
314
326
  drawIrisTransition(ctx, transition, width, height, progress) {
315
327
  ctx.drawImage(transition.fromCanvas, 0, 0, width, height);
316
328
  const maxRadius = Math.max(Math.hypot(transition.centerX, transition.centerY), Math.hypot(width - transition.centerX, transition.centerY), Math.hypot(transition.centerX, height - transition.centerY), Math.hypot(width - transition.centerX, height - transition.centerY));
@@ -321,6 +333,7 @@ export class RenderSystem {
321
333
  ctx.drawImage(transition.toCanvas, 0, 0, width, height);
322
334
  ctx.restore();
323
335
  }
336
+ /** @internal */
324
337
  drawPixelateTransition(ctx, transition, width, height, progress) {
325
338
  const firstHalf = progress < 0.5;
326
339
  const localT = firstHalf ? progress / 0.5 : (progress - 0.5) / 0.5;
@@ -330,6 +343,7 @@ export class RenderSystem {
330
343
  : this.lerp(transition.pixelSize, 1, localT);
331
344
  this.drawPixelatedCanvas(ctx, source, width, height, pixelSize);
332
345
  }
346
+ /** @internal */
333
347
  drawFlashTransition(ctx, transition, width, height, progress) {
334
348
  const swapPoint = 0.28;
335
349
  const overlayPeak = 0.52;
@@ -351,22 +365,38 @@ export class RenderSystem {
351
365
  ctx.fillRect(0, 0, width, height);
352
366
  ctx.restore();
353
367
  }
368
+ /** @internal */
354
369
  getRenderSurface(sprite) {
355
370
  if (this.isDrawSprite(sprite)) {
356
371
  return this.getDrawSurface(sprite);
357
372
  }
358
373
  const cacheKey = sprite.getRenderCacheKey();
359
374
  const cached = this._surfaceCache.get(cacheKey);
360
- if (cached)
375
+ if (cached) {
376
+ this._surfaceCache.delete(cacheKey);
377
+ this._surfaceCache.set(cacheKey, cached);
361
378
  return cached;
379
+ }
362
380
  const surface = this.isTextSprite(sprite)
363
381
  ? this.createTextSurface(sprite)
364
382
  : this.isImageSprite(sprite)
365
383
  ? this.createImageSurface(sprite)
366
384
  : this.createEmojiSurface(this.asEmojiSprite(sprite));
367
385
  this._surfaceCache.set(cacheKey, surface);
386
+ this.trimSurfaceCache();
368
387
  return surface;
369
388
  }
389
+ /** @internal */
390
+ trimSurfaceCache() {
391
+ while (this._surfaceCache.size > RenderSystem.MAX_SURFACE_CACHE_ENTRIES) {
392
+ const oldestKey = this._surfaceCache.keys().next().value;
393
+ if (oldestKey === undefined) {
394
+ break;
395
+ }
396
+ this._surfaceCache.delete(oldestKey);
397
+ }
398
+ }
399
+ /** @internal */
370
400
  createEmojiSurface(sprite) {
371
401
  const size = Math.max(1, Math.round(sprite.size));
372
402
  const color = sprite.color;
@@ -390,6 +420,7 @@ export class RenderSystem {
390
420
  glyphCtx.fillText(sprite.sprite, boxSize / 2, boxSize / 2);
391
421
  return glyphCanvas;
392
422
  }
423
+ /** @internal */
393
424
  createTextSurface(sprite) {
394
425
  const canvas = document.createElement("canvas");
395
426
  const ctx = canvas.getContext("2d");
@@ -458,6 +489,7 @@ export class RenderSystem {
458
489
  }
459
490
  return canvas;
460
491
  }
492
+ /** @internal */
461
493
  createImageSurface(sprite) {
462
494
  const image = sprite.game?.getImage(sprite.imageKey);
463
495
  if (!image) {
@@ -474,6 +506,7 @@ export class RenderSystem {
474
506
  ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
475
507
  return canvas;
476
508
  }
509
+ /** @internal */
477
510
  getDrawSurface(sprite) {
478
511
  const width = Math.max(1, Math.round(sprite.width));
479
512
  const height = Math.max(1, Math.round(sprite.height));
@@ -509,15 +542,19 @@ export class RenderSystem {
509
542
  sprite._lastRedrawPassId = this._dynamicSurfacePassId;
510
543
  return surface;
511
544
  }
545
+ /** @internal */
512
546
  isTextSprite(sprite) {
513
547
  return "text" in sprite && "fontFamily" in sprite && "fontSize" in sprite;
514
548
  }
549
+ /** @internal */
515
550
  isDrawSprite(sprite) {
516
551
  return "redraw" in sprite && "_surface" in sprite && "_surfaceCtx" in sprite;
517
552
  }
553
+ /** @internal */
518
554
  isImageSprite(sprite) {
519
555
  return "imageKey" in sprite && "setTexture" in sprite;
520
556
  }
557
+ /** @internal */
521
558
  getWipeClipRect(drawWidth, drawHeight, direction, progress) {
522
559
  const halfWidth = drawWidth / 2;
523
560
  const halfHeight = drawHeight / 2;
@@ -556,6 +593,7 @@ export class RenderSystem {
556
593
  }
557
594
  }
558
595
  }
596
+ /** @internal */
559
597
  getScreenWipeClipRect(width, height, direction, progress) {
560
598
  switch (direction) {
561
599
  case "left-to-right":
@@ -592,12 +630,14 @@ export class RenderSystem {
592
630
  }
593
631
  }
594
632
  }
633
+ /** @internal */
595
634
  asEmojiSprite(sprite) {
596
635
  if ("sprite" in sprite && "size" in sprite) {
597
636
  return sprite;
598
637
  }
599
638
  throw new Error("MinimoJS: Unsupported sprite type for emoji rendering.");
600
639
  }
640
+ /** @internal */
601
641
  layoutTextLines(ctx, sprite) {
602
642
  this.applyTextStyle(ctx, sprite);
603
643
  const rawLines = sprite.text.split("\n");
@@ -627,6 +667,7 @@ export class RenderSystem {
627
667
  }
628
668
  return wrappedLines.length > 0 ? wrappedLines : [""];
629
669
  }
670
+ /** @internal */
630
671
  getAvailableTextWidth(sprite) {
631
672
  const borderPad = Math.max(0, sprite.borderWidth) * 2;
632
673
  const strokePad = Math.max(0, sprite.strokeWidth) * 2;
@@ -642,6 +683,7 @@ export class RenderSystem {
642
683
  }
643
684
  return Math.max(1, Math.floor(Math.min(...limits)));
644
685
  }
686
+ /** @internal */
645
687
  applyTextStyle(ctx, sprite) {
646
688
  ctx.font = `${sprite.fontWeight} ${sprite.fontSize}px ${sprite.fontFamily}`;
647
689
  ctx.shadowColor = "transparent";
@@ -649,14 +691,17 @@ export class RenderSystem {
649
691
  ctx.shadowOffsetX = 0;
650
692
  ctx.shadowOffsetY = 0;
651
693
  }
694
+ /** @internal */
652
695
  fillRoundedRect(ctx, x, y, width, height, radius) {
653
696
  this.buildRoundedRectPath(ctx, x, y, width, height, radius);
654
697
  ctx.fill();
655
698
  }
699
+ /** @internal */
656
700
  strokeRoundedRect(ctx, x, y, width, height, radius) {
657
701
  this.buildRoundedRectPath(ctx, x, y, width, height, radius);
658
702
  ctx.stroke();
659
703
  }
704
+ /** @internal */
660
705
  buildRoundedRectPath(ctx, x, y, width, height, radius) {
661
706
  const safeRadius = Math.max(0, Math.min(radius, width / 2, height / 2));
662
707
  ctx.beginPath();
@@ -675,23 +720,27 @@ export class RenderSystem {
675
720
  ctx.quadraticCurveTo(x, y, x + safeRadius, y);
676
721
  ctx.closePath();
677
722
  }
723
+ /** @internal */
678
724
  getSafeRenderScale(value) {
679
725
  if (!Number.isFinite(value))
680
726
  return 1;
681
727
  const safeValue = value;
682
728
  return Math.max(0, safeValue);
683
729
  }
730
+ /** @internal */
684
731
  getSafePivot(value) {
685
732
  if (!Number.isFinite(value))
686
733
  return 0.5;
687
734
  const safeValue = value;
688
735
  return Math.max(0, Math.min(1, safeValue));
689
736
  }
737
+ /** @internal */
690
738
  getSafeNumber(value, fallback) {
691
739
  if (!Number.isFinite(value))
692
740
  return fallback;
693
741
  return value;
694
742
  }
743
+ /** @internal */
695
744
  paintCanvasBackground(ctx, width, height, background, backgroundGradient) {
696
745
  ctx.clearRect(0, 0, width, height);
697
746
  if (backgroundGradient !== null) {
@@ -706,6 +755,7 @@ export class RenderSystem {
706
755
  ctx.fillRect(0, 0, width, height);
707
756
  }
708
757
  }
758
+ /** @internal */
709
759
  drawBackgroundLayer(ctx, canvas, layer, image) {
710
760
  const destX = Math.round(layer.x);
711
761
  const destY = Math.round(layer.y);
@@ -739,11 +789,13 @@ export class RenderSystem {
739
789
  ctx.drawImage(image, drawX, drawY, drawW, drawH);
740
790
  ctx.restore();
741
791
  }
792
+ /** @internal */
742
793
  getPositiveLength(value, fallback) {
743
794
  if (!Number.isFinite(value) || value <= 0)
744
795
  return fallback;
745
796
  return Math.round(value);
746
797
  }
798
+ /** @internal */
747
799
  getBackgroundScale(fit, destW, destH, imageW, imageH) {
748
800
  const scaleX = destW / imageW;
749
801
  const scaleY = destH / imageH;
@@ -752,10 +804,12 @@ export class RenderSystem {
752
804
  }
753
805
  return Math.max(scaleX, scaleY);
754
806
  }
807
+ /** @internal */
755
808
  easeTransitionProgress(t) {
756
809
  const clamped = Math.max(0, Math.min(1, t));
757
810
  return clamped * clamped * (3 - 2 * clamped);
758
811
  }
812
+ /** @internal */
759
813
  getSlideOffset(direction, width, height, progress) {
760
814
  switch (direction) {
761
815
  case "left-to-right":
@@ -788,6 +842,7 @@ export class RenderSystem {
788
842
  };
789
843
  }
790
844
  }
845
+ /** @internal */
791
846
  drawPixelatedCanvas(ctx, source, width, height, pixelSize) {
792
847
  const roundedPixelSize = Math.max(1, Math.round(pixelSize));
793
848
  if (roundedPixelSize <= 1) {
@@ -812,6 +867,7 @@ export class RenderSystem {
812
867
  ctx.drawImage(scratch, 0, 0, scaledWidth, scaledHeight, 0, 0, width, height);
813
868
  ctx.restore();
814
869
  }
870
+ /** @internal */
815
871
  getTransitionScratchSurface(width, height) {
816
872
  if (!this._transitionScratchCanvas) {
817
873
  this._transitionScratchCanvas = document.createElement("canvas");
@@ -831,9 +887,11 @@ export class RenderSystem {
831
887
  }
832
888
  return this._transitionScratchCanvas;
833
889
  }
890
+ /** @internal */
834
891
  lerp(from, to, t) {
835
892
  return from + (to - from) * t;
836
893
  }
894
+ /** @internal */
837
895
  applyPageBackground(pageBackground) {
838
896
  if (!document.body)
839
897
  return;
@@ -848,3 +906,5 @@ export class RenderSystem {
848
906
  this._lastAppliedPageBackground = pageBackground;
849
907
  }
850
908
  }
909
+ /** @internal */
910
+ RenderSystem.MAX_SURFACE_CACHE_ENTRIES = 256;
@@ -1,6 +1,7 @@
1
1
  /** @internal */
2
2
  export class SoundSystem {
3
3
  constructor() {
4
+ /** @internal */
4
5
  this._audioCtx = null;
5
6
  }
6
7
  sound(freq, durationMs) {
@@ -40,6 +41,7 @@ export class SoundSystem {
40
41
  startTime += durationSec;
41
42
  }
42
43
  }
44
+ /** @internal */
43
45
  getAudioContext() {
44
46
  if (!this._audioCtx) {
45
47
  this._audioCtx = new AudioContext();