@haklex/rich-renderer-video 0.0.80 → 0.0.81

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.
@@ -0,0 +1,286 @@
1
+ import { Download, Loader2, Maximize, Minimize, Pause, Play, Volume2, VolumeX } from "lucide-react";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { Slider } from "@base-ui/react/slider";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/styles.css.ts
6
+ var semanticClassNames = {
7
+ root: "rr-video-root",
8
+ player: "rr-video-player",
9
+ element: "rr-video-element",
10
+ indicator: "rr-video-indicator",
11
+ controls: "rr-video-controls",
12
+ button: "rr-video-btn",
13
+ progress: "rr-video-progress",
14
+ progressControl: "rr-video-progress-control",
15
+ progressTrack: "rr-video-progress-track",
16
+ progressRange: "rr-video-progress-range",
17
+ progressThumb: "rr-video-progress-thumb",
18
+ spin: "rr-video-spin",
19
+ editTrigger: "rr-video-edit-trigger",
20
+ editPreview: "rr-video-edit-preview",
21
+ editVideo: "rr-video-edit-video",
22
+ editOverlay: "rr-video-edit-overlay",
23
+ editPlaceholder: "rr-video-edit-placeholder",
24
+ editPanel: "rr-video-edit-panel",
25
+ editField: "rr-video-edit-field",
26
+ editFieldIcon: "rr-video-edit-field-icon",
27
+ editInput: "rr-video-edit-input"
28
+ };
29
+ var root = "_1x2h1ol0";
30
+ var player = "_1x2h1ol1";
31
+ var element = "_1x2h1ol2";
32
+ var controls = "_1x2h1ol5";
33
+ var button = "_1x2h1ol6";
34
+ var progress = "_1x2h1ol7";
35
+ var progressControl = "_1x2h1ol8";
36
+ var progressTrack = "_1x2h1ol9";
37
+ var progressRange = "_1x2h1ola";
38
+ var progressThumb = "_1x2h1olb";
39
+ var spin = "_1x2h1old";
40
+ var editTrigger = "_1x2h1ole";
41
+ var editPreview = "_1x2h1olf";
42
+ var editVideo = "_1x2h1olg";
43
+ var editOverlay = "_1x2h1olh";
44
+ var editPlaceholder = "_1x2h1oli";
45
+ var editPanel = "_1x2h1olj";
46
+ var editField = "_1x2h1olk";
47
+ var editFieldIcon = "_1x2h1oll";
48
+ var editInput = "_1x2h1olm";
49
+ //#endregion
50
+ //#region src/VideoRenderer.tsx
51
+ var PlayIcon = ({ size = 20 }) => /* @__PURE__ */ jsx(Play, { size });
52
+ var PauseIcon = ({ size = 20 }) => /* @__PURE__ */ jsx(Pause, { size });
53
+ var VolumeIcon = () => /* @__PURE__ */ jsx(Volume2, { size: 20 });
54
+ var VolumeMuteIcon = () => /* @__PURE__ */ jsx(VolumeX, { size: 20 });
55
+ var DownloadIcon = () => /* @__PURE__ */ jsx(Download, { size: 20 });
56
+ var LoadingIcon = () => /* @__PURE__ */ jsx(Loader2, {
57
+ className: `${spin} ${semanticClassNames.spin}`,
58
+ size: 20
59
+ });
60
+ var FullscreenIcon = () => /* @__PURE__ */ jsx(Maximize, { size: 20 });
61
+ var ExitFullscreenIcon = () => /* @__PURE__ */ jsx(Minimize, { size: 20 });
62
+ var VideoRenderer = ({ src, poster, width, height }) => {
63
+ const wrapperRef = useRef(null);
64
+ const videoRef = useRef(null);
65
+ const [playing, setPlaying] = useState(false);
66
+ const [duration, setDuration] = useState(0);
67
+ const [currentTime, setCurrentTime] = useState(0);
68
+ const [muted, setMuted] = useState(false);
69
+ const [isFullscreen, setIsFullscreen] = useState(false);
70
+ const [indicator$1, setIndicator] = useState(null);
71
+ const [draggingTime, setDraggingTime] = useState(null);
72
+ const [isDownloading, setIsDownloading] = useState(false);
73
+ const dragWasPlayingRef = useRef(false);
74
+ const indicatorTimerRef = useRef(void 0);
75
+ const indicatorKeyRef = useRef(0);
76
+ const aspectRatio = useMemo(() => {
77
+ if (width && height && width > 0 && height > 0) return `${width} / ${height}`;
78
+ return "16 / 9";
79
+ }, [height, width]);
80
+ const showIndicator = useCallback((type) => {
81
+ indicatorKeyRef.current += 1;
82
+ setIndicator({
83
+ type,
84
+ key: indicatorKeyRef.current
85
+ });
86
+ clearTimeout(indicatorTimerRef.current);
87
+ indicatorTimerRef.current = setTimeout(() => setIndicator(null), 500);
88
+ }, []);
89
+ const play = useCallback(() => {
90
+ videoRef.current?.play();
91
+ }, []);
92
+ const pause = useCallback(() => {
93
+ videoRef.current?.pause();
94
+ }, []);
95
+ const togglePlay = useCallback(() => {
96
+ const video = videoRef.current;
97
+ if (!video) return;
98
+ if (video.paused) {
99
+ play();
100
+ showIndicator("play");
101
+ } else {
102
+ pause();
103
+ showIndicator("pause");
104
+ }
105
+ }, [
106
+ pause,
107
+ play,
108
+ showIndicator
109
+ ]);
110
+ const seekTo = useCallback((time) => {
111
+ const video = videoRef.current;
112
+ if (!video) return;
113
+ const clamped = Math.min(Math.max(0, time), Number.isFinite(video.duration) ? video.duration : time);
114
+ video.currentTime = clamped;
115
+ setCurrentTime(clamped);
116
+ }, []);
117
+ const toggleMute = useCallback(() => {
118
+ const video = videoRef.current;
119
+ if (!video) return;
120
+ video.muted = !video.muted;
121
+ setMuted(video.muted);
122
+ }, []);
123
+ const toggleFullscreen = useCallback(() => {
124
+ const wrapper = wrapperRef.current;
125
+ if (!wrapper) return;
126
+ if (!document.fullscreenElement) wrapper.requestFullscreen();
127
+ else document.exitFullscreen();
128
+ }, []);
129
+ const downloadingRef = useRef(false);
130
+ const downloadVideo = useCallback(() => {
131
+ if (downloadingRef.current) return;
132
+ downloadingRef.current = true;
133
+ setIsDownloading(true);
134
+ const filename = src.split("/").pop() || "video";
135
+ const fallback = () => {
136
+ const a = document.createElement("a");
137
+ a.href = src;
138
+ a.download = filename;
139
+ a.rel = "noopener noreferrer";
140
+ a.click();
141
+ };
142
+ fetch(src).then((res) => {
143
+ if (!res.ok) throw new Error(res.statusText);
144
+ return res.blob();
145
+ }).then((blob) => {
146
+ const url = URL.createObjectURL(blob);
147
+ const a = document.createElement("a");
148
+ a.href = url;
149
+ a.download = filename;
150
+ a.click();
151
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
152
+ }).catch(() => fallback()).finally(() => {
153
+ downloadingRef.current = false;
154
+ setIsDownloading(false);
155
+ });
156
+ }, [src]);
157
+ useEffect(() => {
158
+ const video = videoRef.current;
159
+ if (!video) return;
160
+ const onLoadedMetadata = () => {
161
+ setDuration(Number.isFinite(video.duration) ? video.duration : 0);
162
+ setMuted(video.muted);
163
+ };
164
+ const onDurationChange = () => {
165
+ setDuration(Number.isFinite(video.duration) ? video.duration : 0);
166
+ };
167
+ const onTimeUpdate = () => setCurrentTime(video.currentTime);
168
+ const onPlay = () => setPlaying(true);
169
+ const onPause = () => setPlaying(false);
170
+ const onVolumeChange = () => setMuted(video.muted);
171
+ video.addEventListener("loadedmetadata", onLoadedMetadata);
172
+ video.addEventListener("durationchange", onDurationChange);
173
+ video.addEventListener("timeupdate", onTimeUpdate);
174
+ video.addEventListener("play", onPlay);
175
+ video.addEventListener("pause", onPause);
176
+ video.addEventListener("volumechange", onVolumeChange);
177
+ return () => {
178
+ video.removeEventListener("loadedmetadata", onLoadedMetadata);
179
+ video.removeEventListener("durationchange", onDurationChange);
180
+ video.removeEventListener("timeupdate", onTimeUpdate);
181
+ video.removeEventListener("play", onPlay);
182
+ video.removeEventListener("pause", onPause);
183
+ video.removeEventListener("volumechange", onVolumeChange);
184
+ };
185
+ }, []);
186
+ useEffect(() => {
187
+ const handler = () => setIsFullscreen(!!document.fullscreenElement);
188
+ document.addEventListener("fullscreenchange", handler);
189
+ return () => document.removeEventListener("fullscreenchange", handler);
190
+ }, []);
191
+ useEffect(() => {
192
+ return () => clearTimeout(indicatorTimerRef.current);
193
+ }, []);
194
+ const timelineValue = draggingTime ?? currentTime;
195
+ return /* @__PURE__ */ jsx("figure", {
196
+ className: `${root} ${semanticClassNames.root}`,
197
+ children: /* @__PURE__ */ jsxs("div", {
198
+ className: `${player} ${semanticClassNames.player}`,
199
+ ref: wrapperRef,
200
+ style: { aspectRatio },
201
+ children: [
202
+ /* @__PURE__ */ jsx("video", {
203
+ playsInline: true,
204
+ className: `${element} ${semanticClassNames.element}`,
205
+ poster,
206
+ preload: "metadata",
207
+ ref: videoRef,
208
+ src,
209
+ onClick: togglePlay,
210
+ onDoubleClick: toggleFullscreen
211
+ }),
212
+ indicator$1 && /* @__PURE__ */ jsx("span", {
213
+ "aria-hidden": true,
214
+ className: `_1x2h1ol4 ${semanticClassNames.indicator}`,
215
+ children: indicator$1.type === "play" ? /* @__PURE__ */ jsx(PlayIcon, { size: 32 }) : /* @__PURE__ */ jsx(PauseIcon, { size: 32 })
216
+ }, indicator$1.key),
217
+ /* @__PURE__ */ jsxs("div", {
218
+ className: `${controls} ${semanticClassNames.controls}`,
219
+ onClick: (e) => e.stopPropagation(),
220
+ children: [
221
+ /* @__PURE__ */ jsx("button", {
222
+ "aria-label": playing ? "Pause" : "Play",
223
+ className: `${button} ${semanticClassNames.button}`,
224
+ type: "button",
225
+ onClick: togglePlay,
226
+ children: playing ? /* @__PURE__ */ jsx(PauseIcon, {}) : /* @__PURE__ */ jsx(PlayIcon, {})
227
+ }),
228
+ /* @__PURE__ */ jsx(Slider.Root, {
229
+ className: `${progress} ${semanticClassNames.progress}`,
230
+ max: duration || 0,
231
+ min: 0,
232
+ step: .01,
233
+ value: timelineValue,
234
+ onValueChange: (value) => {
235
+ setDraggingTime(value);
236
+ },
237
+ onValueCommitted: (value) => {
238
+ seekTo(value);
239
+ setDraggingTime(null);
240
+ if (dragWasPlayingRef.current) play();
241
+ },
242
+ children: /* @__PURE__ */ jsx(Slider.Control, {
243
+ className: `${progressControl} ${semanticClassNames.progressControl}`,
244
+ onPointerDown: () => {
245
+ dragWasPlayingRef.current = playing;
246
+ pause();
247
+ setDraggingTime(currentTime);
248
+ },
249
+ children: /* @__PURE__ */ jsxs(Slider.Track, {
250
+ className: `${progressTrack} ${semanticClassNames.progressTrack}`,
251
+ children: [/* @__PURE__ */ jsx(Slider.Indicator, { className: `${progressRange} ${semanticClassNames.progressRange}` }), /* @__PURE__ */ jsx(Slider.Thumb, {
252
+ "aria-label": "Playback progress",
253
+ className: `${progressThumb} ${semanticClassNames.progressThumb}`
254
+ })]
255
+ })
256
+ })
257
+ }),
258
+ /* @__PURE__ */ jsx("button", {
259
+ "aria-label": muted ? "Unmute" : "Mute",
260
+ className: `${button} ${semanticClassNames.button}`,
261
+ type: "button",
262
+ onClick: toggleMute,
263
+ children: muted ? /* @__PURE__ */ jsx(VolumeMuteIcon, {}) : /* @__PURE__ */ jsx(VolumeIcon, {})
264
+ }),
265
+ /* @__PURE__ */ jsx("button", {
266
+ "aria-label": "Download",
267
+ className: `${button} ${semanticClassNames.button}`,
268
+ type: "button",
269
+ onClick: downloadVideo,
270
+ children: isDownloading ? /* @__PURE__ */ jsx(LoadingIcon, {}) : /* @__PURE__ */ jsx(DownloadIcon, {})
271
+ }),
272
+ /* @__PURE__ */ jsx("button", {
273
+ "aria-label": isFullscreen ? "Exit fullscreen" : "Fullscreen",
274
+ className: `${button} ${semanticClassNames.button}`,
275
+ type: "button",
276
+ onClick: toggleFullscreen,
277
+ children: isFullscreen ? /* @__PURE__ */ jsx(ExitFullscreenIcon, {}) : /* @__PURE__ */ jsx(FullscreenIcon, {})
278
+ })
279
+ ]
280
+ })
281
+ ]
282
+ })
283
+ });
284
+ };
285
+ //#endregion
286
+ export { editOverlay as a, editPreview as c, root as d, semanticClassNames as f, editInput as i, editTrigger as l, editField as n, editPanel as o, editFieldIcon as r, editPlaceholder as s, VideoRenderer as t, editVideo as u };
package/dist/index.mjs CHANGED
@@ -1,199 +1,169 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { a as editOverlay, c as editPreview, d as root, f as semanticClassNames, i as editInput, l as editTrigger, n as editField, o as editPanel, r as editFieldIcon, s as editPlaceholder, t as VideoRenderer, u as editVideo } from "./VideoRenderer-DUHsloDe.js";
2
2
  import { useRendererMode } from "@haklex/rich-editor";
