@caperjs/core 0.2.4 → 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.
- package/build/assetpack.mjs +90 -0
- package/build/index.mjs +3 -1
- package/build/internal/discovery.mjs +1 -1
- package/build/internal/manifest.mjs +8 -3
- package/build/internal/validate.mjs +3 -1
- package/build/plugins/assetTypes.mjs +4 -4
- package/build/plugins/caperConfig.mjs +12 -5
- package/build/plugins/lists.mjs +29 -9
- package/build/plugins/pruneFallbacks.mjs +25 -2
- package/cli/create.mjs +57 -6
- package/cli/create.test.mjs +36 -0
- package/cli/update.mjs +1 -1
- package/lib/{CaptionsPlugin-WPAcfAkx.js → CaptionsPlugin-cSJj-R9M.js} +5 -5
- package/lib/{CaptionsPlugin-WPAcfAkx.js.map → CaptionsPlugin-cSJj-R9M.js.map} +1 -1
- package/lib/{DataAdapter-DS-twowe.js → DataAdapter-D2tc6hx9.js} +15 -10
- package/lib/DataAdapter-D2tc6hx9.js.map +1 -0
- package/lib/{DebugRenderer-D1ryrRYx.js → DebugRenderer-CGUKy6Fy.js} +2 -2
- package/lib/{DebugRenderer-D1ryrRYx.js.map → DebugRenderer-CGUKy6Fy.js.map} +1 -1
- package/lib/{DevToolsPlugin-DH7zX7HU.js → DevToolsPlugin-B8GX3H9b.js} +2 -2
- package/lib/{DevToolsPlugin-DH7zX7HU.js.map → DevToolsPlugin-B8GX3H9b.js.map} +1 -1
- package/lib/{GSAPPlugin-BBtRjUqe.js → GSAPPlugin-Br9AjuTD.js} +6 -6
- package/lib/{GSAPPlugin-BBtRjUqe.js.map → GSAPPlugin-Br9AjuTD.js.map} +1 -1
- package/lib/{LayoutPlugin-Co6vcoV4.js → LayoutPlugin-BX3OP7pw.js} +4 -4
- package/lib/{LayoutPlugin-Co6vcoV4.js.map → LayoutPlugin-BX3OP7pw.js.map} +1 -1
- package/lib/{SpinePlugin-uN641Hmk.js → SpinePlugin-C-JDdXpf.js} +690 -793
- package/lib/SpinePlugin-C-JDdXpf.js.map +1 -0
- package/lib/{StatsPlugin-vZQRm6wh.js → StatsPlugin-yJlJtoQ2.js} +7 -7
- package/lib/StatsPlugin-yJlJtoQ2.js.map +1 -0
- package/lib/{VoiceOverPlugin-KlZdGj1G.js → VoiceOverPlugin-DjSZp4Ko.js} +7 -6
- package/lib/VoiceOverPlugin-DjSZp4Ko.js.map +1 -0
- package/lib/caper.mjs +159 -1917
- package/lib/caper.mjs.map +1 -1
- package/lib/const-C3sxeYhl.js +1746 -0
- package/lib/const-C3sxeYhl.js.map +1 -0
- package/lib/core/Application.d.ts +8 -3
- package/lib/core/Application.d.ts.map +1 -1
- package/lib/core/Application.test.d.ts +2 -0
- package/lib/core/Application.test.d.ts.map +1 -0
- package/lib/core/config.d.ts +6 -1
- package/lib/core/config.d.ts.map +1 -1
- package/lib/core/config.test.d.ts +2 -0
- package/lib/core/config.test.d.ts.map +1 -0
- package/lib/core/create.d.ts.map +1 -1
- package/lib/core/interfaces/IApplication.d.ts +6 -6
- package/lib/core/interfaces/IApplication.d.ts.map +1 -1
- package/lib/core/interfaces/ICoreSignals.d.ts +2 -1
- package/lib/core/interfaces/ICoreSignals.d.ts.map +1 -1
- package/lib/display/Camera.test.d.ts +2 -0
- package/lib/display/Camera.test.d.ts.map +1 -0
- package/lib/display/Entity.d.ts +2 -2
- package/lib/display/SceneTransition.test.d.ts +1 -0
- package/lib/display/SceneTransition.test.d.ts.map +1 -0
- package/lib/display/SpineAnimation.d.ts +3 -3
- package/lib/mixins/factory/methods.d.ts.map +1 -1
- package/lib/padding-1kHDpGlQ.js +41 -0
- package/lib/padding-1kHDpGlQ.js.map +1 -0
- package/lib/plugins/AssetsPlugin.d.ts.map +1 -1
- package/lib/plugins/AssetsPlugin.test.d.ts +2 -0
- package/lib/plugins/AssetsPlugin.test.d.ts.map +1 -0
- package/lib/plugins/DataAdapter.d.ts.map +1 -1
- package/lib/plugins/DataAdapter.test.d.ts +2 -0
- package/lib/plugins/DataAdapter.test.d.ts.map +1 -0
- package/lib/plugins/FullScreenPlugin.d.ts +0 -5
- package/lib/plugins/FullScreenPlugin.d.ts.map +1 -1
- package/lib/plugins/FullScreenPlugin.test.d.ts +2 -0
- package/lib/plugins/FullScreenPlugin.test.d.ts.map +1 -0
- package/lib/plugins/GSAPPlugin.d.ts +0 -2
- package/lib/plugins/GSAPPlugin.d.ts.map +1 -1
- package/lib/plugins/GSAPPlugin.test.d.ts +2 -0
- package/lib/plugins/GSAPPlugin.test.d.ts.map +1 -0
- package/lib/plugins/KeyboardPlugin.d.ts +0 -1
- package/lib/plugins/KeyboardPlugin.d.ts.map +1 -1
- package/lib/plugins/LookupPlugin.d.ts +6 -1
- package/lib/plugins/LookupPlugin.d.ts.map +1 -1
- package/lib/plugins/LookupPlugin.test.d.ts +2 -0
- package/lib/plugins/LookupPlugin.test.d.ts.map +1 -0
- package/lib/plugins/Plugin.d.ts +101 -2
- package/lib/plugins/Plugin.d.ts.map +1 -1
- package/lib/plugins/PopupManagerPlugin.d.ts.map +1 -1
- package/lib/plugins/PopupManagerPlugin.test.d.ts +1 -0
- package/lib/plugins/PopupManagerPlugin.test.d.ts.map +1 -0
- package/lib/plugins/SceneManagerPlugin.d.ts +1 -1
- package/lib/plugins/SceneManagerPlugin.d.ts.map +1 -1
- package/lib/plugins/SceneManagerPlugin.test.d.ts +2 -0
- package/lib/plugins/SceneManagerPlugin.test.d.ts.map +1 -0
- package/lib/plugins/StatsPlugin.d.ts.map +1 -1
- package/lib/plugins/TimerPlugin.d.ts.map +1 -1
- package/lib/plugins/WebEventsPlugin.d.ts +0 -1
- package/lib/plugins/WebEventsPlugin.d.ts.map +1 -1
- package/lib/plugins/actions/ActionsPlugin.test.d.ts +2 -0
- package/lib/plugins/actions/ActionsPlugin.test.d.ts.map +1 -0
- package/lib/plugins/audio/AudioChannel.d.ts.map +1 -1
- package/lib/plugins/audio/AudioManagerPlugin.d.ts.map +1 -1
- package/lib/plugins/audio/AudioManagerPlugin.test.d.ts +2 -0
- package/lib/plugins/audio/AudioManagerPlugin.test.d.ts.map +1 -0
- package/lib/plugins/audio/VoiceOverPlugin.d.ts.map +1 -1
- package/lib/plugins/breakpoints/BreakpointPlugin.d.ts.map +1 -1
- package/lib/plugins/captions/CaptionsPlugin.test.d.ts +2 -0
- package/lib/plugins/captions/CaptionsPlugin.test.d.ts.map +1 -0
- package/lib/plugins/focus/FocusManagerPlugin.d.ts.map +1 -1
- package/lib/plugins/focus/FocusManagerPlugin.test.d.ts +2 -0
- package/lib/plugins/focus/FocusManagerPlugin.test.d.ts.map +1 -0
- package/lib/plugins/gesture/GesturePlugin.d.ts.map +1 -1
- package/lib/plugins/input/AbstractControls.d.ts +47 -3
- package/lib/plugins/input/AbstractControls.d.ts.map +1 -1
- package/lib/plugins/input/Controls.test.d.ts +2 -0
- package/lib/plugins/input/Controls.test.d.ts.map +1 -0
- package/lib/plugins/input/InputPlugin.d.ts.map +1 -1
- package/lib/plugins/input/controlsCore.d.ts +58 -0
- package/lib/plugins/input/controlsCore.d.ts.map +1 -0
- package/lib/plugins/input/controlsCore.test.d.ts +2 -0
- package/lib/plugins/input/controlsCore.test.d.ts.map +1 -0
- package/lib/plugins/input/keyboard/KeyboardControls.d.ts +15 -0
- package/lib/plugins/input/keyboard/KeyboardControls.d.ts.map +1 -0
- package/lib/plugins/input/keyboard/index.d.ts +1 -1
- package/lib/plugins/input/keyboard/index.d.ts.map +1 -1
- package/lib/plugins/input/touch/VirtualControls.d.ts +8 -14
- package/lib/plugins/input/touch/VirtualControls.d.ts.map +1 -1
- package/lib/plugins/input/touch/VirtualControls.test.d.ts +2 -0
- package/lib/plugins/input/touch/VirtualControls.test.d.ts.map +1 -0
- package/lib/{registries-BLrxALZ0.js → registries-BfV-VF7a.js} +665 -597
- package/lib/registries-BfV-VF7a.js.map +1 -0
- package/lib/store/Store.d.ts.map +1 -1
- package/lib/ui/Button.d.ts.map +1 -1
- package/lib/ui/UICanvas.d.ts +10 -1
- package/lib/ui/UICanvas.d.ts.map +1 -1
- package/lib/ui/UICanvas.test.d.ts +0 -1
- package/lib/ui/UICanvas.test.d.ts.map +1 -1
- package/lib/utils/color.d.ts.map +1 -1
- package/lib/utils/color.test.d.ts +2 -0
- package/lib/utils/color.test.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/core/Application.test.ts +168 -0
- package/src/core/Application.ts +37 -27
- package/src/core/config.test.ts +79 -0
- package/src/core/config.ts +28 -6
- package/src/core/create.ts +11 -0
- package/src/core/interfaces/IApplication.ts +6 -6
- package/src/core/interfaces/ICoreSignals.ts +2 -0
- package/src/display/Camera.test.ts +40 -0
- package/src/display/Camera.ts +3 -3
- package/src/display/Entity.ts +2 -2
- package/src/display/SceneTransition.test.ts +58 -0
- package/src/display/SceneTransition.ts +1 -1
- package/src/display/SpineAnimation.ts +4 -4
- package/src/mixins/factory/methods.ts +0 -1
- package/src/plugins/AssetsPlugin.test.ts +48 -0
- package/src/plugins/AssetsPlugin.ts +3 -1
- package/src/plugins/DataAdapter.test.ts +32 -0
- package/src/plugins/DataAdapter.ts +5 -1
- package/src/plugins/FullScreenPlugin.test.ts +48 -0
- package/src/plugins/FullScreenPlugin.ts +9 -18
- package/src/plugins/GSAPPlugin.test.ts +31 -0
- package/src/plugins/GSAPPlugin.ts +1 -4
- package/src/plugins/KeyboardPlugin.ts +3 -10
- package/src/plugins/LookupPlugin.test.ts +42 -0
- package/src/plugins/LookupPlugin.ts +15 -3
- package/src/plugins/Plugin.test.ts +130 -2
- package/src/plugins/Plugin.ts +159 -3
- package/src/plugins/PopupManagerPlugin.test.ts +53 -0
- package/src/plugins/PopupManagerPlugin.ts +5 -3
- package/src/plugins/SceneManagerPlugin.test.ts +76 -0
- package/src/plugins/SceneManagerPlugin.ts +18 -13
- package/src/plugins/StatsPlugin.ts +3 -2
- package/src/plugins/TimerPlugin.ts +1 -2
- package/src/plugins/WebEventsPlugin.test.ts +32 -0
- package/src/plugins/WebEventsPlugin.ts +9 -16
- package/src/plugins/actions/ActionsPlugin.test.ts +68 -0
- package/src/plugins/actions/ActionsPlugin.ts +5 -5
- package/src/plugins/audio/AudioChannel.test.ts +19 -0
- package/src/plugins/audio/AudioChannel.ts +8 -1
- package/src/plugins/audio/AudioManagerPlugin.test.ts +41 -0
- package/src/plugins/audio/AudioManagerPlugin.ts +3 -2
- package/src/plugins/audio/VoiceOverPlugin.ts +1 -0
- package/src/plugins/breakpoints/BreakpointPlugin.ts +1 -2
- package/src/plugins/captions/CaptionsPlugin.test.ts +40 -0
- package/src/plugins/captions/CaptionsPlugin.ts +2 -2
- package/src/plugins/focus/FocusManagerPlugin.test.ts +71 -0
- package/src/plugins/focus/FocusManagerPlugin.ts +7 -0
- package/src/plugins/gesture/GesturePlugin.ts +4 -8
- package/src/plugins/input/AbstractControls.ts +104 -3
- package/src/plugins/input/Controls.test.ts +205 -0
- package/src/plugins/input/InputPlugin.ts +6 -14
- package/src/plugins/input/controlsCore.test.ts +151 -0
- package/src/plugins/input/controlsCore.ts +162 -0
- package/src/plugins/input/keyboard/KeyboardControls.ts +84 -0
- package/src/plugins/input/keyboard/index.ts +1 -1
- package/src/plugins/input/touch/VirtualControls.test.ts +38 -0
- package/src/plugins/input/touch/VirtualControls.ts +53 -120
- package/src/plugins/spine/SpinePlugin.ts +2 -2
- package/src/store/Store.test.ts +15 -0
- package/src/store/Store.ts +4 -1
- package/src/ui/Button.test.ts +12 -0
- package/src/ui/Button.ts +1 -0
- package/src/ui/UICanvas.test.ts +109 -6
- package/src/ui/UICanvas.ts +41 -3
- package/src/utils/color.test.ts +16 -0
- package/src/utils/color.ts +2 -1
- package/src/utils/platform.ts +1 -1
- package/src/version.ts +1 -1
- package/lib/DataAdapter-DS-twowe.js.map +0 -1
- package/lib/SpinePlugin-uN641Hmk.js.map +0 -1
- package/lib/StatsPlugin-vZQRm6wh.js.map +0 -1
- package/lib/VoiceOverPlugin-KlZdGj1G.js.map +0 -1
- package/lib/plugins/input/keyboard/KeyboardContols.d.ts +0 -28
- package/lib/plugins/input/keyboard/KeyboardContols.d.ts.map +0 -1
- package/lib/registries-BLrxALZ0.js.map +0 -1
- package/src/plugins/input/keyboard/KeyboardContols.ts +0 -182
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Container as PixiContainer } from 'pixi.js';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
// Camera transitively imports Application → Pixi display graph. Stub it.
|
|
5
|
+
vi.mock('../core', () => ({
|
|
6
|
+
coreFunctionRegistry: {},
|
|
7
|
+
coreSignalRegistry: {},
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
const mocks = vi.hoisted(() => ({
|
|
11
|
+
app: {
|
|
12
|
+
size: { width: 800, height: 400 },
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
vi.mock('../core/Application', () => ({
|
|
17
|
+
Application: { getInstance: () => mocks.app },
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
import { Camera } from './Camera';
|
|
21
|
+
|
|
22
|
+
describe('Camera targetScale', () => {
|
|
23
|
+
it('reflects the target scale, not the target pivot', () => {
|
|
24
|
+
const camera = new Camera({ container: new PixiContainer(), viewportWidth: 200, viewportHeight: 100 });
|
|
25
|
+
|
|
26
|
+
camera.zoom(3);
|
|
27
|
+
|
|
28
|
+
expect(camera.targetScale.x).toBe(3);
|
|
29
|
+
expect(camera.targetScale.y).toBe(3);
|
|
30
|
+
expect(camera.targetScale).not.toBe(camera.targetPivot);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('Camera viewportHeight default', () => {
|
|
35
|
+
it('falls back to app.size.height, not app.size.width', () => {
|
|
36
|
+
const camera = new Camera({ container: new PixiContainer() });
|
|
37
|
+
|
|
38
|
+
expect(camera.viewportHeight).toBe(mocks.app.size.height);
|
|
39
|
+
});
|
|
40
|
+
});
|
package/src/display/Camera.ts
CHANGED
|
@@ -85,7 +85,7 @@ export class Camera extends Container implements ICamera {
|
|
|
85
85
|
this.minY = config.minY;
|
|
86
86
|
}
|
|
87
87
|
this.viewportWidth = config.viewportWidth ?? this.app.size.width;
|
|
88
|
-
this.viewportHeight = config.viewportHeight ?? this.app.size.
|
|
88
|
+
this.viewportHeight = config.viewportHeight ?? this.app.size.height;
|
|
89
89
|
this.worldWidth = config.worldWidth ?? this.viewportWidth;
|
|
90
90
|
this.worldHeight = config.worldHeight ?? this.viewportHeight;
|
|
91
91
|
this.maxX = config.maxX ?? this.worldWidth - this.viewportWidth;
|
|
@@ -125,7 +125,7 @@ export class Camera extends Container implements ICamera {
|
|
|
125
125
|
protected _targetScale: Point = new Point(1, 1);
|
|
126
126
|
|
|
127
127
|
get targetScale(): Point {
|
|
128
|
-
return this.
|
|
128
|
+
return this._targetScale;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
private _lerp: number = 0;
|
|
@@ -135,7 +135,7 @@ export class Camera extends Container implements ICamera {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
set lerp(value: number) {
|
|
138
|
-
// if the value is less than 0 or greater than 1,
|
|
138
|
+
// if the value is less than 0 or greater than 1, throw an error
|
|
139
139
|
if (value < 0 || value > 1) {
|
|
140
140
|
throw new Error('Camera lerp value must be in the range [0, 1]');
|
|
141
141
|
}
|
package/src/display/Entity.ts
CHANGED
|
@@ -9,8 +9,8 @@ import { Container } from './Container';
|
|
|
9
9
|
*
|
|
10
10
|
* **Lifecycle** (inherited from Container):
|
|
11
11
|
*
|
|
12
|
-
* 1. `constructor(props)` —
|
|
13
|
-
*
|
|
12
|
+
* 1. `constructor(props)` — Container's constructor runs first, then props
|
|
13
|
+
* are stashed on `this.props`. Don't reference `this.app` yet.
|
|
14
14
|
* 2. **addChild** — the factory auto-adds the instance to the calling
|
|
15
15
|
* Container; Pixi emits an `added` event.
|
|
16
16
|
* 3. `added()` — override this to build the display tree using
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
// Registers the factory method table `Factory()` (part of Container's mixin
|
|
3
|
+
// chain) reads from — must be imported before any display/ui class is
|
|
4
|
+
// constructed. See importOrder.containerFirst.test.ts.
|
|
5
|
+
import '../mixins/factory/const';
|
|
6
|
+
import { Signal } from '../signals';
|
|
7
|
+
import { SceneTransition } from './SceneTransition';
|
|
8
|
+
|
|
9
|
+
describe('SceneTransition asset signal wiring', () => {
|
|
10
|
+
let app: {
|
|
11
|
+
assets: {
|
|
12
|
+
onLoadStart: Signal<() => void>;
|
|
13
|
+
onLoadProgress: Signal<(progress: number) => void>;
|
|
14
|
+
onLoadComplete: Signal<() => void>;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
let originalAppDescriptor: PropertyDescriptor | undefined;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
app = {
|
|
21
|
+
assets: {
|
|
22
|
+
onLoadStart: new Signal<() => void>(),
|
|
23
|
+
onLoadProgress: new Signal<(progress: number) => void>(),
|
|
24
|
+
onLoadComplete: new Signal<() => void>(),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
// Container.app is a getter; override it on the prototype so the
|
|
28
|
+
// constructor picks up the fake app before the instance even exists.
|
|
29
|
+
originalAppDescriptor = Object.getOwnPropertyDescriptor(SceneTransition.prototype, 'app');
|
|
30
|
+
Object.defineProperty(SceneTransition.prototype, 'app', { get: () => app, configurable: true });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
if (originalAppDescriptor) {
|
|
35
|
+
Object.defineProperty(SceneTransition.prototype, 'app', originalAppDescriptor);
|
|
36
|
+
} else {
|
|
37
|
+
delete (SceneTransition.prototype as any).app;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('routes progress ticks to handleLoadProgress and completion to handleLoadComplete', () => {
|
|
42
|
+
const progressSpy = vi.spyOn(SceneTransition.prototype as any, 'handleLoadProgress');
|
|
43
|
+
const completeSpy = vi.spyOn(SceneTransition.prototype as any, 'handleLoadComplete');
|
|
44
|
+
|
|
45
|
+
const transition = new SceneTransition();
|
|
46
|
+
void transition;
|
|
47
|
+
|
|
48
|
+
app.assets.onLoadProgress.emit(0.5);
|
|
49
|
+
|
|
50
|
+
expect(progressSpy).toHaveBeenCalledTimes(1);
|
|
51
|
+
expect(completeSpy).not.toHaveBeenCalled();
|
|
52
|
+
|
|
53
|
+
app.assets.onLoadComplete.emit();
|
|
54
|
+
|
|
55
|
+
expect(completeSpy).toHaveBeenCalledTimes(1);
|
|
56
|
+
expect(progressSpy).toHaveBeenCalledTimes(1);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -46,7 +46,7 @@ export class SceneTransition extends Container {
|
|
|
46
46
|
this.addSignalConnection(
|
|
47
47
|
this.app.assets.onLoadStart.connect(this.handleLoadStart),
|
|
48
48
|
this.app.assets.onLoadProgress.connect(this.handleLoadProgress),
|
|
49
|
-
this.app.assets.
|
|
49
|
+
this.app.assets.onLoadComplete.connect(this.handleLoadComplete),
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -20,8 +20,8 @@ export interface ISpineAnimation<ANames extends string = string> extends Instanc
|
|
|
20
20
|
readonly onAnimationEvent: Signal<(entry: TrackEntry, event: Event) => void>;
|
|
21
21
|
readonly onPaused: Signal<(entry: TrackEntry | null) => void>;
|
|
22
22
|
readonly onResumed: Signal<(entry: TrackEntry | null) => void>;
|
|
23
|
-
setAnimation(name: ANames, loop?: boolean,
|
|
24
|
-
getCurrentAnimation(
|
|
23
|
+
setAnimation(name: ANames, loop?: boolean, trackIndex?: number): void;
|
|
24
|
+
getCurrentAnimation(trackIndex?: number): ANames;
|
|
25
25
|
pause(): void;
|
|
26
26
|
resume(): void;
|
|
27
27
|
togglePause(): void;
|
|
@@ -113,8 +113,8 @@ export class SpineAnimation<ANames extends string = string> extends _SpineAnimat
|
|
|
113
113
|
return (this.spine.state.getCurrent(trackIndex)?.animation?.name as ANames) || ('' as ANames);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
setAnimation(name: ANames, loop = false,
|
|
117
|
-
this.spine.state.setAnimation(
|
|
116
|
+
setAnimation(name: ANames, loop = false, trackIndex: number = 0) {
|
|
117
|
+
this.spine.state.setAnimation(trackIndex, name, loop);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
pause() {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// AssetsPlugin extends Plugin, which transitively imports Application → Pixi
|
|
4
|
+
// display graph. Stub it the same way Plugin.test.ts does.
|
|
5
|
+
vi.mock('../core', () => ({
|
|
6
|
+
coreFunctionRegistry: {},
|
|
7
|
+
coreSignalRegistry: {},
|
|
8
|
+
}));
|
|
9
|
+
vi.mock('../core/Application', () => ({
|
|
10
|
+
Application: { getInstance: () => ({ canvas: { dispatchEvent: vi.fn() } }) },
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock('pixi.js', async (importOriginal) => {
|
|
14
|
+
const actual = await importOriginal<typeof import('pixi.js')>();
|
|
15
|
+
return {
|
|
16
|
+
...actual,
|
|
17
|
+
Assets: {
|
|
18
|
+
load: vi.fn(async () => undefined),
|
|
19
|
+
loadBundle: vi.fn(async () => undefined),
|
|
20
|
+
setPreferences: vi.fn(),
|
|
21
|
+
backgroundLoad: vi.fn(async () => undefined),
|
|
22
|
+
backgroundLoadBundle: vi.fn(async () => undefined),
|
|
23
|
+
unload: vi.fn(async () => undefined),
|
|
24
|
+
unloadBundle: vi.fn(async () => undefined),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
import { Assets } from 'pixi.js';
|
|
30
|
+
import { AssetsPlugin } from './AssetsPlugin';
|
|
31
|
+
|
|
32
|
+
const loadBundle = vi.mocked(Assets.loadBundle);
|
|
33
|
+
|
|
34
|
+
describe('AssetsPlugin', () => {
|
|
35
|
+
it('loadRequired only requests bundles that are not already loaded', async () => {
|
|
36
|
+
const plugin = new AssetsPlugin();
|
|
37
|
+
|
|
38
|
+
// Mark bundle 'a' as already loaded.
|
|
39
|
+
await plugin.loadBundles('a');
|
|
40
|
+
loadBundle.mockClear();
|
|
41
|
+
|
|
42
|
+
plugin.initialize({ preload: { bundles: ['a', 'b'] } });
|
|
43
|
+
await plugin.loadRequired();
|
|
44
|
+
|
|
45
|
+
expect(loadBundle).toHaveBeenCalledTimes(1);
|
|
46
|
+
expect(loadBundle).toHaveBeenCalledWith(['b'], expect.anything());
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -187,7 +187,9 @@ export class AssetsPlugin extends Plugin<AssetLoadingOptions> implements IAssets
|
|
|
187
187
|
bundles = [bundles];
|
|
188
188
|
}
|
|
189
189
|
bundles = bundles.filter((bundle) => !this._isBundleLoaded(bundle));
|
|
190
|
-
|
|
190
|
+
if (bundles.length) {
|
|
191
|
+
await Assets.loadBundle(bundles, this._handleLoadProgress);
|
|
192
|
+
}
|
|
191
193
|
}
|
|
192
194
|
}
|
|
193
195
|
this._handleLoadComplete();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// DataAdapter extends Plugin, which transitively imports Application → Pixi display graph. Stub it.
|
|
4
|
+
vi.mock('../core', () => ({
|
|
5
|
+
coreFunctionRegistry: {},
|
|
6
|
+
coreSignalRegistry: {},
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('../core/Application', () => ({
|
|
9
|
+
Application: { getInstance: () => ({}) },
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
import { vi } from 'vitest';
|
|
13
|
+
import { DataAdapter } from './DataAdapter';
|
|
14
|
+
|
|
15
|
+
describe('DataAdapter', () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
localStorage.clear();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('clear() with no key removes only this namespace\'s keys, leaving unrelated keys intact', () => {
|
|
21
|
+
localStorage.setItem('myns-foo', '"bar"');
|
|
22
|
+
localStorage.setItem('other-bar', '"baz"');
|
|
23
|
+
|
|
24
|
+
const adapter = new DataAdapter();
|
|
25
|
+
adapter.initialize({ namespace: 'myns', initial: { foo: 'bar' } });
|
|
26
|
+
|
|
27
|
+
adapter.clear();
|
|
28
|
+
|
|
29
|
+
expect(localStorage.getItem('myns-foo')).toBeNull();
|
|
30
|
+
expect(localStorage.getItem('other-bar')).toBe('"baz"');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -103,6 +103,7 @@ export class DataAdapter extends Plugin<Partial<IDataAdapterOptions>> implements
|
|
|
103
103
|
|
|
104
104
|
destroy() {
|
|
105
105
|
this.data = {} as DataAdapterType;
|
|
106
|
+
super.destroy();
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
public initialize(options?: Partial<IDataAdapterOptions>): void {
|
|
@@ -205,7 +206,10 @@ export class DataAdapter extends Plugin<Partial<IDataAdapterOptions>> implements
|
|
|
205
206
|
clear<K extends keyof DataAdapterType>(key?: K): void {
|
|
206
207
|
if (key === undefined) {
|
|
207
208
|
this.data = {} as DataAdapterType;
|
|
208
|
-
|
|
209
|
+
const prefix = `${this.namespace}-`;
|
|
210
|
+
for (const storageKey of Object.keys(localStorage).filter((k) => k.startsWith(prefix))) {
|
|
211
|
+
localStorage.removeItem(storageKey);
|
|
212
|
+
}
|
|
209
213
|
this.onDataChange.emit({ key, clear: true });
|
|
210
214
|
} else {
|
|
211
215
|
delete this.data[key];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// FullScreenPlugin transitively imports Application → Pixi display graph. Stub it.
|
|
4
|
+
vi.mock('../core', () => ({
|
|
5
|
+
coreFunctionRegistry: {},
|
|
6
|
+
coreSignalRegistry: {},
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('../core/Application', () => ({
|
|
9
|
+
Application: { getInstance: () => ({}), containerElement: null },
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
import { FullScreenPlugin } from './FullScreenPlugin';
|
|
13
|
+
|
|
14
|
+
describe('FullScreenPlugin fullscreenchange handling', () => {
|
|
15
|
+
let plugin: FullScreenPlugin;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
plugin = new FullScreenPlugin();
|
|
19
|
+
plugin.initialize();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
plugin.destroy();
|
|
24
|
+
delete (document as any).webkitFullscreenElement;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('clears the cached state when the browser leaves fullscreen', () => {
|
|
28
|
+
(plugin as any)._isFullScreen = true;
|
|
29
|
+
const spy = vi.fn();
|
|
30
|
+
plugin.onFullScreenChange.connect(spy);
|
|
31
|
+
|
|
32
|
+
document.dispatchEvent(new Event('fullscreenchange'));
|
|
33
|
+
|
|
34
|
+
expect(plugin.isFullScreen).toBe(false);
|
|
35
|
+
expect(spy).toHaveBeenCalledWith(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('sets the cached state from vendor-prefixed fullscreen elements', () => {
|
|
39
|
+
(document as any).webkitFullscreenElement = document.body;
|
|
40
|
+
const spy = vi.fn();
|
|
41
|
+
plugin.onFullScreenChange.connect(spy);
|
|
42
|
+
|
|
43
|
+
document.dispatchEvent(new Event('webkitfullscreenchange'));
|
|
44
|
+
|
|
45
|
+
expect(plugin.isFullScreen).toBe(true);
|
|
46
|
+
expect(spy).toHaveBeenCalledWith(true);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -143,23 +143,11 @@ export class FullScreenPlugin extends Plugin implements IFullScreenPlugin {
|
|
|
143
143
|
* ```
|
|
144
144
|
*/
|
|
145
145
|
public initialize(): void {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Cleans up the plugin by removing all event listeners.
|
|
155
|
-
* Called automatically when the plugin is destroyed.
|
|
156
|
-
*/
|
|
157
|
-
public destroy() {
|
|
158
|
-
document.removeEventListener('fullscreenchange', this._onFullScreenChange);
|
|
159
|
-
document.removeEventListener('webkitfullscreenchange', this._onFullScreenChange);
|
|
160
|
-
document.removeEventListener('mozfullscreenchange', this._onFullScreenChange);
|
|
161
|
-
document.removeEventListener('msfullscreenchange', this._onFullScreenChange);
|
|
162
|
-
document.removeEventListener('fullscreenchange', this._onFullScreenChange);
|
|
146
|
+
this.listen(document, 'fullscreenchange', this._onFullScreenChange);
|
|
147
|
+
this.listen(document, 'webkitfullscreenchange', this._onFullScreenChange);
|
|
148
|
+
this.listen(document, 'mozfullscreenchange', this._onFullScreenChange);
|
|
149
|
+
this.listen(document, 'msfullscreenchange', this._onFullScreenChange);
|
|
150
|
+
this.listen(document, 'fullscreenchange', this._onFullScreenChange);
|
|
163
151
|
}
|
|
164
152
|
|
|
165
153
|
/**
|
|
@@ -354,6 +342,9 @@ export class FullScreenPlugin extends Plugin implements IFullScreenPlugin {
|
|
|
354
342
|
* @private
|
|
355
343
|
*/
|
|
356
344
|
private _onFullScreenChange(): void {
|
|
357
|
-
|
|
345
|
+
// the user can leave fullscreen without us (Esc / browser chrome), so re-read
|
|
346
|
+
// the real state instead of trusting the cached flag
|
|
347
|
+
this._isFullScreen = this.isFullscreen;
|
|
348
|
+
this.onFullScreenChange.emit(this._isFullScreen);
|
|
358
349
|
}
|
|
359
350
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// GSAPPlugin.ts extends Plugin.ts, which transitively imports Application → Pixi display graph. Stub it.
|
|
4
|
+
vi.mock('../core', () => ({
|
|
5
|
+
coreFunctionRegistry: {},
|
|
6
|
+
coreSignalRegistry: {},
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('../core/Application', () => ({
|
|
9
|
+
Application: { getInstance: () => ({}) },
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
import { GSAPPlugin } from './GSAPPlugin';
|
|
13
|
+
|
|
14
|
+
class TestGSAPPlugin extends GSAPPlugin {
|
|
15
|
+
public createGlobalContextPublic(): void {
|
|
16
|
+
this.createGlobalContext();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('GSAPPlugin', () => {
|
|
21
|
+
it('clearGlobal() does not throw and empties the global context', () => {
|
|
22
|
+
const plugin = new TestGSAPPlugin();
|
|
23
|
+
plugin.createGlobalContextPublic();
|
|
24
|
+
|
|
25
|
+
plugin.addAnimation({} as never);
|
|
26
|
+
expect(plugin.getContext('__caper_global')?.size).toBe(1);
|
|
27
|
+
|
|
28
|
+
expect(() => plugin.clearGlobal()).not.toThrow();
|
|
29
|
+
expect(plugin.getContext('__caper_global')?.size).toBe(0);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -348,9 +348,6 @@ export class GSAPPlugin extends Plugin implements IGSAPPlugin {
|
|
|
348
348
|
(detail: { animation: gsap.core.Tween | gsap.core.Timeline; contextId: string }) => void
|
|
349
349
|
>();
|
|
350
350
|
|
|
351
|
-
/** Global animation context reference for quick access */
|
|
352
|
-
private _globalContext: AnimationContext;
|
|
353
|
-
|
|
354
351
|
/** Map of all custom animation contexts by their string identifiers */
|
|
355
352
|
private _animationContexts: Map<string, AnimationContext> = new Map();
|
|
356
353
|
|
|
@@ -804,7 +801,7 @@ export class GSAPPlugin extends Plugin implements IGSAPPlugin {
|
|
|
804
801
|
if (kill) {
|
|
805
802
|
this.killGlobal();
|
|
806
803
|
}
|
|
807
|
-
this.
|
|
804
|
+
this._animationContexts.get(GSAPPlugin.GLOBAL_CONTEXT_ID)?.clear();
|
|
808
805
|
|
|
809
806
|
this.onClearGlobal.emit(kill);
|
|
810
807
|
}
|
|
@@ -61,15 +61,8 @@ export class KeyboardPlugin extends Plugin implements IKeyboardPlugin {
|
|
|
61
61
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
62
62
|
public initialize(_options: any, _app: IApplication): void {
|
|
63
63
|
// track which keys are down
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
public destroy() {
|
|
69
|
-
document.removeEventListener('keydown', this._handleKeyDown);
|
|
70
|
-
document.removeEventListener('keyup', this._handleKeyUp);
|
|
71
|
-
document.removeEventListener('keydown', this._handleEvent);
|
|
72
|
-
document.removeEventListener('keyup', this._handleEvent);
|
|
64
|
+
this.listen(document, 'keydown', this._handleKeyDown as EventListener);
|
|
65
|
+
this.listen(document, 'keyup', this._handleKeyUp as EventListener);
|
|
73
66
|
}
|
|
74
67
|
|
|
75
68
|
public onKeyDown(key?: KeyboardKey): KeySignal {
|
|
@@ -145,7 +138,7 @@ export class KeyboardPlugin extends Plugin implements IKeyboardPlugin {
|
|
|
145
138
|
}
|
|
146
139
|
|
|
147
140
|
private _listen(eventType: KeyboardEventType): void {
|
|
148
|
-
|
|
141
|
+
this.listen(document, eventType, this._handleEvent as EventListener);
|
|
149
142
|
}
|
|
150
143
|
|
|
151
144
|
private _handleEvent(event: KeyboardEvent): void {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// LookupPlugin -> Plugin.ts transitively imports Application → Pixi display graph. Stub it.
|
|
4
|
+
vi.mock('../core', () => ({
|
|
5
|
+
coreFunctionRegistry: {} as Record<string, unknown>,
|
|
6
|
+
coreSignalRegistry: {} as Record<string, unknown>,
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('../core/Application', () => ({
|
|
9
|
+
Application: { getInstance: () => ({}) },
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
import { coreFunctionRegistry } from '../core';
|
|
13
|
+
import { Container } from '../display/Container';
|
|
14
|
+
import { LookupPlugin } from './LookupPlugin';
|
|
15
|
+
|
|
16
|
+
describe('LookupPlugin', () => {
|
|
17
|
+
it('registers getChildAtPath (and its siblings) in the core function registry', () => {
|
|
18
|
+
const plugin = new LookupPlugin();
|
|
19
|
+
plugin.registerCoreFunctions();
|
|
20
|
+
|
|
21
|
+
expect((coreFunctionRegistry as Record<string, unknown>).getChildAtPath).toBe(plugin.getChildAtPath);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('disconnects its global container subscriptions on destroy', async () => {
|
|
25
|
+
const plugin = new LookupPlugin();
|
|
26
|
+
const added = vi.spyOn(plugin as any, 'onChildAdded');
|
|
27
|
+
const removed = vi.spyOn(plugin as any, 'onChildRemoved');
|
|
28
|
+
await plugin.initialize();
|
|
29
|
+
|
|
30
|
+
const child = { label: 'Thing', children: [] } as any;
|
|
31
|
+
Container.onGlobalChildAdded.emit(child);
|
|
32
|
+
Container.onGlobalChildRemoved.emit(child);
|
|
33
|
+
expect(added).toHaveBeenCalledTimes(1);
|
|
34
|
+
expect(removed).toHaveBeenCalledTimes(1);
|
|
35
|
+
|
|
36
|
+
plugin.destroy();
|
|
37
|
+
Container.onGlobalChildAdded.emit(child);
|
|
38
|
+
Container.onGlobalChildRemoved.emit(child);
|
|
39
|
+
expect(added).toHaveBeenCalledTimes(1);
|
|
40
|
+
expect(removed).toHaveBeenCalledTimes(1);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -97,7 +97,7 @@ export class LookupPlugin extends Plugin implements ILookupPlugin {
|
|
|
97
97
|
/**
|
|
98
98
|
* Core function names exposed by this plugin for framework integration.
|
|
99
99
|
*/
|
|
100
|
-
|
|
100
|
+
protected getCoreFunctions(): string[] {
|
|
101
101
|
return ['getChildAtPath', 'getPathForChild', 'getChildrenAtPaths', 'getPathsForChildren', 'getAllPaths'];
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -106,8 +106,20 @@ export class LookupPlugin extends Plugin implements ILookupPlugin {
|
|
|
106
106
|
* Sets up automatic tracking of container hierarchy changes.
|
|
107
107
|
*/
|
|
108
108
|
public async initialize() {
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
this.addSignalConnection(
|
|
110
|
+
Container.onGlobalChildAdded.connect(this.onChildAdded),
|
|
111
|
+
Container.onGlobalChildRemoved.connect(this.onChildRemoved),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Disconnects the global container subscriptions and drops the lookup maps,
|
|
117
|
+
* so a destroyed plugin stops tracking (and holding on to) containers.
|
|
118
|
+
*/
|
|
119
|
+
public destroy(): void {
|
|
120
|
+
this.pathToContainer.clear();
|
|
121
|
+
this.containerToPath.clear();
|
|
122
|
+
super.destroy();
|
|
111
123
|
}
|
|
112
124
|
|
|
113
125
|
/**
|