pixi-effects 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +16 -10
  3. package/ai/SKILL.md +1 -1
  4. package/ai/reference/cheatsheet.md +5 -1
  5. package/ai/template.html +4 -3
  6. package/ai/tools/check.mjs +14 -5
  7. package/ai/tools/render.mjs +202 -0
  8. package/ai/tools/view.mjs +269 -0
  9. package/dist/{Base-4vNLl4tH.d.cts → Base-CSIulyIq.d.cts} +1 -1
  10. package/dist/{Base-CD4AoT5-.d.ts → Base-DVgwyIUj.d.ts} +1 -1
  11. package/dist/Composition-3KHQPRBA.cjs +14 -0
  12. package/dist/{Composition-Y5VZSDDP.cjs.map → Composition-3KHQPRBA.cjs.map} +1 -1
  13. package/dist/Composition-DT657FO5.js +5 -0
  14. package/dist/{Composition-7SO5WYHU.js.map → Composition-DT657FO5.js.map} +1 -1
  15. package/dist/Controller.cjs +67 -0
  16. package/dist/Controller.cjs.map +1 -1
  17. package/dist/Controller.d.cts +15 -2
  18. package/dist/Controller.d.ts +15 -2
  19. package/dist/Controller.js +67 -0
  20. package/dist/Controller.js.map +1 -1
  21. package/dist/{Movie-CADDBfpl.d.cts → Movie-B0OUKlZI.d.ts} +52 -2
  22. package/dist/{Movie-Ch2SUI1h.d.ts → Movie-BK9daiTt.d.cts} +52 -2
  23. package/dist/{chunk-B2FTJLNB.js → chunk-36Z4RIRK.js} +3 -3
  24. package/dist/{chunk-B2FTJLNB.js.map → chunk-36Z4RIRK.js.map} +1 -1
  25. package/dist/{chunk-7IJHJXDI.js → chunk-4O2NYDJG.js} +127 -10
  26. package/dist/chunk-4O2NYDJG.js.map +1 -0
  27. package/dist/{chunk-4AGJF5RK.cjs → chunk-4VSIJLU3.cjs} +21 -21
  28. package/dist/{chunk-4AGJF5RK.cjs.map → chunk-4VSIJLU3.cjs.map} +1 -1
  29. package/dist/{chunk-KQM2ZZMK.cjs → chunk-6I55T4OH.cjs} +166 -29
  30. package/dist/chunk-6I55T4OH.cjs.map +1 -0
  31. package/dist/index.cjs +188 -17
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +4 -4
  34. package/dist/index.d.ts +4 -4
  35. package/dist/index.js +180 -9
  36. package/dist/index.js.map +1 -1
  37. package/dist/three.cjs +6 -5
  38. package/dist/three.cjs.map +1 -1
  39. package/dist/three.d.cts +2 -2
  40. package/dist/three.d.ts +2 -2
  41. package/dist/three.js +2 -1
  42. package/dist/three.js.map +1 -1
  43. package/dist/{types-DrW9QeKU.d.ts → types-oJijbn-K.d.cts} +14 -1
  44. package/dist/{types-DrW9QeKU.d.cts → types-oJijbn-K.d.ts} +14 -1
  45. package/docs/api.md +6 -0
  46. package/docs/dsl.md +48 -1
  47. package/llms-full.txt +60 -3
  48. package/package.json +7 -4
  49. package/dist/Composition-7SO5WYHU.js +0 -4
  50. package/dist/Composition-Y5VZSDDP.cjs +0 -13
  51. package/dist/chunk-7IJHJXDI.js.map +0 -1
  52. package/dist/chunk-KQM2ZZMK.cjs.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FrameEvent, I as InspectOptions, c as InspectReport, L as LayerInfo, M as Movie, d as MovieOptions, P as ProgressEvent, R as RenderOptions, S as SnapshotOptions, e as SoundMeasure } from './Movie-CADDBfpl.cjs';