3
- import { Popover, PopoverTrigger, PopoverPanel, ActionBar, ActionButton } from "@haklex/rich-editor-ui";
3
+ import { ActionBar, ActionButton, Popover, PopoverPanel, PopoverTrigger } from "@haklex/rich-editor-ui";
4
4
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
5
5
  import { $getNearestNodeFromDOMNode } from "lexical";
6
- import { Video, ImageIcon, ExternalLink, Trash2 } from "lucide-react";
7
- import { useRef, useState, useEffect, useCallback } from "react";
8
- import { V as VideoRenderer, r as root, s as semanticClassNames, e as editPreview, a as editVideo, b as editOverlay, c as editPlaceholder, d as editTrigger, f as editPanel, g as editField, h as editFieldIcon, i as editInput } from "./VideoRenderer-1dPzqoJZ.js";
9
- const UNSAFE_VIDEO_URL_RE = /^(?:javascript\s*:|vbscript\s*:|data\s*:(?!video\/))/i;
6
+ import { ExternalLink, ImageIcon, Trash2, Video } from "lucide-react";
7
+ import { useCallback, useEffect, useRef, useState } from "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ //#region src/VideoEditRenderer.tsx
10
+ var UNSAFE_VIDEO_URL_RE = /^(?:javascript\s*:|vbscript\s*:|data\s*:(?!video\/))/i;
10
11
  function VideoEditRenderer(props) {
11
- const mode = useRendererMode();
12
- if (mode !== "editor") {
13
- return /* @__PURE__ */ jsx(VideoRenderer, { ...props });
14
- }
15
- return /* @__PURE__ */ jsx(VideoEditRendererInner, { ...props });
12
+ if (useRendererMode() !== "editor") return /* @__PURE__ */ jsx(VideoRenderer, { ...props });
13
+ return /* @__PURE__ */ jsx(VideoEditRendererInner, { ...props });
16
14
  }
