@kurot/core 2.5.3 → 2.5.4
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/CHANGELOG.md +44 -3
- package/README.md +39 -10
- package/dist/kurot/media/Sound.d.ts +5 -0
- package/dist/kurot/media/Sound.d.ts.map +1 -1
- package/dist/kurot/media/Sound.js +5 -0
- package/dist/kurot/media/Sound.js.map +1 -1
- package/dist/kurot/media/SoundChannel.d.ts +3 -0
- package/dist/kurot/media/SoundChannel.d.ts.map +1 -1
- package/dist/kurot/media/SoundChannel.js +47 -16
- package/dist/kurot/media/SoundChannel.js.map +1 -1
- package/docs/ai-context.md +15 -5
- package/docs/architecture.md +7 -1
- package/docs/audio-playback.md +82 -0
- package/docs/bitmap-fonts.md +1 -1
- package/docs/text-layout.md +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,9 +4,50 @@ All notable changes to `@kurot/core` are documented here.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [2.5.4] — 2026-10-12
|
|
8
|
+
|
|
9
|
+
Prepared for publication; not yet published.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Fix Web Audio repeats to use one native looping source, retaining the total
|
|
14
|
+
`loops` count and first-play offset. Finite playback ends on the audio clock;
|
|
15
|
+
joins no longer wait for JavaScript ended callbacks.
|
|
16
|
+
- Wrap channel position per iteration, freeze finite completion, suppress queued
|
|
17
|
+
callbacks after manual stop and release source/gain nodes.
|
|
18
|
+
- Use native HTML looping for unlimited fallback playback; retain finite fallback
|
|
19
|
+
counting and document its browser-dependent timing limitation.
|
|
20
|
+
- Add unit and minified browser regressions, including sample comparisons with
|
|
21
|
+
Tentax's application-owned MP3. See [audio playback](docs/audio-playback.md).
|
|
22
|
+
|
|
23
|
+
### Compatibility
|
|
24
|
+
|
|
25
|
+
- Preserve existing `Sound.play(startTime, loops)` parameters: non-positive counts
|
|
26
|
+
are unlimited, positive counts are total plays, and the offset applies only to
|
|
27
|
+
the first play. No public API, dependency, resource or XML-format change.
|
|
28
|
+
- Existing UI/Game/DragonBones/Spine/ui-runtime peers accept 2.5.4 without new
|
|
29
|
+
SDK releases or raised minima. Development and application locks are unchanged.
|
|
30
|
+
Apps adopt published 2.5.4 through their installation/lock and rebuild; exact
|
|
31
|
+
Core pins must be updated explicitly. CLI and headless packages need no release.
|
|
32
|
+
- Published Core 2.5.3 does not contain the fix. Continuous scheduling reproduces
|
|
33
|
+
decoded samples; it does not trim asset silence or repair waveform boundaries.
|
|
34
|
+
|
|
35
|
+
### Verification — 2026-10-12
|
|
36
|
+
|
|
37
|
+
- Core build and all 941 unit tests pass (81 files), including 41 audio tests.
|
|
38
|
+
- All 22 minified browser checks pass, covering 44.1/48 kHz, finite/unlimited
|
|
39
|
+
counts and first-play offsets. Tentax's current MP3 has zero inserted/missing
|
|
40
|
+
frames across unlimited/two-play joins and stops at the exact finite boundary.
|
|
41
|
+
Its unchanged music manager crosses the real-time boundary on one source
|
|
42
|
+
during a 200 ms control-thread stall, using an isolated checkout Core bundle.
|
|
43
|
+
- Clean prepublish build and package packing verify version 2.5.4, all 642 files,
|
|
44
|
+
native looping/finite scheduling, declarations and public docs. Packed files
|
|
45
|
+
match the checkout byte for byte; tests and the application MP3 are excluded.
|
|
46
|
+
Dependency declarations are unchanged and `git diff --check` passes.
|
|
47
|
+
|
|
7
48
|
## [2.5.3] — 2026-10-10
|
|
8
49
|
|
|
9
|
-
|
|
50
|
+
Published; package download, registry integrity and tested alignment modules verified on 2026-10-10.
|
|
10
51
|
|
|
11
52
|
### Changed
|
|
12
53
|
|
|
@@ -21,9 +62,9 @@ Prepared; not yet published. Published 2.5.2 remains the registry baseline.
|
|
|
21
62
|
|
|
22
63
|
- Text with omitted alignment can move within a taller box. Set top explicitly
|
|
23
64
|
for content intended to begin there; XML is not migrated automatically.
|
|
24
|
-
-
|
|
65
|
+
- Published ui-document 0.13.1 records middle for all four UI text types without
|
|
25
66
|
adding a Core dependency or inserting omitted properties. Apps adopt runtime
|
|
26
|
-
and authoring installations/locks explicitly
|
|
67
|
+
and authoring installations/locks explicitly and rebuild.
|
|
27
68
|
- No public API, runtime dependency, resource or XML-format change. Existing SDK
|
|
28
69
|
peers accept the patch without another UI/Game/DragonBones/Spine/runtime release.
|
|
29
70
|
The standalone bitmap-font kernel retains its own layout default.
|
package/README.md
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
# @kurot/core
|
|
2
2
|
|
|
3
|
-
**2.5.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
**2.5.4 is prepared for publication; not yet published.** It fixes continuous
|
|
4
|
+
Web Audio loops while preserving `Sound.play(startTime, loops)` semantics.
|
|
5
|
+
Published 2.5.3 remains the verified registry baseline and does not include this
|
|
6
|
+
audio correction. See [CHANGELOG.md](CHANGELOG.md).
|
|
7
7
|
|
|
8
8
|
A modern rewrite of the Egret game engine. Maintains Egret-compatible display object and event APIs while upgrading the rendering architecture, type safety, and tooling.
|
|
9
9
|
|
|
10
|
-
> **Package version: 2.5.
|
|
10
|
+
> **Package version: 2.5.4 (prepared for publication).** Targets ES2022 and evergreen browsers (Chrome / Edge / Firefox / Safari). No IE / old-Android / pre-2022 Safari support shims.
|
|
11
|
+
|
|
12
|
+
## Continuous audio loops in 2.5.4
|
|
13
|
+
|
|
14
|
+
Core 2.5.4 loops Web Audio through one native source, with
|
|
15
|
+
finite counts ending on the audio clock. Existing `sound.play(startTime, loops)`
|
|
16
|
+
semantics remain: `loops <= 0` is unlimited, positive `loops` counts total plays,
|
|
17
|
+
and the offset applies only to the first play. Position wraps per iteration;
|
|
18
|
+
manual stop suppresses completion and releases nodes. HTML unlimited playback
|
|
19
|
+
uses native looping; finite HTML fallback retains browser-dependent transitions.
|
|
20
|
+
|
|
21
|
+
After 2.5.4 is published, update the application's Core installation/lock and
|
|
22
|
+
rebuild to adopt the fix. Exact Core pins require an explicit declaration update;
|
|
23
|
+
compatible ranges such as `^2.5.3` already accept it. SDK peers and development
|
|
24
|
+
locks remain unchanged; UI/Game/DragonBones/Spine/runtime need no new release.
|
|
25
|
+
No new dependency or public API is introduced.
|
|
26
|
+
See [audio playback](docs/audio-playback.md) for asset boundaries and tests.
|
|
11
27
|
|
|
12
28
|
## Default middle alignment in 2.5.3
|
|
13
29
|
|
|
@@ -26,8 +42,8 @@ its bitmap-font line frame. Nominal measurement and font-fitting sizes are uncha
|
|
|
26
42
|
Content that omitted alignment may move within a taller box. Set
|
|
27
43
|
`verticalAlign = "top"` explicitly when it should start at the top, such as a
|
|
28
44
|
paragraph in a fixed-height region. No XML is rewritten automatically.
|
|
29
|
-
Authoring catalog defaults match in
|
|
30
|
-
bitmap-font kernel is unchanged.
|
|
45
|
+
Authoring catalog defaults match in published ui-document 0.13.1; the headless
|
|
46
|
+
bitmap-font kernel is unchanged. Adopt Core 2.5.3 in the
|
|
31
47
|
application installation/lock and rebuild. Existing SDK peers accept it without
|
|
32
48
|
new SDK releases or higher minima. See [text layout](docs/text-layout.md#default-middle-alignment-core-253).
|
|
33
49
|
|
|
@@ -178,8 +194,10 @@ matching UI release; Core does not expose `textFit` or `minFontSize`.
|
|
|
178
194
|
|
|
179
195
|
## Installation
|
|
180
196
|
|
|
197
|
+
After 2.5.4 is published:
|
|
198
|
+
|
|
181
199
|
```bash
|
|
182
|
-
pnpm add @kurot/core@^2.5.
|
|
200
|
+
pnpm add @kurot/core@^2.5.4
|
|
183
201
|
```
|
|
184
202
|
|
|
185
203
|
Core depends on `@kurot/bitmap-font ^0.1.0` for bitmap-font data and shared
|
|
@@ -422,8 +440,8 @@ Release regression gates are deliberately machine-scoped. Set a stable
|
|
|
422
440
|
name and exact browser version match. Timing limits use both observed variance
|
|
423
441
|
and a 12% floor; a baseline row needs at least five samples before it can gate.
|
|
424
442
|
|
|
425
|
-
|
|
426
|
-
WebGL context restoration:
|
|
443
|
+
Visual regression is a local, on-demand check for renderer changes. The tests
|
|
444
|
+
cover Canvas 2D, WebGL 1, WebGL 2, and WebGL context restoration:
|
|
427
445
|
|
|
428
446
|
```bash
|
|
429
447
|
pnpm test:visual
|
|
@@ -433,6 +451,17 @@ After an intentional renderer change has been visually reviewed, regenerate the
|
|
|
433
451
|
golden images with `pnpm test:visual:update`. Failed comparisons retain expected,
|
|
434
452
|
actual, and diff images under the ignored local `test-results/` directory.
|
|
435
453
|
|
|
454
|
+
The golden scene explicitly uses middle-aligned text, matching Core 2.5.3.
|
|
455
|
+
It loads its bundled Chakra Petch bold font before layout, so glyph-block
|
|
456
|
+
placement does not depend on a system Arial substitute. The font and its SIL
|
|
457
|
+
Open Font License are in `examples/visual-regression/fonts/`. Text-alignment
|
|
458
|
+
tests separately check the default and explicit top/middle/bottom modes.
|
|
459
|
+
Baselines live in `examples/visual-regression/tests/snapshots/<platform>/`
|
|
460
|
+
because font rasterization and ink metrics can differ between operating systems
|
|
461
|
+
even with the same font file. `pnpm test:visual:update` updates only the current
|
|
462
|
+
platform's baselines. Review text-layout changes before updating snapshots and
|
|
463
|
+
keep the existing pixel thresholds.
|
|
464
|
+
|
|
436
465
|
Resource-lifetime soak tests repeatedly rebuild scenes, upload and dispose
|
|
437
466
|
textures, pressure blur framebuffer pools, force garbage collection, and restore
|
|
438
467
|
a lost WebGL context. The short release smoke is:
|
|
@@ -25,6 +25,11 @@ export declare class Sound extends EventDispatcher<SoundEvents> {
|
|
|
25
25
|
private _generation;
|
|
26
26
|
get length(): number;
|
|
27
27
|
load(url: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Starts at startTime seconds on the first play, then repeats from zero.
|
|
30
|
+
* loops <= 0 repeats indefinitely; positive loops is the total play count.
|
|
31
|
+
* Web Audio loops continuously; finite counts end on the audio clock.
|
|
32
|
+
*/
|
|
28
33
|
play(startTime?: number, loops?: number): SoundChannel;
|
|
29
34
|
close(): void;
|
|
30
35
|
private loadWebAudio;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sound.d.ts","sourceRoot":"","sources":["../../../src/kurot/media/Sound.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,WAAW,WAAY,SAAQ,QAAQ;IAC5C,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;IACxB,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;CACtC;AAED,eAAO,MAAM,SAAS;;;CAGZ,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAoDnE;;;;GAIG;AACH,qBAAa,KAAM,SAAQ,eAAe,CAAC,WAAW,CAAC;IAG/C,IAAI,EAAE,SAAS,CAAoB;IAE1C,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,MAAM,CAAC,CAAmB;IAClC,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAK;IAIxB,IAAW,MAAM,IAAI,MAAM,CAI1B;IAIM,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"Sound.d.ts","sourceRoot":"","sources":["../../../src/kurot/media/Sound.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,WAAW,WAAY,SAAQ,QAAQ;IAC5C,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;IACxB,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;CACtC;AAED,eAAO,MAAM,SAAS;;;CAGZ,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAoDnE;;;;GAIG;AACH,qBAAa,KAAM,SAAQ,eAAe,CAAC,WAAW,CAAC;IAG/C,IAAI,EAAE,SAAS,CAAoB;IAE1C,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,MAAM,CAAC,CAAmB;IAClC,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAK;IAIxB,IAAW,MAAM,IAAI,MAAM,CAI1B;IAIM,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAa9B;;;;OAIG;IACI,IAAI,CAAC,SAAS,SAAI,EAAE,KAAK,SAAI,GAAG,YAAY;IAa5C,KAAK,IAAI,IAAI;IAYpB,OAAO,CAAC,YAAY;IAkCpB,OAAO,CAAC,aAAa;CA0BrB"}
|
|
@@ -79,6 +79,11 @@ export class Sound extends EventDispatcher {
|
|
|
79
79
|
this.loadHtmlAudio(url, generation);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Starts at startTime seconds on the first play, then repeats from zero.
|
|
84
|
+
* loops <= 0 repeats indefinitely; positive loops is the total play count.
|
|
85
|
+
* Web Audio loops continuously; finite counts end on the audio clock.
|
|
86
|
+
*/
|
|
82
87
|
play(startTime = 0, loops = 0) {
|
|
83
88
|
if (!this._loaded) {
|
|
84
89
|
IOErrorEvent.dispatchIOErrorEvent(this);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sound.js","sourceRoot":"","sources":["../../../src/kurot/media/Sound.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAiB,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAOjD,MAAM,CAAC,MAAM,SAAS,GAAG;IACxB,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,QAAQ;CACP,CAAC;AAIX,IAAI,aAAuC,CAAC;AAE5C,SAAS,eAAe;IACvB,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IACxC,IAAI,CAAC;QACJ,aAAa,GAAG,IAAI,YAAY,EAAE,CAAC;QACnC,OAAO,aAAa,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AACF,CAAC;AAQD,MAAM,WAAW,GAAiB,EAAE,CAAC;AACrC,IAAI,UAAU,GAAG,KAAK,CAAC;AAEvB,SAAS,iBAAiB,CAAC,IAAgB;IAC1C,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,kBAAkB,EAAE,CAAC;AACtB,CAAC;AAED,SAAS,kBAAkB;IAC1B,IAAI,UAAU,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACnD,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;IAC9B,IAAI,CAAC,GAAG,EAAE,CAAC;QACV,OAAO,WAAW,CAAC,MAAM;YAAE,WAAW,CAAC,KAAK,EAAG,CAAC,OAAO,EAAE,CAAC;QAC1D,OAAO;IACR,CAAC;IACD,UAAU,GAAG,IAAI,CAAC;IAClB,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAG,CAAC;IAClC,GAAG,CAAC,eAAe,CAClB,IAAI,CAAC,MAAM,EACX,GAAG,CAAC,EAAE;QACL,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACpB,UAAU,GAAG,KAAK,CAAC;QACnB,kBAAkB,EAAE,CAAC;IACtB,CAAC,EACD,GAAG,EAAE;QACJ,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,UAAU,GAAG,KAAK,CAAC;QACnB,kBAAkB,EAAE,CAAC;IACtB,CAAC,CACD,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,KAAM,SAAQ,eAA4B;IACtD,6EAA6E;IAEtE,IAAI,GAAc,SAAS,CAAC,MAAM,CAAC;IAElC,YAAY,CAAe;IAC3B,MAAM,CAAoB;IAC1B,IAAI,GAAG,EAAE,CAAC;IACV,OAAO,GAAG,KAAK,CAAC;IAChB,WAAW,GAAG,CAAC,CAAC;IAExB,6EAA6E;IAE7E,IAAW,MAAM;QAChB,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;QACzD,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;QAClD,OAAO,CAAC,CAAC;IACV,CAAC;IAED,6EAA6E;IAEtE,IAAI,CAAC,GAAW;QACtB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;QAChB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,MAAM,UAAU,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC;QAEtC,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;QAC9B,IAAI,GAAG,EAAE,CAAC;YACT,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACrC,CAAC;IACF,CAAC;
|
|
1
|
+
{"version":3,"file":"Sound.js","sourceRoot":"","sources":["../../../src/kurot/media/Sound.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,8 @@ export declare class SoundChannel extends EventDispatcher {
|
|
|
13
13
|
private _audioBuffer?;
|
|
14
14
|
private _webAudioStartTime;
|
|
15
15
|
private _startOffset;
|
|
16
|
+
private _playbackDuration?;
|
|
17
|
+
private _endPosition;
|
|
16
18
|
private _audio?;
|
|
17
19
|
private _stopped;
|
|
18
20
|
constructor(context: AudioContext | undefined, audioBuffer: AudioBuffer | undefined, audio: HTMLAudioElement | undefined, startTime: number, loops: number);
|
|
@@ -22,6 +24,7 @@ export declare class SoundChannel extends EventDispatcher {
|
|
|
22
24
|
stop(): void;
|
|
23
25
|
private playWebAudio;
|
|
24
26
|
private onWebAudioEnded;
|
|
27
|
+
private disconnectWebAudio;
|
|
25
28
|
private onHtmlAudioEnded;
|
|
26
29
|
}
|
|
27
30
|
//# sourceMappingURL=SoundChannel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SoundChannel.d.ts","sourceRoot":"","sources":["../../../src/kurot/media/SoundChannel.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D;;;GAGG;AACH,qBAAa,YAAa,SAAQ,eAAe;IAGhD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,OAAO,CAAK;IAEpB,OAAO,CAAC,QAAQ,CAAC,CAAe;IAChC,OAAO,CAAC,SAAS,CAAC,CAAW;IAC7B,OAAO,CAAC,aAAa,CAAC,CAAwB;IAC9C,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,YAAY,CAAK;IAEzB,OAAO,CAAC,MAAM,CAAC,CAAmB;IAElC,OAAO,CAAC,QAAQ,CAAS;gBAKxB,OAAO,EAAE,YAAY,GAAG,SAAS,EACjC,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,KAAK,EAAE,gBAAgB,GAAG,SAAS,EACnC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"SoundChannel.d.ts","sourceRoot":"","sources":["../../../src/kurot/media/SoundChannel.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D;;;GAGG;AACH,qBAAa,YAAa,SAAQ,eAAe;IAGhD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,OAAO,CAAK;IAEpB,OAAO,CAAC,QAAQ,CAAC,CAAe;IAChC,OAAO,CAAC,SAAS,CAAC,CAAW;IAC7B,OAAO,CAAC,aAAa,CAAC,CAAwB;IAC9C,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,iBAAiB,CAAC,CAAS;IACnC,OAAO,CAAC,YAAY,CAAK;IAEzB,OAAO,CAAC,MAAM,CAAC,CAAmB;IAElC,OAAO,CAAC,QAAQ,CAAS;gBAKxB,OAAO,EAAE,YAAY,GAAG,SAAS,EACjC,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,KAAK,EAAE,gBAAgB,GAAG,SAAS,EACnC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM;IAyBd,IAAW,MAAM,IAAI,MAAM,CAE1B;IACD,IAAW,MAAM,CAAC,KAAK,EAAE,MAAM,EAI9B;IAED,IAAW,QAAQ,IAAI,MAAM,CAU5B;IAIM,IAAI,IAAI,IAAI;IAoBnB,OAAO,CAAC,YAAY;IA2BpB,OAAO,CAAC,eAAe,CAMrB;IAEF,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,gBAAgB,CAWtB;CACF"}
|
|
@@ -15,6 +15,8 @@ export class SoundChannel extends EventDispatcher {
|
|
|
15
15
|
_audioBuffer;
|
|
16
16
|
_webAudioStartTime = 0;
|
|
17
17
|
_startOffset = 0;
|
|
18
|
+
_playbackDuration;
|
|
19
|
+
_endPosition = 0;
|
|
18
20
|
_audio;
|
|
19
21
|
_stopped = false;
|
|
20
22
|
// ── Constructor (Web Audio) ───────────────────────────────────────────────
|
|
@@ -31,8 +33,11 @@ export class SoundChannel extends EventDispatcher {
|
|
|
31
33
|
}
|
|
32
34
|
else if (audio) {
|
|
33
35
|
this._audio = audio;
|
|
36
|
+
this._audio.loop = loops <= 0 || loops === Infinity;
|
|
34
37
|
this._audio.currentTime = startTime;
|
|
35
|
-
this._audio.
|
|
38
|
+
if (!this._audio.loop) {
|
|
39
|
+
this._audio.addEventListener('ended', this.onHtmlAudioEnded);
|
|
40
|
+
}
|
|
36
41
|
this._audio.play();
|
|
37
42
|
}
|
|
38
43
|
}
|
|
@@ -48,8 +53,15 @@ export class SoundChannel extends EventDispatcher {
|
|
|
48
53
|
this._audio.volume = this._volume;
|
|
49
54
|
}
|
|
50
55
|
get position() {
|
|
51
|
-
if (this._context && this.
|
|
52
|
-
|
|
56
|
+
if (this._context && this._audioBuffer) {
|
|
57
|
+
if (this._stopped)
|
|
58
|
+
return this._endPosition;
|
|
59
|
+
const duration = this._audioBuffer.duration;
|
|
60
|
+
const elapsed = Math.max(0, this._context.currentTime - this._webAudioStartTime);
|
|
61
|
+
if (this._playbackDuration !== undefined && elapsed >= this._playbackDuration)
|
|
62
|
+
return duration;
|
|
63
|
+
const position = this._startOffset + elapsed;
|
|
64
|
+
return position < duration ? position : duration > 0 ? position % duration : 0;
|
|
53
65
|
}
|
|
54
66
|
return this._audio?.currentTime ?? 0;
|
|
55
67
|
}
|
|
@@ -61,11 +73,11 @@ export class SoundChannel extends EventDispatcher {
|
|
|
61
73
|
if (this._bufferSource) {
|
|
62
74
|
this._bufferSource.onended = null;
|
|
63
75
|
this._bufferSource.stop();
|
|
64
|
-
this._bufferSource.disconnect();
|
|
65
|
-
this._bufferSource = undefined;
|
|
66
76
|
}
|
|
77
|
+
this.disconnectWebAudio();
|
|
67
78
|
if (this._audio) {
|
|
68
79
|
this._audio.removeEventListener('ended', this.onHtmlAudioEnded);
|
|
80
|
+
this._audio.loop = false;
|
|
69
81
|
this._audio.pause();
|
|
70
82
|
this._audio.currentTime = 0;
|
|
71
83
|
}
|
|
@@ -77,25 +89,44 @@ export class SoundChannel extends EventDispatcher {
|
|
|
77
89
|
const source = this._context.createBufferSource();
|
|
78
90
|
this._bufferSource = source;
|
|
79
91
|
source.buffer = this._audioBuffer;
|
|
92
|
+
const duration = this._audioBuffer.duration;
|
|
93
|
+
// Positive fractional counts historically play ceil(loops) times; NaN plays once.
|
|
94
|
+
const count = this._loops <= 0 ? Infinity : this._loops > 0 ? Math.ceil(this._loops) : 1;
|
|
95
|
+
source.loop = count > 1;
|
|
96
|
+
source.loopStart = 0;
|
|
97
|
+
source.loopEnd = duration;
|
|
80
98
|
source.connect(this._gainNode);
|
|
81
99
|
this._gainNode.gain.value = this._volume;
|
|
82
100
|
source.onended = this.onWebAudioEnded;
|
|
83
101
|
this._webAudioStartTime = this._context.currentTime;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
this._loopCount++;
|
|
90
|
-
if (this._loops <= 0 || this._loopCount < this._loops) {
|
|
91
|
-
this._startOffset = 0;
|
|
92
|
-
this.playWebAudio();
|
|
102
|
+
const offset = Math.min(this._startOffset, duration);
|
|
103
|
+
if (Number.isFinite(count)) {
|
|
104
|
+
// The initial partial play counts once; subsequent iterations start at zero.
|
|
105
|
+
this._playbackDuration = duration - offset + duration * (count - 1);
|
|
106
|
+
source.start(this._webAudioStartTime, this._startOffset, this._playbackDuration);
|
|
93
107
|
}
|
|
94
108
|
else {
|
|
95
|
-
this.
|
|
96
|
-
this.dispatchEventWith(Event.SOUND_COMPLETE);
|
|
109
|
+
source.start(this._webAudioStartTime, this._startOffset);
|
|
97
110
|
}
|
|
111
|
+
this._startOffset = offset;
|
|
112
|
+
}
|
|
113
|
+
onWebAudioEnded = () => {
|
|
114
|
+
if (this._stopped || this._playbackDuration === undefined)
|
|
115
|
+
return;
|
|
116
|
+
this._stopped = true;
|
|
117
|
+
this._endPosition = this._audioBuffer?.duration ?? 0;
|
|
118
|
+
this.disconnectWebAudio();
|
|
119
|
+
this.dispatchEventWith(Event.SOUND_COMPLETE);
|
|
98
120
|
};
|
|
121
|
+
disconnectWebAudio() {
|
|
122
|
+
if (this._bufferSource) {
|
|
123
|
+
this._bufferSource.onended = null;
|
|
124
|
+
this._bufferSource.disconnect();
|
|
125
|
+
this._bufferSource = undefined;
|
|
126
|
+
}
|
|
127
|
+
this._gainNode?.disconnect();
|
|
128
|
+
this._gainNode = undefined;
|
|
129
|
+
}
|
|
99
130
|
onHtmlAudioEnded = () => {
|
|
100
131
|
if (this._stopped)
|
|
101
132
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SoundChannel.js","sourceRoot":"","sources":["../../../src/kurot/media/SoundChannel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D;;;GAGG;AACH,MAAM,OAAO,YAAa,SAAQ,eAAe;IAChD,6EAA6E;IAErE,MAAM,CAAS;IACf,UAAU,GAAG,CAAC,CAAC;IACf,OAAO,GAAG,CAAC,CAAC;IAEZ,QAAQ,CAAgB;IACxB,SAAS,CAAY;IACrB,aAAa,CAAyB;IACtC,YAAY,CAAe;IAC3B,kBAAkB,GAAG,CAAC,CAAC;IACvB,YAAY,GAAG,CAAC,CAAC;IAEjB,MAAM,CAAoB;IAE1B,QAAQ,GAAG,KAAK,CAAC;IAEzB,6EAA6E;IAE7E,YACC,OAAiC,EACjC,WAAoC,EACpC,KAAmC,EACnC,SAAiB,EACjB,KAAa;QAEb,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QAEpB,IAAI,OAAO,IAAI,WAAW,EAAE,CAAC;YAC5B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;YACxB,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;YAChC,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;YAC9B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;YACtC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,CAAC,YAAY,EAAE,CAAC;QACrB,CAAC;aAAM,IAAI,KAAK,EAAE,CAAC;YAClB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;YACpB,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,SAAS,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"SoundChannel.js","sourceRoot":"","sources":["../../../src/kurot/media/SoundChannel.ts"],"names":[],"mappings":"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"}
|
package/docs/ai-context.md
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
# @kurot/core — AI context map
|
|
2
2
|
|
|
3
|
-
Core 2.5.
|
|
3
|
+
Core 2.5.4 is prepared for publication; not yet published. It fixes SoundChannel
|
|
4
|
+
loops with one native Web Audio source and audio-clock finite duration.
|
|
5
|
+
Preserve total `loops` counts and initial offset semantics, wrapped position,
|
|
6
|
+
completion once and manual-stop cleanup.
|
|
7
|
+
HTML unlimited loops are native; finite fallback timing is browser-dependent.
|
|
8
|
+
Read [audio playback](audio-playback.md). Registry 2.5.3 and consumer installations
|
|
9
|
+
do not contain this correction. Dependency ranges and consumer/development locks
|
|
10
|
+
are unchanged. Existing SDK peers accept 2.5.4 without bumps; applications adopt
|
|
11
|
+
the published patch through their own installation/lock and rebuild.
|
|
12
|
+
|
|
13
|
+
Core 2.5.3 is published, verified on npm on 2026-10-10. TextField and BitmapText default to
|
|
4
14
|
middle; inherited UI text components use the same host default. Explicit top/bottom
|
|
5
15
|
remain effective and multiline only controls line generation. Catalog metadata
|
|
6
|
-
matches in
|
|
16
|
+
matches in published ui-document 0.13.1. Existing omitted alignment can move;
|
|
7
17
|
use explicit top when required. Applications remain on their published locks.
|
|
8
18
|
Read [default alignment](text-layout.md#default-middle-alignment-core-253).
|
|
9
19
|
|
|
@@ -24,8 +34,8 @@ agent unfamiliar with Kurot does not need to re-derive the architecture from
|
|
|
24
34
|
scratch on every session. Treat the package source and its `src/index.ts`
|
|
25
35
|
barrel as the authority for current behavior and exports.
|
|
26
36
|
|
|
27
|
-
Package identity: `@kurot/core@2.5.
|
|
28
|
-
events, rendering, text, resource, network and media runtime. Rendering uses a
|
|
37
|
+
Package identity: `@kurot/core@2.5.4`, prepared for publication. It provides Kurot's
|
|
38
|
+
scene graph, events, rendering, text, resource, network and media runtime. Rendering uses a
|
|
29
39
|
flat `InstructionSet + RenderPipe` pipeline. ES2022 / evergreen browsers only
|
|
30
40
|
with `strict: true`. Two
|
|
31
41
|
rendering backends: WebGL (primary, two-phase build/execute, InstructionSet-
|
|
@@ -263,7 +273,7 @@ capture padding so an overflowing viewport cannot inflate surfaces. Edge glyphs
|
|
|
263
273
|
retain antialiasing; CanvasRenderer skips fully hidden dynamic glyph rows before
|
|
264
274
|
stroke painting. Link hits share the offset. Measurements remain nominal.
|
|
265
275
|
Read [text layout](text-layout.md#default-middle-alignment-core-253). Core 2.5.3
|
|
266
|
-
is
|
|
276
|
+
is published; 2.5.2 uses the same geometry but defaults to top. Existing
|
|
267
277
|
peers accept 2.5.3 without another SDK release.
|
|
268
278
|
Applications update installations/locks and rebuild explicitly.
|
|
269
279
|
|
package/docs/architecture.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @kurot/core architecture
|
|
2
2
|
|
|
3
|
-
> Package version: 2.5.
|
|
3
|
+
> Package version: 2.5.4 (prepared for publication; not yet published). See [CHANGELOG.md](../CHANGELOG.md) for release notes.
|
|
4
4
|
> The [AI context map](./ai-context.md) covers directories, non-obvious behavior,
|
|
5
5
|
> terminology and task-to-file references. This document explains design choices
|
|
6
6
|
> and internal mechanisms for contributors; the two documents complement each other.
|
|
@@ -18,6 +18,12 @@ Since 2.2.0, Core uses published `@kurot/bitmap-font ^0.1.0` as the headless fon
|
|
|
18
18
|
and layout kernel. Core manages font resources, image-page ownership and glyph
|
|
19
19
|
drawing in both backends. UI components belong to the UI package.
|
|
20
20
|
|
|
21
|
+
Since 2.5.4, SoundChannel schedules Web Audio repeats through one native looping
|
|
22
|
+
source. Finite total counts end on the audio clock, independently of JavaScript
|
|
23
|
+
completion callbacks. HTML unlimited fallback uses native looping; finite
|
|
24
|
+
fallback transitions remain browser-timed. See [audio playback](audio-playback.md)
|
|
25
|
+
for count/offset semantics, lifecycle, asset boundaries and verification.
|
|
26
|
+
|
|
21
27
|
Since 2.4.0, `DisplayObject.flipX`/`flipY` reflect around a local frame center
|
|
22
28
|
independently of authored scale and anchors. `matrix` exposes the authored
|
|
23
29
|
transform; `$getMatrix()` supplies the effective reflection transform to
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Sound playback and continuous loops
|
|
2
|
+
|
|
3
|
+
Core 2.5.4 fixes continuous Web Audio looping and is prepared for publication;
|
|
4
|
+
not yet published. Published Core 2.5.3 does not contain this correction.
|
|
5
|
+
Dependency ranges and consumer locks are unchanged. Native Sound loads through
|
|
6
|
+
Web Audio when available and decodable, with HTMLAudioElement as a fallback. `Sound.type` labels
|
|
7
|
+
music/effects; it does not select a different loop implementation.
|
|
8
|
+
|
|
9
|
+
## Playback contract
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const infinite = sound.play(0, 0);
|
|
13
|
+
const once = sound.play(0, 1);
|
|
14
|
+
const three = sound.play(1.5, 3);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`loops <= 0` repeats indefinitely. Positive values count total plays, so 1 means
|
|
18
|
+
once and 3 means three plays. `startTime` is an offset in seconds for the first
|
|
19
|
+
play only; repetitions start at zero. The initial partial play counts once,
|
|
20
|
+
including an empty initial play when the offset reaches the buffer's end.
|
|
21
|
+
Existing fractional counts round upward; positive Infinity is unlimited and
|
|
22
|
+
NaN retains the old single-play behavior. Native offset validation is retained.
|
|
23
|
+
|
|
24
|
+
Web Audio uses one AudioBufferSourceNode with `loopStart = 0` and `loopEnd` equal
|
|
25
|
+
to the decoded buffer duration. Repeated plays use `loop = true`; no JavaScript
|
|
26
|
+
callback, timeout or new source is needed at each join. Finite playback passes
|
|
27
|
+
the initial remaining duration plus the full repetitions to `start()` as total
|
|
28
|
+
buffer content. Rendering stops on the audio clock even if the control thread is
|
|
29
|
+
busy or the AudioContext is suspended and later resumed.
|
|
30
|
+
|
|
31
|
+
`position` is seconds within the current iteration, including the first offset.
|
|
32
|
+
It wraps with the audio clock, freezes at buffer duration after finite completion
|
|
33
|
+
and returns zero after an early manual stop. Finite natural completion dispatches
|
|
34
|
+
SOUND_COMPLETE once and disconnects source/gain nodes. `stop()` is idempotent,
|
|
35
|
+
suppresses queued completion callbacks and disconnects both nodes. Volume retains
|
|
36
|
+
its 0–1 range and does not restart playback.
|
|
37
|
+
|
|
38
|
+
HTML fallback uses the element's native `loop` for unlimited playback. Finite
|
|
39
|
+
fallback playback retains ended-based counting because HTMLAudioElement does not
|
|
40
|
+
offer a sample-accurate finite repeat count. Sample-continuous finite looping is
|
|
41
|
+
a Web Audio guarantee; fallback transitions depend on browser media behavior.
|
|
42
|
+
|
|
43
|
+
## Audio assets and adoption
|
|
44
|
+
|
|
45
|
+
Continuous scheduling does not trim silence, repair musical phrasing, remove
|
|
46
|
+
waveform discontinuities or apply a crossfade. MP3 encoder delay/padding handling
|
|
47
|
+
belongs to the browser decoder. Prepare seamless source material as needed; the
|
|
48
|
+
runtime reproduces the decoded samples at their original loop boundary.
|
|
49
|
+
|
|
50
|
+
After 2.5.4 is published, applications adopt it through their Core installation/
|
|
51
|
+
lock and rebuild. Compatible ranges such as `^2.5.3` already accept it; exact
|
|
52
|
+
Core pins require an explicit declaration update. Existing Core 2.x peers,
|
|
53
|
+
including UI 3.6.0's ^2.5.2, accept 2.5.4 without new SDK versions or raised peer
|
|
54
|
+
minima. Development lock adoption is optional. UI, Game, DragonBones, Spine,
|
|
55
|
+
runtime, CLI and the document kernel have no API migration.
|
|
56
|
+
No application dependency or audio asset is edited for isolated verification.
|
|
57
|
+
|
|
58
|
+
## Verification
|
|
59
|
+
|
|
60
|
+
Unit tests cover counts, offsets, volume, position, completion, cleanup and HTML
|
|
61
|
+
fallback. Minified browser tests compare every rendered sample against repeated
|
|
62
|
+
decoded PCM at 44.1/48 kHz while holding the control thread. They check both
|
|
63
|
+
continuous output and the exact finite stop boundary, including end offsets.
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
pnpm build
|
|
67
|
+
pnpm test
|
|
68
|
+
pnpm exec playwright test --config examples/visual-regression/playwright.config.ts audio-loop.spec.ts
|
|
69
|
+
KUROT_AUDIO_FIXTURE=/absolute/path/to/gameplay.mp3 \
|
|
70
|
+
pnpm exec playwright test --config examples/visual-regression/playwright.config.ts audio-loop.spec.ts
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The optional MP3 remains application-owned and is not copied into the SDK.
|
|
74
|
+
|
|
75
|
+
On 2026-10-12, the build and all 941 unit tests pass, including 41 audio tests;
|
|
76
|
+
all 22 minified browser checks pass. Tentax's gameplay.mp3 decodes to 2,016,000
|
|
77
|
+
frames at 44.1 kHz (45.714285714 seconds). Unlimited and two-play rendering insert
|
|
78
|
+
zero missing frames; the maximum difference from directly repeated decoded PCM
|
|
79
|
+
after the 0.6 gain is 4.77e-8. Finite output stops at the exact expected boundary.
|
|
80
|
+
An isolated minified bundle of Tentax's unchanged SoundMusicManager with built
|
|
81
|
+
checkout Core crosses the real-time music boundary on one source, even with a
|
|
82
|
+
200 ms control-thread stall at the join. The verification output is muted.
|
package/docs/bitmap-fonts.md
CHANGED
|
@@ -55,7 +55,7 @@ layout caches assume data does not mutate. Glyph subtextures are borrowed views.
|
|
|
55
55
|
|
|
56
56
|
BitmapText has text/font, lineSpacing/letterSpacing, smoothing, multiline,
|
|
57
57
|
left/center/right textAlign and top/middle/bottom verticalAlign. The default is
|
|
58
|
-
multiline=true.
|
|
58
|
+
multiline=true. Published Core 2.5.3 defaults verticalAlign to middle; published
|
|
59
59
|
2.5.2 and earlier default to top. BitmapLabel inherits its host BitmapText default.
|
|
60
60
|
Explicit top/bottom remain effective in either line mode. Core passes the selected
|
|
61
61
|
alignment into the unchanged headless kernel, whose standalone layout default
|
package/docs/text-layout.md
CHANGED
|
@@ -42,7 +42,7 @@ the browser's grapheme implementation.
|
|
|
42
42
|
|
|
43
43
|
## Default middle alignment (Core 2.5.3)
|
|
44
44
|
|
|
45
|
-
Core 2.5.3 is
|
|
45
|
+
Core 2.5.3 is published, verified on npm on 2026-10-10. TextField and BitmapText default to
|
|
46
46
|
middle, inherited by UI Label, RichLabel, EditableText and BitmapLabel. Explicit
|
|
47
47
|
`top`, `middle` and `bottom` use the same boundaries described below. Changing
|
|
48
48
|
`multiline` or TextField type never changes the selected alignment.
|
|
@@ -63,7 +63,7 @@ UI component in its parent and does not choose its internal text alignment.
|
|
|
63
63
|
Omitted alignment in old content now centers within a taller box. Set top
|
|
64
64
|
explicitly where content should start at the top, for example a fixed-height
|
|
65
65
|
paragraph region. Explicit top/bottom, presets and named states are retained;
|
|
66
|
-
no source XML is rewritten.
|
|
66
|
+
no source XML is rewritten. Published document 0.13.1 reports middle in its catalog
|
|
67
67
|
without inserting omitted properties. Editors must adopt matching runtime/catalog
|
|
68
68
|
installations and remove any private fallback that forces omitted alignment to top.
|
|
69
69
|
Existing SDK peers accept these patches; no UI/CLI/runtime release or higher peer
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kurot/core",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.4",
|
|
4
4
|
"description": "Modern rewrite of the Egret game engine — WebGL multi-texture batching, instruction-driven render pipeline, strict TypeScript, Egret-compatible API",
|
|
5
5
|
"author": "Kelvin <kyia.x52@gmail.com>",
|
|
6
6
|
"license": "MIT",
|