2
- import { S as Sequence } from './Base-4vNLl4tH.cjs';
3
- import { S as SequenceSpec, C as CompositionShape, I as ImageSequenceSpec, a as CameraSequenceSpec, b as SequenceCommon } from './types-DrW9QeKU.cjs';
4
- export { A as ArcShapeSpec, c as AssetSpec, d as AudioAssetSpec, e as AudioSequenceSpec, f as AudioSfxSpec, g as ChromaKeyFilterSpec, h as CircleShapeSpec, i as CompositionSequenceSpec, j as CompositionSpec, k as CrossfadeTransition, l as CustomFilterSpec, D as DipTransition, m as DissolveTransition, E as EllipseShapeSpec, n as Expr, F as FilterSpec, G as GradientSpec, o as IrisTransition, K as Keyframe, L as LineShapeSpec, P as PathShapeSpec, p as PolygonShapeSpec, q as PropValue, r as Props, R as RectShapeSpec, s as SfxKnobs, t as SfxOptions, u as SfxPreset, v as SfxVoice, w as ShapeSequenceSpec, x as SlideTransition, T as TextSequenceSpec, y as TransitionCommon, z as TransitionSpec, V as VideoSequenceSpec, W as WipeTransition, Z as ZoomTransition } from './types-DrW9QeKU.cjs';
1
+ export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FrameEvent, I as InspectOptions, c as InspectReport, L as LayerInfo, M as Movie, d as MovieOptions, P as ProgressEvent, R as RenderOptions, S as SnapshotOptions, e as SoundMeasure, T as TimelineData, f as TimelineHtmlOptions, g as TimelineRow, h as TimelineSvgOptions, i as TimelineTransition } from './Movie-BK9daiTt.cjs';
2
+ import { S as Sequence } from './Base-CSIulyIq.cjs';
3
+ import { S as SequenceSpec, C as CompositionShape, I as ImageSequenceSpec, a as CameraSequenceSpec, b as SequenceCommon } from './types-oJijbn-K.cjs';
4
+ export { A as ArcShapeSpec, c as AssetSpec, d as AudioAssetSpec, e as AudioSequenceSpec, f as AudioSfxSpec, g as ChromaKeyFilterSpec, h as CircleShapeSpec, i as CompositionSequenceSpec, j as CompositionSpec, k as CrossfadeTransition, l as CustomFilterSpec, D as DipTransition, m as DissolveTransition, E as EllipseShapeSpec, n as Expr, F as FilterSpec, G as GradientSpec, o as IrisTransition, K as Keyframe, L as LineShapeSpec, P as PathShapeSpec, p as PolygonShapeSpec, q as PropValue, r as Props, R as RectShapeSpec, s as SfxKnobs, t as SfxOptions, u as SfxPreset, v as SfxVoice, w as ShapeSequenceSpec, x as SlideTransition, T as TextSequenceSpec, y as TransitionCommon, z as TransitionSpec, V as VideoSequenceSpec, W as WipeTransition, Z as ZoomTransition } from './types-oJijbn-K.cjs';
5
5
  import 'pixi.js';
6
6
  import 'gsap';
