@energy8platform/game-engine 0.2.1 → 0.4.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 (63) hide show
  1. package/README.md +400 -35
  2. package/dist/animation.cjs.js +191 -1
  3. package/dist/animation.cjs.js.map +1 -1
  4. package/dist/animation.d.ts +117 -1
  5. package/dist/animation.esm.js +192 -3
  6. package/dist/animation.esm.js.map +1 -1
  7. package/dist/audio.cjs.js +66 -16
  8. package/dist/audio.cjs.js.map +1 -1
  9. package/dist/audio.d.ts +4 -0
  10. package/dist/audio.esm.js +66 -16
  11. package/dist/audio.esm.js.map +1 -1
  12. package/dist/core.cjs.js +307 -85
  13. package/dist/core.cjs.js.map +1 -1
  14. package/dist/core.d.ts +60 -1
  15. package/dist/core.esm.js +308 -86
  16. package/dist/core.esm.js.map +1 -1
  17. package/dist/debug.cjs.js +36 -68
  18. package/dist/debug.cjs.js.map +1 -1
  19. package/dist/debug.d.ts +4 -6
  20. package/dist/debug.esm.js +36 -68
  21. package/dist/debug.esm.js.map +1 -1
  22. package/dist/index.cjs.js +997 -475
  23. package/dist/index.cjs.js.map +1 -1
  24. package/dist/index.d.ts +356 -79
  25. package/dist/index.esm.js +983 -478
  26. package/dist/index.esm.js.map +1 -1
  27. package/dist/ui.cjs.js +816 -529
  28. package/dist/ui.cjs.js.map +1 -1
  29. package/dist/ui.d.ts +179 -41
  30. package/dist/ui.esm.js +798 -531
  31. package/dist/ui.esm.js.map +1 -1
  32. package/dist/vite.cjs.js +85 -68
  33. package/dist/vite.cjs.js.map +1 -1
  34. package/dist/vite.d.ts +17 -23
  35. package/dist/vite.esm.js +86 -68
  36. package/dist/vite.esm.js.map +1 -1
  37. package/package.json +19 -5
  38. package/src/animation/SpriteAnimation.ts +210 -0
  39. package/src/animation/Tween.ts +27 -1
  40. package/src/animation/index.ts +2 -0
  41. package/src/audio/AudioManager.ts +64 -15
  42. package/src/core/EventEmitter.ts +7 -1
  43. package/src/core/GameApplication.ts +19 -7
  44. package/src/core/SceneManager.ts +3 -1
  45. package/src/debug/DevBridge.ts +49 -80
  46. package/src/index.ts +22 -0
  47. package/src/input/InputManager.ts +26 -0
  48. package/src/loading/CSSPreloader.ts +7 -33
  49. package/src/loading/LoadingScene.ts +17 -41
  50. package/src/loading/index.ts +1 -0
  51. package/src/loading/logo.ts +95 -0
  52. package/src/types.ts +4 -0
  53. package/src/ui/BalanceDisplay.ts +12 -1
  54. package/src/ui/Button.ts +71 -130
  55. package/src/ui/Layout.ts +286 -0
  56. package/src/ui/Modal.ts +6 -5
  57. package/src/ui/Panel.ts +52 -55
  58. package/src/ui/ProgressBar.ts +52 -57
  59. package/src/ui/ScrollContainer.ts +126 -0
  60. package/src/ui/Toast.ts +19 -13
  61. package/src/ui/index.ts +17 -0
  62. package/src/viewport/ViewportManager.ts +2 -0
  63. package/src/vite/index.ts +103 -83
package/src/ui/Panel.ts CHANGED
@@ -1,4 +1,6 @@
1
- import { Container, Graphics, NineSliceSprite, Texture } from 'pixi.js';
1
+ import { Container, NineSliceSprite, Texture } from 'pixi.js';
2
+ import { LayoutContainer } from '@pixi/layout/components';
3
+ import type { LayoutStyles } from '@pixi/layout';
2
4
 
