@video-editor/renderer 0.0.1-beta.29 → 0.0.1-beta.30

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/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { createValidator as Gt, createResourceManager as Nt, getResourceKey as N
2
2
  import { toRaw as zt, isRef as Kt, shallowRef as ut, unref as lt, ref as ft, computed as qt, effectScope as Bt, watch as W } from "@vue/reactivity";
3
3
  import { AudioClip as jt, renderTxt2ImgBitmap as _t, MP4Clip as G, Combinator as mt, OffscreenSprite as wt } from "@webav/av-cliper";
4
4
  import { file as Wt } from "opfs-tools";
5
- import { Application as yt, Sprite as P, Texture as V, Graphics as vt, Container as Yt } from "pixi.js";
5
+ import { Application as yt, Sprite as O, Texture as V, Graphics as vt, Container as Yt } from "pixi.js";
6
6
  async function Xt(o) {
7
7
  const t = new yt();
8
8
  return await t.init({ resizeTo: window, backgroundAlpha: 0, ...o }), t;
@@ -58,8 +58,8 @@ function Mt(o, t) {
58
58
  });
59
59
  }
60
60
  function Zt(o, t, i, r) {
61
- const a = ee(t), u = o instanceof P && o.texture.width || i, c = o instanceof P && o.texture.height || r, d = Ht(t, i, r, u, c);
62
- o instanceof P ? (o.anchor.set(0.5), o.width = d.width, o.height = d.height, o.position.set(d.centerX, d.centerY), o.rotation = d.rotationRad, o.texture.source?.addEventListener?.("error", () => {
61
+ const a = ee(t), u = o instanceof O && o.texture.width || i, c = o instanceof O && o.texture.height || r, d = Ht(t, i, r, u, c);
62
+ o instanceof O ? (o.anchor.set(0.5), o.width = d.width, o.height = d.height, o.position.set(d.centerX, d.centerY), o.rotation = d.rotationRad, o.texture.source?.addEventListener?.("error", () => {
63
63
  o.texture = V.from(te(i, r));
64
64
  }, { once: !0 })) : o instanceof vt && (o.clear(), o.rect(0, 0, d.width, d.height).fill({ color: gt("url" in t && typeof t.url == "string" ? t.url : t.segmentType), alpha: xt(t) ? a : 0.35 }), o.pivot.set(d.width / 2, d.height / 2), o.position.set(d.centerX, d.centerY), o.rotation = d.rotationRad), o.alpha = a;
65
65
  }
@@ -109,6 +109,9 @@ class oe {
109
109
  constructor(t) {
110
110
  this.protocol = t, this.ctx = new (window.AudioContext || window.webkitAudioContext)();
111
111
  }
112
+ setProtocol(t) {
113
+ this.protocol = t;
114
+ }
112
115
  async sync(t, i) {
113
116
  if (!i) {
114
117
  this.stopAll(), this.lastTime = t;
@@ -478,19 +481,19 @@ async function le(o) {
478
481
  const c = Nt({ dir: o.resourceDir }), d = /* @__PURE__ */ new Set(), l = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Map(), h = /* @__PURE__ */ new Set(), m = /* @__PURE__ */ new Set(), S = o.videoSourceMode ?? "auto", M = /* @__PURE__ */ new Map(), k = /* @__PURE__ */ new Map(), w = ft(0), x = ft(!1), R = qt(() => J(r.value)), E = new oe(r.value);
479
482
  let A, L = 0, C = 0;
480
483
  async function St(e) {
481
- const s = C, { protocol: n, at: f, layer: y } = e, v = Pt(n, f), g = Mt(n, v), T = e.app.renderer.width, b = e.app.renderer.height;
484
+ const s = C, { protocol: n, at: f, layer: y } = e, v = Ot(n, f), g = Mt(n, v), T = e.app.renderer.width, b = e.app.renderer.height;
482
485
  E.sync(f, x.value);
483
486
  const I = [];
484
487
  for (const { segment: U } of g) {
485
488
  if (s !== C)
486
489
  return;
487
- const O = await e.getDisplay(U);
490
+ const P = await e.getDisplay(U);
488
491
  if (s !== C)
489
492
  return;
490
- if (O && !O.destroyed) {
491
- if (Zt(O, U, T, b), Ot(U) && await j(U, v), s !== C)
493
+ if (P && !P.destroyed) {
494
+ if (Zt(P, U, T, b), Pt(U) && await j(U, v), s !== C)
492
495
  return;
493
- I.push(O);
496
+ I.push(P);
494
497
  }
495
498
  }
496
499
  if (s !== C)
@@ -516,7 +519,7 @@ async function le(o) {
516
519
  console.error("[renderer] invalid protocol update", s);
517
520
  return;
518
521
  }
519
- C += 1, Z(), o.warmUpResources !== !1 && Tt(r.value), bt(r.value), K(), o.manualRender || z();
522
+ E.setProtocol(r.value), C += 1, Z(), o.warmUpResources !== !1 && Tt(r.value), bt(r.value), K(), o.manualRender || z();
520
523
  },
521
524
  { deep: !0, immediate: !0 }
522
525
  ), o.manualRender || W(w, () => {
@@ -597,7 +600,7 @@ async function le(o) {
597
600
  return n || Y(e.segmentType, e.url);
598
601
  }
599
602
  const s = await Lt(e.url);
600
- return s ? new P(s) : Y(e.segmentType, e.url);
603
+ return s ? new O(s) : Y(e.segmentType, e.url);
601
604
  }
602
605
  if (e.segmentType === "text")
603
606
  return await It(e);
@@ -611,7 +614,7 @@ async function le(o) {
611
614
  if (!n)
612
615
  return;
613
616
  const f = await de(s, ce(n)), y = V.from(f);
614
- return new P(y);
617
+ return new O(y);
615
618
  }
616
619
  async function Lt(e) {
617
620
  const s = e.startsWith("data:"), n = /^https?:\/\//.test(e);
@@ -728,10 +731,10 @@ async function le(o) {
728
731
  return await D(e, n).catch(() => {
729
732
  });
730
733
  }
731
- function Ot(e) {
734
+ function Pt(e) {
732
735
  return e.segmentType === "frames" && e.type === "video" && typeof e.url == "string" && ct(e.url);
733
736
  }
734
- function Pt(e, s) {
737
+ function Ot(e, s) {
735
738
  const n = J(e);
736
739
  if (n <= 0)
737
740
  return 0;
@@ -812,8 +815,8 @@ async function le(o) {
812
815
  f = new G(I.body);
813
816
  else {
814
817
  const F = await I.arrayBuffer(), U = new ReadableStream({
815
- start(O) {
816
- O.enqueue(new Uint8Array(F)), O.close();
818
+ start(P) {
819
+ P.enqueue(new Uint8Array(F)), P.close();
817
820
  }
818
821
  });
819
822
  f = new G(U);
@@ -822,7 +825,7 @@ async function le(o) {
822
825
  await f.ready;
823
826
  const { width: y, height: v } = f.meta, g = document.createElement("canvas");
824
827
  g.width = y || 1, g.height = v || 1;
825
- const T = V.from(g), b = s?.sprite ?? new P(T);
828
+ const T = V.from(g), b = s?.sprite ?? new O(T);
826
829
  return s?.sprite && (s.sprite.texture = T, s.oldTexture?.destroy(!0)), { kind: "mp4clip", clip: f, canvas: g, texture: T, sprite: b, meta: { width: y, height: v } };
827
830
  } catch (y) {
828
831
  throw f?.destroy(), y;
@@ -848,7 +851,7 @@ async function le(o) {
848
851
  }
849
852
  const f = n.videoWidth || 1, y = n.videoHeight || 1, v = document.createElement("canvas");
850
853
  v.width = f, v.height = y;
851
- const g = V.from(v), T = s?.sprite ?? new P(g);
854
+ const g = V.from(v), T = s?.sprite ?? new O(g);
852
855
  return s?.sprite && (s.sprite.texture = g, s.oldTexture?.destroy(!0)), { kind: "element", video: n, canvas: v, texture: g, sprite: T, meta: { width: f, height: y } };
853
856
  }
854
857
  async function Ut(e, s) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/2d/index.ts","../src/layout.ts","../src/helpers.ts","../src/audio-manager.ts","../src/text.ts","../src/renderer-core.ts","../src/concat.ts","../src/protocol-clip.ts","../src/compose.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\nimport type { ApplicationOptions } from 'pixi.js'\nimport { Application } from 'pixi.js'\n\ndeclare global {\n\n // eslint-disable-next-line vars-on-top\n var __PIXI_APP__: Application\n}\n\nexport async function createApp(opts?: Partial<ApplicationOptions>) {\n const app = new Application()\n\n await app.init({ resizeTo: window, backgroundAlpha: 0, ...opts })\n\n if (import.meta.env.DEV) {\n globalThis.__PIXI_APP__ = app\n }\n\n return app\n}\n","import type { IFillMode, SegmentUnion } from '@video-editor/shared'\n\nexport interface SegmentLayout {\n width: number\n height: number\n centerX: number\n centerY: number\n rotationRad: number\n}\n\nexport function resolveFillSize(\n mode: IFillMode | undefined,\n sourceWidth: number,\n sourceHeight: number,\n stageWidth: number,\n stageHeight: number,\n) {\n const safeSourceWidth = sourceWidth || stageWidth\n const safeSourceHeight = sourceHeight || stageHeight\n if (!safeSourceWidth || !safeSourceHeight)\n return { width: stageWidth, height: stageHeight }\n\n const sourceRatio = safeSourceWidth / safeSourceHeight\n const stageRatio = stageWidth / stageHeight\n\n switch (mode) {\n case 'none':\n return { width: safeSourceWidth, height: safeSourceHeight }\n case 'cover':\n if (sourceRatio > stageRatio)\n return { width: stageHeight * sourceRatio, height: stageHeight }\n return { width: stageWidth, height: stageWidth / sourceRatio }\n case 'stretch':\n return { width: stageWidth, height: stageHeight }\n case 'contain':\n default:\n if (sourceRatio > stageRatio)\n return { width: stageWidth, height: stageWidth / sourceRatio }\n return { width: stageHeight * sourceRatio, height: stageHeight }\n }\n}\n\nexport function computeSegmentLayout(\n segment: SegmentUnion,\n stageWidth: number,\n stageHeight: number,\n sourceWidth: number,\n sourceHeight: number,\n): SegmentLayout {\n const fillMode = 'fillMode' in segment ? segment.fillMode : undefined\n const { width, height } = resolveFillSize(\n fillMode,\n sourceWidth,\n sourceHeight,\n stageWidth,\n stageHeight,\n )\n\n const transform = 'transform' in segment ? segment.transform : undefined\n const [px, py] = transform?.position ?? [0, 0]\n const [sx, sy] = transform?.scale ?? [1, 1]\n const rotation = transform?.rotation?.[2] ?? 0\n\n const finalWidth = width * sx\n const finalHeight = height * sy\n const centerX = stageWidth / 2 + (px * stageWidth) / 2\n const centerY = stageHeight / 2 - (py * stageHeight) / 2\n\n return {\n width: finalWidth,\n height: finalHeight,\n centerX,\n centerY,\n rotationRad: (rotation / 180) * Math.PI,\n }\n}\n","import type { IVideoProtocol, SegmentUnion } from '@video-editor/shared'\nimport type { TrackTypeMapTrack } from '@video-editor/shared'\nimport type { PixiDisplayObject } from './types'\nimport { toRaw } from '@vue/reactivity'\nimport { Graphics, Sprite, Texture } from 'pixi.js'\nimport { computeSegmentLayout } from './layout'\n\nexport function collectResourceUrls(protocol: IVideoProtocol) {\n const urls = new Set<string>()\n for (const track of protocol.tracks) {\n for (const segment of track.children) {\n if (segment.url)\n urls.add(segment.url)\n }\n }\n return urls\n}\n\nexport function collectActiveSegments(protocol: IVideoProtocol, at: number) {\n const active: { segment: SegmentUnion, trackIndex: number, childIndex: number }[] = []\n protocol.tracks.forEach((track, trackIndex) => {\n track.children.forEach((segment, childIndex) => {\n if (segment.startTime <= at && at < segment.endTime)\n active.push({ segment, trackIndex, childIndex })\n })\n })\n\n return active.sort((a, b) => {\n const aTrack = protocol.tracks[a.trackIndex]\n const bTrack = protocol.tracks[b.trackIndex]\n const aIsMain = aTrack?.trackType === 'frames' && (aTrack as TrackTypeMapTrack['frames']).isMain\n const bIsMain = bTrack?.trackType === 'frames' && (bTrack as TrackTypeMapTrack['frames']).isMain\n const total = protocol.tracks.length\n const aOrder = aIsMain ? 0 : total - a.trackIndex\n const bOrder = bIsMain ? 0 : total - b.trackIndex\n if (aOrder !== bOrder)\n return aOrder - bOrder\n if (a.trackIndex === b.trackIndex)\n return a.childIndex - b.childIndex\n return a.trackIndex - b.trackIndex\n })\n}\n\nexport function applyDisplayProps(display: PixiDisplayObject, segment: SegmentUnion, width: number, height: number) {\n const opacity = readOpacity(segment)\n const sourceWidth = display instanceof Sprite ? display.texture.width || width : width\n const sourceHeight = display instanceof Sprite ? display.texture.height || height : height\n const layout = computeSegmentLayout(segment, width, height, sourceWidth, sourceHeight)\n\n if (display instanceof Sprite) {\n display.anchor.set(0.5)\n display.width = layout.width\n display.height = layout.height\n display.position.set(layout.centerX, layout.centerY)\n display.rotation = layout.rotationRad\n const src = display.texture.source as { addEventListener?: (type: string, cb: () => void, opts?: AddEventListenerOptions) => void } | undefined\n src?.addEventListener?.('error', () => {\n // fallback to a colored rect if texture failed\n display.texture = Texture.from(placeholderTexture(width, height))\n }, { once: true })\n }\n else if (display instanceof Graphics) {\n display.clear()\n display\n .rect(0, 0, layout.width, layout.height)\n .fill({ color: stringToColor('url' in segment && typeof segment.url === 'string' ? segment.url : segment.segmentType), alpha: hasOpacity(segment) ? opacity : 0.35 })\n display.pivot.set(layout.width / 2, layout.height / 2)\n display.position.set(layout.centerX, layout.centerY)\n display.rotation = layout.rotationRad\n }\n\n display.alpha = opacity\n}\n\nexport function placeholder(key: string, url?: string) {\n const g = new Graphics()\n g.rect(0, 0, 10, 10).fill({ color: stringToColor(url ?? key), alpha: 1 })\n return g\n}\n\nexport function placeholderTexture(width: number, height: number, color?: string) {\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\"><rect width=\"100%\" height=\"100%\" fill=\"${color ?? '#0f172a'}\" fill-opacity=\"0.8\"/></svg>`\n return `data:image/svg+xml;base64,${btoa(svg)}`\n}\n\nexport function stringToColor(key: string) {\n let hash = 0\n for (let i = 0; i < key.length; i++)\n hash = key.charCodeAt(i) + ((hash << 5) - hash)\n return hash & 0x00FFFFFF\n}\n\nexport function computeDuration(protocol: IVideoProtocol) {\n const endTimes = protocol.tracks.flatMap(track => track.children.map(seg => seg.endTime))\n return endTimes.length ? Math.max(...endTimes) : 0\n}\n\nexport function clamp(num: number, min: number, max: number) {\n return Math.min(Math.max(num, min), max)\n}\n\nexport function cloneProtocol(protocol: IVideoProtocol) {\n const raw = toRaw(protocol) as IVideoProtocol\n // use JSON clone to avoid structuredClone errors on proxies (e.g., Vue reactive)\n return JSON.parse(JSON.stringify(raw)) as IVideoProtocol\n}\n\nfunction hasOpacity(segment: SegmentUnion): segment is SegmentUnion & { opacity?: number } {\n return 'opacity' in segment\n}\n\nfunction readOpacity(segment: SegmentUnion) {\n if (hasOpacity(segment) && typeof segment.opacity === 'number')\n return segment.opacity\n return 1\n}\n","import type { IAudioSegment, IVideoProtocol } from '@video-editor/shared'\nimport type { MP4Clip, AudioClip } from '@webav/av-cliper'\nimport { collectActiveSegments } from './helpers'\nimport { AudioClip as WebAVAudioClip } from '@webav/av-cliper'\n\ninterface ClipEntry {\n clip: AudioClip\n ready: Promise<unknown>\n}\n\ninterface LoopState {\n stop: () => void\n sources: AudioBufferSourceNode[]\n isStopped: () => boolean\n}\n\ninterface AudioLoopContext {\n segment: IAudioSegment\n startedAt: number // ctx.currentTime when loop started\n segmentRelativeMs: number // position in segment when loop started\n}\n\ninterface Mp4State {\n loop: LoopState\n startUs: number\n startCtxTime: number\n fps: number\n nextStartAt?: number\n}\n\nexport class AudioManager {\n private protocol: IVideoProtocol\n private clips = new Map<string, ClipEntry>()\n private loadingClips = new Map<string, Promise<ClipEntry | undefined>>()\n private audioLoops = new Map<string, LoopState>()\n private audioLoopContexts = new Map<string, AudioLoopContext>()\n private mp4Loops = new Map<string, LoopState>()\n private mp4States = new Map<string, Mp4State>()\n private audioGains = new Map<string, GainNode>()\n private mp4Gains = new Map<string, GainNode>()\n private ctx: AudioContext\n private lastTime = 0\n\n constructor(protocol: IVideoProtocol) {\n this.protocol = protocol\n this.ctx = new (window.AudioContext || (window as any).webkitAudioContext)()\n }\n\n public async sync(currentTime: number, isPlaying: boolean) {\n if (!isPlaying) {\n // Always stop all audio when paused\n this.stopAll()\n this.lastTime = currentTime\n return\n }\n\n if (this.ctx.state === 'suspended')\n await this.ctx.resume().catch(() => {})\n\n if (Math.abs(currentTime - this.lastTime) > 1000)\n this.stopAll()\n this.lastTime = currentTime\n\n const activeSegments = collectActiveSegments(this.protocol, currentTime)\n const activeAudioKeys = new Set<string>()\n const activeVideoKeys = new Set<string>()\n\n for (const { segment } of activeSegments) {\n if (segment.segmentType === 'audio') {\n const key = this.audioKey(segment.id)\n activeAudioKeys.add(key)\n this.ensureAudioLoop(segment as IAudioSegment, currentTime)\n }\n else if (segment.segmentType === 'frames' && 'type' in segment && segment.type === 'video') {\n const key = this.videoKey(segment.id)\n activeVideoKeys.add(key)\n }\n }\n\n for (const [key, loop] of this.audioLoops) {\n if (!activeAudioKeys.has(key)) {\n loop.stop()\n // Stop all audio buffer sources\n for (const source of loop.sources) {\n try {\n source.stop()\n source.disconnect()\n }\n catch (e) {\n // Source may already be stopped or disconnected\n }\n }\n this.audioLoops.delete(key)\n this.audioLoopContexts.delete(key)\n this.audioGains.delete(key)\n }\n }\n\n for (const [key, loop] of this.mp4Loops) {\n if (!activeVideoKeys.has(key)) {\n loop.stop()\n // Stop all audio buffer sources\n for (const source of loop.sources) {\n try {\n source.stop()\n source.disconnect()\n }\n catch (e) {\n // Source may already be stopped or disconnected\n }\n }\n this.mp4Loops.delete(key)\n this.mp4States.delete(key)\n this.mp4Gains.delete(key)\n }\n }\n\n // Also clean up mp4States that don't have active loops (direct playback mode)\n for (const key of this.mp4States.keys()) {\n if (!activeVideoKeys.has(key) && !this.mp4Loops.has(key)) {\n this.mp4States.delete(key)\n this.mp4Gains.delete(key)\n }\n }\n }\n\n /**\n * Play audio frames from MP4 video directly.\n * Called by renderer after it gets audio data from clip.tick()\n */\n public playMp4AudioFrames(id: string, audio: Float32Array[] | undefined, sampleRate: number) {\n if (!audio || audio.length === 0 || !audio[0]?.length)\n return\n\n const key = this.videoKey(id)\n const volume = this.getSegmentVolume(id)\n const gainNode = this.getOrCreateGain(this.mp4Gains, key, volume)\n\n if (this.ctx.state === 'suspended')\n this.ctx.resume().catch(() => {})\n\n // Get or initialize the playback state for this video\n let state = this.mp4States.get(key)\n if (!state) {\n state = {\n loop: { stop: () => {}, sources: [], isStopped: () => false },\n startUs: 0,\n startCtxTime: this.ctx.currentTime,\n fps: 30,\n nextStartAt: 0,\n }\n this.mp4States.set(key, state)\n }\n\n // Play the audio frames\n state.nextStartAt = this.playFrames(audio, sampleRate, state.nextStartAt ?? 0, gainNode, state.loop.sources)\n }\n\n /**\n * Reset MP4 audio playback state (call when seeking or pausing)\n */\n public resetMp4Audio(id: string) {\n const key = this.videoKey(id)\n const state = this.mp4States.get(key)\n if (state) {\n state.nextStartAt = 0\n }\n }\n\n /**\n * Stop and clean up MP4 audio for a video segment\n */\n public stopMp4Audio(id: string) {\n const key = this.videoKey(id)\n const state = this.mp4States.get(key)\n if (state) {\n state.loop.stop()\n // Stop all audio buffer sources\n for (const source of state.loop.sources) {\n try {\n source.stop()\n source.disconnect()\n }\n catch (e) {\n // Source may already be stopped or disconnected\n }\n }\n this.mp4Loops.delete(key)\n this.mp4States.delete(key)\n this.mp4Gains.delete(key)\n }\n }\n\n /**\n * @deprecated Use playMp4AudioFrames instead\n */\n public ensureMp4Audio(id: string, clip: MP4Clip, startUs: number, fps: number) {\n // Legacy method - kept for compatibility but should not be used\n // The new approach is to pass audio data directly via playMp4AudioFrames\n const key = this.videoKey(id)\n const volume = this.getSegmentVolume(id)\n const gainNode = this.getOrCreateGain(this.mp4Gains, key, volume)\n const existing = this.mp4States.get(key)\n if (existing) {\n gainNode.gain.value = Math.max(0, volume)\n const elapsedUs = (this.ctx.currentTime - existing.startCtxTime) * 1e6\n const expectedUs = existing.startUs + elapsedUs\n if (Math.abs(expectedUs - startUs) < 150000 && existing.fps === fps)\n return\n existing.loop.stop()\n // Stop all audio buffer sources\n for (const source of existing.loop.sources) {\n try {\n source.stop()\n source.disconnect()\n }\n catch (e) {\n // Source may already be stopped or disconnected\n }\n }\n this.mp4Loops.delete(key)\n this.mp4States.delete(key)\n }\n if (this.ctx.state === 'suspended')\n this.ctx.resume().catch(() => {})\n const loop = this.startMp4Loop(clip, startUs, fps, gainNode)\n this.mp4Loops.set(key, loop)\n this.mp4States.set(key, {\n loop,\n startUs,\n startCtxTime: this.ctx.currentTime,\n fps,\n })\n }\n\n public destroy() {\n this.stopAll()\n for (const entry of this.clips.values())\n entry.clip.destroy()\n this.clips.clear()\n }\n\n private stopAll() {\n for (const loop of this.audioLoops.values()) {\n loop.stop()\n // Stop all audio buffer sources immediately\n for (const source of loop.sources) {\n try {\n source.stop(0)\n source.disconnect()\n }\n catch (e) {\n // Source may already be stopped or disconnected\n }\n }\n loop.sources.length = 0\n }\n for (const loop of this.mp4Loops.values()) {\n loop.stop()\n // Stop all audio buffer sources immediately\n for (const source of loop.sources) {\n try {\n source.stop(0)\n source.disconnect()\n }\n catch (e) {\n // Source may already be stopped or disconnected\n }\n }\n loop.sources.length = 0\n }\n // Disconnect all gain nodes to completely silence audio\n for (const gain of this.audioGains.values()) {\n try {\n gain.disconnect()\n }\n catch (e) {\n // Gain may already be disconnected\n }\n }\n for (const gain of this.mp4Gains.values()) {\n try {\n gain.disconnect()\n }\n catch (e) {\n // Gain may already be disconnected\n }\n }\n this.audioLoops.clear()\n this.audioLoopContexts.clear()\n this.audioGains.clear()\n this.mp4Loops.clear()\n this.mp4States.clear()\n this.mp4Gains.clear()\n }\n\n private async ensureAudioLoop(segment: IAudioSegment, currentTime: number) {\n const key = this.audioKey(segment.id)\n const entry = await this.loadClip(segment)\n if (!entry)\n return\n const offset = segment.fromTime ?? 0\n const playRate = Math.max(0.1, Math.min(100, segment.playRate ?? 1))\n // relativeMs is position within segment timeline (not source audio)\n const relativeMs = currentTime - segment.startTime\n // sourceOffsetMs is where we are in the source audio (accounting for fromTime and playRate)\n const sourceOffsetMs = offset + relativeMs * playRate\n const gainNode = this.getOrCreateGain(this.audioGains, key, segment.volume)\n\n // Apply fade in/out to gain\n this.applyFadeToGain(segment, relativeMs, gainNode)\n\n const existingLoop = this.audioLoops.get(key)\n if (existingLoop && !existingLoop.isStopped()) {\n // Loop is still running, just update volume with fade\n return\n }\n // Clean up stopped loop if exists\n if (existingLoop) {\n this.audioLoops.delete(key)\n this.audioLoopContexts.delete(key)\n }\n // Calculate segment duration in microseconds\n const segmentDurationMs = segment.endTime - segment.startTime\n const sourceDurationMs = segmentDurationMs * playRate\n const maxSourceOffsetMs = offset + sourceDurationMs\n\n const loop = this.startAudioLoop(entry.clip, Math.max(0, sourceOffsetMs) * 1000, gainNode, playRate, maxSourceOffsetMs * 1000)\n this.audioLoops.set(key, loop)\n this.audioLoopContexts.set(key, {\n segment,\n startedAt: this.ctx.currentTime,\n segmentRelativeMs: relativeMs,\n })\n }\n\n private applyFadeToGain(segment: IAudioSegment, relativeMs: number, gainNode: GainNode) {\n const baseVolume = Math.max(0, typeof segment.volume === 'number' ? segment.volume : 1)\n const segmentDuration = segment.endTime - segment.startTime\n const fadeInDuration = segment.fadeInDuration ?? 0\n const fadeOutDuration = segment.fadeOutDuration ?? 0\n\n let volumeMultiplier = 1\n\n // Apply fade in\n if (fadeInDuration > 0 && relativeMs < fadeInDuration) {\n volumeMultiplier = Math.max(0, relativeMs / fadeInDuration)\n }\n\n // Apply fade out\n const timeUntilEnd = segmentDuration - relativeMs\n if (fadeOutDuration > 0 && timeUntilEnd < fadeOutDuration) {\n volumeMultiplier = Math.min(volumeMultiplier, Math.max(0, timeUntilEnd / fadeOutDuration))\n }\n\n gainNode.gain.value = baseVolume * volumeMultiplier\n }\n\n private startMp4Loop(clip: MP4Clip, startUs: number, fps: number, gainNode: GainNode): LoopState {\n let stopped = false\n let timeUs = startUs\n let startAt = 0\n let first = true\n const stepUs = Math.round((1000 / Math.max(fps || 30, 1)) * 1000)\n const sampleRate = this.getClipSampleRate(clip)\n const sources: AudioBufferSourceNode[] = []\n\n const timer = window.setInterval(async () => {\n if (stopped)\n return\n const { audio, state } = await clip.tick(Math.round(timeUs))\n timeUs += stepUs\n if (state === 'done')\n return\n if (first) {\n first = false\n return\n }\n const len = audio?.[0]?.length ?? 0\n if (len === 0)\n return\n startAt = this.playFrames(audio as Float32Array[], sampleRate, startAt, gainNode, sources)\n }, Math.round(1000 / Math.max(fps || 30, 1)))\n\n return {\n sources,\n stop: () => {\n stopped = true\n window.clearInterval(timer)\n },\n isStopped: () => stopped,\n }\n }\n\n private startAudioLoop(clip: AudioClip, startUs: number, gainNode: GainNode, playRate: number = 1, maxSourceOffsetUs?: number): LoopState {\n let stopped = false\n let timeUs = startUs\n let startAt = 0\n let initialized = false\n const sampleRate = this.getClipSampleRate(clip)\n const sources: AudioBufferSourceNode[] = []\n // Each iteration fetches 100ms of real-time audio\n // For playRate != 1, we need to fetch more/less source audio\n const realTimeStepUs = 100000\n const sourceStepUs = Math.round(realTimeStepUs * playRate)\n\n const play = async () => {\n if (stopped)\n return\n\n if (!initialized) {\n // First tick: seek to startUs position (discard audio before startUs)\n if (startUs > 0) {\n await clip.tick(startUs)\n }\n initialized = true\n }\n\n timeUs += sourceStepUs\n\n // Check if we've reached the maximum duration for this segment\n if (maxSourceOffsetUs !== undefined && timeUs > maxSourceOffsetUs) {\n stopped = true\n return\n }\n\n const { audio, state } = await clip.tick(timeUs)\n\n // Check stopped flag immediately after async operation\n if (stopped)\n return\n\n // Stop when audio file ends (don't loop)\n if (state === 'done') {\n stopped = true\n return\n }\n const len = audio?.[0]?.length ?? 0\n if (len === 0) {\n // No audio data - either at silence or past end of file\n // Stop playing to avoid infinite loop\n stopped = true\n return\n }\n // Adjust playback rate by resampling if playRate !== 1\n const processedAudio = playRate !== 1\n ? this.resampleForPlayRate(audio as Float32Array[], playRate)\n : audio as Float32Array[]\n startAt = this.playFrames(processedAudio, sampleRate, startAt, gainNode, sources)\n\n // Check stopped before recursing\n if (!stopped) {\n // Use setTimeout to avoid deep call stack and allow stop to interrupt\n setTimeout(() => play(), 0)\n }\n }\n\n play()\n return {\n sources,\n stop: () => {\n stopped = true\n },\n isStopped: () => stopped,\n }\n }\n\n private resampleForPlayRate(audio: Float32Array[], playRate: number): Float32Array[] {\n // For playRate > 1, we have more source samples than needed (speed up)\n // For playRate < 1, we have fewer source samples than needed (slow down)\n // We need to stretch/compress the audio to match real-time playback\n const outputLength = Math.round(audio[0].length / playRate)\n if (outputLength <= 0)\n return audio\n\n return audio.map((channel) => {\n const output = new Float32Array(outputLength)\n for (let i = 0; i < outputLength; i++) {\n const srcIndex = i * playRate\n const srcIndexFloor = Math.floor(srcIndex)\n const srcIndexCeil = Math.min(srcIndexFloor + 1, channel.length - 1)\n const t = srcIndex - srcIndexFloor\n // Linear interpolation between samples\n output[i] = channel[srcIndexFloor] * (1 - t) + channel[srcIndexCeil] * t\n }\n return output\n })\n }\n\n private playFrames(audio: Float32Array[], sampleRate: number, startAt: number, gainNode: GainNode, sources?: AudioBufferSourceNode[]) {\n const channels = Math.max(audio.length, 1)\n const len = audio[0]?.length ?? 0\n if (len === 0)\n return startAt\n const buffer = this.ctx.createBuffer(channels, len, sampleRate)\n for (let i = 0; i < channels; i++) {\n const data = audio[i] ?? new Float32Array(len)\n buffer.copyToChannel(new Float32Array(data), i)\n }\n const source = this.ctx.createBufferSource()\n source.buffer = buffer\n source.connect(gainNode)\n const nextStart = Math.max(this.ctx.currentTime, startAt)\n source.start(nextStart)\n\n // Track the source so it can be stopped later\n if (sources) {\n sources.push(source)\n // Clean up finished sources after they complete\n const duration = buffer.duration * 1000\n setTimeout(() => {\n const index = sources.indexOf(source)\n if (index > -1) {\n sources.splice(index, 1)\n }\n }, duration + 100)\n }\n\n return nextStart + buffer.duration\n }\n\n private getOrCreateGain(map: Map<string, GainNode>, id: string, volume?: number) {\n const existing = map.get(id)\n if (existing) {\n if (typeof volume === 'number')\n existing.gain.value = Math.max(0, volume)\n return existing\n }\n const gainNode = this.ctx.createGain()\n gainNode.gain.value = Math.max(0, typeof volume === 'number' ? volume : 1)\n gainNode.connect(this.ctx.destination)\n map.set(id, gainNode)\n return gainNode\n }\n\n private audioKey(id: string) {\n return `audio:${id}`\n }\n\n private videoKey(id: string) {\n return `video:${id}`\n }\n\n private async loadClip(segment: IAudioSegment): Promise<ClipEntry | undefined> {\n // Check if already loaded\n const cached = this.clips.get(segment.id)\n if (cached)\n return cached\n\n // Check if currently loading\n const loading = this.loadingClips.get(segment.id)\n if (loading)\n return loading\n\n // Start loading and cache the promise immediately\n const loadingPromise = (async () => {\n try {\n const response = await fetch(segment.url)\n if (!response.body) {\n this.loadingClips.delete(segment.id)\n return undefined\n }\n const clip = new WebAVAudioClip(response.body)\n const entry: ClipEntry = { clip, ready: clip.ready }\n await clip.ready\n if (!this.findSegmentInProtocol(segment.id)) {\n clip.destroy()\n this.loadingClips.delete(segment.id)\n return undefined\n }\n // Move from loading to loaded\n this.clips.set(segment.id, entry)\n this.loadingClips.delete(segment.id)\n return entry\n }\n catch (e) {\n console.error(`[AudioManager] Failed to load audio ${segment.url}`, e)\n this.loadingClips.delete(segment.id)\n return undefined\n }\n })()\n\n this.loadingClips.set(segment.id, loadingPromise)\n return loadingPromise\n }\n\n private findSegmentInProtocol(id: string): boolean {\n for (const track of this.protocol.tracks) {\n for (const segment of track.children) {\n if (segment.id === id) return true\n }\n }\n return false\n }\n\n private getSegmentVolume(id: string): number {\n for (const track of this.protocol.tracks) {\n for (const segment of track.children) {\n if (segment.id !== id)\n continue\n const volume = (segment as { volume?: number }).volume\n return typeof volume === 'number' ? volume : 1\n }\n }\n return 1\n }\n\n private getClipSampleRate(clip: AudioClip | MP4Clip): number {\n const meta = (clip as { meta?: Record<string, unknown> }).meta\n if (!meta)\n return 48000\n const audioSampleRate = meta.audioSampleRate\n if (typeof audioSampleRate === 'number' && audioSampleRate > 0)\n return audioSampleRate\n const sampleRate = meta.sampleRate\n if (typeof sampleRate === 'number' && sampleRate > 0)\n return sampleRate\n return 48000\n }\n}\n","import type { ITextBasic } from '@video-editor/shared'\nimport { renderTxt2ImgBitmap } from '@webav/av-cliper'\n\nconst DEFAULT_TEXT_BITMAP_CACHE_LIMIT = 100\nconst textBitmapCache = new Map<string, ImageBitmap>()\nlet textBitmapCacheLimit = DEFAULT_TEXT_BITMAP_CACHE_LIMIT\n\nfunction touchCache(key: string, value: ImageBitmap) {\n textBitmapCache.delete(key)\n textBitmapCache.set(key, value)\n}\n\nfunction trimCache() {\n while (textBitmapCache.size > textBitmapCacheLimit) {\n const [oldestKey, bitmap] = textBitmapCache.entries().next().value as [string, ImageBitmap]\n textBitmapCache.delete(oldestKey)\n bitmap.close?.()\n }\n}\n\nexport function setTextBitmapCacheLimit(limit: number) {\n textBitmapCacheLimit = Math.max(0, Math.floor(limit))\n trimCache()\n}\n\nexport function clearTextBitmapCache() {\n for (const bitmap of textBitmapCache.values())\n bitmap.close?.()\n textBitmapCache.clear()\n}\n\nexport function buildTextContent(texts: ITextBasic[]) {\n return texts.map(item => item.content).filter(Boolean).join('\\n')\n}\n\nexport function buildTextCss(text: ITextBasic) {\n const fontFamily = Array.isArray(text.fontFamily)\n ? text.fontFamily.join(', ')\n : text.fontFamily\n const fontSize = text.fontSize ?? 32\n const fontWeight = text.fontWeight ?? 'normal'\n const fontStyle = text.fontStyle ?? 'normal'\n const fill = text.fill ?? '#ffffff'\n const align = text.align ?? 'left'\n\n const css: string[] = [\n `font-size: ${fontSize}px`,\n `font-weight: ${fontWeight}`,\n `font-style: ${fontStyle}`,\n `color: ${fill}`,\n `text-align: ${align}`,\n 'white-space: pre-wrap',\n ]\n\n if (fontFamily)\n css.push(`font-family: ${fontFamily}`)\n if (typeof text.letterSpacing === 'number')\n css.push(`letter-spacing: ${text.letterSpacing}px`)\n if (typeof text.leading === 'number')\n css.push(`line-height: ${text.leading}px`)\n if (text.background?.color)\n css.push(`background: ${text.background.color}`)\n if (text.stroke?.color && typeof text.stroke.width === 'number')\n css.push(`-webkit-text-stroke: ${text.stroke.width}px ${text.stroke.color}`)\n if (text.underline)\n css.push('text-decoration: underline')\n if (text.dropShadow?.color && typeof text.dropShadow.distance === 'number') {\n const angle = (text.dropShadow.angle ?? 45) * (Math.PI / 180)\n const offsetX = Math.cos(angle) * text.dropShadow.distance\n const offsetY = Math.sin(angle) * text.dropShadow.distance\n const blur = text.dropShadow.blur ?? 0\n css.push(`text-shadow: ${offsetX}px ${offsetY}px ${blur}px ${text.dropShadow.color}`)\n }\n\n return css.join('; ')\n}\n\nexport async function renderTextBitmap(content: string, cssText: string) {\n const key = `${cssText}::${content}`\n const cached = textBitmapCache.get(key)\n if (cached) {\n touchCache(key, cached)\n return cached\n }\n\n const bitmap = await renderTxt2ImgBitmap(content, cssText)\n if (textBitmapCacheLimit > 0) {\n textBitmapCache.set(key, bitmap)\n trimCache()\n }\n return bitmap\n}\n","import type { ITextSegment, IVideoFramesSegment, IVideoProtocol, SegmentUnion } from '@video-editor/shared'\nimport type { ComputedRef, Ref, ShallowRef } from '@vue/reactivity'\nimport type { Application, ApplicationOptions } from 'pixi.js'\nimport type { MaybeRef, PixiDisplayObject } from './types'\nimport { createResourceManager, createValidator, getResourceKey } from '@video-editor/protocol'\nimport {\n computed,\n effectScope,\n isRef,\n ref,\n shallowRef,\n unref,\n watch,\n} from '@vue/reactivity'\nimport { MP4Clip } from '@webav/av-cliper'\nimport { file as opfsFile } from 'opfs-tools'\nimport { Container, Sprite, Texture } from 'pixi.js'\nimport { createApp as create2dApp } from './2d'\nimport { AudioManager } from './audio-manager'\nimport {\n applyDisplayProps,\n clamp,\n cloneProtocol,\n collectActiveSegments,\n collectResourceUrls,\n computeDuration,\n placeholder,\n} from './helpers'\nimport { buildTextContent, buildTextCss, renderTextBitmap } from './text'\n\nconst DEFAULT_RES_DIR = '/video-editor-res'\n\nexport interface RendererOptions {\n protocol: MaybeRef<IVideoProtocol>\n app?: Application\n appOptions?: Partial<ApplicationOptions>\n resourceDir?: string\n autoPlay?: boolean\n freezeOnPause?: boolean\n manualRender?: boolean\n videoSourceMode?: 'auto' | 'mp4clip' | 'element'\n warmUpResources?: boolean\n}\n\nexport interface Renderer {\n app: Application\n layer: Container\n currentTime: Ref<number>\n duration: ComputedRef<number>\n isPlaying: Ref<boolean>\n play: () => void\n pause: () => void\n tick: (deltaMs?: number) => void\n seek: (time: number) => void\n renderAt: (time: number) => Promise<void>\n destroy: () => void\n}\n\ninterface AudioManagerApi {\n sync: (currentTime: number, isPlaying: boolean) => void | Promise<void>\n ensureMp4Audio: (id: string, clip: MP4Clip, startUs: number, fps: number) => void\n destroy: () => void\n}\n\n/**\n * Create a renderer that reacts to protocol updates and drives playback state.\n * - Pass a reactive `protocol` (Ref/readonly/normal object)\n * - Call `play/pause/seek/tick` to drive the timeline\n * - Rendering updates when `protocol` or `currentTime` changes\n */\nexport async function createRenderer(opts: RendererOptions): Promise<Renderer> {\n const validator = createValidator()\n const protocolInput: Ref<IVideoProtocol> | ShallowRef<IVideoProtocol>\n = isRef(opts.protocol) ? opts.protocol : shallowRef(opts.protocol)\n const validatedProtocol: ShallowRef<IVideoProtocol> = shallowRef(\n validator.verify(cloneProtocol(unref(protocolInput))),\n )\n\n const app = opts.app ?? await create2dApp(opts.appOptions)\n const layer = new Container()\n app.stage.addChild(layer)\n\n const resourceManager = createResourceManager({ dir: opts.resourceDir })\n const resourceWarmUp = new Set<string>()\n const displayCache = new Map<string, PixiDisplayObject>()\n const displayLoading = new Map<string, Promise<PixiDisplayObject | undefined>>()\n const mp4ClipUnsupportedKeys = new Set<string>()\n const mp4ClipErrorLoggedKeys = new Set<string>()\n const videoSourceMode = opts.videoSourceMode ?? 'auto'\n type VideoEntry = (\n | {\n kind: 'mp4clip'\n clip: MP4Clip\n canvas: HTMLCanvasElement\n texture: Texture\n sprite: Sprite\n meta?: { width: number, height: number }\n }\n | {\n kind: 'element'\n video: HTMLVideoElement\n canvas: HTMLCanvasElement\n texture: Texture\n sprite: Sprite\n meta?: { width: number, height: number }\n }\n | {\n kind: 'frozen'\n canvas: HTMLCanvasElement\n texture: Texture\n sprite: Sprite\n meta?: { width: number, height: number }\n }\n )\n const videoEntries = new Map<string, VideoEntry>()\n const videoObjectUrls = new Map<HTMLVideoElement, string>()\n\n const currentTime = ref(0)\n const isPlaying = ref(false)\n const duration = computed(() => computeDuration(validatedProtocol.value))\n const audioManager: AudioManagerApi = new AudioManager(validatedProtocol.value) as unknown as AudioManagerApi\n\n let rafId: number | undefined\n let lastTickAt = 0\n let renderGeneration = 0\n\n interface RenderTask {\n app: Application\n layer: Container\n protocol: IVideoProtocol\n at: number\n getDisplay: (segment: SegmentUnion) => Promise<PixiDisplayObject | undefined>\n }\n\n async function renderScene(task: RenderTask) {\n const generation = renderGeneration\n const { protocol, at, layer } = task\n const renderAt = normalizeRenderTime(protocol, at)\n const active = collectActiveSegments(protocol, renderAt)\n const stageWidth = task.app.renderer.width\n const stageHeight = task.app.renderer.height\n\n audioManager.sync(at, isPlaying.value)\n\n const renders: (PixiDisplayObject | undefined)[] = []\n for (const { segment } of active) {\n if (generation !== renderGeneration)\n return\n const display = await task.getDisplay(segment)\n if (generation !== renderGeneration)\n return\n if (!display)\n continue\n if ((display as { destroyed?: boolean }).destroyed)\n continue\n applyDisplayProps(display, segment, stageWidth, stageHeight)\n if (isVideoSegment(segment))\n await updateVideoFrame(segment, renderAt)\n if (generation !== renderGeneration)\n return\n renders.push(display)\n }\n\n if (generation !== renderGeneration)\n return\n layer.removeChildren()\n const cleaned = renders.filter(Boolean) as PixiDisplayObject[]\n if (cleaned.length)\n layer.addChild(...cleaned)\n if (generation !== renderGeneration)\n return\n task.app.render()\n }\n\n const queueRender = createRenderQueue(() => renderScene({\n app,\n layer,\n protocol: validatedProtocol.value,\n at: currentTime.value,\n getDisplay: getDisplayForSegment,\n }))\n\n const scope = effectScope()\n scope.run(() => {\n // Sync external protocol mutations into a verified snapshot the renderer can rely on.\n watch(\n () => unref(protocolInput),\n (protocol) => {\n try {\n validatedProtocol.value = validator.verify(cloneProtocol(protocol))\n }\n catch (err) {\n console.error('[renderer] invalid protocol update', err)\n return\n }\n renderGeneration += 1\n clearDisplays()\n if (opts.warmUpResources !== false)\n warmUpResources(validatedProtocol.value)\n cleanupCache(validatedProtocol.value)\n clampCurrentTime()\n if (!opts.manualRender)\n queueRender()\n },\n { deep: true, immediate: true },\n )\n\n if (!opts.manualRender) {\n // React to time changes.\n watch(currentTime, () => {\n clampCurrentTime()\n queueRender()\n })\n }\n\n // Keep duration/currentTime in sync with protocol updates.\n watch(duration, () => clampCurrentTime())\n })\n\n function clampCurrentTime() {\n const nextDuration = duration.value\n if (nextDuration <= 0)\n currentTime.value = 0\n else if (currentTime.value > nextDuration)\n currentTime.value = nextDuration\n else if (currentTime.value < 0)\n currentTime.value = 0\n }\n\n function warmUpResources(protocol: IVideoProtocol) {\n for (const url of collectResourceUrls(protocol)) {\n if (resourceWarmUp.has(url))\n continue\n\n resourceWarmUp.add(url)\n if (inferUrlMediaType(url) === 'video')\n continue\n if (!shouldUseResourceManager(url))\n continue\n resourceManager.add(url).catch(() => {\n // noop – render will fall back to Texture.from(url)\n })\n }\n }\n\n function cleanupCache(protocol: IVideoProtocol) {\n const ids = new Set<string>()\n for (const track of protocol.tracks) {\n for (const child of track.children)\n ids.add(child.id)\n }\n for (const [id, display] of displayCache) {\n if (ids.has(id))\n continue\n display.destroy()\n displayCache.delete(id)\n }\n for (const [id, entry] of videoEntries) {\n if (ids.has(id))\n continue\n destroyVideoEntry(entry)\n videoEntries.delete(id)\n }\n }\n\n function clearDisplays() {\n layer.removeChildren()\n for (const display of displayCache.values()) {\n display.destroy()\n }\n displayCache.clear()\n displayLoading.clear()\n for (const entry of videoEntries.values())\n destroyVideoEntry(entry)\n videoEntries.clear()\n }\n\n function play() {\n if (isPlaying.value)\n return\n isPlaying.value = true\n lastTickAt = performance.now()\n rafId = requestAnimationFrame(loop)\n }\n\n function pause() {\n isPlaying.value = false\n if (rafId !== undefined)\n cancelAnimationFrame(rafId)\n rafId = undefined\n // Stop audio immediately when pausing\n audioManager.sync(currentTime.value, false)\n if (opts.freezeOnPause !== false)\n freezeVideoEntries()\n }\n\n function loop() {\n tick()\n if (isPlaying.value)\n rafId = requestAnimationFrame(loop)\n }\n\n function tick(deltaMs?: number) {\n if (!isPlaying.value && deltaMs === undefined)\n return\n\n const now = performance.now()\n const delta = deltaMs ?? (lastTickAt ? now - lastTickAt : 0)\n lastTickAt = now\n\n if (delta === 0)\n return\n\n currentTime.value = clamp(\n currentTime.value + delta,\n 0,\n duration.value || Number.POSITIVE_INFINITY,\n )\n\n if (duration.value > 0 && currentTime.value >= duration.value)\n pause()\n\n // render happens via watch on currentTime\n }\n\n function seek(time: number) {\n currentTime.value = clamp(time, 0, duration.value || Number.POSITIVE_INFINITY)\n }\n\n async function renderAt(time: number) {\n currentTime.value = clamp(time, 0, duration.value || Number.POSITIVE_INFINITY)\n await queueRender()\n }\n\n async function getDisplayForSegment(segment: SegmentUnion) {\n const cached = displayCache.get(segment.id)\n if (cached)\n return cached\n\n const loading = displayLoading.get(segment.id)\n if (loading)\n return loading\n\n const promise = loadDisplay(segment)\n displayLoading.set(segment.id, promise)\n\n const display = await promise\n if (display)\n displayCache.set(segment.id, display)\n\n displayLoading.delete(segment.id)\n return display\n }\n\n async function loadDisplay(segment: SegmentUnion): Promise<PixiDisplayObject | undefined> {\n // prioritize static resources via protocol resource manager\n if (segment.segmentType === 'frames' || segment.segmentType === 'sticker') {\n if (!segment.url)\n return placeholder(segment.segmentType)\n\n if ('type' in segment && segment.type === 'video') {\n if (isRenderableVideoUrl(segment.url)) {\n const sprite = await loadVideoSprite(segment)\n if (sprite)\n return sprite\n return placeholder(segment.segmentType, segment.url)\n }\n }\n\n const texture = await loadTexture(segment.url)\n if (texture)\n return new Sprite(texture)\n return placeholder(segment.segmentType, segment.url)\n }\n\n if (segment.segmentType === 'text')\n return await buildTextDisplay(segment)\n\n if (segment.segmentType === 'effect' || segment.segmentType === 'filter')\n return undefined\n\n // audio segments do not render visuals\n return undefined\n }\n\n async function buildTextDisplay(segment: ITextSegment): Promise<PixiDisplayObject | undefined> {\n const content = buildTextContent(segment.texts)\n if (!content)\n return undefined\n\n const [text] = segment.texts\n if (!text)\n return undefined\n\n const bitmap = await renderTextBitmap(content, buildTextCss(text))\n const texture = Texture.from(bitmap)\n return new Sprite(texture)\n }\n\n async function loadTexture(url: string) {\n const isDataUrl = url.startsWith('data:')\n const isHttp = /^https?:\\/\\//.test(url)\n\n if (!isDataUrl && !isHttp) {\n try {\n await resourceManager.add(url)\n const res = await resourceManager.get(url)\n if (res instanceof HTMLImageElement)\n return Texture.from(res)\n }\n catch {\n // fall through to direct image load\n }\n }\n\n // load image directly to avoid invalid path issues with http/data URLs\n return await loadImageTexture(url)\n }\n\n async function loadVideoSprite(segment: SegmentUnion & { type: 'video', url: string }): Promise<Sprite | undefined> {\n const existing = videoEntries.get(segment.id)\n if (existing)\n return existing.sprite\n\n const urlKey = getResourceKey(segment.url)\n const allowMp4Clip = videoSourceMode !== 'element'\n const allowVideoElement = videoSourceMode !== 'mp4clip'\n if (urlKey && mp4ClipUnsupportedKeys.has(urlKey)) {\n if (!allowVideoElement)\n throw new Error(`[renderer] MP4Clip unsupported for ${segment.url}`)\n const spriteFromElement = await loadVideoSpriteViaElement(segment.url).catch((err) => {\n console.warn('[renderer] failed to load video via <video>', segment.url, err)\n return undefined\n })\n if (spriteFromElement) {\n videoEntries.set(segment.id, spriteFromElement)\n return spriteFromElement.sprite\n }\n return undefined\n }\n\n if (allowMp4Clip) {\n const spriteFromClip = await loadVideoSpriteViaMP4Clip(segment.url).catch((err) => {\n if (urlKey && isMp4ClipUnsupported(err))\n mp4ClipUnsupportedKeys.add(urlKey)\n if (!urlKey || !mp4ClipErrorLoggedKeys.has(urlKey)) {\n if (urlKey)\n mp4ClipErrorLoggedKeys.add(urlKey)\n console.warn('[renderer] failed to load video via MP4Clip', segment.url, err)\n }\n if (!allowVideoElement)\n throw err\n return undefined\n })\n if (spriteFromClip) {\n console.info('[renderer] video source: mp4clip', segment.url)\n videoEntries.set(segment.id, spriteFromClip)\n return spriteFromClip.sprite\n }\n }\n\n if (allowVideoElement) {\n const spriteFromElement = await loadVideoSpriteViaElement(segment.url).catch((err) => {\n console.warn('[renderer] failed to load video via <video>', segment.url, err)\n return undefined\n })\n if (spriteFromElement) {\n console.info('[renderer] video source: element', segment.url)\n videoEntries.set(segment.id, spriteFromElement)\n return spriteFromElement.sprite\n }\n }\n\n return undefined\n }\n\n function isMp4ClipUnsupported(err: unknown) {\n if (!(err instanceof Error))\n return false\n const msg = err.message || ''\n return msg.includes('stream is done')\n || msg.includes('not emit ready')\n || msg.includes('tick video timeout')\n }\n\n async function updateVideoFrame(segment: IVideoFramesSegment, at: number) {\n const entry = videoEntries.get(segment.id)\n if (!entry)\n return\n\n try {\n const offsetMs = segment.fromTime ?? 0\n const relativeMs = Math.max(0, at - segment.startTime + offsetMs)\n const relativeUs = Math.floor(relativeMs * 1000)\n if (entry.kind === 'frozen') {\n const urlKey = getResourceKey(segment.url)\n if (!urlKey)\n return\n const revived = await loadVideoEntry(segment.url, urlKey, { sprite: entry.sprite, oldTexture: entry.texture })\n if (!revived)\n return\n videoEntries.set(segment.id, revived)\n return await updateVideoFrame(segment, at)\n }\n if (entry.kind === 'mp4clip') {\n try {\n const res = await entry.clip.tick(relativeUs)\n if (res.video) {\n const ctx = entry.canvas.getContext('2d')\n if (ctx) {\n ctx.drawImage(res.video, 0, 0, entry.canvas.width, entry.canvas.height)\n refreshCanvasTexture(entry.texture)\n }\n res.video.close()\n }\n // Play audio directly from tick result (avoid calling tick twice)\n if (isPlaying.value && res.audio && res.audio.length > 0) {\n const sampleRate = (entry.clip as { meta?: { audioSampleRate?: number } }).meta?.audioSampleRate ?? 48000;\n (audioManager as unknown as { playMp4AudioFrames: (id: string, audio: Float32Array[], sampleRate: number) => void })\n .playMp4AudioFrames(segment.id, res.audio as Float32Array[], sampleRate)\n }\n return\n }\n catch (err) {\n const urlKey = getResourceKey(segment.url)\n if (urlKey && isMp4ClipUnsupported(err)) {\n mp4ClipUnsupportedKeys.add(urlKey)\n entry.clip.destroy()\n if (videoSourceMode !== 'mp4clip') {\n const replacement = await loadVideoSpriteViaElement(segment.url, { sprite: entry.sprite, oldTexture: entry.texture }).catch((elementErr) => {\n console.warn('[renderer] failed to fallback to <video> after MP4Clip error', segment.url, elementErr)\n return undefined\n })\n if (replacement) {\n videoEntries.set(segment.id, replacement)\n return await updateVideoFrame(segment, at)\n }\n }\n }\n if (urlKey && !mp4ClipErrorLoggedKeys.has(urlKey)) {\n mp4ClipErrorLoggedKeys.add(urlKey)\n console.warn('[renderer] MP4Clip tick failed', segment.url, err)\n }\n return\n }\n }\n\n const relativeSec = relativeMs / 1000\n if (!Number.isFinite(relativeSec))\n return\n if (entry.kind !== 'element')\n return\n await updateVideoElementFrame(entry, {\n targetSec: relativeSec,\n playbackRate: segment.playRate ?? 1,\n volume: segment.volume ?? 1,\n })\n }\n catch (err) {\n console.warn('[renderer] update video frame failed', err)\n }\n }\n\n async function loadVideoEntry(url: string, urlKey: string, reuse: { sprite: Sprite, oldTexture?: Texture }) {\n const allowMp4Clip = videoSourceMode !== 'element'\n const allowVideoElement = videoSourceMode !== 'mp4clip'\n if (mp4ClipUnsupportedKeys.has(urlKey)) {\n if (!allowVideoElement)\n throw new Error(`[renderer] MP4Clip unsupported for ${url}`)\n return await loadVideoSpriteViaElement(url, reuse).catch(() => undefined)\n }\n\n if (allowMp4Clip) {\n const fromClip = await loadVideoSpriteViaMP4Clip(url, reuse).catch((err) => {\n if (isMp4ClipUnsupported(err))\n mp4ClipUnsupportedKeys.add(urlKey)\n if (!allowVideoElement)\n throw err\n return undefined\n })\n if (fromClip)\n return fromClip\n }\n\n if (allowVideoElement)\n return await loadVideoSpriteViaElement(url, reuse).catch(() => undefined)\n\n return undefined\n }\n\n function isVideoSegment(segment: SegmentUnion): segment is IVideoFramesSegment {\n return segment.segmentType === 'frames'\n && segment.type === 'video'\n && typeof segment.url === 'string'\n && isRenderableVideoUrl(segment.url)\n }\n\n\n function normalizeRenderTime(protocol: IVideoProtocol, at: number) {\n const total = computeDuration(protocol)\n if (total <= 0)\n return 0\n if (at < total)\n return at\n // Keep the last visible frame when playback reaches the end.\n const frameWindow = Math.max(1000 / Math.max(protocol.fps || 30, 1), 1)\n return Math.max(total - frameWindow, 0)\n }\n\n async function getOpfsFile(url: string) {\n const dir = opts.resourceDir ?? DEFAULT_RES_DIR\n try {\n const key = getResourceKey(url)\n if (!key)\n return undefined\n const file = opfsFile(`${dir}/${key}`, 'r')\n if (await file.exists())\n return file\n }\n catch {\n return undefined\n }\n return undefined\n }\n\n function shouldUseResourceManager(url: string) {\n if (!url)\n return false\n if (url.startsWith('data:') || url.startsWith('blob:'))\n return false\n return true\n }\n\n function freezeVideoEntries() {\n for (const [id, entry] of videoEntries) {\n if (entry.kind === 'mp4clip') {\n entry.clip.destroy()\n videoEntries.set(id, {\n kind: 'frozen',\n canvas: entry.canvas,\n texture: entry.texture,\n sprite: entry.sprite,\n meta: entry.meta,\n })\n continue\n }\n\n if (entry.kind === 'element')\n entry.video.pause()\n }\n }\n\n function destroyVideoEntry(entry: VideoEntry) {\n if (entry.kind === 'mp4clip') {\n entry.clip.destroy()\n return\n }\n\n if (entry.kind === 'frozen')\n return\n\n entry.video.pause()\n const objectUrl = videoObjectUrls.get(entry.video)\n if (objectUrl) {\n URL.revokeObjectURL(objectUrl)\n videoObjectUrls.delete(entry.video)\n }\n entry.video.removeAttribute('src')\n entry.video.load()\n }\n\n function waitForMediaEvent(target: HTMLMediaElement, type: string, timeoutMs = 1000) {\n return new Promise<void>((resolve, reject) => {\n const timer = window.setTimeout(() => {\n cleanup()\n reject(new Error(`Timed out waiting for media event: ${type}`))\n }, timeoutMs)\n\n const onOk = () => {\n cleanup()\n resolve()\n }\n const onErr = () => {\n cleanup()\n const mediaError = target.error ? `${target.error.code}` : 'unknown'\n reject(new Error(`Media error (${mediaError}) while waiting for ${type}`))\n }\n const cleanup = () => {\n window.clearTimeout(timer)\n target.removeEventListener(type, onOk)\n target.removeEventListener('error', onErr)\n }\n\n target.addEventListener(type, onOk, { once: true })\n target.addEventListener('error', onErr, { once: true })\n })\n }\n\n async function loadVideoSpriteViaMP4Clip(url: string, reuse?: { sprite: Sprite, oldTexture?: Texture }): Promise<VideoEntry | undefined> {\n let file: ReturnType<typeof opfsFile> | undefined\n if (shouldUseResourceManager(url)) {\n file = await getOpfsFile(url)\n if (!file) {\n await resourceManager.add(url).catch(() => {})\n file = await getOpfsFile(url)\n }\n }\n\n let clip: MP4Clip | undefined\n try {\n if (file) {\n clip = new MP4Clip(file)\n }\n else {\n const res = await fetch(url)\n if (!res.body) {\n const buffer = await res.arrayBuffer()\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(new Uint8Array(buffer))\n controller.close()\n },\n })\n clip = new MP4Clip(stream)\n }\n else {\n clip = new MP4Clip(res.body)\n }\n }\n\n await clip.ready\n\n const { width, height } = clip.meta\n const canvas = document.createElement('canvas')\n canvas.width = width || 1\n canvas.height = height || 1\n const texture = Texture.from(canvas)\n const sprite = reuse?.sprite ?? new Sprite(texture)\n if (reuse?.sprite) {\n reuse.sprite.texture = texture\n reuse.oldTexture?.destroy(true)\n }\n\n return { kind: 'mp4clip', clip, canvas, texture, sprite, meta: { width, height } }\n }\n catch (err) {\n clip?.destroy()\n throw err\n }\n }\n\n function inferUrlMediaType(url: string): 'video' | 'image' | 'audio' | 'unknown' {\n const raw = url.split('#')[0]!.split('?')[0]!\n const ext = raw.split('/').pop()?.split('.').pop()?.toLowerCase() ?? ''\n if (['mp4', 'm4v', 'mov', 'webm'].includes(ext))\n return 'video'\n if (['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg', 'avif'].includes(ext))\n return 'image'\n if (['mp3', 'wav', 'aac', 'm4a', 'ogg', 'flac'].includes(ext))\n return 'audio'\n return 'unknown'\n }\n\n function isRenderableVideoUrl(url: string) {\n const kind = inferUrlMediaType(url)\n if (kind === 'image' || kind === 'audio')\n return false\n // Treat unknown as video to support blob URLs or extension-less endpoints.\n return true\n }\n\n async function loadVideoSpriteViaElement(url: string, reuse?: { sprite: Sprite, oldTexture?: Texture }): Promise<VideoEntry | undefined> {\n const video = document.createElement('video')\n video.crossOrigin = 'anonymous'\n video.muted = false\n video.playsInline = true\n video.preload = 'metadata'\n video.src = url\n video.load()\n\n try {\n await waitForMediaEvent(video, 'loadedmetadata', 15000)\n }\n catch (err) {\n video.pause()\n const objectUrl = videoObjectUrls.get(video)\n if (objectUrl) {\n URL.revokeObjectURL(objectUrl)\n videoObjectUrls.delete(video)\n }\n video.removeAttribute('src')\n video.load()\n throw err\n }\n\n const width = video.videoWidth || 1\n const height = video.videoHeight || 1\n\n const canvas = document.createElement('canvas')\n canvas.width = width\n canvas.height = height\n const texture = Texture.from(canvas)\n const sprite = reuse?.sprite ?? new Sprite(texture)\n if (reuse?.sprite) {\n reuse.sprite.texture = texture\n reuse.oldTexture?.destroy(true)\n }\n\n return { kind: 'element', video, canvas, texture, sprite, meta: { width, height } }\n }\n\n\n async function updateVideoElementFrame(entry: Extract<VideoEntry, { kind: 'element' }>, opts: { targetSec: number, playbackRate: number, volume?: number }) {\n const { video, canvas, texture } = entry\n\n video.playbackRate = Number.isFinite(opts.playbackRate) && opts.playbackRate > 0 ? opts.playbackRate : 1\n video.volume = Math.max(0, Math.min(1, opts.volume ?? 1))\n\n if (isPlaying.value)\n video.play().catch(() => {})\n else\n video.pause()\n\n const duration = Number.isFinite(video.duration) && video.duration > 0 ? video.duration : null\n const targetSec = duration ? Math.min(opts.targetSec, Math.max(duration - 0.03, 0)) : opts.targetSec\n\n const current = video.currentTime\n const drift = Math.abs(current - targetSec)\n const driftThreshold = isPlaying.value ? 0.25 : 0.03\n if (Number.isFinite(current) && drift > driftThreshold) {\n try {\n video.currentTime = targetSec\n }\n catch {\n // ignore seek errors for not-yet-ready media\n }\n await waitForMediaEvent(video, 'seeked', 250).catch(() => {})\n }\n\n if (video.readyState < 2) {\n // Avoid blocking the render queue for too long.\n await waitForMediaEvent(video, 'canplay', 250).catch(() => {})\n if (video.readyState < 2)\n return\n }\n\n const ctx = canvas.getContext('2d')\n if (!ctx)\n return\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height)\n refreshCanvasTexture(texture)\n }\n\n function loadImageTexture(url: string): Promise<Texture | undefined> {\n return new Promise((resolve) => {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.onload = () => resolve(Texture.from(img))\n img.onerror = () => {\n console.warn('[renderer] failed to load image', url)\n resolve(undefined)\n }\n img.src = url\n })\n }\n\n function refreshCanvasTexture(texture: Texture) {\n const source = texture.source\n if ('update' in source && typeof source.update === 'function') {\n source.update()\n return\n }\n\n if (typeof texture.update === 'function')\n texture.update()\n }\n\n function destroy() {\n pause()\n renderGeneration += 1\n scope.stop()\n clearDisplays()\n layer.destroy({ children: true })\n displayCache.clear()\n displayLoading.clear()\n resourceWarmUp.clear()\n if (!opts.app)\n app.destroy()\n \n audioManager.destroy()\n }\n\n if (opts.autoPlay)\n play()\n\n return {\n app,\n layer,\n currentTime,\n duration,\n isPlaying,\n play,\n pause,\n tick,\n seek,\n renderAt,\n destroy,\n }\n}\n\nfunction createRenderQueue(job: () => Promise<void> | void) {\n let queued = false\n let running = false\n let pending: Promise<void> | null = null\n let resolvePending: (() => void) | null = null\n\n const run = async () => {\n if (!pending) {\n pending = new Promise((resolve) => {\n resolvePending = resolve\n })\n }\n const done = pending\n if (running) {\n queued = true\n return done\n }\n running = true\n do {\n queued = false\n await job()\n } while (queued)\n running = false\n resolvePending?.()\n pending = null\n resolvePending = null\n return done\n }\n\n return run\n}\n","import type { file as opfsFile } from 'opfs-tools'\nimport type { ICombinatorOpts } from '@webav/av-cliper'\nimport { Combinator, MP4Clip, OffscreenSprite } from '@webav/av-cliper'\n\nexport type VideoConcatSource =\n | string\n | ReadableStream<Uint8Array>\n | Blob\n | ReturnType<typeof opfsFile>\n\nexport interface ConcatVideoSource {\n source: VideoConcatSource\n}\n\nexport interface ConcatVideoOptions extends Omit<ICombinatorOpts, 'width' | 'height'> {\n width?: number\n height?: number\n onProgress?: (progress: number) => void\n}\n\nexport interface ConcatVideoResult {\n stream: ReadableStream<Uint8Array>\n width: number\n height: number\n durationMs: number\n destroy: () => void\n}\n\nfunction isOpfsFile(value: unknown): value is ReturnType<typeof opfsFile> {\n return typeof value === 'object'\n && value !== null\n && 'createReader' in value\n && 'getSize' in value\n}\n\nfunction isReadableStream(value: unknown): value is ReadableStream<Uint8Array> {\n return typeof ReadableStream !== 'undefined' && value instanceof ReadableStream\n}\n\nfunction normalizeInput(input: ConcatVideoSource | VideoConcatSource): ConcatVideoSource {\n if (typeof input === 'string' || input instanceof Blob || isOpfsFile(input) || isReadableStream(input))\n return { source: input }\n return input\n}\n\nasync function toClipSource(source: VideoConcatSource): Promise<ReadableStream<Uint8Array> | ReturnType<typeof opfsFile>> {\n if (typeof source === 'string') {\n const res = await fetch(source)\n if (!res.body)\n throw new Error('concatVideos: unable to read video stream from url')\n return res.body\n }\n\n if (source instanceof Blob)\n return source.stream() as ReadableStream<Uint8Array>\n\n return source\n}\n\nfunction wrapStreamWithCleanup(\n stream: ReadableStream<Uint8Array>,\n cleanup: () => void,\n): ReadableStream<Uint8Array> {\n let cleaned = false\n const finalize = () => {\n if (cleaned)\n return\n cleaned = true\n cleanup()\n }\n\n const reader = stream.getReader()\n return new ReadableStream({\n async pull(controller) {\n const { done, value } = await reader.read()\n if (done) {\n finalize()\n controller.close()\n return\n }\n controller.enqueue(value)\n },\n async cancel(reason) {\n try {\n await reader.cancel(reason)\n }\n finally {\n finalize()\n }\n },\n })\n}\n\nexport async function concatVideos(\n inputs: Array<ConcatVideoSource | VideoConcatSource>,\n opts: ConcatVideoOptions = {},\n): Promise<ConcatVideoResult> {\n if (inputs.length === 0)\n throw new Error('concatVideos: expected at least one source')\n\n const {\n onProgress,\n width: requestedWidth,\n height: requestedHeight,\n ...combinatorOpts\n } = opts\n\n const normalized = inputs.map(normalizeInput)\n\n const [first, ...rest] = normalized\n const firstSource = await toClipSource(first.source)\n const firstClip = new MP4Clip(firstSource)\n await firstClip.ready\n\n const width = requestedWidth ?? Math.round(firstClip.meta.width || 0)\n const height = requestedHeight ?? Math.round(firstClip.meta.height || 0)\n if (!width || !height)\n {\n firstClip.destroy()\n throw new Error('concatVideos: output width/height is required')\n }\n\n const combinator = new Combinator({\n ...combinatorOpts,\n width,\n height,\n })\n\n if (onProgress)\n combinator.on('OutputProgress', onProgress)\n\n let offset = 0\n\n const addClip = async (clip: MP4Clip) => {\n const duration = clip.meta.duration\n if (!Number.isFinite(duration) || duration <= 0) {\n clip.destroy()\n throw new Error('concatVideos: invalid clip duration')\n }\n\n const sprite = new OffscreenSprite(clip)\n try {\n await sprite.ready\n sprite.rect.x = 0\n sprite.rect.y = 0\n sprite.rect.w = width\n sprite.rect.h = height\n sprite.time.offset = offset\n sprite.time.duration = duration\n\n await combinator.addSprite(sprite)\n offset += duration\n }\n finally {\n sprite.destroy()\n }\n }\n\n try {\n await addClip(firstClip)\n for (const entry of rest) {\n const source = await toClipSource(entry.source)\n const clip = new MP4Clip(source)\n await clip.ready\n await addClip(clip)\n }\n }\n catch (err) {\n combinator.destroy()\n throw err\n }\n\n const maxTime = offset\n const stream = combinator.output({ maxTime })\n const destroy = () => {\n combinator.destroy()\n }\n\n return {\n stream: wrapStreamWithCleanup(stream, destroy),\n width,\n height,\n durationMs: Math.round(maxTime / 1000),\n destroy,\n }\n}\n","import type { IVideoProtocol } from '@video-editor/shared'\nimport type { IClip } from '@webav/av-cliper'\nimport type { ApplicationOptions } from 'pixi.js'\nimport { Application } from 'pixi.js'\nimport type { RendererOptions } from './renderer-core'\nimport { createRenderer } from './renderer-core'\nimport { computeDuration } from './helpers'\n\nexport interface ProtocolVideoClipOptions {\n width?: number\n height?: number\n fps?: number\n appOptions?: Partial<ApplicationOptions>\n rendererOptions?: Partial<Omit<RendererOptions, 'protocol' | 'app' | 'appOptions'>>\n}\n\ninterface ClipMeta {\n width: number\n height: number\n duration: number\n}\n\nexport class ProtocolVideoClip implements IClip {\n readonly ready: Promise<ClipMeta>\n meta: ClipMeta\n\n private readonly protocol: IVideoProtocol\n private readonly options: ProtocolVideoClipOptions\n private renderer?: Awaited<ReturnType<typeof createRenderer>>\n private app?: Application\n private destroyed = false\n\n constructor(protocol: IVideoProtocol, options: ProtocolVideoClipOptions = {}) {\n this.protocol = protocol\n this.options = options\n\n const width = options.width ?? protocol.width\n const height = options.height ?? protocol.height\n const durationMs = computeDuration(protocol)\n this.meta = {\n width,\n height,\n duration: Math.max(0, Math.round(durationMs * 1000)),\n }\n\n this.ready = this.init()\n }\n\n private async init() {\n const width = this.options.width ?? this.protocol.width\n const height = this.options.height ?? this.protocol.height\n if (!width || !height)\n throw new Error('ProtocolVideoClip: output width/height is required')\n\n const app = new Application()\n await app.init({\n width,\n height,\n backgroundAlpha: 0,\n ...this.options.appOptions,\n })\n app.ticker.stop()\n this.app = app\n\n const rendererOptions = this.options.rendererOptions ?? {}\n const renderer = await createRenderer({\n protocol: this.protocol,\n app,\n ...rendererOptions,\n autoPlay: false,\n freezeOnPause: false,\n manualRender: true,\n videoSourceMode: rendererOptions.videoSourceMode ?? 'mp4clip',\n })\n this.renderer = renderer\n\n const durationMs = renderer.duration.value\n this.meta = {\n width: app.renderer.width,\n height: app.renderer.height,\n duration: Math.max(0, Math.round(durationMs * 1000)),\n }\n\n return this.meta\n }\n\n async tick(time: number): Promise<{\n video?: VideoFrame | ImageBitmap | null\n audio?: Float32Array[]\n state: 'done' | 'success'\n }> {\n const emptyAudio: Float32Array[] = []\n if (this.destroyed)\n return { audio: emptyAudio, state: 'done' }\n\n await this.ready\n if (!this.renderer)\n return { audio: emptyAudio, state: 'done' }\n\n const durationUs = this.meta.duration\n if (time >= durationUs)\n return { audio: emptyAudio, state: 'done' }\n\n const clampedUs = Math.max(0, Math.min(time, durationUs))\n await this.renderer.renderAt(clampedUs / 1000)\n\n const frame = new VideoFrame(this.renderer.app.canvas, {\n timestamp: time,\n })\n\n return {\n video: frame,\n audio: emptyAudio,\n state: 'success',\n }\n }\n\n async clone(): Promise<this> {\n const copy = new ProtocolVideoClip(this.protocol, this.options) as this\n await copy.ready\n return copy\n }\n\n destroy() {\n if (this.destroyed)\n return\n this.destroyed = true\n this.renderer?.destroy()\n this.app?.destroy(true)\n }\n}\n","import type { IVideoProtocol } from '@video-editor/shared'\nimport type { ICombinatorOpts } from '@webav/av-cliper'\nimport { Combinator, OffscreenSprite } from '@webav/av-cliper'\nimport { ProtocolVideoClip } from './protocol-clip'\nimport type { ProtocolVideoClipOptions } from './protocol-clip'\n\nexport interface ComposeProtocolOptions extends Omit<ICombinatorOpts, 'width' | 'height' | 'fps'> {\n width?: number\n height?: number\n fps?: number\n onProgress?: (progress: number) => void\n clipOptions?: ProtocolVideoClipOptions\n audioSprites?: (protocol: IVideoProtocol) => Promise<OffscreenSprite[]>\n}\n\nexport interface ComposeProtocolResult {\n stream: ReadableStream<Uint8Array>\n width: number\n height: number\n durationMs: number\n destroy: () => void\n}\n\nfunction wrapStreamWithCleanup(\n stream: ReadableStream<Uint8Array>,\n cleanup: () => void,\n): ReadableStream<Uint8Array> {\n let cleaned = false\n const finalize = () => {\n if (cleaned)\n return\n cleaned = true\n cleanup()\n }\n\n const reader = stream.getReader()\n return new ReadableStream({\n async pull(controller) {\n const { done, value } = await reader.read()\n if (done) {\n finalize()\n controller.close()\n return\n }\n controller.enqueue(value)\n },\n async cancel(reason) {\n try {\n await reader.cancel(reason)\n }\n finally {\n finalize()\n }\n },\n })\n}\n\nexport async function composeProtocol(\n protocol: IVideoProtocol,\n opts: ComposeProtocolOptions = {},\n): Promise<ComposeProtocolResult> {\n const {\n width: requestedWidth,\n height: requestedHeight,\n fps: requestedFps,\n onProgress,\n clipOptions,\n audioSprites,\n ...combinatorOpts\n } = opts\n\n const width = requestedWidth ?? protocol.width\n const height = requestedHeight ?? protocol.height\n if (!width || !height)\n throw new Error('composeProtocol: output width/height is required')\n\n const fps = requestedFps ?? protocol.fps\n\n const hasAudioSprites = typeof audioSprites === 'function'\n const audio = combinatorOpts.audio ?? (hasAudioSprites ? undefined : false)\n const combinator = new Combinator({\n ...combinatorOpts,\n audio,\n width,\n height,\n fps,\n })\n\n if (onProgress)\n combinator.on('OutputProgress', onProgress)\n\n let clip: ProtocolVideoClip | undefined\n let sprite: OffscreenSprite | undefined\n try {\n clip = new ProtocolVideoClip(protocol, {\n width,\n height,\n fps,\n ...clipOptions,\n rendererOptions: {\n warmUpResources: false,\n ...clipOptions?.rendererOptions,\n },\n })\n await clip.ready\n\n sprite = new OffscreenSprite(clip)\n await sprite.ready\n sprite.time.offset = 0\n sprite.time.duration = clip.meta.duration\n sprite.rect.x = 0\n sprite.rect.y = 0\n sprite.rect.w = clip.meta.width\n sprite.rect.h = clip.meta.height\n\n await combinator.addSprite(sprite, { main: true })\n\n if (audioSprites) {\n const sprites = await audioSprites(protocol)\n for (const extra of sprites)\n await combinator.addSprite(extra)\n }\n }\n catch (err) {\n combinator.destroy()\n throw err\n }\n\n const maxTime = clip?.meta.duration ?? 0\n if (!maxTime)\n throw new Error('composeProtocol: protocol has no duration')\n\n const stream = combinator.output({ maxTime })\n const destroy = () => {\n sprite?.destroy()\n clip?.destroy()\n combinator.destroy()\n }\n\n return {\n stream: wrapStreamWithCleanup(stream, destroy),\n width,\n height,\n durationMs: Math.round(maxTime / 1000),\n destroy,\n }\n}\n"],"names":["createApp","opts","app","Application","resolveFillSize","mode","sourceWidth","sourceHeight","stageWidth","stageHeight","safeSourceWidth","safeSourceHeight","sourceRatio","stageRatio","computeSegmentLayout","segment","fillMode","width","height","transform","px","py","sx","sy","rotation","finalWidth","finalHeight","centerX","centerY","collectResourceUrls","protocol","urls","track","collectActiveSegments","at","active","trackIndex","childIndex","a","b","aTrack","bTrack","aIsMain","bIsMain","total","aOrder","bOrder","applyDisplayProps","display","opacity","readOpacity","Sprite","layout","Texture","placeholderTexture","Graphics","stringToColor","hasOpacity","placeholder","key","url","g","color","svg","hash","computeDuration","endTimes","seg","clamp","num","min","max","cloneProtocol","raw","toRaw","AudioManager","currentTime","isPlaying","activeSegments","activeAudioKeys","activeVideoKeys","loop","source","id","audio","sampleRate","volume","gainNode","state","clip","startUs","fps","existing","elapsedUs","expectedUs","entry","gain","offset","playRate","relativeMs","sourceOffsetMs","existingLoop","sourceDurationMs","maxSourceOffsetMs","baseVolume","segmentDuration","fadeInDuration","fadeOutDuration","volumeMultiplier","timeUntilEnd","stopped","timeUs","startAt","first","stepUs","sources","timer","maxSourceOffsetUs","initialized","sourceStepUs","play","processedAudio","outputLength","channel","output","i","srcIndex","srcIndexFloor","srcIndexCeil","t","channels","len","buffer","data","nextStart","duration","index","map","cached","loading","loadingPromise","response","WebAVAudioClip","e","meta","audioSampleRate","DEFAULT_TEXT_BITMAP_CACHE_LIMIT","textBitmapCache","textBitmapCacheLimit","touchCache","value","trimCache","oldestKey","bitmap","buildTextContent","texts","item","buildTextCss","text","fontFamily","fontSize","fontWeight","fontStyle","fill","align","css","angle","offsetX","offsetY","blur","renderTextBitmap","content","cssText","renderTxt2ImgBitmap","DEFAULT_RES_DIR","createRenderer","validator","createValidator","protocolInput","isRef","shallowRef","validatedProtocol","unref","create2dApp","layer","Container","resourceManager","createResourceManager","resourceWarmUp","displayCache","displayLoading","mp4ClipUnsupportedKeys","mp4ClipErrorLoggedKeys","videoSourceMode","videoEntries","videoObjectUrls","ref","computed","audioManager","rafId","lastTickAt","renderGeneration","renderScene","task","generation","renderAt","normalizeRenderTime","renders","isVideoSegment","updateVideoFrame","cleaned","queueRender","createRenderQueue","getDisplayForSegment","scope","effectScope","watch","err","clearDisplays","warmUpResources","cleanupCache","clampCurrentTime","nextDuration","inferUrlMediaType","shouldUseResourceManager","ids","child","destroyVideoEntry","pause","freezeVideoEntries","tick","deltaMs","now","delta","seek","time","promise","loadDisplay","isRenderableVideoUrl","sprite","loadVideoSprite","texture","loadTexture","buildTextDisplay","isDataUrl","isHttp","res","loadImageTexture","urlKey","getResourceKey","allowMp4Clip","allowVideoElement","spriteFromElement","loadVideoSpriteViaElement","spriteFromClip","loadVideoSpriteViaMP4Clip","isMp4ClipUnsupported","msg","offsetMs","relativeUs","revived","loadVideoEntry","ctx","refreshCanvasTexture","replacement","elementErr","relativeSec","updateVideoElementFrame","reuse","fromClip","frameWindow","getOpfsFile","dir","file","opfsFile","objectUrl","waitForMediaEvent","target","type","timeoutMs","resolve","reject","cleanup","onOk","onErr","mediaError","MP4Clip","stream","controller","canvas","ext","kind","video","targetSec","current","drift","driftThreshold","img","destroy","job","queued","running","pending","resolvePending","done","isOpfsFile","isReadableStream","normalizeInput","input","toClipSource","wrapStreamWithCleanup","finalize","reader","reason","concatVideos","inputs","onProgress","requestedWidth","requestedHeight","combinatorOpts","normalized","rest","firstSource","firstClip","combinator","Combinator","addClip","OffscreenSprite","maxTime","ProtocolVideoClip","options","durationMs","rendererOptions","renderer","emptyAudio","durationUs","clampedUs","copy","composeProtocol","requestedFps","clipOptions","audioSprites","hasAudioSprites","sprites","extra"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/2d/index.ts","../src/layout.ts","../src/helpers.ts","../src/audio-manager.ts","../src/text.ts","../src/renderer-core.ts","../src/concat.ts","../src/protocol-clip.ts","../src/compose.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\nimport type { ApplicationOptions } from 'pixi.js'\nimport { Application } from 'pixi.js'\n\ndeclare global {\n\n // eslint-disable-next-line vars-on-top\n var __PIXI_APP__: Application\n}\n\nexport async function createApp(opts?: Partial<ApplicationOptions>) {\n const app = new Application()\n\n await app.init({ resizeTo: window, backgroundAlpha: 0, ...opts })\n\n if (import.meta.env.DEV) {\n globalThis.__PIXI_APP__ = app\n }\n\n return app\n}\n","import type { IFillMode, SegmentUnion } from '@video-editor/shared'\n\nexport interface SegmentLayout {\n width: number\n height: number\n centerX: number\n centerY: number\n rotationRad: number\n}\n\nexport function resolveFillSize(\n mode: IFillMode | undefined,\n sourceWidth: number,\n sourceHeight: number,\n stageWidth: number,\n stageHeight: number,\n) {\n const safeSourceWidth = sourceWidth || stageWidth\n const safeSourceHeight = sourceHeight || stageHeight\n if (!safeSourceWidth || !safeSourceHeight)\n return { width: stageWidth, height: stageHeight }\n\n const sourceRatio = safeSourceWidth / safeSourceHeight\n const stageRatio = stageWidth / stageHeight\n\n switch (mode) {\n case 'none':\n return { width: safeSourceWidth, height: safeSourceHeight }\n case 'cover':\n if (sourceRatio > stageRatio)\n return { width: stageHeight * sourceRatio, height: stageHeight }\n return { width: stageWidth, height: stageWidth / sourceRatio }\n case 'stretch':\n return { width: stageWidth, height: stageHeight }\n case 'contain':\n default:\n if (sourceRatio > stageRatio)\n return { width: stageWidth, height: stageWidth / sourceRatio }\n return { width: stageHeight * sourceRatio, height: stageHeight }\n }\n}\n\nexport function computeSegmentLayout(\n segment: SegmentUnion,\n stageWidth: number,\n stageHeight: number,\n sourceWidth: number,\n sourceHeight: number,\n): SegmentLayout {\n const fillMode = 'fillMode' in segment ? segment.fillMode : undefined\n const { width, height } = resolveFillSize(\n fillMode,\n sourceWidth,\n sourceHeight,\n stageWidth,\n stageHeight,\n )\n\n const transform = 'transform' in segment ? segment.transform : undefined\n const [px, py] = transform?.position ?? [0, 0]\n const [sx, sy] = transform?.scale ?? [1, 1]\n const rotation = transform?.rotation?.[2] ?? 0\n\n const finalWidth = width * sx\n const finalHeight = height * sy\n const centerX = stageWidth / 2 + (px * stageWidth) / 2\n const centerY = stageHeight / 2 - (py * stageHeight) / 2\n\n return {\n width: finalWidth,\n height: finalHeight,\n centerX,\n centerY,\n rotationRad: (rotation / 180) * Math.PI,\n }\n}\n","import type { IVideoProtocol, SegmentUnion } from '@video-editor/shared'\nimport type { TrackTypeMapTrack } from '@video-editor/shared'\nimport type { PixiDisplayObject } from './types'\nimport { toRaw } from '@vue/reactivity'\nimport { Graphics, Sprite, Texture } from 'pixi.js'\nimport { computeSegmentLayout } from './layout'\n\nexport function collectResourceUrls(protocol: IVideoProtocol) {\n const urls = new Set<string>()\n for (const track of protocol.tracks) {\n for (const segment of track.children) {\n if (segment.url)\n urls.add(segment.url)\n }\n }\n return urls\n}\n\nexport function collectActiveSegments(protocol: IVideoProtocol, at: number) {\n const active: { segment: SegmentUnion, trackIndex: number, childIndex: number }[] = []\n protocol.tracks.forEach((track, trackIndex) => {\n track.children.forEach((segment, childIndex) => {\n if (segment.startTime <= at && at < segment.endTime)\n active.push({ segment, trackIndex, childIndex })\n })\n })\n\n return active.sort((a, b) => {\n const aTrack = protocol.tracks[a.trackIndex]\n const bTrack = protocol.tracks[b.trackIndex]\n const aIsMain = aTrack?.trackType === 'frames' && (aTrack as TrackTypeMapTrack['frames']).isMain\n const bIsMain = bTrack?.trackType === 'frames' && (bTrack as TrackTypeMapTrack['frames']).isMain\n const total = protocol.tracks.length\n const aOrder = aIsMain ? 0 : total - a.trackIndex\n const bOrder = bIsMain ? 0 : total - b.trackIndex\n if (aOrder !== bOrder)\n return aOrder - bOrder\n if (a.trackIndex === b.trackIndex)\n return a.childIndex - b.childIndex\n return a.trackIndex - b.trackIndex\n })\n}\n\nexport function applyDisplayProps(display: PixiDisplayObject, segment: SegmentUnion, width: number, height: number) {\n const opacity = readOpacity(segment)\n const sourceWidth = display instanceof Sprite ? display.texture.width || width : width\n const sourceHeight = display instanceof Sprite ? display.texture.height || height : height\n const layout = computeSegmentLayout(segment, width, height, sourceWidth, sourceHeight)\n\n if (display instanceof Sprite) {\n display.anchor.set(0.5)\n display.width = layout.width\n display.height = layout.height\n display.position.set(layout.centerX, layout.centerY)\n display.rotation = layout.rotationRad\n const src = display.texture.source as { addEventListener?: (type: string, cb: () => void, opts?: AddEventListenerOptions) => void } | undefined\n src?.addEventListener?.('error', () => {\n // fallback to a colored rect if texture failed\n display.texture = Texture.from(placeholderTexture(width, height))\n }, { once: true })\n }\n else if (display instanceof Graphics) {\n display.clear()\n display\n .rect(0, 0, layout.width, layout.height)\n .fill({ color: stringToColor('url' in segment && typeof segment.url === 'string' ? segment.url : segment.segmentType), alpha: hasOpacity(segment) ? opacity : 0.35 })\n display.pivot.set(layout.width / 2, layout.height / 2)\n display.position.set(layout.centerX, layout.centerY)\n display.rotation = layout.rotationRad\n }\n\n display.alpha = opacity\n}\n\nexport function placeholder(key: string, url?: string) {\n const g = new Graphics()\n g.rect(0, 0, 10, 10).fill({ color: stringToColor(url ?? key), alpha: 1 })\n return g\n}\n\nexport function placeholderTexture(width: number, height: number, color?: string) {\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\"><rect width=\"100%\" height=\"100%\" fill=\"${color ?? '#0f172a'}\" fill-opacity=\"0.8\"/></svg>`\n return `data:image/svg+xml;base64,${btoa(svg)}`\n}\n\nexport function stringToColor(key: string) {\n let hash = 0\n for (let i = 0; i < key.length; i++)\n hash = key.charCodeAt(i) + ((hash << 5) - hash)\n return hash & 0x00FFFFFF\n}\n\nexport function computeDuration(protocol: IVideoProtocol) {\n const endTimes = protocol.tracks.flatMap(track => track.children.map(seg => seg.endTime))\n return endTimes.length ? Math.max(...endTimes) : 0\n}\n\nexport function clamp(num: number, min: number, max: number) {\n return Math.min(Math.max(num, min), max)\n}\n\nexport function cloneProtocol(protocol: IVideoProtocol) {\n const raw = toRaw(protocol) as IVideoProtocol\n // use JSON clone to avoid structuredClone errors on proxies (e.g., Vue reactive)\n return JSON.parse(JSON.stringify(raw)) as IVideoProtocol\n}\n\nfunction hasOpacity(segment: SegmentUnion): segment is SegmentUnion & { opacity?: number } {\n return 'opacity' in segment\n}\n\nfunction readOpacity(segment: SegmentUnion) {\n if (hasOpacity(segment) && typeof segment.opacity === 'number')\n return segment.opacity\n return 1\n}\n","import type { IAudioSegment, IVideoProtocol } from '@video-editor/shared'\nimport type { MP4Clip, AudioClip } from '@webav/av-cliper'\nimport { collectActiveSegments } from './helpers'\nimport { AudioClip as WebAVAudioClip } from '@webav/av-cliper'\n\ninterface ClipEntry {\n clip: AudioClip\n ready: Promise<unknown>\n}\n\ninterface LoopState {\n stop: () => void\n sources: AudioBufferSourceNode[]\n isStopped: () => boolean\n}\n\ninterface AudioLoopContext {\n segment: IAudioSegment\n startedAt: number // ctx.currentTime when loop started\n segmentRelativeMs: number // position in segment when loop started\n}\n\ninterface Mp4State {\n loop: LoopState\n startUs: number\n startCtxTime: number\n fps: number\n nextStartAt?: number\n}\n\nexport class AudioManager {\n private protocol: IVideoProtocol\n private clips = new Map<string, ClipEntry>()\n private loadingClips = new Map<string, Promise<ClipEntry | undefined>>()\n private audioLoops = new Map<string, LoopState>()\n private audioLoopContexts = new Map<string, AudioLoopContext>()\n private mp4Loops = new Map<string, LoopState>()\n private mp4States = new Map<string, Mp4State>()\n private audioGains = new Map<string, GainNode>()\n private mp4Gains = new Map<string, GainNode>()\n private ctx: AudioContext\n private lastTime = 0\n\n constructor(protocol: IVideoProtocol) {\n this.protocol = protocol\n this.ctx = new (window.AudioContext || (window as any).webkitAudioContext)()\n }\n\n public setProtocol(protocol: IVideoProtocol) {\n this.protocol = protocol\n }\n\n public async sync(currentTime: number, isPlaying: boolean) {\n if (!isPlaying) {\n // Always stop all audio when paused\n this.stopAll()\n this.lastTime = currentTime\n return\n }\n\n if (this.ctx.state === 'suspended')\n await this.ctx.resume().catch(() => {})\n\n if (Math.abs(currentTime - this.lastTime) > 1000)\n this.stopAll()\n this.lastTime = currentTime\n\n const activeSegments = collectActiveSegments(this.protocol, currentTime)\n const activeAudioKeys = new Set<string>()\n const activeVideoKeys = new Set<string>()\n\n for (const { segment } of activeSegments) {\n if (segment.segmentType === 'audio') {\n const key = this.audioKey(segment.id)\n activeAudioKeys.add(key)\n this.ensureAudioLoop(segment as IAudioSegment, currentTime)\n }\n else if (segment.segmentType === 'frames' && 'type' in segment && segment.type === 'video') {\n const key = this.videoKey(segment.id)\n activeVideoKeys.add(key)\n }\n }\n\n for (const [key, loop] of this.audioLoops) {\n if (!activeAudioKeys.has(key)) {\n loop.stop()\n // Stop all audio buffer sources\n for (const source of loop.sources) {\n try {\n source.stop()\n source.disconnect()\n }\n catch (e) {\n // Source may already be stopped or disconnected\n }\n }\n this.audioLoops.delete(key)\n this.audioLoopContexts.delete(key)\n this.audioGains.delete(key)\n }\n }\n\n for (const [key, loop] of this.mp4Loops) {\n if (!activeVideoKeys.has(key)) {\n loop.stop()\n // Stop all audio buffer sources\n for (const source of loop.sources) {\n try {\n source.stop()\n source.disconnect()\n }\n catch (e) {\n // Source may already be stopped or disconnected\n }\n }\n this.mp4Loops.delete(key)\n this.mp4States.delete(key)\n this.mp4Gains.delete(key)\n }\n }\n\n // Also clean up mp4States that don't have active loops (direct playback mode)\n for (const key of this.mp4States.keys()) {\n if (!activeVideoKeys.has(key) && !this.mp4Loops.has(key)) {\n this.mp4States.delete(key)\n this.mp4Gains.delete(key)\n }\n }\n }\n\n /**\n * Play audio frames from MP4 video directly.\n * Called by renderer after it gets audio data from clip.tick()\n */\n public playMp4AudioFrames(id: string, audio: Float32Array[] | undefined, sampleRate: number) {\n if (!audio || audio.length === 0 || !audio[0]?.length)\n return\n\n const key = this.videoKey(id)\n const volume = this.getSegmentVolume(id)\n const gainNode = this.getOrCreateGain(this.mp4Gains, key, volume)\n\n if (this.ctx.state === 'suspended')\n this.ctx.resume().catch(() => {})\n\n // Get or initialize the playback state for this video\n let state = this.mp4States.get(key)\n if (!state) {\n state = {\n loop: { stop: () => {}, sources: [], isStopped: () => false },\n startUs: 0,\n startCtxTime: this.ctx.currentTime,\n fps: 30,\n nextStartAt: 0,\n }\n this.mp4States.set(key, state)\n }\n\n // Play the audio frames\n state.nextStartAt = this.playFrames(audio, sampleRate, state.nextStartAt ?? 0, gainNode, state.loop.sources)\n }\n\n /**\n * Reset MP4 audio playback state (call when seeking or pausing)\n */\n public resetMp4Audio(id: string) {\n const key = this.videoKey(id)\n const state = this.mp4States.get(key)\n if (state) {\n state.nextStartAt = 0\n }\n }\n\n /**\n * Stop and clean up MP4 audio for a video segment\n */\n public stopMp4Audio(id: string) {\n const key = this.videoKey(id)\n const state = this.mp4States.get(key)\n if (state) {\n state.loop.stop()\n // Stop all audio buffer sources\n for (const source of state.loop.sources) {\n try {\n source.stop()\n source.disconnect()\n }\n catch (e) {\n // Source may already be stopped or disconnected\n }\n }\n this.mp4Loops.delete(key)\n this.mp4States.delete(key)\n this.mp4Gains.delete(key)\n }\n }\n\n /**\n * @deprecated Use playMp4AudioFrames instead\n */\n public ensureMp4Audio(id: string, clip: MP4Clip, startUs: number, fps: number) {\n // Legacy method - kept for compatibility but should not be used\n // The new approach is to pass audio data directly via playMp4AudioFrames\n const key = this.videoKey(id)\n const volume = this.getSegmentVolume(id)\n const gainNode = this.getOrCreateGain(this.mp4Gains, key, volume)\n const existing = this.mp4States.get(key)\n if (existing) {\n gainNode.gain.value = Math.max(0, volume)\n const elapsedUs = (this.ctx.currentTime - existing.startCtxTime) * 1e6\n const expectedUs = existing.startUs + elapsedUs\n if (Math.abs(expectedUs - startUs) < 150000 && existing.fps === fps)\n return\n existing.loop.stop()\n // Stop all audio buffer sources\n for (const source of existing.loop.sources) {\n try {\n source.stop()\n source.disconnect()\n }\n catch (e) {\n // Source may already be stopped or disconnected\n }\n }\n this.mp4Loops.delete(key)\n this.mp4States.delete(key)\n }\n if (this.ctx.state === 'suspended')\n this.ctx.resume().catch(() => {})\n const loop = this.startMp4Loop(clip, startUs, fps, gainNode)\n this.mp4Loops.set(key, loop)\n this.mp4States.set(key, {\n loop,\n startUs,\n startCtxTime: this.ctx.currentTime,\n fps,\n })\n }\n\n public destroy() {\n this.stopAll()\n for (const entry of this.clips.values())\n entry.clip.destroy()\n this.clips.clear()\n }\n\n private stopAll() {\n for (const loop of this.audioLoops.values()) {\n loop.stop()\n // Stop all audio buffer sources immediately\n for (const source of loop.sources) {\n try {\n source.stop(0)\n source.disconnect()\n }\n catch (e) {\n // Source may already be stopped or disconnected\n }\n }\n loop.sources.length = 0\n }\n for (const loop of this.mp4Loops.values()) {\n loop.stop()\n // Stop all audio buffer sources immediately\n for (const source of loop.sources) {\n try {\n source.stop(0)\n source.disconnect()\n }\n catch (e) {\n // Source may already be stopped or disconnected\n }\n }\n loop.sources.length = 0\n }\n // Disconnect all gain nodes to completely silence audio\n for (const gain of this.audioGains.values()) {\n try {\n gain.disconnect()\n }\n catch (e) {\n // Gain may already be disconnected\n }\n }\n for (const gain of this.mp4Gains.values()) {\n try {\n gain.disconnect()\n }\n catch (e) {\n // Gain may already be disconnected\n }\n }\n this.audioLoops.clear()\n this.audioLoopContexts.clear()\n this.audioGains.clear()\n this.mp4Loops.clear()\n this.mp4States.clear()\n this.mp4Gains.clear()\n }\n\n private async ensureAudioLoop(segment: IAudioSegment, currentTime: number) {\n const key = this.audioKey(segment.id)\n const entry = await this.loadClip(segment)\n if (!entry)\n return\n const offset = segment.fromTime ?? 0\n const playRate = Math.max(0.1, Math.min(100, segment.playRate ?? 1))\n // relativeMs is position within segment timeline (not source audio)\n const relativeMs = currentTime - segment.startTime\n // sourceOffsetMs is where we are in the source audio (accounting for fromTime and playRate)\n const sourceOffsetMs = offset + relativeMs * playRate\n const gainNode = this.getOrCreateGain(this.audioGains, key, segment.volume)\n\n // Apply fade in/out to gain\n this.applyFadeToGain(segment, relativeMs, gainNode)\n\n const existingLoop = this.audioLoops.get(key)\n if (existingLoop && !existingLoop.isStopped()) {\n // Loop is still running, just update volume with fade\n return\n }\n // Clean up stopped loop if exists\n if (existingLoop) {\n this.audioLoops.delete(key)\n this.audioLoopContexts.delete(key)\n }\n // Calculate segment duration in microseconds\n const segmentDurationMs = segment.endTime - segment.startTime\n const sourceDurationMs = segmentDurationMs * playRate\n const maxSourceOffsetMs = offset + sourceDurationMs\n\n const loop = this.startAudioLoop(entry.clip, Math.max(0, sourceOffsetMs) * 1000, gainNode, playRate, maxSourceOffsetMs * 1000)\n this.audioLoops.set(key, loop)\n this.audioLoopContexts.set(key, {\n segment,\n startedAt: this.ctx.currentTime,\n segmentRelativeMs: relativeMs,\n })\n }\n\n private applyFadeToGain(segment: IAudioSegment, relativeMs: number, gainNode: GainNode) {\n const baseVolume = Math.max(0, typeof segment.volume === 'number' ? segment.volume : 1)\n const segmentDuration = segment.endTime - segment.startTime\n const fadeInDuration = segment.fadeInDuration ?? 0\n const fadeOutDuration = segment.fadeOutDuration ?? 0\n\n let volumeMultiplier = 1\n\n // Apply fade in\n if (fadeInDuration > 0 && relativeMs < fadeInDuration) {\n volumeMultiplier = Math.max(0, relativeMs / fadeInDuration)\n }\n\n // Apply fade out\n const timeUntilEnd = segmentDuration - relativeMs\n if (fadeOutDuration > 0 && timeUntilEnd < fadeOutDuration) {\n volumeMultiplier = Math.min(volumeMultiplier, Math.max(0, timeUntilEnd / fadeOutDuration))\n }\n\n gainNode.gain.value = baseVolume * volumeMultiplier\n }\n\n private startMp4Loop(clip: MP4Clip, startUs: number, fps: number, gainNode: GainNode): LoopState {\n let stopped = false\n let timeUs = startUs\n let startAt = 0\n let first = true\n const stepUs = Math.round((1000 / Math.max(fps || 30, 1)) * 1000)\n const sampleRate = this.getClipSampleRate(clip)\n const sources: AudioBufferSourceNode[] = []\n\n const timer = window.setInterval(async () => {\n if (stopped)\n return\n const { audio, state } = await clip.tick(Math.round(timeUs))\n timeUs += stepUs\n if (state === 'done')\n return\n if (first) {\n first = false\n return\n }\n const len = audio?.[0]?.length ?? 0\n if (len === 0)\n return\n startAt = this.playFrames(audio as Float32Array[], sampleRate, startAt, gainNode, sources)\n }, Math.round(1000 / Math.max(fps || 30, 1)))\n\n return {\n sources,\n stop: () => {\n stopped = true\n window.clearInterval(timer)\n },\n isStopped: () => stopped,\n }\n }\n\n private startAudioLoop(clip: AudioClip, startUs: number, gainNode: GainNode, playRate: number = 1, maxSourceOffsetUs?: number): LoopState {\n let stopped = false\n let timeUs = startUs\n let startAt = 0\n let initialized = false\n const sampleRate = this.getClipSampleRate(clip)\n const sources: AudioBufferSourceNode[] = []\n // Each iteration fetches 100ms of real-time audio\n // For playRate != 1, we need to fetch more/less source audio\n const realTimeStepUs = 100000\n const sourceStepUs = Math.round(realTimeStepUs * playRate)\n\n const play = async () => {\n if (stopped)\n return\n\n if (!initialized) {\n // First tick: seek to startUs position (discard audio before startUs)\n if (startUs > 0) {\n await clip.tick(startUs)\n }\n initialized = true\n }\n\n timeUs += sourceStepUs\n\n // Check if we've reached the maximum duration for this segment\n if (maxSourceOffsetUs !== undefined && timeUs > maxSourceOffsetUs) {\n stopped = true\n return\n }\n\n const { audio, state } = await clip.tick(timeUs)\n\n // Check stopped flag immediately after async operation\n if (stopped)\n return\n\n // Stop when audio file ends (don't loop)\n if (state === 'done') {\n stopped = true\n return\n }\n const len = audio?.[0]?.length ?? 0\n if (len === 0) {\n // No audio data - either at silence or past end of file\n // Stop playing to avoid infinite loop\n stopped = true\n return\n }\n // Adjust playback rate by resampling if playRate !== 1\n const processedAudio = playRate !== 1\n ? this.resampleForPlayRate(audio as Float32Array[], playRate)\n : audio as Float32Array[]\n startAt = this.playFrames(processedAudio, sampleRate, startAt, gainNode, sources)\n\n // Check stopped before recursing\n if (!stopped) {\n // Use setTimeout to avoid deep call stack and allow stop to interrupt\n setTimeout(() => play(), 0)\n }\n }\n\n play()\n return {\n sources,\n stop: () => {\n stopped = true\n },\n isStopped: () => stopped,\n }\n }\n\n private resampleForPlayRate(audio: Float32Array[], playRate: number): Float32Array[] {\n // For playRate > 1, we have more source samples than needed (speed up)\n // For playRate < 1, we have fewer source samples than needed (slow down)\n // We need to stretch/compress the audio to match real-time playback\n const outputLength = Math.round(audio[0].length / playRate)\n if (outputLength <= 0)\n return audio\n\n return audio.map((channel) => {\n const output = new Float32Array(outputLength)\n for (let i = 0; i < outputLength; i++) {\n const srcIndex = i * playRate\n const srcIndexFloor = Math.floor(srcIndex)\n const srcIndexCeil = Math.min(srcIndexFloor + 1, channel.length - 1)\n const t = srcIndex - srcIndexFloor\n // Linear interpolation between samples\n output[i] = channel[srcIndexFloor] * (1 - t) + channel[srcIndexCeil] * t\n }\n return output\n })\n }\n\n private playFrames(audio: Float32Array[], sampleRate: number, startAt: number, gainNode: GainNode, sources?: AudioBufferSourceNode[]) {\n const channels = Math.max(audio.length, 1)\n const len = audio[0]?.length ?? 0\n if (len === 0)\n return startAt\n const buffer = this.ctx.createBuffer(channels, len, sampleRate)\n for (let i = 0; i < channels; i++) {\n const data = audio[i] ?? new Float32Array(len)\n buffer.copyToChannel(new Float32Array(data), i)\n }\n const source = this.ctx.createBufferSource()\n source.buffer = buffer\n source.connect(gainNode)\n const nextStart = Math.max(this.ctx.currentTime, startAt)\n source.start(nextStart)\n\n // Track the source so it can be stopped later\n if (sources) {\n sources.push(source)\n // Clean up finished sources after they complete\n const duration = buffer.duration * 1000\n setTimeout(() => {\n const index = sources.indexOf(source)\n if (index > -1) {\n sources.splice(index, 1)\n }\n }, duration + 100)\n }\n\n return nextStart + buffer.duration\n }\n\n private getOrCreateGain(map: Map<string, GainNode>, id: string, volume?: number) {\n const existing = map.get(id)\n if (existing) {\n if (typeof volume === 'number')\n existing.gain.value = Math.max(0, volume)\n return existing\n }\n const gainNode = this.ctx.createGain()\n gainNode.gain.value = Math.max(0, typeof volume === 'number' ? volume : 1)\n gainNode.connect(this.ctx.destination)\n map.set(id, gainNode)\n return gainNode\n }\n\n private audioKey(id: string) {\n return `audio:${id}`\n }\n\n private videoKey(id: string) {\n return `video:${id}`\n }\n\n private async loadClip(segment: IAudioSegment): Promise<ClipEntry | undefined> {\n // Check if already loaded\n const cached = this.clips.get(segment.id)\n if (cached)\n return cached\n\n // Check if currently loading\n const loading = this.loadingClips.get(segment.id)\n if (loading)\n return loading\n\n // Start loading and cache the promise immediately\n const loadingPromise = (async () => {\n try {\n const response = await fetch(segment.url)\n if (!response.body) {\n this.loadingClips.delete(segment.id)\n return undefined\n }\n const clip = new WebAVAudioClip(response.body)\n const entry: ClipEntry = { clip, ready: clip.ready }\n await clip.ready\n if (!this.findSegmentInProtocol(segment.id)) {\n clip.destroy()\n this.loadingClips.delete(segment.id)\n return undefined\n }\n // Move from loading to loaded\n this.clips.set(segment.id, entry)\n this.loadingClips.delete(segment.id)\n return entry\n }\n catch (e) {\n console.error(`[AudioManager] Failed to load audio ${segment.url}`, e)\n this.loadingClips.delete(segment.id)\n return undefined\n }\n })()\n\n this.loadingClips.set(segment.id, loadingPromise)\n return loadingPromise\n }\n\n private findSegmentInProtocol(id: string): boolean {\n for (const track of this.protocol.tracks) {\n for (const segment of track.children) {\n if (segment.id === id) return true\n }\n }\n return false\n }\n\n private getSegmentVolume(id: string): number {\n for (const track of this.protocol.tracks) {\n for (const segment of track.children) {\n if (segment.id !== id)\n continue\n const volume = (segment as { volume?: number }).volume\n return typeof volume === 'number' ? volume : 1\n }\n }\n return 1\n }\n\n private getClipSampleRate(clip: AudioClip | MP4Clip): number {\n const meta = (clip as { meta?: Record<string, unknown> }).meta\n if (!meta)\n return 48000\n const audioSampleRate = meta.audioSampleRate\n if (typeof audioSampleRate === 'number' && audioSampleRate > 0)\n return audioSampleRate\n const sampleRate = meta.sampleRate\n if (typeof sampleRate === 'number' && sampleRate > 0)\n return sampleRate\n return 48000\n }\n}\n","import type { ITextBasic } from '@video-editor/shared'\nimport { renderTxt2ImgBitmap } from '@webav/av-cliper'\n\nconst DEFAULT_TEXT_BITMAP_CACHE_LIMIT = 100\nconst textBitmapCache = new Map<string, ImageBitmap>()\nlet textBitmapCacheLimit = DEFAULT_TEXT_BITMAP_CACHE_LIMIT\n\nfunction touchCache(key: string, value: ImageBitmap) {\n textBitmapCache.delete(key)\n textBitmapCache.set(key, value)\n}\n\nfunction trimCache() {\n while (textBitmapCache.size > textBitmapCacheLimit) {\n const [oldestKey, bitmap] = textBitmapCache.entries().next().value as [string, ImageBitmap]\n textBitmapCache.delete(oldestKey)\n bitmap.close?.()\n }\n}\n\nexport function setTextBitmapCacheLimit(limit: number) {\n textBitmapCacheLimit = Math.max(0, Math.floor(limit))\n trimCache()\n}\n\nexport function clearTextBitmapCache() {\n for (const bitmap of textBitmapCache.values())\n bitmap.close?.()\n textBitmapCache.clear()\n}\n\nexport function buildTextContent(texts: ITextBasic[]) {\n return texts.map(item => item.content).filter(Boolean).join('\\n')\n}\n\nexport function buildTextCss(text: ITextBasic) {\n const fontFamily = Array.isArray(text.fontFamily)\n ? text.fontFamily.join(', ')\n : text.fontFamily\n const fontSize = text.fontSize ?? 32\n const fontWeight = text.fontWeight ?? 'normal'\n const fontStyle = text.fontStyle ?? 'normal'\n const fill = text.fill ?? '#ffffff'\n const align = text.align ?? 'left'\n\n const css: string[] = [\n `font-size: ${fontSize}px`,\n `font-weight: ${fontWeight}`,\n `font-style: ${fontStyle}`,\n `color: ${fill}`,\n `text-align: ${align}`,\n 'white-space: pre-wrap',\n ]\n\n if (fontFamily)\n css.push(`font-family: ${fontFamily}`)\n if (typeof text.letterSpacing === 'number')\n css.push(`letter-spacing: ${text.letterSpacing}px`)\n if (typeof text.leading === 'number')\n css.push(`line-height: ${text.leading}px`)\n if (text.background?.color)\n css.push(`background: ${text.background.color}`)\n if (text.stroke?.color && typeof text.stroke.width === 'number')\n css.push(`-webkit-text-stroke: ${text.stroke.width}px ${text.stroke.color}`)\n if (text.underline)\n css.push('text-decoration: underline')\n if (text.dropShadow?.color && typeof text.dropShadow.distance === 'number') {\n const angle = (text.dropShadow.angle ?? 45) * (Math.PI / 180)\n const offsetX = Math.cos(angle) * text.dropShadow.distance\n const offsetY = Math.sin(angle) * text.dropShadow.distance\n const blur = text.dropShadow.blur ?? 0\n css.push(`text-shadow: ${offsetX}px ${offsetY}px ${blur}px ${text.dropShadow.color}`)\n }\n\n return css.join('; ')\n}\n\nexport async function renderTextBitmap(content: string, cssText: string) {\n const key = `${cssText}::${content}`\n const cached = textBitmapCache.get(key)\n if (cached) {\n touchCache(key, cached)\n return cached\n }\n\n const bitmap = await renderTxt2ImgBitmap(content, cssText)\n if (textBitmapCacheLimit > 0) {\n textBitmapCache.set(key, bitmap)\n trimCache()\n }\n return bitmap\n}\n","import type { ITextSegment, IVideoFramesSegment, IVideoProtocol, SegmentUnion } from '@video-editor/shared'\nimport type { ComputedRef, Ref, ShallowRef } from '@vue/reactivity'\nimport type { Application, ApplicationOptions } from 'pixi.js'\nimport type { MaybeRef, PixiDisplayObject } from './types'\nimport { createResourceManager, createValidator, getResourceKey } from '@video-editor/protocol'\nimport {\n computed,\n effectScope,\n isRef,\n ref,\n shallowRef,\n unref,\n watch,\n} from '@vue/reactivity'\nimport { MP4Clip } from '@webav/av-cliper'\nimport { file as opfsFile } from 'opfs-tools'\nimport { Container, Sprite, Texture } from 'pixi.js'\nimport { createApp as create2dApp } from './2d'\nimport { AudioManager } from './audio-manager'\nimport {\n applyDisplayProps,\n clamp,\n cloneProtocol,\n collectActiveSegments,\n collectResourceUrls,\n computeDuration,\n placeholder,\n} from './helpers'\nimport { buildTextContent, buildTextCss, renderTextBitmap } from './text'\n\nconst DEFAULT_RES_DIR = '/video-editor-res'\n\nexport interface RendererOptions {\n protocol: MaybeRef<IVideoProtocol>\n app?: Application\n appOptions?: Partial<ApplicationOptions>\n resourceDir?: string\n autoPlay?: boolean\n freezeOnPause?: boolean\n manualRender?: boolean\n videoSourceMode?: 'auto' | 'mp4clip' | 'element'\n warmUpResources?: boolean\n}\n\nexport interface Renderer {\n app: Application\n layer: Container\n currentTime: Ref<number>\n duration: ComputedRef<number>\n isPlaying: Ref<boolean>\n play: () => void\n pause: () => void\n tick: (deltaMs?: number) => void\n seek: (time: number) => void\n renderAt: (time: number) => Promise<void>\n destroy: () => void\n}\n\ninterface AudioManagerApi {\n setProtocol: (protocol: IVideoProtocol) => void\n sync: (currentTime: number, isPlaying: boolean) => void | Promise<void>\n ensureMp4Audio: (id: string, clip: MP4Clip, startUs: number, fps: number) => void\n destroy: () => void\n}\n\n/**\n * Create a renderer that reacts to protocol updates and drives playback state.\n * - Pass a reactive `protocol` (Ref/readonly/normal object)\n * - Call `play/pause/seek/tick` to drive the timeline\n * - Rendering updates when `protocol` or `currentTime` changes\n */\nexport async function createRenderer(opts: RendererOptions): Promise<Renderer> {\n const validator = createValidator()\n const protocolInput: Ref<IVideoProtocol> | ShallowRef<IVideoProtocol>\n = isRef(opts.protocol) ? opts.protocol : shallowRef(opts.protocol)\n const validatedProtocol: ShallowRef<IVideoProtocol> = shallowRef(\n validator.verify(cloneProtocol(unref(protocolInput))),\n )\n\n const app = opts.app ?? await create2dApp(opts.appOptions)\n const layer = new Container()\n app.stage.addChild(layer)\n\n const resourceManager = createResourceManager({ dir: opts.resourceDir })\n const resourceWarmUp = new Set<string>()\n const displayCache = new Map<string, PixiDisplayObject>()\n const displayLoading = new Map<string, Promise<PixiDisplayObject | undefined>>()\n const mp4ClipUnsupportedKeys = new Set<string>()\n const mp4ClipErrorLoggedKeys = new Set<string>()\n const videoSourceMode = opts.videoSourceMode ?? 'auto'\n type VideoEntry = (\n | {\n kind: 'mp4clip'\n clip: MP4Clip\n canvas: HTMLCanvasElement\n texture: Texture\n sprite: Sprite\n meta?: { width: number, height: number }\n }\n | {\n kind: 'element'\n video: HTMLVideoElement\n canvas: HTMLCanvasElement\n texture: Texture\n sprite: Sprite\n meta?: { width: number, height: number }\n }\n | {\n kind: 'frozen'\n canvas: HTMLCanvasElement\n texture: Texture\n sprite: Sprite\n meta?: { width: number, height: number }\n }\n )\n const videoEntries = new Map<string, VideoEntry>()\n const videoObjectUrls = new Map<HTMLVideoElement, string>()\n\n const currentTime = ref(0)\n const isPlaying = ref(false)\n const duration = computed(() => computeDuration(validatedProtocol.value))\n const audioManager: AudioManagerApi = new AudioManager(validatedProtocol.value) as unknown as AudioManagerApi\n\n let rafId: number | undefined\n let lastTickAt = 0\n let renderGeneration = 0\n\n interface RenderTask {\n app: Application\n layer: Container\n protocol: IVideoProtocol\n at: number\n getDisplay: (segment: SegmentUnion) => Promise<PixiDisplayObject | undefined>\n }\n\n async function renderScene(task: RenderTask) {\n const generation = renderGeneration\n const { protocol, at, layer } = task\n const renderAt = normalizeRenderTime(protocol, at)\n const active = collectActiveSegments(protocol, renderAt)\n const stageWidth = task.app.renderer.width\n const stageHeight = task.app.renderer.height\n\n audioManager.sync(at, isPlaying.value)\n\n const renders: (PixiDisplayObject | undefined)[] = []\n for (const { segment } of active) {\n if (generation !== renderGeneration)\n return\n const display = await task.getDisplay(segment)\n if (generation !== renderGeneration)\n return\n if (!display)\n continue\n if ((display as { destroyed?: boolean }).destroyed)\n continue\n applyDisplayProps(display, segment, stageWidth, stageHeight)\n if (isVideoSegment(segment))\n await updateVideoFrame(segment, renderAt)\n if (generation !== renderGeneration)\n return\n renders.push(display)\n }\n\n if (generation !== renderGeneration)\n return\n layer.removeChildren()\n const cleaned = renders.filter(Boolean) as PixiDisplayObject[]\n if (cleaned.length)\n layer.addChild(...cleaned)\n if (generation !== renderGeneration)\n return\n task.app.render()\n }\n\n const queueRender = createRenderQueue(() => renderScene({\n app,\n layer,\n protocol: validatedProtocol.value,\n at: currentTime.value,\n getDisplay: getDisplayForSegment,\n }))\n\n const scope = effectScope()\n scope.run(() => {\n // Sync external protocol mutations into a verified snapshot the renderer can rely on.\n watch(\n () => unref(protocolInput),\n (protocol) => {\n try {\n validatedProtocol.value = validator.verify(cloneProtocol(protocol))\n }\n catch (err) {\n console.error('[renderer] invalid protocol update', err)\n return\n }\n audioManager.setProtocol(validatedProtocol.value)\n renderGeneration += 1\n clearDisplays()\n if (opts.warmUpResources !== false)\n warmUpResources(validatedProtocol.value)\n cleanupCache(validatedProtocol.value)\n clampCurrentTime()\n if (!opts.manualRender)\n queueRender()\n },\n { deep: true, immediate: true },\n )\n\n if (!opts.manualRender) {\n // React to time changes.\n watch(currentTime, () => {\n clampCurrentTime()\n queueRender()\n })\n }\n\n // Keep duration/currentTime in sync with protocol updates.\n watch(duration, () => clampCurrentTime())\n })\n\n function clampCurrentTime() {\n const nextDuration = duration.value\n if (nextDuration <= 0)\n currentTime.value = 0\n else if (currentTime.value > nextDuration)\n currentTime.value = nextDuration\n else if (currentTime.value < 0)\n currentTime.value = 0\n }\n\n function warmUpResources(protocol: IVideoProtocol) {\n for (const url of collectResourceUrls(protocol)) {\n if (resourceWarmUp.has(url))\n continue\n\n resourceWarmUp.add(url)\n if (inferUrlMediaType(url) === 'video')\n continue\n if (!shouldUseResourceManager(url))\n continue\n resourceManager.add(url).catch(() => {\n // noop – render will fall back to Texture.from(url)\n })\n }\n }\n\n function cleanupCache(protocol: IVideoProtocol) {\n const ids = new Set<string>()\n for (const track of protocol.tracks) {\n for (const child of track.children)\n ids.add(child.id)\n }\n for (const [id, display] of displayCache) {\n if (ids.has(id))\n continue\n display.destroy()\n displayCache.delete(id)\n }\n for (const [id, entry] of videoEntries) {\n if (ids.has(id))\n continue\n destroyVideoEntry(entry)\n videoEntries.delete(id)\n }\n }\n\n function clearDisplays() {\n layer.removeChildren()\n for (const display of displayCache.values()) {\n display.destroy()\n }\n displayCache.clear()\n displayLoading.clear()\n for (const entry of videoEntries.values())\n destroyVideoEntry(entry)\n videoEntries.clear()\n }\n\n function play() {\n if (isPlaying.value)\n return\n isPlaying.value = true\n lastTickAt = performance.now()\n rafId = requestAnimationFrame(loop)\n }\n\n function pause() {\n isPlaying.value = false\n if (rafId !== undefined)\n cancelAnimationFrame(rafId)\n rafId = undefined\n // Stop audio immediately when pausing\n audioManager.sync(currentTime.value, false)\n if (opts.freezeOnPause !== false)\n freezeVideoEntries()\n }\n\n function loop() {\n tick()\n if (isPlaying.value)\n rafId = requestAnimationFrame(loop)\n }\n\n function tick(deltaMs?: number) {\n if (!isPlaying.value && deltaMs === undefined)\n return\n\n const now = performance.now()\n const delta = deltaMs ?? (lastTickAt ? now - lastTickAt : 0)\n lastTickAt = now\n\n if (delta === 0)\n return\n\n currentTime.value = clamp(\n currentTime.value + delta,\n 0,\n duration.value || Number.POSITIVE_INFINITY,\n )\n\n if (duration.value > 0 && currentTime.value >= duration.value)\n pause()\n\n // render happens via watch on currentTime\n }\n\n function seek(time: number) {\n currentTime.value = clamp(time, 0, duration.value || Number.POSITIVE_INFINITY)\n }\n\n async function renderAt(time: number) {\n currentTime.value = clamp(time, 0, duration.value || Number.POSITIVE_INFINITY)\n await queueRender()\n }\n\n async function getDisplayForSegment(segment: SegmentUnion) {\n const cached = displayCache.get(segment.id)\n if (cached)\n return cached\n\n const loading = displayLoading.get(segment.id)\n if (loading)\n return loading\n\n const promise = loadDisplay(segment)\n displayLoading.set(segment.id, promise)\n\n const display = await promise\n if (display)\n displayCache.set(segment.id, display)\n\n displayLoading.delete(segment.id)\n return display\n }\n\n async function loadDisplay(segment: SegmentUnion): Promise<PixiDisplayObject | undefined> {\n // prioritize static resources via protocol resource manager\n if (segment.segmentType === 'frames' || segment.segmentType === 'sticker') {\n if (!segment.url)\n return placeholder(segment.segmentType)\n\n if ('type' in segment && segment.type === 'video') {\n if (isRenderableVideoUrl(segment.url)) {\n const sprite = await loadVideoSprite(segment)\n if (sprite)\n return sprite\n return placeholder(segment.segmentType, segment.url)\n }\n }\n\n const texture = await loadTexture(segment.url)\n if (texture)\n return new Sprite(texture)\n return placeholder(segment.segmentType, segment.url)\n }\n\n if (segment.segmentType === 'text')\n return await buildTextDisplay(segment)\n\n if (segment.segmentType === 'effect' || segment.segmentType === 'filter')\n return undefined\n\n // audio segments do not render visuals\n return undefined\n }\n\n async function buildTextDisplay(segment: ITextSegment): Promise<PixiDisplayObject | undefined> {\n const content = buildTextContent(segment.texts)\n if (!content)\n return undefined\n\n const [text] = segment.texts\n if (!text)\n return undefined\n\n const bitmap = await renderTextBitmap(content, buildTextCss(text))\n const texture = Texture.from(bitmap)\n return new Sprite(texture)\n }\n\n async function loadTexture(url: string) {\n const isDataUrl = url.startsWith('data:')\n const isHttp = /^https?:\\/\\//.test(url)\n\n if (!isDataUrl && !isHttp) {\n try {\n await resourceManager.add(url)\n const res = await resourceManager.get(url)\n if (res instanceof HTMLImageElement)\n return Texture.from(res)\n }\n catch {\n // fall through to direct image load\n }\n }\n\n // load image directly to avoid invalid path issues with http/data URLs\n return await loadImageTexture(url)\n }\n\n async function loadVideoSprite(segment: SegmentUnion & { type: 'video', url: string }): Promise<Sprite | undefined> {\n const existing = videoEntries.get(segment.id)\n if (existing)\n return existing.sprite\n\n const urlKey = getResourceKey(segment.url)\n const allowMp4Clip = videoSourceMode !== 'element'\n const allowVideoElement = videoSourceMode !== 'mp4clip'\n if (urlKey && mp4ClipUnsupportedKeys.has(urlKey)) {\n if (!allowVideoElement)\n throw new Error(`[renderer] MP4Clip unsupported for ${segment.url}`)\n const spriteFromElement = await loadVideoSpriteViaElement(segment.url).catch((err) => {\n console.warn('[renderer] failed to load video via <video>', segment.url, err)\n return undefined\n })\n if (spriteFromElement) {\n videoEntries.set(segment.id, spriteFromElement)\n return spriteFromElement.sprite\n }\n return undefined\n }\n\n if (allowMp4Clip) {\n const spriteFromClip = await loadVideoSpriteViaMP4Clip(segment.url).catch((err) => {\n if (urlKey && isMp4ClipUnsupported(err))\n mp4ClipUnsupportedKeys.add(urlKey)\n if (!urlKey || !mp4ClipErrorLoggedKeys.has(urlKey)) {\n if (urlKey)\n mp4ClipErrorLoggedKeys.add(urlKey)\n console.warn('[renderer] failed to load video via MP4Clip', segment.url, err)\n }\n if (!allowVideoElement)\n throw err\n return undefined\n })\n if (spriteFromClip) {\n console.info('[renderer] video source: mp4clip', segment.url)\n videoEntries.set(segment.id, spriteFromClip)\n return spriteFromClip.sprite\n }\n }\n\n if (allowVideoElement) {\n const spriteFromElement = await loadVideoSpriteViaElement(segment.url).catch((err) => {\n console.warn('[renderer] failed to load video via <video>', segment.url, err)\n return undefined\n })\n if (spriteFromElement) {\n console.info('[renderer] video source: element', segment.url)\n videoEntries.set(segment.id, spriteFromElement)\n return spriteFromElement.sprite\n }\n }\n\n return undefined\n }\n\n function isMp4ClipUnsupported(err: unknown) {\n if (!(err instanceof Error))\n return false\n const msg = err.message || ''\n return msg.includes('stream is done')\n || msg.includes('not emit ready')\n || msg.includes('tick video timeout')\n }\n\n async function updateVideoFrame(segment: IVideoFramesSegment, at: number) {\n const entry = videoEntries.get(segment.id)\n if (!entry)\n return\n\n try {\n const offsetMs = segment.fromTime ?? 0\n const relativeMs = Math.max(0, at - segment.startTime + offsetMs)\n const relativeUs = Math.floor(relativeMs * 1000)\n if (entry.kind === 'frozen') {\n const urlKey = getResourceKey(segment.url)\n if (!urlKey)\n return\n const revived = await loadVideoEntry(segment.url, urlKey, { sprite: entry.sprite, oldTexture: entry.texture })\n if (!revived)\n return\n videoEntries.set(segment.id, revived)\n return await updateVideoFrame(segment, at)\n }\n if (entry.kind === 'mp4clip') {\n try {\n const res = await entry.clip.tick(relativeUs)\n if (res.video) {\n const ctx = entry.canvas.getContext('2d')\n if (ctx) {\n ctx.drawImage(res.video, 0, 0, entry.canvas.width, entry.canvas.height)\n refreshCanvasTexture(entry.texture)\n }\n res.video.close()\n }\n // Play audio directly from tick result (avoid calling tick twice)\n if (isPlaying.value && res.audio && res.audio.length > 0) {\n const sampleRate = (entry.clip as { meta?: { audioSampleRate?: number } }).meta?.audioSampleRate ?? 48000;\n (audioManager as unknown as { playMp4AudioFrames: (id: string, audio: Float32Array[], sampleRate: number) => void })\n .playMp4AudioFrames(segment.id, res.audio as Float32Array[], sampleRate)\n }\n return\n }\n catch (err) {\n const urlKey = getResourceKey(segment.url)\n if (urlKey && isMp4ClipUnsupported(err)) {\n mp4ClipUnsupportedKeys.add(urlKey)\n entry.clip.destroy()\n if (videoSourceMode !== 'mp4clip') {\n const replacement = await loadVideoSpriteViaElement(segment.url, { sprite: entry.sprite, oldTexture: entry.texture }).catch((elementErr) => {\n console.warn('[renderer] failed to fallback to <video> after MP4Clip error', segment.url, elementErr)\n return undefined\n })\n if (replacement) {\n videoEntries.set(segment.id, replacement)\n return await updateVideoFrame(segment, at)\n }\n }\n }\n if (urlKey && !mp4ClipErrorLoggedKeys.has(urlKey)) {\n mp4ClipErrorLoggedKeys.add(urlKey)\n console.warn('[renderer] MP4Clip tick failed', segment.url, err)\n }\n return\n }\n }\n\n const relativeSec = relativeMs / 1000\n if (!Number.isFinite(relativeSec))\n return\n if (entry.kind !== 'element')\n return\n await updateVideoElementFrame(entry, {\n targetSec: relativeSec,\n playbackRate: segment.playRate ?? 1,\n volume: segment.volume ?? 1,\n })\n }\n catch (err) {\n console.warn('[renderer] update video frame failed', err)\n }\n }\n\n async function loadVideoEntry(url: string, urlKey: string, reuse: { sprite: Sprite, oldTexture?: Texture }) {\n const allowMp4Clip = videoSourceMode !== 'element'\n const allowVideoElement = videoSourceMode !== 'mp4clip'\n if (mp4ClipUnsupportedKeys.has(urlKey)) {\n if (!allowVideoElement)\n throw new Error(`[renderer] MP4Clip unsupported for ${url}`)\n return await loadVideoSpriteViaElement(url, reuse).catch(() => undefined)\n }\n\n if (allowMp4Clip) {\n const fromClip = await loadVideoSpriteViaMP4Clip(url, reuse).catch((err) => {\n if (isMp4ClipUnsupported(err))\n mp4ClipUnsupportedKeys.add(urlKey)\n if (!allowVideoElement)\n throw err\n return undefined\n })\n if (fromClip)\n return fromClip\n }\n\n if (allowVideoElement)\n return await loadVideoSpriteViaElement(url, reuse).catch(() => undefined)\n\n return undefined\n }\n\n function isVideoSegment(segment: SegmentUnion): segment is IVideoFramesSegment {\n return segment.segmentType === 'frames'\n && segment.type === 'video'\n && typeof segment.url === 'string'\n && isRenderableVideoUrl(segment.url)\n }\n\n\n function normalizeRenderTime(protocol: IVideoProtocol, at: number) {\n const total = computeDuration(protocol)\n if (total <= 0)\n return 0\n if (at < total)\n return at\n // Keep the last visible frame when playback reaches the end.\n const frameWindow = Math.max(1000 / Math.max(protocol.fps || 30, 1), 1)\n return Math.max(total - frameWindow, 0)\n }\n\n async function getOpfsFile(url: string) {\n const dir = opts.resourceDir ?? DEFAULT_RES_DIR\n try {\n const key = getResourceKey(url)\n if (!key)\n return undefined\n const file = opfsFile(`${dir}/${key}`, 'r')\n if (await file.exists())\n return file\n }\n catch {\n return undefined\n }\n return undefined\n }\n\n function shouldUseResourceManager(url: string) {\n if (!url)\n return false\n if (url.startsWith('data:') || url.startsWith('blob:'))\n return false\n return true\n }\n\n function freezeVideoEntries() {\n for (const [id, entry] of videoEntries) {\n if (entry.kind === 'mp4clip') {\n entry.clip.destroy()\n videoEntries.set(id, {\n kind: 'frozen',\n canvas: entry.canvas,\n texture: entry.texture,\n sprite: entry.sprite,\n meta: entry.meta,\n })\n continue\n }\n\n if (entry.kind === 'element')\n entry.video.pause()\n }\n }\n\n function destroyVideoEntry(entry: VideoEntry) {\n if (entry.kind === 'mp4clip') {\n entry.clip.destroy()\n return\n }\n\n if (entry.kind === 'frozen')\n return\n\n entry.video.pause()\n const objectUrl = videoObjectUrls.get(entry.video)\n if (objectUrl) {\n URL.revokeObjectURL(objectUrl)\n videoObjectUrls.delete(entry.video)\n }\n entry.video.removeAttribute('src')\n entry.video.load()\n }\n\n function waitForMediaEvent(target: HTMLMediaElement, type: string, timeoutMs = 1000) {\n return new Promise<void>((resolve, reject) => {\n const timer = window.setTimeout(() => {\n cleanup()\n reject(new Error(`Timed out waiting for media event: ${type}`))\n }, timeoutMs)\n\n const onOk = () => {\n cleanup()\n resolve()\n }\n const onErr = () => {\n cleanup()\n const mediaError = target.error ? `${target.error.code}` : 'unknown'\n reject(new Error(`Media error (${mediaError}) while waiting for ${type}`))\n }\n const cleanup = () => {\n window.clearTimeout(timer)\n target.removeEventListener(type, onOk)\n target.removeEventListener('error', onErr)\n }\n\n target.addEventListener(type, onOk, { once: true })\n target.addEventListener('error', onErr, { once: true })\n })\n }\n\n async function loadVideoSpriteViaMP4Clip(url: string, reuse?: { sprite: Sprite, oldTexture?: Texture }): Promise<VideoEntry | undefined> {\n let file: ReturnType<typeof opfsFile> | undefined\n if (shouldUseResourceManager(url)) {\n file = await getOpfsFile(url)\n if (!file) {\n await resourceManager.add(url).catch(() => {})\n file = await getOpfsFile(url)\n }\n }\n\n let clip: MP4Clip | undefined\n try {\n if (file) {\n clip = new MP4Clip(file)\n }\n else {\n const res = await fetch(url)\n if (!res.body) {\n const buffer = await res.arrayBuffer()\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(new Uint8Array(buffer))\n controller.close()\n },\n })\n clip = new MP4Clip(stream)\n }\n else {\n clip = new MP4Clip(res.body)\n }\n }\n\n await clip.ready\n\n const { width, height } = clip.meta\n const canvas = document.createElement('canvas')\n canvas.width = width || 1\n canvas.height = height || 1\n const texture = Texture.from(canvas)\n const sprite = reuse?.sprite ?? new Sprite(texture)\n if (reuse?.sprite) {\n reuse.sprite.texture = texture\n reuse.oldTexture?.destroy(true)\n }\n\n return { kind: 'mp4clip', clip, canvas, texture, sprite, meta: { width, height } }\n }\n catch (err) {\n clip?.destroy()\n throw err\n }\n }\n\n function inferUrlMediaType(url: string): 'video' | 'image' | 'audio' | 'unknown' {\n const raw = url.split('#')[0]!.split('?')[0]!\n const ext = raw.split('/').pop()?.split('.').pop()?.toLowerCase() ?? ''\n if (['mp4', 'm4v', 'mov', 'webm'].includes(ext))\n return 'video'\n if (['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg', 'avif'].includes(ext))\n return 'image'\n if (['mp3', 'wav', 'aac', 'm4a', 'ogg', 'flac'].includes(ext))\n return 'audio'\n return 'unknown'\n }\n\n function isRenderableVideoUrl(url: string) {\n const kind = inferUrlMediaType(url)\n if (kind === 'image' || kind === 'audio')\n return false\n // Treat unknown as video to support blob URLs or extension-less endpoints.\n return true\n }\n\n async function loadVideoSpriteViaElement(url: string, reuse?: { sprite: Sprite, oldTexture?: Texture }): Promise<VideoEntry | undefined> {\n const video = document.createElement('video')\n video.crossOrigin = 'anonymous'\n video.muted = false\n video.playsInline = true\n video.preload = 'metadata'\n video.src = url\n video.load()\n\n try {\n await waitForMediaEvent(video, 'loadedmetadata', 15000)\n }\n catch (err) {\n video.pause()\n const objectUrl = videoObjectUrls.get(video)\n if (objectUrl) {\n URL.revokeObjectURL(objectUrl)\n videoObjectUrls.delete(video)\n }\n video.removeAttribute('src')\n video.load()\n throw err\n }\n\n const width = video.videoWidth || 1\n const height = video.videoHeight || 1\n\n const canvas = document.createElement('canvas')\n canvas.width = width\n canvas.height = height\n const texture = Texture.from(canvas)\n const sprite = reuse?.sprite ?? new Sprite(texture)\n if (reuse?.sprite) {\n reuse.sprite.texture = texture\n reuse.oldTexture?.destroy(true)\n }\n\n return { kind: 'element', video, canvas, texture, sprite, meta: { width, height } }\n }\n\n\n async function updateVideoElementFrame(entry: Extract<VideoEntry, { kind: 'element' }>, opts: { targetSec: number, playbackRate: number, volume?: number }) {\n const { video, canvas, texture } = entry\n\n video.playbackRate = Number.isFinite(opts.playbackRate) && opts.playbackRate > 0 ? opts.playbackRate : 1\n video.volume = Math.max(0, Math.min(1, opts.volume ?? 1))\n\n if (isPlaying.value)\n video.play().catch(() => {})\n else\n video.pause()\n\n const duration = Number.isFinite(video.duration) && video.duration > 0 ? video.duration : null\n const targetSec = duration ? Math.min(opts.targetSec, Math.max(duration - 0.03, 0)) : opts.targetSec\n\n const current = video.currentTime\n const drift = Math.abs(current - targetSec)\n const driftThreshold = isPlaying.value ? 0.25 : 0.03\n if (Number.isFinite(current) && drift > driftThreshold) {\n try {\n video.currentTime = targetSec\n }\n catch {\n // ignore seek errors for not-yet-ready media\n }\n await waitForMediaEvent(video, 'seeked', 250).catch(() => {})\n }\n\n if (video.readyState < 2) {\n // Avoid blocking the render queue for too long.\n await waitForMediaEvent(video, 'canplay', 250).catch(() => {})\n if (video.readyState < 2)\n return\n }\n\n const ctx = canvas.getContext('2d')\n if (!ctx)\n return\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height)\n refreshCanvasTexture(texture)\n }\n\n function loadImageTexture(url: string): Promise<Texture | undefined> {\n return new Promise((resolve) => {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.onload = () => resolve(Texture.from(img))\n img.onerror = () => {\n console.warn('[renderer] failed to load image', url)\n resolve(undefined)\n }\n img.src = url\n })\n }\n\n function refreshCanvasTexture(texture: Texture) {\n const source = texture.source\n if ('update' in source && typeof source.update === 'function') {\n source.update()\n return\n }\n\n if (typeof texture.update === 'function')\n texture.update()\n }\n\n function destroy() {\n pause()\n renderGeneration += 1\n scope.stop()\n clearDisplays()\n layer.destroy({ children: true })\n displayCache.clear()\n displayLoading.clear()\n resourceWarmUp.clear()\n if (!opts.app)\n app.destroy()\n \n audioManager.destroy()\n }\n\n if (opts.autoPlay)\n play()\n\n return {\n app,\n layer,\n currentTime,\n duration,\n isPlaying,\n play,\n pause,\n tick,\n seek,\n renderAt,\n destroy,\n }\n}\n\nfunction createRenderQueue(job: () => Promise<void> | void) {\n let queued = false\n let running = false\n let pending: Promise<void> | null = null\n let resolvePending: (() => void) | null = null\n\n const run = async () => {\n if (!pending) {\n pending = new Promise((resolve) => {\n resolvePending = resolve\n })\n }\n const done = pending\n if (running) {\n queued = true\n return done\n }\n running = true\n do {\n queued = false\n await job()\n } while (queued)\n running = false\n resolvePending?.()\n pending = null\n resolvePending = null\n return done\n }\n\n return run\n}\n","import type { file as opfsFile } from 'opfs-tools'\nimport type { ICombinatorOpts } from '@webav/av-cliper'\nimport { Combinator, MP4Clip, OffscreenSprite } from '@webav/av-cliper'\n\nexport type VideoConcatSource =\n | string\n | ReadableStream<Uint8Array>\n | Blob\n | ReturnType<typeof opfsFile>\n\nexport interface ConcatVideoSource {\n source: VideoConcatSource\n}\n\nexport interface ConcatVideoOptions extends Omit<ICombinatorOpts, 'width' | 'height'> {\n width?: number\n height?: number\n onProgress?: (progress: number) => void\n}\n\nexport interface ConcatVideoResult {\n stream: ReadableStream<Uint8Array>\n width: number\n height: number\n durationMs: number\n destroy: () => void\n}\n\nfunction isOpfsFile(value: unknown): value is ReturnType<typeof opfsFile> {\n return typeof value === 'object'\n && value !== null\n && 'createReader' in value\n && 'getSize' in value\n}\n\nfunction isReadableStream(value: unknown): value is ReadableStream<Uint8Array> {\n return typeof ReadableStream !== 'undefined' && value instanceof ReadableStream\n}\n\nfunction normalizeInput(input: ConcatVideoSource | VideoConcatSource): ConcatVideoSource {\n if (typeof input === 'string' || input instanceof Blob || isOpfsFile(input) || isReadableStream(input))\n return { source: input }\n return input\n}\n\nasync function toClipSource(source: VideoConcatSource): Promise<ReadableStream<Uint8Array> | ReturnType<typeof opfsFile>> {\n if (typeof source === 'string') {\n const res = await fetch(source)\n if (!res.body)\n throw new Error('concatVideos: unable to read video stream from url')\n return res.body\n }\n\n if (source instanceof Blob)\n return source.stream() as ReadableStream<Uint8Array>\n\n return source\n}\n\nfunction wrapStreamWithCleanup(\n stream: ReadableStream<Uint8Array>,\n cleanup: () => void,\n): ReadableStream<Uint8Array> {\n let cleaned = false\n const finalize = () => {\n if (cleaned)\n return\n cleaned = true\n cleanup()\n }\n\n const reader = stream.getReader()\n return new ReadableStream({\n async pull(controller) {\n const { done, value } = await reader.read()\n if (done) {\n finalize()\n controller.close()\n return\n }\n controller.enqueue(value)\n },\n async cancel(reason) {\n try {\n await reader.cancel(reason)\n }\n finally {\n finalize()\n }\n },\n })\n}\n\nexport async function concatVideos(\n inputs: Array<ConcatVideoSource | VideoConcatSource>,\n opts: ConcatVideoOptions = {},\n): Promise<ConcatVideoResult> {\n if (inputs.length === 0)\n throw new Error('concatVideos: expected at least one source')\n\n const {\n onProgress,\n width: requestedWidth,\n height: requestedHeight,\n ...combinatorOpts\n } = opts\n\n const normalized = inputs.map(normalizeInput)\n\n const [first, ...rest] = normalized\n const firstSource = await toClipSource(first.source)\n const firstClip = new MP4Clip(firstSource)\n await firstClip.ready\n\n const width = requestedWidth ?? Math.round(firstClip.meta.width || 0)\n const height = requestedHeight ?? Math.round(firstClip.meta.height || 0)\n if (!width || !height)\n {\n firstClip.destroy()\n throw new Error('concatVideos: output width/height is required')\n }\n\n const combinator = new Combinator({\n ...combinatorOpts,\n width,\n height,\n })\n\n if (onProgress)\n combinator.on('OutputProgress', onProgress)\n\n let offset = 0\n\n const addClip = async (clip: MP4Clip) => {\n const duration = clip.meta.duration\n if (!Number.isFinite(duration) || duration <= 0) {\n clip.destroy()\n throw new Error('concatVideos: invalid clip duration')\n }\n\n const sprite = new OffscreenSprite(clip)\n try {\n await sprite.ready\n sprite.rect.x = 0\n sprite.rect.y = 0\n sprite.rect.w = width\n sprite.rect.h = height\n sprite.time.offset = offset\n sprite.time.duration = duration\n\n await combinator.addSprite(sprite)\n offset += duration\n }\n finally {\n sprite.destroy()\n }\n }\n\n try {\n await addClip(firstClip)\n for (const entry of rest) {\n const source = await toClipSource(entry.source)\n const clip = new MP4Clip(source)\n await clip.ready\n await addClip(clip)\n }\n }\n catch (err) {\n combinator.destroy()\n throw err\n }\n\n const maxTime = offset\n const stream = combinator.output({ maxTime })\n const destroy = () => {\n combinator.destroy()\n }\n\n return {\n stream: wrapStreamWithCleanup(stream, destroy),\n width,\n height,\n durationMs: Math.round(maxTime / 1000),\n destroy,\n }\n}\n","import type { IVideoProtocol } from '@video-editor/shared'\nimport type { IClip } from '@webav/av-cliper'\nimport type { ApplicationOptions } from 'pixi.js'\nimport { Application } from 'pixi.js'\nimport type { RendererOptions } from './renderer-core'\nimport { createRenderer } from './renderer-core'\nimport { computeDuration } from './helpers'\n\nexport interface ProtocolVideoClipOptions {\n width?: number\n height?: number\n fps?: number\n appOptions?: Partial<ApplicationOptions>\n rendererOptions?: Partial<Omit<RendererOptions, 'protocol' | 'app' | 'appOptions'>>\n}\n\ninterface ClipMeta {\n width: number\n height: number\n duration: number\n}\n\nexport class ProtocolVideoClip implements IClip {\n readonly ready: Promise<ClipMeta>\n meta: ClipMeta\n\n private readonly protocol: IVideoProtocol\n private readonly options: ProtocolVideoClipOptions\n private renderer?: Awaited<ReturnType<typeof createRenderer>>\n private app?: Application\n private destroyed = false\n\n constructor(protocol: IVideoProtocol, options: ProtocolVideoClipOptions = {}) {\n this.protocol = protocol\n this.options = options\n\n const width = options.width ?? protocol.width\n const height = options.height ?? protocol.height\n const durationMs = computeDuration(protocol)\n this.meta = {\n width,\n height,\n duration: Math.max(0, Math.round(durationMs * 1000)),\n }\n\n this.ready = this.init()\n }\n\n private async init() {\n const width = this.options.width ?? this.protocol.width\n const height = this.options.height ?? this.protocol.height\n if (!width || !height)\n throw new Error('ProtocolVideoClip: output width/height is required')\n\n const app = new Application()\n await app.init({\n width,\n height,\n backgroundAlpha: 0,\n ...this.options.appOptions,\n })\n app.ticker.stop()\n this.app = app\n\n const rendererOptions = this.options.rendererOptions ?? {}\n const renderer = await createRenderer({\n protocol: this.protocol,\n app,\n ...rendererOptions,\n autoPlay: false,\n freezeOnPause: false,\n manualRender: true,\n videoSourceMode: rendererOptions.videoSourceMode ?? 'mp4clip',\n })\n this.renderer = renderer\n\n const durationMs = renderer.duration.value\n this.meta = {\n width: app.renderer.width,\n height: app.renderer.height,\n duration: Math.max(0, Math.round(durationMs * 1000)),\n }\n\n return this.meta\n }\n\n async tick(time: number): Promise<{\n video?: VideoFrame | ImageBitmap | null\n audio?: Float32Array[]\n state: 'done' | 'success'\n }> {\n const emptyAudio: Float32Array[] = []\n if (this.destroyed)\n return { audio: emptyAudio, state: 'done' }\n\n await this.ready\n if (!this.renderer)\n return { audio: emptyAudio, state: 'done' }\n\n const durationUs = this.meta.duration\n if (time >= durationUs)\n return { audio: emptyAudio, state: 'done' }\n\n const clampedUs = Math.max(0, Math.min(time, durationUs))\n await this.renderer.renderAt(clampedUs / 1000)\n\n const frame = new VideoFrame(this.renderer.app.canvas, {\n timestamp: time,\n })\n\n return {\n video: frame,\n audio: emptyAudio,\n state: 'success',\n }\n }\n\n async clone(): Promise<this> {\n const copy = new ProtocolVideoClip(this.protocol, this.options) as this\n await copy.ready\n return copy\n }\n\n destroy() {\n if (this.destroyed)\n return\n this.destroyed = true\n this.renderer?.destroy()\n this.app?.destroy(true)\n }\n}\n","import type { IVideoProtocol } from '@video-editor/shared'\nimport type { ICombinatorOpts } from '@webav/av-cliper'\nimport { Combinator, OffscreenSprite } from '@webav/av-cliper'\nimport { ProtocolVideoClip } from './protocol-clip'\nimport type { ProtocolVideoClipOptions } from './protocol-clip'\n\nexport interface ComposeProtocolOptions extends Omit<ICombinatorOpts, 'width' | 'height' | 'fps'> {\n width?: number\n height?: number\n fps?: number\n onProgress?: (progress: number) => void\n clipOptions?: ProtocolVideoClipOptions\n audioSprites?: (protocol: IVideoProtocol) => Promise<OffscreenSprite[]>\n}\n\nexport interface ComposeProtocolResult {\n stream: ReadableStream<Uint8Array>\n width: number\n height: number\n durationMs: number\n destroy: () => void\n}\n\nfunction wrapStreamWithCleanup(\n stream: ReadableStream<Uint8Array>,\n cleanup: () => void,\n): ReadableStream<Uint8Array> {\n let cleaned = false\n const finalize = () => {\n if (cleaned)\n return\n cleaned = true\n cleanup()\n }\n\n const reader = stream.getReader()\n return new ReadableStream({\n async pull(controller) {\n const { done, value } = await reader.read()\n if (done) {\n finalize()\n controller.close()\n return\n }\n controller.enqueue(value)\n },\n async cancel(reason) {\n try {\n await reader.cancel(reason)\n }\n finally {\n finalize()\n }\n },\n })\n}\n\nexport async function composeProtocol(\n protocol: IVideoProtocol,\n opts: ComposeProtocolOptions = {},\n): Promise<ComposeProtocolResult> {\n const {\n width: requestedWidth,\n height: requestedHeight,\n fps: requestedFps,\n onProgress,\n clipOptions,\n audioSprites,\n ...combinatorOpts\n } = opts\n\n const width = requestedWidth ?? protocol.width\n const height = requestedHeight ?? protocol.height\n if (!width || !height)\n throw new Error('composeProtocol: output width/height is required')\n\n const fps = requestedFps ?? protocol.fps\n\n const hasAudioSprites = typeof audioSprites === 'function'\n const audio = combinatorOpts.audio ?? (hasAudioSprites ? undefined : false)\n const combinator = new Combinator({\n ...combinatorOpts,\n audio,\n width,\n height,\n fps,\n })\n\n if (onProgress)\n combinator.on('OutputProgress', onProgress)\n\n let clip: ProtocolVideoClip | undefined\n let sprite: OffscreenSprite | undefined\n try {\n clip = new ProtocolVideoClip(protocol, {\n width,\n height,\n fps,\n ...clipOptions,\n rendererOptions: {\n warmUpResources: false,\n ...clipOptions?.rendererOptions,\n },\n })\n await clip.ready\n\n sprite = new OffscreenSprite(clip)\n await sprite.ready\n sprite.time.offset = 0\n sprite.time.duration = clip.meta.duration\n sprite.rect.x = 0\n sprite.rect.y = 0\n sprite.rect.w = clip.meta.width\n sprite.rect.h = clip.meta.height\n\n await combinator.addSprite(sprite, { main: true })\n\n if (audioSprites) {\n const sprites = await audioSprites(protocol)\n for (const extra of sprites)\n await combinator.addSprite(extra)\n }\n }\n catch (err) {\n combinator.destroy()\n throw err\n }\n\n const maxTime = clip?.meta.duration ?? 0\n if (!maxTime)\n throw new Error('composeProtocol: protocol has no duration')\n\n const stream = combinator.output({ maxTime })\n const destroy = () => {\n sprite?.destroy()\n clip?.destroy()\n combinator.destroy()\n }\n\n return {\n stream: wrapStreamWithCleanup(stream, destroy),\n width,\n height,\n durationMs: Math.round(maxTime / 1000),\n destroy,\n }\n}\n"],"names":["createApp","opts","app","Application","resolveFillSize","mode","sourceWidth","sourceHeight","stageWidth","stageHeight","safeSourceWidth","safeSourceHeight","sourceRatio","stageRatio","computeSegmentLayout","segment","fillMode","width","height","transform","px","py","sx","sy","rotation","finalWidth","finalHeight","centerX","centerY","collectResourceUrls","protocol","urls","track","collectActiveSegments","at","active","trackIndex","childIndex","a","b","aTrack","bTrack","aIsMain","bIsMain","total","aOrder","bOrder","applyDisplayProps","display","opacity","readOpacity","Sprite","layout","Texture","placeholderTexture","Graphics","stringToColor","hasOpacity","placeholder","key","url","g","color","svg","hash","computeDuration","endTimes","seg","clamp","num","min","max","cloneProtocol","raw","toRaw","AudioManager","currentTime","isPlaying","activeSegments","activeAudioKeys","activeVideoKeys","loop","source","id","audio","sampleRate","volume","gainNode","state","clip","startUs","fps","existing","elapsedUs","expectedUs","entry","gain","offset","playRate","relativeMs","sourceOffsetMs","existingLoop","sourceDurationMs","maxSourceOffsetMs","baseVolume","segmentDuration","fadeInDuration","fadeOutDuration","volumeMultiplier","timeUntilEnd","stopped","timeUs","startAt","first","stepUs","sources","timer","maxSourceOffsetUs","initialized","sourceStepUs","play","processedAudio","outputLength","channel","output","i","srcIndex","srcIndexFloor","srcIndexCeil","t","channels","len","buffer","data","nextStart","duration","index","map","cached","loading","loadingPromise","response","WebAVAudioClip","e","meta","audioSampleRate","DEFAULT_TEXT_BITMAP_CACHE_LIMIT","textBitmapCache","textBitmapCacheLimit","touchCache","value","trimCache","oldestKey","bitmap","buildTextContent","texts","item","buildTextCss","text","fontFamily","fontSize","fontWeight","fontStyle","fill","align","css","angle","offsetX","offsetY","blur","renderTextBitmap","content","cssText","renderTxt2ImgBitmap","DEFAULT_RES_DIR","createRenderer","validator","createValidator","protocolInput","isRef","shallowRef","validatedProtocol","unref","create2dApp","layer","Container","resourceManager","createResourceManager","resourceWarmUp","displayCache","displayLoading","mp4ClipUnsupportedKeys","mp4ClipErrorLoggedKeys","videoSourceMode","videoEntries","videoObjectUrls","ref","computed","audioManager","rafId","lastTickAt","renderGeneration","renderScene","task","generation","renderAt","normalizeRenderTime","renders","isVideoSegment","updateVideoFrame","cleaned","queueRender","createRenderQueue","getDisplayForSegment","scope","effectScope","watch","err","clearDisplays","warmUpResources","cleanupCache","clampCurrentTime","nextDuration","inferUrlMediaType","shouldUseResourceManager","ids","child","destroyVideoEntry","pause","freezeVideoEntries","tick","deltaMs","now","delta","seek","time","promise","loadDisplay","isRenderableVideoUrl","sprite","loadVideoSprite","texture","loadTexture","buildTextDisplay","isDataUrl","isHttp","res","loadImageTexture","urlKey","getResourceKey","allowMp4Clip","allowVideoElement","spriteFromElement","loadVideoSpriteViaElement","spriteFromClip","loadVideoSpriteViaMP4Clip","isMp4ClipUnsupported","msg","offsetMs","relativeUs","revived","loadVideoEntry","ctx","refreshCanvasTexture","replacement","elementErr","relativeSec","updateVideoElementFrame","reuse","fromClip","frameWindow","getOpfsFile","dir","file","opfsFile","objectUrl","waitForMediaEvent","target","type","timeoutMs","resolve","reject","cleanup","onOk","onErr","mediaError","MP4Clip","stream","controller","canvas","ext","kind","video","targetSec","current","drift","driftThreshold","img","destroy","job","queued","running","pending","resolvePending","done","isOpfsFile","isReadableStream","normalizeInput","input","toClipSource","wrapStreamWithCleanup","finalize","reader","reason","concatVideos","inputs","onProgress","requestedWidth","requestedHeight","combinatorOpts","normalized","rest","firstSource","firstClip","combinator","Combinator","addClip","OffscreenSprite","maxTime","ProtocolVideoClip","options","durationMs","rendererOptions","renderer","emptyAudio","durationUs","clampedUs","copy","composeProtocol","requestedFps","clipOptions","audioSprites","hasAudioSprites","sprites","extra"],"mappings":";;;;;AAUA,eAAsBA,GAAUC,GAAoC;AAClE,QAAMC,IAAM,IAAIC,GAAA;AAEhB,eAAMD,EAAI,KAAK,EAAE,UAAU,QAAQ,iBAAiB,GAAG,GAAGD,GAAM,GAMzDC;AACT;ACVO,SAASE,GACdC,GACAC,GACAC,GACAC,GACAC,GACA;AACA,QAAMC,IAAkBJ,KAAeE,GACjCG,IAAmBJ,KAAgBE;AACzC,MAAI,CAACC,KAAmB,CAACC;AACvB,WAAO,EAAE,OAAOH,GAAY,QAAQC,EAAA;AAEtC,QAAMG,IAAcF,IAAkBC,GAChCE,IAAaL,IAAaC;AAEhC,UAAQJ,GAAA;AAAA,IACN,KAAK;AACH,aAAO,EAAE,OAAOK,GAAiB,QAAQC,EAAA;AAAA,IAC3C,KAAK;AACH,aAAIC,IAAcC,IACT,EAAE,OAAOJ,IAAcG,GAAa,QAAQH,EAAA,IAC9C,EAAE,OAAOD,GAAY,QAAQA,IAAaI,EAAA;AAAA,IACnD,KAAK;AACH,aAAO,EAAE,OAAOJ,GAAY,QAAQC,EAAA;AAAA,IAEtC;AACE,aAAIG,IAAcC,IACT,EAAE,OAAOL,GAAY,QAAQA,IAAaI,EAAA,IAC5C,EAAE,OAAOH,IAAcG,GAAa,QAAQH,EAAA;AAAA,EAAY;AAErE;AAEO,SAASK,GACdC,GACAP,GACAC,GACAH,GACAC,GACe;AACf,QAAMS,IAAW,cAAcD,IAAUA,EAAQ,WAAW,QACtD,EAAE,OAAAE,GAAO,QAAAC,EAAA,IAAWd;AAAA,IACxBY;AAAA,IACAV;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,EAAA,GAGIU,IAAY,eAAeJ,IAAUA,EAAQ,YAAY,QACzD,CAACK,GAAIC,CAAE,IAAIF,GAAW,YAAY,CAAC,GAAG,CAAC,GACvC,CAACG,GAAIC,CAAE,IAAIJ,GAAW,SAAS,CAAC,GAAG,CAAC,GACpCK,IAAWL,GAAW,WAAW,CAAC,KAAK,GAEvCM,IAAaR,IAAQK,GACrBI,IAAcR,IAASK,GACvBI,IAAUnB,IAAa,IAAKY,IAAKZ,IAAc,GAC/CoB,IAAUnB,IAAc,IAAKY,IAAKZ,IAAe;AAEvD,SAAO;AAAA,IACL,OAAOgB;AAAA,IACP,QAAQC;AAAA,IACR,SAAAC;AAAA,IACA,SAAAC;AAAA,IACA,aAAcJ,IAAW,MAAO,KAAK;AAAA,EAAA;AAEzC;ACpEO,SAASK,GAAoBC,GAA0B;AAC5D,QAAMC,wBAAW,IAAA;AACjB,aAAWC,KAASF,EAAS;AAC3B,eAAWf,KAAWiB,EAAM;AAC1B,MAAIjB,EAAQ,OACVgB,EAAK,IAAIhB,EAAQ,GAAG;AAG1B,SAAOgB;AACT;AAEO,SAASE,GAAsBH,GAA0BI,GAAY;AAC1E,QAAMC,IAA8E,CAAA;AACpF,SAAAL,EAAS,OAAO,QAAQ,CAACE,GAAOI,MAAe;AAC7C,IAAAJ,EAAM,SAAS,QAAQ,CAACjB,GAASsB,MAAe;AAC9C,MAAItB,EAAQ,aAAamB,KAAMA,IAAKnB,EAAQ,WAC1CoB,EAAO,KAAK,EAAE,SAAApB,GAAS,YAAAqB,GAAY,YAAAC,GAAY;AAAA,IACnD,CAAC;AAAA,EACH,CAAC,GAEMF,EAAO,KAAK,CAACG,GAAGC,MAAM;AAC3B,UAAMC,IAASV,EAAS,OAAOQ,EAAE,UAAU,GACrCG,IAASX,EAAS,OAAOS,EAAE,UAAU,GACrCG,IAAUF,GAAQ,cAAc,YAAaA,EAAuC,QACpFG,IAAUF,GAAQ,cAAc,YAAaA,EAAuC,QACpFG,IAAQd,EAAS,OAAO,QACxBe,IAASH,IAAU,IAAIE,IAAQN,EAAE,YACjCQ,IAASH,IAAU,IAAIC,IAAQL,EAAE;AACvC,WAAIM,MAAWC,IACND,IAASC,IACdR,EAAE,eAAeC,EAAE,aACdD,EAAE,aAAaC,EAAE,aACnBD,EAAE,aAAaC,EAAE;AAAA,EAC1B,CAAC;AACH;AAEO,SAASQ,GAAkBC,GAA4BjC,GAAuBE,GAAeC,GAAgB;AAClH,QAAM+B,IAAUC,GAAYnC,CAAO,GAC7BT,IAAc0C,aAAmBG,KAASH,EAAQ,QAAQ,SAAS/B,GACnEV,IAAeyC,aAAmBG,KAASH,EAAQ,QAAQ,UAAU9B,GACrEkC,IAAStC,GAAqBC,GAASE,GAAOC,GAAQZ,GAAaC,CAAY;AAErF,EAAIyC,aAAmBG,KACrBH,EAAQ,OAAO,IAAI,GAAG,GACtBA,EAAQ,QAAQI,EAAO,OACvBJ,EAAQ,SAASI,EAAO,QACxBJ,EAAQ,SAAS,IAAII,EAAO,SAASA,EAAO,OAAO,GACnDJ,EAAQ,WAAWI,EAAO,aACdJ,EAAQ,QAAQ,QACvB,mBAAmB,SAAS,MAAM;AAErC,IAAAA,EAAQ,UAAUK,EAAQ,KAAKC,GAAmBrC,GAAOC,CAAM,CAAC;AAAA,EAClE,GAAG,EAAE,MAAM,IAAM,KAEV8B,aAAmBO,OAC1BP,EAAQ,MAAA,GACRA,EACG,KAAK,GAAG,GAAGI,EAAO,OAAOA,EAAO,MAAM,EACtC,KAAK,EAAE,OAAOI,GAAc,SAASzC,KAAW,OAAOA,EAAQ,OAAQ,WAAWA,EAAQ,MAAMA,EAAQ,WAAW,GAAG,OAAO0C,GAAW1C,CAAO,IAAIkC,IAAU,MAAM,GACtKD,EAAQ,MAAM,IAAII,EAAO,QAAQ,GAAGA,EAAO,SAAS,CAAC,GACrDJ,EAAQ,SAAS,IAAII,EAAO,SAASA,EAAO,OAAO,GACnDJ,EAAQ,WAAWI,EAAO,cAG5BJ,EAAQ,QAAQC;AAClB;AAEO,SAASS,EAAYC,GAAaC,GAAc;AACrD,QAAMC,IAAI,IAAIN,GAAA;AACd,SAAAM,EAAE,KAAK,GAAG,GAAG,IAAI,EAAE,EAAE,KAAK,EAAE,OAAOL,GAAcI,KAAOD,CAAG,GAAG,OAAO,GAAG,GACjEE;AACT;AAEO,SAASP,GAAmBrC,GAAeC,GAAgB4C,GAAgB;AAChF,QAAMC,IAAM,kDAAkD9C,CAAK,aAAaC,CAAM;AACtF,SAAO,6BAA6B,KAAK6C,CAAG,CAAC;AAC/C;AAEO,SAASP,GAAcG,GAAa;AACzC,MAAIK,IAAO;AACX,WAAS,IAAI,GAAG,IAAIL,EAAI,QAAQ;AAC9B,IAAAK,IAAOL,EAAI,WAAW,CAAC,MAAMK,KAAQ,KAAKA;AAC5C,SAAOA,IAAO;AAChB;AAEO,SAASC,EAAgBnC,GAA0B;AACxD,QAAMoC,IAAWpC,EAAS,OAAO,QAAQ,CAAAE,MAASA,EAAM,SAAS,IAAI,CAAAmC,MAAOA,EAAI,OAAO,CAAC;AACxF,SAAOD,EAAS,SAAS,KAAK,IAAI,GAAGA,CAAQ,IAAI;AACnD;AAEO,SAASE,EAAMC,GAAaC,GAAaC,GAAa;AAC3D,SAAO,KAAK,IAAI,KAAK,IAAIF,GAAKC,CAAG,GAAGC,CAAG;AACzC;AAEO,SAASC,GAAc1C,GAA0B;AACtD,QAAM2C,IAAMC,GAAM5C,CAAQ;AAE1B,SAAO,KAAK,MAAM,KAAK,UAAU2C,CAAG,CAAC;AACvC;AAEA,SAAShB,GAAW1C,GAAuE;AACzF,SAAO,aAAaA;AACtB;AAEA,SAASmC,GAAYnC,GAAuB;AAC1C,SAAI0C,GAAW1C,CAAO,KAAK,OAAOA,EAAQ,WAAY,WAC7CA,EAAQ,UACV;AACT;ACrFO,MAAM4D,GAAa;AAAA,EAChB;AAAA,EACA,4BAAY,IAAA;AAAA,EACZ,mCAAmB,IAAA;AAAA,EACnB,iCAAiB,IAAA;AAAA,EACjB,wCAAwB,IAAA;AAAA,EACxB,+BAAe,IAAA;AAAA,EACf,gCAAgB,IAAA;AAAA,EAChB,iCAAiB,IAAA;AAAA,EACjB,+BAAe,IAAA;AAAA,EACf;AAAA,EACA,WAAW;AAAA,EAEnB,YAAY7C,GAA0B;AACpC,SAAK,WAAWA,GAChB,KAAK,MAAM,KAAK,OAAO,gBAAiB,OAAe,oBAAA;AAAA,EACzD;AAAA,EAEO,YAAYA,GAA0B;AAC3C,SAAK,WAAWA;AAAA,EAClB;AAAA,EAEA,MAAa,KAAK8C,GAAqBC,GAAoB;AACzD,QAAI,CAACA,GAAW;AAEd,WAAK,QAAA,GACL,KAAK,WAAWD;AAChB;AAAA,IACF;AAEA,IAAI,KAAK,IAAI,UAAU,eACrB,MAAM,KAAK,IAAI,OAAA,EAAS,MAAM,MAAM;AAAA,IAAC,CAAC,GAEpC,KAAK,IAAIA,IAAc,KAAK,QAAQ,IAAI,OAC1C,KAAK,QAAA,GACP,KAAK,WAAWA;AAEhB,UAAME,IAAiB7C,GAAsB,KAAK,UAAU2C,CAAW,GACjEG,wBAAsB,IAAA,GACtBC,wBAAsB,IAAA;AAE5B,eAAW,EAAE,SAAAjE,EAAA,KAAa+D;AACxB,UAAI/D,EAAQ,gBAAgB,SAAS;AACnC,cAAM4C,IAAM,KAAK,SAAS5C,EAAQ,EAAE;AACpC,QAAAgE,EAAgB,IAAIpB,CAAG,GACvB,KAAK,gBAAgB5C,GAA0B6D,CAAW;AAAA,MAC5D,WACS7D,EAAQ,gBAAgB,YAAY,UAAUA,KAAWA,EAAQ,SAAS,SAAS;AAC1F,cAAM4C,IAAM,KAAK,SAAS5C,EAAQ,EAAE;AACpC,QAAAiE,EAAgB,IAAIrB,CAAG;AAAA,MACzB;AAGF,eAAW,CAACA,GAAKsB,CAAI,KAAK,KAAK;AAC7B,UAAI,CAACF,EAAgB,IAAIpB,CAAG,GAAG;AAC7B,QAAAsB,EAAK,KAAA;AAEL,mBAAWC,KAAUD,EAAK;AACxB,cAAI;AACF,YAAAC,EAAO,KAAA,GACPA,EAAO,WAAA;AAAA,UACT,QACU;AAAA,UAEV;AAEF,aAAK,WAAW,OAAOvB,CAAG,GAC1B,KAAK,kBAAkB,OAAOA,CAAG,GACjC,KAAK,WAAW,OAAOA,CAAG;AAAA,MAC5B;AAGF,eAAW,CAACA,GAAKsB,CAAI,KAAK,KAAK;AAC7B,UAAI,CAACD,EAAgB,IAAIrB,CAAG,GAAG;AAC7B,QAAAsB,EAAK,KAAA;AAEL,mBAAWC,KAAUD,EAAK;AACxB,cAAI;AACF,YAAAC,EAAO,KAAA,GACPA,EAAO,WAAA;AAAA,UACT,QACU;AAAA,UAEV;AAEF,aAAK,SAAS,OAAOvB,CAAG,GACxB,KAAK,UAAU,OAAOA,CAAG,GACzB,KAAK,SAAS,OAAOA,CAAG;AAAA,MAC1B;AAIF,eAAWA,KAAO,KAAK,UAAU,KAAA;AAC/B,MAAI,CAACqB,EAAgB,IAAIrB,CAAG,KAAK,CAAC,KAAK,SAAS,IAAIA,CAAG,MACrD,KAAK,UAAU,OAAOA,CAAG,GACzB,KAAK,SAAS,OAAOA,CAAG;AAAA,EAG9B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,mBAAmBwB,GAAYC,GAAmCC,GAAoB;AAC3F,QAAI,CAACD,KAASA,EAAM,WAAW,KAAK,CAACA,EAAM,CAAC,GAAG;AAC7C;AAEF,UAAMzB,IAAM,KAAK,SAASwB,CAAE,GACtBG,IAAS,KAAK,iBAAiBH,CAAE,GACjCI,IAAW,KAAK,gBAAgB,KAAK,UAAU5B,GAAK2B,CAAM;AAEhE,IAAI,KAAK,IAAI,UAAU,eACrB,KAAK,IAAI,OAAA,EAAS,MAAM,MAAM;AAAA,IAAC,CAAC;AAGlC,QAAIE,IAAQ,KAAK,UAAU,IAAI7B,CAAG;AAClC,IAAK6B,MACHA,IAAQ;AAAA,MACN,MAAM,EAAE,MAAM,MAAM;AAAA,MAAC,GAAG,SAAS,CAAA,GAAI,WAAW,MAAM,GAAA;AAAA,MACtD,SAAS;AAAA,MACT,cAAc,KAAK,IAAI;AAAA,MACvB,KAAK;AAAA,MACL,aAAa;AAAA,IAAA,GAEf,KAAK,UAAU,IAAI7B,GAAK6B,CAAK,IAI/BA,EAAM,cAAc,KAAK,WAAWJ,GAAOC,GAAYG,EAAM,eAAe,GAAGD,GAAUC,EAAM,KAAK,OAAO;AAAA,EAC7G;AAAA;AAAA;AAAA;AAAA,EAKO,cAAcL,GAAY;AAC/B,UAAMxB,IAAM,KAAK,SAASwB,CAAE,GACtBK,IAAQ,KAAK,UAAU,IAAI7B,CAAG;AACpC,IAAI6B,MACFA,EAAM,cAAc;AAAA,EAExB;AAAA;AAAA;AAAA;AAAA,EAKO,aAAaL,GAAY;AAC9B,UAAMxB,IAAM,KAAK,SAASwB,CAAE,GACtBK,IAAQ,KAAK,UAAU,IAAI7B,CAAG;AACpC,QAAI6B,GAAO;AACT,MAAAA,EAAM,KAAK,KAAA;AAEX,iBAAWN,KAAUM,EAAM,KAAK;AAC9B,YAAI;AACF,UAAAN,EAAO,KAAA,GACPA,EAAO,WAAA;AAAA,QACT,QACU;AAAA,QAEV;AAEF,WAAK,SAAS,OAAOvB,CAAG,GACxB,KAAK,UAAU,OAAOA,CAAG,GACzB,KAAK,SAAS,OAAOA,CAAG;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,eAAewB,GAAYM,GAAeC,GAAiBC,GAAa;AAG7E,UAAMhC,IAAM,KAAK,SAASwB,CAAE,GACtBG,IAAS,KAAK,iBAAiBH,CAAE,GACjCI,IAAW,KAAK,gBAAgB,KAAK,UAAU5B,GAAK2B,CAAM,GAC1DM,IAAW,KAAK,UAAU,IAAIjC,CAAG;AACvC,QAAIiC,GAAU;AACZ,MAAAL,EAAS,KAAK,QAAQ,KAAK,IAAI,GAAGD,CAAM;AACxC,YAAMO,KAAa,KAAK,IAAI,cAAcD,EAAS,gBAAgB,KAC7DE,IAAaF,EAAS,UAAUC;AACtC,UAAI,KAAK,IAAIC,IAAaJ,CAAO,IAAI,QAAUE,EAAS,QAAQD;AAC9D;AACF,MAAAC,EAAS,KAAK,KAAA;AAEd,iBAAWV,KAAUU,EAAS,KAAK;AACjC,YAAI;AACF,UAAAV,EAAO,KAAA,GACPA,EAAO,WAAA;AAAA,QACT,QACU;AAAA,QAEV;AAEF,WAAK,SAAS,OAAOvB,CAAG,GACxB,KAAK,UAAU,OAAOA,CAAG;AAAA,IAC3B;AACA,IAAI,KAAK,IAAI,UAAU,eACrB,KAAK,IAAI,OAAA,EAAS,MAAM,MAAM;AAAA,IAAC,CAAC;AAClC,UAAMsB,IAAO,KAAK,aAAaQ,GAAMC,GAASC,GAAKJ,CAAQ;AAC3D,SAAK,SAAS,IAAI5B,GAAKsB,CAAI,GAC3B,KAAK,UAAU,IAAItB,GAAK;AAAA,MACtB,MAAAsB;AAAA,MACA,SAAAS;AAAA,MACA,cAAc,KAAK,IAAI;AAAA,MACvB,KAAAC;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEO,UAAU;AACf,SAAK,QAAA;AACL,eAAWI,KAAS,KAAK,MAAM,OAAA;AAC7B,MAAAA,EAAM,KAAK,QAAA;AACb,SAAK,MAAM,MAAA;AAAA,EACb;AAAA,EAEQ,UAAU;AAChB,eAAWd,KAAQ,KAAK,WAAW,OAAA,GAAU;AAC3C,MAAAA,EAAK,KAAA;AAEL,iBAAWC,KAAUD,EAAK;AACxB,YAAI;AACF,UAAAC,EAAO,KAAK,CAAC,GACbA,EAAO,WAAA;AAAA,QACT,QACU;AAAA,QAEV;AAEF,MAAAD,EAAK,QAAQ,SAAS;AAAA,IACxB;AACA,eAAWA,KAAQ,KAAK,SAAS,OAAA,GAAU;AACzC,MAAAA,EAAK,KAAA;AAEL,iBAAWC,KAAUD,EAAK;AACxB,YAAI;AACF,UAAAC,EAAO,KAAK,CAAC,GACbA,EAAO,WAAA;AAAA,QACT,QACU;AAAA,QAEV;AAEF,MAAAD,EAAK,QAAQ,SAAS;AAAA,IACxB;AAEA,eAAWe,KAAQ,KAAK,WAAW,OAAA;AACjC,UAAI;AACF,QAAAA,EAAK,WAAA;AAAA,MACP,QACU;AAAA,MAEV;AAEF,eAAWA,KAAQ,KAAK,SAAS,OAAA;AAC/B,UAAI;AACF,QAAAA,EAAK,WAAA;AAAA,MACP,QACU;AAAA,MAEV;AAEF,SAAK,WAAW,MAAA,GAChB,KAAK,kBAAkB,MAAA,GACvB,KAAK,WAAW,MAAA,GAChB,KAAK,SAAS,MAAA,GACd,KAAK,UAAU,MAAA,GACf,KAAK,SAAS,MAAA;AAAA,EAChB;AAAA,EAEA,MAAc,gBAAgBjF,GAAwB6D,GAAqB;AACzE,UAAMjB,IAAM,KAAK,SAAS5C,EAAQ,EAAE,GAC9BgF,IAAQ,MAAM,KAAK,SAAShF,CAAO;AACzC,QAAI,CAACgF;AACH;AACF,UAAME,IAASlF,EAAQ,YAAY,GAC7BmF,IAAW,KAAK,IAAI,KAAK,KAAK,IAAI,KAAKnF,EAAQ,YAAY,CAAC,CAAC,GAE7DoF,IAAavB,IAAc7D,EAAQ,WAEnCqF,IAAiBH,IAASE,IAAaD,GACvCX,IAAW,KAAK,gBAAgB,KAAK,YAAY5B,GAAK5C,EAAQ,MAAM;AAG1E,SAAK,gBAAgBA,GAASoF,GAAYZ,CAAQ;AAElD,UAAMc,IAAe,KAAK,WAAW,IAAI1C,CAAG;AAC5C,QAAI0C,KAAgB,CAACA,EAAa;AAEhC;AAGF,IAAIA,MACF,KAAK,WAAW,OAAO1C,CAAG,GAC1B,KAAK,kBAAkB,OAAOA,CAAG;AAInC,UAAM2C,KADoBvF,EAAQ,UAAUA,EAAQ,aACPmF,GACvCK,IAAoBN,IAASK,GAE7BrB,IAAO,KAAK,eAAec,EAAM,MAAM,KAAK,IAAI,GAAGK,CAAc,IAAI,KAAMb,GAAUW,GAAUK,IAAoB,GAAI;AAC7H,SAAK,WAAW,IAAI5C,GAAKsB,CAAI,GAC7B,KAAK,kBAAkB,IAAItB,GAAK;AAAA,MAC9B,SAAA5C;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,MACpB,mBAAmBoF;AAAA,IAAA,CACpB;AAAA,EACH;AAAA,EAEQ,gBAAgBpF,GAAwBoF,GAAoBZ,GAAoB;AACtF,UAAMiB,IAAa,KAAK,IAAI,GAAG,OAAOzF,EAAQ,UAAW,WAAWA,EAAQ,SAAS,CAAC,GAChF0F,IAAkB1F,EAAQ,UAAUA,EAAQ,WAC5C2F,IAAiB3F,EAAQ,kBAAkB,GAC3C4F,IAAkB5F,EAAQ,mBAAmB;AAEnD,QAAI6F,IAAmB;AAGvB,IAAIF,IAAiB,KAAKP,IAAaO,MACrCE,IAAmB,KAAK,IAAI,GAAGT,IAAaO,CAAc;AAI5D,UAAMG,IAAeJ,IAAkBN;AACvC,IAAIQ,IAAkB,KAAKE,IAAeF,MACxCC,IAAmB,KAAK,IAAIA,GAAkB,KAAK,IAAI,GAAGC,IAAeF,CAAe,CAAC,IAG3FpB,EAAS,KAAK,QAAQiB,IAAaI;AAAA,EACrC;AAAA,EAEQ,aAAanB,GAAeC,GAAiBC,GAAaJ,GAA+B;AAC/F,QAAIuB,IAAU,IACVC,IAASrB,GACTsB,IAAU,GACVC,IAAQ;AACZ,UAAMC,IAAS,KAAK,MAAO,MAAO,KAAK,IAAIvB,KAAO,IAAI,CAAC,IAAK,GAAI,GAC1DN,IAAa,KAAK,kBAAkBI,CAAI,GACxC0B,IAAmC,CAAA,GAEnCC,IAAQ,OAAO,YAAY,YAAY;AAC3C,UAAIN;AACF;AACF,YAAM,EAAE,OAAA1B,GAAO,OAAAI,EAAA,IAAU,MAAMC,EAAK,KAAK,KAAK,MAAMsB,CAAM,CAAC;AAE3D,UADAA,KAAUG,GACN1B,MAAU;AACZ;AACF,UAAIyB,GAAO;AACT,QAAAA,IAAQ;AACR;AAAA,MACF;AAEA,OADY7B,IAAQ,CAAC,GAAG,UAAU,OACtB,MAEZ4B,IAAU,KAAK,WAAW5B,GAAyBC,GAAY2B,GAASzB,GAAU4B,CAAO;AAAA,IAC3F,GAAG,KAAK,MAAM,MAAO,KAAK,IAAIxB,KAAO,IAAI,CAAC,CAAC,CAAC;AAE5C,WAAO;AAAA,MACL,SAAAwB;AAAA,MACA,MAAM,MAAM;AACV,QAAAL,IAAU,IACV,OAAO,cAAcM,CAAK;AAAA,MAC5B;AAAA,MACA,WAAW,MAAMN;AAAA,IAAA;AAAA,EAErB;AAAA,EAEQ,eAAerB,GAAiBC,GAAiBH,GAAoBW,IAAmB,GAAGmB,GAAuC;AACxI,QAAIP,IAAU,IACVC,IAASrB,GACTsB,IAAU,GACVM,IAAc;AAClB,UAAMjC,IAAa,KAAK,kBAAkBI,CAAI,GACxC0B,IAAmC,CAAA,GAInCI,IAAe,KAAK,MADH,MAC0BrB,CAAQ,GAEnDsB,IAAO,YAAY;AACvB,UAAIV;AACF;AAaF,UAXKQ,MAEC5B,IAAU,KACZ,MAAMD,EAAK,KAAKC,CAAO,GAEzB4B,IAAc,KAGhBP,KAAUQ,GAGNF,MAAsB,UAAaN,IAASM,GAAmB;AACjE,QAAAP,IAAU;AACV;AAAA,MACF;AAEA,YAAM,EAAE,OAAA1B,GAAO,OAAAI,EAAA,IAAU,MAAMC,EAAK,KAAKsB,CAAM;AAG/C,UAAID;AACF;AAGF,UAAItB,MAAU,QAAQ;AACpB,QAAAsB,IAAU;AACV;AAAA,MACF;AAEA,WADY1B,IAAQ,CAAC,GAAG,UAAU,OACtB,GAAG;AAGb,QAAA0B,IAAU;AACV;AAAA,MACF;AAEA,YAAMW,IAAiBvB,MAAa,IAChC,KAAK,oBAAoBd,GAAyBc,CAAQ,IAC1Dd;AACJ,MAAA4B,IAAU,KAAK,WAAWS,GAAgBpC,GAAY2B,GAASzB,GAAU4B,CAAO,GAG3EL,KAEH,WAAW,MAAMU,EAAA,GAAQ,CAAC;AAAA,IAE9B;AAEA,WAAAA,EAAA,GACO;AAAA,MACL,SAAAL;AAAA,MACA,MAAM,MAAM;AACV,QAAAL,IAAU;AAAA,MACZ;AAAA,MACA,WAAW,MAAMA;AAAA,IAAA;AAAA,EAErB;AAAA,EAEQ,oBAAoB1B,GAAuBc,GAAkC;AAInF,UAAMwB,IAAe,KAAK,MAAMtC,EAAM,CAAC,EAAE,SAASc,CAAQ;AAC1D,WAAIwB,KAAgB,IACXtC,IAEFA,EAAM,IAAI,CAACuC,MAAY;AAC5B,YAAMC,IAAS,IAAI,aAAaF,CAAY;AAC5C,eAASG,IAAI,GAAGA,IAAIH,GAAcG,KAAK;AACrC,cAAMC,IAAWD,IAAI3B,GACf6B,IAAgB,KAAK,MAAMD,CAAQ,GACnCE,IAAe,KAAK,IAAID,IAAgB,GAAGJ,EAAQ,SAAS,CAAC,GAC7DM,IAAIH,IAAWC;AAErB,QAAAH,EAAOC,CAAC,IAAIF,EAAQI,CAAa,KAAK,IAAIE,KAAKN,EAAQK,CAAY,IAAIC;AAAA,MACzE;AACA,aAAOL;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEQ,WAAWxC,GAAuBC,GAAoB2B,GAAiBzB,GAAoB4B,GAAmC;AACpI,UAAMe,IAAW,KAAK,IAAI9C,EAAM,QAAQ,CAAC,GACnC+C,IAAM/C,EAAM,CAAC,GAAG,UAAU;AAChC,QAAI+C,MAAQ;AACV,aAAOnB;AACT,UAAMoB,IAAS,KAAK,IAAI,aAAaF,GAAUC,GAAK9C,CAAU;AAC9D,aAASwC,IAAI,GAAGA,IAAIK,GAAUL,KAAK;AACjC,YAAMQ,IAAOjD,EAAMyC,CAAC,KAAK,IAAI,aAAaM,CAAG;AAC7C,MAAAC,EAAO,cAAc,IAAI,aAAaC,CAAI,GAAGR,CAAC;AAAA,IAChD;AACA,UAAM3C,IAAS,KAAK,IAAI,mBAAA;AACxB,IAAAA,EAAO,SAASkD,GAChBlD,EAAO,QAAQK,CAAQ;AACvB,UAAM+C,IAAY,KAAK,IAAI,KAAK,IAAI,aAAatB,CAAO;AAIxD,QAHA9B,EAAO,MAAMoD,CAAS,GAGlBnB,GAAS;AACX,MAAAA,EAAQ,KAAKjC,CAAM;AAEnB,YAAMqD,IAAWH,EAAO,WAAW;AACnC,iBAAW,MAAM;AACf,cAAMI,IAAQrB,EAAQ,QAAQjC,CAAM;AACpC,QAAIsD,IAAQ,MACVrB,EAAQ,OAAOqB,GAAO,CAAC;AAAA,MAE3B,GAAGD,IAAW,GAAG;AAAA,IACnB;AAEA,WAAOD,IAAYF,EAAO;AAAA,EAC5B;AAAA,EAEQ,gBAAgBK,GAA4BtD,GAAYG,GAAiB;AAC/E,UAAMM,IAAW6C,EAAI,IAAItD,CAAE;AAC3B,QAAIS;AACF,aAAI,OAAON,KAAW,aACpBM,EAAS,KAAK,QAAQ,KAAK,IAAI,GAAGN,CAAM,IACnCM;AAET,UAAML,IAAW,KAAK,IAAI,WAAA;AAC1B,WAAAA,EAAS,KAAK,QAAQ,KAAK,IAAI,GAAG,OAAOD,KAAW,WAAWA,IAAS,CAAC,GACzEC,EAAS,QAAQ,KAAK,IAAI,WAAW,GACrCkD,EAAI,IAAItD,GAAII,CAAQ,GACbA;AAAA,EACT;AAAA,EAEQ,SAASJ,GAAY;AAC3B,WAAO,SAASA,CAAE;AAAA,EACpB;AAAA,EAEQ,SAASA,GAAY;AAC3B,WAAO,SAASA,CAAE;AAAA,EACpB;AAAA,EAEA,MAAc,SAASpE,GAAwD;AAE7E,UAAM2H,IAAS,KAAK,MAAM,IAAI3H,EAAQ,EAAE;AACxC,QAAI2H;AACF,aAAOA;AAGT,UAAMC,IAAU,KAAK,aAAa,IAAI5H,EAAQ,EAAE;AAChD,QAAI4H;AACF,aAAOA;AAGT,UAAMC,KAAkB,YAAY;AAClC,UAAI;AACF,cAAMC,IAAW,MAAM,MAAM9H,EAAQ,GAAG;AACxC,YAAI,CAAC8H,EAAS,MAAM;AAClB,eAAK,aAAa,OAAO9H,EAAQ,EAAE;AACnC;AAAA,QACF;AACA,cAAM0E,IAAO,IAAIqD,GAAeD,EAAS,IAAI,GACvC9C,IAAmB,EAAE,MAAAN,GAAM,OAAOA,EAAK,MAAA;AAE7C,YADA,MAAMA,EAAK,OACP,CAAC,KAAK,sBAAsB1E,EAAQ,EAAE,GAAG;AAC3C,UAAA0E,EAAK,QAAA,GACL,KAAK,aAAa,OAAO1E,EAAQ,EAAE;AACnC;AAAA,QACF;AAEA,oBAAK,MAAM,IAAIA,EAAQ,IAAIgF,CAAK,GAChC,KAAK,aAAa,OAAOhF,EAAQ,EAAE,GAC5BgF;AAAA,MACT,SACOgD,GAAG;AACR,gBAAQ,MAAM,uCAAuChI,EAAQ,GAAG,IAAIgI,CAAC,GACrE,KAAK,aAAa,OAAOhI,EAAQ,EAAE;AACnC;AAAA,MACF;AAAA,IACF,GAAA;AAEA,gBAAK,aAAa,IAAIA,EAAQ,IAAI6H,CAAc,GACzCA;AAAA,EACT;AAAA,EAEQ,sBAAsBzD,GAAqB;AACjD,eAAWnD,KAAS,KAAK,SAAS;AAChC,iBAAWjB,KAAWiB,EAAM;AAC1B,YAAIjB,EAAQ,OAAOoE,EAAI,QAAO;AAGlC,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAiBA,GAAoB;AAC3C,eAAWnD,KAAS,KAAK,SAAS;AAChC,iBAAWjB,KAAWiB,EAAM,UAAU;AACpC,YAAIjB,EAAQ,OAAOoE;AACjB;AACF,cAAMG,IAAUvE,EAAgC;AAChD,eAAO,OAAOuE,KAAW,WAAWA,IAAS;AAAA,MAC/C;AAEF,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkBG,GAAmC;AAC3D,UAAMuD,IAAQvD,EAA4C;AAC1D,QAAI,CAACuD;AACH,aAAO;AACT,UAAMC,IAAkBD,EAAK;AAC7B,QAAI,OAAOC,KAAoB,YAAYA,IAAkB;AAC3D,aAAOA;AACT,UAAM5D,IAAa2D,EAAK;AACxB,WAAI,OAAO3D,KAAe,YAAYA,IAAa,IAC1CA,IACF;AAAA,EACT;AACF;AC5mBA,MAAM6D,KAAkC,KAClCC,wBAAsB,IAAA;AAC5B,IAAIC,KAAuBF;AAE3B,SAASG,GAAW1F,GAAa2F,GAAoB;AACnD,EAAAH,EAAgB,OAAOxF,CAAG,GAC1BwF,EAAgB,IAAIxF,GAAK2F,CAAK;AAChC;AAEA,SAASC,KAAY;AACnB,SAAOJ,EAAgB,OAAOC,MAAsB;AAClD,UAAM,CAACI,GAAWC,CAAM,IAAIN,EAAgB,QAAA,EAAU,OAAO;AAC7D,IAAAA,EAAgB,OAAOK,CAAS,GAChCC,EAAO,QAAA;AAAA,EACT;AACF;AAaO,SAASC,GAAiBC,GAAqB;AACpD,SAAOA,EAAM,IAAI,CAAAC,MAAQA,EAAK,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK;AAAA,CAAI;AAClE;AAEO,SAASC,GAAaC,GAAkB;AAC7C,QAAMC,IAAa,MAAM,QAAQD,EAAK,UAAU,IAC5CA,EAAK,WAAW,KAAK,IAAI,IACzBA,EAAK,YACHE,IAAWF,EAAK,YAAY,IAC5BG,IAAaH,EAAK,cAAc,UAChCI,IAAYJ,EAAK,aAAa,UAC9BK,IAAOL,EAAK,QAAQ,WACpBM,IAAQN,EAAK,SAAS,QAEtBO,IAAgB;AAAA,IACpB,cAAcL,CAAQ;AAAA,IACtB,gBAAgBC,CAAU;AAAA,IAC1B,eAAeC,CAAS;AAAA,IACxB,UAAUC,CAAI;AAAA,IACd,eAAeC,CAAK;AAAA,IACpB;AAAA,EAAA;AAeF,MAZIL,KACFM,EAAI,KAAK,gBAAgBN,CAAU,EAAE,GACnC,OAAOD,EAAK,iBAAkB,YAChCO,EAAI,KAAK,mBAAmBP,EAAK,aAAa,IAAI,GAChD,OAAOA,EAAK,WAAY,YAC1BO,EAAI,KAAK,gBAAgBP,EAAK,OAAO,IAAI,GACvCA,EAAK,YAAY,SACnBO,EAAI,KAAK,eAAeP,EAAK,WAAW,KAAK,EAAE,GAC7CA,EAAK,QAAQ,SAAS,OAAOA,EAAK,OAAO,SAAU,YACrDO,EAAI,KAAK,wBAAwBP,EAAK,OAAO,KAAK,MAAMA,EAAK,OAAO,KAAK,EAAE,GACzEA,EAAK,aACPO,EAAI,KAAK,4BAA4B,GACnCP,EAAK,YAAY,SAAS,OAAOA,EAAK,WAAW,YAAa,UAAU;AAC1E,UAAMQ,KAASR,EAAK,WAAW,SAAS,OAAO,KAAK,KAAK,MACnDS,IAAU,KAAK,IAAID,CAAK,IAAIR,EAAK,WAAW,UAC5CU,IAAU,KAAK,IAAIF,CAAK,IAAIR,EAAK,WAAW,UAC5CW,IAAOX,EAAK,WAAW,QAAQ;AACrC,IAAAO,EAAI,KAAK,gBAAgBE,CAAO,MAAMC,CAAO,MAAMC,CAAI,MAAMX,EAAK,WAAW,KAAK,EAAE;AAAA,EACtF;AAEA,SAAOO,EAAI,KAAK,IAAI;AACtB;AAEA,eAAsBK,GAAiBC,GAAiBC,GAAiB;AACvE,QAAMjH,IAAM,GAAGiH,CAAO,KAAKD,CAAO,IAC5BjC,IAASS,EAAgB,IAAIxF,CAAG;AACtC,MAAI+E;AACF,WAAAW,GAAW1F,GAAK+E,CAAM,GACfA;AAGT,QAAMe,IAAS,MAAMoB,GAAoBF,GAASC,CAAO;AAEvD,SAAAzB,EAAgB,IAAIxF,GAAK8F,CAAM,GAC/BF,GAAA,GAEKE;AACT;AC7DA,MAAMqB,KAAkB;AAyCxB,eAAsBC,GAAe9K,GAA0C;AAC7E,QAAM+K,IAAYC,GAAA,GACZC,IACFC,GAAMlL,EAAK,QAAQ,IAAIA,EAAK,WAAWmL,GAAWnL,EAAK,QAAQ,GAC7DoL,IAAgDD;AAAA,IACpDJ,EAAU,OAAOxG,GAAc8G,GAAMJ,CAAa,CAAC,CAAC;AAAA,EAAA,GAGhDhL,IAAMD,EAAK,OAAO,MAAMsL,GAAYtL,EAAK,UAAU,GACnDuL,IAAQ,IAAIC,GAAA;AAClB,EAAAvL,EAAI,MAAM,SAASsL,CAAK;AAExB,QAAME,IAAkBC,GAAsB,EAAE,KAAK1L,EAAK,aAAa,GACjE2L,wBAAqB,IAAA,GACrBC,wBAAmB,IAAA,GACnBC,wBAAqB,IAAA,GACrBC,wBAA6B,IAAA,GAC7BC,wBAA6B,IAAA,GAC7BC,IAAkBhM,EAAK,mBAAmB,QA0B1CiM,wBAAmB,IAAA,GACnBC,wBAAsB,IAAA,GAEtBvH,IAAcwH,GAAI,CAAC,GACnBvH,IAAYuH,GAAI,EAAK,GACrB7D,IAAW8D,GAAS,MAAMpI,EAAgBoH,EAAkB,KAAK,CAAC,GAClEiB,IAAgC,IAAI3H,GAAa0G,EAAkB,KAAK;AAE9E,MAAIkB,GACAC,IAAa,GACbC,IAAmB;AAUvB,iBAAeC,GAAYC,GAAkB;AAC3C,UAAMC,IAAaH,GACb,EAAE,UAAA3K,GAAU,IAAAI,GAAI,OAAAsJ,MAAUmB,GAC1BE,IAAWC,GAAoBhL,GAAUI,CAAE,GAC3CC,IAASF,GAAsBH,GAAU+K,CAAQ,GACjDrM,IAAamM,EAAK,IAAI,SAAS,OAC/BlM,IAAckM,EAAK,IAAI,SAAS;AAEtC,IAAAL,EAAa,KAAKpK,GAAI2C,EAAU,KAAK;AAErC,UAAMkI,IAA6C,CAAA;AACnD,eAAW,EAAE,SAAAhM,EAAA,KAAaoB,GAAQ;AAChC,UAAIyK,MAAeH;AACjB;AACF,YAAMzJ,IAAU,MAAM2J,EAAK,WAAW5L,CAAO;AAC7C,UAAI6L,MAAeH;AACjB;AACF,UAAKzJ,KAEA,CAAAA,EAAoC,WAKzC;AAAA,YAHAD,GAAkBC,GAASjC,GAASP,GAAYC,CAAW,GACvDuM,GAAejM,CAAO,KACxB,MAAMkM,EAAiBlM,GAAS8L,CAAQ,GACtCD,MAAeH;AACjB;AACF,QAAAM,EAAQ,KAAK/J,CAAO;AAAA;AAAA,IACtB;AAEA,QAAI4J,MAAeH;AACjB;AACFjB,IAAAA,EAAM,eAAA;AACN,UAAM0B,IAAUH,EAAQ,OAAO,OAAO;AAGtC,IAFIG,EAAQ,UACV1B,EAAM,SAAS,GAAG0B,CAAO,GACvBN,MAAeH,KAEnBE,EAAK,IAAI,OAAA;AAAA,EACX;AAEA,QAAMQ,IAAcC,GAAkB,MAAMV,GAAY;AAAA,IACtD,KAAAxM;AAAA,IACA,OAAAsL;AAAA,IACA,UAAUH,EAAkB;AAAA,IAC5B,IAAIzG,EAAY;AAAA,IAChB,YAAYyI;AAAA,EAAA,CACb,CAAC,GAEIC,IAAQC,GAAA;AACd,EAAAD,EAAM,IAAI,MAAM;AAEd,IAAAE;AAAA,MACE,MAAMlC,GAAMJ,CAAa;AAAA,MACzB,CAACpJ,MAAa;AACZ,YAAI;AACF,UAAAuJ,EAAkB,QAAQL,EAAU,OAAOxG,GAAc1C,CAAQ,CAAC;AAAA,QACpE,SACO2L,GAAK;AACV,kBAAQ,MAAM,sCAAsCA,CAAG;AACvD;AAAA,QACF;AACA,QAAAnB,EAAa,YAAYjB,EAAkB,KAAK,GAChDoB,KAAoB,GACpBiB,EAAA,GACIzN,EAAK,oBAAoB,MAC3B0N,GAAgBtC,EAAkB,KAAK,GACzCuC,GAAavC,EAAkB,KAAK,GACpCwC,EAAA,GACK5N,EAAK,gBACRkN,EAAA;AAAA,MACJ;AAAA,MACA,EAAE,MAAM,IAAM,WAAW,GAAA;AAAA,IAAK,GAG3BlN,EAAK,gBAERuN,EAAM5I,GAAa,MAAM;AACvB,MAAAiJ,EAAA,GACAV,EAAA;AAAA,IACF,CAAC,GAIHK,EAAMjF,GAAU,MAAMsF,GAAkB;AAAA,EAC1C,CAAC;AAED,WAASA,IAAmB;AAC1B,UAAMC,IAAevF,EAAS;AAC9B,IAAIuF,KAAgB,IAClBlJ,EAAY,QAAQ,IACbA,EAAY,QAAQkJ,IAC3BlJ,EAAY,QAAQkJ,IACblJ,EAAY,QAAQ,MAC3BA,EAAY,QAAQ;AAAA,EACxB;AAEA,WAAS+I,GAAgB7L,GAA0B;AACjD,eAAW8B,KAAO/B,GAAoBC,CAAQ;AAC5C,MAAI8J,EAAe,IAAIhI,CAAG,MAG1BgI,EAAe,IAAIhI,CAAG,GAClBmK,GAAkBnK,CAAG,MAAM,WAE1BoK,GAAyBpK,CAAG,KAEjC8H,EAAgB,IAAI9H,CAAG,EAAE,MAAM,MAAM;AAAA,MAErC,CAAC;AAAA,EAEL;AAEA,WAASgK,GAAa9L,GAA0B;AAC9C,UAAMmM,wBAAU,IAAA;AAChB,eAAWjM,KAASF,EAAS;AAC3B,iBAAWoM,KAASlM,EAAM;AACxB,QAAAiM,EAAI,IAAIC,EAAM,EAAE;AAEpB,eAAW,CAAC/I,GAAInC,CAAO,KAAK6I;AAC1B,MAAIoC,EAAI,IAAI9I,CAAE,MAEdnC,EAAQ,QAAA,GACR6I,EAAa,OAAO1G,CAAE;AAExB,eAAW,CAACA,GAAIY,CAAK,KAAKmG;AACxB,MAAI+B,EAAI,IAAI9I,CAAE,MAEdgJ,GAAkBpI,CAAK,GACvBmG,EAAa,OAAO/G,CAAE;AAAA,EAE1B;AAEA,WAASuI,IAAgB;AACvB,IAAAlC,EAAM,eAAA;AACN,eAAWxI,KAAW6I,EAAa;AACjC,MAAA7I,EAAQ,QAAA;AAEV,IAAA6I,EAAa,MAAA,GACbC,EAAe,MAAA;AACf,eAAW/F,KAASmG,EAAa,OAAA;AAC/B,MAAAiC,GAAkBpI,CAAK;AACzB,IAAAmG,EAAa,MAAA;AAAA,EACf;AAEA,WAAS1E,KAAO;AACd,IAAI3C,EAAU,UAEdA,EAAU,QAAQ,IAClB2H,IAAa,YAAY,IAAA,GACzBD,IAAQ,sBAAsBtH,EAAI;AAAA,EACpC;AAEA,WAASmJ,IAAQ;AACf,IAAAvJ,EAAU,QAAQ,IACd0H,MAAU,UACZ,qBAAqBA,CAAK,GAC5BA,IAAQ,QAERD,EAAa,KAAK1H,EAAY,OAAO,EAAK,GACtC3E,EAAK,kBAAkB,MACzBoO,GAAA;AAAA,EACJ;AAEA,WAASpJ,KAAO;AACd,IAAAqJ,GAAA,GACIzJ,EAAU,UACZ0H,IAAQ,sBAAsBtH,EAAI;AAAA,EACtC;AAEA,WAASqJ,GAAKC,GAAkB;AAC9B,QAAI,CAAC1J,EAAU,SAAS0J,MAAY;AAClC;AAEF,UAAMC,IAAM,YAAY,IAAA,GAClBC,IAAQF,MAAY/B,IAAagC,IAAMhC,IAAa;AAG1D,IAFAA,IAAagC,GAETC,MAAU,MAGd7J,EAAY,QAAQR;AAAA,MAClBQ,EAAY,QAAQ6J;AAAA,MACpB;AAAA,MACAlG,EAAS,SAAS,OAAO;AAAA,IAAA,GAGvBA,EAAS,QAAQ,KAAK3D,EAAY,SAAS2D,EAAS,SACtD6F,EAAA;AAAA,EAGJ;AAEA,WAASM,GAAKC,GAAc;AAC1B,IAAA/J,EAAY,QAAQR,EAAMuK,GAAM,GAAGpG,EAAS,SAAS,OAAO,iBAAiB;AAAA,EAC/E;AAEA,iBAAesE,GAAS8B,GAAc;AACpC,IAAA/J,EAAY,QAAQR,EAAMuK,GAAM,GAAGpG,EAAS,SAAS,OAAO,iBAAiB,GAC7E,MAAM4E,EAAA;AAAA,EACR;AAEA,iBAAeE,GAAqBtM,GAAuB;AACzD,UAAM2H,IAASmD,EAAa,IAAI9K,EAAQ,EAAE;AAC1C,QAAI2H;AACF,aAAOA;AAET,UAAMC,IAAUmD,EAAe,IAAI/K,EAAQ,EAAE;AAC7C,QAAI4H;AACF,aAAOA;AAET,UAAMiG,IAAUC,GAAY9N,CAAO;AACnC,IAAA+K,EAAe,IAAI/K,EAAQ,IAAI6N,CAAO;AAEtC,UAAM5L,IAAU,MAAM4L;AACtB,WAAI5L,KACF6I,EAAa,IAAI9K,EAAQ,IAAIiC,CAAO,GAEtC8I,EAAe,OAAO/K,EAAQ,EAAE,GACzBiC;AAAA,EACT;AAEA,iBAAe6L,GAAY9N,GAA+D;AAExF,QAAIA,EAAQ,gBAAgB,YAAYA,EAAQ,gBAAgB,WAAW;AACzE,UAAI,CAACA,EAAQ;AACX,eAAO2C,EAAY3C,EAAQ,WAAW;AAExC,UAAI,UAAUA,KAAWA,EAAQ,SAAS,WACpC+N,GAAqB/N,EAAQ,GAAG,GAAG;AACrC,cAAMgO,IAAS,MAAMC,GAAgBjO,CAAO;AAC5C,eAAIgO,KAEGrL,EAAY3C,EAAQ,aAAaA,EAAQ,GAAG;AAAA,MACrD;AAGF,YAAMkO,IAAU,MAAMC,GAAYnO,EAAQ,GAAG;AAC7C,aAAIkO,IACK,IAAI9L,EAAO8L,CAAO,IACpBvL,EAAY3C,EAAQ,aAAaA,EAAQ,GAAG;AAAA,IACrD;AAEA,QAAIA,EAAQ,gBAAgB;AAC1B,aAAO,MAAMoO,GAAiBpO,CAAO;AAEvC,IAAIA,EAAQ,gBAAgB,YAAYA,EAAQ;AAAA,EAKlD;AAEA,iBAAeoO,GAAiBpO,GAA+D;AAC7F,UAAM4J,IAAUjB,GAAiB3I,EAAQ,KAAK;AAC9C,QAAI,CAAC4J;AACH;AAEF,UAAM,CAACb,CAAI,IAAI/I,EAAQ;AACvB,QAAI,CAAC+I;AACH;AAEF,UAAML,IAAS,MAAMiB,GAAiBC,GAASd,GAAaC,CAAI,CAAC,GAC3DmF,IAAU5L,EAAQ,KAAKoG,CAAM;AACnC,WAAO,IAAItG,EAAO8L,CAAO;AAAA,EAC3B;AAEA,iBAAeC,GAAYtL,GAAa;AACtC,UAAMwL,IAAYxL,EAAI,WAAW,OAAO,GAClCyL,IAAS,eAAe,KAAKzL,CAAG;AAEtC,QAAI,CAACwL,KAAa,CAACC;AACjB,UAAI;AACF,cAAM3D,EAAgB,IAAI9H,CAAG;AAC7B,cAAM0L,IAAM,MAAM5D,EAAgB,IAAI9H,CAAG;AACzC,YAAI0L,aAAe;AACjB,iBAAOjM,EAAQ,KAAKiM,CAAG;AAAA,MAC3B,QACM;AAAA,MAEN;AAIF,WAAO,MAAMC,GAAiB3L,CAAG;AAAA,EACnC;AAEA,iBAAeoL,GAAgBjO,GAAqF;AAClH,UAAM6E,IAAWsG,EAAa,IAAInL,EAAQ,EAAE;AAC5C,QAAI6E;AACF,aAAOA,EAAS;AAElB,UAAM4J,IAASC,EAAe1O,EAAQ,GAAG,GACnC2O,IAAezD,MAAoB,WACnC0D,IAAoB1D,MAAoB;AAC9C,QAAIuD,KAAUzD,EAAuB,IAAIyD,CAAM,GAAG;AAChD,UAAI,CAACG;AACH,cAAM,IAAI,MAAM,sCAAsC5O,EAAQ,GAAG,EAAE;AACrE,YAAM6O,IAAoB,MAAMC,EAA0B9O,EAAQ,GAAG,EAAE,MAAM,CAAC0M,MAAQ;AACpF,gBAAQ,KAAK,+CAA+C1M,EAAQ,KAAK0M,CAAG;AAAA,MAE9E,CAAC;AACD,aAAImC,KACF1D,EAAa,IAAInL,EAAQ,IAAI6O,CAAiB,GACvCA,EAAkB,UAE3B;AAAA,IACF;AAEA,QAAIF,GAAc;AAChB,YAAMI,IAAiB,MAAMC,GAA0BhP,EAAQ,GAAG,EAAE,MAAM,CAAC0M,MAAQ;AAQjF,YAPI+B,KAAUQ,EAAqBvC,CAAG,KACpC1B,EAAuB,IAAIyD,CAAM,IAC/B,CAACA,KAAU,CAACxD,EAAuB,IAAIwD,CAAM,OAC3CA,KACFxD,EAAuB,IAAIwD,CAAM,GACnC,QAAQ,KAAK,+CAA+CzO,EAAQ,KAAK0M,CAAG,IAE1E,CAACkC;AACH,gBAAMlC;AAAA,MAEV,CAAC;AACD,UAAIqC;AACF,uBAAQ,KAAK,oCAAoC/O,EAAQ,GAAG,GAC5DmL,EAAa,IAAInL,EAAQ,IAAI+O,CAAc,GACpCA,EAAe;AAAA,IAE1B;AAEA,QAAIH,GAAmB;AACrB,YAAMC,IAAoB,MAAMC,EAA0B9O,EAAQ,GAAG,EAAE,MAAM,CAAC0M,MAAQ;AACpF,gBAAQ,KAAK,+CAA+C1M,EAAQ,KAAK0M,CAAG;AAAA,MAE9E,CAAC;AACD,UAAImC;AACF,uBAAQ,KAAK,oCAAoC7O,EAAQ,GAAG,GAC5DmL,EAAa,IAAInL,EAAQ,IAAI6O,CAAiB,GACvCA,EAAkB;AAAA,IAE7B;AAAA,EAGF;AAEA,WAASI,EAAqBvC,GAAc;AAC1C,QAAI,EAAEA,aAAe;AACnB,aAAO;AACT,UAAMwC,IAAMxC,EAAI,WAAW;AAC3B,WAAOwC,EAAI,SAAS,gBAAgB,KAC/BA,EAAI,SAAS,gBAAgB,KAC7BA,EAAI,SAAS,oBAAoB;AAAA,EACxC;AAEA,iBAAehD,EAAiBlM,GAA8BmB,GAAY;AACxE,UAAM6D,IAAQmG,EAAa,IAAInL,EAAQ,EAAE;AACzC,QAAKgF;AAGL,UAAI;AACF,cAAMmK,IAAWnP,EAAQ,YAAY,GAC/BoF,IAAa,KAAK,IAAI,GAAGjE,IAAKnB,EAAQ,YAAYmP,CAAQ,GAC1DC,IAAa,KAAK,MAAMhK,IAAa,GAAI;AAC/C,YAAIJ,EAAM,SAAS,UAAU;AAC3B,gBAAMyJ,IAASC,EAAe1O,EAAQ,GAAG;AACzC,cAAI,CAACyO;AACH;AACF,gBAAMY,IAAU,MAAMC,GAAetP,EAAQ,KAAKyO,GAAQ,EAAE,QAAQzJ,EAAM,QAAQ,YAAYA,EAAM,SAAS;AAC7G,iBAAKqK,KAELlE,EAAa,IAAInL,EAAQ,IAAIqP,CAAO,GAC7B,MAAMnD,EAAiBlM,GAASmB,CAAE,KAFvC;AAAA,QAGJ;AACA,YAAI6D,EAAM,SAAS;AACjB,cAAI;AACF,kBAAMuJ,IAAM,MAAMvJ,EAAM,KAAK,KAAKoK,CAAU;AAC5C,gBAAIb,EAAI,OAAO;AACb,oBAAMgB,IAAMvK,EAAM,OAAO,WAAW,IAAI;AACxC,cAAIuK,MACFA,EAAI,UAAUhB,EAAI,OAAO,GAAG,GAAGvJ,EAAM,OAAO,OAAOA,EAAM,OAAO,MAAM,GACtEwK,GAAqBxK,EAAM,OAAO,IAEpCuJ,EAAI,MAAM,MAAA;AAAA,YACZ;AAEA,gBAAIzK,EAAU,SAASyK,EAAI,SAASA,EAAI,MAAM,SAAS,GAAG;AACxD,oBAAMjK,IAAcU,EAAM,KAAiD,MAAM,mBAAmB;AACnG,cAAAuG,EACE,mBAAmBvL,EAAQ,IAAIuO,EAAI,OAAyBjK,CAAU;AAAA,YAC3E;AACA;AAAA,UACF,SACOoI,GAAK;AACV,kBAAM+B,IAASC,EAAe1O,EAAQ,GAAG;AACzC,gBAAIyO,KAAUQ,EAAqBvC,CAAG,MACpC1B,EAAuB,IAAIyD,CAAM,GACjCzJ,EAAM,KAAK,QAAA,GACPkG,MAAoB,YAAW;AACjC,oBAAMuE,IAAc,MAAMX,EAA0B9O,EAAQ,KAAK,EAAE,QAAQgF,EAAM,QAAQ,YAAYA,EAAM,QAAA,CAAS,EAAE,MAAM,CAAC0K,MAAe;AAC1I,wBAAQ,KAAK,gEAAgE1P,EAAQ,KAAK0P,CAAU;AAAA,cAEtG,CAAC;AACD,kBAAID;AACF,uBAAAtE,EAAa,IAAInL,EAAQ,IAAIyP,CAAW,GACjC,MAAMvD,EAAiBlM,GAASmB,CAAE;AAAA,YAE7C;AAEF,YAAIsN,KAAU,CAACxD,EAAuB,IAAIwD,CAAM,MAC9CxD,EAAuB,IAAIwD,CAAM,GACjC,QAAQ,KAAK,kCAAkCzO,EAAQ,KAAK0M,CAAG;AAEjE;AAAA,UACF;AAGF,cAAMiD,IAAcvK,IAAa;AAGjC,YAFI,CAAC,OAAO,SAASuK,CAAW,KAE5B3K,EAAM,SAAS;AACjB;AACF,cAAM4K,GAAwB5K,GAAO;AAAA,UACnC,WAAW2K;AAAA,UACX,cAAc3P,EAAQ,YAAY;AAAA,UAClC,QAAQA,EAAQ,UAAU;AAAA,QAAA,CAC3B;AAAA,MACH,SACO0M,GAAK;AACV,gBAAQ,KAAK,wCAAwCA,CAAG;AAAA,MAC1D;AAAA,EACF;AAEA,iBAAe4C,GAAezM,GAAa4L,GAAgBoB,GAAiD;AAC1G,UAAMlB,IAAezD,MAAoB,WACnC0D,IAAoB1D,MAAoB;AAC9C,QAAIF,EAAuB,IAAIyD,CAAM,GAAG;AACtC,UAAI,CAACG;AACH,cAAM,IAAI,MAAM,sCAAsC/L,CAAG,EAAE;AAC7D,aAAO,MAAMiM,EAA0BjM,GAAKgN,CAAK,EAAE,MAAM,MAAA;AAAA,OAAe;AAAA,IAC1E;AAEA,QAAIlB,GAAc;AAChB,YAAMmB,IAAW,MAAMd,GAA0BnM,GAAKgN,CAAK,EAAE,MAAM,CAACnD,MAAQ;AAG1E,YAFIuC,EAAqBvC,CAAG,KAC1B1B,EAAuB,IAAIyD,CAAM,GAC/B,CAACG;AACH,gBAAMlC;AAAA,MAEV,CAAC;AACD,UAAIoD;AACF,eAAOA;AAAA,IACX;AAEA,QAAIlB;AACF,aAAO,MAAME,EAA0BjM,GAAKgN,CAAK,EAAE,MAAM,MAAA;AAAA,OAAe;AAAA,EAG5E;AAEA,WAAS5D,GAAejM,GAAuD;AAC7E,WAAOA,EAAQ,gBAAgB,YAC1BA,EAAQ,SAAS,WACjB,OAAOA,EAAQ,OAAQ,YACvB+N,GAAqB/N,EAAQ,GAAG;AAAA,EACvC;AAGA,WAAS+L,GAAoBhL,GAA0BI,GAAY;AACjE,UAAMU,IAAQqB,EAAgBnC,CAAQ;AACtC,QAAIc,KAAS;AACX,aAAO;AACT,QAAIV,IAAKU;AACP,aAAOV;AAET,UAAM4O,IAAc,KAAK,IAAI,MAAO,KAAK,IAAIhP,EAAS,OAAO,IAAI,CAAC,GAAG,CAAC;AACtE,WAAO,KAAK,IAAIc,IAAQkO,GAAa,CAAC;AAAA,EACxC;AAEA,iBAAeC,GAAYnN,GAAa;AACtC,UAAMoN,IAAM/Q,EAAK,eAAe6K;AAChC,QAAI;AACF,YAAMnH,IAAM8L,EAAe7L,CAAG;AAC9B,UAAI,CAACD;AACH;AACF,YAAMsN,IAAOC,GAAS,GAAGF,CAAG,IAAIrN,CAAG,IAAI,GAAG;AAC1C,UAAI,MAAMsN,EAAK,OAAA;AACb,eAAOA;AAAAA,IACX,QACM;AACJ;AAAA,IACF;AAAA,EAEF;AAEA,WAASjD,GAAyBpK,GAAa;AAG7C,WAFI,GAACA,KAEDA,EAAI,WAAW,OAAO,KAAKA,EAAI,WAAW,OAAO;AAAA,EAGvD;AAEA,WAASyK,KAAqB;AAC5B,eAAW,CAAClJ,GAAIY,CAAK,KAAKmG,GAAc;AACtC,UAAInG,EAAM,SAAS,WAAW;AAC5B,QAAAA,EAAM,KAAK,QAAA,GACXmG,EAAa,IAAI/G,GAAI;AAAA,UACnB,MAAM;AAAA,UACN,QAAQY,EAAM;AAAA,UACd,SAASA,EAAM;AAAA,UACf,QAAQA,EAAM;AAAA,UACd,MAAMA,EAAM;AAAA,QAAA,CACb;AACD;AAAA,MACF;AAEA,MAAIA,EAAM,SAAS,aACjBA,EAAM,MAAM,MAAA;AAAA,IAChB;AAAA,EACF;AAEA,WAASoI,GAAkBpI,GAAmB;AAC5C,QAAIA,EAAM,SAAS,WAAW;AAC5B,MAAAA,EAAM,KAAK,QAAA;AACX;AAAA,IACF;AAEA,QAAIA,EAAM,SAAS;AACjB;AAEF,IAAAA,EAAM,MAAM,MAAA;AACZ,UAAMoL,IAAYhF,EAAgB,IAAIpG,EAAM,KAAK;AACjD,IAAIoL,MACF,IAAI,gBAAgBA,CAAS,GAC7BhF,EAAgB,OAAOpG,EAAM,KAAK,IAEpCA,EAAM,MAAM,gBAAgB,KAAK,GACjCA,EAAM,MAAM,KAAA;AAAA,EACd;AAEA,WAASqL,EAAkBC,GAA0BC,GAAcC,IAAY,KAAM;AACnF,WAAO,IAAI,QAAc,CAACC,GAASC,MAAW;AAC5C,YAAMrK,IAAQ,OAAO,WAAW,MAAM;AACpC,QAAAsK,EAAA,GACAD,EAAO,IAAI,MAAM,sCAAsCH,CAAI,EAAE,CAAC;AAAA,MAChE,GAAGC,CAAS,GAENI,IAAO,MAAM;AACjB,QAAAD,EAAA,GACAF,EAAA;AAAA,MACF,GACMI,IAAQ,MAAM;AAClB,QAAAF,EAAA;AACA,cAAMG,IAAaR,EAAO,QAAQ,GAAGA,EAAO,MAAM,IAAI,KAAK;AAC3D,QAAAI,EAAO,IAAI,MAAM,gBAAgBI,CAAU,uBAAuBP,CAAI,EAAE,CAAC;AAAA,MAC3E,GACMI,IAAU,MAAM;AACpB,eAAO,aAAatK,CAAK,GACzBiK,EAAO,oBAAoBC,GAAMK,CAAI,GACrCN,EAAO,oBAAoB,SAASO,CAAK;AAAA,MAC3C;AAEA,MAAAP,EAAO,iBAAiBC,GAAMK,GAAM,EAAE,MAAM,IAAM,GAClDN,EAAO,iBAAiB,SAASO,GAAO,EAAE,MAAM,IAAM;AAAA,IACxD,CAAC;AAAA,EACH;AAEA,iBAAe7B,GAA0BnM,GAAagN,GAAmF;AACvI,QAAIK;AACJ,IAAIjD,GAAyBpK,CAAG,MAC9BqN,IAAO,MAAMF,GAAYnN,CAAG,GACvBqN,MACH,MAAMvF,EAAgB,IAAI9H,CAAG,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC,GAC7CqN,IAAO,MAAMF,GAAYnN,CAAG;AAIhC,QAAI6B;AACJ,QAAI;AACF,UAAIwL;AACF,QAAAxL,IAAO,IAAIqM,EAAQb,CAAI;AAAA,WAEpB;AACH,cAAM3B,IAAM,MAAM,MAAM1L,CAAG;AAC3B,YAAK0L,EAAI;AAWP,UAAA7J,IAAO,IAAIqM,EAAQxC,EAAI,IAAI;AAAA,aAXd;AACb,gBAAMlH,IAAS,MAAMkH,EAAI,YAAA,GACnByC,IAAS,IAAI,eAA2B;AAAA,YAC5C,MAAMC,GAAY;AAChB,cAAAA,EAAW,QAAQ,IAAI,WAAW5J,CAAM,CAAC,GACzC4J,EAAW,MAAA;AAAA,YACb;AAAA,UAAA,CACD;AACD,UAAAvM,IAAO,IAAIqM,EAAQC,CAAM;AAAA,QAC3B;AAAA,MAIF;AAEA,YAAMtM,EAAK;AAEX,YAAM,EAAE,OAAAxE,GAAO,QAAAC,EAAA,IAAWuE,EAAK,MACzBwM,IAAS,SAAS,cAAc,QAAQ;AAC9C,MAAAA,EAAO,QAAQhR,KAAS,GACxBgR,EAAO,SAAS/Q,KAAU;AAC1B,YAAM+N,IAAU5L,EAAQ,KAAK4O,CAAM,GAC7BlD,IAAS6B,GAAO,UAAU,IAAIzN,EAAO8L,CAAO;AAClD,aAAI2B,GAAO,WACTA,EAAM,OAAO,UAAU3B,GACvB2B,EAAM,YAAY,QAAQ,EAAI,IAGzB,EAAE,MAAM,WAAW,MAAAnL,GAAM,QAAAwM,GAAQ,SAAAhD,GAAS,QAAAF,GAAQ,MAAM,EAAE,OAAA9N,GAAO,QAAAC,EAAA,EAAO;AAAA,IACjF,SACOuM,GAAK;AACV,YAAAhI,GAAM,QAAA,GACAgI;AAAA,IACR;AAAA,EACF;AAEA,WAASM,GAAkBnK,GAAsD;AAE/E,UAAMsO,IADMtO,EAAI,MAAM,GAAG,EAAE,CAAC,EAAG,MAAM,GAAG,EAAE,CAAC,EAC3B,MAAM,GAAG,EAAE,IAAA,GAAO,MAAM,GAAG,EAAE,OAAO,iBAAiB;AACrE,WAAI,CAAC,OAAO,OAAO,OAAO,MAAM,EAAE,SAASsO,CAAG,IACrC,UACL,CAAC,OAAO,OAAO,QAAQ,OAAO,QAAQ,OAAO,OAAO,MAAM,EAAE,SAASA,CAAG,IACnE,UACL,CAAC,OAAO,OAAO,OAAO,OAAO,OAAO,MAAM,EAAE,SAASA,CAAG,IACnD,UACF;AAAA,EACT;AAEA,WAASpD,GAAqBlL,GAAa;AACzC,UAAMuO,IAAOpE,GAAkBnK,CAAG;AAClC,WAAI,EAAAuO,MAAS,WAAWA,MAAS;AAAA,EAInC;AAEA,iBAAetC,EAA0BjM,GAAagN,GAAmF;AACvI,UAAMwB,IAAQ,SAAS,cAAc,OAAO;AAC5C,IAAAA,EAAM,cAAc,aACpBA,EAAM,QAAQ,IACdA,EAAM,cAAc,IACpBA,EAAM,UAAU,YAChBA,EAAM,MAAMxO,GACZwO,EAAM,KAAA;AAEN,QAAI;AACF,YAAMhB,EAAkBgB,GAAO,kBAAkB,IAAK;AAAA,IACxD,SACO3E,GAAK;AACV,MAAA2E,EAAM,MAAA;AACN,YAAMjB,IAAYhF,EAAgB,IAAIiG,CAAK;AAC3C,YAAIjB,MACF,IAAI,gBAAgBA,CAAS,GAC7BhF,EAAgB,OAAOiG,CAAK,IAE9BA,EAAM,gBAAgB,KAAK,GAC3BA,EAAM,KAAA,GACA3E;AAAA,IACR;AAEA,UAAMxM,IAAQmR,EAAM,cAAc,GAC5BlR,IAASkR,EAAM,eAAe,GAE9BH,IAAS,SAAS,cAAc,QAAQ;AAC9C,IAAAA,EAAO,QAAQhR,GACfgR,EAAO,SAAS/Q;AAChB,UAAM+N,IAAU5L,EAAQ,KAAK4O,CAAM,GAC7BlD,IAAS6B,GAAO,UAAU,IAAIzN,EAAO8L,CAAO;AAClD,WAAI2B,GAAO,WACTA,EAAM,OAAO,UAAU3B,GACvB2B,EAAM,YAAY,QAAQ,EAAI,IAGzB,EAAE,MAAM,WAAW,OAAAwB,GAAO,QAAAH,GAAQ,SAAAhD,GAAS,QAAAF,GAAQ,MAAM,EAAE,OAAA9N,GAAO,QAAAC,EAAA,EAAO;AAAA,EAClF;AAGA,iBAAeyP,GAAwB5K,GAAiD9F,GAAoE;AAC1J,UAAM,EAAE,OAAAmS,GAAO,QAAAH,GAAQ,SAAAhD,EAAA,IAAYlJ;AAEnC,IAAAqM,EAAM,eAAe,OAAO,SAASnS,EAAK,YAAY,KAAKA,EAAK,eAAe,IAAIA,EAAK,eAAe,GACvGmS,EAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,GAAGnS,EAAK,UAAU,CAAC,CAAC,GAEpD4E,EAAU,QACZuN,EAAM,OAAO,MAAM,MAAM;AAAA,IAAC,CAAC,IAE3BA,EAAM,MAAA;AAER,UAAM7J,IAAW,OAAO,SAAS6J,EAAM,QAAQ,KAAKA,EAAM,WAAW,IAAIA,EAAM,WAAW,MACpFC,IAAY9J,IAAW,KAAK,IAAItI,EAAK,WAAW,KAAK,IAAIsI,IAAW,MAAM,CAAC,CAAC,IAAItI,EAAK,WAErFqS,IAAUF,EAAM,aAChBG,IAAQ,KAAK,IAAID,IAAUD,CAAS,GACpCG,IAAiB3N,EAAU,QAAQ,OAAO;AAChD,QAAI,OAAO,SAASyN,CAAO,KAAKC,IAAQC,GAAgB;AACtD,UAAI;AACF,QAAAJ,EAAM,cAAcC;AAAA,MACtB,QACM;AAAA,MAEN;AACA,YAAMjB,EAAkBgB,GAAO,UAAU,GAAG,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC9D;AAEA,QAAIA,EAAM,aAAa,MAErB,MAAMhB,EAAkBgB,GAAO,WAAW,GAAG,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC,GACzDA,EAAM,aAAa;AACrB;AAGJ,UAAM9B,IAAM2B,EAAO,WAAW,IAAI;AAClC,IAAK3B,MAELA,EAAI,UAAU8B,GAAO,GAAG,GAAGH,EAAO,OAAOA,EAAO,MAAM,GACtD1B,GAAqBtB,CAAO;AAAA,EAC9B;AAEA,WAASM,GAAiB3L,GAA2C;AACnE,WAAO,IAAI,QAAQ,CAAC4N,MAAY;AAC9B,YAAMiB,IAAM,IAAI,MAAA;AAChB,MAAAA,EAAI,cAAc,aAClBA,EAAI,SAAS,MAAMjB,EAAQnO,EAAQ,KAAKoP,CAAG,CAAC,GAC5CA,EAAI,UAAU,MAAM;AAClB,gBAAQ,KAAK,mCAAmC7O,CAAG,GACnD4N,EAAQ,MAAS;AAAA,MACnB,GACAiB,EAAI,MAAM7O;AAAA,IACZ,CAAC;AAAA,EACH;AAEA,WAAS2M,GAAqBtB,GAAkB;AAC9C,UAAM/J,IAAS+J,EAAQ;AACvB,QAAI,YAAY/J,KAAU,OAAOA,EAAO,UAAW,YAAY;AAC7D,MAAAA,EAAO,OAAA;AACP;AAAA,IACF;AAEA,IAAI,OAAO+J,EAAQ,UAAW,cAC5BA,EAAQ,OAAA;AAAA,EACZ;AAEA,WAASyD,KAAU;AACjB,IAAAtE,EAAA,GACA3B,KAAoB,GACpBa,EAAM,KAAA,GACNI,EAAA,GACAlC,EAAM,QAAQ,EAAE,UAAU,GAAA,CAAM,GAChCK,EAAa,MAAA,GACbC,EAAe,MAAA,GACfF,EAAe,MAAA,GACV3L,EAAK,OACRC,EAAI,QAAA,GAENoM,EAAa,QAAA;AAAA,EACf;AAEA,SAAIrM,EAAK,YACPuH,GAAA,GAEK;AAAA,IACL,KAAAtH;AAAA,IACA,OAAAsL;AAAA,IACA,aAAA5G;AAAA,IACA,UAAA2D;AAAA,IACA,WAAA1D;AAAA,IACA,MAAA2C;AAAA,IACA,OAAA4G;AAAA,IACA,MAAAE;AAAA,IACA,MAAAI;AAAA,IACA,UAAA7B;AAAA,IACA,SAAA6F;AAAA,EAAA;AAEJ;AAEA,SAAStF,GAAkBuF,GAAiC;AAC1D,MAAIC,IAAS,IACTC,IAAU,IACVC,IAAgC,MAChCC,IAAsC;AAyB1C,SAvBY,YAAY;AACtB,IAAKD,MACHA,IAAU,IAAI,QAAQ,CAACtB,MAAY;AACjC,MAAAuB,IAAiBvB;AAAA,IACnB,CAAC;AAEH,UAAMwB,IAAOF;AACb,QAAID;AACF,aAAAD,IAAS,IACFI;AAET,IAAAH,IAAU;AACV;AACE,MAAAD,IAAS,IACT,MAAMD,EAAA;AAAA,WACCC;AACT,WAAAC,IAAU,IACVE,IAAA,GACAD,IAAU,MACVC,IAAiB,MACVC;AAAA,EACT;AAGF;ACl5BA,SAASC,GAAW3J,GAAsD;AACxE,SAAO,OAAOA,KAAU,YACnBA,MAAU,QACV,kBAAkBA,KAClB,aAAaA;AACpB;AAEA,SAAS4J,GAAiB5J,GAAqD;AAC7E,SAAO,OAAO,iBAAmB,OAAeA,aAAiB;AACnE;AAEA,SAAS6J,GAAeC,GAAiE;AACvF,SAAI,OAAOA,KAAU,YAAYA,aAAiB,QAAQH,GAAWG,CAAK,KAAKF,GAAiBE,CAAK,IAC5F,EAAE,QAAQA,EAAA,IACZA;AACT;AAEA,eAAeC,GAAanO,GAA8F;AACxH,MAAI,OAAOA,KAAW,UAAU;AAC9B,UAAMoK,IAAM,MAAM,MAAMpK,CAAM;AAC9B,QAAI,CAACoK,EAAI;AACP,YAAM,IAAI,MAAM,oDAAoD;AACtE,WAAOA,EAAI;AAAA,EACb;AAEA,SAAIpK,aAAkB,OACbA,EAAO,OAAA,IAETA;AACT;AAEA,SAASoO,GACPvB,GACAL,GAC4B;AAC5B,MAAIxE,IAAU;AACd,QAAMqG,IAAW,MAAM;AACrB,IAAIrG,MAEJA,IAAU,IACVwE,EAAA;AAAA,EACF,GAEM8B,IAASzB,EAAO,UAAA;AACtB,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,KAAKC,GAAY;AACrB,YAAM,EAAE,MAAAgB,GAAM,OAAA1J,EAAA,IAAU,MAAMkK,EAAO,KAAA;AACrC,UAAIR,GAAM;AACR,QAAAO,EAAA,GACAvB,EAAW,MAAA;AACX;AAAA,MACF;AACA,MAAAA,EAAW,QAAQ1I,CAAK;AAAA,IAC1B;AAAA,IACA,MAAM,OAAOmK,GAAQ;AACnB,UAAI;AACF,cAAMD,EAAO,OAAOC,CAAM;AAAA,MAC5B,UAAA;AAEE,QAAAF,EAAA;AAAA,MACF;AAAA,IACF;AAAA,EAAA,CACD;AACH;AAEA,eAAsBG,GACpBC,GACA1T,IAA2B,IACC;AAC5B,MAAI0T,EAAO,WAAW;AACpB,UAAM,IAAI,MAAM,4CAA4C;AAE9D,QAAM;AAAA,IACJ,YAAAC;AAAA,IACA,OAAOC;AAAA,IACP,QAAQC;AAAA,IACR,GAAGC;AAAA,EAAA,IACD9T,GAEE+T,IAAaL,EAAO,IAAIR,EAAc,GAEtC,CAAClM,GAAO,GAAGgN,CAAI,IAAID,GACnBE,IAAc,MAAMb,GAAapM,EAAM,MAAM,GAC7CkN,IAAY,IAAIrC,EAAQoC,CAAW;AACzC,QAAMC,EAAU;AAEhB,QAAMlT,IAAQ4S,KAAkB,KAAK,MAAMM,EAAU,KAAK,SAAS,CAAC,GAC9DjT,IAAS4S,KAAmB,KAAK,MAAMK,EAAU,KAAK,UAAU,CAAC;AACvE,MAAI,CAAClT,KAAS,CAACC;AAEX,UAAAiT,EAAU,QAAA,GACJ,IAAI,MAAM,+CAA+C;AAGnE,QAAMC,IAAa,IAAIC,GAAW;AAAA,IAChC,GAAGN;AAAA,IACH,OAAA9S;AAAA,IACA,QAAAC;AAAA,EAAA,CACD;AAED,EAAI0S,KACFQ,EAAW,GAAG,kBAAkBR,CAAU;AAE5C,MAAI3N,IAAS;AAEb,QAAMqO,IAAU,OAAO7O,MAAkB;AACvC,UAAM8C,IAAW9C,EAAK,KAAK;AAC3B,QAAI,CAAC,OAAO,SAAS8C,CAAQ,KAAKA,KAAY;AAC5C,YAAA9C,EAAK,QAAA,GACC,IAAI,MAAM,qCAAqC;AAGvD,UAAMsJ,IAAS,IAAIwF,GAAgB9O,CAAI;AACvC,QAAI;AACF,YAAMsJ,EAAO,OACbA,EAAO,KAAK,IAAI,GAChBA,EAAO,KAAK,IAAI,GAChBA,EAAO,KAAK,IAAI9N,GAChB8N,EAAO,KAAK,IAAI7N,GAChB6N,EAAO,KAAK,SAAS9I,GACrB8I,EAAO,KAAK,WAAWxG,GAEvB,MAAM6L,EAAW,UAAUrF,CAAM,GACjC9I,KAAUsC;AAAA,IACZ,UAAA;AAEE,MAAAwG,EAAO,QAAA;AAAA,IACT;AAAA,EACF;AAEA,MAAI;AACF,UAAMuF,EAAQH,CAAS;AACvB,eAAWpO,KAASkO,GAAM;AACxB,YAAM/O,IAAS,MAAMmO,GAAatN,EAAM,MAAM,GACxCN,IAAO,IAAIqM,EAAQ5M,CAAM;AAC/B,YAAMO,EAAK,OACX,MAAM6O,EAAQ7O,CAAI;AAAA,IACpB;AAAA,EACF,SACOgI,GAAK;AACV,UAAA2G,EAAW,QAAA,GACL3G;AAAA,EACR;AAEA,QAAM+G,IAAUvO,GACV8L,IAASqC,EAAW,OAAO,EAAE,SAAAI,GAAS,GACtC9B,IAAU,MAAM;AACpB,IAAA0B,EAAW,QAAA;AAAA,EACb;AAEA,SAAO;AAAA,IACL,QAAQd,GAAsBvB,GAAQW,CAAO;AAAA,IAC7C,OAAAzR;AAAA,IACA,QAAAC;AAAA,IACA,YAAY,KAAK,MAAMsT,IAAU,GAAI;AAAA,IACrC,SAAA9B;AAAA,EAAA;AAEJ;ACnKO,MAAM+B,EAAmC;AAAA,EACrC;AAAA,EACT;AAAA,EAEiB;AAAA,EACA;AAAA,EACT;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EAEpB,YAAY3S,GAA0B4S,IAAoC,IAAI;AAC5E,SAAK,WAAW5S,GAChB,KAAK,UAAU4S;AAEf,UAAMzT,IAAQyT,EAAQ,SAAS5S,EAAS,OAClCZ,IAASwT,EAAQ,UAAU5S,EAAS,QACpC6S,IAAa1Q,EAAgBnC,CAAQ;AAC3C,SAAK,OAAO;AAAA,MACV,OAAAb;AAAA,MACA,QAAAC;AAAA,MACA,UAAU,KAAK,IAAI,GAAG,KAAK,MAAMyT,IAAa,GAAI,CAAC;AAAA,IAAA,GAGrD,KAAK,QAAQ,KAAK,KAAA;AAAA,EACpB;AAAA,EAEA,MAAc,OAAO;AACnB,UAAM1T,IAAQ,KAAK,QAAQ,SAAS,KAAK,SAAS,OAC5CC,IAAS,KAAK,QAAQ,UAAU,KAAK,SAAS;AACpD,QAAI,CAACD,KAAS,CAACC;AACb,YAAM,IAAI,MAAM,oDAAoD;AAEtE,UAAMhB,IAAM,IAAIC,GAAA;AAChB,UAAMD,EAAI,KAAK;AAAA,MACb,OAAAe;AAAA,MACA,QAAAC;AAAA,MACA,iBAAiB;AAAA,MACjB,GAAG,KAAK,QAAQ;AAAA,IAAA,CACjB,GACDhB,EAAI,OAAO,KAAA,GACX,KAAK,MAAMA;AAEX,UAAM0U,IAAkB,KAAK,QAAQ,mBAAmB,CAAA,GAClDC,IAAW,MAAM9J,GAAe;AAAA,MACpC,UAAU,KAAK;AAAA,MACf,KAAA7K;AAAA,MACA,GAAG0U;AAAA,MACH,UAAU;AAAA,MACV,eAAe;AAAA,MACf,cAAc;AAAA,MACd,iBAAiBA,EAAgB,mBAAmB;AAAA,IAAA,CACrD;AACD,SAAK,WAAWC;AAEhB,UAAMF,IAAaE,EAAS,SAAS;AACrC,gBAAK,OAAO;AAAA,MACV,OAAO3U,EAAI,SAAS;AAAA,MACpB,QAAQA,EAAI,SAAS;AAAA,MACrB,UAAU,KAAK,IAAI,GAAG,KAAK,MAAMyU,IAAa,GAAI,CAAC;AAAA,IAAA,GAG9C,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,KAAKhG,GAIR;AACD,UAAMmG,IAA6B,CAAA;AACnC,QAAI,KAAK;AACP,aAAO,EAAE,OAAOA,GAAY,OAAO,OAAA;AAGrC,QADA,MAAM,KAAK,OACP,CAAC,KAAK;AACR,aAAO,EAAE,OAAOA,GAAY,OAAO,OAAA;AAErC,UAAMC,IAAa,KAAK,KAAK;AAC7B,QAAIpG,KAAQoG;AACV,aAAO,EAAE,OAAOD,GAAY,OAAO,OAAA;AAErC,UAAME,IAAY,KAAK,IAAI,GAAG,KAAK,IAAIrG,GAAMoG,CAAU,CAAC;AACxD,iBAAM,KAAK,SAAS,SAASC,IAAY,GAAI,GAMtC;AAAA,MACL,OALY,IAAI,WAAW,KAAK,SAAS,IAAI,QAAQ;AAAA,QACrD,WAAWrG;AAAA,MAAA,CACZ;AAAA,MAIC,OAAOmG;AAAA,MACP,OAAO;AAAA,IAAA;AAAA,EAEX;AAAA,EAEA,MAAM,QAAuB;AAC3B,UAAMG,IAAO,IAAIR,EAAkB,KAAK,UAAU,KAAK,OAAO;AAC9D,iBAAMQ,EAAK,OACJA;AAAA,EACT;AAAA,EAEA,UAAU;AACR,IAAI,KAAK,cAET,KAAK,YAAY,IACjB,KAAK,UAAU,QAAA,GACf,KAAK,KAAK,QAAQ,EAAI;AAAA,EACxB;AACF;AC3GA,SAAS3B,GACPvB,GACAL,GAC4B;AAC5B,MAAIxE,IAAU;AACd,QAAMqG,IAAW,MAAM;AACrB,IAAIrG,MAEJA,IAAU,IACVwE,EAAA;AAAA,EACF,GAEM8B,IAASzB,EAAO,UAAA;AACtB,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,KAAKC,GAAY;AACrB,YAAM,EAAE,MAAAgB,GAAM,OAAA1J,EAAA,IAAU,MAAMkK,EAAO,KAAA;AACrC,UAAIR,GAAM;AACR,QAAAO,EAAA,GACAvB,EAAW,MAAA;AACX;AAAA,MACF;AACA,MAAAA,EAAW,QAAQ1I,CAAK;AAAA,IAC1B;AAAA,IACA,MAAM,OAAOmK,GAAQ;AACnB,UAAI;AACF,cAAMD,EAAO,OAAOC,CAAM;AAAA,MAC5B,UAAA;AAEE,QAAAF,EAAA;AAAA,MACF;AAAA,IACF;AAAA,EAAA,CACD;AACH;AAEA,eAAsB2B,GACpBpT,GACA7B,IAA+B,IACC;AAChC,QAAM;AAAA,IACJ,OAAO4T;AAAA,IACP,QAAQC;AAAA,IACR,KAAKqB;AAAA,IACL,YAAAvB;AAAA,IACA,aAAAwB;AAAA,IACA,cAAAC;AAAA,IACA,GAAGtB;AAAA,EAAA,IACD9T,GAEEgB,IAAQ4S,KAAkB/R,EAAS,OACnCZ,IAAS4S,KAAmBhS,EAAS;AAC3C,MAAI,CAACb,KAAS,CAACC;AACb,UAAM,IAAI,MAAM,kDAAkD;AAEpE,QAAMyE,IAAMwP,KAAgBrT,EAAS,KAE/BwT,IAAkB,OAAOD,KAAiB,YAC1CjQ,IAAQ2O,EAAe,UAAUuB,IAAkB,SAAY,KAC/DlB,IAAa,IAAIC,GAAW;AAAA,IAChC,GAAGN;AAAA,IACH,OAAA3O;AAAA,IACA,OAAAnE;AAAA,IACA,QAAAC;AAAA,IACA,KAAAyE;AAAA,EAAA,CACD;AAED,EAAIiO,KACFQ,EAAW,GAAG,kBAAkBR,CAAU;AAE5C,MAAInO,GACAsJ;AACJ,MAAI;AAwBF,QAvBAtJ,IAAO,IAAIgP,EAAkB3S,GAAU;AAAA,MACrC,OAAAb;AAAA,MACA,QAAAC;AAAA,MACA,KAAAyE;AAAA,MACA,GAAGyP;AAAA,MACH,iBAAiB;AAAA,QACf,iBAAiB;AAAA,QACjB,GAAGA,GAAa;AAAA,MAAA;AAAA,IAClB,CACD,GACD,MAAM3P,EAAK,OAEXsJ,IAAS,IAAIwF,GAAgB9O,CAAI,GACjC,MAAMsJ,EAAO,OACbA,EAAO,KAAK,SAAS,GACrBA,EAAO,KAAK,WAAWtJ,EAAK,KAAK,UACjCsJ,EAAO,KAAK,IAAI,GAChBA,EAAO,KAAK,IAAI,GAChBA,EAAO,KAAK,IAAItJ,EAAK,KAAK,OAC1BsJ,EAAO,KAAK,IAAItJ,EAAK,KAAK,QAE1B,MAAM2O,EAAW,UAAUrF,GAAQ,EAAE,MAAM,IAAM,GAE7CsG,GAAc;AAChB,YAAME,IAAU,MAAMF,EAAavT,CAAQ;AAC3C,iBAAW0T,KAASD;AAClB,cAAMnB,EAAW,UAAUoB,CAAK;AAAA,IACpC;AAAA,EACF,SACO/H,GAAK;AACV,UAAA2G,EAAW,QAAA,GACL3G;AAAA,EACR;AAEA,QAAM+G,IAAU/O,GAAM,KAAK,YAAY;AACvC,MAAI,CAAC+O;AACH,UAAM,IAAI,MAAM,2CAA2C;AAE7D,QAAMzC,IAASqC,EAAW,OAAO,EAAE,SAAAI,GAAS,GACtC9B,IAAU,MAAM;AACpB,IAAA3D,GAAQ,QAAA,GACRtJ,GAAM,QAAA,GACN2O,EAAW,QAAA;AAAA,EACb;AAEA,SAAO;AAAA,IACL,QAAQd,GAAsBvB,GAAQW,CAAO;AAAA,IAC7C,OAAAzR;AAAA,IACA,QAAAC;AAAA,IACA,YAAY,KAAK,MAAMsT,IAAU,GAAI;AAAA,IACrC,SAAA9B;AAAA,EAAA;AAEJ;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@video-editor/renderer",
3
3
  "type": "module",
4
- "version": "0.0.1-beta.29",
4
+ "version": "0.0.1-beta.30",
5
5
  "exports": {
6
6
  ".": {
7
7
  "import": "./dist/index.js"
@@ -24,12 +24,13 @@
24
24
  "@webav/av-cliper": "^1.2.7",
25
25
  "opfs-tools": "^0.7.4",
26
26
  "pixi.js": "^8.14.3",
27
- "@video-editor/shared": "0.0.1-beta.29",
28
- "@video-editor/protocol": "0.0.1-beta.29"
27
+ "@video-editor/protocol": "0.0.1-beta.30",
28
+ "@video-editor/shared": "0.0.1-beta.30"
29
29
  },
30
30
  "scripts": {
31
31
  "build": "vite build",
32
32
  "build:types": "vue-tsc --declaration --emitDeclarationOnly",
33
- "preview": "vite preview"
33
+ "preview": "vite preview",
34
+ "test": "vitest run --config ./vitest.config.ts"
34
35
  }
35
36
  }