7
7
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FrameEvent, I as InspectOptions, c as InspectReport, L as LayerInfo, M as Movie, d as MovieOptions, P as ProgressEvent, R as RenderOptions, S as SnapshotOptions, e as SoundMeasure } from './Movie-Ch2SUI1h.js';
2
- import { S as Sequence } from './Base-CD4AoT5-.js';
3
- import { S as SequenceSpec, C as CompositionShape, I as ImageSequenceSpec, a as CameraSequenceSpec, b as SequenceCommon } from './types-DrW9QeKU.js';
4
- export { A as ArcShapeSpec, c as AssetSpec, d as AudioAssetSpec, e as AudioSequenceSpec, f as AudioSfxSpec, g as ChromaKeyFilterSpec, h as CircleShapeSpec, i as CompositionSequenceSpec, j as CompositionSpec, k as CrossfadeTransition, l as CustomFilterSpec, D as DipTransition, m as DissolveTransition, E as EllipseShapeSpec, n as Expr, F as FilterSpec, G as GradientSpec, o as IrisTransition, K as Keyframe, L as LineShapeSpec, P as PathShapeSpec, p as PolygonShapeSpec, q as PropValue, r as Props, R as RectShapeSpec, s as SfxKnobs, t as SfxOptions, u as SfxPreset, v as SfxVoice, w as ShapeSequenceSpec, x as SlideTransition, T as TextSequenceSpec, y as TransitionCommon, z as TransitionSpec, V as VideoSequenceSpec, W as WipeTransition, Z as ZoomTransition } from './types-DrW9QeKU.js';
1
+ export { A as AudioInspectOptions, a as AudioReport, b as AudioSourceReport, C as ContactSheetOptions, F as FrameEvent, I as InspectOptions, c as InspectReport, L as LayerInfo, M as Movie, d as MovieOptions, P as ProgressEvent, R as RenderOptions, S as SnapshotOptions, e as SoundMeasure, T as TimelineData, f as TimelineHtmlOptions, g as TimelineRow, h as TimelineSvgOptions, i as TimelineTransition } from './Movie-B0OUKlZI.js';
2
+ import { S as Sequence } from './Base-DVgwyIUj.js';
3
+ import { S as SequenceSpec, C as CompositionShape, I as ImageSequenceSpec, a as CameraSequenceSpec, b as SequenceCommon } from './types-oJijbn-K.js';
4
+ export { A as ArcShapeSpec, c as AssetSpec, d as AudioAssetSpec, e as AudioSequenceSpec, f as AudioSfxSpec, g as ChromaKeyFilterSpec, h as CircleShapeSpec, i as CompositionSequenceSpec, j as CompositionSpec, k as CrossfadeTransition, l as CustomFilterSpec, D as DipTransition, m as DissolveTransition, E as EllipseShapeSpec, n as Expr, F as FilterSpec, G as GradientSpec, o as IrisTransition, K as Keyframe, L as LineShapeSpec, P as PathShapeSpec, p as PolygonShapeSpec, q as PropValue, r as Props, R as RectShapeSpec, s as SfxKnobs, t as SfxOptions, u as SfxPreset, v as SfxVoice, w as ShapeSequenceSpec, x as SlideTransition, T as TextSequenceSpec, y as TransitionCommon, z as TransitionSpec, V as VideoSequenceSpec, W as WipeTransition, Z as ZoomTransition } from './types-oJijbn-K.js';
5
5
  import 'pixi.js';
6
6
  import 'gsap';
7
7
 
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
+ import { CompositionSequence } from './chunk-36Z4RIRK.js';
2
+ import { ensureFilterLibrary, homeDistance, DEFAULT_FOV, DEG, resolveAt, collectPropKeys } from './chunk-4O2NYDJG.js';
3
+ export { registerSequenceType } from './chunk-4O2NYDJG.js';
1
4
  import { warnUnknownOptions, suggestName } from './chunk-DKGG7WD4.js';
2
- import { CompositionSequence } from './chunk-B2FTJLNB.js';
3
- import { homeDistance, DEFAULT_FOV, DEG, resolveAt, collectPropKeys } from './chunk-7IJHJXDI.js';
4
- export { registerSequenceType } from './chunk-7IJHJXDI.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';
7
7
  import { gsap } from 'gsap';
@@ -161,7 +161,7 @@ function fft(re, im) {
161
161
  }
162
162
  var FRAME = 2048;
