pixi-effects 0.17.0 → 0.18.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/CHANGELOG.md +26 -0
- package/README.md +6 -6
- package/ai/CHAT.md +3 -0
- package/ai/SKILL.md +2 -2
- package/ai/chat-template.html +8 -4
- package/ai/reference/cheatsheet.md +2 -0
- package/ai/template.html +5 -5
- package/ai/tools/check.mjs +25 -109
- package/ai/tools/render.mjs +1 -0
- package/dist/Composition-6JMXWNVV.cjs +14 -0
- package/dist/{Composition-LMVEASHF.cjs.map → Composition-6JMXWNVV.cjs.map} +1 -1
- package/dist/Composition-MLBHYYFF.js +5 -0
- package/dist/{Composition-HYAOYPAX.js.map → Composition-MLBHYYFF.js.map} +1 -1
- package/dist/Controller.d.cts +1 -1
- package/dist/Controller.d.ts +1 -1
- package/dist/{Movie-DHJRrUfI.d.ts → Movie-DlHQ9C02.d.ts} +46 -2
- package/dist/{Movie-CoxF2Z7t.d.cts → Movie-K2YuVAdg.d.cts} +46 -2
- package/dist/Presenter.d.cts +1 -1
- package/dist/Presenter.d.ts +1 -1
- package/dist/{chunk-LBJRXU7N.cjs → chunk-ANO5R2E6.cjs} +14 -4
- package/dist/chunk-ANO5R2E6.cjs.map +1 -0
- package/dist/{chunk-ETAIVOHP.cjs → chunk-JW67MDHQ.cjs} +26 -26
- package/dist/{chunk-ETAIVOHP.cjs.map → chunk-JW67MDHQ.cjs.map} +1 -1
- package/dist/{chunk-ORTYBA42.js → chunk-SG3Y66FN.js} +14 -4
- package/dist/chunk-SG3Y66FN.js.map +1 -0
- package/dist/{chunk-Q3IVBYT7.js → chunk-UXDR3ZDU.js} +3 -3
- package/dist/{chunk-Q3IVBYT7.js.map → chunk-UXDR3ZDU.js.map} +1 -1
- package/dist/index.cjs +142 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +119 -8
- package/dist/index.js.map +1 -1
- package/dist/loader.css +1 -1
- package/dist/three.cjs +5 -5
- package/dist/three.js +1 -1
- package/llms-full.txt +4 -2
- package/package.json +1 -1
- package/dist/Composition-HYAOYPAX.js +0 -5
- package/dist/Composition-LMVEASHF.cjs +0 -14
- package/dist/chunk-LBJRXU7N.cjs.map +0 -1
- package/dist/chunk-ORTYBA42.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FontReport, c as FrameEvent, I as InspectOptions, d as InspectReport, L as LayerInfo,
|
|
1
|
+
export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FontReport, c as FrameEvent, I as InspectOptions, d as InspectReport, e as IssueGroup, L as LayerInfo, f as LoaderOption, M as MotionBlurOptions, g as MotionBlurSpec, h as Movie, i as MovieErrorEvent, j as MovieOptions, O as OnionSkinOptions, P as ProgressEvent, R as RenderOptions, k as ReviewOptions, l as ReviewReport, S as SnapshotOptions, m as SoundMeasure, n as StopEvent, o as StopImage, p as StopImagesOptions, T as TimelineData, q as TimelineHtmlOptions, r as TimelineRow, s as TimelineSvgOptions, t as TimelineTransition, V as VolumeEvent, u as dismissLoader, v as failLoader, w as resolveLoader } from './Movie-K2YuVAdg.cjs';
|
|
2
2
|
import { S as SequenceSpec, C as CompositionShape, I as ImageSequenceSpec, a as CameraSequenceSpec, K as Keyframe, T as TextSequenceSpec, M as MusicOptions, P as Props, b as TransitionSpec, c as CompositionSpec, d as SequenceCommon } from './types-SS51lP6O.cjs';
|
|
3
3
|
export { A as ArcShapeSpec, e as AssetSpec, f as AudioAssetSpec, g as AudioMusicSpec, h as AudioSequenceSpec, i as AudioSfxSpec, j as ChromaKeyFilterSpec, k as CircleShapeSpec, l as CompositionSequenceSpec, m as CrossfadeTransition, n as CustomFilterSpec, D as DipTransition, o as DissolveTransition, E as EllipseShapeSpec, p as Expr, F as FilterSpec, G as GradientSpec, q as IrisTransition, L as LineShapeSpec, r as MusicDrum, s as MusicDrums, t as MusicInstrument, u as MusicLevel, v as MusicTrack, N as NullSequenceSpec, w as PathShapeSpec, x as PolygonShapeSpec, y as PropValue, R as RectShapeSpec, z as SfxKnobs, B as SfxOptions, H as SfxPreset, J as SfxVoice, O as ShapeSequenceSpec, Q as SlideTransition, U as Stop, V as StopSpec, W as TransitionCommon, X as VideoSequenceSpec, Y as WipeTransition, Z as ZoomTransition, _ as normalizeStops, $ as pictureStops } from './types-SS51lP6O.cjs';
|
|
4
4
|
import { S as Sequence } from './Base-OHqp8qvX.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FontReport, c as FrameEvent, I as InspectOptions, d as InspectReport, L as LayerInfo,
|
|
1
|
+
export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FontReport, c as FrameEvent, I as InspectOptions, d as InspectReport, e as IssueGroup, L as LayerInfo, f as LoaderOption, M as MotionBlurOptions, g as MotionBlurSpec, h as Movie, i as MovieErrorEvent, j as MovieOptions, O as OnionSkinOptions, P as ProgressEvent, R as RenderOptions, k as ReviewOptions, l as ReviewReport, S as SnapshotOptions, m as SoundMeasure, n as StopEvent, o as StopImage, p as StopImagesOptions, T as TimelineData, q as TimelineHtmlOptions, r as TimelineRow, s as TimelineSvgOptions, t as TimelineTransition, V as VolumeEvent, u as dismissLoader, v as failLoader, w as resolveLoader } from './Movie-DlHQ9C02.js';
|
|
2
2
|
import { S as SequenceSpec, C as CompositionShape, I as ImageSequenceSpec, a as CameraSequenceSpec, K as Keyframe, T as TextSequenceSpec, M as MusicOptions, P as Props, b as TransitionSpec, c as CompositionSpec, d as SequenceCommon } from './types-SS51lP6O.js';
|
|
3
3
|
export { A as ArcShapeSpec, e as AssetSpec, f as AudioAssetSpec, g as AudioMusicSpec, h as AudioSequenceSpec, i as AudioSfxSpec, j as ChromaKeyFilterSpec, k as CircleShapeSpec, l as CompositionSequenceSpec, m as CrossfadeTransition, n as CustomFilterSpec, D as DipTransition, o as DissolveTransition, E as EllipseShapeSpec, p as Expr, F as FilterSpec, G as GradientSpec, q as IrisTransition, L as LineShapeSpec, r as MusicDrum, s as MusicDrums, t as MusicInstrument, u as MusicLevel, v as MusicTrack, N as NullSequenceSpec, w as PathShapeSpec, x as PolygonShapeSpec, y as PropValue, R as RectShapeSpec, z as SfxKnobs, B as SfxOptions, H as SfxPreset, J as SfxVoice, O as ShapeSequenceSpec, Q as SlideTransition, U as Stop, V as StopSpec, W as TransitionCommon, X as VideoSequenceSpec, Y as WipeTransition, Z as ZoomTransition, _ as normalizeStops, $ as pictureStops } from './types-SS51lP6O.js';
|
|
4
4
|
import { S as Sequence } from './Base-BQUodYZK.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { CompositionSequence } from './chunk-
|
|
2
|
-
import { ensureFilterLibrary, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, rand, DEG, resolveAt, checkEase, collectPropKeys } from './chunk-
|
|
3
|
-
export { noise, rand, random, registerSequenceType } from './chunk-
|
|
1
|
+
import { CompositionSequence } from './chunk-UXDR3ZDU.js';
|
|
2
|
+
import { ensureFilterLibrary, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, rand, DEG, resolveAt, checkEase, collectPropKeys } from './chunk-SG3Y66FN.js';
|
|
3
|
+
export { noise, rand, random, registerSequenceType } from './chunk-SG3Y66FN.js';
|
|
4
4
|
import { warnUnknownOptions, suggestName } from './chunk-DKGG7WD4.js';
|
|
5
5
|
import * as PIXI from 'pixi.js';
|
|
6
6
|
import { extensions, CullerPlugin, Application, Container, Rectangle, Culler, CanvasTextMetrics, TextStyle, Assets, ExtensionType, Filter, UniformGroup, GpuProgram, GlProgram, defaultFilterVert } from 'pixi.js';
|
|
@@ -446,19 +446,22 @@ function inspectFonts(root) {
|
|
|
446
446
|
if (face.status === "error") failed.add(face.family.replace(/^["']|["']$/g, ""));
|
|
447
447
|
});
|
|
448
448
|
const missing = [];
|
|
449
|
+
const used = /* @__PURE__ */ new Set();
|
|
449
450
|
const walk2 = (seq, index) => {
|
|
450
451
|
const spec = seq.spec;
|
|
451
452
|
if (spec.type === "text") {
|
|
452
453
|
const raw = spec.style?.fontFamily;
|
|
453
454
|
const family2 = Array.isArray(raw) ? raw.join(", ") : typeof raw === "string" ? raw : "";
|
|
454
455
|
const list = family2.split(",").map((f) => f.trim()).filter(Boolean);
|
|
456
|
+
for (const f of list) used.add(f.replace(/^["']|["']$/g, "").toLowerCase());
|
|
455
457
|
if (list.length && !list.some(fontAvailable) && !list.some((f) => failed.has(f.replace(/^["']|["']$/g, "")))) missing.push({ layer: spec.name ?? `text#${index}`, family: family2 });
|
|
456
458
|
}
|
|
457
459
|
if (seq.maskSequence) walk2(seq.maskSequence, index);
|
|
458
460
|
(seq._children ?? []).forEach((child, i) => walk2(child, i));
|
|
459
461
|
};
|
|
460
462
|
walk2(root, 0);
|
|
461
|
-
|
|
463
|
+
const all = [...failed];
|
|
464
|
+
return { missing, failed: all.filter((f) => used.has(f.toLowerCase())), failedUnused: all.filter((f) => !used.has(f.toLowerCase())) };
|
|
462
465
|
}
|
|
463
466
|
|
|
464
467
|
// src/core/scenes.ts
|
|
@@ -466,6 +469,92 @@ function namedScenes(rows) {
|
|
|
466
469
|
return rows.filter((r) => r.depth === 0 && r.type === "composition" && !/[#×]/.test(r.name) && r.end - r.start >= 1).map((r) => ({ name: r.name, start: r.start, end: r.end }));
|
|
467
470
|
}
|
|
468
471
|
|
|
472
|
+
// src/core/review.ts
|
|
473
|
+
function sampleFrames({ totalFrames, frameRate, scenes = [], step = 0.25, cap = 240 }) {
|
|
474
|
+
const last = totalFrames;
|
|
475
|
+
const clamp2 = (f) => Math.max(0, Math.min(last, Math.round(f)));
|
|
476
|
+
const edges = /* @__PURE__ */ new Set([0, last]);
|
|
477
|
+
for (const s of scenes) {
|
|
478
|
+
edges.add(clamp2(s.start * frameRate));
|
|
479
|
+
edges.add(clamp2(s.end * frameRate - 1));
|
|
480
|
+
}
|
|
481
|
+
const room = Math.max(0, cap - edges.size);
|
|
482
|
+
const seconds = totalFrames / frameRate;
|
|
483
|
+
const wanted = Math.floor(seconds / step) + 1;
|
|
484
|
+
const stride = wanted <= room ? step : seconds / Math.max(1, room);
|
|
485
|
+
const frames = new Set(edges);
|
|
486
|
+
for (let t = stride; frames.size < cap && t < seconds; t += stride) frames.add(clamp2(t * frameRate));
|
|
487
|
+
return [...frames].sort((a, b) => a - b);
|
|
488
|
+
}
|
|
489
|
+
var AT_HELP = "use seconds (3.5), a percentage (50%), a frame (f120) or a layer's start / mid / end (name@end)";
|
|
490
|
+
function resolveAtList(list, ctx) {
|
|
491
|
+
const { frameRate: fps, totalFrames, duration, rows } = ctx;
|
|
492
|
+
const out = [], used = /* @__PURE__ */ new Set();
|
|
493
|
+
const named = rows.flatMap((r) => r.parts && r.partNames ? r.partNames.map((name, i) => ({ name, start: r.parts[i].start, end: r.parts[i].end })) : [r]);
|
|
494
|
+
for (const raw of String(list).split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
495
|
+
let frame, label, m;
|
|
496
|
+
if (m = raw.match(/^(.+)@(start|mid|end)$/)) {
|
|
497
|
+
const row = named.find((r) => r.name === m[1]);
|
|
498
|
+
if (!row) throw new Error(`--at "${raw}": no layer named "${m[1]}" (names: ${named.slice(0, 40).map((r) => r.name).join(", ")}${named.length > 40 ? ", \u2026" : ""})`);
|
|
499
|
+
const t = m[2] === "start" ? row.start : m[2] === "mid" ? (row.start + row.end) / 2 : row.end - 1 / fps;
|
|
500
|
+
frame = Math.round(t * fps);
|
|
501
|
+
label = `${m[1]}-${m[2]}`;
|
|
502
|
+
} else if (m = raw.match(/^(\d+(?:\.\d+)?)%$/)) {
|
|
503
|
+
frame = Math.round(Number(m[1]) / 100 * totalFrames);
|
|
504
|
+
label = `${m[1]}pct`;
|
|
505
|
+
} else if (m = raw.match(/^f(\d+)$/i)) {
|
|
506
|
+
frame = Number(m[1]);
|
|
507
|
+
label = `f${m[1]}`;
|
|
508
|
+
} else if (/^\d+(\.\d+)?$/.test(raw)) {
|
|
509
|
+
frame = Math.round(Number(raw) * fps);
|
|
510
|
+
label = `${Number(raw).toFixed(2)}s`;
|
|
511
|
+
} else throw new Error(`--at: cannot read "${raw}": ${AT_HELP}`);
|
|
512
|
+
if (frame > totalFrames || frame < 0) throw new Error(`--at "${raw}" is past the end: the movie is ${duration} s (${totalFrames} frames)`);
|
|
513
|
+
label = label.replace(/[\\/:*?"<>|]+/g, "-");
|
|
514
|
+
let unique = label, n = 2;
|
|
515
|
+
while (used.has(unique)) unique = `${label}-${n++}`;
|
|
516
|
+
used.add(unique);
|
|
517
|
+
out.push({ label: unique, frame });
|
|
518
|
+
}
|
|
519
|
+
return out;
|
|
520
|
+
}
|
|
521
|
+
function groupIssues(perFrame) {
|
|
522
|
+
const groups = /* @__PURE__ */ new Map();
|
|
523
|
+
for (const { frame, issues } of perFrame) {
|
|
524
|
+
for (const message of issues) {
|
|
525
|
+
const key = message.replace(/-?\d+(\.\d+)?/g, "#");
|
|
526
|
+
const g = groups.get(key);
|
|
527
|
+
if (g) {
|
|
528
|
+
g.count++;
|
|
529
|
+
g.lastFrame = frame;
|
|
530
|
+
} else groups.set(key, { message, count: 1, firstFrame: frame, lastFrame: frame });
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return [...groups.values()];
|
|
534
|
+
}
|
|
535
|
+
function splitIssues(groups, strict = false) {
|
|
536
|
+
if (strict) return { problems: groups, review: [] };
|
|
537
|
+
const overlap = (g) => /\boverlap\b/.test(g.message);
|
|
538
|
+
return { problems: groups.filter((g) => !overlap(g)), review: groups.filter(overlap) };
|
|
539
|
+
}
|
|
540
|
+
async function reviewMovie(movie, opts = {}) {
|
|
541
|
+
const rows = movie.timelineData().rows;
|
|
542
|
+
const at = opts.at ? resolveAtList(opts.at, { frameRate: movie.frameRate, totalFrames: movie.totalFrames, duration: movie.duration, rows }) : [];
|
|
543
|
+
const frames = sampleFrames({ totalFrames: movie.totalFrames, frameRate: movie.frameRate, scenes: namedScenes(rows) });
|
|
544
|
+
const perFrame = [];
|
|
545
|
+
for (const frame of frames) {
|
|
546
|
+
const r = await movie.inspect(frame, { layers: "none" });
|
|
547
|
+
if (r.issues.length) perFrame.push({ frame, issues: r.issues });
|
|
548
|
+
}
|
|
549
|
+
const grouped = splitIssues(groupIssues(perFrame), !!opts.strict);
|
|
550
|
+
const fonts = movie.inspectFonts();
|
|
551
|
+
for (const f of fonts.failedUnused) grouped.review.push({ message: `web font "${f}" failed to load, but no text layer uses it (a broken or unused @font-face)`, count: 1, firstFrame: 0, lastFrame: 0 });
|
|
552
|
+
for (const m of fonts.missing) {
|
|
553
|
+
(opts.strict ? grouped.problems : grouped.review).push({ message: `layer "${m.layer}": none of the fonts "${m.family}" is available here (it is drawn in a fallback font)`, count: 1, firstFrame: 0, lastFrame: 0 });
|
|
554
|
+
}
|
|
555
|
+
return { frames: frames.length, problems: grouped.problems, review: grouped.review, fonts, audio: movie.audioBuffer ? movie.inspectAudio() : null, at };
|
|
556
|
+
}
|
|
557
|
+
|
|
469
558
|
// src/core/onion.ts
|
|
470
559
|
function onionTimes(from, to, count) {
|
|
471
560
|
if (count <= 1) return [from];
|
|
@@ -502,9 +591,9 @@ function resolveRange(range2, ctx) {
|
|
|
502
591
|
if (range2 === void 0) return { fromFrame: 0, toFrame: totalFrames, fromSec: 0, toSec: duration, partial: false };
|
|
503
592
|
let from, to;
|
|
504
593
|
if (typeof range2 === "string") {
|
|
505
|
-
const rows = (ctx.rows ?? []).filter((r) => r.depth === 0);
|
|
594
|
+
const rows = (ctx.rows ?? []).filter((r) => r.depth === 0).flatMap((r) => r.parts && r.partNames ? r.partNames.map((name, i) => ({ name, start: r.parts[i].start, end: r.parts[i].end })) : [r]);
|
|
506
595
|
const row = rows.find((r) => r.name === range2);
|
|
507
|
-
if (!row) throw new Error(`pixi-effects: render({ range: "${range2}" }): no layer named "${range2}" at the top level (names: ${rows.map((r) => r.name).join(", ") || "none"})`);
|
|
596
|
+
if (!row) throw new Error(`pixi-effects: render({ range: "${range2}" }): no layer named "${range2}" at the top level (names: ${rows.slice(0, 40).map((r) => r.name).join(", ") || "none"}${rows.length > 40 ? ", \u2026" : ""})`);
|
|
508
597
|
from = row.start;
|
|
509
598
|
to = row.end;
|
|
510
599
|
} else {
|
|
@@ -669,7 +758,7 @@ async function exportFrames(movie, options = {}) {
|
|
|
669
758
|
}
|
|
670
759
|
await canvasSource.close();
|
|
671
760
|
await output.finalize();
|
|
672
|
-
if (mb) await movie.gotoFrame(
|
|
761
|
+
if (mb) await movie.gotoFrame(range2.toFrame, true);
|
|
673
762
|
return new Blob([output.target.buffer], { type: output.format.mimeType });
|
|
674
763
|
} finally {
|
|
675
764
|
movie.app.ticker.start();
|
|
@@ -1585,7 +1674,8 @@ function groupFamilies(entries) {
|
|
|
1585
1674
|
start: Math.min(...members.map((x) => x.start)),
|
|
1586
1675
|
end: Math.max(...members.map((x) => x.end)),
|
|
1587
1676
|
keys: members.flatMap((x) => x.keys).sort((a, b) => a - b).slice(0, MAX_KEYS),
|
|
1588
|
-
parts: members.map((x) => ({ start: x.start, end: x.end }))
|
|
1677
|
+
parts: members.map((x) => ({ start: x.start, end: x.end })),
|
|
1678
|
+
partNames: members.map((x) => x.name)
|
|
1589
1679
|
});
|
|
1590
1680
|
} else {
|
|
1591
1681
|
out.push(e.row, ...e.inner);
|
|
@@ -2405,6 +2495,27 @@ var Movie = class {
|
|
|
2405
2495
|
* The soundtrack, measured: when every audio layer plays, how loud the mix is over time, and `issues`
|
|
2406
2496
|
* (limiting, inaudible layers, sounds cut off by the end). Use it to CHECK sound you cannot hear.
|
|
2407
2497
|
*/
|
|
2498
|
+
/** The pictures to look at for moments such as `3.5`, `50%`, `f120`, `title@end` (comma-separated). A mistake says what could not be read. */
|
|
2499
|
+
resolveAt(list) {
|
|
2500
|
+
this._requireReady("resolveAt");
|
|
2501
|
+
return resolveAtList(list, { frameRate: this.frameRate, totalFrames: this.totalFrames, duration: this.duration, rows: this.timelineData().rows });
|
|
2502
|
+
}
|
|
2503
|
+
/**
|
|
2504
|
+
* One call that looks at everything `pixi-effects-check` looks at inside the page: layout issues over the whole timeline (every
|
|
2505
|
+
* scene's first and last frame and a frame every 0.25 s), fonts and the sound. See `ReviewReport`. Page warnings are in `window.__logs`. The movie is left as it was.
|
|
2506
|
+
*/
|
|
2507
|
+
async review(opts = {}) {
|
|
2508
|
+
warnUnknownOptions("movie.review()", opts, ["at", "strict"]);
|
|
2509
|
+
this._requireReady("review");
|
|
2510
|
+
const back = this.currentFrame, wasAtPoster = this._atPoster;
|
|
2511
|
+
try {
|
|
2512
|
+
return await reviewMovie(this, opts);
|
|
2513
|
+
} finally {
|
|
2514
|
+
this.currentFrame = back;
|
|
2515
|
+
if (wasAtPoster) await this._showPoster();
|
|
2516
|
+
else await this._quietly(() => this.gotoFrame(back, true));
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2408
2519
|
/**
|
|
2409
2520
|
* The fonts of the text layers: `missing` lists layers none of whose `fontFamily` entries is available in this browser (they are
|
|
2410
2521
|
* drawn in a fallback font), `failed` lists web fonts (`@font-face`) whose file could not be loaded.
|