pixi-effects 0.26.0 → 0.26.2
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 +20 -0
- package/README.md +6 -6
- package/ai/chat-template.html +3 -3
- package/ai/tools/check.mjs +12 -0
- package/dist/{Base-BgnRXbtJ.d.ts → Base-CpdDs4HT.d.ts} +1 -1
- package/dist/{Base-DHVKqA46.d.cts → Base-TbBhHPrG.d.cts} +1 -1
- package/dist/Composition-H4QA24MV.js +7 -0
- package/dist/{Composition-C4FSWJZF.js.map → Composition-H4QA24MV.js.map} +1 -1
- package/dist/Composition-HM4YT5L7.cjs +16 -0
- package/dist/{Composition-2GJSCHPP.cjs.map → Composition-HM4YT5L7.cjs.map} +1 -1
- package/dist/Controller.d.cts +2 -2
- package/dist/Controller.d.ts +2 -2
- package/dist/{Movie-DP5p1r4E.d.ts → Movie-DVeDTdnc.d.ts} +23 -2
- package/dist/{Movie-DXReF4-W.d.cts → Movie-i05Oh9mJ.d.cts} +23 -2
- package/dist/Presenter.d.cts +2 -2
- package/dist/Presenter.d.ts +2 -2
- package/dist/{chunk-SY27HZC5.cjs → chunk-555URQHQ.cjs} +59 -59
- package/dist/{chunk-SY27HZC5.cjs.map → chunk-555URQHQ.cjs.map} +1 -1
- package/dist/{chunk-XS6GSJCN.js → chunk-GBUM7X2S.js} +3 -3
- package/dist/{chunk-XS6GSJCN.js.map → chunk-GBUM7X2S.js.map} +1 -1
- package/dist/{chunk-DP37TTMO.cjs → chunk-WPXDRSGN.cjs} +164 -9
- package/dist/chunk-WPXDRSGN.cjs.map +1 -0
- package/dist/{chunk-PRZ4U6JN.js → chunk-X3HB4DSR.js} +164 -9
- package/dist/chunk-X3HB4DSR.js.map +1 -0
- package/dist/index.cjs +188 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +164 -10
- package/dist/index.js.map +1 -1
- package/dist/loader.css +35 -2
- package/dist/{music-AF4VTFAF.js → music-3O5MDQXY.js} +52 -6
- package/dist/music-3O5MDQXY.js.map +1 -0
- package/dist/{music-KB7YZLHP.cjs → music-4O367SPR.cjs} +52 -6
- package/dist/music-4O367SPR.cjs.map +1 -0
- package/dist/three.cjs +5 -5
- package/dist/three.d.cts +2 -2
- package/dist/three.d.ts +2 -2
- package/dist/three.js +1 -1
- package/dist/{types-CoT0ypcx.d.ts → types-DSGwN5w1.d.cts} +2 -0
- package/dist/{types-CoT0ypcx.d.cts → types-DSGwN5w1.d.ts} +2 -0
- package/docs/api.md +3 -2
- package/docs/dsl.md +2 -2
- package/llms-full.txt +8 -6
- package/package.json +1 -1
- package/skills/pixi-effects/SKILL.md +2 -2
- package/skills/pixi-effects/reference/cheatsheet.md +1 -1
- package/skills/pixi-effects/reference/pitfalls.md +1 -0
- package/skills/pixi-effects/template.html +6 -5
- package/dist/Composition-2GJSCHPP.cjs +0 -16
- package/dist/Composition-C4FSWJZF.js +0 -7
- package/dist/chunk-DP37TTMO.cjs.map +0 -1
- package/dist/chunk-PRZ4U6JN.js.map +0 -1
- package/dist/music-AF4VTFAF.js.map +0 -1
- package/dist/music-KB7YZLHP.cjs.map +0 -1
|
@@ -2986,7 +2986,7 @@ var AudioSequence = class extends Sequence {
|
|
|
2986
2986
|
const raw = spec;
|
|
2987
2987
|
if (raw.asset !== void 0) console.warn(`pixi-effects: ${who}: has both asset and music \u2014 the music plays; remove one`);
|
|
2988
2988
|
if (raw.sfx !== void 0) console.warn(`pixi-effects: ${who}: has both sfx and music \u2014 the music plays; remove one`);
|
|
2989
|
-
const { resolveMusic, renderMusic, musicLength, musicKey } = await import('./music-
|
|
2989
|
+
const { resolveMusic, renderMusic, musicLength, musicKey } = await import('./music-4O367SPR.cjs');
|
|
2990
2990
|
const music = resolveMusic(spec.music, who);
|
|
2991
2991
|
if (!music) return;
|
|
2992
2992
|
const natural = musicLength(music);
|
|
@@ -3063,19 +3063,41 @@ var AudioSequence = class extends Sequence {
|
|
|
3063
3063
|
};
|
|
3064
3064
|
|
|
3065
3065
|
// src/core/FrameCache.ts
|
|
3066
|
+
var BUDGET_BYTES = 96 * 1024 * 1024;
|
|
3067
|
+
var MIN_HELD = 3;
|
|
3068
|
+
var JUMP_SECONDS = 1;
|
|
3069
|
+
var EPS = 1e-6;
|
|
3070
|
+
function reportDecodeError(err, time) {
|
|
3071
|
+
const isTransient = err instanceof DOMException && (err.name === "EncodingError" || err.name === "NotSupportedError");
|
|
3072
|
+
if (isTransient) console.debug("pixi-effects: transient video decode failure at", time, "s \u2014", err.name);
|
|
3073
|
+
else console.warn("pixi-effects: video frame decode failed at", time, "s \u2014", err);
|
|
3074
|
+
}
|
|
3066
3075
|
var FrameCache = class {
|
|
3067
3076
|
sink;
|
|
3068
3077
|
capacity;
|
|
3069
3078
|
cache = /* @__PURE__ */ new Map();
|
|
3070
3079
|
_pending = /* @__PURE__ */ new Map();
|
|
3080
|
+
// Sequential mode (the sink has `samples()`): one decoder pass that runs forward as the movie asks for frame after frame.
|
|
3081
|
+
_frames = [];
|
|
3082
|
+
_cur = null;
|
|
3083
|
+
_iter = null;
|
|
3084
|
+
_next = null;
|
|
3085
|
+
_held = 0;
|
|
3086
|
+
_lock = Promise.resolve();
|
|
3087
|
+
_lastTime = null;
|
|
3071
3088
|
constructor(sink, options = {}) {
|
|
3072
3089
|
this.sink = sink;
|
|
3073
3090
|
this.capacity = options.capacity ?? 30;
|
|
3074
3091
|
}
|
|
3092
|
+
/** How many decoded frames are held right now (the sequential reader's own list, plus the exact-time cache of the old path). */
|
|
3093
|
+
get heldFrames() {
|
|
3094
|
+
return this._frames.length + this.cache.size;
|
|
3095
|
+
}
|
|
3075
3096
|
_key(time) {
|
|
3076
3097
|
return Math.round(time * 1e6);
|
|
3077
3098
|
}
|
|
3078
3099
|
async getFrameAt(time) {
|
|
3100
|
+
if (this.sink.samples) return this._enqueue(() => this._sequentialAt(time));
|
|
3079
3101
|
const key = this._key(time);
|
|
3080
3102
|
if (this.cache.has(key)) {
|
|
3081
3103
|
const v = this.cache.get(key);
|
|
@@ -3113,15 +3135,138 @@ var FrameCache = class {
|
|
|
3113
3135
|
sample.close?.();
|
|
3114
3136
|
return frame;
|
|
3115
3137
|
} catch (err) {
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3138
|
+
reportDecodeError(err, time);
|
|
3139
|
+
return null;
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
// ── sequential mode ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
3143
|
+
/** Requests are answered one at a time, in the order they arrive: one pass cannot serve two places at once. */
|
|
3144
|
+
_enqueue(fn) {
|
|
3145
|
+
const run = this._lock.then(fn, fn);
|
|
3146
|
+
this._lock = run.then(() => void 0, () => void 0);
|
|
3147
|
+
return run;
|
|
3148
|
+
}
|
|
3149
|
+
_covering(time) {
|
|
3150
|
+
for (const e of this._frames) {
|
|
3151
|
+
if (e.end === null ? e.req !== void 0 && Math.abs(e.req - time) < EPS : e.ts <= time + EPS && time < e.end - EPS) {
|
|
3152
|
+
this._frames.splice(this._frames.indexOf(e), 1);
|
|
3153
|
+
this._frames.push(e);
|
|
3154
|
+
return e;
|
|
3121
3155
|
}
|
|
3156
|
+
}
|
|
3157
|
+
return null;
|
|
3158
|
+
}
|
|
3159
|
+
async _sequentialAt(time) {
|
|
3160
|
+
try {
|
|
3161
|
+
const last = this._lastTime;
|
|
3162
|
+
this._lastTime = time;
|
|
3163
|
+
const hit = this._covering(time);
|
|
3164
|
+
if (hit) return hit.frame;
|
|
3165
|
+
if (last === null || Math.abs(time - last) > JUMP_SECONDS) return await this._single(time);
|
|
3166
|
+
const cur = this._cur;
|
|
3167
|
+
if (this._iter && cur && time >= cur.ts && time - cur.ts <= JUMP_SECONDS) return await this._advance(time);
|
|
3168
|
+
const backwards = !!cur && time < cur.ts;
|
|
3169
|
+
return await this._restart(time, backwards ? Math.max(0, time - this._held / 30) : time);
|
|
3170
|
+
} catch (err) {
|
|
3171
|
+
reportDecodeError(err, time);
|
|
3172
|
+
await this._closeIter();
|
|
3122
3173
|
return null;
|
|
3123
3174
|
}
|
|
3124
3175
|
}
|
|
3176
|
+
/** One frame by itself (a seek): getSample, kept so that asking again for the same time costs nothing. The running pass, if any, is closed: it no longer follows the playhead. */
|
|
3177
|
+
async _single(time) {
|
|
3178
|
+
await this._closeIter();
|
|
3179
|
+
this._cur = null;
|
|
3180
|
+
const sample = await this.sink.getSample(time);
|
|
3181
|
+
if (!sample) return null;
|
|
3182
|
+
const frame = sample.toVideoFrame();
|
|
3183
|
+
sample.close?.();
|
|
3184
|
+
this._keep({ ts: sample.timestamp, end: null, frame, req: time }, frame);
|
|
3185
|
+
return frame;
|
|
3186
|
+
}
|
|
3187
|
+
/** Start a pass at `time` (a seek and the frames of one group of pictures), keeping what is still held for a step back. */
|
|
3188
|
+
async _restart(time, from = time) {
|
|
3189
|
+
await this._closeIter();
|
|
3190
|
+
this._cur = null;
|
|
3191
|
+
const iter = this.sink.samples(from);
|
|
3192
|
+
this._iter = iter;
|
|
3193
|
+
const first = await iter.next();
|
|
3194
|
+
if (first.done) {
|
|
3195
|
+
await this._closeIter();
|
|
3196
|
+
return null;
|
|
3197
|
+
}
|
|
3198
|
+
if (first.value.timestamp > time + EPS) {
|
|
3199
|
+
first.value.close?.();
|
|
3200
|
+
await this._closeIter();
|
|
3201
|
+
return null;
|
|
3202
|
+
}
|
|
3203
|
+
this._take(first.value);
|
|
3204
|
+
return this._advance(time);
|
|
3205
|
+
}
|
|
3206
|
+
/** Run the pass forward until the frame that covers `time` is the current one; the next frame is read ahead (its start is where this one ends). */
|
|
3207
|
+
async _advance(time) {
|
|
3208
|
+
const cur = () => this._cur;
|
|
3209
|
+
for (; ; ) {
|
|
3210
|
+
if (!this._next) {
|
|
3211
|
+
let r;
|
|
3212
|
+
try {
|
|
3213
|
+
r = await this._iter.next();
|
|
3214
|
+
} catch (err) {
|
|
3215
|
+
reportDecodeError(err, time);
|
|
3216
|
+
await this._closeIter();
|
|
3217
|
+
const prev = this._frames.length > 1 ? this._frames[this._frames.length - 2] : null;
|
|
3218
|
+
const spacing = prev ? cur().ts - prev.ts : 1 / 24;
|
|
3219
|
+
return time < cur().ts + spacing - EPS ? cur().frame : null;
|
|
3220
|
+
}
|
|
3221
|
+
if (r.done) {
|
|
3222
|
+
cur().end = Infinity;
|
|
3223
|
+
this._iter = null;
|
|
3224
|
+
break;
|
|
3225
|
+
}
|
|
3226
|
+
this._next = r.value;
|
|
3227
|
+
}
|
|
3228
|
+
cur().end = this._next.timestamp;
|
|
3229
|
+
if (this._next.timestamp <= time + EPS) {
|
|
3230
|
+
const s = this._next;
|
|
3231
|
+
this._next = null;
|
|
3232
|
+
this._take(s);
|
|
3233
|
+
} else break;
|
|
3234
|
+
}
|
|
3235
|
+
return cur().frame;
|
|
3236
|
+
}
|
|
3237
|
+
_take(sample) {
|
|
3238
|
+
const frame = sample.toVideoFrame();
|
|
3239
|
+
sample.close?.();
|
|
3240
|
+
const entry = { ts: sample.timestamp, end: null, frame };
|
|
3241
|
+
this._keep(entry, frame);
|
|
3242
|
+
this._cur = entry;
|
|
3243
|
+
}
|
|
3244
|
+
/** Hold a frame (the first one sets how many fit: a byte budget, not a count) and close the oldest beyond that; the frame the pass stands on is never closed. */
|
|
3245
|
+
_keep(entry, frame) {
|
|
3246
|
+
if (this._held === 0) {
|
|
3247
|
+
const bytes = frame.allocationSize?.() ?? frame.displayWidth * frame.displayHeight * 1.5;
|
|
3248
|
+
this._held = Math.max(MIN_HELD, Math.min(this.capacity, Math.floor(BUDGET_BYTES / Math.max(1, bytes))));
|
|
3249
|
+
}
|
|
3250
|
+
this._frames.push(entry);
|
|
3251
|
+
while (this._frames.length > this._held) {
|
|
3252
|
+
const oldest = this._frames.find((e) => e !== this._cur && e !== entry);
|
|
3253
|
+
if (!oldest) break;
|
|
3254
|
+
this._frames.splice(this._frames.indexOf(oldest), 1);
|
|
3255
|
+
oldest.frame.close?.();
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
async _closeIter() {
|
|
3259
|
+
const iter = this._iter;
|
|
3260
|
+
this._iter = null;
|
|
3261
|
+
this._next?.close?.();
|
|
3262
|
+
this._next = null;
|
|
3263
|
+
if (iter) {
|
|
3264
|
+
try {
|
|
3265
|
+
await iter.return?.(void 0);
|
|
3266
|
+
} catch {
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3125
3270
|
_evictIfNeeded() {
|
|
3126
3271
|
while (this.cache.size > this.capacity) {
|
|
3127
3272
|
const oldestKey = this.cache.keys().next().value;
|
|
@@ -3135,6 +3280,14 @@ var FrameCache = class {
|
|
|
3135
3280
|
for (const f of this.cache.values()) f?.close?.();
|
|
3136
3281
|
this.cache.clear();
|
|
3137
3282
|
this._pending.clear();
|
|
3283
|
+
for (const e of this._frames) e.frame.close?.();
|
|
3284
|
+
this._frames = [];
|
|
3285
|
+
this._cur = null;
|
|
3286
|
+
this._next?.close?.();
|
|
3287
|
+
this._next = null;
|
|
3288
|
+
const iter = this._iter;
|
|
3289
|
+
this._iter = null;
|
|
3290
|
+
void iter?.return?.(void 0);
|
|
3138
3291
|
}
|
|
3139
3292
|
};
|
|
3140
3293
|
|
|
@@ -4365,7 +4518,7 @@ function resolveDim(v, scope) {
|
|
|
4365
4518
|
var _CompositionSequence = null;
|
|
4366
4519
|
async function getCompositionSequence() {
|
|
4367
4520
|
if (!_CompositionSequence) {
|
|
4368
|
-
_CompositionSequence = (await import('./Composition-
|
|
4521
|
+
_CompositionSequence = (await import('./Composition-HM4YT5L7.cjs')).CompositionSequence;
|
|
4369
4522
|
}
|
|
4370
4523
|
return _CompositionSequence;
|
|
4371
4524
|
}
|
|
@@ -4393,10 +4546,12 @@ async function buildSequenceTree(specs, parent, root) {
|
|
|
4393
4546
|
}
|
|
4394
4547
|
if (!Cls) {
|
|
4395
4548
|
console.warn(`pixi-effects: unknown sequence type "${spec.type}"`);
|
|
4549
|
+
await root.onLayerBuilt?.();
|
|
4396
4550
|
continue;
|
|
4397
4551
|
}
|
|
4398
4552
|
const seq = new Cls(spec, parent, root);
|
|
4399
4553
|
await seq.build();
|
|
4554
|
+
await root.onLayerBuilt?.();
|
|
4400
4555
|
if (seq.duration === void 0) {
|
|
4401
4556
|
seq.duration = parent?.duration ?? root.duration;
|
|
4402
4557
|
}
|
|
@@ -4448,5 +4603,5 @@ exports.registerSequenceType = registerSequenceType;
|
|
|
4448
4603
|
exports.remapOf = remapOf;
|
|
4449
4604
|
exports.resolveAt = resolveAt;
|
|
4450
4605
|
exports.timeRemapOf = timeRemapOf;
|
|
4451
|
-
//# sourceMappingURL=chunk-
|
|
4452
|
-
//# sourceMappingURL=chunk-
|
|
4606
|
+
//# sourceMappingURL=chunk-WPXDRSGN.cjs.map
|
|
4607
|
+
//# sourceMappingURL=chunk-WPXDRSGN.cjs.map
|