163
163
  function spectralCentroid(samples, sampleRate, from = 0, to = samples.length) {
164
- let num = 0;
164
+ let num2 = 0;
165
165
  let den = 0;
166
166
  const re = new Float64Array(FRAME);
167
167
  const im = new Float64Array(FRAME);
@@ -176,11 +176,11 @@ function spectralCentroid(samples, sampleRate, from = 0, to = samples.length) {
176
176
  fft(re, im);
177
177
  for (let b = 1; b < FRAME / 2; b++) {
178
178
  const power = re[b] * re[b] + im[b] * im[b];
179
- num += power * (b * sampleRate / FRAME);
179
+ num2 += power * (b * sampleRate / FRAME);
180
180
  den += power;
181
181
  }
182
182
  }
183
- return den > 1e-9 ? Math.round(num / den / 10) * 10 : 0;
183
+ return den > 1e-9 ? Math.round(num2 / den / 10) * 10 : 0;
184
184
  }
185
185
 
186
186
  // src/core/inspectAudio.ts
@@ -1120,7 +1120,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1120
1120
  const seen = /* @__PURE__ */ new Map();
1121
1121
  const scene = /* @__PURE__ */ new Map();
1122
1122
  const scaledToNothing = /* @__PURE__ */ new Set();
1123
- const walk = (comp, prefix, insideThreeD, parentVisible, parentScene, parentMoving = false, parentZero = false) => {
1123
+ const walk2 = (comp, prefix, insideThreeD, parentVisible, parentScene, parentMoving = false, parentZero = false) => {
1124
1124
  const blending = /* @__PURE__ */ new Set();
1125
1125
  for (const w of transitionWindowsOf(comp.spec)) {
1126
1126
  const start = (comp.absoluteStart ?? 0) + w.start, end = (comp.absoluteStart ?? 0) + w.end;
@@ -1164,10 +1164,10 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1164
1164
  const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
1165
1165
  if (group) scene.set(info, group);
1166
1166
  if (zero || seq.showsNothingYet) scaledToNothing.add(info);
1167
- if (seq instanceof CompositionSequence) walk(seq, prefix + label + "/", insideThreeD || threeD, visible, group, moving, zero);
1167
+ if (seq instanceof CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, moving, zero);
1168
1168
  });
1169
1169
  };
1170
- walk(root, "", false, true);
1170
+ walk2(root, "", false, true);
1171
1171
  const issues = [];
1172
1172
  const texts = layers.filter((l) => l.type === "text" && l.visible && l.alpha >= FAINT && l.bounds);
1173
1173
  for (const l of texts) {
@@ -1204,6 +1204,158 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1204
1204
  return { frame, time, canvas, summary: { layers: layers.length, visible: layers.filter((l) => l.visible).length }, issues, layers: listed };
1205
1205
  }
1206
1206
 