3
5
  export interface PanelConfig {
4
6
  /** Width */
@@ -24,7 +26,10 @@ export interface PanelConfig {
24
26
  }
25
27
 
26
28
  /**
27
- * Background panel that can use either Graphics or 9-slice sprite.
29
+ * Background panel powered by `@pixi/layout` LayoutContainer.
30
+ *
31
+ * Supports both Graphics-based (color + border) and 9-slice sprite backgrounds.
32
+ * Children added to `content` participate in flexbox layout automatically.
28
33
  *
29
34
  * @example
30
35
  * ```ts
@@ -39,87 +44,79 @@ export interface PanelConfig {
39
44
  * });
40
45
  * ```
41
46
  */
42
- export class Panel extends Container {
43
- private _bg: Graphics | NineSliceSprite;
44
- private _content: Container;
45
- private _config: Required<
47
+ export class Panel extends LayoutContainer {
48
+ private _panelConfig: Required<
46
49
  Pick<PanelConfig, 'width' | 'height' | 'padding' | 'backgroundAlpha'>
47
50
  > & PanelConfig;
48
51
 
49
52
  constructor(config: PanelConfig = {}) {
50
- super();
51
-
52
- this._config = {
53
- width: 400,
54
- height: 300,
55
- padding: 16,
56
- backgroundAlpha: 1,
53
+ const resolvedConfig = {
54
+ width: config.width ?? 400,
55
+ height: config.height ?? 300,
56
+ padding: config.padding ?? 16,
57
+ backgroundAlpha: config.backgroundAlpha ?? 1,
57
58
  ...config,
58
59
  };
59
60
 
60
- // Create background
61
+ // If using a 9-slice texture, pass it as a custom background
62
+ let customBackground: Container | undefined;
61
63
  if (config.nineSliceTexture) {
62
64
  const texture =
63
65
  typeof config.nineSliceTexture === 'string'
64
66
  ? Texture.from(config.nineSliceTexture)
65
67
  : config.nineSliceTexture;
66
-
67
68
  const [left, top, right, bottom] = config.nineSliceBorders ?? [10, 10, 10, 10];
68
-
69
- this._bg = new NineSliceSprite({
69
+ const nineSlice = new NineSliceSprite({
70
70
  texture,
71
71
  leftWidth: left,
72
72
  topHeight: top,
73
73
  rightWidth: right,
74
74
  bottomHeight: bottom,
75
75
  });
76
- (this._bg as NineSliceSprite).width = this._config.width;
77
- (this._bg as NineSliceSprite).height = this._config.height;
78
- } else {
79
- this._bg = new Graphics();
80
- this.drawGraphicsBg();
76
+ nineSlice.width = resolvedConfig.width;
77
+ nineSlice.height = resolvedConfig.height;
78
+ nineSlice.alpha = resolvedConfig.backgroundAlpha;
79
+ customBackground = nineSlice;
81
80
  }
82
81
 
83
- this._bg.alpha = this._config.backgroundAlpha;
84
- this.addChild(this._bg);
82
+ super(customBackground ? { background: customBackground } : undefined);
85
83
 
86
- // Content container with padding
87
- this._content = new Container();
88
- this._content.x = this._config.padding;
89
- this._content.y = this._config.padding;
90
- this.addChild(this._content);
91
- }
84
+ this._panelConfig = resolvedConfig;
92
85
 
93
- /** Content container — add children here */
94
- get content(): Container {
95
- return this._content;
96
- }
86
+ // Apply layout styles
87
+ const layoutStyles: LayoutStyles = {
88
+ width: resolvedConfig.width,
89
+ height: resolvedConfig.height,
90
+ padding: resolvedConfig.padding,
91
+ flexDirection: 'column',
92
+ };
97
93
 
98
- /** Resize the panel */
99
- setSize(width: number, height: number): void {
100
- this._config.width = width;
101
- this._config.height = height;
94
+ // Graphics-based background via layout styles
95
+ if (!config.nineSliceTexture) {
96
+ layoutStyles.backgroundColor = config.backgroundColor ?? 0x1a1a2e;
97
+ layoutStyles.borderRadius = config.borderRadius ?? 0;
98
+ if (config.borderColor !== undefined && config.borderWidth) {
99
+ layoutStyles.borderColor = config.borderColor;
100
+ layoutStyles.borderWidth = config.borderWidth;
101
+ }
102
+ }
103
+
104
+ this.layout = layoutStyles;
102
105
 
103
- if (this._bg instanceof Graphics) {
104
- this.drawGraphicsBg();
105
- } else {
106
- this._bg.width = width;
107
- this._bg.height = height;
106
+ if (!config.nineSliceTexture) {
107
+ this.background.alpha = resolvedConfig.backgroundAlpha;
108
108
  }
109
109
  }
110
110
 
111
- private drawGraphicsBg(): void {
112
- const bg = this._bg as Graphics;
113
- const {
114
- width, height, backgroundColor, borderRadius, borderColor, borderWidth,
115
- } = this._config;
116
-
117
- bg.clear();
118
- bg.roundRect(0, 0, width!, height!, borderRadius ?? 0).fill(backgroundColor ?? 0x1a1a2e);
111
+ /** Access the content container (children added here participate in layout) */
112
+ get content(): Container {
113
+ return this.overflowContainer;
114
+ }
119
115
 
120
- if (borderColor !== undefined && borderWidth) {
121
- bg.roundRect(0, 0, width!, height!, borderRadius ?? 0)
122
- .stroke({ color: borderColor, width: borderWidth });
123
- }
116
+ /** Resize the panel */
117
+ setSize(width: number, height: number): void {
118
+ this._panelConfig.width = width;
119
+ this._panelConfig.height = height;
120
+ this._layout?.setStyle({ width, height });
124
121
  }
125
122
  }
@@ -1,4 +1,6 @@
1
1
  import { Container, Graphics } from 'pixi.js';
2
+ import { ProgressBar as PixiProgressBar } from '@pixi/ui';
3
+ import type { ProgressBarOptions } from '@pixi/ui';
2
4
 
3
5
  export interface ProgressBarConfig {
4
6
  width?: number;
@@ -14,8 +16,16 @@ export interface ProgressBarConfig {
14
16
  animationSpeed?: number;
15
17
  }
16
18
 
19
+ function makeBarGraphics(
20
+ w: number, h: number, radius: number, color: number,
21
+ ): Graphics {
22
+ return new Graphics().roundRect(0, 0, w, h, radius).fill(color);
23
+ }
24
+
17
25
  /**
18
- * Horizontal progress bar with optional smooth fill animation.
26
+ * Horizontal progress bar powered by `@pixi/ui` ProgressBar.
27
+ *
28
+ * Provides optional smooth animated fill via per-frame `update()`.
19
29
  *
20
30
  * @example
21
31
  * ```ts
@@ -25,9 +35,8 @@ export interface ProgressBarConfig {
25
35
  * ```
26
36
  */
27
37
  export class ProgressBar extends Container {
28
- private _track: Graphics;
29
- private _fill: Graphics;
30
- private _border: Graphics;
38
+ private _bar: PixiProgressBar;
39
+ private _borderGfx: Graphics;
31
40
  private _config: Required<ProgressBarConfig>;
32
41
  private _progress = 0;
33
42
  private _displayedProgress = 0;
@@ -36,26 +45,45 @@ export class ProgressBar extends Container {
36
45
  super();
37
46
 
38
47
  this._config = {
39
- width: 300,
40
- height: 16,
41
- borderRadius: 8,
42
- fillColor: 0xffd700,
43
- trackColor: 0x333333,
44
- borderColor: 0x555555,
45
- borderWidth: 1,
46
- animated: true,
47
- animationSpeed: 0.1,
48
- ...config,
48
+ width: config.width ?? 300,
49
+ height: config.height ?? 16,
50
+ borderRadius: config.borderRadius ?? 8,
51
+ fillColor: config.fillColor ?? 0xffd700,
52
+ trackColor: config.trackColor ?? 0x333333,
53
+ borderColor: config.borderColor ?? 0x555555,
54
+ borderWidth: config.borderWidth ?? 1,
55
+ animated: config.animated ?? true,
56
+ animationSpeed: config.animationSpeed ?? 0.1,
49
57
  };
50
58
 
51
- this._track = new Graphics();
52
- this._fill = new Graphics();
53
- this._border = new Graphics();
59
+ const { width, height, borderRadius, fillColor, trackColor, borderColor, borderWidth } = this._config;
54
60
 
55
- this.addChild(this._track, this._fill, this._border);
56
- this.drawTrack();
57
- this.drawBorder();
58
- this.drawFill(0);
61
+ const bgGraphics = makeBarGraphics(width, height, borderRadius, trackColor);
62
+ const fillGraphics = makeBarGraphics(width - borderWidth * 2, height - borderWidth * 2, Math.max(0, borderRadius - 1), fillColor);
63
+
64
+ const options: ProgressBarOptions = {
65
+ bg: bgGraphics,
66
+ fill: fillGraphics,
67
+ fillPaddings: {
68
+ top: borderWidth,
69
+ right: borderWidth,
70
+ bottom: borderWidth,
71
+ left: borderWidth,
72
+ },
73
+ progress: 0,
74
+ };
75
+
76
+ this._bar = new PixiProgressBar(options);
77
+ this.addChild(this._bar);
78
+
79
+ // Border overlay
80
+ this._borderGfx = new Graphics();
81
+ if (borderColor !== undefined && borderWidth > 0) {
82
+ this._borderGfx
83
+ .roundRect(0, 0, width, height, borderRadius)
84
+ .stroke({ color: borderColor, width: borderWidth });
85
+ }
86
+ this.addChild(this._borderGfx);
59
87
  }
60
88
 
61
89
  /** Get/set progress (0..1) */
@@ -67,14 +95,14 @@ export class ProgressBar extends Container {
67
95
  this._progress = Math.max(0, Math.min(1, value));
68
96
  if (!this._config.animated) {
69
97
  this._displayedProgress = this._progress;
70
- this.drawFill(this._displayedProgress);
98
+ this._bar.progress = this._displayedProgress * 100;
71
99
  }
72
100
  }
73
101
 
74
102
  /**
75
103
  * Call each frame if animated is true.
76
104
  */
77
- update(dt: number): void {
105
+ update(_dt: number): void {
78
106
  if (!this._config.animated) return;
79
107
  if (Math.abs(this._displayedProgress - this._progress) < 0.001) {
80
108
  this._displayedProgress = this._progress;
@@ -83,39 +111,6 @@ export class ProgressBar extends Container {
83
111
 
84
112
  this._displayedProgress +=
85
113
  (this._progress - this._displayedProgress) * this._config.animationSpeed;
86
- this.drawFill(this._displayedProgress);
87
- }
88
-
89
- private drawTrack(): void {
90
- const { width, height, borderRadius, trackColor } = this._config;
91
- this._track.clear();
92
- this._track.roundRect(0, 0, width, height, borderRadius).fill(trackColor);
93
- }
94
-
95
- private drawBorder(): void {
96
- const { width, height, borderRadius, borderColor, borderWidth } = this._config;
97
- this._border.clear();
98
- this._border
99
- .roundRect(0, 0, width, height, borderRadius)
100
- .stroke({ color: borderColor, width: borderWidth });
101
- }
102
-
103
- private drawFill(progress: number): void {
104
- const { width, height, borderRadius, fillColor, borderWidth } = this._config;
105
- const innerWidth = width - borderWidth * 2;
106
- const innerHeight = height - borderWidth * 2;
107
- const fillWidth = Math.max(0, innerWidth * progress);
108
-
109
- this._fill.clear();
110
- if (fillWidth > 0) {
111
- this._fill.x = borderWidth;
112
- this._fill.y = borderWidth;
113
- this._fill.roundRect(0, 0, fillWidth, innerHeight, borderRadius - 1).fill(fillColor);
114
-
115
- // Highlight
116
- this._fill
117
- .roundRect(0, 0, fillWidth, innerHeight * 0.4, borderRadius - 1)
118
- .fill({ color: 0xffffff, alpha: 0.15 });
119
- }
114
+ this._bar.progress = this._displayedProgress * 100;
120
115
  }
121
116
  }
@@ -0,0 +1,126 @@
1
+ import { Container, type ColorSource } from 'pixi.js';
2
+ import { ScrollBox as PixiScrollBox } from '@pixi/ui';
3
+ import type { ScrollBoxOptions } from '@pixi/ui';
4
+
5
+ // ─── Types ───────────────────────────────────────────────
6
+
7
+ export type ScrollDirection = 'vertical' | 'horizontal' | 'both';
8
+
9
+ export interface ScrollContainerConfig {
10
+ /** Visible viewport width */
11
+ width: number;
12
+
13
+ /** Visible viewport height */
14
+ height: number;
15
+
16
+ /** Scroll direction (default: 'vertical') */
17
+ direction?: ScrollDirection;
18
+
19
+ /** Background color (undefined = transparent) */
20
+ backgroundColor?: ColorSource;
21
+
22
+ /** Border radius for the mask (default: 0) */
23
+ borderRadius?: number;
24
+
25
+ /** Gap between items (default: 0) */
26
+ elementsMargin?: number;
27
+
28
+ /** Padding */
29
+ padding?: number;
30
+
31
+ /** Disable dynamic rendering (render all items even when offscreen) */
32
+ disableDynamicRendering?: boolean;
33
+
34
+ /** Disable easing/inertia */
35
+ disableEasing?: boolean;
36
+
37
+ /** Global scroll — scroll even when mouse is not over the component */
38
+ globalScroll?: boolean;
39
+ }
40
+
41
+ const DIRECTION_MAP: Record<ScrollDirection, 'vertical' | 'horizontal' | 'bidirectional'> = {
42
+ vertical: 'vertical',
43
+ horizontal: 'horizontal',
44
+ both: 'bidirectional',
45
+ };
46
+
47
+ /**
48
+ * Scrollable container powered by `@pixi/ui` ScrollBox.
49
+ *
50
+ * Provides touch/drag scrolling, mouse wheel support, inertia, and
51
+ * dynamic rendering optimization for off-screen items.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * const scroll = new ScrollContainer({
56
+ * width: 600,
57
+ * height: 400,
58
+ * direction: 'vertical',
59
+ * elementsMargin: 8,
60
+ * });
61
+ *
62
+ * for (let i = 0; i < 50; i++) {
63
+ * scroll.addItem(createRow(i));
64
+ * }
65
+ *
66
+ * scene.container.addChild(scroll);
67
+ * ```
68
+ */
69
+ export class ScrollContainer extends PixiScrollBox {
70
+ private _scrollConfig: ScrollContainerConfig;
71
+
72
+ constructor(config: ScrollContainerConfig) {
73
+ const options: ScrollBoxOptions = {
74
+ width: config.width,
75
+ height: config.height,
76
+ type: DIRECTION_MAP[config.direction ?? 'vertical'],
77
+ radius: config.borderRadius ?? 0,
78
+ elementsMargin: config.elementsMargin ?? 0,
79
+ padding: config.padding ?? 0,
80
+ disableDynamicRendering: config.disableDynamicRendering ?? false,
81
+ disableEasing: config.disableEasing ?? false,
82
+ globalScroll: config.globalScroll ?? true,
83
+ };
84
+
85
+ if (config.backgroundColor !== undefined) {
86
+ options.background = config.backgroundColor;
87
+ }
88
+
89
+ super(options);
90
+
91
+ this._scrollConfig = config;
92
+ }
93
+
94
+ /** Set scrollable content. Replaces any existing content. */
95
+ setContent(content: Container): void {
96
+ // Remove existing items
97
+ const existing = this.items;
98
+ if (existing.length > 0) {
99
+ for (let i = existing.length - 1; i >= 0; i--) {
100
+ this.removeItem(i);
101
+ }
102
+ }
103
+
104
+ // Add all children from the content container
105
+ const children = [...content.children] as Container[];
106
+ if (children.length > 0) {
107
+ this.addItems(children);
108
+ }
109
+ }
110
+
111
+ /** Add a single item */
112
+ addItem(...items: Container[]): Container {
113
+ this.addItems(items);
114
+ return items[0];
115
+ }
116
+
117
+ /** Scroll to make a specific item/child visible */
118
+ scrollToItem(index: number): void {
119
+ this.scrollTo(index);
120
+ }
121
+
122
+ /** Current scroll position */
123
+ get scrollPosition(): { x: number; y: number } {
124
+ return { x: this.scrollX, y: this.scrollY };
125
+ }
126
+ }
package/src/ui/Toast.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { Container, Graphics, Text } from 'pixi.js';
1
+ import { Container, Text } from 'pixi.js';
2
+ import { LayoutContainer } from '@pixi/layout/components';
2
3
  import { Tween } from '../animation/Tween';
3
4
  import { Easing } from '../animation/Easing';
4
5
 
@@ -21,6 +22,8 @@ const TOAST_COLORS: Record<ToastType, number> = {
21
22
  /**
22
23
  * Toast notification component for displaying transient messages.
23
24
  *
25
+ * Uses `@pixi/layout` LayoutContainer for auto-sized background.
26
+ *
24
27
  * @example
25
28
  * ```ts
26
29
  * const toast = new Toast();
@@ -29,7 +32,7 @@ const TOAST_COLORS: Record<ToastType, number> = {
29
32
  * ```
30
33
  */
31
34
  export class Toast extends Container {
32
- private _bg: Graphics;
35
+ private _bg: LayoutContainer;
33
36
  private _text: Text;
34
37
  private _config: Required<ToastConfig>;
35
38
  private _dismissTimeout: ReturnType<typeof setTimeout> | null = null;
@@ -38,12 +41,11 @@ export class Toast extends Container {
38
41
  super();
39
42
 
40
43
  this._config = {
41
- duration: 3000,
42
- bottomOffset: 60,
43
- ...config,
44
+ duration: config.duration ?? 3000,
45
+ bottomOffset: config.bottomOffset ?? 60,
44
46
  };
45
47
 
46
- this._bg = new Graphics();
48
+ this._bg = new LayoutContainer();
47
49
  this.addChild(this._bg);
48
50
 
49
51
  this._text = new Text({
@@ -69,7 +71,6 @@ export class Toast extends Container {
69
71
  viewWidth?: number,
70
72
  viewHeight?: number,
71
73
  ): Promise<void> {
72
- // Clear previous dismiss
73
74
  if (this._dismissTimeout) {
74
75
  clearTimeout(this._dismissTimeout);
75
76
  }
@@ -81,10 +82,17 @@ export class Toast extends Container {
81
82
  const height = 44;
82
83
  const radius = 8;
83
84
 
84
- this._bg.clear();
85
- this._bg.roundRect(-width / 2, -height / 2, width, height, radius).fill(TOAST_COLORS[type]);
86
- this._bg.roundRect(-width / 2, -height / 2, width, height, radius)
87
- .fill({ color: 0x000000, alpha: 0.2 });
85
+ // Style the background
86
+ this._bg.layout = {
87
+ width,
88
+ height,
89
+ borderRadius: radius,
90
+ backgroundColor: TOAST_COLORS[type],
91
+ };
92
+
93
+ // Center the bg around origin
94
+ this._bg.x = -width / 2;
95
+ this._bg.y = -height / 2;
88
96
 
89
97
  // Position
90
98
  if (viewWidth && viewHeight) {
@@ -96,10 +104,8 @@ export class Toast extends Container {
96
104
  this.alpha = 0;
97
105
  this.y += 20;
98
106
 
99
- // Animate in
100
107
  await Tween.to(this, { alpha: 1, y: this.y - 20 }, 300, Easing.easeOutCubic);
101
108
 
102
- // Auto-dismiss
103
109
  if (this._config.duration > 0) {
104
110
  this._dismissTimeout = setTimeout(() => {
105
111
  this.dismiss();
package/src/ui/index.ts CHANGED
@@ -1,3 +1,7 @@
1
+ // ─── @pixi/layout setup (must be imported before creating containers) ────
2
+ import '@pixi/layout';
3
+
4
+ // ─── Engine UI Components ─────────────────────────────────
1
5
  export { Button } from './Button';
2
6
  export type { ButtonConfig, ButtonState } from './Button';
3
7
  export { ProgressBar } from './ProgressBar';
@@ -14,3 +18,16 @@ export { Modal } from './Modal';
14
18
  export type { ModalConfig } from './Modal';
15
19
  export { Toast } from './Toast';
16
20
  export type { ToastConfig, ToastType } from './Toast';
21
+ export { Layout } from './Layout';
22
+ export type { LayoutConfig, LayoutDirection, LayoutAlignment, LayoutAnchor } from './Layout';
23
+ export { ScrollContainer } from './ScrollContainer';
24
+ export type { ScrollContainerConfig, ScrollDirection } from './ScrollContainer';
25
+
26
+ // ─── Re-exports from @pixi/ui for direct use ─────────────
27
+ export { FancyButton, ScrollBox, ButtonContainer } from '@pixi/ui';
28
+ export type { ButtonOptions, ScrollBoxOptions, ProgressBarOptions } from '@pixi/ui';
29
+
30
+ // ─── Re-exports from @pixi/layout for direct use ─────────
31
+ export { LayoutContainer } from '@pixi/layout/components';
32
+ export { Layout as PixiLayout } from '@pixi/layout';
33
+ export type { LayoutStyles, LayoutOptions } from '@pixi/layout';
@@ -205,6 +205,8 @@ export class ViewportManager extends EventEmitter<ViewportEvents> {
205
205
  this._destroyed = true;
206
206
  this._resizeObserver?.disconnect();
207
207
  this._resizeObserver = null;
208
+ // Remove fallback window resize listener if it was used
209
+ window.removeEventListener('resize', this.onWindowResize);
208
210
  if (this._resizeTimeout !== null) {
209
211
  clearTimeout(this._resizeTimeout);
210
212
  }