@twick/player-react 0.15.19 → 0.15.21

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.cjs CHANGED
@@ -22,11 +22,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
22
22
 
23
23
  // src/internal.ts
24
24
  var internal_exports = {};
25
- var import_core, import_core2, stylesNew, TEMPLATE, ID, TwickPlayer;
25
+ var import_core, import_gl_runtime, stylesNew, TEMPLATE, ID, TwickPlayer;
26
26
  var init_internal = __esm({
27
27
  "src/internal.ts"() {
28
28
  import_core = require("@twick/core");
29
- import_core2 = require("@twick/core");
29
+ import_gl_runtime = require("@twick/gl-runtime");
30
30
  stylesNew = `
31
31
  .overlay {
32
32
  position: absolute;
@@ -145,6 +145,15 @@ var init_internal = __esm({
145
145
  _looping = true;
146
146
  _volume = 1;
147
147
  volumeChangeRequested = true;
148
+ /** WebGL canvas and context for applying effects to the live preview. */
149
+ effectGlCanvas = null;
150
+ effectContext = null;
151
+ effectReadbackFbo = null;
152
+ /**
153
+ * Optional resolver for active effects at a given time. Set by the host (e.g. twick) so
154
+ * effect logic lives outside twick-base. When set, used for live preview effects.
155
+ */
156
+ getActiveEffectsForTime;
148
157
  constructor() {
149
158
  super();
150
159
  this.root = this.attachShadow({ mode: "open" });
@@ -271,6 +280,84 @@ var init_internal = __esm({
271
280
  this.volumeChangeRequested = false;
272
281
  }
273
282
  };
283
+ /**
284
+ * Resolve active effects for the given time. Uses host-provided callback when set.
285
+ */
286
+ resolveActiveEffectsForTime(timeInSec) {
287
+ if (this.getActiveEffectsForTime) {
288
+ const fps = this.player?.playback.fps ?? 30;
289
+ return this.getActiveEffectsForTime(this.variables, timeInSec, fps);
290
+ }
291
+ return [];
292
+ }
293
+ /**
294
+ * Apply GL effects to the current frame and draw the result back to the stage canvas.
295
+ */
296
+ applyEffectsToFinalBuffer() {
297
+ const canvas = this.stage.finalBuffer;
298
+ const w = canvas.width;
299
+ const h = canvas.height;
300
+ if (w <= 0 || h <= 0) return;
301
+ const activeEffects = this.resolveActiveEffectsForTime(this.time);
302
+ if (activeEffects.length === 0) return;
303
+ if (!this.effectGlCanvas) {
304
+ this.effectGlCanvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(w, h) : document.createElement("canvas");
305
+ this.effectGlCanvas.width = w;
306
+ this.effectGlCanvas.height = h;
307
+ }
308
+ const glCanvas = this.effectGlCanvas;
309
+ if (glCanvas.width !== w || glCanvas.height !== h) {
310
+ glCanvas.width = w;
311
+ glCanvas.height = h;
312
+ }
313
+ if (!this.effectContext) {
314
+ this.effectContext = (0, import_gl_runtime.createEffectContext)(glCanvas);
315
+ }
316
+ const gl = this.effectContext.gl;
317
+ const sourceTexture = gl.createTexture();
318
+ if (!sourceTexture) return;
319
+ gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
320
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
321
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
322
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
323
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
324
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
325
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
326
+ const resultTexture = (0, import_gl_runtime.applyEffects)({
327
+ ctx: this.effectContext,
328
+ sourceTexture,
329
+ width: w,
330
+ height: h,
331
+ effects: activeEffects
332
+ });
333
+ gl.deleteTexture(sourceTexture);
334
+ if (!this.effectReadbackFbo) {
335
+ this.effectReadbackFbo = gl.createFramebuffer();
336
+ }
337
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.effectReadbackFbo);
338
+ gl.framebufferTexture2D(
339
+ gl.FRAMEBUFFER,
340
+ gl.COLOR_ATTACHMENT0,
341
+ gl.TEXTURE_2D,
342
+ resultTexture,
343
+ 0
344
+ );
345
+ const pixels = new Uint8Array(w * h * 4);
346
+ gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
347
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
348
+ const ctx2d = canvas.getContext("2d");
349
+ if (ctx2d) {
350
+ const imageData = ctx2d.createImageData(w, h);
351
+ const rowBytes = w * 4;
352
+ for (let y = 0; y < h; y++) {
353
+ imageData.data.set(
354
+ pixels.subarray((h - 1 - y) * rowBytes, (h - y) * rowBytes),
355
+ y * rowBytes
356
+ );
357
+ }
358
+ ctx2d.putImageData(imageData, 0, 0);
359
+ }
360
+ }
274
361
  /**
275
362
  * Called on every frame.
276
363
  */
@@ -280,6 +367,7 @@ var init_internal = __esm({
280
367
  this.player.playback.currentScene,
281
368
  this.player.playback.previousScene
282
369
  );
370
+ this.applyEffectsToFinalBuffer();
283
371
  this.dispatchEvent(new CustomEvent("timeupdate", { detail: this.time }));
284
372
  const durationInFrames = this.player.playback.duration;
285
373
  if (durationInFrames === this.duration) {
@@ -299,7 +387,7 @@ var init_internal = __esm({
299
387
  const resolutionScale = Number.isFinite(this.quality) && this.quality > 0 ? this.quality : this.defaultSettings.resolutionScale ?? 1;
300
388
  const settings = {
301
389
  ...this.defaultSettings,
302
- size: new import_core2.Vector2(this.width, this.height),
390
+ size: new import_core.Vector2(this.width, this.height),
303
391
  resolutionScale,
304
392
  fps: this.fps
305
393
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal.ts","../src/index.tsx","../src/controls.tsx","../src/icons.tsx","../src/utils.ts"],"sourcesContent":["import type {Project} from '@twick/core';\nimport {Player, Stage, getFullPreviewSettings} from '@twick/core';\n\nimport {Vector2} from '@twick/core';\n\nconst stylesNew = `\n.overlay {\n\tposition: absolute;\n\tleft: 0;\n\tright: 0;\n\ttop: 0;\n\tbottom: 0;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\topacity: 0;\n\ttransition: opacity 0.1s;\n\tz-index: 0;\n }\n .canvas {\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: block;\n\topacity: 1;\n\ttransition: opacity 0.1s;\n }\n`;\n\nconst TEMPLATE = `<style>${stylesNew}</style><div class=\"overlay\"></div>`;\nconst ID = 'twick-player';\n\nenum State {\n Initial = 'initial',\n Loading = 'loading',\n Ready = 'ready',\n Error = 'error',\n}\n\nclass TwickPlayer extends HTMLElement {\n public static get observedAttributes() {\n return [\n 'playing',\n 'variables',\n 'looping',\n 'fps',\n 'quality',\n 'width',\n 'height',\n 'volume',\n ];\n }\n\n public get fps() {\n const attr = this.getAttribute('fps');\n return attr ? parseFloat(attr) : (this.defaultSettings?.fps ?? 60);\n }\n\n public set fps(value: number) {\n if (value != null && Number.isFinite(value)) {\n this.setAttribute('fps', String(value));\n }\n }\n\n public get quality() {\n const attr = this.getAttribute('quality');\n return attr\n ? parseFloat(attr)\n : (this.defaultSettings?.resolutionScale ?? 1);\n }\n\n public set quality(value: number) {\n if (value != null && Number.isFinite(value)) {\n this.setAttribute('quality', String(value));\n }\n }\n\n public get width() {\n const attr = this.getAttribute('width');\n return attr ? parseFloat(attr) : (this.defaultSettings?.size.width ?? 0);\n }\n\n public set width(value: number) {\n if (Number.isFinite(value)) {\n this.setAttribute('width', String(value));\n }\n }\n\n public get height() {\n const attr = this.getAttribute('height');\n return attr ? parseFloat(attr) : (this.defaultSettings?.size.height ?? 0);\n }\n\n public set height(value: number) {\n if (Number.isFinite(value)) {\n this.setAttribute('height', String(value));\n }\n }\n\n private get variables() {\n try {\n const attr = this.getAttribute('variables');\n return attr ? JSON.parse(attr) : {};\n } catch {\n this.project?.logger.warn(`Project variables could not be parsed.`);\n return {};\n }\n }\n\n public get volume() {\n return this._volume;\n }\n\n public set volume(value: number) {\n if (value != null) {\n this.setAttribute('volume', String(value));\n }\n }\n\n public set playing(value: boolean | string) {\n this.setAttribute(\n 'playing',\n value === true || value === 'true' ? 'true' : 'false',\n );\n }\n\n public set looping(value: boolean | string) {\n this.setAttribute(\n 'looping',\n value === true || value === 'true' ? 'true' : 'false',\n );\n }\n\n private readonly root: ShadowRoot;\n private readonly canvas: HTMLCanvasElement;\n private readonly overlay: HTMLCanvasElement;\n\n private state = State.Initial;\n private project: Project | null = null;\n private player: Player | null = null;\n private defaultSettings:\n | ReturnType<typeof getFullPreviewSettings>\n | undefined;\n private abortController: AbortController | null = null;\n private _playing = false;\n private stage = new Stage();\n\n private time: number = 0;\n private duration: number = 0; // in frames\n private _looping = true;\n private _volume = 1;\n private volumeChangeRequested = true;\n\n public constructor() {\n super();\n this.root = this.attachShadow({mode: 'open'});\n this.root.innerHTML = TEMPLATE;\n\n this.overlay = this.root.querySelector('.overlay')!;\n this.canvas = this.stage.finalBuffer;\n this.canvas.classList.add('canvas');\n this.root.prepend(this.canvas);\n this.setState(State.Initial);\n }\n\n public setProject(project: Project) {\n this.updateProject(project);\n }\n\n private setState(state: State) {\n this.state = state;\n this.setPlaying(this._playing);\n }\n\n private setPlaying(value: boolean) {\n if (this.state === State.Ready && value) {\n this.player?.togglePlayback(true);\n this._playing = true;\n } else {\n this.player?.togglePlayback(false);\n this._playing = false;\n }\n }\n\n private async updateProject(project: Project) {\n const playing = this._playing;\n this.setState(State.Initial);\n\n this.abortController?.abort();\n this.abortController = new AbortController();\n\n this.project = project;\n this.defaultSettings = getFullPreviewSettings(this.project);\n\n const player = new Player(this.project);\n player.setVariables(this.variables);\n player.toggleLoop(this._looping);\n\n this.player?.onRender.unsubscribe(this.render);\n this.player?.onFrameChanged.unsubscribe(this.handleFrameChanged);\n this.player?.togglePlayback(false);\n this.player?.deactivate();\n\n this.player = player;\n this.updateSettings();\n\n this.setState(State.Ready);\n this.dispatchEvent(new CustomEvent('playerready', {detail: this.player}));\n\n // Restore previous state\n this.setPlaying(playing);\n this.player.onRender.subscribe(this.render);\n this.player.onFrameChanged.subscribe(this.handleFrameChanged);\n }\n\n public attributeChangedCallback(name: string, _: any, newValue: any) {\n switch (name) {\n case 'playing':\n this.setPlaying(newValue === 'true');\n break;\n case 'variables':\n this.player?.setVariables(this.variables);\n this.player?.requestSeek(this.player.playback.frame);\n this.player?.playback.reload();\n break;\n case 'looping':\n this._looping = newValue === 'true';\n this.player?.toggleLoop(newValue === 'true');\n break;\n case 'fps':\n case 'quality':\n case 'width':\n case 'height':\n this.updateSettings();\n break;\n case 'volume':\n this._volume = newValue;\n this.volumeChangeRequested = true;\n }\n }\n\n /**\n * Runs when the element is removed from the DOM.\n */\n public disconnectedCallback() {\n this.player?.deactivate();\n this.player?.onRender.unsubscribe(this.render);\n\n this.removeEventListener('seekto', this.handleSeekTo);\n this.removeEventListener('volumechange', this.handleVolumeChange);\n }\n\n /**\n * Runs when the element is added to the DOM.\n */\n public connectedCallback() {\n this.player?.activate();\n this.player?.onRender.subscribe(this.render);\n\n this.addEventListener('seekto', this.handleSeekTo);\n this.addEventListener('volumechange', this.handleVolumeChange);\n }\n\n /**\n * Triggered by the timeline.\n */\n private handleSeekTo = (event: Event) => {\n if (!this.project) {\n return;\n }\n\n const e = event as CustomEvent;\n const timeSec = e.detail as number;\n const frame = timeSec * this.player!.playback.fps;\n this.time = timeSec;\n this.player?.requestSeek(frame);\n this.volumeChangeRequested = true;\n };\n\n private handleVolumeChange = (event: Event) => {\n if (!this.project) {\n return;\n }\n\n const e = event as CustomEvent;\n this._volume = e.detail;\n\n this.player?.playback.currentScene.adjustVolume(this._volume);\n };\n\n /**\n * Triggered by the player.\n */\n private handleFrameChanged = (frame: number) => {\n if (!this.project || !this.player) {\n return;\n }\n this.time = frame / this.player.playback.fps;\n\n if (this.volumeChangeRequested || frame === 0) {\n this.player?.playback.currentScene.adjustVolume(this._volume);\n this.volumeChangeRequested = false;\n }\n };\n\n /**\n * Called on every frame.\n */\n private render = async () => {\n if (this.player && this.project) {\n await this.stage.render(\n this.player.playback.currentScene,\n this.player.playback.previousScene,\n );\n\n this.dispatchEvent(new CustomEvent('timeupdate', {detail: this.time}));\n\n const durationInFrames = this.player.playback.duration;\n if (durationInFrames === this.duration) {\n return;\n }\n\n this.duration = durationInFrames;\n\n const durationInSeconds = durationInFrames / this.player.playback.fps;\n this.dispatchEvent(\n new CustomEvent('duration', {detail: durationInSeconds}),\n );\n }\n };\n\n private updateSettings() {\n if (!this.defaultSettings) {\n return;\n }\n\n // Use the requested quality (resolutionScale) instead of forcing 1,\n // so the preview canvas can render at higher internal resolution.\n const resolutionScale =\n Number.isFinite(this.quality) && this.quality > 0\n ? this.quality\n : this.defaultSettings.resolutionScale ?? 1;\n\n const settings = {\n ...this.defaultSettings,\n size: new Vector2(this.width, this.height),\n resolutionScale,\n fps: this.fps,\n };\n this.stage.configure(settings);\n this.player?.configure(settings);\n }\n}\n\nif (!customElements.get(ID)) {\n customElements.define(ID, TwickPlayer);\n}\n","'use client';\nimport type {Player as CorePlayer, Project} from '@twick/core';\nimport type {ComponentProps} from 'react';\nimport {useCallback, useEffect, useRef, useState} from 'react';\nimport {Controls} from './controls';\nimport './index.css';\nimport {shouldShowControls} from './utils';\n\ninterface TwickPlayerProps {\n playing?: boolean | string;\n variables?: string;\n looping?: boolean | string;\n width?: number;\n height?: number;\n quality?: number;\n fps?: number;\n volume?: number;\n}\n\ndeclare global {\n namespace JSX {\n interface IntrinsicElements {\n // eslint-disable-next-line\n 'twick-player': TwickPlayerProps & ComponentProps<'div'>;\n }\n }\n}\n\ninterface PlayerProps {\n project: Project;\n controls?: boolean;\n variables?: Record<string, any>;\n playing?: boolean;\n currentTime?: number;\n volume?: number;\n looping?: boolean;\n fps?: number;\n\n width?: number;\n height?: number;\n quality?: number;\n timeDisplayFormat?: 'MM:SS' | 'MM:SS.mm' | 'MM:SS.m';\n\n onDurationChange?: (duration: number) => void;\n onTimeUpdate?: (currentTime: number) => void;\n onPlayerReady?: (player: CorePlayer) => void;\n onPlayerResize?: (rect: DOMRectReadOnly) => void;\n}\n\nexport function Player({\n project,\n controls = true,\n variables = {},\n playing = false,\n currentTime = 0,\n volume = 1,\n looping = true,\n fps = 30,\n\n width = undefined,\n height = undefined,\n quality = undefined,\n timeDisplayFormat = 'MM:SS',\n\n onDurationChange = () => {},\n onTimeUpdate = () => {},\n onPlayerReady = () => {},\n onPlayerResize = () => {},\n}: PlayerProps) {\n const [playingState, setPlaying] = useState(playing);\n const [isMouseOver, setIsMouseOver] = useState(false);\n const [currentTimeState, setCurrentTime] = useState(currentTime);\n const [volumeState, setVolumeState] = useState(volume);\n const [duration, setDuration] = useState(-1);\n\n const focus = useRef(false);\n const playerRef = useRef<HTMLDivElement | null>(null);\n const wrapperRef = useRef<HTMLDivElement | null>(null);\n const lastRect = useRef<DOMRectReadOnly | null>(null);\n const lastLoggedTimeRef = useRef<number | null>(null);\n\n const onClickHandler = controls ? () => setPlaying(prev => !prev) : undefined;\n\n /**\n * Sync the playing prop with the player's own state when it changes.\n */\n useEffect(() => {\n setPlaying(playing);\n }, [playing]);\n\n /**\n * Sync the current time with the player's own state.\n */\n useEffect(() => {\n const diff = Math.abs(currentTime - currentTimeState);\n if (diff > 0.05) {\n setForcedTime(currentTime);\n }\n }, [currentTime]);\n\n useEffect(() => {\n setForcedVolume(volume);\n }, [volume]);\n\n /**\n * Set variables via setAttribute - the twick-player custom element's variables\n * property is read-only (getter only). React would fail if we passed it as a prop.\n */\n const variablesJson = JSON.stringify(variables);\n useEffect(() => {\n if (playerRef.current) {\n playerRef.current.setAttribute('variables', variablesJson);\n }\n }, [variablesJson]);\n\n /**\n * Receives the current time of the video from the player.\n * Use refs to ensure we always call the latest callbacks.\n */\n const onTimeUpdateRef = useRef(onTimeUpdate);\n const onDurationChangeRef = useRef(onDurationChange);\n \n // Update refs when callbacks change\n useEffect(() => {\n onTimeUpdateRef.current = onTimeUpdate;\n }, [onTimeUpdate]);\n \n useEffect(() => {\n onDurationChangeRef.current = onDurationChange;\n }, [onDurationChange]);\n\n const handleTimeUpdate = useCallback((event: Event) => {\n const e = event as CustomEvent;\n const t = e.detail as number;\n const last = lastLoggedTimeRef.current;\n if (last === null || Math.abs(t - last) > 0.05) {\n lastLoggedTimeRef.current = t;\n }\n setCurrentTime(t);\n onTimeUpdateRef.current(t);\n }, []);\n\n /**\n * Receives the duration of the video from the player.\n */\n const handleDurationUpdate = useCallback((event: Event) => {\n const e = event as CustomEvent;\n setDuration(e.detail);\n onDurationChangeRef.current(e.detail);\n }, []);\n\n /**\n * Play and pause using the space key.\n */\n const handleKeyDown = useCallback((event: KeyboardEvent) => {\n if (event.code === 'Space' && focus.current) {\n event.preventDefault();\n setPlaying(prev => !prev);\n }\n }, []);\n\n const onPlayerReadyRef = useRef(onPlayerReady);\n \n useEffect(() => {\n onPlayerReadyRef.current = onPlayerReady;\n }, [onPlayerReady]);\n\n const handlePlayerReady = useCallback((event: Event) => {\n const player = (event as CustomEvent).detail;\n if (player) {\n onPlayerReadyRef.current(player);\n }\n \n // Ensure event listeners are attached when player becomes ready\n // This is a fallback in case listeners weren't attached earlier\n const playerElement = playerRef.current;\n if (playerElement) {\n // Remove and re-add to ensure they're attached\n playerElement.removeEventListener('timeupdate', handleTimeUpdate);\n playerElement.removeEventListener('duration', handleDurationUpdate);\n playerElement.addEventListener('timeupdate', handleTimeUpdate);\n playerElement.addEventListener('duration', handleDurationUpdate);\n }\n }, [handleTimeUpdate, handleDurationUpdate]);\n\n const handlePlayerResize = useCallback(\n (entries: ResizeObserverEntry[]) => {\n const [firstEntry] = entries;\n if (!firstEntry || !wrapperRef.current) {\n return;\n }\n\n const newRect = wrapperRef.current.getBoundingClientRect();\n if (\n !lastRect.current ||\n newRect.width !== lastRect.current.width ||\n newRect.height !== lastRect.current.height ||\n newRect.x !== lastRect.current.x ||\n newRect.y !== lastRect.current.y\n ) {\n lastRect.current = newRect;\n onPlayerResize(newRect);\n }\n },\n [onPlayerResize],\n );\n\n useEffect(() => {\n if (!wrapperRef.current) return;\n\n const resizeObserver = new ResizeObserver(handlePlayerResize);\n resizeObserver.observe(wrapperRef.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, [handlePlayerResize]);\n\n /**\n * Import the player and add all event listeners.\n */\n useEffect(() => {\n let cleanup: (() => void) | null = null;\n\n const setupListeners = () => {\n const player = playerRef.current;\n if (!player) return;\n\n // Remove any existing listeners first to avoid duplicates\n player.removeEventListener('timeupdate', handleTimeUpdate);\n player.removeEventListener('duration', handleDurationUpdate);\n player.removeEventListener('playerready', handlePlayerReady);\n\n // Add event listeners\n player.addEventListener('timeupdate', handleTimeUpdate);\n player.addEventListener('duration', handleDurationUpdate);\n player.addEventListener('playerready', handlePlayerReady);\n document.addEventListener('keydown', handleKeyDown);\n\n cleanup = () => {\n player.removeEventListener('timeupdate', handleTimeUpdate);\n player.removeEventListener('duration', handleDurationUpdate);\n player.removeEventListener('playerready', handlePlayerReady);\n document.removeEventListener('keydown', handleKeyDown);\n };\n };\n\n // Import the custom element definition\n import('./internal').then(() => {\n // Wait for the next tick to ensure the element is in the DOM\n // Use requestAnimationFrame to ensure the custom element is ready\n requestAnimationFrame(() => {\n if (playerRef.current) {\n (playerRef.current as any).setProject(project);\n // Set up listeners after the element is ready\n setupListeners();\n }\n });\n });\n\n // Also set up listeners immediately if element already exists\n // This handles the case where the element was already in the DOM\n if (playerRef.current) {\n setupListeners();\n }\n\n return () => {\n if (cleanup) {\n cleanup();\n }\n };\n }, [project, handleTimeUpdate, handleDurationUpdate, handlePlayerReady, handleKeyDown]);\n\n /**\n * When the forced time changes, seek to that time.\n */\n function setForcedTime(forcedTime: number) {\n if (playerRef.current) {\n playerRef.current.dispatchEvent(\n new CustomEvent('seekto', {detail: forcedTime}),\n );\n }\n }\n\n function setForcedVolume(volume: number) {\n setVolumeState(volume);\n if (playerRef.current) {\n playerRef.current.dispatchEvent(\n new CustomEvent('volumechange', {detail: volume}),\n );\n }\n }\n\n return (\n <div className=\"twick-player-root w-full h-full\" style={{display: 'contents'}}>\n <div\n ref={wrapperRef}\n className=\"relative cursor-default w-full h-full focus:outline-none\"\n onFocus={() => (focus.current = true)}\n onBlur={() => (focus.current = false)}\n tabIndex={0}\n onMouseEnter={() => setIsMouseOver(true)}\n onMouseLeave={() => setIsMouseOver(false)}\n >\n <div className=\"relative w-full h-full\">\n <twick-player\n ref={playerRef}\n quality={quality}\n fps={fps}\n width={width}\n height={height}\n volume={volumeState}\n playing={playingState}\n looping={looping}\n onClick={onClickHandler}\n />\n <div\n className={`absolute bottom-0 w-full transition-opacity duration-200 ${\n shouldShowControls(playingState, isMouseOver, !controls)\n ? 'opacity-100'\n : 'opacity-0'\n }`}\n >\n <Controls\n duration={duration}\n playing={playingState}\n setPlaying={setPlaying}\n currentTime={currentTimeState}\n setForcedTime={setForcedTime}\n timeDisplayFormat={timeDisplayFormat}\n volume={volumeState}\n setVolume={setForcedVolume}\n />\n </div>\n </div>\n </div>\n </div>\n );\n}\n","import {useState} from 'react';\nimport {MutedSoundIcon, PauseButton, PlayButton, SoundIcon} from './icons';\nimport {getFormattedTime} from './utils';\n\nfunction PlayPause({\n playing,\n setPlaying,\n}: {\n playing: boolean;\n setPlaying: (playing: boolean) => void;\n}) {\n return (\n <button type=\"button\" className=\"p-1\" onClick={() => setPlaying(!playing)}>\n {playing ? <PauseButton /> : <PlayButton />}\n </button>\n );\n}\n\nfunction VolumeSlider({\n volume,\n setVolume,\n}: {\n volume: number;\n setVolume: (volume: number) => void;\n}) {\n const [isHovering, setIsHovering] = useState(false);\n const [isInteracting, setIsInteracting] = useState(false);\n const [previousVolume, setPreviousVolume] = useState(1);\n\n const handleIconClick = () => {\n if (volume > 0) {\n setPreviousVolume(volume);\n setVolume(0);\n } else {\n setVolume(previousVolume);\n }\n };\n\n return (\n <div\n className=\"flex items-center space-x-2 relative\"\n onMouseEnter={() => setIsHovering(true)}\n onMouseLeave={() => {\n if (!isInteracting) {\n setIsHovering(false);\n }\n }}\n >\n <div\n className=\"w-6 h-6 flex items-center justify-center cursor-pointer\"\n onClick={handleIconClick}\n >\n {volume === 0 ? <MutedSoundIcon /> : <SoundIcon />}\n </div>\n {(isHovering || isInteracting) && (\n <div className=\"flex items-center h-1.5 whitespace-nowrap\">\n <div className=\"relative w-20 h-1.5 bg-gray-300 rounded-full\">\n <div\n className=\"absolute top-0 left-0 h-full bg-gray-100 rounded-full\"\n style={{width: `${volume * 100}%`}}\n />\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.01}\n value={volume}\n onChange={e => {\n const newVolume = Number(e.target.value);\n setVolume(newVolume);\n if (newVolume > 0) {\n setPreviousVolume(newVolume);\n }\n }}\n onMouseDown={() => setIsInteracting(true)}\n onMouseUp={() => setIsInteracting(false)}\n onMouseLeave={() => setIsInteracting(false)}\n className=\"absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer\"\n />\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction Timeline({\n currentTime,\n duration,\n setCurrentTime,\n}: {\n currentTime: number;\n duration: number;\n setCurrentTime: (currentTime: number) => void;\n}) {\n const progressPercentage = (currentTime / duration) * 100;\n\n return (\n <div className=\"relative flex-1 w-full h-1.5 bg-gray-300 rounded-full overflow-hidden\">\n <div\n className=\"absolute top-0 left-0 h-full bg-gray-100\"\n style={{width: `${progressPercentage}%`}}\n />\n <input\n type=\"range\"\n value={currentTime}\n min={0}\n max={duration}\n step={0.01}\n className=\"absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer\"\n onChange={event => setCurrentTime(Number(event.target.value))}\n />\n </div>\n );\n}\n\nexport function Controls({\n duration,\n playing,\n setPlaying,\n currentTime,\n setForcedTime,\n timeDisplayFormat,\n volume,\n setVolume,\n}: {\n duration: number;\n playing: boolean;\n setPlaying: (playing: boolean) => void;\n currentTime: number;\n setForcedTime: (currentTime: number) => void;\n timeDisplayFormat: 'MM:SS' | 'MM:SS.m' | 'MM:SS.mm';\n volume: number;\n setVolume: (volume: number) => void;\n}) {\n return (\n <div className=\"text-white p-4 flex-col space-y-2 bg-gradient-to-t from-gray-500 to-transparent\">\n <div className=\"flex items-center space-x-2\">\n <PlayPause playing={playing} setPlaying={setPlaying} />\n <div className=\"flex items-center space-x-2\">\n <VolumeSlider volume={volume} setVolume={setVolume} />\n <div>\n <span>\n {getFormattedTime(currentTime, duration, timeDisplayFormat)}\n </span>\n </div>\n </div>\n <div className=\"flex-grow\" />\n </div>\n <Timeline\n currentTime={currentTime}\n duration={duration}\n setCurrentTime={setForcedTime}\n />\n </div>\n );\n}\n","export function PlayButton() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className=\"w-6 h-6\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.5 5.653c0-1.427 1.529-2.33 2.779-1.643l11.54 6.347c1.295.712 1.295 2.573 0 3.286L7.28 19.99c-1.25.687-2.779-.217-2.779-1.643V5.653Z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n}\n\nexport function PauseButton() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className=\"w-6 h-6\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M6.75 5.25a.75.75 0 0 1 .75-.75H9a.75.75 0 0 1 .75.75v13.5a.75.75 0 0 1-.75.75H7.5a.75.75 0 0 1-.75-.75V5.25Zm7.5 0A.75.75 0 0 1 15 4.5h1.5a.75.75 0 0 1 .75.75v13.5a.75.75 0 0 1-.75.75H15a.75.75 0 0 1-.75-.75V5.25Z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n}\n\nexport function SoundIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className=\"p-w-6 p-h-6\"\n >\n <path d=\"M18.36,19.36a1,1,0,0,1-.7-.29,1,1,0,0,1,0-1.41,8,8,0,0,0,0-11.32,1,1,0,0,1,1.41-1.41,10,10,0,0,1,0,14.14A1,1,0,0,1,18.36,19.36Z\" />\n <path d=\"M15.54,16.54a1,1,0,0,1-.71-.3,1,1,0,0,1,0-1.41,4,4,0,0,0,0-5.66,1,1,0,0,1,1.41-1.41,6,6,0,0,1,0,8.48A1,1,0,0,1,15.54,16.54Z\" />\n <path d=\"M11.38,4.08a1,1,0,0,0-1.09.21L6.59,8H4a2,2,0,0,0-2,2v4a2,2,0,0,0,2,2H6.59l3.7,3.71A1,1,0,0,0,11,20a.84.84,0,0,0,.38-.08A1,1,0,0,0,12,19V5A1,1,0,0,0,11.38,4.08Z\" />\n </svg>\n );\n}\n\nexport function MutedSoundIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className=\"p-w-6 p-h-6\"\n >\n <path d=\"M11.38,4.08a1,1,0,0,0-1.09.21L6.59,8H4a2,2,0,0,0-2,2v4a2,2,0,0,0,2,2H6.59l3.7,3.71A1,1,0,0,0,11,20a.84.84,0,0,0,.38-.08A1,1,0,0,0,12,19V5A1,1,0,0,0,11.38,4.08Z\" />\n <path d=\"M16,15.5a1,1,0,0,1-.71-.29,1,1,0,0,1,0-1.42l5-5a1,1,0,0,1,1.42,1.42l-5,5A1,1,0,0,1,16,15.5Z\" />\n <path d=\"M21,15.5a1,1,0,0,1-.71-.29l-5-5a1,1,0,0,1,1.42-1.42l5,5a1,1,0,0,1,0,1.42A1,1,0,0,1,21,15.5Z\" />\n </svg>\n );\n}\n","export function getFormattedTime(\n timeInSeconds: number,\n absoluteTimeInSeconds: number,\n timeDisplayFormat: 'MM:SS' | 'MM:SS.mm' | 'MM:SS.m',\n) {\n function toFormattedTime(timeInSeconds: number) {\n const minutes = Math.floor(timeInSeconds / 60);\n const seconds = Math.floor(timeInSeconds % 60)\n .toString()\n .padStart(2, '0');\n const milliseconds = Math.floor((timeInSeconds % 1) * 1000)\n .toString()\n .padStart(3, '0');\n\n if (timeDisplayFormat === 'MM:SS') {\n return `${minutes}:${seconds}`;\n }\n\n if (timeDisplayFormat === 'MM:SS.m') {\n return `${minutes}:${seconds}.${milliseconds[0]}`;\n }\n\n if (timeDisplayFormat === 'MM:SS.mm') {\n return `${minutes}:${seconds}.${milliseconds.slice(0, 2)}`;\n }\n }\n\n return `${toFormattedTime(timeInSeconds)} / ${toFormattedTime(absoluteTimeInSeconds)}`;\n}\n\nexport function shouldShowControls(\n playing: boolean,\n isMouseOver: boolean,\n areControlsDisabled: boolean,\n) {\n if (areControlsDisabled) {\n return false;\n }\n\n return !playing || isMouseOver;\n}\n"],"mappings":"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","names":["import_core","Player","import_react","timeInSeconds","import_jsx_runtime","import_jsx_runtime","Player","volume"]}
1
+ {"version":3,"sources":["../src/internal.ts","../src/index.tsx","../src/controls.tsx","../src/icons.tsx","../src/utils.ts"],"sourcesContent":["import type {Project} from '@twick/core';\nimport {Player, Stage, getFullPreviewSettings, Vector2} from '@twick/core';\nimport {\n applyEffects,\n createEffectContext,\n type ActiveEffect,\n type EffectContext,\n} from '@twick/gl-runtime';\n\nconst stylesNew = `\n.overlay {\n\tposition: absolute;\n\tleft: 0;\n\tright: 0;\n\ttop: 0;\n\tbottom: 0;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\topacity: 0;\n\ttransition: opacity 0.1s;\n\tz-index: 0;\n }\n .canvas {\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: block;\n\topacity: 1;\n\ttransition: opacity 0.1s;\n }\n`;\n\nconst TEMPLATE = `<style>${stylesNew}</style><div class=\"overlay\"></div>`;\nconst ID = 'twick-player';\n\nenum State {\n Initial = 'initial',\n Loading = 'loading',\n Ready = 'ready',\n Error = 'error',\n}\n\nclass TwickPlayer extends HTMLElement {\n public static get observedAttributes() {\n return [\n 'playing',\n 'variables',\n 'looping',\n 'fps',\n 'quality',\n 'width',\n 'height',\n 'volume',\n ];\n }\n\n public get fps() {\n const attr = this.getAttribute('fps');\n return attr ? parseFloat(attr) : (this.defaultSettings?.fps ?? 60);\n }\n\n public set fps(value: number) {\n if (value != null && Number.isFinite(value)) {\n this.setAttribute('fps', String(value));\n }\n }\n\n public get quality() {\n const attr = this.getAttribute('quality');\n return attr\n ? parseFloat(attr)\n : (this.defaultSettings?.resolutionScale ?? 1);\n }\n\n public set quality(value: number) {\n if (value != null && Number.isFinite(value)) {\n this.setAttribute('quality', String(value));\n }\n }\n\n public get width() {\n const attr = this.getAttribute('width');\n return attr ? parseFloat(attr) : (this.defaultSettings?.size.width ?? 0);\n }\n\n public set width(value: number) {\n if (Number.isFinite(value)) {\n this.setAttribute('width', String(value));\n }\n }\n\n public get height() {\n const attr = this.getAttribute('height');\n return attr ? parseFloat(attr) : (this.defaultSettings?.size.height ?? 0);\n }\n\n public set height(value: number) {\n if (Number.isFinite(value)) {\n this.setAttribute('height', String(value));\n }\n }\n\n private get variables() {\n try {\n const attr = this.getAttribute('variables');\n return attr ? JSON.parse(attr) : {};\n } catch {\n this.project?.logger.warn(`Project variables could not be parsed.`);\n return {};\n }\n }\n\n public get volume() {\n return this._volume;\n }\n\n public set volume(value: number) {\n if (value != null) {\n this.setAttribute('volume', String(value));\n }\n }\n\n public set playing(value: boolean | string) {\n this.setAttribute(\n 'playing',\n value === true || value === 'true' ? 'true' : 'false',\n );\n }\n\n public set looping(value: boolean | string) {\n this.setAttribute(\n 'looping',\n value === true || value === 'true' ? 'true' : 'false',\n );\n }\n\n private readonly root: ShadowRoot;\n private readonly canvas: HTMLCanvasElement;\n private readonly overlay: HTMLCanvasElement;\n\n private state = State.Initial;\n private project: Project | null = null;\n private player: Player | null = null;\n private defaultSettings:\n | ReturnType<typeof getFullPreviewSettings>\n | undefined;\n private abortController: AbortController | null = null;\n private _playing = false;\n private stage = new Stage();\n\n private time: number = 0;\n private duration: number = 0; // in frames\n private _looping = true;\n private _volume = 1;\n private volumeChangeRequested = true;\n\n /** WebGL canvas and context for applying effects to the live preview. */\n private effectGlCanvas: HTMLCanvasElement | OffscreenCanvas | null = null;\n private effectContext: EffectContext | null = null;\n private effectReadbackFbo: WebGLFramebuffer | null = null;\n\n /**\n * Optional resolver for active effects at a given time. Set by the host (e.g. twick) so\n * effect logic lives outside twick-base. When set, used for live preview effects.\n */\n public getActiveEffectsForTime?: (\n variables: Record<string, unknown>,\n timeInSec: number,\n fps: number,\n ) => Array<{fragment: string; progress: number; intensity: number}>;\n\n public constructor() {\n super();\n this.root = this.attachShadow({mode: 'open'});\n this.root.innerHTML = TEMPLATE;\n\n this.overlay = this.root.querySelector('.overlay')!;\n this.canvas = this.stage.finalBuffer;\n this.canvas.classList.add('canvas');\n this.root.prepend(this.canvas);\n this.setState(State.Initial);\n }\n\n public setProject(project: Project) {\n this.updateProject(project);\n }\n\n private setState(state: State) {\n this.state = state;\n this.setPlaying(this._playing);\n }\n\n private setPlaying(value: boolean) {\n if (this.state === State.Ready && value) {\n this.player?.togglePlayback(true);\n this._playing = true;\n } else {\n this.player?.togglePlayback(false);\n this._playing = false;\n }\n }\n\n private async updateProject(project: Project) {\n const playing = this._playing;\n this.setState(State.Initial);\n\n this.abortController?.abort();\n this.abortController = new AbortController();\n\n this.project = project;\n this.defaultSettings = getFullPreviewSettings(this.project);\n\n const player = new Player(this.project);\n player.setVariables(this.variables);\n player.toggleLoop(this._looping);\n\n this.player?.onRender.unsubscribe(this.render);\n this.player?.onFrameChanged.unsubscribe(this.handleFrameChanged);\n this.player?.togglePlayback(false);\n this.player?.deactivate();\n\n this.player = player;\n this.updateSettings();\n\n this.setState(State.Ready);\n this.dispatchEvent(new CustomEvent('playerready', {detail: this.player}));\n\n // Restore previous state\n this.setPlaying(playing);\n this.player.onRender.subscribe(this.render);\n this.player.onFrameChanged.subscribe(this.handleFrameChanged);\n }\n\n public attributeChangedCallback(name: string, _: any, newValue: any) {\n switch (name) {\n case 'playing':\n this.setPlaying(newValue === 'true');\n break;\n case 'variables':\n this.player?.setVariables(this.variables);\n this.player?.requestSeek(this.player.playback.frame);\n this.player?.playback.reload();\n break;\n case 'looping':\n this._looping = newValue === 'true';\n this.player?.toggleLoop(newValue === 'true');\n break;\n case 'fps':\n case 'quality':\n case 'width':\n case 'height':\n this.updateSettings();\n break;\n case 'volume':\n this._volume = newValue;\n this.volumeChangeRequested = true;\n }\n }\n\n /**\n * Runs when the element is removed from the DOM.\n */\n public disconnectedCallback() {\n this.player?.deactivate();\n this.player?.onRender.unsubscribe(this.render);\n\n this.removeEventListener('seekto', this.handleSeekTo);\n this.removeEventListener('volumechange', this.handleVolumeChange);\n }\n\n /**\n * Runs when the element is added to the DOM.\n */\n public connectedCallback() {\n this.player?.activate();\n this.player?.onRender.subscribe(this.render);\n\n this.addEventListener('seekto', this.handleSeekTo);\n this.addEventListener('volumechange', this.handleVolumeChange);\n }\n\n /**\n * Triggered by the timeline.\n */\n private handleSeekTo = (event: Event) => {\n if (!this.project) {\n return;\n }\n\n const e = event as CustomEvent;\n const timeSec = e.detail as number;\n const frame = timeSec * this.player!.playback.fps;\n this.time = timeSec;\n this.player?.requestSeek(frame);\n this.volumeChangeRequested = true;\n };\n\n private handleVolumeChange = (event: Event) => {\n if (!this.project) {\n return;\n }\n\n const e = event as CustomEvent;\n this._volume = e.detail;\n\n this.player?.playback.currentScene.adjustVolume(this._volume);\n };\n\n /**\n * Triggered by the player.\n */\n private handleFrameChanged = (frame: number) => {\n if (!this.project || !this.player) {\n return;\n }\n this.time = frame / this.player.playback.fps;\n\n if (this.volumeChangeRequested || frame === 0) {\n this.player?.playback.currentScene.adjustVolume(this._volume);\n this.volumeChangeRequested = false;\n }\n };\n\n /**\n * Resolve active effects for the given time. Uses host-provided callback when set.\n */\n private resolveActiveEffectsForTime(timeInSec: number): ActiveEffect[] {\n if (this.getActiveEffectsForTime) {\n const fps = this.player?.playback.fps ?? 30;\n return this.getActiveEffectsForTime(this.variables, timeInSec, fps);\n }\n return [];\n }\n\n /**\n * Apply GL effects to the current frame and draw the result back to the stage canvas.\n */\n private applyEffectsToFinalBuffer(): void {\n const canvas = this.stage.finalBuffer;\n const w = canvas.width;\n const h = canvas.height;\n if (w <= 0 || h <= 0) return;\n\n const activeEffects = this.resolveActiveEffectsForTime(this.time);\n if (activeEffects.length === 0) return;\n\n if (!this.effectGlCanvas) {\n this.effectGlCanvas = typeof OffscreenCanvas !== 'undefined'\n ? new OffscreenCanvas(w, h)\n : document.createElement('canvas');\n (this.effectGlCanvas as HTMLCanvasElement).width = w;\n (this.effectGlCanvas as HTMLCanvasElement).height = h;\n }\n const glCanvas = this.effectGlCanvas as HTMLCanvasElement & { width: number; height: number };\n if (glCanvas.width !== w || glCanvas.height !== h) {\n glCanvas.width = w;\n glCanvas.height = h;\n }\n\n if (!this.effectContext) {\n this.effectContext = createEffectContext(glCanvas);\n }\n\n const gl = this.effectContext.gl;\n const sourceTexture = gl.createTexture();\n if (!sourceTexture) return;\n\n gl.bindTexture(gl.TEXTURE_2D, sourceTexture);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);\n\n const resultTexture = applyEffects({\n ctx: this.effectContext,\n sourceTexture,\n width: w,\n height: h,\n effects: activeEffects,\n });\n\n gl.deleteTexture(sourceTexture);\n\n // Read back from the result texture (it's an FBO attachment) via a readback FBO\n if (!this.effectReadbackFbo) {\n this.effectReadbackFbo = gl.createFramebuffer();\n }\n gl.bindFramebuffer(gl.FRAMEBUFFER, this.effectReadbackFbo);\n gl.framebufferTexture2D(\n gl.FRAMEBUFFER,\n gl.COLOR_ATTACHMENT0,\n gl.TEXTURE_2D,\n resultTexture,\n 0,\n );\n const pixels = new Uint8Array(w * h * 4);\n gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels);\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\n const ctx2d = canvas.getContext('2d');\n if (ctx2d) {\n const imageData = ctx2d.createImageData(w, h);\n const rowBytes = w * 4;\n for (let y = 0; y < h; y++) {\n imageData.data.set(\n pixels.subarray((h - 1 - y) * rowBytes, (h - y) * rowBytes),\n y * rowBytes,\n );\n }\n ctx2d.putImageData(imageData, 0, 0);\n }\n }\n\n /**\n * Called on every frame.\n */\n private render = async () => {\n if (this.player && this.project) {\n await this.stage.render(\n this.player.playback.currentScene,\n this.player.playback.previousScene,\n );\n\n this.applyEffectsToFinalBuffer();\n\n this.dispatchEvent(new CustomEvent('timeupdate', {detail: this.time}));\n\n const durationInFrames = this.player.playback.duration;\n if (durationInFrames === this.duration) {\n return;\n }\n\n this.duration = durationInFrames;\n\n const durationInSeconds = durationInFrames / this.player.playback.fps;\n this.dispatchEvent(\n new CustomEvent('duration', {detail: durationInSeconds}),\n );\n }\n };\n\n private updateSettings() {\n if (!this.defaultSettings) {\n return;\n }\n\n // Use the requested quality (resolutionScale) instead of forcing 1,\n // so the preview canvas can render at higher internal resolution.\n const resolutionScale =\n Number.isFinite(this.quality) && this.quality > 0\n ? this.quality\n : this.defaultSettings.resolutionScale ?? 1;\n\n const settings = {\n ...this.defaultSettings,\n size: new Vector2(this.width, this.height),\n resolutionScale,\n fps: this.fps,\n };\n this.stage.configure(settings);\n this.player?.configure(settings);\n }\n}\n\nif (!customElements.get(ID)) {\n customElements.define(ID, TwickPlayer);\n}\n","'use client';\nimport type {Player as CorePlayer, Project} from '@twick/core';\nimport type {ComponentProps} from 'react';\nimport {useCallback, useEffect, useRef, useState} from 'react';\nimport {Controls} from './controls';\nimport './index.css';\nimport {shouldShowControls} from './utils';\n\ninterface TwickPlayerProps {\n playing?: boolean | string;\n variables?: string;\n looping?: boolean | string;\n width?: number;\n height?: number;\n quality?: number;\n fps?: number;\n volume?: number;\n}\n\ndeclare global {\n namespace JSX {\n interface IntrinsicElements {\n // eslint-disable-next-line\n 'twick-player': TwickPlayerProps & ComponentProps<'div'>;\n }\n }\n}\n\ninterface PlayerProps {\n project: Project;\n controls?: boolean;\n variables?: Record<string, any>;\n playing?: boolean;\n currentTime?: number;\n volume?: number;\n looping?: boolean;\n fps?: number;\n\n width?: number;\n height?: number;\n quality?: number;\n timeDisplayFormat?: 'MM:SS' | 'MM:SS.mm' | 'MM:SS.m';\n\n onDurationChange?: (duration: number) => void;\n onTimeUpdate?: (currentTime: number) => void;\n onPlayerReady?: (player: CorePlayer) => void;\n onPlayerResize?: (rect: DOMRectReadOnly) => void;\n}\n\nexport function Player({\n project,\n controls = true,\n variables = {},\n playing = false,\n currentTime = 0,\n volume = 1,\n looping = true,\n fps = 30,\n\n width = undefined,\n height = undefined,\n quality = undefined,\n timeDisplayFormat = 'MM:SS',\n\n onDurationChange = () => {},\n onTimeUpdate = () => {},\n onPlayerReady = () => {},\n onPlayerResize = () => {},\n}: PlayerProps) {\n const [playingState, setPlaying] = useState(playing);\n const [isMouseOver, setIsMouseOver] = useState(false);\n const [currentTimeState, setCurrentTime] = useState(currentTime);\n const [volumeState, setVolumeState] = useState(volume);\n const [duration, setDuration] = useState(-1);\n\n const focus = useRef(false);\n const playerRef = useRef<HTMLDivElement | null>(null);\n const wrapperRef = useRef<HTMLDivElement | null>(null);\n const lastRect = useRef<DOMRectReadOnly | null>(null);\n const lastLoggedTimeRef = useRef<number | null>(null);\n\n const onClickHandler = controls ? () => setPlaying(prev => !prev) : undefined;\n\n /**\n * Sync the playing prop with the player's own state when it changes.\n */\n useEffect(() => {\n setPlaying(playing);\n }, [playing]);\n\n /**\n * Sync the current time with the player's own state.\n */\n useEffect(() => {\n const diff = Math.abs(currentTime - currentTimeState);\n if (diff > 0.05) {\n setForcedTime(currentTime);\n }\n }, [currentTime]);\n\n useEffect(() => {\n setForcedVolume(volume);\n }, [volume]);\n\n /**\n * Set variables via setAttribute - the twick-player custom element's variables\n * property is read-only (getter only). React would fail if we passed it as a prop.\n */\n const variablesJson = JSON.stringify(variables);\n useEffect(() => {\n if (playerRef.current) {\n playerRef.current.setAttribute('variables', variablesJson);\n }\n }, [variablesJson]);\n\n /**\n * Receives the current time of the video from the player.\n * Use refs to ensure we always call the latest callbacks.\n */\n const onTimeUpdateRef = useRef(onTimeUpdate);\n const onDurationChangeRef = useRef(onDurationChange);\n \n // Update refs when callbacks change\n useEffect(() => {\n onTimeUpdateRef.current = onTimeUpdate;\n }, [onTimeUpdate]);\n \n useEffect(() => {\n onDurationChangeRef.current = onDurationChange;\n }, [onDurationChange]);\n\n const handleTimeUpdate = useCallback((event: Event) => {\n const e = event as CustomEvent;\n const t = e.detail as number;\n const last = lastLoggedTimeRef.current;\n if (last === null || Math.abs(t - last) > 0.05) {\n lastLoggedTimeRef.current = t;\n }\n setCurrentTime(t);\n onTimeUpdateRef.current(t);\n }, []);\n\n /**\n * Receives the duration of the video from the player.\n */\n const handleDurationUpdate = useCallback((event: Event) => {\n const e = event as CustomEvent;\n setDuration(e.detail);\n onDurationChangeRef.current(e.detail);\n }, []);\n\n /**\n * Play and pause using the space key.\n */\n const handleKeyDown = useCallback((event: KeyboardEvent) => {\n if (event.code === 'Space' && focus.current) {\n event.preventDefault();\n setPlaying(prev => !prev);\n }\n }, []);\n\n const onPlayerReadyRef = useRef(onPlayerReady);\n \n useEffect(() => {\n onPlayerReadyRef.current = onPlayerReady;\n }, [onPlayerReady]);\n\n const handlePlayerReady = useCallback((event: Event) => {\n const player = (event as CustomEvent).detail;\n if (player) {\n onPlayerReadyRef.current(player);\n }\n \n // Ensure event listeners are attached when player becomes ready\n // This is a fallback in case listeners weren't attached earlier\n const playerElement = playerRef.current;\n if (playerElement) {\n // Remove and re-add to ensure they're attached\n playerElement.removeEventListener('timeupdate', handleTimeUpdate);\n playerElement.removeEventListener('duration', handleDurationUpdate);\n playerElement.addEventListener('timeupdate', handleTimeUpdate);\n playerElement.addEventListener('duration', handleDurationUpdate);\n }\n }, [handleTimeUpdate, handleDurationUpdate]);\n\n const handlePlayerResize = useCallback(\n (entries: ResizeObserverEntry[]) => {\n const [firstEntry] = entries;\n if (!firstEntry || !wrapperRef.current) {\n return;\n }\n\n const newRect = wrapperRef.current.getBoundingClientRect();\n if (\n !lastRect.current ||\n newRect.width !== lastRect.current.width ||\n newRect.height !== lastRect.current.height ||\n newRect.x !== lastRect.current.x ||\n newRect.y !== lastRect.current.y\n ) {\n lastRect.current = newRect;\n onPlayerResize(newRect);\n }\n },\n [onPlayerResize],\n );\n\n useEffect(() => {\n if (!wrapperRef.current) return;\n\n const resizeObserver = new ResizeObserver(handlePlayerResize);\n resizeObserver.observe(wrapperRef.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, [handlePlayerResize]);\n\n /**\n * Import the player and add all event listeners.\n */\n useEffect(() => {\n let cleanup: (() => void) | null = null;\n\n const setupListeners = () => {\n const player = playerRef.current;\n if (!player) return;\n\n // Remove any existing listeners first to avoid duplicates\n player.removeEventListener('timeupdate', handleTimeUpdate);\n player.removeEventListener('duration', handleDurationUpdate);\n player.removeEventListener('playerready', handlePlayerReady);\n\n // Add event listeners\n player.addEventListener('timeupdate', handleTimeUpdate);\n player.addEventListener('duration', handleDurationUpdate);\n player.addEventListener('playerready', handlePlayerReady);\n document.addEventListener('keydown', handleKeyDown);\n\n cleanup = () => {\n player.removeEventListener('timeupdate', handleTimeUpdate);\n player.removeEventListener('duration', handleDurationUpdate);\n player.removeEventListener('playerready', handlePlayerReady);\n document.removeEventListener('keydown', handleKeyDown);\n };\n };\n\n // Import the custom element definition\n import('./internal').then(() => {\n // Wait for the next tick to ensure the element is in the DOM\n // Use requestAnimationFrame to ensure the custom element is ready\n requestAnimationFrame(() => {\n if (playerRef.current) {\n (playerRef.current as any).setProject(project);\n // Set up listeners after the element is ready\n setupListeners();\n }\n });\n });\n\n // Also set up listeners immediately if element already exists\n // This handles the case where the element was already in the DOM\n if (playerRef.current) {\n setupListeners();\n }\n\n return () => {\n if (cleanup) {\n cleanup();\n }\n };\n }, [project, handleTimeUpdate, handleDurationUpdate, handlePlayerReady, handleKeyDown]);\n\n /**\n * When the forced time changes, seek to that time.\n */\n function setForcedTime(forcedTime: number) {\n if (playerRef.current) {\n playerRef.current.dispatchEvent(\n new CustomEvent('seekto', {detail: forcedTime}),\n );\n }\n }\n\n function setForcedVolume(volume: number) {\n setVolumeState(volume);\n if (playerRef.current) {\n playerRef.current.dispatchEvent(\n new CustomEvent('volumechange', {detail: volume}),\n );\n }\n }\n\n return (\n <div className=\"twick-player-root w-full h-full\" style={{display: 'contents'}}>\n <div\n ref={wrapperRef}\n className=\"relative cursor-default w-full h-full focus:outline-none\"\n onFocus={() => (focus.current = true)}\n onBlur={() => (focus.current = false)}\n tabIndex={0}\n onMouseEnter={() => setIsMouseOver(true)}\n onMouseLeave={() => setIsMouseOver(false)}\n >\n <div className=\"relative w-full h-full\">\n <twick-player\n ref={playerRef}\n quality={quality}\n fps={fps}\n width={width}\n height={height}\n volume={volumeState}\n playing={playingState}\n looping={looping}\n onClick={onClickHandler}\n />\n <div\n className={`absolute bottom-0 w-full transition-opacity duration-200 ${\n shouldShowControls(playingState, isMouseOver, !controls)\n ? 'opacity-100'\n : 'opacity-0'\n }`}\n >\n <Controls\n duration={duration}\n playing={playingState}\n setPlaying={setPlaying}\n currentTime={currentTimeState}\n setForcedTime={setForcedTime}\n timeDisplayFormat={timeDisplayFormat}\n volume={volumeState}\n setVolume={setForcedVolume}\n />\n </div>\n </div>\n </div>\n </div>\n );\n}\n","import {useState} from 'react';\nimport {MutedSoundIcon, PauseButton, PlayButton, SoundIcon} from './icons';\nimport {getFormattedTime} from './utils';\n\nfunction PlayPause({\n playing,\n setPlaying,\n}: {\n playing: boolean;\n setPlaying: (playing: boolean) => void;\n}) {\n return (\n <button type=\"button\" className=\"p-1\" onClick={() => setPlaying(!playing)}>\n {playing ? <PauseButton /> : <PlayButton />}\n </button>\n );\n}\n\nfunction VolumeSlider({\n volume,\n setVolume,\n}: {\n volume: number;\n setVolume: (volume: number) => void;\n}) {\n const [isHovering, setIsHovering] = useState(false);\n const [isInteracting, setIsInteracting] = useState(false);\n const [previousVolume, setPreviousVolume] = useState(1);\n\n const handleIconClick = () => {\n if (volume > 0) {\n setPreviousVolume(volume);\n setVolume(0);\n } else {\n setVolume(previousVolume);\n }\n };\n\n return (\n <div\n className=\"flex items-center space-x-2 relative\"\n onMouseEnter={() => setIsHovering(true)}\n onMouseLeave={() => {\n if (!isInteracting) {\n setIsHovering(false);\n }\n }}\n >\n <div\n className=\"w-6 h-6 flex items-center justify-center cursor-pointer\"\n onClick={handleIconClick}\n >\n {volume === 0 ? <MutedSoundIcon /> : <SoundIcon />}\n </div>\n {(isHovering || isInteracting) && (\n <div className=\"flex items-center h-1.5 whitespace-nowrap\">\n <div className=\"relative w-20 h-1.5 bg-gray-300 rounded-full\">\n <div\n className=\"absolute top-0 left-0 h-full bg-gray-100 rounded-full\"\n style={{width: `${volume * 100}%`}}\n />\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.01}\n value={volume}\n onChange={e => {\n const newVolume = Number(e.target.value);\n setVolume(newVolume);\n if (newVolume > 0) {\n setPreviousVolume(newVolume);\n }\n }}\n onMouseDown={() => setIsInteracting(true)}\n onMouseUp={() => setIsInteracting(false)}\n onMouseLeave={() => setIsInteracting(false)}\n className=\"absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer\"\n />\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction Timeline({\n currentTime,\n duration,\n setCurrentTime,\n}: {\n currentTime: number;\n duration: number;\n setCurrentTime: (currentTime: number) => void;\n}) {\n const progressPercentage = (currentTime / duration) * 100;\n\n return (\n <div className=\"relative flex-1 w-full h-1.5 bg-gray-300 rounded-full overflow-hidden\">\n <div\n className=\"absolute top-0 left-0 h-full bg-gray-100\"\n style={{width: `${progressPercentage}%`}}\n />\n <input\n type=\"range\"\n value={currentTime}\n min={0}\n max={duration}\n step={0.01}\n className=\"absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer\"\n onChange={event => setCurrentTime(Number(event.target.value))}\n />\n </div>\n );\n}\n\nexport function Controls({\n duration,\n playing,\n setPlaying,\n currentTime,\n setForcedTime,\n timeDisplayFormat,\n volume,\n setVolume,\n}: {\n duration: number;\n playing: boolean;\n setPlaying: (playing: boolean) => void;\n currentTime: number;\n setForcedTime: (currentTime: number) => void;\n timeDisplayFormat: 'MM:SS' | 'MM:SS.m' | 'MM:SS.mm';\n volume: number;\n setVolume: (volume: number) => void;\n}) {\n return (\n <div className=\"text-white p-4 flex-col space-y-2 bg-gradient-to-t from-gray-500 to-transparent\">\n <div className=\"flex items-center space-x-2\">\n <PlayPause playing={playing} setPlaying={setPlaying} />\n <div className=\"flex items-center space-x-2\">\n <VolumeSlider volume={volume} setVolume={setVolume} />\n <div>\n <span>\n {getFormattedTime(currentTime, duration, timeDisplayFormat)}\n </span>\n </div>\n </div>\n <div className=\"flex-grow\" />\n </div>\n <Timeline\n currentTime={currentTime}\n duration={duration}\n setCurrentTime={setForcedTime}\n />\n </div>\n );\n}\n","export function PlayButton() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className=\"w-6 h-6\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M4.5 5.653c0-1.427 1.529-2.33 2.779-1.643l11.54 6.347c1.295.712 1.295 2.573 0 3.286L7.28 19.99c-1.25.687-2.779-.217-2.779-1.643V5.653Z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n}\n\nexport function PauseButton() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className=\"w-6 h-6\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M6.75 5.25a.75.75 0 0 1 .75-.75H9a.75.75 0 0 1 .75.75v13.5a.75.75 0 0 1-.75.75H7.5a.75.75 0 0 1-.75-.75V5.25Zm7.5 0A.75.75 0 0 1 15 4.5h1.5a.75.75 0 0 1 .75.75v13.5a.75.75 0 0 1-.75.75H15a.75.75 0 0 1-.75-.75V5.25Z\"\n clipRule=\"evenodd\"\n />\n </svg>\n );\n}\n\nexport function SoundIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className=\"p-w-6 p-h-6\"\n >\n <path d=\"M18.36,19.36a1,1,0,0,1-.7-.29,1,1,0,0,1,0-1.41,8,8,0,0,0,0-11.32,1,1,0,0,1,1.41-1.41,10,10,0,0,1,0,14.14A1,1,0,0,1,18.36,19.36Z\" />\n <path d=\"M15.54,16.54a1,1,0,0,1-.71-.3,1,1,0,0,1,0-1.41,4,4,0,0,0,0-5.66,1,1,0,0,1,1.41-1.41,6,6,0,0,1,0,8.48A1,1,0,0,1,15.54,16.54Z\" />\n <path d=\"M11.38,4.08a1,1,0,0,0-1.09.21L6.59,8H4a2,2,0,0,0-2,2v4a2,2,0,0,0,2,2H6.59l3.7,3.71A1,1,0,0,0,11,20a.84.84,0,0,0,.38-.08A1,1,0,0,0,12,19V5A1,1,0,0,0,11.38,4.08Z\" />\n </svg>\n );\n}\n\nexport function MutedSoundIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n className=\"p-w-6 p-h-6\"\n >\n <path d=\"M11.38,4.08a1,1,0,0,0-1.09.21L6.59,8H4a2,2,0,0,0-2,2v4a2,2,0,0,0,2,2H6.59l3.7,3.71A1,1,0,0,0,11,20a.84.84,0,0,0,.38-.08A1,1,0,0,0,12,19V5A1,1,0,0,0,11.38,4.08Z\" />\n <path d=\"M16,15.5a1,1,0,0,1-.71-.29,1,1,0,0,1,0-1.42l5-5a1,1,0,0,1,1.42,1.42l-5,5A1,1,0,0,1,16,15.5Z\" />\n <path d=\"M21,15.5a1,1,0,0,1-.71-.29l-5-5a1,1,0,0,1,1.42-1.42l5,5a1,1,0,0,1,0,1.42A1,1,0,0,1,21,15.5Z\" />\n </svg>\n );\n}\n","export function getFormattedTime(\n timeInSeconds: number,\n absoluteTimeInSeconds: number,\n timeDisplayFormat: 'MM:SS' | 'MM:SS.mm' | 'MM:SS.m',\n) {\n function toFormattedTime(timeInSeconds: number) {\n const minutes = Math.floor(timeInSeconds / 60);\n const seconds = Math.floor(timeInSeconds % 60)\n .toString()\n .padStart(2, '0');\n const milliseconds = Math.floor((timeInSeconds % 1) * 1000)\n .toString()\n .padStart(3, '0');\n\n if (timeDisplayFormat === 'MM:SS') {\n return `${minutes}:${seconds}`;\n }\n\n if (timeDisplayFormat === 'MM:SS.m') {\n return `${minutes}:${seconds}.${milliseconds[0]}`;\n }\n\n if (timeDisplayFormat === 'MM:SS.mm') {\n return `${minutes}:${seconds}.${milliseconds.slice(0, 2)}`;\n }\n }\n\n return `${toFormattedTime(timeInSeconds)} / ${toFormattedTime(absoluteTimeInSeconds)}`;\n}\n\nexport function shouldShowControls(\n playing: boolean,\n isMouseOver: boolean,\n areControlsDisabled: boolean,\n) {\n if (areControlsDisabled) {\n return false;\n }\n\n return !playing || isMouseOver;\n}\n"],"mappings":"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","names":["Player","import_react","timeInSeconds","import_jsx_runtime","import_jsx_runtime","Player","volume"]}
package/dist/index.js CHANGED
@@ -378,7 +378,7 @@ function Player({
378
378
  document.removeEventListener("keydown", handleKeyDown);
379
379
  };
380
380
  };
381
- import("./internal-LKI2GAXH.js").then(() => {
381
+ import("./internal-B6KRB6EV.js").then(() => {
382
382
  requestAnimationFrame(() => {
383
383
  if (playerRef.current) {
384
384
  playerRef.current.setProject(project);
@@ -1,6 +1,9 @@
1
1
  // src/internal.ts
2
- import { Player, Stage, getFullPreviewSettings } from "@twick/core";
3
- import { Vector2 } from "@twick/core";
2
+ import { Player, Stage, getFullPreviewSettings, Vector2 } from "@twick/core";
3
+ import {
4
+ applyEffects,
5
+ createEffectContext
6
+ } from "@twick/gl-runtime";
4
7
  var stylesNew = `
5
8
  .overlay {
6
9
  position: absolute;
@@ -119,6 +122,15 @@ var TwickPlayer = class extends HTMLElement {
119
122
  _looping = true;
120
123
  _volume = 1;
121
124
  volumeChangeRequested = true;
125
+ /** WebGL canvas and context for applying effects to the live preview. */
126
+ effectGlCanvas = null;
127
+ effectContext = null;
128
+ effectReadbackFbo = null;
129
+ /**
130
+ * Optional resolver for active effects at a given time. Set by the host (e.g. twick) so
131
+ * effect logic lives outside twick-base. When set, used for live preview effects.
132
+ */
133
+ getActiveEffectsForTime;
122
134
  constructor() {
123
135
  super();
124
136
  this.root = this.attachShadow({ mode: "open" });
@@ -245,6 +257,84 @@ var TwickPlayer = class extends HTMLElement {
245
257
  this.volumeChangeRequested = false;
246
258
  }
247
259
  };
260
+ /**
261
+ * Resolve active effects for the given time. Uses host-provided callback when set.
262
+ */
263
+ resolveActiveEffectsForTime(timeInSec) {
264
+ if (this.getActiveEffectsForTime) {
265
+ const fps = this.player?.playback.fps ?? 30;
266
+ return this.getActiveEffectsForTime(this.variables, timeInSec, fps);
267
+ }
268
+ return [];
269
+ }
270
+ /**
271
+ * Apply GL effects to the current frame and draw the result back to the stage canvas.
272
+ */
273
+ applyEffectsToFinalBuffer() {
274
+ const canvas = this.stage.finalBuffer;
275
+ const w = canvas.width;
276
+ const h = canvas.height;
277
+ if (w <= 0 || h <= 0) return;
278
+ const activeEffects = this.resolveActiveEffectsForTime(this.time);
279
+ if (activeEffects.length === 0) return;
280
+ if (!this.effectGlCanvas) {
281
+ this.effectGlCanvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(w, h) : document.createElement("canvas");
282
+ this.effectGlCanvas.width = w;
283
+ this.effectGlCanvas.height = h;
284
+ }
285
+ const glCanvas = this.effectGlCanvas;
286
+ if (glCanvas.width !== w || glCanvas.height !== h) {
287
+ glCanvas.width = w;
288
+ glCanvas.height = h;
289
+ }
290
+ if (!this.effectContext) {
291
+ this.effectContext = createEffectContext(glCanvas);
292
+ }
293
+ const gl = this.effectContext.gl;
294
+ const sourceTexture = gl.createTexture();
295
+ if (!sourceTexture) return;
296
+ gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
297
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
298
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
299
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
300
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
301
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
302
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
303
+ const resultTexture = applyEffects({
304
+ ctx: this.effectContext,
305
+ sourceTexture,
306
+ width: w,
307
+ height: h,
308
+ effects: activeEffects
309
+ });
310
+ gl.deleteTexture(sourceTexture);
311
+ if (!this.effectReadbackFbo) {
312
+ this.effectReadbackFbo = gl.createFramebuffer();
313
+ }
314
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.effectReadbackFbo);
315
+ gl.framebufferTexture2D(
316
+ gl.FRAMEBUFFER,
317
+ gl.COLOR_ATTACHMENT0,
318
+ gl.TEXTURE_2D,
319
+ resultTexture,
320
+ 0
321
+ );
322
+ const pixels = new Uint8Array(w * h * 4);
323
+ gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
324
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
325
+ const ctx2d = canvas.getContext("2d");
326
+ if (ctx2d) {
327
+ const imageData = ctx2d.createImageData(w, h);
328
+ const rowBytes = w * 4;
329
+ for (let y = 0; y < h; y++) {
330
+ imageData.data.set(
331
+ pixels.subarray((h - 1 - y) * rowBytes, (h - y) * rowBytes),
332
+ y * rowBytes
333
+ );
334
+ }
335
+ ctx2d.putImageData(imageData, 0, 0);
336
+ }
337
+ }
248
338
  /**
249
339
  * Called on every frame.
250
340
  */
@@ -254,6 +344,7 @@ var TwickPlayer = class extends HTMLElement {
254
344
  this.player.playback.currentScene,
255
345
  this.player.playback.previousScene
256
346
  );
347
+ this.applyEffectsToFinalBuffer();
257
348
  this.dispatchEvent(new CustomEvent("timeupdate", { detail: this.time }));
258
349
  const durationInFrames = this.player.playback.duration;
259
350
  if (durationInFrames === this.duration) {
@@ -284,4 +375,4 @@ var TwickPlayer = class extends HTMLElement {
284
375
  if (!customElements.get(ID)) {
285
376
  customElements.define(ID, TwickPlayer);
286
377
  }
287
- //# sourceMappingURL=internal-LKI2GAXH.js.map
378
+ //# sourceMappingURL=internal-B6KRB6EV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal.ts"],"sourcesContent":["import type {Project} from '@twick/core';\nimport {Player, Stage, getFullPreviewSettings, Vector2} from '@twick/core';\nimport {\n applyEffects,\n createEffectContext,\n type ActiveEffect,\n type EffectContext,\n} from '@twick/gl-runtime';\n\nconst stylesNew = `\n.overlay {\n\tposition: absolute;\n\tleft: 0;\n\tright: 0;\n\ttop: 0;\n\tbottom: 0;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\topacity: 0;\n\ttransition: opacity 0.1s;\n\tz-index: 0;\n }\n .canvas {\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: block;\n\topacity: 1;\n\ttransition: opacity 0.1s;\n }\n`;\n\nconst TEMPLATE = `<style>${stylesNew}</style><div class=\"overlay\"></div>`;\nconst ID = 'twick-player';\n\nenum State {\n Initial = 'initial',\n Loading = 'loading',\n Ready = 'ready',\n Error = 'error',\n}\n\nclass TwickPlayer extends HTMLElement {\n public static get observedAttributes() {\n return [\n 'playing',\n 'variables',\n 'looping',\n 'fps',\n 'quality',\n 'width',\n 'height',\n 'volume',\n ];\n }\n\n public get fps() {\n const attr = this.getAttribute('fps');\n return attr ? parseFloat(attr) : (this.defaultSettings?.fps ?? 60);\n }\n\n public set fps(value: number) {\n if (value != null && Number.isFinite(value)) {\n this.setAttribute('fps', String(value));\n }\n }\n\n public get quality() {\n const attr = this.getAttribute('quality');\n return attr\n ? parseFloat(attr)\n : (this.defaultSettings?.resolutionScale ?? 1);\n }\n\n public set quality(value: number) {\n if (value != null && Number.isFinite(value)) {\n this.setAttribute('quality', String(value));\n }\n }\n\n public get width() {\n const attr = this.getAttribute('width');\n return attr ? parseFloat(attr) : (this.defaultSettings?.size.width ?? 0);\n }\n\n public set width(value: number) {\n if (Number.isFinite(value)) {\n this.setAttribute('width', String(value));\n }\n }\n\n public get height() {\n const attr = this.getAttribute('height');\n return attr ? parseFloat(attr) : (this.defaultSettings?.size.height ?? 0);\n }\n\n public set height(value: number) {\n if (Number.isFinite(value)) {\n this.setAttribute('height', String(value));\n }\n }\n\n private get variables() {\n try {\n const attr = this.getAttribute('variables');\n return attr ? JSON.parse(attr) : {};\n } catch {\n this.project?.logger.warn(`Project variables could not be parsed.`);\n return {};\n }\n }\n\n public get volume() {\n return this._volume;\n }\n\n public set volume(value: number) {\n if (value != null) {\n this.setAttribute('volume', String(value));\n }\n }\n\n public set playing(value: boolean | string) {\n this.setAttribute(\n 'playing',\n value === true || value === 'true' ? 'true' : 'false',\n );\n }\n\n public set looping(value: boolean | string) {\n this.setAttribute(\n 'looping',\n value === true || value === 'true' ? 'true' : 'false',\n );\n }\n\n private readonly root: ShadowRoot;\n private readonly canvas: HTMLCanvasElement;\n private readonly overlay: HTMLCanvasElement;\n\n private state = State.Initial;\n private project: Project | null = null;\n private player: Player | null = null;\n private defaultSettings:\n | ReturnType<typeof getFullPreviewSettings>\n | undefined;\n private abortController: AbortController | null = null;\n private _playing = false;\n private stage = new Stage();\n\n private time: number = 0;\n private duration: number = 0; // in frames\n private _looping = true;\n private _volume = 1;\n private volumeChangeRequested = true;\n\n /** WebGL canvas and context for applying effects to the live preview. */\n private effectGlCanvas: HTMLCanvasElement | OffscreenCanvas | null = null;\n private effectContext: EffectContext | null = null;\n private effectReadbackFbo: WebGLFramebuffer | null = null;\n\n /**\n * Optional resolver for active effects at a given time. Set by the host (e.g. twick) so\n * effect logic lives outside twick-base. When set, used for live preview effects.\n */\n public getActiveEffectsForTime?: (\n variables: Record<string, unknown>,\n timeInSec: number,\n fps: number,\n ) => Array<{fragment: string; progress: number; intensity: number}>;\n\n public constructor() {\n super();\n this.root = this.attachShadow({mode: 'open'});\n this.root.innerHTML = TEMPLATE;\n\n this.overlay = this.root.querySelector('.overlay')!;\n this.canvas = this.stage.finalBuffer;\n this.canvas.classList.add('canvas');\n this.root.prepend(this.canvas);\n this.setState(State.Initial);\n }\n\n public setProject(project: Project) {\n this.updateProject(project);\n }\n\n private setState(state: State) {\n this.state = state;\n this.setPlaying(this._playing);\n }\n\n private setPlaying(value: boolean) {\n if (this.state === State.Ready && value) {\n this.player?.togglePlayback(true);\n this._playing = true;\n } else {\n this.player?.togglePlayback(false);\n this._playing = false;\n }\n }\n\n private async updateProject(project: Project) {\n const playing = this._playing;\n this.setState(State.Initial);\n\n this.abortController?.abort();\n this.abortController = new AbortController();\n\n this.project = project;\n this.defaultSettings = getFullPreviewSettings(this.project);\n\n const player = new Player(this.project);\n player.setVariables(this.variables);\n player.toggleLoop(this._looping);\n\n this.player?.onRender.unsubscribe(this.render);\n this.player?.onFrameChanged.unsubscribe(this.handleFrameChanged);\n this.player?.togglePlayback(false);\n this.player?.deactivate();\n\n this.player = player;\n this.updateSettings();\n\n this.setState(State.Ready);\n this.dispatchEvent(new CustomEvent('playerready', {detail: this.player}));\n\n // Restore previous state\n this.setPlaying(playing);\n this.player.onRender.subscribe(this.render);\n this.player.onFrameChanged.subscribe(this.handleFrameChanged);\n }\n\n public attributeChangedCallback(name: string, _: any, newValue: any) {\n switch (name) {\n case 'playing':\n this.setPlaying(newValue === 'true');\n break;\n case 'variables':\n this.player?.setVariables(this.variables);\n this.player?.requestSeek(this.player.playback.frame);\n this.player?.playback.reload();\n break;\n case 'looping':\n this._looping = newValue === 'true';\n this.player?.toggleLoop(newValue === 'true');\n break;\n case 'fps':\n case 'quality':\n case 'width':\n case 'height':\n this.updateSettings();\n break;\n case 'volume':\n this._volume = newValue;\n this.volumeChangeRequested = true;\n }\n }\n\n /**\n * Runs when the element is removed from the DOM.\n */\n public disconnectedCallback() {\n this.player?.deactivate();\n this.player?.onRender.unsubscribe(this.render);\n\n this.removeEventListener('seekto', this.handleSeekTo);\n this.removeEventListener('volumechange', this.handleVolumeChange);\n }\n\n /**\n * Runs when the element is added to the DOM.\n */\n public connectedCallback() {\n this.player?.activate();\n this.player?.onRender.subscribe(this.render);\n\n this.addEventListener('seekto', this.handleSeekTo);\n this.addEventListener('volumechange', this.handleVolumeChange);\n }\n\n /**\n * Triggered by the timeline.\n */\n private handleSeekTo = (event: Event) => {\n if (!this.project) {\n return;\n }\n\n const e = event as CustomEvent;\n const timeSec = e.detail as number;\n const frame = timeSec * this.player!.playback.fps;\n this.time = timeSec;\n this.player?.requestSeek(frame);\n this.volumeChangeRequested = true;\n };\n\n private handleVolumeChange = (event: Event) => {\n if (!this.project) {\n return;\n }\n\n const e = event as CustomEvent;\n this._volume = e.detail;\n\n this.player?.playback.currentScene.adjustVolume(this._volume);\n };\n\n /**\n * Triggered by the player.\n */\n private handleFrameChanged = (frame: number) => {\n if (!this.project || !this.player) {\n return;\n }\n this.time = frame / this.player.playback.fps;\n\n if (this.volumeChangeRequested || frame === 0) {\n this.player?.playback.currentScene.adjustVolume(this._volume);\n this.volumeChangeRequested = false;\n }\n };\n\n /**\n * Resolve active effects for the given time. Uses host-provided callback when set.\n */\n private resolveActiveEffectsForTime(timeInSec: number): ActiveEffect[] {\n if (this.getActiveEffectsForTime) {\n const fps = this.player?.playback.fps ?? 30;\n return this.getActiveEffectsForTime(this.variables, timeInSec, fps);\n }\n return [];\n }\n\n /**\n * Apply GL effects to the current frame and draw the result back to the stage canvas.\n */\n private applyEffectsToFinalBuffer(): void {\n const canvas = this.stage.finalBuffer;\n const w = canvas.width;\n const h = canvas.height;\n if (w <= 0 || h <= 0) return;\n\n const activeEffects = this.resolveActiveEffectsForTime(this.time);\n if (activeEffects.length === 0) return;\n\n if (!this.effectGlCanvas) {\n this.effectGlCanvas = typeof OffscreenCanvas !== 'undefined'\n ? new OffscreenCanvas(w, h)\n : document.createElement('canvas');\n (this.effectGlCanvas as HTMLCanvasElement).width = w;\n (this.effectGlCanvas as HTMLCanvasElement).height = h;\n }\n const glCanvas = this.effectGlCanvas as HTMLCanvasElement & { width: number; height: number };\n if (glCanvas.width !== w || glCanvas.height !== h) {\n glCanvas.width = w;\n glCanvas.height = h;\n }\n\n if (!this.effectContext) {\n this.effectContext = createEffectContext(glCanvas);\n }\n\n const gl = this.effectContext.gl;\n const sourceTexture = gl.createTexture();\n if (!sourceTexture) return;\n\n gl.bindTexture(gl.TEXTURE_2D, sourceTexture);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);\n\n const resultTexture = applyEffects({\n ctx: this.effectContext,\n sourceTexture,\n width: w,\n height: h,\n effects: activeEffects,\n });\n\n gl.deleteTexture(sourceTexture);\n\n // Read back from the result texture (it's an FBO attachment) via a readback FBO\n if (!this.effectReadbackFbo) {\n this.effectReadbackFbo = gl.createFramebuffer();\n }\n gl.bindFramebuffer(gl.FRAMEBUFFER, this.effectReadbackFbo);\n gl.framebufferTexture2D(\n gl.FRAMEBUFFER,\n gl.COLOR_ATTACHMENT0,\n gl.TEXTURE_2D,\n resultTexture,\n 0,\n );\n const pixels = new Uint8Array(w * h * 4);\n gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels);\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\n const ctx2d = canvas.getContext('2d');\n if (ctx2d) {\n const imageData = ctx2d.createImageData(w, h);\n const rowBytes = w * 4;\n for (let y = 0; y < h; y++) {\n imageData.data.set(\n pixels.subarray((h - 1 - y) * rowBytes, (h - y) * rowBytes),\n y * rowBytes,\n );\n }\n ctx2d.putImageData(imageData, 0, 0);\n }\n }\n\n /**\n * Called on every frame.\n */\n private render = async () => {\n if (this.player && this.project) {\n await this.stage.render(\n this.player.playback.currentScene,\n this.player.playback.previousScene,\n );\n\n this.applyEffectsToFinalBuffer();\n\n this.dispatchEvent(new CustomEvent('timeupdate', {detail: this.time}));\n\n const durationInFrames = this.player.playback.duration;\n if (durationInFrames === this.duration) {\n return;\n }\n\n this.duration = durationInFrames;\n\n const durationInSeconds = durationInFrames / this.player.playback.fps;\n this.dispatchEvent(\n new CustomEvent('duration', {detail: durationInSeconds}),\n );\n }\n };\n\n private updateSettings() {\n if (!this.defaultSettings) {\n return;\n }\n\n // Use the requested quality (resolutionScale) instead of forcing 1,\n // so the preview canvas can render at higher internal resolution.\n const resolutionScale =\n Number.isFinite(this.quality) && this.quality > 0\n ? this.quality\n : this.defaultSettings.resolutionScale ?? 1;\n\n const settings = {\n ...this.defaultSettings,\n size: new Vector2(this.width, this.height),\n resolutionScale,\n fps: this.fps,\n };\n this.stage.configure(settings);\n this.player?.configure(settings);\n }\n}\n\nif (!customElements.get(ID)) {\n customElements.define(ID, TwickPlayer);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twick/player-react",
3
- "version": "0.15.19",
3
+ "version": "0.15.21",
4
4
  "description": "",
5
5
  "author": "twick",
6
6
  "license": "MIT",
@@ -25,7 +25,8 @@
25
25
  },
26
26
  "keywords": [],
27
27
  "dependencies": {
28
- "@twick/core": "0.15.19",
28
+ "@twick/core": "0.15.21",
29
+ "@twick/gl-runtime": "0.15.21",
29
30
  "react": "^18",
30
31
  "react-dom": "^18",
31
32
  "uuid": "^10.0.0"
@@ -46,5 +47,5 @@
46
47
  "url": "https://github.com/ncounterspecialist/twick-base.git"
47
48
  },
48
49
  "bugs": "https://github.com/ncounterspecialist/twick-base/issues",
49
- "gitHead": "bb49cf1d4c48b8884a4e4a87141d58d3dd817745"
50
+ "gitHead": "3712f98914034fa4c1807feeba7a9cbf7b3b45cc"
50
51
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/internal.ts"],"sourcesContent":["import type {Project} from '@twick/core';\nimport {Player, Stage, getFullPreviewSettings} from '@twick/core';\n\nimport {Vector2} from '@twick/core';\n\nconst stylesNew = `\n.overlay {\n\tposition: absolute;\n\tleft: 0;\n\tright: 0;\n\ttop: 0;\n\tbottom: 0;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\topacity: 0;\n\ttransition: opacity 0.1s;\n\tz-index: 0;\n }\n .canvas {\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: block;\n\topacity: 1;\n\ttransition: opacity 0.1s;\n }\n`;\n\nconst TEMPLATE = `<style>${stylesNew}</style><div class=\"overlay\"></div>`;\nconst ID = 'twick-player';\n\nenum State {\n Initial = 'initial',\n Loading = 'loading',\n Ready = 'ready',\n Error = 'error',\n}\n\nclass TwickPlayer extends HTMLElement {\n public static get observedAttributes() {\n return [\n 'playing',\n 'variables',\n 'looping',\n 'fps',\n 'quality',\n 'width',\n 'height',\n 'volume',\n ];\n }\n\n public get fps() {\n const attr = this.getAttribute('fps');\n return attr ? parseFloat(attr) : (this.defaultSettings?.fps ?? 60);\n }\n\n public set fps(value: number) {\n if (value != null && Number.isFinite(value)) {\n this.setAttribute('fps', String(value));\n }\n }\n\n public get quality() {\n const attr = this.getAttribute('quality');\n return attr\n ? parseFloat(attr)\n : (this.defaultSettings?.resolutionScale ?? 1);\n }\n\n public set quality(value: number) {\n if (value != null && Number.isFinite(value)) {\n this.setAttribute('quality', String(value));\n }\n }\n\n public get width() {\n const attr = this.getAttribute('width');\n return attr ? parseFloat(attr) : (this.defaultSettings?.size.width ?? 0);\n }\n\n public set width(value: number) {\n if (Number.isFinite(value)) {\n this.setAttribute('width', String(value));\n }\n }\n\n public get height() {\n const attr = this.getAttribute('height');\n return attr ? parseFloat(attr) : (this.defaultSettings?.size.height ?? 0);\n }\n\n public set height(value: number) {\n if (Number.isFinite(value)) {\n this.setAttribute('height', String(value));\n }\n }\n\n private get variables() {\n try {\n const attr = this.getAttribute('variables');\n return attr ? JSON.parse(attr) : {};\n } catch {\n this.project?.logger.warn(`Project variables could not be parsed.`);\n return {};\n }\n }\n\n public get volume() {\n return this._volume;\n }\n\n public set volume(value: number) {\n if (value != null) {\n this.setAttribute('volume', String(value));\n }\n }\n\n public set playing(value: boolean | string) {\n this.setAttribute(\n 'playing',\n value === true || value === 'true' ? 'true' : 'false',\n );\n }\n\n public set looping(value: boolean | string) {\n this.setAttribute(\n 'looping',\n value === true || value === 'true' ? 'true' : 'false',\n );\n }\n\n private readonly root: ShadowRoot;\n private readonly canvas: HTMLCanvasElement;\n private readonly overlay: HTMLCanvasElement;\n\n private state = State.Initial;\n private project: Project | null = null;\n private player: Player | null = null;\n private defaultSettings:\n | ReturnType<typeof getFullPreviewSettings>\n | undefined;\n private abortController: AbortController | null = null;\n private _playing = false;\n private stage = new Stage();\n\n private time: number = 0;\n private duration: number = 0; // in frames\n private _looping = true;\n private _volume = 1;\n private volumeChangeRequested = true;\n\n public constructor() {\n super();\n this.root = this.attachShadow({mode: 'open'});\n this.root.innerHTML = TEMPLATE;\n\n this.overlay = this.root.querySelector('.overlay')!;\n this.canvas = this.stage.finalBuffer;\n this.canvas.classList.add('canvas');\n this.root.prepend(this.canvas);\n this.setState(State.Initial);\n }\n\n public setProject(project: Project) {\n this.updateProject(project);\n }\n\n private setState(state: State) {\n this.state = state;\n this.setPlaying(this._playing);\n }\n\n private setPlaying(value: boolean) {\n if (this.state === State.Ready && value) {\n this.player?.togglePlayback(true);\n this._playing = true;\n } else {\n this.player?.togglePlayback(false);\n this._playing = false;\n }\n }\n\n private async updateProject(project: Project) {\n const playing = this._playing;\n this.setState(State.Initial);\n\n this.abortController?.abort();\n this.abortController = new AbortController();\n\n this.project = project;\n this.defaultSettings = getFullPreviewSettings(this.project);\n\n const player = new Player(this.project);\n player.setVariables(this.variables);\n player.toggleLoop(this._looping);\n\n this.player?.onRender.unsubscribe(this.render);\n this.player?.onFrameChanged.unsubscribe(this.handleFrameChanged);\n this.player?.togglePlayback(false);\n this.player?.deactivate();\n\n this.player = player;\n this.updateSettings();\n\n this.setState(State.Ready);\n this.dispatchEvent(new CustomEvent('playerready', {detail: this.player}));\n\n // Restore previous state\n this.setPlaying(playing);\n this.player.onRender.subscribe(this.render);\n this.player.onFrameChanged.subscribe(this.handleFrameChanged);\n }\n\n public attributeChangedCallback(name: string, _: any, newValue: any) {\n switch (name) {\n case 'playing':\n this.setPlaying(newValue === 'true');\n break;\n case 'variables':\n this.player?.setVariables(this.variables);\n this.player?.requestSeek(this.player.playback.frame);\n this.player?.playback.reload();\n break;\n case 'looping':\n this._looping = newValue === 'true';\n this.player?.toggleLoop(newValue === 'true');\n break;\n case 'fps':\n case 'quality':\n case 'width':\n case 'height':\n this.updateSettings();\n break;\n case 'volume':\n this._volume = newValue;\n this.volumeChangeRequested = true;\n }\n }\n\n /**\n * Runs when the element is removed from the DOM.\n */\n public disconnectedCallback() {\n this.player?.deactivate();\n this.player?.onRender.unsubscribe(this.render);\n\n this.removeEventListener('seekto', this.handleSeekTo);\n this.removeEventListener('volumechange', this.handleVolumeChange);\n }\n\n /**\n * Runs when the element is added to the DOM.\n */\n public connectedCallback() {\n this.player?.activate();\n this.player?.onRender.subscribe(this.render);\n\n this.addEventListener('seekto', this.handleSeekTo);\n this.addEventListener('volumechange', this.handleVolumeChange);\n }\n\n /**\n * Triggered by the timeline.\n */\n private handleSeekTo = (event: Event) => {\n if (!this.project) {\n return;\n }\n\n const e = event as CustomEvent;\n const timeSec = e.detail as number;\n const frame = timeSec * this.player!.playback.fps;\n this.time = timeSec;\n this.player?.requestSeek(frame);\n this.volumeChangeRequested = true;\n };\n\n private handleVolumeChange = (event: Event) => {\n if (!this.project) {\n return;\n }\n\n const e = event as CustomEvent;\n this._volume = e.detail;\n\n this.player?.playback.currentScene.adjustVolume(this._volume);\n };\n\n /**\n * Triggered by the player.\n */\n private handleFrameChanged = (frame: number) => {\n if (!this.project || !this.player) {\n return;\n }\n this.time = frame / this.player.playback.fps;\n\n if (this.volumeChangeRequested || frame === 0) {\n this.player?.playback.currentScene.adjustVolume(this._volume);\n this.volumeChangeRequested = false;\n }\n };\n\n /**\n * Called on every frame.\n */\n private render = async () => {\n if (this.player && this.project) {\n await this.stage.render(\n this.player.playback.currentScene,\n this.player.playback.previousScene,\n );\n\n this.dispatchEvent(new CustomEvent('timeupdate', {detail: this.time}));\n\n const durationInFrames = this.player.playback.duration;\n if (durationInFrames === this.duration) {\n return;\n }\n\n this.duration = durationInFrames;\n\n const durationInSeconds = durationInFrames / this.player.playback.fps;\n this.dispatchEvent(\n new CustomEvent('duration', {detail: durationInSeconds}),\n );\n }\n };\n\n private updateSettings() {\n if (!this.defaultSettings) {\n return;\n }\n\n // Use the requested quality (resolutionScale) instead of forcing 1,\n // so the preview canvas can render at higher internal resolution.\n const resolutionScale =\n Number.isFinite(this.quality) && this.quality > 0\n ? this.quality\n : this.defaultSettings.resolutionScale ?? 1;\n\n const settings = {\n ...this.defaultSettings,\n size: new Vector2(this.width, this.height),\n resolutionScale,\n fps: this.fps,\n };\n this.stage.configure(settings);\n this.player?.configure(settings);\n }\n}\n\nif (!customElements.get(ID)) {\n customElements.define(ID, TwickPlayer);\n}\n"],"mappings":"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","names":[]}