1207
+ // src/core/timelineChart.ts
1208
+ var GROUP_MIN = 4;
1209
+ var MAX_KEYS = 200;
1210
+ var round = (n) => Math.round(n * 1e6) / 1e6;
1211
+ var family = (name) => name.replace(/\d+/g, "#");
1212
+ function detailOf(seq) {
1213
+ const s = seq.spec;
1214
+ if (typeof s.text === "string") return s.text.length > 60 ? s.text.slice(0, 57) + "\u2026" : s.text;
1215
+ if (typeof s.asset === "string") return `asset \u201C${s.asset}\u201D`;
1216
+ if (typeof s.sfx === "string") return `sfx \u201C${s.sfx}\u201D`;
1217
+ if (s.sfx && typeof s.sfx === "object") return "sfx (custom)";
1218
+ return void 0;
1219
+ }
1220
+ function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions) {
1221
+ const entries = [];
1222
+ comp._children.forEach((seq, i) => {
1223
+ const name = seq.spec.name ?? `${seq.spec.type}#${i}`;
1224
+ const start = parentStart + (seq.at ?? 0);
1225
+ const end = Math.min(parentEnd, seq.duration !== void 0 ? start + seq.duration : parentEnd);
1226
+ const span = end - start;
1227
+ const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
1228
+ const row = { path: prefix + name, name, type: seq.spec.type, depth, start: round(start), end: round(end), keys, detail: detailOf(seq) };
1229
+ const inner = [];
1230
+ const isComp = seq instanceof CompositionSequence;
1231
+ if (isComp) walk(seq, start, end, depth + 1, prefix + name + "/", inner, transitions);
1232
+ entries.push({ row, inner, groupable: !isComp });
1233
+ });
1234
+ rows.push(...groupFamilies(entries));
1235
+ for (const w of transitionWindowsOf(comp.spec)) {
1236
+ transitions.push({ from: prefix + w.from, to: prefix + w.to, start: round(parentStart + w.start), end: round(parentStart + w.end) });
1237
+ }
1238
+ }
1239
+ function groupFamilies(entries) {
1240
+ const buckets = /* @__PURE__ */ new Map();
1241
+ for (const e of entries) {
1242
+ if (!e.groupable) continue;
1243
+ const key = e.row.type + "|" + family(e.row.name);
1244
+ (buckets.get(key) ?? buckets.set(key, []).get(key)).push(e.row);
1245
+ }
1246
+ const out = [];
1247
+ const done = /* @__PURE__ */ new Set();
1248
+ for (const e of entries) {
1249
+ const key = e.row.type + "|" + family(e.row.name);
1250
+ const members = e.groupable ? buckets.get(key) : null;
1251
+ if (members && members.length >= GROUP_MIN) {
1252
+ if (done.has(key)) continue;
1253
+ done.add(key);
1254
+ const label = `${family(e.row.name)} \xD7${members.length}`;
1255
+ out.push({
1256
+ path: e.row.path.slice(0, e.row.path.length - e.row.name.length) + label,
1257
+ name: label,
1258
+ type: e.row.type,
1259
+ depth: e.row.depth,
1260
+ start: Math.min(...members.map((x) => x.start)),
1261
+ end: Math.max(...members.map((x) => x.end)),
1262
+ keys: members.flatMap((x) => x.keys).sort((a, b) => a - b).slice(0, MAX_KEYS),
1263
+ parts: members.map((x) => ({ start: x.start, end: x.end }))
1264
+ });
1265
+ } else {
1266
+ out.push(e.row, ...e.inner);
1267
+ continue;
1268
+ }
1269
+ out.push(...e.inner);
1270
+ }
1271
+ return out;
1272
+ }
1273
+ function collectTimeline(root, duration) {
1274
+ const rows = [];
1275
+ const transitions = [];
1276
+ walk(root, 0, duration, 0, "", rows, transitions);
1277
+ return { duration, rows, transitions };
1278
+ }
1279
+ var COLORS = {
1280
+ text: "#4cc9f0",
1281
+ shape: "#ffd166",
1282
+ image: "#7bd88f",
1283
+ video: "#c77dff",
1284
+ audio: "#ff7aa8",
1285
+ composition: "#8ea4c8",
1286
+ three: "#ff9f68",
1287
+ camera: "#b0b0b0"
1288
+ };
1289
+ var esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1290
+ var num = (n) => String(Math.round(n * 100) / 100);
1291
+ function tickStep(duration, chartWidth) {
1292
+ for (const s of [0.04, 0.1, 0.2, 0.5, 1, 2, 5, 10, 15, 30, 60, 120, 300, 600, 1200, 3600]) if (s / duration * chartWidth >= 70) return s;
1293
+ return 7200;
1294
+ }
1295
+ function timelineSvg(data, opts = {}) {
1296
+ const CHART = Math.max(100, opts.chartWidth ?? 950), withLabels = opts.labels !== false;
1297
+ const LABEL = withLabels ? 250 : 0, ROW = 22, TOP = 34, PAD = 16;
1298
+ const W = LABEL + CHART + PAD * 2, H = TOP + data.rows.length * ROW + PAD;
1299
+ const x = (t) => PAD + LABEL + Math.max(0, Math.min(data.duration, t)) / data.duration * CHART;
1300
+ const step = tickStep(data.duration, CHART);
1301
+ const out = [];
1302
+ for (let t = 0; t <= data.duration + 1e-9; t += step) {
1303
+ out.push(`<line class="grid" x1="${num(x(t))}" y1="${TOP - 6}" x2="${num(x(t))}" y2="${H - PAD}"/>`);
1304
+ out.push(`<text class="tick" x="${num(x(t))}" y="${TOP - 12}" text-anchor="middle">${num(t)}${t === 0 || Math.abs(t - data.duration) < 1e-9 ? " s" : ""}</text>`);
1305
+ }
1306
+ if (Math.abs(data.duration % step) > 1e-9) out.push(`<text class="tick" x="${num(x(data.duration))}" y="${TOP - 12}" text-anchor="middle">${num(data.duration)} s</text>`);
1307
+ for (const t of data.transitions) {
1308
+ out.push(`<g><title>transition ${esc(t.from)} \u2192 ${esc(t.to)} (${num(t.start)}\u2013${num(t.end)} s)</title><rect class="transition" x="${num(x(t.start))}" y="${TOP - 4}" width="${num(Math.max(2, x(t.end) - x(t.start)))}" height="${H - TOP - PAD + 4}"/></g>`);
1309
+ }
1310
+ data.rows.forEach((r, i) => {
1311
+ const y = TOP + i * ROW;
1312
+ const color = COLORS[r.type] ?? "#9aa7b8";
1313
+ const tip = `${r.path} \xB7 ${r.type} \xB7 ${num(r.start)}\u2013${num(r.end)} s (${num(r.end - r.start)} s)${r.keys.length ? ` \xB7 ${r.keys.length} keyframe${r.keys.length > 1 ? "s" : ""}` : ""}${r.detail ? ` \xB7 ${r.detail}` : ""}`;
1314
+ out.push(`<g class="row" data-start="${num(r.start)}"><title>${esc(tip)}</title>`);
1315
+ out.push(`<rect class="band" x="${PAD}" y="${y}" width="${LABEL + CHART}" height="${ROW}" ${i % 2 ? 'fill-opacity=".05"' : 'fill-opacity="0"'}/>`);
1316
+ if (withLabels) out.push(`<text class="label" x="${PAD + 6 + r.depth * 14}" y="${y + 15}">${esc(r.name)}</text>`);
1317
+ for (const p of r.parts ?? [{ start: r.start, end: r.end }]) {
1318
+ out.push(`<rect class="bar" x="${num(x(p.start))}" y="${y + 4}" width="${num(Math.max(2, x(p.end) - x(p.start)))}" height="${ROW - 8}" rx="3" fill="${color}"/>`);
1319
+ }
1320
+ for (const k of r.keys) out.push(`<path class="key" d="M ${num(x(k))} ${y + 7} l 3 4 l -3 4 l -3 -4 z"/>`);
1321
+ out.push("</g>");
1322
+ });
1323
+ const legend = Object.entries(COLORS).map(([type, c], i) => `<g transform="translate(${PAD + i * 108},${H + 10})"><rect width="12" height="12" rx="3" fill="${c}"/><text x="18" y="10" class="tick">${type}</text></g>`).join("");
1324
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H + 32}" viewBox="0 0 ${W} ${H + 32}" role="img" aria-label="Timeline of ${data.rows.length} rows" data-duration="${num(data.duration)}" data-x0="${num(x(0))}" data-x1="${num(x(data.duration))}" data-top="${TOP - 6}" data-bottom="${H - PAD}" data-row="${ROW}">
1325
+ ${out.join("\n")}
1326
+ ${legend}
1327
+ </svg>`;
1328
+ }
1329
+ function timelineHtml(data, opts = {}) {
1330
+ const W = 250 + 950 + 32;
1331
+ const title = esc(opts.title ?? "Timeline");
1332
+ return `<!doctype html>
1333
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${title} \u2014 timeline</title>
1334
+ <style>
1335
+ :root { --bg: #f7f5f1; --ink: #1d2433; --dim: #6b7587; --grid: #d9d5cc; --key: #1d2433; --trans: #4cc9f0; }
1336
+ @media (prefers-color-scheme: dark) { :root { --bg: #0e1320; --ink: #e8eefc; --dim: #8fa0bf; --grid: #26304a; --key: #ffffff; --trans: #4cc9f0; } }
1337
+ body { margin: 0; background: var(--bg); color: var(--ink); font: 14px system-ui, sans-serif; }
1338
+ main { padding: 20px 16px 32px; max-width: ${W + 32}px; margin: 0 auto; overflow-x: auto; }
1339
+ h1 { font-size: 18px; font-weight: 600; margin: 0 0 4px; }
1340
+ p { margin: 0 0 14px; color: var(--dim); font-size: 13px; }
1341
+ svg { display: block; }
1342
+ .grid { stroke: var(--grid); stroke-width: 1; }
1343
+ .tick { fill: var(--dim); font-size: 11px; }
1344
+ .label { fill: var(--ink); font-size: 12px; font-family: ui-monospace, Menlo, monospace; }
1345
+ .band { fill: var(--ink); }
1346
+ .key { fill: var(--key); opacity: .85; }
1347
+ .transition { fill: var(--trans); opacity: .16; }
1348
+ .bar { opacity: .88; }
1349
+ .row:hover .bar { opacity: 1; }
1350
+ </style></head>
1351
+ <body><main>
1352
+ <h1>${title}</h1>
1353
+ <p>${data.rows.length} row${data.rows.length === 1 ? "" : "s"} \xB7 ${num(data.duration)} s \xB7 \u25C6 keyframe start \xB7 shaded band: a transition \xB7 hover a bar for details</p>
1354
+ ${timelineSvg(data)}
1355
+ </main></body></html>
1356
+ `;
1357
+ }
1358
+
1207
1359
  // src/core/frames.ts
1208
1360
  function pickFrames(lastFrame, count) {
1209
1361
  const last = Math.max(0, Math.floor(lastFrame));
@@ -1359,6 +1511,7 @@ var Movie = class {
1359
1511
  height: options.composition.height ?? this.height,
1360
1512
  duration: options.composition.duration ?? this.duration
1361
1513
  } : void 0;
1514
+ await ensureFilterLibrary(seededComposition);
1362
1515
  const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
1363
1516
  const rootSeqSpec = {
1364
1517
  type: "composition",
@@ -1467,6 +1620,24 @@ var Movie = class {
1467
1620
  await this.gotoFrame(f, true);
1468
1621
  return inspectScene(this._rootSequence, f, f / this.frameRate, { width: this.width, height: this.height }, opts);
1469
1622
  }
1623
+ /**
1624
+ * Every layer as a row with absolute start / end / keyframe times (seconds), plus the transition windows. Runs of
1625
+ * similar layers (`pop-1` … `pop-12`) are one row with a `parts` span each. Read it, or draw it with `timelineChart()`.
1626
+ */
1627
+ timelineData() {
1628
+ this._requireReady("timelineData");
1629
+ return collectTimeline(this._rootSequence, this.duration);
1630
+ }
1631
+ /** The timeline as one self-contained HTML page (an inline SVG, no scripts): layers as bars on a time axis, ◆ keyframes, transition bands. Open it in a browser. */
1632
+ timelineChart(opts = {}) {
1633
+ warnUnknownOptions("movie.timelineChart()", opts, ["title"]);
1634
+ return timelineHtml(this.timelineData(), opts);
1635
+ }
1636
+ /** Just the chart: one `<svg>` whose geometry is in `data-*` attributes (a viewer puts a playhead on it: `pixi-effects-view`). */
1637
+ timelineSvg(opts = {}) {
1638
+ warnUnknownOptions("movie.timelineSvg()", opts, ["chartWidth", "labels"]);
1639
+ return timelineSvg(this.timelineData(), opts);
1640
+ }
1470
1641
  /**
1471
1642
  * The soundtrack, measured: when every audio layer plays, how loud the mix is over time, and `issues`
1472
1643
  * (limiting, inaudible layers, sounds cut off by the end). Use it to CHECK sound you cannot hear.