@liqvid/controls 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) Yuri Sulyma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @liqvid/recording.
2
+
3
+ Recording functionality for [`Liqvid`](https://liqvidjs.org/). Documentation at https://liqvidjs.org/docs/plugins/recording.
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ import { useColorScheme } from "@liqvid/color-scheme/react";
3
+ import { useKeymap } from "@liqvid/keymap/react";
4
+ import { onClickReact } from "@liqvid/utils";
5
+ import classNames from "classnames";
6
+ import { useEffect, useMemo } from "react";
7
+ import { bind, convertShortcuts, unbind } from "./utils";
8
+ export function ColorSchemeToggle({ className, render, shortcuts, }) {
9
+ const { colorScheme, toggleColorScheme } = useColorScheme();
10
+ const keymap = useKeymap();
11
+ useEffect(() => {
12
+ // keyboard shortcut
13
+ bind(keymap, shortcuts, toggleColorScheme);
14
+ return () => {
15
+ unbind(keymap, shortcuts, toggleColorScheme);
16
+ };
17
+ }, [keymap, shortcuts, toggleColorScheme]);
18
+ const events = useMemo(() => onClickReact(toggleColorScheme), [toggleColorScheme]);
19
+ return render({ colorScheme }, {
20
+ "aria-keyshortcuts": convertShortcuts(shortcuts),
21
+ className: classNames("lv-controls-color-scheme lv-controls-button", className),
22
+ ...events,
23
+ });
24
+ }
@@ -0,0 +1,32 @@
1
+ "use client";
2
+ import { useKeymap } from "@liqvid/keymap/react";
3
+ import { onClickReact, useForceUpdate } from "@liqvid/utils";
4
+ import classNames from "classnames";
5
+ import { useEffect } from "react";
6
+ import { exitFullScreen, isFullScreen, onFullScreenChange, requestFullScreen, } from "./fake-fullscreen";
7
+ import { convertShortcuts } from "./utils";
8
+ const toggleFullScreen = () => isFullScreen() ? exitFullScreen() : requestFullScreen();
9
+ const events = onClickReact(toggleFullScreen);
10
+ /** Fullscreen control */
11
+ export function FullScreen({ className, render, shortcuts, }) {
12
+ const keymap = useKeymap();
13
+ const forceUpdate = useForceUpdate();
14
+ useEffect(() => {
15
+ // listener
16
+ onFullScreenChange(forceUpdate);
17
+ // keyboard shortcut
18
+ for (const seq of shortcuts ?? []) {
19
+ keymap.bind(seq, toggleFullScreen);
20
+ }
21
+ return () => {
22
+ for (const seq of shortcuts ?? []) {
23
+ keymap.unbind(seq, toggleFullScreen);
24
+ }
25
+ };
26
+ }, [forceUpdate, keymap, shortcuts]);
27
+ return render({ isFullScreen: isFullScreen() ?? false }, {
28
+ "aria-keyshortcuts": convertShortcuts(shortcuts),
29
+ className: classNames("lv-controls-fullscreen lv-controls-button", className),
30
+ ...events,
31
+ });
32
+ }
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { useKeymap } from "@liqvid/keymap/react";
3
+ import { usePlayback, usePlaybackEvent } from "@liqvid/playback/react";
4
+ import { onClickReact, useForceUpdate } from "@liqvid/utils";
5
+ import classNames from "classnames";
6
+ import { useCallback, useEffect, useMemo } from "react";
7
+ import { bind, convertShortcuts, unbind } from "./utils";
8
+ /** Mute/unmute button */
9
+ export function Mute({ className, render, shortcuts }) {
10
+ const keymap = useKeymap();
11
+ const playback = usePlayback();
12
+ const forceUpdate = useForceUpdate();
13
+ // keyboard controls
14
+ const toggleMute = useCallback(() => {
15
+ playback.muted = !playback.muted;
16
+ }, [playback]);
17
+ usePlaybackEvent("volumechange", forceUpdate);
18
+ useEffect(() => {
19
+ // keyboard shortcuts
20
+ bind(keymap, shortcuts, toggleMute);
21
+ return () => {
22
+ // keyboard shortcuts
23
+ unbind(keymap, shortcuts, toggleMute);
24
+ };
25
+ }, [keymap, shortcuts, toggleMute]);
26
+ const events = useMemo(() => onClickReact(toggleMute), [toggleMute]);
27
+ if (render) {
28
+ return render({
29
+ muted: playback.muted,
30
+ volume: playback.volume,
31
+ }, {
32
+ "aria-keyshortcuts": convertShortcuts(shortcuts),
33
+ className: classNames("lv-controls-mute lv-controls-button", className),
34
+ ...events,
35
+ });
36
+ }
37
+ }
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import { useKeymap } from "@liqvid/keymap/react";
3
+ import { usePlayback, usePlaybackEvent } from "@liqvid/playback/react";
4
+ import { onClickReact, useForceUpdate } from "@liqvid/utils";
5
+ import classNames from "classnames";
6
+ import { useCallback, useEffect, useMemo } from "react";
7
+ import { convertShortcuts } from "./utils";
8
+ /** Control for playing/pausing */
9
+ export function PlayPause({ className, render, shortcuts, }) {
10
+ const keymap = useKeymap();
11
+ const playback = usePlayback();
12
+ const forceUpdate = useForceUpdate();
13
+ usePlaybackEvent("pause", forceUpdate);
14
+ usePlaybackEvent("play", forceUpdate);
15
+ usePlaybackEvent("seeked", forceUpdate);
16
+ usePlaybackEvent("seeking", forceUpdate);
17
+ usePlaybackEvent("stop", forceUpdate);
18
+ // keyboard controls
19
+ const toggle = useCallback(() => playback[playback.paused ? "play" : "pause"](), [playback]);
20
+ useEffect(() => {
21
+ // keyboard shortcut
22
+ for (const seq of shortcuts ?? []) {
23
+ keymap.bind(seq, toggle);
24
+ }
25
+ return () => {
26
+ // unbind keyboard controls
27
+ for (const seq of shortcuts ?? []) {
28
+ keymap.unbind(seq, toggle);
29
+ }
30
+ };
31
+ }, [keymap, shortcuts, toggle]);
32
+ // event handler
33
+ const events = useMemo(() => onClickReact(toggle), [toggle]);
34
+ return render({ paused: playback.paused, seeking: playback.seeking }, {
35
+ "aria-keyshortcuts": convertShortcuts(shortcuts),
36
+ className: classNames("lv-controls-playpause lv-controls-button", className),
37
+ ...events,
38
+ });
39
+ }
@@ -0,0 +1,194 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Duration } from "@liqvid/duration";
4
+ import { useKeymap } from "@liqvid/keymap/react";
5
+ import { usePlayback, usePlaybackEvent } from "@liqvid/playback/react";
6
+ import { anyHover, between, clamp, onDrag } from "@liqvid/utils";
7
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
8
+ import { ThumbnailBox } from "./ThumbnailBox";
9
+ export function ScrubberBar({ shortcuts, thumbs, ...props }) {
10
+ const playback = usePlayback();
11
+ const [progress, setProgress] = useState({
12
+ scrubber: playback.currentTime / playback.duration,
13
+ thumb: playback.currentTime / playback.duration,
14
+ });
15
+ const [showThumb, setShowThumb] = useState(false);
16
+ // refs
17
+ const scrubberBar = useRef(null);
18
+ /* Event handlers */
19
+ usePlaybackEvent("seek", useCallback(() => {
20
+ if (playback.seeking)
21
+ return;
22
+ const progress = playback.currentTime / playback.duration;
23
+ setProgress({ scrubber: progress, thumb: progress });
24
+ }, [playback]));
25
+ usePlaybackEvent("seeked", useCallback(() => {
26
+ const progress = playback.currentTime / playback.duration;
27
+ setProgress((prev) => ({ scrubber: progress, thumb: prev.thumb }));
28
+ }, [playback]));
29
+ usePlaybackEvent("timeupdate", useCallback(() => {
30
+ const progress = playback.currentTime / playback.duration;
31
+ setProgress((prev) => ({ scrubber: progress, thumb: prev.thumb }));
32
+ }, [playback]));
33
+ useRelativeShortcuts(shortcuts?.relative);
34
+ usePercentageShortcuts(shortcuts?.percentage);
35
+ // event handlers
36
+ const divEvents = useMemo(() => {
37
+ if (!anyHover)
38
+ return {};
39
+ const listener = onDrag(
40
+ // move
41
+ (_e, { x }) => {
42
+ if (!scrubberBar.current)
43
+ return;
44
+ const rect = scrubberBar.current.getBoundingClientRect(), progress = clamp(0, (x - rect.left) / rect.width, 1);
45
+ setProgress({ scrubber: progress, thumb: progress });
46
+ playback.currentTime = progress * playback.duration;
47
+ },
48
+ // down
49
+ (_e, { x }) => {
50
+ if (!scrubberBar.current)
51
+ return;
52
+ playback.seeking = true;
53
+ const rect = scrubberBar.current.getBoundingClientRect(), progress = clamp(0, (x - rect.left) / rect.width, 1);
54
+ setProgress({ scrubber: progress, thumb: progress });
55
+ playback.currentTime = progress * playback.duration;
56
+ },
57
+ // up
58
+ () => {
59
+ playback.seeking = false;
60
+ });
61
+ return {
62
+ onMouseDown: (e) => listener(e.nativeEvent),
63
+ };
64
+ }, [playback]);
65
+ // events to attach on the wrapper
66
+ const wrapEvents = useMemo(() => {
67
+ const props = {};
68
+ if (anyHover) {
69
+ Object.assign(props, {
70
+ onMouseMove: (e) => {
71
+ if (!scrubberBar.current)
72
+ return;
73
+ const rect = scrubberBar.current.getBoundingClientRect(), progress = clamp(0, (e.clientX - rect.left) / rect.width, 1);
74
+ setProgress((prev) => ({ scrubber: prev.scrubber, thumb: progress }));
75
+ },
76
+ onMouseOut: () => setShowThumb(false),
77
+ // show thumb preview on hover
78
+ onMouseOver: () => setShowThumb(true),
79
+ });
80
+ }
81
+ const listener = onDrag(
82
+ // move
83
+ (_e, { x }) => {
84
+ if (!scrubberBar.current)
85
+ return;
86
+ const rect = scrubberBar.current.getBoundingClientRect(), progress = clamp(0, (x - rect.left) / rect.width, 1);
87
+ setProgress({ scrubber: progress, thumb: progress });
88
+ },
89
+ // start
90
+ (e) => {
91
+ e.preventDefault();
92
+ e.stopPropagation();
93
+ playback.seeking = true;
94
+ setShowThumb(true);
95
+ },
96
+ // end
97
+ (e, { x }) => {
98
+ e.preventDefault();
99
+ if (!scrubberBar.current)
100
+ return;
101
+ const rect = scrubberBar.current.getBoundingClientRect(), progress = clamp(0, (x - rect.left) / rect.width, 1);
102
+ setShowThumb(false);
103
+ playback.seeking = false;
104
+ playback.currentTime = progress * playback.duration;
105
+ });
106
+ props.onTouchStart = listener;
107
+ return props;
108
+ }, [playback]);
109
+ // events to be attached to the scrubber
110
+ const scrubberEvents = useMemo(() => {
111
+ // if (anyHover) return {};
112
+ const listener = onDrag(
113
+ // move
114
+ (_e, { x }) => {
115
+ if (!scrubberBar.current)
116
+ return;
117
+ const rect = scrubberBar.current.getBoundingClientRect(), progress = clamp(0, (x - rect.left) / rect.width, 1);
118
+ setProgress({ scrubber: progress, thumb: progress });
119
+ },
120
+ // start
121
+ (e) => {
122
+ e.preventDefault();
123
+ e.stopPropagation();
124
+ playback.seeking = true;
125
+ setShowThumb(true);
126
+ },
127
+ // end
128
+ (e, { x }) => {
129
+ e.preventDefault();
130
+ if (!scrubberBar.current)
131
+ return;
132
+ const rect = scrubberBar.current.getBoundingClientRect(), progress = clamp(0, (x - rect.left) / rect.width, 1);
133
+ setShowThumb(false);
134
+ playback.seeking = false;
135
+ playback.currentTime = progress * playback.duration;
136
+ });
137
+ return {
138
+ onTouchStart: listener,
139
+ };
140
+ }, [playback]);
141
+ // TODO: optimize this
142
+ const activeHighlight = thumbs?.highlights?.find((h) => between(Duration.from(h.time).inSeconds() / playback.duration, progress.thumb, Duration.from(h.time).inSeconds() / playback.duration + 0.01));
143
+ return (_jsxs("div", { className: "lv-controls-scrub", ref: scrubberBar, ...divEvents, ...props, children: [thumbs && (_jsx(ThumbnailBox, { ...thumbs, progress: progress.thumb, show: showThumb, title: activeHighlight?.title })), _jsxs("div", { className: "lv-controls-scrub-wrap", ...wrapEvents, children: [_jsxs("svg", { className: "lv-controls-scrub-progress", preserveAspectRatio: "none", viewBox: "0 0 100 10", children: [_jsx("rect", { className: "lv-progress-elapsed", height: "10", width: progress.scrubber * 100, x: "0", y: "0" }), _jsx("rect", { className: "lv-progress-remaining", height: "10", width: (1 - progress.scrubber) * 100, x: progress.scrubber * 100, y: "0" }), thumbs?.highlights?.map(({ time }) => {
144
+ const $time = Duration.from(time);
145
+ return (_jsx("rect", { className: ["lv-thumb-highlight"]
146
+ .concat($time.inSeconds() <= playback.currentTime ? "past" : [])
147
+ .join(" "), height: "10", width: "1", x: ($time.inSeconds() / playback.duration) * 100, y: "0" }, $time.inSeconds()));
148
+ })] }), _jsx("svg", { className: "lv-scrubber", style: { left: `calc(${progress.scrubber * 100}% - 6px)` }, viewBox: "0 0 100 100", ...scrubberEvents, children: _jsx("circle", { cx: "50", cy: "50", r: "50", stroke: "none" }) })] })] }));
149
+ }
150
+ function useRelativeShortcuts(shortcuts) {
151
+ const keymap = useKeymap();
152
+ const playback = usePlayback();
153
+ const configs = useMemo(() => shortcuts?.map(({ key, delta }) => {
154
+ delta = Duration.from(delta);
155
+ const callback = () => {
156
+ playback.currentTime$ = playback.currentTime$.plus(delta);
157
+ };
158
+ return [key, callback];
159
+ }) ?? [], [playback, shortcuts]);
160
+ useEffect(() => {
161
+ // bind
162
+ for (const [key, callback] of configs) {
163
+ keymap.bind(key, callback);
164
+ }
165
+ // unbind
166
+ return () => {
167
+ for (const [key, callback] of configs) {
168
+ keymap.unbind(key, callback);
169
+ }
170
+ };
171
+ }, [configs, keymap]);
172
+ }
173
+ function usePercentageShortcuts(shortcuts) {
174
+ const keymap = useKeymap();
175
+ const playback = usePlayback();
176
+ const configs = useMemo(() => shortcuts?.map(({ key, multiplier }) => {
177
+ const callback = () => {
178
+ playback.currentTime = playback.duration * multiplier;
179
+ };
180
+ return [key, callback];
181
+ }) ?? [], [playback, shortcuts]);
182
+ useEffect(() => {
183
+ // bind
184
+ for (const [key, callback] of configs) {
185
+ keymap.bind(key, callback);
186
+ }
187
+ // unbind
188
+ return () => {
189
+ for (const [key, callback] of configs) {
190
+ keymap.unbind(key, callback);
191
+ }
192
+ };
193
+ }, [configs, keymap]);
194
+ }
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { usePlayback } from "@liqvid/playback/react";
3
+ import { formatTime } from "@liqvid/utils";
4
+ import { useEffect } from "react";
5
+ export function ThumbnailBox({ cols = 5, rows = 5, frequency = 4, path, progress, show, title, height = 100, width = 160, }) {
6
+ const { duration } = usePlayback();
7
+ const count = cols * rows;
8
+ useEffect(() => {
9
+ // preload thumbs (once more important loading has taken place)
10
+ const maxSlide = Math.floor(duration / frequency), maxSheet = Math.floor(maxSlide / count);
11
+ for (let sheetNum = 0; sheetNum <= maxSheet; ++sheetNum) {
12
+ const img = new Image();
13
+ img.src = path.replace("%s", sheetNum.toString());
14
+ }
15
+ }, [count, frequency, path, duration]);
16
+ const time = progress * duration;
17
+ const markerNum = Math.floor(time / frequency);
18
+ const sheetNum = Math.floor(markerNum / count);
19
+ const markerNumOnSheet = markerNum % count;
20
+ const row = Math.floor(markerNumOnSheet / rows);
21
+ const col = markerNumOnSheet % rows;
22
+ const sheetName = path.replace("%s", sheetNum.toString());
23
+ return (_jsxs("div", { className: "lv-controls-thumbnail", style: {
24
+ display: show ? "block" : "none",
25
+ left: `${progress * 100}%`,
26
+ }, children: [title && _jsx("span", { className: "lv-thumbnail-title", children: title }), _jsxs("div", { className: "lv-thumbnail-box", children: [_jsx("img", { alt: "", src: sheetName, style: {
27
+ left: `-${col * width}px`,
28
+ maxWidth: "unset",
29
+ top: `-${row * height}px`,
30
+ } }), _jsx("span", { className: "lv-thumbnail-time", children: formatTime(time * 1000) })] })] }));
31
+ }
@@ -0,0 +1,19 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { usePlayback, usePlaybackEvent, useTime } from "@liqvid/playback/react";
4
+ import { formatTime, useForceUpdate } from "@liqvid/utils";
5
+ import { useRef } from "react";
6
+ export function TimeDisplay() {
7
+ const playback = usePlayback();
8
+ const forceUpdate = useForceUpdate();
9
+ usePlaybackEvent("durationchange", forceUpdate);
10
+ usePlaybackEvent("seek", forceUpdate);
11
+ useTime(() => {
12
+ const timeElt = timeRef.current;
13
+ if (!timeElt)
14
+ return;
15
+ timeElt.innerText = formatTime(playback.currentTime$);
16
+ });
17
+ const timeRef = useRef(null);
18
+ return (_jsxs("span", { className: "lv-controls-time", children: [_jsx("time", { className: "lv-current-time", ref: timeRef, children: formatTime(playback.currentTime$) }), _jsx("span", { className: "lv-time-separator", children: "/" }), _jsx("time", { className: "lv-total-time", children: formatTime(playback.duration$) })] }));
19
+ }
@@ -0,0 +1,65 @@
1
+ "use client";
2
+ import { useKeymap } from "@liqvid/keymap/react";
3
+ import { usePlayback, usePlaybackEvent } from "@liqvid/playback/react";
4
+ import { useForceUpdate } from "@liqvid/utils";
5
+ import classNames from "classnames";
6
+ import { useCallback, useEffect } from "react";
7
+ const VOLUME_MAX = 100;
8
+ /** Volume control */
9
+ export function VolumeSlider({ className, render, shortcuts, }) {
10
+ const keymap = useKeymap();
11
+ const playback = usePlayback();
12
+ const forceUpdate = useForceUpdate();
13
+ usePlaybackEvent("volumechange", forceUpdate);
14
+ useEffect(() => {
15
+ // keyboard shortcuts
16
+ const shortcutCallbacks = (shortcuts ?? []).map((s) => {
17
+ if ("delta" in s) {
18
+ return {
19
+ callback: () => {
20
+ playback.muted = false;
21
+ playback.volume += s.delta / VOLUME_MAX;
22
+ },
23
+ seq: s.seq,
24
+ };
25
+ }
26
+ else {
27
+ return {
28
+ callback: () => {
29
+ playback.muted = false;
30
+ playback.volume = s.value;
31
+ },
32
+ seq: s.seq,
33
+ };
34
+ }
35
+ });
36
+ for (const { seq, callback } of shortcutCallbacks) {
37
+ keymap.bind(seq, callback);
38
+ }
39
+ return () => {
40
+ // keyboard shortcuts
41
+ for (const { seq: key, callback } of shortcutCallbacks) {
42
+ keymap.unbind(key, callback);
43
+ }
44
+ };
45
+ }, [forceUpdate, keymap, playback]);
46
+ // input
47
+ const onChange = useCallback((e) => {
48
+ playback.muted = false;
49
+ playback.volume = parseFloat(e.target.value) / VOLUME_MAX;
50
+ }, [playback]);
51
+ if (render) {
52
+ return render({
53
+ muted: playback.muted,
54
+ volume: playback.volume,
55
+ }, {
56
+ // "aria-keyshortcuts": convertShortcuts(shortcuts),
57
+ className: classNames("lv-controls-volume-slider", className),
58
+ max: VOLUME_MAX,
59
+ min: 0,
60
+ onChange,
61
+ type: "range",
62
+ value: playback.muted ? 0 : playback.volume * VOLUME_MAX,
63
+ });
64
+ }
65
+ }
@@ -0,0 +1,33 @@
1
+ import { exitFullScreen as $exitFullScreen, isFullScreen as $isFullScreen, onFullScreenChange as $onFullScreenChange, requestFullScreen as $requestFullScreen, fullscreenEnabled, } from "./polyfills";
2
+ let __isFullScreen = false;
3
+ const __callbacks = [];
4
+ export const requestFullScreen = fullscreenEnabled
5
+ ? $requestFullScreen
6
+ : () => {
7
+ window.parent.postMessage({ type: "fake-fullscreen", value: true }, window.parent.origin);
8
+ if (!__isFullScreen) {
9
+ __isFullScreen = true;
10
+ for (const _ of __callbacks)
11
+ _();
12
+ }
13
+ };
14
+ export const exitFullScreen = fullscreenEnabled
15
+ ? $exitFullScreen
16
+ : () => {
17
+ window.parent.postMessage({ type: "fake-fullscreen", value: false }, window.parent.origin);
18
+ if (__isFullScreen) {
19
+ __isFullScreen = false;
20
+ for (const _ of __callbacks)
21
+ _();
22
+ }
23
+ };
24
+ export const isFullScreen = fullscreenEnabled
25
+ ? $isFullScreen
26
+ : () => {
27
+ return __isFullScreen;
28
+ };
29
+ export const onFullScreenChange = fullscreenEnabled
30
+ ? $onFullScreenChange
31
+ : (callback) => {
32
+ __callbacks.push(callback);
33
+ };
@@ -0,0 +1,7 @@
1
+ export { ColorSchemeToggle } from "./ColorSchemeToggle";
2
+ export { FullScreen } from "./FullScreen";
3
+ export { Mute } from "./MuteButton";
4
+ export { PlayPause } from "./PlayPause";
5
+ export { ScrubberBar, } from "./ScrubberBar";
6
+ export { TimeDisplay } from "./TimeDisplay";
7
+ export { VolumeSlider, } from "./VolumeSlider";
@@ -0,0 +1,53 @@
1
+ import { isClient } from "@liqvid/ssr";
2
+ const id = (_) => _;
3
+ export const fullscreenEnabled = isClient
4
+ ? [
5
+ "fullscreenEnabled",
6
+ "webkitFullscreenEnabled",
7
+ "mozFullScreenEnabled",
8
+ "msFullscreenEnabled",
9
+ ]
10
+ // biome-ignore lint/suspicious/noExplicitAny: vendor-specific
11
+ .map((_) => document[_])
12
+ .concat(false)
13
+ .find((_) => _ !== undefined)
14
+ : false;
15
+ export const requestFullScreen = isClient
16
+ ? [
17
+ "requestFullscreen",
18
+ "webkitRequestFullscreen",
19
+ "mozRequestFullScreen",
20
+ "msRequestFullscreen",
21
+ ]
22
+ // biome-ignore lint/suspicious/noExplicitAny: vendor-specific
23
+ .map((_) => document.body[_])
24
+ .concat(() => { })
25
+ .find(id)
26
+ .bind(document.body)
27
+ : async () => { };
28
+ export const exitFullScreen = isClient
29
+ ? [
30
+ "exitFullscreen",
31
+ "webkitExitFullscreen",
32
+ "mozCancelFullScreen",
33
+ "msExitFullscreen",
34
+ ]
35
+ // biome-ignore lint/suspicious/noExplicitAny: vendor-specific
36
+ .map((_) => document[_])
37
+ .concat(async () => { })
38
+ .find(id)
39
+ .bind(document)
40
+ : async () => { };
41
+ export const isFullScreen = () => ["fullscreen", "webkitIsFullScreen", "mozFullScreen"]
42
+ // biome-ignore lint/suspicious/noExplicitAny: vendor-specific
43
+ .map((_) => document[_])
44
+ .find((_) => _ !== undefined);
45
+ export function onFullScreenChange(callback) {
46
+ for (const event of [
47
+ "fullscreenchange",
48
+ "webkitfullscreenchange",
49
+ "mozfullscreenchange",
50
+ "MSFullscreenChange",
51
+ ])
52
+ document.addEventListener(event, callback);
53
+ }
@@ -0,0 +1,31 @@
1
+ export function bind(keymap, seqs, callback) {
2
+ if (!seqs)
3
+ return;
4
+ if (typeof seqs === "string") {
5
+ keymap.bind(seqs, callback);
6
+ }
7
+ else {
8
+ for (const s of seqs) {
9
+ keymap.bind(s, callback);
10
+ }
11
+ }
12
+ }
13
+ export function unbind(keymap, seqs, callback) {
14
+ if (!seqs)
15
+ return;
16
+ if (typeof seqs === "string") {
17
+ keymap.bind(seqs, callback);
18
+ }
19
+ else {
20
+ for (const s of seqs) {
21
+ keymap.bind(s, callback);
22
+ }
23
+ }
24
+ }
25
+ export function convertShortcuts(keys) {
26
+ if (typeof keys === "string")
27
+ return keys;
28
+ if (typeof keys === "undefined")
29
+ return undefined;
30
+ return keys.join(" ");
31
+ }
@@ -0,0 +1 @@
1
+ {"fileNames":["../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.iterable.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.core.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.date.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.object.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.string.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.array.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.object.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.string.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.date.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.string.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.number.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.promise.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.string.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.array.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.error.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.object.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.string.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../../node_modules/.pnpm/@types+react@19.2.14/node_modules/@types/react/global.d.ts","../../../node_modules/.pnpm/csstype@3.2.3/node_modules/csstype/index.d.ts","../../../node_modules/.pnpm/@types+react@19.2.14/node_modules/@types/react/index.d.ts","../../../node_modules/.pnpm/@types+react@19.2.14/node_modules/@types/react/jsx-runtime.d.ts","../../hydration/dist/types/types.d.ts","../../hydration/dist/types/hydrateelement.d.ts","../../hydration/dist/types/hydrateonclient.d.ts","../../hydration/dist/types/hydratevariants.d.ts","../../hydration/dist/types/persistence.d.ts","../../hydration/dist/types/sneakyscript.d.ts","../../hydration/dist/types/index.d.ts","../../color-scheme/dist/types/react.d.ts","../../keymap/dist/types/index.d.mts","../../keymap/dist/types/react.d.ts","../../duration/dist/types/index.d.mts","../../../node_modules/.pnpm/bezier-easing@2.1.0/node_modules/bezier-easing/src/index.d.ts","../../utils/dist/types/replay-data.d.ts","../../utils/dist/types/animation.d.ts","../../utils/dist/types/collections.d.ts","../../utils/dist/types/interaction.d.ts","../../utils/dist/types/math.d.ts","../../utils/dist/types/misc.d.ts","../../utils/dist/types/react.d.ts","../../utils/dist/types/svg.d.ts","../../utils/dist/types/time.d.ts","../../utils/dist/types/types.d.ts","../../utils/dist/types/index.d.ts","../../../node_modules/.pnpm/classnames@2.5.1/node_modules/classnames/index.d.ts","../src/utils.ts","../src/colorschemetoggle.tsx","../../ssr/dist/types/index.d.mts","../src/polyfills.ts","../src/fake-fullscreen.ts","../src/fullscreen.tsx","../../event-emitter/dist/types/types.d.mts","../../event-emitter/dist/types/index.d.mts","../../playback/dist/types/synthetic-playback.d.mts","../../playback/dist/types/playback.d.mts","../../playback/dist/types/react.d.ts","../src/mutebutton.tsx","../src/playpause.tsx","../src/thumbnailbox.tsx","../src/scrubberbar.tsx","../src/timedisplay.tsx","../src/volumeslider.tsx","../src/index.ts","../../../node_modules/.pnpm/@types+react-dom@19.2.3_@types+react@19.2.14/node_modules/@types/react-dom/index.d.ts"],"fileIdsList":[[62],[60,61],[63,70],[62,63,71,73,86,87,88],[63,91],[62,63,73,86,87,88,92],[63,89,93,99,100,101,102,103,104],[62,63,73,86,87,88,98],[63,90],[62,63,73,74,86,98,101],[62,63,74,86,98],[62,63,86,98],[63,72],[62,63,73,86,87,98],[94],[95],[63,64],[64,65,66,67,68,69],[64],[63],[62,63,72],[74,96],[62,63,74,96,97],[74,75,76],[76,77,78,79,80,81,82,83,84,85],[79],[74]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"7e29f41b158de217f94cb9676bf9cbd0cd9b5a46e1985141ed36e075c52bf6ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"dc0a7f107690ee5cd8afc8dbf05c4df78085471ce16bdd9881642ec738bc81fe","impliedFormat":1},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},"0eff7e8c827a6b4d69d8d70da4fb83f24f1a0b2b600556c7929dc9925095f3ba","3bacb44aeb3ebd20957d1dc2ef689ee8007157144b9cb1cbdaf2a3531d44c66b","cee0ce9ca1e0bc522c0144c26f1a65eb20b901b2761804bcc26c4713cc83f841","4c5a1176bb2807ba5c543bbd5ca2722ed22bdf27022c51f3a7c9ee872f1cc730","b4f2d9d6adc44b6b5186a439721d7679eb3bee520aacd746b3116675883cd831","8125f98748dd4bbd4ce71d509ecea3474a3cad8f3d1352e29c2a6a5b8b399738","10240982d1ac7dc0590c971472849d48a954f31fa5884afcc2eed3e11cdcd28e","b8b662c06dc5c6f1a8e4455776542ed35bcbd220dd5c66439854d21f46e74dfa",{"version":"0427b580231f52ef097919ba61fcfe5e37f5fa5523ed4fd6d4864bde4dd8470c","impliedFormat":99},"de635f3aa40a27cca6d425276d463213f07bdaf54873a776f4a03b5fdecee1ce",{"version":"6f2dfeeb74d340004f68f576add21aac0668ae1da4c4e9ba3193541f3e865867","impliedFormat":99},{"version":"5e221d7876afbd3b808798515c5dd53d49e8aa4fe4c6095bc6760b30acff4b37","impliedFormat":1},"3ec9f283a3eb75a2fb4a89467a97ae6d13d3cd7589dadb5e1e5b59a875e6f6c9","cfbb4e78e9b657a14c100a97f6f8d9997a208fe30695895ab384f3fc0246492e","06f5a3f26c2c2dddab05df1375db0a6353efa4bcd818d53e0bbde7b94024d732",{"version":"8005021af1514f4193c860c4b7eeb2f01f660c443c2f7180f2c93aed44af3b16","affectsGlobalScope":true},"2b0dafed8302e979ebbee421ac05e790ee5621457a264cb1b8e76ef3afd72f3b","7f8b57b8dd275f27fc63e58f9f0d608054060cd7f2d57a3bc53653c39f6326e3","4ea806290d11878e3ee168b836612c19bc630c4f0782b1d1582b05b90d771ff8","69eaf11c5a8479bc92d32da085bfb9cba8ca6884366b4941369a4d24a27992c6","eca06e9eb6ebd0a81decc4d04aa1d0df7a2e6cb4588c3059269321c9e0fecc09","239f3c704fc87252b5dd00f70e35bc57af83a33e20a3ad71d14c3d2d99c3b4a4","3db60448ebc41502a9869dd39a4a6a3682ca9b5fcb8297399676fea8c88b7678",{"version":"1dfdec0ec9c299625d20c5cb8f96e2a801c81d91669c6245f520e8734a92fb3d","impliedFormat":1},{"version":"b131ef9faad176b6c142cc6fe1500558f1afb70bce4f2ba2c0df60d76f4ee536","signature":"5cbe0951bd491345f6ef3e9b38dbc0b62c6a8bd553e8ed9c7f523a848ecd72cb"},{"version":"d972ca7887d35ee6efd3781ac892fece1a9c3846f7163aeadbddff8616ecf763","signature":"38b01c0690caaaf23d9959db3fbcabca5313bd83eaaa7cfe3f076fa142e0286f"},{"version":"3954f113df61328c6af4bc83c9e50ab16ddac2ee9793210608e79e40da190061","impliedFormat":99},{"version":"80baa4231e6c2d35c254d82f3fdff692dc6c88f3e4f1b97b05b42a1c29020f5d","signature":"ccd62f94aa21c99e133a749d6e87e3d1695b48a7e91e7503b9fa1549f60b767a"},{"version":"765d7581ac15e7a7aa31b7c00c55291d6ba74b188591cc382e49af47390fc2b6","signature":"94870bb11f04ff117af0715c59d24a38ccfe1f10a3a4992f9c0f8d01443c5213"},{"version":"5e789963d8dca646eae96af4669e88b61ed3acac924bc1ec62b443fff9c3645c","signature":"acf27d2f62d5d325bbe4da5a5068204fb94ae542607529ee896e6b2df6d989d0"},{"version":"be831a6a1a2b57943dff1d23aa3b335b3b1621adc35807d8e3de0a0a9e96d0c9","impliedFormat":99},{"version":"5ce65fa2203ccb5304b06fadecfc53b41325ca5069f285a80f980acc476031f5","impliedFormat":99},{"version":"a6e7dd43e08acdccbc1d7578edeb2ce08b3bca87de26782bbc66192731f9cb83","impliedFormat":99},{"version":"5d4aad5c5865c0358f488e447a0874080b0ef6094f8b313abfb4ef09de83dce5","affectsGlobalScope":true,"impliedFormat":99},"33e8acec13777aeccf10a56d2b39f2ebc46b924dde7989365fc51b7c8b390778",{"version":"0c0efef6a8299f087e4cc90636068fc36e60802856cc375b191835efff149ddf","signature":"cdf78dfee97282d3c40412a02f815e2203b8a9d548e714f78c9dac071047fc46"},{"version":"b37bf0ccebe05b1a40f816313e6a5ef84a2cb1b91a8a336b5f79b6ebdba7c430","signature":"e5609d862c97cf859e4c70ae0aeb7f82e610b8db023847cd759c8c818897e7a1"},{"version":"9dc2c42fbe64f8d3a07136707b3414912d7ca253a890514f9ae66f6c1a2b4a73","signature":"ea2cc5eb65c508b48ab52485c5c0466ea0830841cc383cd0fbb24130e30ca85e"},{"version":"4efd8eacc2e28cffcbeffdd9952f4fd0c5b5b56ec98d1a39b4a3359544fc22af","signature":"c2e4312739fbeba0ad0252ed639e3e789bb6cd2982fb560ccc0b9b6546782455"},{"version":"468abb1fdf97ce4c62cb805c358b9c164d5d23324e58368501ecd75d292099fe","signature":"f455e9d1d6106b6330464b46f3e15ff86781fc09812999abc100462b5f858d72"},{"version":"93a4e22f8716f1b77a04c1f523ada1194c4baf6bd068054afdb37c187200d06f","signature":"7cc475b8c880bd32f223e0ade4ba75b1785880740211469dc38c21e59d696bf9"},{"version":"9ed6420e92e7bd2796786b4a1d0ca16da7c695453addf04cc02847a1888f8a50","signature":"8967ac0df21fd15975f3e4f0446e518d29d2c767c21ae55d916f46b6cff1d5d4"},{"version":"be1cc4d94ea60cbe567bc29ed479d42587bf1e6cba490f123d329976b0fe4ee5","impliedFormat":1}],"root":[88,89,[91,93],[99,105]],"options":{"alwaysStrict":true,"composite":true,"declaration":true,"declarationDir":"./types","esModuleInterop":true,"jsx":4,"module":99,"noImplicitAny":true,"noImplicitOverride":true,"noUncheckedIndexedAccess":false,"outDir":"./esm","removeComments":false,"rewriteRelativeImportExtensions":true,"rootDir":"../src","skipLibCheck":true,"strict":true,"target":9,"verbatimModuleSyntax":true},"referencedMap":[[106,1],[62,2],[63,1],[71,3],[89,4],[92,5],[93,6],[105,7],[99,8],[100,8],[91,9],[102,10],[101,11],[103,12],[88,13],[104,14],[95,15],[94,16],[65,17],[66,17],[67,17],[70,18],[68,19],[69,20],[73,21],[97,22],[98,23],[96,16],[77,24],[86,25],[82,26],[84,27]],"semanticDiagnosticsPerFile":[[102,[{"start":4607,"length":18,"code":2322,"category":1,"messageText":{"messageText":"Type '(e: MouseEvent | TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLDivElement>'.","category":1,"code":2322,"next":[{"messageText":"Types of parameters 'e' and 'event' are incompatible.","category":1,"code":2328,"next":[{"messageText":"Type 'TouchEvent<HTMLDivElement>' is not assignable to type 'MouseEvent | TouchEvent'.","category":1,"code":2322,"next":[{"messageText":"Type 'TouchEvent<HTMLDivElement>' is missing the following properties from type 'TouchEvent': which, initUIEvent, cancelBubble, composed, and 9 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type 'TouchEvent<HTMLDivElement>' is not assignable to type 'TouchEvent'."}}]}]}]}},{"start":7736,"length":3,"code":2322,"category":1,"messageText":{"messageText":"Type '{ children: Element; onTouchStart: (e: MouseEvent | TouchEvent) => void; className: string; style: { left: string; }; viewBox: string; }' is not assignable to type 'SVGProps<SVGSVGElement>'.","category":1,"code":2322,"next":[{"messageText":"Types of property 'onTouchStart' are incompatible.","category":1,"code":2326,"next":[{"messageText":"Type '(e: MouseEvent | TouchEvent) => void' is not assignable to type 'TouchEventHandler<SVGSVGElement>'.","category":1,"code":2322,"next":[{"messageText":"Types of parameters 'e' and 'event' are incompatible.","category":1,"code":2328,"next":[{"messageText":"Type 'TouchEvent<SVGSVGElement>' is not assignable to type 'MouseEvent | TouchEvent'.","category":1,"code":2322,"next":[{"messageText":"Type 'TouchEvent<SVGSVGElement>' is missing the following properties from type 'TouchEvent': which, initUIEvent, cancelBubble, composed, and 9 more.","category":1,"code":2740,"canonicalHead":{"code":2322,"messageText":"Type 'TouchEvent<SVGSVGElement>' is not assignable to type 'TouchEvent'."}}]}]}],"canonicalHead":{"code":2322,"messageText":"Type '{ children: Element; onTouchStart: (e: MouseEvent | TouchEvent) => void; className: string; style: { left: string; }; viewBox: string; }' is not assignable to type 'SVGProps<SVGSVGElement>'."}}]}]}}]]],"latestChangedDtsFile":"./types/index.d.ts","version":"5.9.3"}
@@ -0,0 +1,8 @@
1
+ import { type ColorScheme } from "@liqvid/color-scheme/react";
2
+ export declare function ColorSchemeToggle({ className, render, shortcuts, }: {
3
+ className?: string;
4
+ render: (state: {
5
+ colorScheme: ColorScheme;
6
+ }, props: React.ButtonHTMLAttributes<HTMLButtonElement>) => React.ReactNode;
7
+ shortcuts?: string | string[];
8
+ }): import("react").ReactNode;
@@ -0,0 +1,10 @@
1
+ interface FullScreenControlProps {
2
+ className?: string;
3
+ render: (state: {
4
+ isFullScreen: boolean;
5
+ }, props: React.ButtonHTMLAttributes<HTMLButtonElement>) => React.ReactNode;
6
+ shortcuts?: string | string[];
7
+ }
8
+ /** Fullscreen control */
9
+ export declare function FullScreen({ className, render, shortcuts, }: FullScreenControlProps): import("react").ReactNode;
10
+ export {};
@@ -0,0 +1,24 @@
1
+ interface MutePropsBase {
2
+ className?: string;
3
+ shortcuts?: string[] | string;
4
+ }
5
+ interface MutesPropsCustomRender {
6
+ render: (state: {
7
+ muted: boolean;
8
+ volume: number;
9
+ }, props: React.HTMLAttributes<HTMLButtonElement>) => React.ReactNode;
10
+ variants?: undefined;
11
+ }
12
+ interface MutePropsVariants {
13
+ render?: undefined;
14
+ variants: {
15
+ muted?: true;
16
+ gte?: number;
17
+ lt?: number;
18
+ props: React.ButtonHTMLAttributes<HTMLButtonElement>;
19
+ }[];
20
+ }
21
+ export type MuteProps = MutePropsBase & (MutesPropsCustomRender | MutePropsVariants);
22
+ /** Mute/unmute button */
23
+ export declare function Mute({ className, render, shortcuts }: MuteProps): import("react").ReactNode;
24
+ export {};
@@ -0,0 +1,9 @@
1
+ /** Control for playing/pausing */
2
+ export declare function PlayPause({ className, render, shortcuts, }: {
3
+ className?: string;
4
+ render: (state: {
5
+ paused: boolean;
6
+ seeking: boolean;
7
+ }, props: React.ButtonHTMLAttributes<HTMLButtonElement>) => React.ReactNode;
8
+ shortcuts?: string[];
9
+ }): import("react").ReactNode;
@@ -0,0 +1,23 @@
1
+ import { type DurationLike } from "@liqvid/duration";
2
+ import { type ThumbData } from "./ThumbnailBox";
3
+ export type { ThumbData };
4
+ export interface RelativeSeekShortcut {
5
+ delta: DurationLike;
6
+ key: string;
7
+ }
8
+ export interface PercentageSeekShortcut {
9
+ key: string;
10
+ /**
11
+ * @min 0
12
+ * @max 1
13
+ */
14
+ multiplier: number;
15
+ }
16
+ export interface ScrubberBarProps {
17
+ shortcuts?: {
18
+ relative?: RelativeSeekShortcut[];
19
+ percentage?: PercentageSeekShortcut[];
20
+ };
21
+ thumbs?: ThumbData;
22
+ }
23
+ export declare function ScrubberBar({ shortcuts, thumbs, ...props }: ScrubberBarProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,42 @@
1
+ import type { DurationLike } from "@liqvid/duration";
2
+ export interface ThumbData {
3
+ /**
4
+ * Number of columns per thumbnail sheet.
5
+ * @default 5
6
+ */
7
+ cols?: number;
8
+ /**
9
+ * Number of rows per thumbnail sheet.
10
+ * @default 5
11
+ */
12
+ rows?: number;
13
+ /**
14
+ * Width of individual thumbnails.
15
+ * @default 160
16
+ */
17
+ width?: number;
18
+ /**
19
+ * Height of individual thumbnails.
20
+ * @default 100
21
+ */
22
+ height?: number;
23
+ /**
24
+ * How many seconds between thumbnails.
25
+ * @default 4
26
+ */
27
+ frequency?: number;
28
+ /** URL pattern for thumbnails. Must include "%s". */
29
+ path: string;
30
+ /** Points of interest in the video to highlight. */
31
+ highlights?: VideoHighlight[];
32
+ }
33
+ export interface ThumbnailBoxProps extends Omit<ThumbData, "highlights"> {
34
+ progress: number;
35
+ show: boolean;
36
+ title?: string;
37
+ }
38
+ export interface VideoHighlight {
39
+ time: DurationLike;
40
+ title: string;
41
+ }
42
+ export declare function ThumbnailBox({ cols, rows, frequency, path, progress, show, title, height, width, }: ThumbnailBoxProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function TimeDisplay(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,25 @@
1
+ export interface AdjustVolumeShortcut {
2
+ /** Keyboard sequence */
3
+ seq: string;
4
+ /**
5
+ * A value of 5 will increase the volume by 5%; a value of -10 will decrease the volume by 10%.
6
+ */
7
+ delta: number;
8
+ }
9
+ export interface SetVolumeShortcut {
10
+ /** Keyboard sequence */
11
+ seq: string;
12
+ /** Value between 0 and 100 to set the volume to. */
13
+ value: number;
14
+ }
15
+ export type VolumeShortcut = AdjustVolumeShortcut | SetVolumeShortcut;
16
+ export interface VolumeSliderProps extends React.InputHTMLAttributes<HTMLInputElement> {
17
+ className?: string;
18
+ render: (state: {
19
+ muted: boolean;
20
+ volume: number;
21
+ }, props: React.InputHTMLAttributes<HTMLInputElement>) => React.ReactNode;
22
+ shortcuts?: VolumeShortcut[];
23
+ }
24
+ /** Volume control */
25
+ export declare function VolumeSlider({ className, render, shortcuts, }: VolumeSliderProps): import("react").ReactNode;
@@ -0,0 +1,4 @@
1
+ export declare const requestFullScreen: () => void;
2
+ export declare const exitFullScreen: () => void;
3
+ export declare const isFullScreen: () => boolean | undefined;
4
+ export declare const onFullScreenChange: (callback: () => void) => void;
@@ -0,0 +1,8 @@
1
+ export { ColorSchemeToggle } from "./ColorSchemeToggle";
2
+ export { FullScreen } from "./FullScreen";
3
+ export { Mute, type MuteProps } from "./MuteButton";
4
+ export { PlayPause } from "./PlayPause";
5
+ export { type PercentageSeekShortcut, type RelativeSeekShortcut, ScrubberBar, type ScrubberBarProps, } from "./ScrubberBar";
6
+ export type { ThumbData, VideoHighlight } from "./ThumbnailBox";
7
+ export { TimeDisplay } from "./TimeDisplay";
8
+ export { type VolumeShortcut, VolumeSlider, type VolumeSliderProps, } from "./VolumeSlider";
@@ -0,0 +1,5 @@
1
+ export declare const fullscreenEnabled: boolean;
2
+ export declare const requestFullScreen: () => Promise<void>;
3
+ export declare const exitFullScreen: () => Promise<void>;
4
+ export declare const isFullScreen: () => boolean | undefined;
5
+ export declare function onFullScreenChange(callback: EventListener): void;
@@ -0,0 +1,4 @@
1
+ import type { Keymap } from "@liqvid/keymap";
2
+ export declare function bind(keymap: Keymap, seqs: string | string[] | undefined, callback: () => void): void;
3
+ export declare function unbind(keymap: Keymap, seqs: string | string[] | undefined, callback: () => void): void;
4
+ export declare function convertShortcuts(keys: string | string[] | undefined): string | undefined;
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@liqvid/controls",
3
+ "version": "1.0.0-alpha.0",
4
+ "description": "Controls for Liqvid",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./dist/esm/index.js",
8
+ "types": "./dist/types/index.d.ts"
9
+ }
10
+ },
11
+ "files": [
12
+ "dist/*"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/liqvidjs/liqvid.git"
17
+ },
18
+ "author": "Yuri Sulyma <yuri@liqvidjs.org>",
19
+ "license": "MIT",
20
+ "bugs": {
21
+ "url": "https://github.com/liqvidjs/liqvid/issues"
22
+ },
23
+ "homepage": "https://github.com/liqvidjs/liqvid/tree/main/packages/controls#readme",
24
+ "dependencies": {
25
+ "classnames": "^2.5.1",
26
+ "@liqvid/player": "^1.0.0-alpha.0",
27
+ "@liqvid/utils": "^2.0.0-alpha.0",
28
+ "@liqvid/ssr": "^0.0.2",
29
+ "@liqvid/keymap": "^2.0.0-alpha.0",
30
+ "@liqvid/color-scheme": "^1.0.0-alpha.0",
31
+ "@liqvid/playback": "^2.0.0-alpha.0",
32
+ "@liqvid/duration": "^1.3.0"
33
+ },
34
+ "devDependencies": {
35
+ "@biomejs/biome": "2.4.4",
36
+ "@types/react": "^19",
37
+ "typescript": "^5.9"
38
+ },
39
+ "peerDependencies": {
40
+ "react": ">=18"
41
+ },
42
+ "type": "module",
43
+ "scripts": {
44
+ "build": "pnpm build:clean && mkdir dist && pnpm build:js && pnpm build:postclean",
45
+ "build:clean": "rm -rf dist",
46
+ "build:js": "tsc",
47
+ "build:postclean": "rm dist/tsconfig.tsbuildinfo",
48
+ "lint": "biome check --fix",
49
+ "watch": "tsc --watch"
50
+ }
51
+ }