17
15
  function VideoEditRendererInner({ src, poster, width, height }) {
18
- const [editor] = useLexicalComposerContext();
19
- const editable = editor.isEditable();
20
- const wrapperRef = useRef(null);
21
- const srcInputRef = useRef(null);
22
- const [open, setOpen] = useState(!src);
23
- const [editSrc, setEditSrc] = useState(src);
24
- const [editPoster, setEditPoster] = useState(poster || "");
25
- useEffect(() => {
26
- setEditSrc(src);
27
- setEditPoster(poster || "");
28
- }, [src, poster]);
29
- useEffect(() => {
30
- if (!src) setOpen(true);
31
- }, [src]);
32
- useEffect(() => {
33
- if (open) {
34
- requestAnimationFrame(() => srcInputRef.current?.focus());
35
- }
36
- }, [open]);
37
- const commitChanges = useCallback(() => {
38
- const trimmedSrc = editSrc.trim();
39
- if (!trimmedSrc) return;
40
- if (UNSAFE_VIDEO_URL_RE.test(trimmedSrc)) return;
41
- if (!wrapperRef.current) return;
42
- editor.update(() => {
43
- const node = $getNearestNodeFromDOMNode(wrapperRef.current);
44
- if (!node) return;
45
- const writable = node.getWritable();
46
- writable.__src = trimmedSrc;
47
- writable.__poster = editPoster.trim() || void 0;
48
- });
49
- setOpen(false);
50
- }, [editor, editSrc, editPoster]);
51
- const handleDelete = useCallback(() => {
52
- if (!wrapperRef.current) return;
53
- editor.update(() => {
54
- const node = $getNearestNodeFromDOMNode(wrapperRef.current);
55
- if (node) node.remove();
56
- });
57
- setOpen(false);
58
- }, [editor]);
59
- const handleOpen = useCallback(() => {
60
- if (src) window.open(src, "_blank", "noopener,noreferrer");
61
- }, [src]);
62
- const handleKeyDown = useCallback(
63
- (e) => {
64
- if (e.key === "Enter") {
65
- e.preventDefault();
66
- commitChanges();
67
- } else if (e.key === "Escape") {
68
- e.preventDefault();
69
- setEditSrc(src);
70
- setEditPoster(poster || "");
71
- setOpen(false);
72
- }
73
- },
74
- [commitChanges, src, poster]
75
- );
76
- if (!editable) {
77
- return /* @__PURE__ */ jsx(VideoRenderer, { height, poster, src, width });
78
- }
79
- const aspectRatio = width && height && width > 0 && height > 0 ? `${width} / ${height}` : "16 / 9";
80
- return /* @__PURE__ */ jsxs(
81
- Popover,
82
- {
83
- open,
84
- onOpenChange: (nextOpen) => {
85
- setOpen(nextOpen);
86
- if (!nextOpen) {
87
- setEditSrc(src);
88
- setEditPoster(poster || "");
89
- }
90
- },
91
- children: [
92
- /* @__PURE__ */ jsx(
93
- PopoverTrigger,
94
- {
95
- nativeButton: false,
96
- render: /* @__PURE__ */ jsx(
97
- "div",
98
- {
99
- className: `${editTrigger} ${semanticClassNames.editTrigger}`,
100
- ref: wrapperRef
101
- }
102
- ),
103
- children: src ? /* @__PURE__ */ jsx("figure", { className: `${root} ${semanticClassNames.root}`, children: /* @__PURE__ */ jsxs(
104
- "div",
105
- {
106
- className: `${editPreview} ${semanticClassNames.editPreview}`,
107
- style: {
108
- aspectRatio,
109
- backgroundImage: poster ? `url(${poster})` : void 0
110
- },
111
- children: [
112
- /* @__PURE__ */ jsx(
113
- "video",
114
- {
115
- className: `${editVideo} ${semanticClassNames.editVideo}`,
116
- poster,
117
- preload: "metadata",
118
- src
119
- }
120
- ),
121
- /* @__PURE__ */ jsx("span", { className: `${editOverlay} ${semanticClassNames.editOverlay}`, children: /* @__PURE__ */ jsx(Video, { size: 32 }) })
122
- ]
123
- }
124
- ) }) : /* @__PURE__ */ jsxs("div", { className: `${editPlaceholder} ${semanticClassNames.editPlaceholder}`, children: [
125
- /* @__PURE__ */ jsx(Video, { size: 24 }),
126
- /* @__PURE__ */ jsx("span", { children: "Click to add video" })
127
- ] })
128
- }
129
- ),
130
- /* @__PURE__ */ jsxs(
131
- PopoverPanel,
132
- {
133
- className: `${editPanel} ${semanticClassNames.editPanel}`,
134
- side: "bottom",
135
- sideOffset: 8,
136
- children: [
137
- /* @__PURE__ */ jsxs("div", { className: `${editField} ${semanticClassNames.editField}`, children: [
138
- /* @__PURE__ */ jsx(
139
- Video,
140
- {
141
- className: `${editFieldIcon} ${semanticClassNames.editFieldIcon}`,
142
- size: 14
143
- }
144
- ),
145
- /* @__PURE__ */ jsx(
146
- "input",
147
- {
148
- className: `${editInput} ${semanticClassNames.editInput}`,
149
- placeholder: "Video URL",
150
- ref: srcInputRef,
151
- type: "url",
152
- value: editSrc,
153
- onChange: (e) => setEditSrc(e.target.value),
154
- onKeyDown: handleKeyDown
155
- }
156
- )
157
- ] }),
158
- /* @__PURE__ */ jsxs("div", { className: `${editField} ${semanticClassNames.editField}`, children: [
159
- /* @__PURE__ */ jsx(
160
- ImageIcon,
161
- {
162
- className: `${editFieldIcon} ${semanticClassNames.editFieldIcon}`,
163
- size: 14
164
- }
165
- ),
166
- /* @__PURE__ */ jsx(
167
- "input",
168
- {
169
- className: `${editInput} ${semanticClassNames.editInput}`,
170
- placeholder: "Poster URL (optional)",
171
- type: "url",
172
- value: editPoster,
173
- onBlur: commitChanges,
174
- onChange: (e) => setEditPoster(e.target.value),
175
- onKeyDown: handleKeyDown
176
- }
177
- )
178
- ] }),
179
- /* @__PURE__ */ jsxs(ActionBar, { children: [
180
- /* @__PURE__ */ jsxs(ActionButton, { onClick: handleOpen, children: [
181
- /* @__PURE__ */ jsx(ExternalLink, { size: 14 }),
182
- "Open"
183
- ] }),
184
- /* @__PURE__ */ jsxs(ActionButton, { danger: true, onClick: handleDelete, children: [
185
- /* @__PURE__ */ jsx(Trash2, { size: 14 }),
186
- "Remove"
187
- ] })
188
- ] })
189
- ]
190
- }
191
- )
192
- ]
193
- }
194
- );
16
+ const [editor] = useLexicalComposerContext();
17
+ const editable = editor.isEditable();
18
+ const wrapperRef = useRef(null);
19
+ const srcInputRef = useRef(null);
20
+ const [open, setOpen] = useState(!src);
21
+ const [editSrc, setEditSrc] = useState(src);
22
+ const [editPoster, setEditPoster] = useState(poster || "");
23
+ useEffect(() => {
24
+ setEditSrc(src);
25
+ setEditPoster(poster || "");
26
+ }, [src, poster]);
27
+ useEffect(() => {
28
+ if (!src) setOpen(true);
29
+ }, [src]);
30
+ useEffect(() => {
31
+ if (open) requestAnimationFrame(() => srcInputRef.current?.focus());
32
+ }, [open]);
33
+ const commitChanges = useCallback(() => {
34
+ const trimmedSrc = editSrc.trim();
35
+ if (!trimmedSrc) return;
36
+ if (UNSAFE_VIDEO_URL_RE.test(trimmedSrc)) return;
37
+ if (!wrapperRef.current) return;
38
+ editor.update(() => {
39
+ const node = $getNearestNodeFromDOMNode(wrapperRef.current);
40
+ if (!node) return;
41
+ const writable = node.getWritable();
42
+ writable.__src = trimmedSrc;
43
+ writable.__poster = editPoster.trim() || void 0;
44
+ });
45
+ setOpen(false);
46
+ }, [
47
+ editor,
48
+ editSrc,
49
+ editPoster
50
+ ]);
51
+ const handleDelete = useCallback(() => {
52
+ if (!wrapperRef.current) return;
53
+ editor.update(() => {
54
+ const node = $getNearestNodeFromDOMNode(wrapperRef.current);
55
+ if (node) node.remove();
56
+ });
57
+ setOpen(false);
58
+ }, [editor]);
59
+ const handleOpen = useCallback(() => {
60
+ if (src) window.open(src, "_blank", "noopener,noreferrer");
61
+ }, [src]);
62
+ const handleKeyDown = useCallback((e) => {
63
+ if (e.key === "Enter") {
64
+ e.preventDefault();
65
+ commitChanges();
66
+ } else if (e.key === "Escape") {
67
+ e.preventDefault();
68
+ setEditSrc(src);
69
+ setEditPoster(poster || "");
70
+ setOpen(false);
71
+ }
72
+ }, [
73
+ commitChanges,
74
+ src,
75
+ poster
76
+ ]);
77
+ if (!editable) return /* @__PURE__ */ jsx(VideoRenderer, {
78
+ height,
79
+ poster,
80
+ src,
81
+ width
82
+ });
83
+ const aspectRatio = width && height && width > 0 && height > 0 ? `${width} / ${height}` : "16 / 9";
84
+ return /* @__PURE__ */ jsxs(Popover, {
85
+ open,
86
+ onOpenChange: (nextOpen) => {
87
+ setOpen(nextOpen);
88
+ if (!nextOpen) {
89
+ setEditSrc(src);
90
+ setEditPoster(poster || "");
91
+ }
92
+ },
93
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
94
+ nativeButton: false,
95
+ render: /* @__PURE__ */ jsx("div", {
96
+ className: `${editTrigger} ${semanticClassNames.editTrigger}`,
97
+ ref: wrapperRef
98
+ }),
99
+ children: src ? /* @__PURE__ */ jsx("figure", {
100
+ className: `${root} ${semanticClassNames.root}`,
101
+ children: /* @__PURE__ */ jsxs("div", {
102
+ className: `${editPreview} ${semanticClassNames.editPreview}`,
103
+ style: {
104
+ aspectRatio,
105
+ backgroundImage: poster ? `url(${poster})` : void 0
106
+ },
107
+ children: [/* @__PURE__ */ jsx("video", {
108
+ className: `${editVideo} ${semanticClassNames.editVideo}`,
109
+ poster,
110
+ preload: "metadata",
111
+ src
112
+ }), /* @__PURE__ */ jsx("span", {
113
+ className: `${editOverlay} ${semanticClassNames.editOverlay}`,
114
+ children: /* @__PURE__ */ jsx(Video, { size: 32 })
115
+ })]
116
+ })
117
+ }) : /* @__PURE__ */ jsxs("div", {
118
+ className: `${editPlaceholder} ${semanticClassNames.editPlaceholder}`,
119
+ children: [/* @__PURE__ */ jsx(Video, { size: 24 }), /* @__PURE__ */ jsx("span", { children: "Click to add video" })]
120
+ })
121
+ }), /* @__PURE__ */ jsxs(PopoverPanel, {
122
+ className: `${editPanel} ${semanticClassNames.editPanel}`,
123
+ side: "bottom",
124
+ sideOffset: 8,
125
+ children: [
126
+ /* @__PURE__ */ jsxs("div", {
127
+ className: `${editField} ${semanticClassNames.editField}`,
128
+ children: [/* @__PURE__ */ jsx(Video, {
129
+ className: `${editFieldIcon} ${semanticClassNames.editFieldIcon}`,
130
+ size: 14
131
+ }), /* @__PURE__ */ jsx("input", {
132
+ className: `${editInput} ${semanticClassNames.editInput}`,
133
+ placeholder: "Video URL",
134
+ ref: srcInputRef,
135
+ type: "url",
136
+ value: editSrc,
137
+ onChange: (e) => setEditSrc(e.target.value),
138
+ onKeyDown: handleKeyDown
139
+ })]
140
+ }),
141
+ /* @__PURE__ */ jsxs("div", {
142
+ className: `${editField} ${semanticClassNames.editField}`,
143
+ children: [/* @__PURE__ */ jsx(ImageIcon, {
144
+ className: `${editFieldIcon} ${semanticClassNames.editFieldIcon}`,
145
+ size: 14
146
+ }), /* @__PURE__ */ jsx("input", {
147
+ className: `${editInput} ${semanticClassNames.editInput}`,
148
+ placeholder: "Poster URL (optional)",
149
+ type: "url",
150
+ value: editPoster,
151
+ onBlur: commitChanges,
152
+ onChange: (e) => setEditPoster(e.target.value),
153
+ onKeyDown: handleKeyDown
154
+ })]
155
+ }),
156
+ /* @__PURE__ */ jsxs(ActionBar, { children: [/* @__PURE__ */ jsxs(ActionButton, {
157
+ onClick: handleOpen,
158
+ children: [/* @__PURE__ */ jsx(ExternalLink, { size: 14 }), "Open"]
159
+ }), /* @__PURE__ */ jsxs(ActionButton, {
160
+ danger: true,
161
+ onClick: handleDelete,
162
+ children: [/* @__PURE__ */ jsx(Trash2, { size: 14 }), "Remove"]
163
+ })] })
164
+ ]
165
+ })]
166
+ });
195
167
  }
