@number10/phaserjsx 4.0.0 → 4.2.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 (64) hide show
  1. package/README.md +30 -0
  2. package/dist/chunk-C2EiDwsr.cjs +35 -0
  3. package/dist/clip/index.cjs +8 -0
  4. package/dist/clip/index.d.ts +3 -0
  5. package/dist/clip/index.d.ts.map +1 -0
  6. package/dist/clip/index.js +2 -0
  7. package/dist/clip/stencil-clip-depth.d.ts +10 -0
  8. package/dist/clip/stencil-clip-depth.d.ts.map +1 -0
  9. package/dist/clip/stencil-clip-extension.d.ts +18 -0
  10. package/dist/clip/stencil-clip-extension.d.ts.map +1 -0
  11. package/dist/clip/stencil-clip-fbo-bridge.d.ts +7 -0
  12. package/dist/clip/stencil-clip-fbo-bridge.d.ts.map +1 -0
  13. package/dist/clip/stencil-clip-renderer.d.ts +7 -0
  14. package/dist/clip/stencil-clip-renderer.d.ts.map +1 -0
  15. package/dist/clip/stencil-clip-state.d.ts +28 -0
  16. package/dist/clip/stencil-clip-state.d.ts.map +1 -0
  17. package/dist/clip/stencil-clip-types.d.ts +67 -0
  18. package/dist/clip/stencil-clip-types.d.ts.map +1 -0
  19. package/dist/clip/stencil-clip.d.ts +9 -42
  20. package/dist/clip/stencil-clip.d.ts.map +1 -1
  21. package/dist/clip-CHmjztBQ.cjs +705 -0
  22. package/dist/clip-CHmjztBQ.cjs.map +1 -0
  23. package/dist/clip-CPufWCSD.js +668 -0
  24. package/dist/clip-CPufWCSD.js.map +1 -0
  25. package/dist/components/appliers/applyBackground.d.ts +2 -1
  26. package/dist/components/appliers/applyBackground.d.ts.map +1 -1
  27. package/dist/components/appliers/applyTooltip.d.ts.map +1 -1
  28. package/dist/components/backgroundImage.d.ts +12 -0
  29. package/dist/components/backgroundImage.d.ts.map +1 -0
  30. package/dist/components/creators/createBackground.d.ts +2 -1
  31. package/dist/components/creators/createBackground.d.ts.map +1 -1
  32. package/dist/components/custom/Accordion.d.ts.map +1 -1
  33. package/dist/components/custom/DebugPanel.d.ts +30 -0
  34. package/dist/components/custom/DebugPanel.d.ts.map +1 -0
  35. package/dist/components/custom/Dropdown.d.ts.map +1 -1
  36. package/dist/components/custom/Toggle.d.ts.map +1 -1
  37. package/dist/components/custom/index.cjs +2 -1
  38. package/dist/components/custom/index.d.ts +1 -0
  39. package/dist/components/custom/index.d.ts.map +1 -1
  40. package/dist/components/custom/index.js +2 -2
  41. package/dist/components/primitives/graphics.d.ts +2 -2
  42. package/dist/components/primitives/view.d.ts.map +1 -1
  43. package/dist/{custom-oy3mBnrW.js → custom-BXDJDGOl.js} +439 -485
  44. package/dist/custom-BXDJDGOl.js.map +1 -0
  45. package/dist/{custom-BN31OAJq.cjs → custom-DTd4LxDn.cjs} +459 -518
  46. package/dist/custom-DTd4LxDn.cjs.map +1 -0
  47. package/dist/gestures/gesture-manager.d.ts +1 -1
  48. package/dist/hooks.d.ts +9 -8
  49. package/dist/hooks.d.ts.map +1 -1
  50. package/dist/index.cjs +105 -106
  51. package/dist/index.cjs.map +1 -1
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +96 -105
  55. package/dist/index.js.map +1 -1
  56. package/dist/layout/appliers/background-applier.d.ts.map +1 -1
  57. package/dist/layout/layout-engine.d.ts.map +1 -1
  58. package/dist/layout/types.d.ts +2 -1
  59. package/dist/layout/types.d.ts.map +1 -1
  60. package/dist/scene-backgrounds.d.ts +51 -1
  61. package/dist/scene-backgrounds.d.ts.map +1 -1
  62. package/package.json +8 -2
  63. package/dist/custom-BN31OAJq.cjs.map +0 -1
  64. package/dist/custom-oy3mBnrW.js.map +0 -1
@@ -1,29 +1,7 @@
1
- //#region \0rolldown/runtime.js
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
23
- //#endregion
1
+ const require_chunk = require("./chunk-C2EiDwsr.cjs");
24
2
  const require_jsx_runtime = require("./jsx-runtime.cjs");
25
3
  let phaser = require("phaser");
26
- phaser = __toESM(phaser, 1);
4
+ phaser = require_chunk.__toESM(phaser, 1);
27
5
  let _preact_signals_core = require("@preact/signals-core");
28
6
  //#region src/utils/phaser-guards.ts
29
7
  /**
@@ -1135,8 +1113,8 @@ var spritePatcher = (node, prev, next) => {
1135
1113
  applySpriteLayout(node, prev, next);
1136
1114
  };
1137
1115
  //#endregion
1138
- //#region src/utils/fast-deep-equal.ts
1139
- var import_fast_deep_equal = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
1116
+ //#region ../../node_modules/.pnpm/fast-deep-equal@3.1.3/node_modules/fast-deep-equal/index.js
1117
+ var require_fast_deep_equal = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module) => {
1140
1118
  module.exports = function equal(a, b) {
1141
1119
  if (a === b) return true;
1142
1120
  if (a && b && typeof a == "object" && typeof b == "object") {
@@ -1163,7 +1141,10 @@ var import_fast_deep_equal = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJS
1163
1141
  }
1164
1142
  return a !== a && b !== b;
1165
1143
  };
1166
- })))(), 1);
1144
+ }));
1145
+ //#endregion
1146
+ //#region src/utils/fast-deep-equal.ts
1147
+ var import_fast_deep_equal = /* @__PURE__ */ require_chunk.__toESM(require_fast_deep_equal(), 1);
1167
1148
  var equal = typeof import_fast_deep_equal === "function" ? import_fast_deep_equal : import_fast_deep_equal.default;
1168
1149
  /**
1169
1150
  * @license
@@ -1175,7 +1156,7 @@ var equal = typeof import_fast_deep_equal === "function" ? import_fast_deep_equa
1175
1156
  */
1176
1157
  //#endregion
1177
1158
  //#region src/dev-config.ts