196
- export {
197
- VideoEditRenderer,
198
- VideoRenderer
199
- };
168
+ //#endregion
169
+ export { VideoEditRenderer, VideoRenderer };
@@ -1 +1,2 @@
1
- :root{--rc-text: #000;--rc-text-secondary: #27272a;--rc-text-tertiary: #71717a;--rc-text-quaternary: #a1a1aa;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f4f4f5;--rc-fill: #e8e8ec;--rc-fill-secondary: #eeeeef;--rc-fill-tertiary: #f4f4f6;--rc-fill-quaternary: #f9f9fa;--rc-border: #f4f4f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #3f3f46;--rc-code-bg: #f4f4f5;--rc-hr-border: #e4e4e7;--rc-quote-border: #2563eb;--rc-quote-bg: #eff6ff;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}:root.dark{--rc-text: #fafafa;--rc-text-secondary: #a1a1aa;--rc-text-tertiary: #71717a;--rc-text-quaternary: #52525b;--rc-bg: #09090b;--rc-bg-secondary: #18181b;--rc-bg-tertiary: #27272a;--rc-fill: #2a2a2f;--rc-fill-secondary: #222226;--rc-fill-tertiary: #1b1b1f;--rc-fill-quaternary: #131316;--rc-border: #27272a;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #e4e4e7;--rc-code-bg: #27272a;--rc-hr-border: #27272a;--rc-quote-border: #60a5fa;--rc-quote-bg: #1e3a5f;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1ju0cb20{--rc-text: #000;--rc-text-secondary: #27272a;--rc-text-tertiary: #71717a;--rc-text-quaternary: #a1a1aa;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f4f4f5;--rc-fill: #e8e8ec;--rc-fill-secondary: #eeeeef;--rc-fill-tertiary: #f4f4f6;--rc-fill-quaternary: #f9f9fa;--rc-border: #f4f4f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #3f3f46;--rc-code-bg: #f4f4f5;--rc-hr-border: #e4e4e7;--rc-quote-border: #2563eb;--rc-quote-bg: #eff6ff;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1ju0cb21{--rc-text: #000;--rc-text-secondary: #27272a;--rc-text-tertiary: #71717a;--rc-text-quaternary: #a1a1aa;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f4f4f5;--rc-fill: #e8e8ec;--rc-fill-secondary: #eeeeef;--rc-fill-tertiary: #f4f4f6;--rc-fill-quaternary: #f9f9fa;--rc-border: #f4f4f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #3f3f46;--rc-code-bg: #f4f4f5;--rc-hr-border: #e4e4e7;--rc-quote-border: #2563eb;--rc-quote-bg: #eff6ff;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.8;--rc-line-height-tight: 1.4;--rc-font-family: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1ju0cb22{--rc-text: #000;--rc-text-secondary: #27272a;--rc-text-tertiary: #71717a;--rc-text-quaternary: #a1a1aa;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f4f4f5;--rc-fill: #e8e8ec;--rc-fill-secondary: #eeeeef;--rc-fill-tertiary: #f4f4f6;--rc-fill-quaternary: #f9f9fa;--rc-border: #f4f4f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #3f3f46;--rc-code-bg: #f4f4f5;--rc-hr-border: #e4e4e7;--rc-quote-border: #a1a1aa;--rc-quote-bg: #fafafa;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: none;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 2px;--rc-space-sm: 4px;--rc-space-md: 10px;--rc-space-lg: 16px;--rc-space-xl: 20px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 14px;--rc-font-size-small: 12px;--rc-line-height: 1.5;--rc-line-height-tight: 1.3;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 3px;--rc-radius-md: 6px;--rc-radius-lg: 12px}.dark ._1ju0cb20,[data-theme=dark] ._1ju0cb20,.dark._1ju0cb20,[data-theme=dark]._1ju0cb20,.dark ._1ju0cb21,[data-theme=dark] ._1ju0cb21,.dark._1ju0cb21,[data-theme=dark]._1ju0cb21,.dark ._1ju0cb22,[data-theme=dark] ._1ju0cb22,.dark._1ju0cb22,[data-theme=dark]._1ju0cb22{--rc-text: #fafafa;--rc-text-secondary: #a1a1aa;--rc-text-tertiary: #71717a;--rc-text-quaternary: #52525b;--rc-bg: #09090b;--rc-bg-secondary: #18181b;--rc-bg-tertiary: #27272a;--rc-fill: #2a2a2f;--rc-fill-secondary: #222226;--rc-fill-tertiary: #1b1b1f;--rc-fill-quaternary: #131316;--rc-border: #27272a;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #e4e4e7;--rc-code-bg: #27272a;--rc-hr-border: #27272a;--rc-quote-border: #60a5fa;--rc-quote-bg: #1e3a5f;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4)}@keyframes _1x2h1ol3{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(1.3)}}@keyframes _1x2h1olc{0%{transform:rotate(0)}to{transform:rotate(360deg)}}._1x2h1ol0{margin:1.25rem 0}._1x2h1ol1{position:relative;width:100%;background:#000;border-radius:.75rem;overflow:hidden}._1x2h1ol2{width:100%;height:100%;display:block;object-fit:contain;background:#000;cursor:pointer}._1x2h1ol4{position:absolute;inset:0;margin:auto;width:5rem;height:5rem;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;background:#000000b3;color:#fff;pointer-events:none;animation:_1x2h1ol3 .5s ease forwards}._1x2h1ol5{position:absolute;left:2rem;right:2rem;bottom:.5rem;height:2.5rem;border-radius:999px;border:1px solid color-mix(in srgb,var(--rc-border) 20%,transparent);background:color-mix(in srgb,var(--rc-bg-secondary) 90%,transparent);backdrop-filter:blur(24px) saturate(180%);color:var(--rc-text);display:flex;align-items:center;gap:.75rem;padding:0 1rem;max-width:80vw;margin:0 auto;opacity:0;transform:translateY(4px);transition:opacity .2s ease,transform .2s ease}._1x2h1ol1:hover ._1x2h1ol5,._1x2h1ol1:focus-within ._1x2h1ol5{opacity:1;transform:translateY(0)}._1x2h1ol6{appearance:none;border:none;background:none;color:inherit;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;padding:.125rem;line-height:1;flex-shrink:0;transition:transform .15s ease}._1x2h1ol6:hover{transform:scale(1.1)}._1x2h1ol6:active{transform:scale(.93)}._1x2h1ol6:focus-visible{outline:2px solid currentColor;outline-offset:2px;border-radius:4px}._1x2h1ol6:disabled{opacity:.5;pointer-events:none}._1x2h1ol7{flex:1;height:100%}._1x2h1ol8{position:relative;display:flex;align-items:center;width:100%;height:100%;touch-action:none;user-select:none}._1x2h1ol9{position:relative;flex:1;height:.25rem;border-radius:999px;background:var(--rc-bg)}._1x2h1ola{position:absolute;height:100%;border-radius:999px;background:color-mix(in srgb,var(--rc-text-secondary) 40%,transparent)}._1x2h1olb{display:block;height:.75rem;width:3px;border-radius:1px;border:none;background:var(--rc-text-secondary)}._1x2h1old{animation:_1x2h1olc .8s linear infinite}._1x2h1ole{display:block;cursor:pointer}._1x2h1olf{position:relative;width:100%;border-radius:.75rem;overflow:hidden;background:#000;background-size:cover;background-position:center;cursor:pointer}._1x2h1olg{width:100%;height:100%;display:block;object-fit:contain;pointer-events:none}._1x2h1olh{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#00000059;color:#fff;transition:background .2s}._1x2h1ole:hover ._1x2h1olh{background:#00000080}._1x2h1oli{display:flex;align-items:center;justify-content:center;gap:8px;padding:2rem;border:2px dashed var(--rc-border);border-radius:var(--rc-radius-md);color:var(--rc-text-secondary);font-size:var(--rc-font-size-md);cursor:pointer;transition:border-color .2s,color .2s}._1x2h1oli:hover{border-color:var(--rc-accent);color:var(--rc-text)}._1x2h1olj{display:flex;flex-direction:column;gap:8px;width:340px;padding:12px;font-family:var(--rc-font-family-sans)}._1x2h1olk{display:flex;align-items:center;gap:8px;padding:6px 10px;background-color:var(--rc-bg-secondary);border-radius:6px;min-width:0}._1x2h1oll{flex-shrink:0;color:var(--rc-text-secondary)}._1x2h1olm{flex:1;appearance:none;border:none;background-color:transparent;color:inherit;font-size:var(--rc-font-size-sm);padding:0;outline:none;min-width:0}._1x2h1olm::placeholder{color:var(--rc-text-secondary)}@media(max-width:768px){._1x2h1ol5{left:.75rem;right:.75rem;opacity:1;transform:none;gap:.5rem;padding:0 .75rem}}
1
+ :root{--rc-text:#000;--rc-text-secondary:#27272a;--rc-text-tertiary:#71717a;--rc-text-quaternary:#a1a1aa;--rc-bg:#fff;--rc-bg-secondary:#fafafa;--rc-bg-tertiary:#f4f4f5;--rc-fill:#e8e8ec;--rc-fill-secondary:#eeeeef;--rc-fill-tertiary:#f4f4f6;--rc-fill-quaternary:#f9f9fa;--rc-border:#f4f4f5;--rc-accent:#2563eb;--rc-accent-light:#2563eb20;--rc-link:#2563eb;--rc-code-text:#3f3f46;--rc-code-bg:#f4f4f5;--rc-hr-border:#e4e4e7;--rc-quote-border:#2563eb;--rc-quote-bg:#eff6ff;--rc-alert-info:#006bb7;--rc-alert-warning:#c50;--rc-alert-tip:#1c0;--rc-alert-caution:#c01;--rc-alert-important:#50c;--rc-max-width:700px;--rc-shadow-top-bar:0 8px 30px #0000001f, 0 2px 8px #0000000f;--rc-shadow-modal:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--rc-shadow-menu:0 1px 4px #0000000a, 0 4px 16px #00000014;--rc-space-xs:4px;--rc-space-sm:8px;--rc-space-md:16px;--rc-space-lg:24px;--rc-space-xl:32px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:16px;--rc-font-size-small:14px;--rc-line-height:1.7;--rc-line-height-tight:1.4;--rc-font-family:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm:4px;--rc-radius-md:8px;--rc-radius-lg:12px}:root.dark{--rc-text:#fafafa;--rc-text-secondary:#a1a1aa;--rc-text-tertiary:#71717a;--rc-text-quaternary:#52525b;--rc-bg:#09090b;--rc-bg-secondary:#18181b;--rc-bg-tertiary:#27272a;--rc-fill:#2a2a2f;--rc-fill-secondary:#222226;--rc-fill-tertiary:#1b1b1f;--rc-fill-quaternary:#131316;--rc-border:#27272a;--rc-accent:#60a5fa;--rc-accent-light:#60a5fa20;--rc-link:#60a5fa;--rc-code-text:#e4e4e7;--rc-code-bg:#27272a;--rc-hr-border:#27272a;--rc-quote-border:#60a5fa;--rc-quote-bg:#1e3a5f;--rc-alert-info:#7db9e5;--rc-alert-warning:#da864a;--rc-alert-tip:#54da48;--rc-alert-caution:#e16973;--rc-alert-important:#9966e0;--rc-max-width:700px;--rc-shadow-top-bar:0 8px 30px #00000073, 0 2px 8px #0000004d;--rc-shadow-modal:0 10px 15px -3px #0006, 0 4px 6px -4px #00000059;--rc-shadow-menu:0 1px 4px #00000040, 0 4px 16px #0006;--rc-space-xs:4px;--rc-space-sm:8px;--rc-space-md:16px;--rc-space-lg:24px;--rc-space-xl:32px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:16px;--rc-font-size-small:14px;--rc-line-height:1.7;--rc-line-height-tight:1.4;--rc-font-family:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm:4px;--rc-radius-md:8px;--rc-radius-lg:12px}._1ju0cb20{--rc-text:#000;--rc-text-secondary:#27272a;--rc-text-tertiary:#71717a;--rc-text-quaternary:#a1a1aa;--rc-bg:#fff;--rc-bg-secondary:#fafafa;--rc-bg-tertiary:#f4f4f5;--rc-fill:#e8e8ec;--rc-fill-secondary:#eeeeef;--rc-fill-tertiary:#f4f4f6;--rc-fill-quaternary:#f9f9fa;--rc-border:#f4f4f5;--rc-accent:#2563eb;--rc-accent-light:#2563eb20;--rc-link:#2563eb;--rc-code-text:#3f3f46;--rc-code-bg:#f4f4f5;--rc-hr-border:#e4e4e7;--rc-quote-border:#2563eb;--rc-quote-bg:#eff6ff;--rc-alert-info:#006bb7;--rc-alert-warning:#c50;--rc-alert-tip:#1c0;--rc-alert-caution:#c01;--rc-alert-important:#50c;--rc-max-width:700px;--rc-shadow-top-bar:0 8px 30px #0000001f, 0 2px 8px #0000000f;--rc-shadow-modal:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--rc-shadow-menu:0 1px 4px #0000000a, 0 4px 16px #00000014;--rc-space-xs:4px;--rc-space-sm:8px;--rc-space-md:16px;--rc-space-lg:24px;--rc-space-xl:32px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:16px;--rc-font-size-small:14px;--rc-line-height:1.7;--rc-line-height-tight:1.4;--rc-font-family:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm:4px;--rc-radius-md:8px;--rc-radius-lg:12px}._1ju0cb21{--rc-text:#000;--rc-text-secondary:#27272a;--rc-text-tertiary:#71717a;--rc-text-quaternary:#a1a1aa;--rc-bg:#fff;--rc-bg-secondary:#fafafa;--rc-bg-tertiary:#f4f4f5;--rc-fill:#e8e8ec;--rc-fill-secondary:#eeeeef;--rc-fill-tertiary:#f4f4f6;--rc-fill-quaternary:#f9f9fa;--rc-border:#f4f4f5;--rc-accent:#2563eb;--rc-accent-light:#2563eb20;--rc-link:#2563eb;--rc-code-text:#3f3f46;--rc-code-bg:#f4f4f5;--rc-hr-border:#e4e4e7;--rc-quote-border:#2563eb;--rc-quote-bg:#eff6ff;--rc-alert-info:#006bb7;--rc-alert-warning:#c50;--rc-alert-tip:#1c0;--rc-alert-caution:#c01;--rc-alert-important:#50c;--rc-max-width:700px;--rc-shadow-top-bar:0 8px 30px #0000001f, 0 2px 8px #0000000f;--rc-shadow-modal:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--rc-shadow-menu:0 1px 4px #0000000a, 0 4px 16px #00000014;--rc-space-xs:4px;--rc-space-sm:8px;--rc-space-md:16px;--rc-space-lg:24px;--rc-space-xl:32px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:16px;--rc-font-size-small:14px;--rc-line-height:1.8;--rc-line-height-tight:1.4;--rc-font-family:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-radius-sm:4px;--rc-radius-md:8px;--rc-radius-lg:12px}._1ju0cb22{--rc-text:#000;--rc-text-secondary:#27272a;--rc-text-tertiary:#71717a;--rc-text-quaternary:#a1a1aa;--rc-bg:#fff;--rc-bg-secondary:#fafafa;--rc-bg-tertiary:#f4f4f5;--rc-fill:#e8e8ec;--rc-fill-secondary:#eeeeef;--rc-fill-tertiary:#f4f4f6;--rc-fill-quaternary:#f9f9fa;--rc-border:#f4f4f5;--rc-accent:#2563eb;--rc-accent-light:#2563eb20;--rc-link:#2563eb;--rc-code-text:#3f3f46;--rc-code-bg:#f4f4f5;--rc-hr-border:#e4e4e7;--rc-quote-border:#a1a1aa;--rc-quote-bg:#fafafa;--rc-alert-info:#006bb7;--rc-alert-warning:#c50;--rc-alert-tip:#1c0;--rc-alert-caution:#c01;--rc-alert-important:#50c;--rc-max-width:none;--rc-shadow-top-bar:0 8px 30px #0000001f, 0 2px 8px #0000000f;--rc-shadow-modal:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--rc-shadow-menu:0 1px 4px #0000000a, 0 4px 16px #00000014;--rc-space-xs:2px;--rc-space-sm:4px;--rc-space-md:10px;--rc-space-lg:16px;--rc-space-xl:20px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:14px;--rc-font-size-small:12px;--rc-line-height:1.5;--rc-line-height-tight:1.3;--rc-font-family:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm:3px;--rc-radius-md:6px;--rc-radius-lg:12px}.dark ._1ju0cb20,[data-theme=dark] ._1ju0cb20,.dark._1ju0cb20,[data-theme=dark]._1ju0cb20,.dark ._1ju0cb21,[data-theme=dark] ._1ju0cb21,.dark._1ju0cb21,[data-theme=dark]._1ju0cb21,.dark ._1ju0cb22,[data-theme=dark] ._1ju0cb22,.dark._1ju0cb22,[data-theme=dark]._1ju0cb22{--rc-text:#fafafa;--rc-text-secondary:#a1a1aa;--rc-text-tertiary:#71717a;--rc-text-quaternary:#52525b;--rc-bg:#09090b;--rc-bg-secondary:#18181b;--rc-bg-tertiary:#27272a;--rc-fill:#2a2a2f;--rc-fill-secondary:#222226;--rc-fill-tertiary:#1b1b1f;--rc-fill-quaternary:#131316;--rc-border:#27272a;--rc-accent:#60a5fa;--rc-accent-light:#60a5fa20;--rc-link:#60a5fa;--rc-code-text:#e4e4e7;--rc-code-bg:#27272a;--rc-hr-border:#27272a;--rc-quote-border:#60a5fa;--rc-quote-bg:#1e3a5f;--rc-alert-info:#7db9e5;--rc-alert-warning:#da864a;--rc-alert-tip:#54da48;--rc-alert-caution:#e16973;--rc-alert-important:#9966e0;--rc-shadow-top-bar:0 8px 30px #00000073, 0 2px 8px #0000004d;--rc-shadow-modal:0 10px 15px -3px #0006, 0 4px 6px -4px #00000059;--rc-shadow-menu:0 1px 4px #00000040, 0 4px 16px #0006}@keyframes _1x2h1ol3{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(1.3)}}@keyframes _1x2h1olc{0%{transform:rotate(0)}to{transform:rotate(360deg)}}._1x2h1ol0{margin:1.25rem 0}._1x2h1ol1{background:#000;border-radius:.75rem;width:100%;position:relative;overflow:hidden}._1x2h1ol2{object-fit:contain;cursor:pointer;background:#000;width:100%;height:100%;display:block}._1x2h1ol4{color:#fff;pointer-events:none;background:#000000b3;border-radius:999px;justify-content:center;align-items:center;width:5rem;height:5rem;margin:auto;animation:.5s forwards _1x2h1ol3;display:inline-flex;position:absolute;top:0;bottom:0;left:0;right:0}._1x2h1ol5{border:1px solid color-mix(in srgb, var(--rc-border) 20%, transparent);background:color-mix(in srgb, var(--rc-bg-secondary) 90%, transparent);-webkit-backdrop-filter:blur(24px)saturate(180%);backdrop-filter:blur(24px)saturate(180%);height:2.5rem;color:var(--rc-text);opacity:0;border-radius:999px;align-items:center;gap:.75rem;max-width:80vw;margin:0 auto;padding:0 1rem;transition:opacity .2s,transform .2s;display:flex;position:absolute;bottom:.5rem;left:2rem;right:2rem;transform:translateY(4px)}._1x2h1ol1:hover ._1x2h1ol5,._1x2h1ol1:focus-within ._1x2h1ol5{opacity:1;transform:translateY(0)}._1x2h1ol6{-webkit-appearance:none;appearance:none;color:inherit;cursor:pointer;background:0 0;border:none;flex-shrink:0;justify-content:center;align-items:center;padding:.125rem;line-height:1;transition:transform .15s;display:inline-flex}._1x2h1ol6:hover{transform:scale(1.1)}._1x2h1ol6:active{transform:scale(.93)}._1x2h1ol6:focus-visible{outline-offset:2px;border-radius:4px;outline:2px solid}._1x2h1ol6:disabled{opacity:.5;pointer-events:none}._1x2h1ol7{flex:1;height:100%}._1x2h1ol8{touch-action:none;-webkit-user-select:none;user-select:none;align-items:center;width:100%;height:100%;display:flex;position:relative}._1x2h1ol9{background:var(--rc-bg);border-radius:999px;flex:1;height:.25rem;position:relative}._1x2h1ola{background:color-mix(in srgb, var(--rc-text-secondary) 40%, transparent);border-radius:999px;height:100%;position:absolute}._1x2h1olb{background:var(--rc-text-secondary);border:none;border-radius:1px;width:3px;height:.75rem;display:block}._1x2h1old{animation:.8s linear infinite _1x2h1olc}._1x2h1ole{cursor:pointer;display:block}._1x2h1olf{cursor:pointer;background:#000 50%/cover;border-radius:.75rem;width:100%;position:relative;overflow:hidden}._1x2h1olg{object-fit:contain;pointer-events:none;width:100%;height:100%;display:block}._1x2h1olh{color:#fff;background:#00000059;justify-content:center;align-items:center;transition:background .2s;display:flex;position:absolute;top:0;bottom:0;left:0;right:0}._1x2h1ole:hover ._1x2h1olh{background:#00000080}._1x2h1oli{border:2px dashed var(--rc-border);border-radius:var(--rc-radius-md);color:var(--rc-text-secondary);font-size:var(--rc-font-size-md);cursor:pointer;justify-content:center;align-items:center;gap:8px;padding:2rem;transition:border-color .2s,color .2s;display:flex}._1x2h1oli:hover{border-color:var(--rc-accent);color:var(--rc-text)}._1x2h1olj{width:340px;font-family:var(--rc-font-family-sans);flex-direction:column;gap:8px;padding:12px;display:flex}._1x2h1olk{background-color:var(--rc-bg-secondary);border-radius:6px;align-items:center;gap:8px;min-width:0;padding:6px 10px;display:flex}._1x2h1oll{color:var(--rc-text-secondary);flex-shrink:0}._1x2h1olm{-webkit-appearance:none;appearance:none;color:inherit;font-size:var(--rc-font-size-sm);background-color:#0000;border:none;outline:none;flex:1;min-width:0;padding:0}._1x2h1olm::placeholder{color:var(--rc-text-secondary)}@media (max-width:768px){._1x2h1ol5{opacity:1;gap:.5rem;padding:0 .75rem;left:.75rem;right:.75rem;transform:none}}
2
+ /*$vite$:1*/
package/dist/static.mjs CHANGED
@@ -1,4 +1,2 @@
1
- import { V } from "./VideoRenderer-1dPzqoJZ.js";
2
- export {
3
- V as VideoRenderer
4
- };
1
+ import { t as VideoRenderer } from "./VideoRenderer-DUHsloDe.js";
2
+ export { VideoRenderer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haklex/rich-renderer-video",
3
- "version": "0.0.80",
3
+ "version": "0.0.81",
4
4
  "description": "Video player renderer",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,9 +27,9 @@
27
27
  "dependencies": {
28
28
  "@base-ui/react": "^1.2.0",
29
29
  "lucide-react": "^0.577.0",
30
- "@haklex/rich-editor": "0.0.80",
31
- "@haklex/rich-editor-ui": "0.0.80",
32
- "@haklex/rich-style-token": "0.0.80"
30
+ "@haklex/rich-editor": "0.0.81",
31
+ "@haklex/rich-editor-ui": "0.0.81",
32
+ "@haklex/rich-style-token": "0.0.81"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@lexical/react": "^0.41.0",
@@ -1,321 +0,0 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import { Slider } from "@base-ui/react/slider";
3
- import { Play, Pause, VolumeX, Volume2, Loader2, Download, Minimize, Maximize } from "lucide-react";
4
- import { useRef, useState, useMemo, useCallback, useEffect } from "react";
5
- var semanticClassNames = { root: "rr-video-root", player: "rr-video-player", element: "rr-video-element", indicator: "rr-video-indicator", controls: "rr-video-controls", button: "rr-video-btn", progress: "rr-video-progress", progressControl: "rr-video-progress-control", progressTrack: "rr-video-progress-track", progressRange: "rr-video-progress-range", progressThumb: "rr-video-progress-thumb", spin: "rr-video-spin", editTrigger: "rr-video-edit-trigger", editPreview: "rr-video-edit-preview", editVideo: "rr-video-edit-video", editOverlay: "rr-video-edit-overlay", editPlaceholder: "rr-video-edit-placeholder", editPanel: "rr-video-edit-panel", editField: "rr-video-edit-field", editFieldIcon: "rr-video-edit-field-icon", editInput: "rr-video-edit-input" };
6
- var root = "_1x2h1ol0";
7
- var player = "_1x2h1ol1";
8
- var element = "_1x2h1ol2";
9
- var indicator = "_1x2h1ol4";
10
- var controls = "_1x2h1ol5";
11
- var button = "_1x2h1ol6";
12
- var progress = "_1x2h1ol7";
13
- var progressControl = "_1x2h1ol8";
14
- var progressTrack = "_1x2h1ol9";
15
- var progressRange = "_1x2h1ola";
16
- var progressThumb = "_1x2h1olb";
17
- var spin = "_1x2h1old";
18
- var editTrigger = "_1x2h1ole";
19
- var editPreview = "_1x2h1olf";
20
- var editVideo = "_1x2h1olg";
21
- var editOverlay = "_1x2h1olh";
22
- var editPlaceholder = "_1x2h1oli";
23
- var editPanel = "_1x2h1olj";
24
- var editField = "_1x2h1olk";
25
- var editFieldIcon = "_1x2h1oll";
26
- var editInput = "_1x2h1olm";
27
- const PlayIcon = ({ size = 20 }) => /* @__PURE__ */ jsx(Play, { size });
28
- const PauseIcon = ({ size = 20 }) => /* @__PURE__ */ jsx(Pause, { size });
29
- const VolumeIcon = () => /* @__PURE__ */ jsx(Volume2, { size: 20 });
30
- const VolumeMuteIcon = () => /* @__PURE__ */ jsx(VolumeX, { size: 20 });
31
- const DownloadIcon = () => /* @__PURE__ */ jsx(Download, { size: 20 });
32
- const LoadingIcon = () => /* @__PURE__ */ jsx(Loader2, { className: `${spin} ${semanticClassNames.spin}`, size: 20 });
33
- const FullscreenIcon = () => /* @__PURE__ */ jsx(Maximize, { size: 20 });
34
- const ExitFullscreenIcon = () => /* @__PURE__ */ jsx(Minimize, { size: 20 });
35
- const VideoRenderer = ({
36
- src,
37
- poster,
38
- width,
39
- height
40
- }) => {
41
- const wrapperRef = useRef(null);
42
- const videoRef = useRef(null);
43
- const [playing, setPlaying] = useState(false);
44
- const [duration, setDuration] = useState(0);
45
- const [currentTime, setCurrentTime] = useState(0);
46
- const [muted, setMuted] = useState(false);
47
- const [isFullscreen, setIsFullscreen] = useState(false);
48
- const [indicator$1, setIndicator] = useState(null);
49
- const [draggingTime, setDraggingTime] = useState(null);
50
- const [isDownloading, setIsDownloading] = useState(false);
51
- const dragWasPlayingRef = useRef(false);
52
- const indicatorTimerRef = useRef(void 0);
53
- const indicatorKeyRef = useRef(0);
54
- const aspectRatio = useMemo(() => {
55
- if (width && height && width > 0 && height > 0) {
56
- return `${width} / ${height}`;
57
- }
58
- return "16 / 9";
59
- }, [height, width]);
60
- const showIndicator = useCallback((type) => {
61
- indicatorKeyRef.current += 1;
62
- setIndicator({ type, key: indicatorKeyRef.current });
63
- clearTimeout(indicatorTimerRef.current);
64
- indicatorTimerRef.current = setTimeout(() => setIndicator(null), 500);
65
- }, []);
66
- const play = useCallback(() => {
67
- void videoRef.current?.play();
68
- }, []);
69
- const pause = useCallback(() => {
70
- videoRef.current?.pause();
71
- }, []);
72
- const togglePlay = useCallback(() => {
73
- const video = videoRef.current;
74
- if (!video) return;
75
- if (video.paused) {
76
- play();
77
- showIndicator("play");
78
- } else {
79
- pause();
80
- showIndicator("pause");
81
- }
82
- }, [pause, play, showIndicator]);
83
- const seekTo = useCallback((time) => {
84
- const video = videoRef.current;
85
- if (!video) return;
86
- const clamped = Math.min(
87
- Math.max(0, time),
88
- Number.isFinite(video.duration) ? video.duration : time
89
- );
90
- video.currentTime = clamped;
91
- setCurrentTime(clamped);
92
- }, []);
93
- const toggleMute = useCallback(() => {
94
- const video = videoRef.current;
95
- if (!video) return;
96
- video.muted = !video.muted;
97
- setMuted(video.muted);
98
- }, []);
99
- const toggleFullscreen = useCallback(() => {
100
- const wrapper = wrapperRef.current;
101
- if (!wrapper) return;
102
- if (!document.fullscreenElement) {
103
- void wrapper.requestFullscreen();
104
- } else {
105
- void document.exitFullscreen();
106
- }
107
- }, []);
108
- const downloadingRef = useRef(false);
109
- const downloadVideo = useCallback(() => {
110
- if (downloadingRef.current) return;
111
- downloadingRef.current = true;
112
- setIsDownloading(true);
113
- const filename = src.split("/").pop() || "video";
114
- const fallback = () => {
115
- const a = document.createElement("a");
116
- a.href = src;
117
- a.download = filename;
118
- a.rel = "noopener noreferrer";
119
- a.click();
120
- };
121
- fetch(src).then((res) => {
122
- if (!res.ok) throw new Error(res.statusText);
123
- return res.blob();
124
- }).then((blob) => {
125
- const url = URL.createObjectURL(blob);
126
- const a = document.createElement("a");
127
- a.href = url;
128
- a.download = filename;
129
- a.click();
130
- setTimeout(() => URL.revokeObjectURL(url), 1e3);
131
- }).catch(() => fallback()).finally(() => {
132
- downloadingRef.current = false;
133
- setIsDownloading(false);
134
- });
135
- }, [src]);
136
- useEffect(() => {
137
- const video = videoRef.current;
138
- if (!video) return;
139
- const onLoadedMetadata = () => {
140
- setDuration(Number.isFinite(video.duration) ? video.duration : 0);
141
- setMuted(video.muted);
142
- };
143
- const onDurationChange = () => {
144
- setDuration(Number.isFinite(video.duration) ? video.duration : 0);
145
- };
146
- const onTimeUpdate = () => setCurrentTime(video.currentTime);
147
- const onPlay = () => setPlaying(true);
148
- const onPause = () => setPlaying(false);
149
- const onVolumeChange = () => setMuted(video.muted);
150
- video.addEventListener("loadedmetadata", onLoadedMetadata);
151
- video.addEventListener("durationchange", onDurationChange);
152
- video.addEventListener("timeupdate", onTimeUpdate);
153
- video.addEventListener("play", onPlay);
154
- video.addEventListener("pause", onPause);
155
- video.addEventListener("volumechange", onVolumeChange);
156
- return () => {
157
- video.removeEventListener("loadedmetadata", onLoadedMetadata);
158
- video.removeEventListener("durationchange", onDurationChange);
159
- video.removeEventListener("timeupdate", onTimeUpdate);
160
- video.removeEventListener("play", onPlay);
161
- video.removeEventListener("pause", onPause);
162
- video.removeEventListener("volumechange", onVolumeChange);
163
- };
164
- }, []);
165
- useEffect(() => {
166
- const handler = () => setIsFullscreen(!!document.fullscreenElement);
167
- document.addEventListener("fullscreenchange", handler);
168
- return () => document.removeEventListener("fullscreenchange", handler);
169
- }, []);
170
- useEffect(() => {
171
- return () => clearTimeout(indicatorTimerRef.current);
172
- }, []);
173
- const timelineValue = draggingTime ?? currentTime;
174
- return /* @__PURE__ */ jsx("figure", { className: `${root} ${semanticClassNames.root}`, children: /* @__PURE__ */ jsxs(
175
- "div",
176
- {
177
- className: `${player} ${semanticClassNames.player}`,
178
- ref: wrapperRef,
179
- style: { aspectRatio },
180
- children: [
181
- /* @__PURE__ */ jsx(
182
- "video",
183
- {
184
- playsInline: true,
185
- className: `${element} ${semanticClassNames.element}`,
186
- poster,
187
- preload: "metadata",
188
- ref: videoRef,
189
- src,
190
- onClick: togglePlay,
191
- onDoubleClick: toggleFullscreen
192
- }
193
- ),
194
- indicator$1 && /* @__PURE__ */ jsx(
195
- "span",
196
- {
197
- "aria-hidden": true,
198
- className: `${indicator} ${semanticClassNames.indicator}`,
199
- children: indicator$1.type === "play" ? /* @__PURE__ */ jsx(PlayIcon, { size: 32 }) : /* @__PURE__ */ jsx(PauseIcon, { size: 32 })
200
- },
201
- indicator$1.key
202
- ),
203
- /* @__PURE__ */ jsxs(
204
- "div",
205
- {
206
- className: `${controls} ${semanticClassNames.controls}`,
207
- onClick: (e) => e.stopPropagation(),
208
- children: [
209
- /* @__PURE__ */ jsx(
210
- "button",
211
- {
212
- "aria-label": playing ? "Pause" : "Play",
213
- className: `${button} ${semanticClassNames.button}`,
214
- type: "button",
215
- onClick: togglePlay,
216
- children: playing ? /* @__PURE__ */ jsx(PauseIcon, {}) : /* @__PURE__ */ jsx(PlayIcon, {})
217
- }
218
- ),
219
- /* @__PURE__ */ jsx(
220
- Slider.Root,
221
- {
222
- className: `${progress} ${semanticClassNames.progress}`,
223
- max: duration || 0,
224
- min: 0,
225
- step: 0.01,
226
- value: timelineValue,
227
- onValueChange: (value) => {
228
- setDraggingTime(value);
229
- },
230
- onValueCommitted: (value) => {
231
- seekTo(value);
232
- setDraggingTime(null);
233
- if (dragWasPlayingRef.current) {
234
- play();
235
- }
236
- },
237
- children: /* @__PURE__ */ jsx(
238
- Slider.Control,
239
- {
240
- className: `${progressControl} ${semanticClassNames.progressControl}`,
241
- onPointerDown: () => {
242
- dragWasPlayingRef.current = playing;
243
- pause();
244
- setDraggingTime(currentTime);
245
- },
246
- children: /* @__PURE__ */ jsxs(
247
- Slider.Track,
248
- {
249
- className: `${progressTrack} ${semanticClassNames.progressTrack}`,
250
- children: [
251
- /* @__PURE__ */ jsx(
252
- Slider.Indicator,
253
- {
254
- className: `${progressRange} ${semanticClassNames.progressRange}`
255
- }
256
- ),
257
- /* @__PURE__ */ jsx(
258
- Slider.Thumb,
259
- {
260
- "aria-label": "Playback progress",
261
- className: `${progressThumb} ${semanticClassNames.progressThumb}`
262
- }
263
- )
264
- ]
265
- }
266
- )
267
- }
268
- )
269
- }
270
- ),
271
- /* @__PURE__ */ jsx(
272
- "button",
273
- {
274
- "aria-label": muted ? "Unmute" : "Mute",
275
- className: `${button} ${semanticClassNames.button}`,
276
- type: "button",
277
- onClick: toggleMute,
278
- children: muted ? /* @__PURE__ */ jsx(VolumeMuteIcon, {}) : /* @__PURE__ */ jsx(VolumeIcon, {})
279
- }
280
- ),
281
- /* @__PURE__ */ jsx(
282
- "button",
283
- {
284
- "aria-label": "Download",
285
- className: `${button} ${semanticClassNames.button}`,
286
- type: "button",
287
- onClick: downloadVideo,
288
- children: isDownloading ? /* @__PURE__ */ jsx(LoadingIcon, {}) : /* @__PURE__ */ jsx(DownloadIcon, {})
289
- }
290
- ),
291
- /* @__PURE__ */ jsx(
292
- "button",
293
- {
294
- "aria-label": isFullscreen ? "Exit fullscreen" : "Fullscreen",
295
- className: `${button} ${semanticClassNames.button}`,
296
- type: "button",
297
- onClick: toggleFullscreen,
298
- children: isFullscreen ? /* @__PURE__ */ jsx(ExitFullscreenIcon, {}) : /* @__PURE__ */ jsx(FullscreenIcon, {})
299
- }
300
- )
301
- ]
302
- }
303
- )
304
- ]
305
- }
306
- ) });
307
- };
308
- export {
309
- VideoRenderer as V,
310
- editVideo as a,
311
- editOverlay as b,
312
- editPlaceholder as c,
313
- editTrigger as d,
314
- editPreview as e,
315
- editPanel as f,
316
- editField as g,
317
- editFieldIcon as h,
318
- editInput as i,
319
- root as r,
320
- semanticClassNames as s
321
- };