1178
- var import_lodash = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
1159
+ var import_lodash = (/* @__PURE__ */ require_chunk.__commonJSMin(((exports, module) => {
1179
1160
  (function() {
1180
1161
  /** Used as a safe reference for `undefined` in pre-ES5 environments. */
1181
1162
  var undefined;
@@ -16572,6 +16553,111 @@ var tileSpritePatcher = (_node, _prev, _next) => {
16572
16553
  throw new Error("TileSprite component not implemented yet. This is a placeholder for architecture planning.");
16573
16554
  };
16574
16555
  //#endregion
16556
+ //#region src/components/backgroundImage.ts
16557
+ var sceneBackgroundTextureCaches = /* @__PURE__ */ new WeakMap();
16558
+ function hasRenderableBackground(props) {
16559
+ const hasBackground = props.backgroundColor !== void 0;
16560
+ const hasBorder = (props.borderWidth ?? 0) > 0 && props.borderColor !== void 0;
16561
+ return hasBackground || hasBorder;
16562
+ }
16563
+ function drawBackground(graphics, props, width, height) {
16564
+ const bgColor = props.backgroundColor;
16565
+ const bgAlpha = props.backgroundAlpha ?? 1;
16566
+ const cornerRadius = props.cornerRadius ?? 0;
16567
+ const borderColor = props.borderColor;
16568
+ const borderWidth = props.borderWidth ?? 0;
16569
+ const borderAlpha = props.borderAlpha ?? 1;
16570
+ const hasBorder = borderWidth > 0 && borderColor !== void 0;
16571
+ if (bgColor !== void 0) graphics.fillStyle(bgColor, bgAlpha);
16572
+ if (hasBorder) graphics.lineStyle(borderWidth, borderColor, borderAlpha);
16573
+ if (cornerRadius !== 0) {
16574
+ if (bgColor !== void 0) graphics.fillRoundedRect(0, 0, width, height, cornerRadius);
16575
+ if (hasBorder) graphics.strokeRoundedRect(0, 0, width, height, cornerRadius);
16576
+ } else {
16577
+ if (bgColor !== void 0) graphics.fillRect(0, 0, width, height);
16578
+ if (hasBorder) graphics.strokeRect(0, 0, width, height);
16579
+ }
16580
+ }
16581
+ function getSceneBackgroundTextureCache(scene) {
16582
+ let cache = sceneBackgroundTextureCaches.get(scene);
16583
+ if (!cache) {
16584
+ cache = /* @__PURE__ */ new Map();
16585
+ sceneBackgroundTextureCaches.set(scene, cache);
16586
+ }
16587
+ return cache;
16588
+ }
16589
+ function hashCacheKey(value) {
16590
+ let hash = 2166136261;
16591
+ for (let i = 0; i < value.length; i++) {
16592
+ hash ^= value.charCodeAt(i);
16593
+ hash = Math.imul(hash, 16777619);
16594
+ }
16595
+ return (hash >>> 0).toString(36);
16596
+ }
16597
+ function normalizeCornerRadius$1(radius) {
16598
+ if (typeof radius === "number") return String(radius);
16599
+ if (!radius) return "0";
16600
+ return `${radius.tl ?? 0},${radius.tr ?? 0},${radius.bl ?? 0},${radius.br ?? 0}`;
16601
+ }
16602
+ function getBackgroundImageCacheKey(props, width, height) {
16603
+ return [
16604
+ Math.max(1, Math.ceil(width)),
16605
+ Math.max(1, Math.ceil(height)),
16606
+ props.backgroundColor ?? "none",
16607
+ props.backgroundAlpha ?? 1,
16608
+ normalizeCornerRadius$1(props.cornerRadius),
16609
+ props.borderWidth ?? 0,
16610
+ props.borderColor ?? "none",
16611
+ props.borderAlpha ?? 1
16612
+ ].join("|");
16613
+ }
16614
+ function createBackgroundImage(scene, props, width, height) {
16615
+ if (!hasRenderableBackground(props)) return;
16616
+ const textureWidth = Math.max(1, Math.ceil(width));
16617
+ const textureHeight = Math.max(1, Math.ceil(height));
16618
+ const cacheKey = getBackgroundImageCacheKey(props, textureWidth, textureHeight);
16619
+ const textureKey = `__phaserjsx_bg_${hashCacheKey(cacheKey)}`;
16620
+ const cache = getSceneBackgroundTextureCache(scene);
16621
+ let entry = cache.get(cacheKey);
16622
+ if (!entry || !scene.textures.exists(entry.textureKey)) {
16623
+ const graphics = scene.add.graphics();
16624
+ drawBackground(graphics, props, textureWidth, textureHeight);
16625
+ graphics.generateTexture(textureKey, textureWidth, textureHeight);
16626
+ graphics.destroy();
16627
+ entry = {
16628
+ textureKey,
16629
+ refs: 0
16630
+ };
16631
+ cache.set(cacheKey, entry);
16632
+ }
16633
+ entry.refs++;
16634
+ const background = scene.add.image(0, 0, entry.textureKey);
16635
+ background.setOrigin(0, 0);
16636
+ background.__isBackground = true;
16637
+ background.__backgroundCacheKey = cacheKey;
16638
+ background.__backgroundTextureKey = entry.textureKey;
16639
+ background.once("destroy", () => releaseBackgroundImageTexture(background));
16640
+ return background;
16641
+ }
16642
+ function releaseBackgroundImageTexture(background) {
16643
+ if (background.__backgroundTextureReleased) return;
16644
+ const scene = background.scene;
16645
+ const cacheKey = background.__backgroundCacheKey;
16646
+ background.__backgroundTextureReleased = true;
16647
+ if (!scene || !cacheKey) return;
16648
+ const cache = sceneBackgroundTextureCaches.get(scene);
16649
+ const entry = cache?.get(cacheKey);
16650
+ if (!cache || !entry) return;
16651
+ entry.refs--;
16652
+ if (entry.refs <= 0) {
16653
+ if (scene.textures.exists(entry.textureKey)) scene.textures.remove(entry.textureKey);
16654
+ cache.delete(cacheKey);
16655
+ }
16656
+ }
16657
+ function destroyBackgroundImage(background) {
16658
+ background.destroy();
16659
+ }
16660
+ //#endregion
16575
16661
  //#region src/components/appliers/applyBackground.ts
16576
16662
  /**
16577
16663
  * Shared property appliers for component patching
@@ -16614,37 +16700,24 @@ function applyBackgroundProps(container, prev, next) {
16614
16700
  const nextHasGraphics = nextBgColor !== void 0 || nextHasBorder;
16615
16701
  if (prevHasGraphics && !nextHasGraphics) {
16616
16702
  if (container.__background) {
16617
- container.__background.destroy();
16703
+ destroyBackgroundImage(container.__background);
16618
16704
  delete container.__background;
16619
16705
  }
16620
16706
  } else if (!prevHasGraphics && nextHasGraphics) {
16621
16707
  if (container.scene) {
16622
- const background = container.scene.add.graphics();
16623
- if (nextBgColor !== void 0) background.fillStyle(nextBgColor, nextBgAlpha);
16624
- if (nextHasBorder) background.lineStyle(nextBorderWidth, nextBorderColor, nextBorderAlpha);
16625
- if (nextCornerRadius !== 0) {
16626
- if (nextBgColor !== void 0) background.fillRoundedRect(0, 0, nextWidth, nextHeight, nextCornerRadius);
16627
- if (nextHasBorder) background.strokeRoundedRect(0, 0, nextWidth, nextHeight, nextCornerRadius);
16628
- } else {
16629
- if (nextBgColor !== void 0) background.fillRect(0, 0, nextWidth, nextHeight);
16630
- if (nextHasBorder) background.strokeRect(0, 0, nextWidth, nextHeight);
16631
- }
16708
+ const background = createBackgroundImage(container.scene, next, nextWidth, nextHeight);
16709
+ if (!background) return;
16632
16710
  container.addAt(background, 0);
16633
16711
  container.__background = background;
16634
- background.__isBackground = true;
16635
16712
  }
16636
16713
  } else if (container.__background && nextHasGraphics) {
16637
16714
  if (prevBgColor !== nextBgColor || prevBgAlpha !== nextBgAlpha || prevWidth !== nextWidth || prevHeight !== nextHeight || prevCornerRadius !== nextCornerRadius || prevBorderWidth !== nextBorderWidth || prevBorderColor !== nextBorderColor || prevBorderAlpha !== nextBorderAlpha) {
16638
- container.__background.clear();
16639
- if (nextBgColor !== void 0) container.__background.fillStyle(nextBgColor, nextBgAlpha);
16640
- if (nextHasBorder) container.__background.lineStyle(nextBorderWidth, nextBorderColor, nextBorderAlpha);
16641
- if (nextCornerRadius !== 0) {
16642
- if (nextBgColor !== void 0) container.__background.fillRoundedRect(0, 0, nextWidth, nextHeight, nextCornerRadius);
16643
- if (nextHasBorder) container.__background.strokeRoundedRect(0, 0, nextWidth, nextHeight, nextCornerRadius);
16644
- } else {
16645
- if (nextBgColor !== void 0) container.__background.fillRect(0, 0, nextWidth, nextHeight);
16646
- if (nextHasBorder) container.__background.strokeRect(0, 0, nextWidth, nextHeight);
16647
- }
16715
+ const oldBackground = container.__background;
16716
+ const background = createBackgroundImage(container.scene, next, nextWidth, nextHeight);
16717
+ if (!background) return;
16718
+ container.addAt(background, 0);
16719
+ container.__background = background;
16720
+ destroyBackgroundImage(oldBackground);
16648
16721
  }
16649
16722
  }
16650
16723
  }
@@ -16865,7 +16938,7 @@ var GestureManager = class {
16865
16938
  /**
16866
16939
  * Handle global pointer down event
16867
16940
  * Registers all containers that were hit for move event tracking
16868
- * Only the topmost gets touch/longpress callbacks
16941
+ * Only the topmost gets touch/long-press callbacks
16869
16942
  */
16870
16943
  handlePointerDown(pointer) {
16871
16944
  const hitContainers = /* @__PURE__ */ new Set();
@@ -17419,403 +17492,6 @@ function applyGesturesProps(scene, container, prev, next) {
17419
17492
  }
17420
17493
  }
17421
17494
  //#endregion
17422
- //#region src/clip/stencil-clip.ts
17423
- /**
17424
- * WebGL stencil-buffer clip for Phaser 4 Containers.
17425
- *
17426
- * Supports arbitrary nesting via the INCR/DECR model: each clip level
17427
- * increments the stencil on enter and decrements on exit, so child clips are
17428
- * automatically intersected with their parent clips at the hardware level.
17429
- *
17430
- * Shape variants:
17431
- * - Plain rectangle (cornerRadius omitted or 0)
17432
- * - Rounded rectangle (uniform radius or per-corner object)
17433
- *
17434
- * A single SDF-based shader handles both variants. For plain rectangles
17435
- * u_radii is vec4(0) and the `discard` branch never fires — no overhead
17436
- * compared to a rectangle-only shader.
17437
- *
17438
- * Transforms (translate, scale, rotation) are fully supported: the quad
17439
- * corners are transformed through `container.getWorldTransformMatrix()` at
17440
- * render time, so no per-layout world-position tracking is needed.
17441
- */
17442
- /**
17443
- * Shared depth counter per GL context.
17444
- * Incremented before each clip's INCR pass, decremented after the DECR pass.
17445
- * Reset to 0 at the start of every frame via a `prerender` hook.
17446
- */
17447
- var _depthByGl = /* @__PURE__ */ new WeakMap();
17448
- function getDepth(gl) {
17449
- let d = _depthByGl.get(gl);
17450
- if (!d) {
17451
- d = { value: 0 };
17452
- _depthByGl.set(gl, d);
17453
- }
17454
- return d;
17455
- }
17456
- var _prerenderHooked = /* @__PURE__ */ new WeakSet();
17457
- /**
17458
- * Registers a per-frame `prerender` listener that resets the depth counter.
17459
- * Registered at most once per Phaser.Game instance.
17460
- */
17461
- function ensurePrerenderReset(gl, game) {
17462
- if (_prerenderHooked.has(game)) return;
17463
- _prerenderHooked.add(game);
17464
- game.events.on("prerender", () => {
17465
- const d = _depthByGl.get(gl);
17466
- if (d) d.value = 0;
17467
- const fbo = _fboPatchByGl.get(gl);
17468
- if (fbo) {
17469
- fbo.current = null;
17470
- fbo.saved = null;
17471
- }
17472
- });
17473
- }
17474
- var _fboPatchByGl = /* @__PURE__ */ new WeakMap();
17475
- /**
17476
- * Patches `gl.bindFramebuffer` once per GL context so that the stencil test
17477
- * is automatically disabled when Phaser switches to an off-screen FBO (for
17478
- * PostFX / RenderTexture rendering) and restored when switching back.
17479
- *
17480
- * Without this, a PostFX child rendered inside a stencil-clipped container
17481
- * would be invisible: the FBO's stencil buffer starts at 0 while the active
17482
- * stencil test requires `EQUAL(myDepth + 1)`, causing every fragment to fail.
17483
- *
17484
- * The patch is installed once per GL context and remains active for the
17485
- * lifetime of the renderer. It is a no-op when no stencil clip is active.
17486
- */
17487
- function ensureFboPatch(gl) {
17488
- if (_fboPatchByGl.has(gl)) return;
17489
- const state = {
17490
- current: null,
17491
- saved: null
17492
- };
17493
- _fboPatchByGl.set(gl, state);
17494
- const origBind = gl.bindFramebuffer.bind(gl);
17495
- gl.bindFramebuffer = (target, fb) => {
17496
- const wasMain = state.current === null;
17497
- const willBeMain = fb === null;
17498
- const enteringOffscreen = wasMain && !willBeMain;
17499
- const leavingOffscreen = !wasMain && willBeMain;
17500
- if (enteringOffscreen && gl.getParameter(gl.STENCIL_TEST)) {
17501
- state.saved = {
17502
- func: gl.getParameter(gl.STENCIL_FUNC),
17503
- ref: gl.getParameter(gl.STENCIL_REF),
17504
- valueMask: gl.getParameter(gl.STENCIL_VALUE_MASK),
17505
- fail: gl.getParameter(gl.STENCIL_FAIL),
17506
- zfail: gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL),
17507
- zpass: gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS),
17508
- writeMask: gl.getParameter(gl.STENCIL_WRITEMASK)
17509
- };
17510
- gl.disable(gl.STENCIL_TEST);
17511
- }
17512
- origBind(target, fb);
17513
- state.current = fb;
17514
- if (leavingOffscreen && state.saved) {
17515
- gl.enable(gl.STENCIL_TEST);
17516
- gl.stencilFunc(state.saved.func, state.saved.ref, state.saved.valueMask);
17517
- gl.stencilOp(state.saved.fail, state.saved.zfail, state.saved.zpass);
17518
- gl.stencilMask(state.saved.writeMask);
17519
- state.saved = null;
17520
- }
17521
- };
17522
- }
17523
- /**
17524
- * Vertex attributes:
17525
- * a_ndc vec2 — NDC clip-space position (CPU-computed from world transform)
17526
- * a_loc vec2 — position relative to the clip rect's center (for SDF)
17527
- *
17528
- * All four corners are pre-transformed on the CPU so the vertex shader is a
17529
- * pure pass-through. This correctly handles translation, scale, and rotation
17530
- * without a matrix uniform.
17531
- */
17532
- var VERT_SRC = `
17533
- attribute vec2 a_ndc;
17534
- attribute vec2 a_loc;
17535
- varying vec2 v_loc;
17536
- void main(){gl_Position=vec4(a_ndc,0.,1.);v_loc=a_loc;}
17537
- `;
17538
- /**
17539
- * SDF rounded-rectangle fragment shader.
17540
- *
17541
- * sdRoundedBox uses the IQ per-corner-radius technique:
17542
- * r.xy = p.x > 0 ? r.yz : r.xw (right → tr/br, left → tl/bl)
17543
- * r.x = p.y > 0 ? r.y : r.x (bottom or top within that pair)
17544
- *
17545
- * u_radii layout: (tl, tr, br, bl).
17546
- *
17547
- * For plain rectangles u_radii = vec4(0.0) and sdRoundedBox returns ≤ 0 for
17548
- * all fragments inside the quad, so `discard` is never executed.
17549
- */
17550
- var FRAG_SRC = `
17551
- precision mediump float;
17552
- varying vec2 v_loc;
17553
- uniform vec2 u_halfSize;
17554
- uniform vec4 u_radii;
17555
- float sdRoundedBox(vec2 p,vec2 b,vec4 r){
17556
- r.xy=p.x>0.?r.yz:r.xw;
17557
- r.x =p.y>0.?r.y :r.x;
17558
- vec2 q=abs(p)-b+r.x;
17559
- return length(max(q,0.))+min(max(q.x,q.y),0.)-r.x;
17560
- }
17561
- void main(){
17562
- if(sdRoundedBox(v_loc,u_halfSize,u_radii)>0.)discard;
17563
- gl_FragColor=vec4(0.);
17564
- }
17565
- `;
17566
- var _progByGl = /* @__PURE__ */ new WeakMap();
17567
- /**
17568
- * Returns (or lazily creates) the SDF stencil shader program for a GL context.
17569
- * @param gl - The WebGL context.
17570
- * @returns Compiled and linked WebGLProgram.
17571
- */
17572
- function getStencilProg(gl) {
17573
- let prog = _progByGl.get(gl);
17574
- if (prog) return prog;
17575
- const vs = gl.createShader(gl.VERTEX_SHADER);
17576
- gl.shaderSource(vs, VERT_SRC);
17577
- gl.compileShader(vs);
17578
- const fs = gl.createShader(gl.FRAGMENT_SHADER);
17579
- gl.shaderSource(fs, FRAG_SRC);
17580
- gl.compileShader(fs);
17581
- prog = gl.createProgram();
17582
- gl.attachShader(prog, vs);
17583
- gl.attachShader(prog, fs);
17584
- gl.linkProgram(prog);
17585
- _progByGl.set(gl, prog);
17586
- return prog;
17587
- }
17588
- var _locsByProg = /* @__PURE__ */ new WeakMap();
17589
- /**
17590
- * Returns (or resolves and caches) the attribute/uniform locations for a program.
17591
- * @param gl - The WebGL context.
17592
- * @param prog - The shader program.
17593
- * @returns Cached locations.
17594
- */
17595
- function getShaderLocs(gl, prog) {
17596
- let l = _locsByProg.get(prog);
17597
- if (!l) {
17598
- l = {
17599
- ndc: gl.getAttribLocation(prog, "a_ndc"),
17600
- loc: gl.getAttribLocation(prog, "a_loc"),
17601
- halfSize: gl.getUniformLocation(prog, "u_halfSize"),
17602
- radii: gl.getUniformLocation(prog, "u_radii")
17603
- };
17604
- _locsByProg.set(prog, l);
17605
- }
17606
- return l;
17607
- }
17608
- /**
17609
- * Resolves the `cornerRadius` field to `[tl, tr, br, bl]` order matching the
17610
- * `u_radii` vec4 uniform layout.
17611
- * @param r - Raw corner radius value from the clip shape.
17612
- * @returns Tuple `[tl, tr, br, bl]`.
17613
- */
17614
- function resolveRadii(r) {
17615
- if (!r) return [
17616
- 0,
17617
- 0,
17618
- 0,
17619
- 0
17620
- ];
17621
- if (typeof r === "number") return [
17622
- r,
17623
- r,
17624
- r,
17625
- r
17626
- ];
17627
- return [
17628
- r.tl ?? 0,
17629
- r.tr ?? 0,
17630
- r.br ?? 0,
17631
- r.bl ?? 0
17632
- ];
17633
- }
17634
- /** Stride in bytes: 4 floats × 4 bytes. */
17635
- var STRIDE = 16;
17636
- /**
17637
- * Transforms the four clip-rect corners through the container's world matrix,
17638
- * uploads them to the vertex buffer, and draws the quad into the stencil.
17639
- *
17640
- * Vertex buffer layout (16 floats / 4 vertices, TRIANGLE_FAN, TL→TR→BR→BL):
17641
- * [ndcX, ndcY, locX, locY] × 4
17642
- * where locX/Y is the position relative to the clip rect's center (for SDF).
17643
- *
17644
- * The three GL states tracked by Phaser's `WebGLGlobalWrapper`
17645
- * (`CURRENT_PROGRAM`, `ARRAY_BUFFER_BINDING`, `VERTEX_ARRAY_BINDING`) are
17646
- * saved and restored so its internal caches stay consistent.
17647
- *
17648
- * @param gl - The (VAO-polyfilled) WebGL context.
17649
- * @param matrix - Container's current world transform matrix.
17650
- * @param offsetX - Top-left X of the clip rect in local space.
17651
- * @param offsetY - Top-left Y of the clip rect in local space.
17652
- * @param w - Clip rect width in local units.
17653
- * @param h - Clip rect height in local units.
17654
- * @param logW - Logical game width (`renderer.width`).
17655
- * @param logH - Logical game height (`renderer.height`).
17656
- * @param radii - Per-corner radii tuple `[tl, tr, br, bl]`.
17657
- * @param vertBuf - Persistent WebGLBuffer (64 bytes, DYNAMIC_DRAW).
17658
- * @param verts - Reusable Float32Array(16) to avoid per-frame allocation.
17659
- */
17660
- function drawMaskShape(gl, matrix, offsetX, offsetY, w, h, logW, logH, radii, vertBuf, verts) {
17661
- const { a, b, c, d, tx, ty } = matrix;
17662
- const hw = w / 2;
17663
- const hh = h / 2;
17664
- const cx = offsetX + hw;
17665
- const cy = offsetY + hh;
17666
- const corners = [
17667
- [cx - hw, cy - hh],
17668
- [cx + hw, cy - hh],
17669
- [cx + hw, cy + hh],
17670
- [cx - hw, cy + hh]
17671
- ];
17672
- for (let i = 0; i < 4; i++) {
17673
- const corner = corners[i];
17674
- const lx = corner[0];
17675
- const ly = corner[1];
17676
- const wx = a * lx + c * ly + tx;
17677
- const wy = b * lx + d * ly + ty;
17678
- verts[i * 4 + 0] = wx / logW * 2 - 1;
17679
- verts[i * 4 + 1] = 1 - wy / logH * 2;
17680
- verts[i * 4 + 2] = lx - cx;
17681
- verts[i * 4 + 3] = ly - cy;
17682
- }
17683
- const prevProg = gl.getParameter(gl.CURRENT_PROGRAM);
17684
- const prevBuf = gl.getParameter(gl.ARRAY_BUFFER_BINDING);
17685
- const prevVAO = gl.getParameter(34229);
17686
- gl.bindVertexArray(null);
17687
- gl.bindBuffer(gl.ARRAY_BUFFER, vertBuf);
17688
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, verts);
17689
- const prog = getStencilProg(gl);
17690
- gl.useProgram(prog);
17691
- const locs = getShaderLocs(gl, prog);
17692
- gl.enableVertexAttribArray(locs.ndc);
17693
- gl.vertexAttribPointer(locs.ndc, 2, gl.FLOAT, false, STRIDE, 0);
17694
- gl.enableVertexAttribArray(locs.loc);
17695
- gl.vertexAttribPointer(locs.loc, 2, gl.FLOAT, false, STRIDE, 8);
17696
- gl.uniform2f(locs.halfSize, hw, hh);
17697
- gl.uniform4f(locs.radii, radii[0], radii[1], radii[2], radii[3]);
17698
- gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
17699
- gl.disableVertexAttribArray(locs.ndc);
17700
- gl.disableVertexAttribArray(locs.loc);
17701
- gl.bindBuffer(gl.ARRAY_BUFFER, prevBuf);
17702
- gl.useProgram(prevProg);
17703
- gl.bindVertexArray(prevVAO);
17704
- }
17705
- var STENCIL_HANDLE = Symbol("stencilClipHandle");
17706
- /**
17707
- * Applies a WebGL stencil-buffer clip to a Phaser 4 Container.
17708
- *
17709
- * The clip rectangle is expressed in the container's **local coordinate
17710
- * space**. `offsetX`/`offsetY` mark the top-left corner (default 0/0), so
17711
- * a container whose visual area starts at its local origin can be clipped with
17712
- * `applyStencilClip(container, { width, height })`.
17713
- *
17714
- * **Nesting** is handled transparently: each clip level occupies one stencil
17715
- * value (0 = no clip, 1 = depth 0, 2 = depth 1, …). Child clips are always
17716
- * the intersection of their own shape and all ancestor shapes.
17717
- *
17718
- * **Transforms** (translate, scale, rotation) are re-evaluated from
17719
- * `container.getWorldTransformMatrix()` on every frame, so animated or
17720
- * scroll-driven containers clip correctly without any manual update call.
17721
- *
17722
- * If a stencil clip is already attached to the container, calling this
17723
- * function again calls `handle.update(shape)` on the existing handle and
17724
- * returns it.
17725
- *
17726
- * @param container - The container to clip.
17727
- * @param shape - Clip rect geometry in local units.
17728
- * @returns A handle to modify dimensions / corner radii or remove the clip.
17729
- */
17730
- function applyStencilClip(container, shape) {
17731
- const obj = container;
17732
- if (obj[STENCIL_HANDLE]) {
17733
- obj[STENCIL_HANDLE].update(shape);
17734
- return obj[STENCIL_HANDLE];
17735
- }
17736
- if (container.scene.renderer.type !== phaser.WEBGL) return {
17737
- update() {},
17738
- destroy() {}
17739
- };
17740
- const gl = container.scene.renderer.gl;
17741
- ensurePrerenderReset(gl, container.scene.game);
17742
- ensureFboPatch(gl);
17743
- const vertBuf = gl.createBuffer();
17744
- gl.bindBuffer(gl.ARRAY_BUFFER, vertBuf);
17745
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(16), gl.DYNAMIC_DRAW);
17746
- gl.bindBuffer(gl.ARRAY_BUFFER, null);
17747
- const verts = new Float32Array(16);
17748
- let clipW = shape.width;
17749
- let clipH = shape.height;
17750
- let clipOffsetX = shape.offsetX ?? 0;
17751
- let clipOffsetY = shape.offsetY ?? 0;
17752
- let radii = resolveRadii(shape.cornerRadius);
17753
- let destroyed = false;
17754
- const wrapper = (webglRenderer, go, drawingContext, parentMatrix, renderStep = 0, displayList, displayListIndex) => {
17755
- const renderNext = () => {
17756
- go.renderWebGLStep(webglRenderer, go, drawingContext, parentMatrix, renderStep + 1, displayList, displayListIndex);
17757
- };
17758
- if (destroyed) {
17759
- renderNext();
17760
- return;
17761
- }
17762
- const matrix = container.getWorldTransformMatrix();
17763
- const rn = webglRenderer.renderNodes;
17764
- const depth = getDepth(gl);
17765
- const myDepth = depth.value++;
17766
- const logW = webglRenderer.width;
17767
- const logH = webglRenderer.height;
17768
- rn.finishBatch();
17769
- if (myDepth === 0) gl.enable(gl.STENCIL_TEST);
17770
- gl.colorMask(false, false, false, false);
17771
- gl.stencilMask(255);
17772
- gl.stencilFunc(gl.EQUAL, myDepth, 255);
17773
- gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);
17774
- drawMaskShape(gl, matrix, clipOffsetX, clipOffsetY, clipW, clipH, logW, logH, radii, vertBuf, verts);
17775
- gl.colorMask(true, true, true, true);
17776
- gl.stencilFunc(gl.EQUAL, myDepth + 1, 255);
17777
- gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
17778
- gl.stencilMask(0);
17779
- renderNext();
17780
- rn.finishBatch();
17781
- gl.colorMask(false, false, false, false);
17782
- gl.stencilMask(255);
17783
- gl.stencilFunc(gl.EQUAL, myDepth + 1, 255);
17784
- gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR);
17785
- drawMaskShape(gl, matrix, clipOffsetX, clipOffsetY, clipW, clipH, logW, logH, radii, vertBuf, verts);
17786
- gl.colorMask(true, true, true, true);
17787
- depth.value--;
17788
- if (myDepth === 0) {
17789
- gl.disable(gl.STENCIL_TEST);
17790
- gl.stencilMask(255);
17791
- } else {
17792
- gl.stencilFunc(gl.EQUAL, myDepth, 255);
17793
- gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
17794
- gl.stencilMask(0);
17795
- }
17796
- };
17797
- const handle = {
17798
- update(s) {
17799
- if (s.width !== void 0) clipW = s.width;
17800
- if (s.height !== void 0) clipH = s.height;
17801
- if (s.offsetX !== void 0) clipOffsetX = s.offsetX;
17802
- if (s.offsetY !== void 0) clipOffsetY = s.offsetY;
17803
- if ("cornerRadius" in s) radii = resolveRadii(s.cornerRadius);
17804
- },
17805
- destroy() {
17806
- if (destroyed) return;
17807
- destroyed = true;
17808
- gl.deleteBuffer(vertBuf);
17809
- const index = obj._renderSteps.indexOf(wrapper);
17810
- if (index !== -1) obj._renderSteps.splice(index, 1);
17811
- delete obj[STENCIL_HANDLE];
17812
- }
17813
- };
17814
- obj.addRenderStep(wrapper, 0);
17815
- obj[STENCIL_HANDLE] = handle;
17816
- return handle;
17817
- }
17818
- //#endregion
17819
17495
  //#region src/core-props.ts
17820
17496
  /**
17821
17497
  * Normalize edge insets - converts number to all-sides object
@@ -17874,23 +17550,13 @@ function updateBackground(container, width, height) {
17874
17550
  if (background) {
17875
17551
  const layoutProps = container.__layoutProps;
17876
17552
  if (layoutProps) {
17877
- const bgColor = layoutProps.backgroundColor;
17878
- const bgAlpha = layoutProps.backgroundAlpha ?? 1;
17879
- const cornerRadius = layoutProps.cornerRadius ?? 0;
17880
- const borderWidth = layoutProps.borderWidth ?? 0;
17881
- const borderColor = layoutProps.borderColor;
17882
- const borderAlpha = layoutProps.borderAlpha ?? 1;
17883
- const hasBorder = borderWidth > 0 && borderColor !== void 0;
17884
- background.clear();
17885
- if (bgColor !== void 0) background.fillStyle(bgColor, bgAlpha);
17886
- if (hasBorder) background.lineStyle(borderWidth, borderColor, borderAlpha);
17887
- if (cornerRadius !== 0) {
17888
- if (bgColor !== void 0) background.fillRoundedRect(0, 0, width, height, cornerRadius);
17889
- if (hasBorder) background.strokeRoundedRect(0, 0, width, height, cornerRadius);
17890
- } else {
17891
- if (bgColor !== void 0) background.fillRect(0, 0, width, height);
17892
- if (hasBorder) background.strokeRect(0, 0, width, height);
17893
- }
17553
+ const cacheKey = getBackgroundImageCacheKey(layoutProps, width, height);
17554
+ if (background.__backgroundCacheKey === cacheKey) return;
17555
+ const nextBackground = createBackgroundImage(container.scene, layoutProps, width, height);
17556
+ if (!nextBackground) return;
17557
+ container.addAt(nextBackground, 0);
17558
+ container.__background = nextBackground;
17559
+ destroyBackgroundImage(background);
17894
17560
  DebugLogger.log("layout", "Background redrawn to:", {
17895
17561
  width,
17896
17562
  height
@@ -18851,12 +18517,10 @@ function applyOverflowMask(container, containerProps, width, height) {
18851
18517
  height
18852
18518
  };
18853
18519
  if (containerProps.cornerRadius !== void 0) shapeBase.cornerRadius = containerProps.cornerRadius;
18854
- const handle = applyStencilClip(container, shapeBase);
18520
+ container.setStencilClip(shapeBase);
18521
+ const handle = container.getStencilClipHandle();
18522
+ if (!handle) return;
18855
18523
  ext.__overflowClip = handle;
18856
- container.once("destroy", () => {
18857
- handle.destroy();
18858
- delete ext.__overflowClip;
18859
- });
18860
18524
  DebugLogger.log("overflowMask", "Created stencil clip");
18861
18525
  } else {
18862
18526
  const update = {
@@ -18864,10 +18528,10 @@ function applyOverflowMask(container, containerProps, width, height) {
18864
18528
  height,
18865
18529
  cornerRadius: containerProps.cornerRadius ?? 0
18866
18530
  };
18867
- ext.__overflowClip.update(update);
18531
+ container.updateStencilClip(update);
18868
18532
  }
18869
18533
  else if (ext.__overflowClip) {
18870
- ext.__overflowClip.destroy();
18534
+ container.clearStencilClip();
18871
18535
  delete ext.__overflowClip;
18872
18536
  DebugLogger.log("overflowMask", "Removed stencil clip");
18873
18537
  }
@@ -18885,6 +18549,7 @@ var LAYOUT_CYCLE_TIME_MS = 20;
18885
18549
  var LAYOUT_CYCLE_MAX = 5;
18886
18550
  var LAYOUT_MAX_SIZE = 2e5;
18887
18551
  var layoutCycleGuard = /* @__PURE__ */ new WeakMap();
18552
+ var deferredParentInvalidations = /* @__PURE__ */ new WeakSet();
18888
18553
  function isCloseSize(a, b, epsilon) {
18889
18554
  return Math.abs(a.width - b.width) < epsilon && Math.abs(a.height - b.height) < epsilon;
18890
18555
  }
@@ -18908,6 +18573,8 @@ function invalidateParentLayoutIfNeeded(container, oldSize, newWidth, newHeight)
18908
18573
  height: newHeight
18909
18574
  };
18910
18575
  const now = Date.now();
18576
+ const parent = container.parentContainer;
18577
+ if (!parent || !parent.__layoutProps) return;
18911
18578
  const guard = layoutCycleGuard.get(container);
18912
18579
  if (guard) {
18913
18580
  if ((guard.prev ? isCloseSize(guard.prev, newSize, LAYOUT_CYCLE_EPSILON) : false) && now - guard.lastTime < LAYOUT_CYCLE_TIME_MS) guard.count += 1;
@@ -18917,11 +18584,15 @@ function invalidateParentLayoutIfNeeded(container, oldSize, newWidth, newHeight)
18917
18584
  guard.lastTime = now;
18918
18585
  layoutCycleGuard.set(container, guard);
18919
18586
  if (guard.count >= LAYOUT_CYCLE_MAX) {
18920
- DebugLogger.warn("layout", "Layout cycle detected, skipping parent invalidation", {
18587
+ DebugLogger.log("layout", "Layout cycle detected, deferring parent invalidation", {
18921
18588
  container,
18922
18589
  oldSize,
18923
18590
  newSize
18924
18591
  });
18592
+ guard.count = 0;
18593
+ guard.lastTime = 0;
18594
+ layoutCycleGuard.set(container, guard);
18595
+ deferParentLayoutInvalidation(container);
18925
18596
  return;
18926
18597
  }
18927
18598
  } else layoutCycleGuard.set(container, {
@@ -18929,9 +18600,20 @@ function invalidateParentLayoutIfNeeded(container, oldSize, newWidth, newHeight)
18929
18600
  count: 0,
18930
18601
  lastTime: now
18931
18602
  });
18603
+ DebugLogger.log("layout", "Container size changed, invalidating parent:", `${oldSize.width}x${oldSize.height} -> ${newWidth}x${newHeight}`);
18604
+ invalidateParentLayout(container);
18605
+ }
18606
+ function deferParentLayoutInvalidation(container) {
18607
+ if (deferredParentInvalidations.has(container)) return;
18608
+ deferredParentInvalidations.add(container);
18609
+ DeferredLayoutQueue.defer(() => {
18610
+ deferredParentInvalidations.delete(container);
18611
+ invalidateParentLayout(container);
18612
+ });
18613
+ }
18614
+ function invalidateParentLayout(container) {
18932
18615
  const parent = container.parentContainer;
18933
18616
  if (!parent || !parent.__layoutProps) return;
18934
- DebugLogger.log("layout", "Container size changed, invalidating parent:", `${oldSize.width}x${oldSize.height} -> ${newWidth}x${newHeight}`);
18935
18617
  const grandParent = parent.parentContainer;
18936
18618
  let grandParentSize;
18937
18619
  if (grandParent && grandParent.__getLayoutSize) {
@@ -20961,22 +20643,22 @@ function getLayoutProps(container) {
20961
20643
  return container.__layoutProps;
20962
20644
  }
20963
20645
  /**
20964
- * Utility function to get background graphics from a container
20965
- * Returns the Graphics object used for backgroundColor, border, cornerRadius
20646
+ * Utility function to get background object from a container
20647
+ * Returns the Image object used for backgroundColor, border, cornerRadius
20966
20648
  * Useful for custom animations or modifications
20967
20649
  * @param container - Phaser container with background
20968
- * @returns Graphics object or undefined if container has no background
20650
+ * @returns Background image or undefined if container has no background
20969
20651
  */
20970
20652
  function getBackgroundGraphics(container) {
20971
20653
  if (!container) return void 0;
20972
20654
  return container.__background;
20973
20655
  }
20974
20656
  /**
20975
- * Hook to get background graphics from a ref
20976
- * Returns the Graphics object used for rendering the background
20657
+ * Hook to get background image from a ref
20658
+ * Returns the Image object used for rendering the background
20977
20659
  * Useful for animating background properties (tint, alpha, etc.)
20978
20660
  * @param ref - Ref to a Phaser container
20979
- * @returns Graphics object or undefined
20661
+ * @returns Background image or undefined
20980
20662
  */
20981
20663
  function useBackgroundGraphics(ref) {
20982
20664
  return getBackgroundGraphics(ref.current);
@@ -22730,8 +22412,6 @@ function showTooltip(scene, container, config) {
22730
22412
  const textHeight = text.height;
22731
22413
  const bgWidth = textWidth + paddingX * 2;
22732
22414
  const bgHeight = textHeight + paddingY * 2;
22733
- const cornerRadius = tooltipTheme.cornerRadius ?? 6;
22734
- const graphics = scene.add.graphics();
22735
22415
  const bg = bgColor ?? "#000000dd";
22736
22416
  let fillColor = 0;
22737
22417
  let fillAlpha = .87;
@@ -22747,9 +22427,17 @@ function showTooltip(scene, container, config) {
22747
22427
  }
22748
22428
  }
22749
22429
  }
22750
- graphics.fillStyle(fillColor, fillAlpha);
22751
- graphics.fillRoundedRect(-bgWidth / 2, -bgHeight / 2, bgWidth, bgHeight, cornerRadius);
22752
- const tooltipContainer = scene.add.container(0, 0, [graphics, text]);
22430
+ const background = createBackgroundImage(scene, {
22431
+ backgroundColor: fillColor,
22432
+ backgroundAlpha: fillAlpha,
22433
+ cornerRadius: tooltipTheme.cornerRadius ?? 6
22434
+ }, bgWidth, bgHeight);
22435
+ if (!background) {
22436
+ text.destroy();
22437
+ return;
22438
+ }
22439
+ background.setOrigin(.5, .5);
22440
+ const tooltipContainer = scene.add.container(0, 0, [background, text]);
22753
22441
  tooltipContainer.setDepth(1e4);
22754
22442
  const textBounds = tooltipContainer.getBounds();
22755
22443
  const pos = calculateTooltipPosition(targetBounds, position, offset, textBounds.width, textBounds.height);
@@ -22900,29 +22588,12 @@ function applyTooltip(scene, container, nextCallback, existingOnHoverStart, exis
22900
22588
  */
22901
22589
  function createBackground(scene, container, props) {
22902
22590
  const hasBackground = props.backgroundColor !== void 0;
22903
- const hasBorder = props.borderColor !== void 0;
22591
+ const hasBorder = (props.borderWidth ?? 0) > 0 && props.borderColor !== void 0;
22904
22592
  if (hasBackground || hasBorder) {
22905
- const width = typeof props.width === "number" ? props.width : 100;
22906
- const height = typeof props.height === "number" ? props.height : 100;
22907
- const bgColor = props.backgroundColor;
22908
- const bgAlpha = props.backgroundAlpha ?? 1;
22909
- const cornerRadius = props.cornerRadius ?? 0;
22910
- const borderColor = props.borderColor;
22911
- const borderWidth = props.borderWidth ?? 0;
22912
- const borderAlpha = props.borderAlpha ?? 1;
22913
- const background = scene.add.graphics();
22914
- if (bgColor !== void 0) background.fillStyle(bgColor, bgAlpha);
22915
- if (borderWidth > 0 && borderColor !== void 0) background.lineStyle(borderWidth, borderColor, borderAlpha);
22916
- if (cornerRadius !== 0) {
22917
- if (bgColor !== void 0) background.fillRoundedRect(0, 0, width, height, cornerRadius);
22918
- if (borderWidth > 0 && borderColor !== void 0) background.strokeRoundedRect(0, 0, width, height, cornerRadius);
22919
- } else {
22920
- if (bgColor !== void 0) background.fillRect(0, 0, width, height);
22921
- if (borderWidth > 0 && borderColor !== void 0) background.strokeRect(0, 0, width, height);
22922
- }
22593
+ const background = createBackgroundImage(scene, props, typeof props.width === "number" ? props.width : 100, typeof props.height === "number" ? props.height : 100);
22594
+ if (!background) return;
22923
22595
  container.addAt(background, 0);
22924
22596
  container.__background = background;
22925
- background.__isBackground = true;
22926
22597
  }
22927
22598
  }
22928
22599
  //#endregion
@@ -24484,7 +24155,7 @@ function registerBuiltins() {
24484
24155
  /**
24485
24156
  * Preprocesses SVG string to ensure tinting works correctly
24486
24157
  * Replaces fill/stroke="currentColor" and other colors with white (#FFFFFF)
24487
- * This allows Phaser's tint to work multiplicatively (white × tint = tint color)
24158
+ * This allows Phaser's tint to work by multiplication (white × tint = tint color)
24488
24159
  * @param svg - Raw SVG string
24489
24160
  * @returns Preprocessed SVG string with white fills/strokes
24490
24161
  */
@@ -25489,6 +25160,9 @@ function Accordion(props) {
25489
25160
  }, [isOpen, autoHeight]);
25490
25161
  const [contentHeight, setContentHeight] = useSpring(animated ? isOpen ? autoHeight ? measuredHeight : maxHeight : 0 : isOpen ? autoHeight ? measuredHeight : maxHeight : 0, animationConfig, props.onAnimationEnd);
25491
25162
  if (animated) useForceRedraw(20, contentHeight);
25163
+ const animatedContentHeight = Math.max(0, contentHeight.value);
25164
+ const resolvedContentHeight = animated ? animatedContentHeight : isOpen ? void 0 : 0;
25165
+ const resolvedContentVisible = animated ? animatedContentHeight > .5 : isOpen;
25492
25166
  const handleToggle = () => {
25493
25167
  const newState = !isOpen;
25494
25168
  setInternalOpen(newState);
@@ -25522,6 +25196,9 @@ function Accordion(props) {
25522
25196
  autoHeight && /* @__PURE__ */ require_jsx_runtime.jsx(View, {
25523
25197
  visible: false,
25524
25198
  direction: "stack",
25199
+ headless: true,
25200
+ width: 0,
25201
+ height: 0,
25525
25202
  children: /* @__PURE__ */ require_jsx_runtime.jsx(View, {
25526
25203
  ref: measurementRef,
25527
25204
  ...contentTheme,
@@ -25530,9 +25207,9 @@ function Accordion(props) {
25530
25207
  }),
25531
25208
  /* @__PURE__ */ require_jsx_runtime.jsx(View, {
25532
25209
  direction: "column",
25533
- height: animated ? contentHeight.value : isOpen ? void 0 : 0,
25210
+ height: resolvedContentHeight,
25534
25211
  overflow: "hidden",
25535
- visible: animated ? contentHeight.value > .5 : isOpen,
25212
+ visible: resolvedContentVisible,
25536
25213
  ...contentTheme,
25537
25214
  children: props.children
25538
25215
  })
@@ -27070,6 +26747,264 @@ function CharTextInput(props) {
27070
26747
  });
27071
26748
  }
27072
26749
  //#endregion
26750
+ //#region src/design-tokens/use-theme-tokens.ts
26751
+ /**
26752
+ * Hook to access complete design token system
26753
+ * Combines colors with text styles, spacing, sizes, and radius tokens
26754
+ */
26755
+ /**
26756
+ * Hook to access complete design token system from theme context
26757
+ * Provides colors, text styles, spacing, sizes, and radius tokens
26758
+ * Automatically updates when color mode or preset changes
26759
+ * @returns Current DesignTokens or undefined
26760
+ * @example
26761
+ * ```typescript
26762
+ * function MyComponent() {
26763
+ * const tokens = useThemeTokens()
26764
+ *
26765
+ * if (!tokens) return null
26766
+ *
26767
+ * return (
26768
+ * <View
26769
+ * backgroundColor={tokens.colors.surface.DEFAULT}
26770
+ * padding={tokens.spacing.lg}
26771
+ * cornerRadius={tokens.radius.md}
26772
+ * >
26773
+ * <Text text="Title" style={tokens.textStyles.title} />
26774
+ * <Text text="Body text" style={tokens.textStyles.DEFAULT} />
26775
+ * </View>
26776
+ * )
26777
+ * }
26778
+ * ```
26779
+ */
26780
+ function useThemeTokens() {
26781
+ const localTheme = useTheme();
26782
+ const getInitialTokens = () => {
26783
+ if (localTheme?.__colorPreset) {
26784
+ const preset = getPresetWithMode(localTheme.__colorPreset.name, localTheme.__colorPreset.mode ?? "light");
26785
+ return {
26786
+ colors: preset.colors,
26787
+ textStyles: createTextStyleTokens(preset.colors.text.DEFAULT.toString()),
26788
+ spacing: defaultSpacingTokens,
26789
+ sizes: defaultSizeTokens,
26790
+ radius: defaultRadiusTokens
26791
+ };
26792
+ }
26793
+ const colors = themeRegistry.getColorTokens();
26794
+ if (!colors) return void 0;
26795
+ return {
26796
+ colors,
26797
+ textStyles: createTextStyleTokens(colors.text.DEFAULT.toString()),
26798
+ spacing: defaultSpacingTokens,
26799
+ sizes: defaultSizeTokens,
26800
+ radius: defaultRadiusTokens
26801
+ };
26802
+ };
26803
+ const [tokens, setTokens] = useState(getInitialTokens());
26804
+ const [, forceUpdate] = useState(0);
26805
+ useEffect(() => {
26806
+ return themeRegistry.subscribe(() => {
26807
+ if (localTheme?.__colorPreset) {
26808
+ const currentMode = themeRegistry.getColorMode();
26809
+ const preset = getPresetWithMode(localTheme.__colorPreset.name, currentMode);
26810
+ setTokens({
26811
+ colors: preset.colors,
26812
+ textStyles: createTextStyleTokens(preset.colors.text.DEFAULT.toString()),
26813
+ spacing: defaultSpacingTokens,
26814
+ sizes: defaultSizeTokens,
26815
+ radius: defaultRadiusTokens
26816
+ });
26817
+ } else {
26818
+ const colors = themeRegistry.getColorTokens();
26819
+ if (colors) setTokens({
26820
+ colors,
26821
+ textStyles: createTextStyleTokens(colors.text.DEFAULT.toString()),
26822
+ spacing: defaultSpacingTokens,
26823
+ sizes: defaultSizeTokens,
26824
+ radius: defaultRadiusTokens
26825
+ });
26826
+ }
26827
+ forceUpdate((n) => n + 1);
26828
+ });
26829
+ }, [localTheme]);
26830
+ return tokens;
26831
+ }
26832
+ //#endregion
26833
+ //#region src/components/custom/DebugPanel.tsx
26834
+ /** @jsxImportSource ../.. */
26835
+ /**
26836
+ * DebugPanel component - lightweight runtime diagnostics for Phaser + PhaserJSX
26837
+ * Renders selected metrics as text rows or a compact single-line summary.
26838
+ */
26839
+ var PRESET_METRICS = {
26840
+ fps: ["fps", "frameMs"],
26841
+ perf: [
26842
+ "fps",
26843
+ "frameMs",
26844
+ "renderer",
26845
+ "viewport"
26846
+ ],
26847
+ vdom: [
26848
+ "mountsTotal",
26849
+ "mountsByType",
26850
+ "mountsByParent",
26851
+ "mountsByKey"
26852
+ ],
26853
+ textures: ["textureCount"],
26854
+ full: [
26855
+ "fps",
26856
+ "frameMs",
26857
+ "phaserVersion",
26858
+ "renderer",
26859
+ "viewport",
26860
+ "textureCount",
26861
+ "mountsTotal",
26862
+ "mountsByType",
26863
+ "mountsByParent",
26864
+ "mountsByKey",
26865
+ "debugFlags"
26866
+ ]
26867
+ };
26868
+ var DEFAULT_LABELS = {
26869
+ fps: "FPS",
26870
+ frameMs: "Frame ms",
26871
+ phaserVersion: "Phaser",
26872
+ renderer: "Renderer",
26873
+ viewport: "Viewport",
26874
+ textureCount: "Textures",
26875
+ mountsTotal: "Mounts",
26876
+ mountsByType: "Mounts/type",
26877
+ mountsByParent: "Mounts/parent",
26878
+ mountsByKey: "Mounts/key",
26879
+ debugFlags: "Debug flags"
26880
+ };
26881
+ function resolveRendererName(scene) {
26882
+ const renderer = scene.renderer;
26883
+ const canvasType = phaser.CANVAS;
26884
+ if (renderer.type === phaser.WEBGL) return "WebGL";
26885
+ if (canvasType !== void 0 && renderer.type === canvasType) return "Canvas";
26886
+ if (renderer.constructor?.name) return renderer.constructor.name;
26887
+ if (typeof renderer.type === "number") return `Type ${renderer.type}`;
26888
+ return "Unknown";
26889
+ }
26890
+ function resolveTextureCount(scene) {
26891
+ const manager = scene.textures;
26892
+ const keys = typeof manager.getTextureKeys === "function" ? manager.getTextureKeys() : Object.keys(manager.list ?? {});
26893
+ const internalKeys = new Set([
26894
+ "__DEFAULT",
26895
+ "__MISSING",
26896
+ "__NORMAL",
26897
+ "__WHITE"
26898
+ ]);
26899
+ return keys.filter((key) => !internalKeys.has(key.toUpperCase())).length;
26900
+ }
26901
+ function resolveFrameStats(scene) {
26902
+ const loop = scene.game.loop;
26903
+ const rawFps = loop?.actualFps ?? loop?.fps ?? 0;
26904
+ const rawFrameMs = loop?.delta ?? loop?.frameDelta ?? 0;
26905
+ return {
26906
+ fps: Number.isFinite(rawFps) ? Number(rawFps.toFixed(1)) : 0,
26907
+ frameMs: Number.isFinite(rawFrameMs) ? Number(rawFrameMs.toFixed(2)) : 0
26908
+ };
26909
+ }
26910
+ function summarizeMap(map, maxEntries = 3) {
26911
+ return Array.from(map.entries()).sort((a, b) => b[1] - a[1]).slice(0, maxEntries).map(([key, value]) => `${String(key)}:${value}`).join(" | ") || "-";
26912
+ }
26913
+ function resolveDebugFlags() {
26914
+ const debugConfig = DevConfig.debug;
26915
+ if (!debugConfig.enabled) return "off";
26916
+ const enabledFlags = Object.entries(debugConfig).filter(([key, value]) => key !== "enabled" && value).map(([key]) => key);
26917
+ return enabledFlags.length ? enabledFlags.join(",") : "enabled";
26918
+ }
26919
+ function collectSnapshot(scene) {
26920
+ const { fps, frameMs } = resolveFrameStats(scene);
26921
+ const mountStats = getMountStats();
26922
+ return {
26923
+ fps,
26924
+ frameMs,
26925
+ phaserVersion: phaser.VERSION,
26926
+ renderer: resolveRendererName(scene),
26927
+ viewport: `${scene.scale.width}x${scene.scale.height}`,
26928
+ textureCount: resolveTextureCount(scene),
26929
+ mountsTotal: mountStats.totalMounts,
26930
+ mountsByType: summarizeMap(mountStats.byType),
26931
+ mountsByParent: summarizeMap(mountStats.byParent),
26932
+ mountsByKey: summarizeMap(mountStats.byKey),
26933
+ debugFlags: resolveDebugFlags()
26934
+ };
26935
+ }
26936
+ function formatDefault(value) {
26937
+ return typeof value === "number" ? String(value) : value;
26938
+ }
26939
+ /**
26940
+ * DebugPanel component
26941
+ * Shows selected diagnostics from Phaser + PhaserJSX as overlay-ready content.
26942
+ */
26943
+ function DebugPanel(props) {
26944
+ const scene = useScene();
26945
+ const { preset = "fps", metrics, intervalMs = 250, compact = false, maxRows, labels, formatters, innerProps, ...viewProps } = props;
26946
+ const selectedMetrics = useMemo(() => {
26947
+ const source = metrics && metrics.length > 0 ? metrics : PRESET_METRICS[preset];
26948
+ return Array.from(new Set(source));
26949
+ }, [metrics, preset]);
26950
+ const [snapshot, setSnapshot] = useState(collectSnapshot(scene));
26951
+ useEffect(() => {
26952
+ const delay = Math.max(50, intervalMs);
26953
+ setSnapshot(collectSnapshot(scene));
26954
+ const timer = window.setInterval(() => {
26955
+ setSnapshot(collectSnapshot(scene));
26956
+ }, delay);
26957
+ return () => {
26958
+ window.clearInterval(timer);
26959
+ };
26960
+ }, [
26961
+ scene,
26962
+ intervalMs,
26963
+ selectedMetrics
26964
+ ]);
26965
+ const tokens = useThemeTokens();
26966
+ const rowStyle = tokens?.textStyles.small;
26967
+ const valueStyle = tokens?.textStyles.small;
26968
+ const rows = selectedMetrics.map((metric) => {
26969
+ const rawValue = snapshot[metric];
26970
+ const label = labels?.[metric] ?? DEFAULT_LABELS[metric];
26971
+ const formatter = formatters?.[metric];
26972
+ return {
26973
+ metric,
26974
+ label,
26975
+ value: formatter ? formatter(rawValue) : formatDefault(rawValue)
26976
+ };
26977
+ });
26978
+ const limitedRows = typeof maxRows === "number" ? rows.slice(0, Math.max(1, maxRows)) : rows;
26979
+ if (compact) {
26980
+ const compactText = limitedRows.map((row) => `${row.label} ${row.value}`).join(" | ");
26981
+ return /* @__PURE__ */ require_jsx_runtime.jsx(View, {
26982
+ ...viewProps,
26983
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(Text, {
26984
+ text: compactText,
26985
+ style: valueStyle
26986
+ })
26987
+ });
26988
+ }
26989
+ return /* @__PURE__ */ require_jsx_runtime.jsx(View, {
26990
+ direction: "column",
26991
+ gap: 4,
26992
+ ...viewProps,
26993
+ children: limitedRows.map((row) => /* @__PURE__ */ require_jsx_runtime.jsxs(View, {
26994
+ direction: "row",
26995
+ gap: 8,
26996
+ ...innerProps,
26997
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Text, {
26998
+ text: `${row.label}:`,
26999
+ style: rowStyle
27000
+ }), /* @__PURE__ */ require_jsx_runtime.jsx(Text, {
27001
+ text: row.value,
27002
+ style: valueStyle
27003
+ })]
27004
+ }, `debug-${row.metric}`))
27005
+ });
27006
+ }
27007
+ //#endregion
27073
27008
  //#region src/components/custom/Divider.tsx
27074
27009
  /**
27075
27010
  * Divider component - renders a simple line separator
@@ -27850,6 +27785,7 @@ function Dropdown(props) {
27850
27785
  useForceRedraw(20, overlayHeight);
27851
27786
  const [arrowRotation, setArrowRotation] = useSpring(isOpen ? Math.PI : 0, animationConfig);
27852
27787
  useForceRedraw(20, arrowRotation);
27788
+ const resolvedOverlayHeight = Math.max(0, overlayHeight.value);
27853
27789
  const handleToggle = (event) => {
27854
27790
  if (props.disabled) return;
27855
27791
  event?.stopPropagation();
@@ -28021,7 +27957,7 @@ function Dropdown(props) {
28021
27957
  })
28022
27958
  });
28023
27959
  const overlay = /* @__PURE__ */ require_jsx_runtime.jsx(View, {
28024
- height: overlayHeight.value,
27960
+ height: resolvedOverlayHeight,
28025
27961
  width: overlayPosition.width,
28026
27962
  overflow: "hidden",
28027
27963
  children: /* @__PURE__ */ require_jsx_runtime.jsx(View, {
@@ -28029,7 +27965,7 @@ function Dropdown(props) {
28029
27965
  direction: "column",
28030
27966
  width: "fill",
28031
27967
  height: "fill",
28032
- visible: isOpen || Math.abs(overlayHeight.value) > .1,
27968
+ visible: isOpen || resolvedOverlayHeight > .1,
28033
27969
  ...overlayTheme,
28034
27970
  children: placement === "top" ? /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [optionsList, filterInput] }) : /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [filterInput, optionsList] })
28035
27971
  })
@@ -29521,7 +29457,6 @@ function Toggle(props) {
29521
29457
  setIsAnimating(true);
29522
29458
  const endX = newChecked ? thumbOffsetOn : thumbOffsetOff;
29523
29459
  const startX = thumbX;
29524
- console.log("Duration:", duration.current);
29525
29460
  scene.tweens.addCounter({
29526
29461
  from: 0,
29527
29462
  to: 1,
@@ -29742,6 +29677,12 @@ Object.defineProperty(exports, "DebugLogger", {
29742
29677
  return DebugLogger;
29743
29678
  }
29744
29679
  });
29680
+ Object.defineProperty(exports, "DebugPanel", {
29681
+ enumerable: true,
29682
+ get: function() {
29683
+ return DebugPanel;
29684
+ }
29685
+ });
29745
29686
  Object.defineProperty(exports, "DevConfig", {
29746
29687
  enumerable: true,
29747
29688
  get: function() {
@@ -29970,12 +29911,6 @@ Object.defineProperty(exports, "WrapText", {
29970
29911
  return WrapText;
29971
29912
  }
29972
29913
  });
29973
- Object.defineProperty(exports, "__toESM", {
29974
- enumerable: true,
29975
- get: function() {
29976
- return __toESM;
29977
- }
29978
- });
29979
29914
  Object.defineProperty(exports, "alpha", {
29980
29915
  enumerable: true,
29981
29916
  get: function() {
@@ -30786,6 +30721,12 @@ Object.defineProperty(exports, "useTheme", {
30786
30721
  return useTheme;
30787
30722
  }
30788
30723
  });
30724
+ Object.defineProperty(exports, "useThemeTokens", {
30725
+ enumerable: true,
30726
+ get: function() {
30727
+ return useThemeTokens;
30728
+ }
30729
+ });
30789
30730
  Object.defineProperty(exports, "useViewportSize", {
30790
30731
  enumerable: true,
30791
30732
  get: function() {
@@ -30823,4 +30764,4 @@ Object.defineProperty(exports, "withHooks", {
30823
30764
  }
30824
30765
  });
30825
30766
 
30826
- //# sourceMappingURL=custom-BN31OAJq.cjs.map
30767
+ //# sourceMappingURL=custom-DTd4LxDn.cjs.map