@sesionario/blocks 1.33.1 → 1.34.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/README.md +1 -1
- package/dist/index.cjs +233 -1539
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -247
- package/dist/index.d.ts +2 -247
- package/dist/index.js +41 -1337
- package/dist/index.js.map +1 -1
- package/dist/livo.cjs +220 -0
- package/dist/livo.cjs.map +1 -0
- package/dist/livo.d.cts +34 -0
- package/dist/livo.d.ts +34 -0
- package/dist/livo.js +215 -0
- package/dist/livo.js.map +1 -0
- package/package.json +14 -4
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import * as
|
|
2
|
+
import * as React2 from 'react';
|
|
3
3
|
import { createContext, forwardRef, useContext, useState, useRef, useId, useEffect, useCallback, useLayoutEffect, useMemo } from 'react';
|
|
4
|
-
import { ChevronsUpDown, CheckIcon, Smile, SearchIcon, Search, Clock, Flag, Hash, Lightbulb, Trophy, Plane, Apple, Leaf, Hand, Loader2, AlertTriangle, Inbox, X, ChevronLeft, ChevronRight, Monitor, Sun, Moon, Building2, Check, Plus, Languages, BadgeCheck, Settings, User, Activity, LogOut, LogIn, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, Pencil,
|
|
4
|
+
import { ChevronsUpDown, CheckIcon, Smile, SearchIcon, Search, Clock, Flag, Hash, Lightbulb, Trophy, Plane, Apple, Leaf, Hand, Loader2, AlertTriangle, Inbox, X, ChevronLeft, ChevronRight, Monitor, Sun, Moon, Building2, Check, Plus, Languages, BadgeCheck, Settings, User, Activity, LogOut, LogIn, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, Pencil, ArrowDownIcon, MessageCircleDashedIcon, MessageCircle, XIcon, ChevronUp, ChevronDown, ChevronRightIcon, CircleIcon } from 'lucide-react';
|
|
5
5
|
import { clsx } from 'clsx';
|
|
6
6
|
import { twMerge } from 'tailwind-merge';
|
|
7
7
|
import { cva } from 'class-variance-authority';
|
|
@@ -2991,7 +2991,7 @@ function Slider({
|
|
|
2991
2991
|
max = 100,
|
|
2992
2992
|
...props
|
|
2993
2993
|
}) {
|
|
2994
|
-
const _values =
|
|
2994
|
+
const _values = React2.useMemo(
|
|
2995
2995
|
() => Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max],
|
|
2996
2996
|
[value, defaultValue, min, max]
|
|
2997
2997
|
);
|
|
@@ -4014,22 +4014,22 @@ function EmojiPicker({
|
|
|
4014
4014
|
recentLimit = 24,
|
|
4015
4015
|
storageKey = "blocks-emoji"
|
|
4016
4016
|
}) {
|
|
4017
|
-
const strings =
|
|
4017
|
+
const strings = React2.useMemo(
|
|
4018
4018
|
() => ({ ...DEFAULT_LABELS[locale], ...labels }),
|
|
4019
4019
|
[locale, labels]
|
|
4020
4020
|
);
|
|
4021
|
-
const titles =
|
|
4021
|
+
const titles = React2.useMemo(
|
|
4022
4022
|
() => ({ ...DEFAULT_TITLES[locale], ...labels?.categoryTitles }),
|
|
4023
4023
|
[locale, labels]
|
|
4024
4024
|
);
|
|
4025
|
-
const [query, setQuery] =
|
|
4026
|
-
const [skinTone, setSkinTone] =
|
|
4025
|
+
const [query, setQuery] = React2.useState("");
|
|
4026
|
+
const [skinTone, setSkinTone] = React2.useState(() => {
|
|
4027
4027
|
const raw = readStored(storageKey, "tone");
|
|
4028
4028
|
const parsed = raw != null ? Number.parseInt(raw, 10) : 0;
|
|
4029
4029
|
return parsed >= 0 && parsed <= 5 ? parsed : 0;
|
|
4030
4030
|
});
|
|
4031
|
-
const [toneOpen, setToneOpen] =
|
|
4032
|
-
const [recents, setRecents] =
|
|
4031
|
+
const [toneOpen, setToneOpen] = React2.useState(false);
|
|
4032
|
+
const [recents, setRecents] = React2.useState(() => {
|
|
4033
4033
|
const raw = readStored(storageKey, "recent");
|
|
4034
4034
|
if (!raw) return [];
|
|
4035
4035
|
try {
|
|
@@ -4039,13 +4039,13 @@ function EmojiPicker({
|
|
|
4039
4039
|
return [];
|
|
4040
4040
|
}
|
|
4041
4041
|
});
|
|
4042
|
-
const [focusIndex, setFocusIndex] =
|
|
4043
|
-
const scrollRef =
|
|
4044
|
-
const sectionRefs =
|
|
4042
|
+
const [focusIndex, setFocusIndex] = React2.useState(-1);
|
|
4043
|
+
const scrollRef = React2.useRef(null);
|
|
4044
|
+
const sectionRefs = React2.useRef(
|
|
4045
4045
|
/* @__PURE__ */ new Map()
|
|
4046
4046
|
);
|
|
4047
4047
|
const normalizedQuery = normalizeEmojiQuery(query);
|
|
4048
|
-
const sections =
|
|
4048
|
+
const sections = React2.useMemo(() => {
|
|
4049
4049
|
if (normalizedQuery) return [];
|
|
4050
4050
|
const result = [];
|
|
4051
4051
|
if (recents.length > 0) {
|
|
@@ -4064,7 +4064,7 @@ function EmojiPicker({
|
|
|
4064
4064
|
}
|
|
4065
4065
|
return result;
|
|
4066
4066
|
}, [normalizedQuery, recents, titles]);
|
|
4067
|
-
const searchResults =
|
|
4067
|
+
const searchResults = React2.useMemo(() => {
|
|
4068
4068
|
if (!normalizedQuery) return [];
|
|
4069
4069
|
const out = [];
|
|
4070
4070
|
for (const category of EMOJI_CATEGORIES) {
|
|
@@ -4074,21 +4074,21 @@ function EmojiPicker({
|
|
|
4074
4074
|
}
|
|
4075
4075
|
return out;
|
|
4076
4076
|
}, [normalizedQuery]);
|
|
4077
|
-
const flatList =
|
|
4077
|
+
const flatList = React2.useMemo(() => {
|
|
4078
4078
|
if (normalizedQuery) return searchResults;
|
|
4079
4079
|
return sections.flatMap((s) => s.emojis);
|
|
4080
4080
|
}, [normalizedQuery, searchResults, sections]);
|
|
4081
|
-
|
|
4081
|
+
React2.useEffect(() => {
|
|
4082
4082
|
setFocusIndex(-1);
|
|
4083
4083
|
}, [normalizedQuery]);
|
|
4084
|
-
|
|
4084
|
+
React2.useEffect(() => {
|
|
4085
4085
|
if (focusIndex < 0) return;
|
|
4086
4086
|
const el = scrollRef.current?.querySelector(
|
|
4087
4087
|
`[data-emoji-index="${focusIndex}"]`
|
|
4088
4088
|
);
|
|
4089
4089
|
el?.focus();
|
|
4090
4090
|
}, [focusIndex]);
|
|
4091
|
-
const commitRecent =
|
|
4091
|
+
const commitRecent = React2.useCallback(
|
|
4092
4092
|
(glyph) => {
|
|
4093
4093
|
setRecents((prev) => {
|
|
4094
4094
|
const next = [glyph, ...prev.filter((e) => e !== glyph)].slice(
|
|
@@ -4101,14 +4101,14 @@ function EmojiPicker({
|
|
|
4101
4101
|
},
|
|
4102
4102
|
[recentLimit, storageKey]
|
|
4103
4103
|
);
|
|
4104
|
-
const handleSelect =
|
|
4104
|
+
const handleSelect = React2.useCallback(
|
|
4105
4105
|
(glyph) => {
|
|
4106
4106
|
commitRecent(glyph);
|
|
4107
4107
|
onSelect(glyph);
|
|
4108
4108
|
},
|
|
4109
4109
|
[commitRecent, onSelect]
|
|
4110
4110
|
);
|
|
4111
|
-
const chooseTone =
|
|
4111
|
+
const chooseTone = React2.useCallback(
|
|
4112
4112
|
(tone) => {
|
|
4113
4113
|
setSkinTone(tone);
|
|
4114
4114
|
setToneOpen(false);
|
|
@@ -4414,17 +4414,17 @@ function EmojiPickerPopover({
|
|
|
4414
4414
|
locale = "en",
|
|
4415
4415
|
...pickerProps
|
|
4416
4416
|
}) {
|
|
4417
|
-
const [internalOpen, setInternalOpen] =
|
|
4417
|
+
const [internalOpen, setInternalOpen] = React2.useState(false);
|
|
4418
4418
|
const isControlled = open !== void 0;
|
|
4419
4419
|
const isOpen = isControlled ? open : internalOpen;
|
|
4420
|
-
const setOpen =
|
|
4420
|
+
const setOpen = React2.useCallback(
|
|
4421
4421
|
(next) => {
|
|
4422
4422
|
if (!isControlled) setInternalOpen(next);
|
|
4423
4423
|
onOpenChange?.(next);
|
|
4424
4424
|
},
|
|
4425
4425
|
[isControlled, onOpenChange]
|
|
4426
4426
|
);
|
|
4427
|
-
const handleSelect =
|
|
4427
|
+
const handleSelect = React2.useCallback(
|
|
4428
4428
|
(emoji) => {
|
|
4429
4429
|
onSelect(emoji);
|
|
4430
4430
|
if (!keepOpenOnSelect) setOpen(false);
|
|
@@ -4493,7 +4493,7 @@ function emitChange(element, nextValue, onChange) {
|
|
|
4493
4493
|
});
|
|
4494
4494
|
}
|
|
4495
4495
|
function useEmojiInsert(fieldRef, value, onChange) {
|
|
4496
|
-
return
|
|
4496
|
+
return React2.useCallback(
|
|
4497
4497
|
(emoji) => {
|
|
4498
4498
|
const el = fieldRef.current;
|
|
4499
4499
|
const current = value !== void 0 && value !== null ? String(value) : el?.value ?? "";
|
|
@@ -4514,7 +4514,7 @@ function useEmojiInsert(fieldRef, value, onChange) {
|
|
|
4514
4514
|
[fieldRef, value, onChange]
|
|
4515
4515
|
);
|
|
4516
4516
|
}
|
|
4517
|
-
var EmojiTextarea =
|
|
4517
|
+
var EmojiTextarea = React2.forwardRef(
|
|
4518
4518
|
function EmojiTextarea2({
|
|
4519
4519
|
className,
|
|
4520
4520
|
locale = "en",
|
|
@@ -4534,7 +4534,7 @@ var EmojiTextarea = React7.forwardRef(
|
|
|
4534
4534
|
emojiTriggerClassName,
|
|
4535
4535
|
...props
|
|
4536
4536
|
}, ref) {
|
|
4537
|
-
const fieldRef =
|
|
4537
|
+
const fieldRef = React2.useRef(null);
|
|
4538
4538
|
const handleEmojiSelect = useEmojiInsert(fieldRef, value, onChange);
|
|
4539
4539
|
const pickerDisabled = Boolean(disabled || readOnly);
|
|
4540
4540
|
return /* @__PURE__ */ jsxs("div", { className: cn(emojiFieldWrapperClassName, className), children: [
|
|
@@ -4580,7 +4580,7 @@ var EmojiTextarea = React7.forwardRef(
|
|
|
4580
4580
|
}
|
|
4581
4581
|
);
|
|
4582
4582
|
EmojiTextarea.displayName = "EmojiTextarea";
|
|
4583
|
-
var EmojiInput =
|
|
4583
|
+
var EmojiInput = React2.forwardRef(
|
|
4584
4584
|
function EmojiInput2({
|
|
4585
4585
|
className,
|
|
4586
4586
|
locale = "en",
|
|
@@ -4601,7 +4601,7 @@ var EmojiInput = React7.forwardRef(
|
|
|
4601
4601
|
emojiTriggerClassName,
|
|
4602
4602
|
...props
|
|
4603
4603
|
}, ref) {
|
|
4604
|
-
const fieldRef =
|
|
4604
|
+
const fieldRef = React2.useRef(null);
|
|
4605
4605
|
const handleEmojiSelect = useEmojiInsert(fieldRef, value, onChange);
|
|
4606
4606
|
const pickerDisabled = Boolean(disabled || readOnly);
|
|
4607
4607
|
return /* @__PURE__ */ jsxs("div", { className: cn(emojiFieldWrapperClassName, className), children: [
|
|
@@ -4648,1302 +4648,6 @@ var EmojiInput = React7.forwardRef(
|
|
|
4648
4648
|
}
|
|
4649
4649
|
);
|
|
4650
4650
|
EmojiInput.displayName = "EmojiInput";
|
|
4651
|
-
|
|
4652
|
-
// src/components/video/hls-config.ts
|
|
4653
|
-
function shouldRetryLiveEdge404(retryConfig, retryCount, _isTimeout, loaderResponse, defaultRetry) {
|
|
4654
|
-
if (loaderResponse?.code === 404) {
|
|
4655
|
-
return retryConfig != null && retryCount < retryConfig.maxNumRetry;
|
|
4656
|
-
}
|
|
4657
|
-
return defaultRetry;
|
|
4658
|
-
}
|
|
4659
|
-
var liveEdgeErrorRetry = {
|
|
4660
|
-
maxNumRetry: 8,
|
|
4661
|
-
retryDelayMs: 250,
|
|
4662
|
-
maxRetryDelayMs: 4e3,
|
|
4663
|
-
backoff: "exponential",
|
|
4664
|
-
shouldRetry: shouldRetryLiveEdge404
|
|
4665
|
-
};
|
|
4666
|
-
function createLiveHlsConfig() {
|
|
4667
|
-
return {
|
|
4668
|
-
enableWorker: true,
|
|
4669
|
-
// No custom request headers: Cache-Control/Pragma are not
|
|
4670
|
-
// CORS-safelisted and would force an OPTIONS preflight before every
|
|
4671
|
-
// manifest poll and segment fetch. Freshness is controlled by the
|
|
4672
|
-
// origin's response Cache-Control headers instead.
|
|
4673
|
-
liveSyncDurationCount: 4,
|
|
4674
|
-
liveMaxLatencyDurationCount: 12,
|
|
4675
|
-
fragLoadPolicy: {
|
|
4676
|
-
default: {
|
|
4677
|
-
maxTimeToFirstByteMs: 1e4,
|
|
4678
|
-
maxLoadTimeMs: 12e4,
|
|
4679
|
-
timeoutRetry: {
|
|
4680
|
-
maxNumRetry: 4,
|
|
4681
|
-
retryDelayMs: 0,
|
|
4682
|
-
maxRetryDelayMs: 0
|
|
4683
|
-
},
|
|
4684
|
-
errorRetry: liveEdgeErrorRetry
|
|
4685
|
-
}
|
|
4686
|
-
},
|
|
4687
|
-
playlistLoadPolicy: {
|
|
4688
|
-
default: {
|
|
4689
|
-
maxTimeToFirstByteMs: 1e4,
|
|
4690
|
-
maxLoadTimeMs: 3e4,
|
|
4691
|
-
timeoutRetry: {
|
|
4692
|
-
maxNumRetry: 2,
|
|
4693
|
-
retryDelayMs: 0,
|
|
4694
|
-
maxRetryDelayMs: 0
|
|
4695
|
-
},
|
|
4696
|
-
errorRetry: {
|
|
4697
|
-
...liveEdgeErrorRetry,
|
|
4698
|
-
maxNumRetry: 6
|
|
4699
|
-
}
|
|
4700
|
-
}
|
|
4701
|
-
}
|
|
4702
|
-
};
|
|
4703
|
-
}
|
|
4704
|
-
function createVodHlsConfig() {
|
|
4705
|
-
return {
|
|
4706
|
-
enableWorker: true
|
|
4707
|
-
};
|
|
4708
|
-
}
|
|
4709
|
-
|
|
4710
|
-
// src/components/video/hls-errors.ts
|
|
4711
|
-
var MEDIA_ERROR_NAMES = {
|
|
4712
|
-
1: "MEDIA_ERR_ABORTED",
|
|
4713
|
-
2: "MEDIA_ERR_NETWORK",
|
|
4714
|
-
3: "MEDIA_ERR_DECODE",
|
|
4715
|
-
4: "MEDIA_ERR_SRC_NOT_SUPPORTED"
|
|
4716
|
-
};
|
|
4717
|
-
var MEDIA_ERR_NETWORK = 2;
|
|
4718
|
-
var MEDIA_ERR_DECODE = 3;
|
|
4719
|
-
var MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
|
|
4720
|
-
function errorMessageFromUnknown(error) {
|
|
4721
|
-
if (error instanceof Error) return error.message;
|
|
4722
|
-
if (typeof error === "string") return error;
|
|
4723
|
-
return void 0;
|
|
4724
|
-
}
|
|
4725
|
-
function isNotFound(httpStatus, details) {
|
|
4726
|
-
return httpStatus === 404 || details === "manifestLoadError" || details === "levelLoadError" || details === "fragLoadError";
|
|
4727
|
-
}
|
|
4728
|
-
function kindForHls(input) {
|
|
4729
|
-
const httpStatus = input.response?.code;
|
|
4730
|
-
if (input.type === "networkError" && isNotFound(httpStatus, input.details)) {
|
|
4731
|
-
return input.context.mode === "vod" || !input.context.manifestParsed ? "unavailable" : "network";
|
|
4732
|
-
}
|
|
4733
|
-
if (input.type === "mediaError" || input.details === "bufferAppendError" || input.details === "bufferSeekOverHole") {
|
|
4734
|
-
return "media";
|
|
4735
|
-
}
|
|
4736
|
-
return "generic";
|
|
4737
|
-
}
|
|
4738
|
-
function kindForNative(input) {
|
|
4739
|
-
const code = input.mediaError?.code;
|
|
4740
|
-
if (code === MEDIA_ERR_DECODE) return "media";
|
|
4741
|
-
if (code === MEDIA_ERR_SRC_NOT_SUPPORTED) return "unsupported";
|
|
4742
|
-
if (code === MEDIA_ERR_NETWORK) return "unavailable";
|
|
4743
|
-
return "generic";
|
|
4744
|
-
}
|
|
4745
|
-
function describeAvResync(input) {
|
|
4746
|
-
const lag = Number.isFinite(input.lagSeconds) ? input.lagSeconds : 0;
|
|
4747
|
-
return {
|
|
4748
|
-
summary: `av-resync lag=${lag.toFixed(3)}s (non-fatal)`,
|
|
4749
|
-
kind: "resync",
|
|
4750
|
-
detail: {
|
|
4751
|
-
source: input.context.source,
|
|
4752
|
-
kind: "resync",
|
|
4753
|
-
fatal: false,
|
|
4754
|
-
reason: `presented frame lag ${lag.toFixed(3)}s`,
|
|
4755
|
-
context: input.context
|
|
4756
|
-
}
|
|
4757
|
-
};
|
|
4758
|
-
}
|
|
4759
|
-
function describePlaybackError(input) {
|
|
4760
|
-
if (input.source === "unsupported") {
|
|
4761
|
-
return {
|
|
4762
|
-
summary: "playback unsupported (hls.js unavailable, native HLS unsupported)",
|
|
4763
|
-
kind: "unsupported",
|
|
4764
|
-
detail: {
|
|
4765
|
-
source: "unsupported",
|
|
4766
|
-
kind: "unsupported",
|
|
4767
|
-
context: input.context
|
|
4768
|
-
}
|
|
4769
|
-
};
|
|
4770
|
-
}
|
|
4771
|
-
if (input.source === "native") {
|
|
4772
|
-
const code = input.mediaError?.code;
|
|
4773
|
-
const kind2 = kindForNative(input);
|
|
4774
|
-
const detail2 = {
|
|
4775
|
-
source: "native",
|
|
4776
|
-
kind: kind2,
|
|
4777
|
-
mediaErrorCode: code,
|
|
4778
|
-
mediaErrorName: code !== void 0 ? MEDIA_ERROR_NAMES[code] : void 0,
|
|
4779
|
-
errorMessage: input.mediaError?.message,
|
|
4780
|
-
context: input.context
|
|
4781
|
-
};
|
|
4782
|
-
const name = detail2.mediaErrorName ?? "MEDIA_ERR_UNKNOWN";
|
|
4783
|
-
return {
|
|
4784
|
-
summary: `native ${name}${input.mediaError?.message ? `: ${input.mediaError.message}` : ""}`,
|
|
4785
|
-
kind: kind2,
|
|
4786
|
-
detail: detail2
|
|
4787
|
-
};
|
|
4788
|
-
}
|
|
4789
|
-
const httpStatus = input.response?.code;
|
|
4790
|
-
const kind = kindForHls(input);
|
|
4791
|
-
const detail = {
|
|
4792
|
-
source: "hls",
|
|
4793
|
-
kind,
|
|
4794
|
-
type: input.type,
|
|
4795
|
-
details: input.details,
|
|
4796
|
-
fatal: input.fatal,
|
|
4797
|
-
httpStatus,
|
|
4798
|
-
url: input.url,
|
|
4799
|
-
reason: input.reason,
|
|
4800
|
-
errorMessage: errorMessageFromUnknown(input.error),
|
|
4801
|
-
context: input.context
|
|
4802
|
-
};
|
|
4803
|
-
const statusSuffix = httpStatus !== void 0 ? ` ${httpStatus}` : "";
|
|
4804
|
-
const summary = [
|
|
4805
|
-
"hls",
|
|
4806
|
-
input.type ?? "unknown",
|
|
4807
|
-
input.details ?? "unknown",
|
|
4808
|
-
input.fatal ? "(fatal)" : "(non-fatal)",
|
|
4809
|
-
statusSuffix.trim()
|
|
4810
|
-
].filter(Boolean).join(" ");
|
|
4811
|
-
return { summary, kind, detail };
|
|
4812
|
-
}
|
|
4813
|
-
|
|
4814
|
-
// src/components/video/useHlsPlayback.ts
|
|
4815
|
-
var RETRY_INTERVAL_MS = 3e3;
|
|
4816
|
-
var MAX_MEDIA_ERROR_RETRIES = 5;
|
|
4817
|
-
var DRIFT_THRESHOLD_S = 0.4;
|
|
4818
|
-
var DRIFT_CONSECUTIVE_SAMPLES = 3;
|
|
4819
|
-
var RESYNC_COOLDOWN_MS = 1e4;
|
|
4820
|
-
var LIVE_EDGE_RESYNC_THRESHOLD_S = 8;
|
|
4821
|
-
function useHlsPlayback({
|
|
4822
|
-
src,
|
|
4823
|
-
mode,
|
|
4824
|
-
active,
|
|
4825
|
-
autoPlay = false,
|
|
4826
|
-
muted = false,
|
|
4827
|
-
onError,
|
|
4828
|
-
onPlayingChange
|
|
4829
|
-
}) {
|
|
4830
|
-
const videoRef = React7.useRef(null);
|
|
4831
|
-
const hlsRef = React7.useRef(null);
|
|
4832
|
-
const [state, setState] = React7.useState("idle");
|
|
4833
|
-
const [qualities, setQualities] = React7.useState([]);
|
|
4834
|
-
const [activeLevel, setActiveLevel] = React7.useState(-1);
|
|
4835
|
-
const [autoplayState, setAutoplayState] = React7.useState("none");
|
|
4836
|
-
const [error, setError] = React7.useState(null);
|
|
4837
|
-
const [reloadKey, setReloadKey] = React7.useState(0);
|
|
4838
|
-
const onErrorRef = React7.useRef(onError);
|
|
4839
|
-
const onPlayingChangeRef = React7.useRef(onPlayingChange);
|
|
4840
|
-
React7.useEffect(() => {
|
|
4841
|
-
onErrorRef.current = onError;
|
|
4842
|
-
onPlayingChangeRef.current = onPlayingChange;
|
|
4843
|
-
}, [onError, onPlayingChange]);
|
|
4844
|
-
const retry = React7.useCallback(() => {
|
|
4845
|
-
setError(null);
|
|
4846
|
-
setState("loading");
|
|
4847
|
-
setAutoplayState("none");
|
|
4848
|
-
setReloadKey((k) => k + 1);
|
|
4849
|
-
}, []);
|
|
4850
|
-
React7.useEffect(() => {
|
|
4851
|
-
const media = videoRef.current;
|
|
4852
|
-
if (!media || !src || !active) {
|
|
4853
|
-
setState("idle");
|
|
4854
|
-
return;
|
|
4855
|
-
}
|
|
4856
|
-
let cancelled = false;
|
|
4857
|
-
let retryTimer = null;
|
|
4858
|
-
let manifestParsed = false;
|
|
4859
|
-
let mediaErrorRetries = 0;
|
|
4860
|
-
let playbackSource = "hls";
|
|
4861
|
-
const teardown = [];
|
|
4862
|
-
media.muted = muted;
|
|
4863
|
-
setState("loading");
|
|
4864
|
-
setError(null);
|
|
4865
|
-
setAutoplayState("none");
|
|
4866
|
-
setQualities([]);
|
|
4867
|
-
setActiveLevel(-1);
|
|
4868
|
-
function baseContext() {
|
|
4869
|
-
return {
|
|
4870
|
-
mode,
|
|
4871
|
-
src,
|
|
4872
|
-
manifestParsed,
|
|
4873
|
-
retryCount: mediaErrorRetries,
|
|
4874
|
-
source: playbackSource
|
|
4875
|
-
};
|
|
4876
|
-
}
|
|
4877
|
-
function clearRetryTimer() {
|
|
4878
|
-
if (retryTimer !== null) {
|
|
4879
|
-
clearTimeout(retryTimer);
|
|
4880
|
-
retryTimer = null;
|
|
4881
|
-
}
|
|
4882
|
-
}
|
|
4883
|
-
function destroyHls() {
|
|
4884
|
-
hlsRef.current?.destroy();
|
|
4885
|
-
hlsRef.current = null;
|
|
4886
|
-
}
|
|
4887
|
-
function reportFatal(input) {
|
|
4888
|
-
const described = describePlaybackError(input);
|
|
4889
|
-
onErrorRef.current?.(described);
|
|
4890
|
-
setError(described);
|
|
4891
|
-
setState("error");
|
|
4892
|
-
}
|
|
4893
|
-
function attemptAutoplay(el) {
|
|
4894
|
-
void el.play().then(
|
|
4895
|
-
() => setAutoplayState("playing"),
|
|
4896
|
-
() => {
|
|
4897
|
-
el.muted = true;
|
|
4898
|
-
void el.play().then(
|
|
4899
|
-
() => setAutoplayState("muted"),
|
|
4900
|
-
() => setAutoplayState("blocked")
|
|
4901
|
-
);
|
|
4902
|
-
}
|
|
4903
|
-
);
|
|
4904
|
-
}
|
|
4905
|
-
function scheduleRetry(retryFn) {
|
|
4906
|
-
clearRetryTimer();
|
|
4907
|
-
if (cancelled) return;
|
|
4908
|
-
setState("waiting");
|
|
4909
|
-
retryTimer = setTimeout(() => {
|
|
4910
|
-
retryTimer = null;
|
|
4911
|
-
if (!cancelled) retryFn();
|
|
4912
|
-
}, RETRY_INTERVAL_MS);
|
|
4913
|
-
}
|
|
4914
|
-
function startDriftWatchdog(el) {
|
|
4915
|
-
if (typeof el.requestVideoFrameCallback !== "function") return;
|
|
4916
|
-
let consecutiveLagSamples = 0;
|
|
4917
|
-
let lastResyncAt = 0;
|
|
4918
|
-
let handle = null;
|
|
4919
|
-
const tick = (_now, metadata) => {
|
|
4920
|
-
if (cancelled) return;
|
|
4921
|
-
handle = el.requestVideoFrameCallback(tick);
|
|
4922
|
-
if (el.paused || el.ended) {
|
|
4923
|
-
consecutiveLagSamples = 0;
|
|
4924
|
-
return;
|
|
4925
|
-
}
|
|
4926
|
-
const lag = el.currentTime - metadata.mediaTime;
|
|
4927
|
-
if (!(lag > DRIFT_THRESHOLD_S)) {
|
|
4928
|
-
consecutiveLagSamples = 0;
|
|
4929
|
-
return;
|
|
4930
|
-
}
|
|
4931
|
-
consecutiveLagSamples += 1;
|
|
4932
|
-
if (consecutiveLagSamples < DRIFT_CONSECUTIVE_SAMPLES) return;
|
|
4933
|
-
const nowMs = performance.now();
|
|
4934
|
-
if (nowMs - lastResyncAt < RESYNC_COOLDOWN_MS) return;
|
|
4935
|
-
lastResyncAt = nowMs;
|
|
4936
|
-
consecutiveLagSamples = 0;
|
|
4937
|
-
if (mode === "live" && el.seekable.length > 0) {
|
|
4938
|
-
const edge = el.seekable.end(el.seekable.length - 1);
|
|
4939
|
-
if (edge - el.currentTime > LIVE_EDGE_RESYNC_THRESHOLD_S) {
|
|
4940
|
-
el.currentTime = edge;
|
|
4941
|
-
} else {
|
|
4942
|
-
el.currentTime = el.currentTime + 1e-3;
|
|
4943
|
-
}
|
|
4944
|
-
} else {
|
|
4945
|
-
el.currentTime = el.currentTime + 1e-3;
|
|
4946
|
-
}
|
|
4947
|
-
onErrorRef.current?.(
|
|
4948
|
-
describeAvResync({
|
|
4949
|
-
lagSeconds: lag,
|
|
4950
|
-
context: baseContext()
|
|
4951
|
-
})
|
|
4952
|
-
);
|
|
4953
|
-
};
|
|
4954
|
-
handle = el.requestVideoFrameCallback(tick);
|
|
4955
|
-
teardown.push(() => {
|
|
4956
|
-
if (handle != null && typeof el.cancelVideoFrameCallback === "function") {
|
|
4957
|
-
el.cancelVideoFrameCallback(handle);
|
|
4958
|
-
}
|
|
4959
|
-
});
|
|
4960
|
-
}
|
|
4961
|
-
function onPlaying() {
|
|
4962
|
-
manifestParsed = true;
|
|
4963
|
-
mediaErrorRetries = 0;
|
|
4964
|
-
clearRetryTimer();
|
|
4965
|
-
setState("ready");
|
|
4966
|
-
onPlayingChangeRef.current?.(true);
|
|
4967
|
-
}
|
|
4968
|
-
function onPaused() {
|
|
4969
|
-
onPlayingChangeRef.current?.(false);
|
|
4970
|
-
}
|
|
4971
|
-
media.addEventListener("playing", onPlaying);
|
|
4972
|
-
media.addEventListener("pause", onPaused);
|
|
4973
|
-
media.addEventListener("ended", onPaused);
|
|
4974
|
-
teardown.push(() => {
|
|
4975
|
-
media.removeEventListener("playing", onPlaying);
|
|
4976
|
-
media.removeEventListener("pause", onPaused);
|
|
4977
|
-
media.removeEventListener("ended", onPaused);
|
|
4978
|
-
});
|
|
4979
|
-
startDriftWatchdog(media);
|
|
4980
|
-
function initNative() {
|
|
4981
|
-
playbackSource = "native";
|
|
4982
|
-
manifestParsed = false;
|
|
4983
|
-
media.removeAttribute("src");
|
|
4984
|
-
media.load();
|
|
4985
|
-
media.src = src;
|
|
4986
|
-
const onReady = () => {
|
|
4987
|
-
manifestParsed = true;
|
|
4988
|
-
clearRetryTimer();
|
|
4989
|
-
setState("ready");
|
|
4990
|
-
if (mode === "live" || autoPlay) attemptAutoplay(media);
|
|
4991
|
-
};
|
|
4992
|
-
const onErr = () => {
|
|
4993
|
-
if (manifestParsed) {
|
|
4994
|
-
reportFatal({
|
|
4995
|
-
source: "native",
|
|
4996
|
-
mediaError: media.error,
|
|
4997
|
-
context: baseContext()
|
|
4998
|
-
});
|
|
4999
|
-
return;
|
|
5000
|
-
}
|
|
5001
|
-
onErrorRef.current?.(
|
|
5002
|
-
describePlaybackError({
|
|
5003
|
-
source: "native",
|
|
5004
|
-
mediaError: media.error,
|
|
5005
|
-
context: baseContext()
|
|
5006
|
-
})
|
|
5007
|
-
);
|
|
5008
|
-
scheduleRetry(() => {
|
|
5009
|
-
media.src = src;
|
|
5010
|
-
media.load();
|
|
5011
|
-
});
|
|
5012
|
-
};
|
|
5013
|
-
media.addEventListener("loadedmetadata", onReady);
|
|
5014
|
-
media.addEventListener("error", onErr);
|
|
5015
|
-
teardown.push(() => {
|
|
5016
|
-
media.removeEventListener("loadedmetadata", onReady);
|
|
5017
|
-
media.removeEventListener("error", onErr);
|
|
5018
|
-
});
|
|
5019
|
-
}
|
|
5020
|
-
function initHlsJs(Hls) {
|
|
5021
|
-
playbackSource = "hls";
|
|
5022
|
-
destroyHls();
|
|
5023
|
-
manifestParsed = false;
|
|
5024
|
-
const config = mode === "live" ? createLiveHlsConfig() : createVodHlsConfig();
|
|
5025
|
-
const hls = new Hls(config);
|
|
5026
|
-
hlsRef.current = hls;
|
|
5027
|
-
hls.loadSource(src);
|
|
5028
|
-
hls.attachMedia(media);
|
|
5029
|
-
hls.on(Hls.Events.MANIFEST_PARSED, (_e, data) => {
|
|
5030
|
-
manifestParsed = true;
|
|
5031
|
-
clearRetryTimer();
|
|
5032
|
-
setQualities(
|
|
5033
|
-
data.levels.map((l, index) => ({ index, height: l.height }))
|
|
5034
|
-
);
|
|
5035
|
-
setState("ready");
|
|
5036
|
-
if (mode === "live" || autoPlay) attemptAutoplay(media);
|
|
5037
|
-
});
|
|
5038
|
-
hls.on(Hls.Events.LEVEL_SWITCHED, (_e, data) => {
|
|
5039
|
-
if (hls.autoLevelEnabled) setActiveLevel(-1);
|
|
5040
|
-
else setActiveLevel(data.level);
|
|
5041
|
-
});
|
|
5042
|
-
hls.on(Hls.Events.ERROR, (_e, data) => {
|
|
5043
|
-
onErrorRef.current?.(
|
|
5044
|
-
describePlaybackError({
|
|
5045
|
-
source: "hls",
|
|
5046
|
-
type: data.type,
|
|
5047
|
-
details: data.details,
|
|
5048
|
-
fatal: data.fatal,
|
|
5049
|
-
url: data.url,
|
|
5050
|
-
response: data.response,
|
|
5051
|
-
reason: data.reason,
|
|
5052
|
-
error: data.error,
|
|
5053
|
-
context: baseContext()
|
|
5054
|
-
})
|
|
5055
|
-
);
|
|
5056
|
-
if (!data.fatal) return;
|
|
5057
|
-
if (data.type === Hls.ErrorTypes.NETWORK_ERROR) {
|
|
5058
|
-
if (!manifestParsed) {
|
|
5059
|
-
scheduleRetry(() => initHlsJs(Hls));
|
|
5060
|
-
return;
|
|
5061
|
-
}
|
|
5062
|
-
hls.startLoad();
|
|
5063
|
-
return;
|
|
5064
|
-
}
|
|
5065
|
-
mediaErrorRetries += 1;
|
|
5066
|
-
if (mediaErrorRetries === 1) {
|
|
5067
|
-
hls.recoverMediaError();
|
|
5068
|
-
return;
|
|
5069
|
-
}
|
|
5070
|
-
if (mediaErrorRetries === 2) {
|
|
5071
|
-
hls.swapAudioCodec();
|
|
5072
|
-
hls.recoverMediaError();
|
|
5073
|
-
return;
|
|
5074
|
-
}
|
|
5075
|
-
if (mediaErrorRetries < MAX_MEDIA_ERROR_RETRIES) {
|
|
5076
|
-
scheduleRetry(() => initHlsJs(Hls));
|
|
5077
|
-
return;
|
|
5078
|
-
}
|
|
5079
|
-
reportFatal({
|
|
5080
|
-
source: "hls",
|
|
5081
|
-
type: data.type,
|
|
5082
|
-
details: data.details,
|
|
5083
|
-
fatal: true,
|
|
5084
|
-
url: data.url,
|
|
5085
|
-
response: data.response,
|
|
5086
|
-
reason: data.reason,
|
|
5087
|
-
error: data.error,
|
|
5088
|
-
context: baseContext()
|
|
5089
|
-
});
|
|
5090
|
-
});
|
|
5091
|
-
}
|
|
5092
|
-
async function setup() {
|
|
5093
|
-
let Hls = null;
|
|
5094
|
-
try {
|
|
5095
|
-
Hls = (await import('hls.js')).default;
|
|
5096
|
-
} catch {
|
|
5097
|
-
Hls = null;
|
|
5098
|
-
}
|
|
5099
|
-
if (cancelled) return;
|
|
5100
|
-
if (Hls && Hls.isSupported()) {
|
|
5101
|
-
initHlsJs(Hls);
|
|
5102
|
-
} else if (media.canPlayType("application/vnd.apple.mpegurl")) {
|
|
5103
|
-
initNative();
|
|
5104
|
-
} else {
|
|
5105
|
-
playbackSource = "unsupported";
|
|
5106
|
-
reportFatal({
|
|
5107
|
-
source: "unsupported",
|
|
5108
|
-
context: baseContext()
|
|
5109
|
-
});
|
|
5110
|
-
}
|
|
5111
|
-
}
|
|
5112
|
-
void setup();
|
|
5113
|
-
return () => {
|
|
5114
|
-
cancelled = true;
|
|
5115
|
-
clearRetryTimer();
|
|
5116
|
-
for (const fn of teardown) fn();
|
|
5117
|
-
destroyHls();
|
|
5118
|
-
};
|
|
5119
|
-
}, [src, mode, active, autoPlay, muted, reloadKey]);
|
|
5120
|
-
const setLevel = React7.useCallback((index) => {
|
|
5121
|
-
const hls = hlsRef.current;
|
|
5122
|
-
if (!hls) return;
|
|
5123
|
-
hls.nextLevel = index;
|
|
5124
|
-
setActiveLevel(index);
|
|
5125
|
-
}, []);
|
|
5126
|
-
return {
|
|
5127
|
-
videoRef,
|
|
5128
|
-
state,
|
|
5129
|
-
qualities,
|
|
5130
|
-
activeLevel,
|
|
5131
|
-
setLevel,
|
|
5132
|
-
autoplayState,
|
|
5133
|
-
error,
|
|
5134
|
-
retry
|
|
5135
|
-
};
|
|
5136
|
-
}
|
|
5137
|
-
var DEFAULT_LABELS2 = {
|
|
5138
|
-
play: "Play",
|
|
5139
|
-
pause: "Pause",
|
|
5140
|
-
mute: "Mute",
|
|
5141
|
-
unmute: "Unmute",
|
|
5142
|
-
enterFullscreen: "Fullscreen",
|
|
5143
|
-
exitFullscreen: "Exit fullscreen",
|
|
5144
|
-
pictureInPicture: "Picture in picture",
|
|
5145
|
-
settings: "Settings",
|
|
5146
|
-
quality: "Quality",
|
|
5147
|
-
auto: "Auto",
|
|
5148
|
-
speed: "Speed",
|
|
5149
|
-
live: "LIVE",
|
|
5150
|
-
goLive: "Go live",
|
|
5151
|
-
tapToUnmute: "Tap to unmute",
|
|
5152
|
-
loading: "Loading\u2026",
|
|
5153
|
-
waiting: "Waiting for stream\u2026",
|
|
5154
|
-
retry: "Retry",
|
|
5155
|
-
errorUnavailable: "This video isn't available yet.",
|
|
5156
|
-
errorNetwork: "We couldn't load the video. Check your connection.",
|
|
5157
|
-
errorMedia: "There was a playback problem.",
|
|
5158
|
-
errorUnsupported: "Your browser can't play this video.",
|
|
5159
|
-
errorGeneric: "We couldn't play this video."
|
|
5160
|
-
};
|
|
5161
|
-
var PLAYBACK_RATES = [0.75, 1, 1.25, 1.5, 2];
|
|
5162
|
-
var CONTROLS_HIDE_MS = 3e3;
|
|
5163
|
-
var LIVE_EDGE_THRESHOLD_S = 8;
|
|
5164
|
-
var SEEK_STEP_S = 5;
|
|
5165
|
-
function asWebkitVideo(video) {
|
|
5166
|
-
return video;
|
|
5167
|
-
}
|
|
5168
|
-
function hasWebkitVideoFullscreen(video) {
|
|
5169
|
-
const webkit = asWebkitVideo(video);
|
|
5170
|
-
return typeof webkit?.webkitEnterFullscreen === "function";
|
|
5171
|
-
}
|
|
5172
|
-
function hasStandardFullscreen() {
|
|
5173
|
-
if (typeof document === "undefined") return false;
|
|
5174
|
-
if (document.fullscreenEnabled) return true;
|
|
5175
|
-
const doc = document;
|
|
5176
|
-
return !!doc.webkitFullscreenEnabled;
|
|
5177
|
-
}
|
|
5178
|
-
function formatTime(seconds) {
|
|
5179
|
-
if (!Number.isFinite(seconds) || seconds < 0) return "0:00";
|
|
5180
|
-
const total = Math.floor(seconds);
|
|
5181
|
-
const h = Math.floor(total / 3600);
|
|
5182
|
-
const m = Math.floor(total % 3600 / 60);
|
|
5183
|
-
const s = total % 60;
|
|
5184
|
-
const mm = h > 0 ? String(m).padStart(2, "0") : String(m);
|
|
5185
|
-
const ss = String(s).padStart(2, "0");
|
|
5186
|
-
return h > 0 ? `${h}:${mm}:${ss}` : `${mm}:${ss}`;
|
|
5187
|
-
}
|
|
5188
|
-
function VideoPlayer({
|
|
5189
|
-
src,
|
|
5190
|
-
mode,
|
|
5191
|
-
poster,
|
|
5192
|
-
autoPlay = false,
|
|
5193
|
-
muted = false,
|
|
5194
|
-
stats,
|
|
5195
|
-
labels,
|
|
5196
|
-
showQualitySelector = true,
|
|
5197
|
-
paused = false,
|
|
5198
|
-
onPlayingChange,
|
|
5199
|
-
onFirstPlay,
|
|
5200
|
-
onError,
|
|
5201
|
-
fit = "aspect-video",
|
|
5202
|
-
className
|
|
5203
|
-
}) {
|
|
5204
|
-
const t = { ...DEFAULT_LABELS2, ...labels };
|
|
5205
|
-
const containerRef = React7.useRef(null);
|
|
5206
|
-
const firstPlayFiredRef = React7.useRef(false);
|
|
5207
|
-
const [userStarted, setUserStarted] = React7.useState(false);
|
|
5208
|
-
const active = autoPlay || !poster || userStarted;
|
|
5209
|
-
const autoplayActive = autoPlay || userStarted;
|
|
5210
|
-
const handlePlayingChange = React7.useCallback(
|
|
5211
|
-
(playing) => {
|
|
5212
|
-
setIsPlaying(playing);
|
|
5213
|
-
if (playing && !firstPlayFiredRef.current) {
|
|
5214
|
-
firstPlayFiredRef.current = true;
|
|
5215
|
-
onFirstPlay?.();
|
|
5216
|
-
}
|
|
5217
|
-
onPlayingChange?.(playing);
|
|
5218
|
-
},
|
|
5219
|
-
[onFirstPlay, onPlayingChange]
|
|
5220
|
-
);
|
|
5221
|
-
const {
|
|
5222
|
-
videoRef,
|
|
5223
|
-
state,
|
|
5224
|
-
qualities,
|
|
5225
|
-
activeLevel,
|
|
5226
|
-
setLevel,
|
|
5227
|
-
autoplayState,
|
|
5228
|
-
error,
|
|
5229
|
-
retry
|
|
5230
|
-
} = useHlsPlayback({
|
|
5231
|
-
src,
|
|
5232
|
-
mode,
|
|
5233
|
-
active,
|
|
5234
|
-
autoPlay: autoplayActive,
|
|
5235
|
-
muted,
|
|
5236
|
-
onError,
|
|
5237
|
-
onPlayingChange: handlePlayingChange
|
|
5238
|
-
});
|
|
5239
|
-
const [isPlaying, setIsPlaying] = React7.useState(false);
|
|
5240
|
-
const [isMuted, setIsMuted] = React7.useState(muted);
|
|
5241
|
-
const [volume, setVolume] = React7.useState(1);
|
|
5242
|
-
const [currentTime, setCurrentTime] = React7.useState(0);
|
|
5243
|
-
const [duration, setDuration] = React7.useState(0);
|
|
5244
|
-
const [buffered, setBuffered] = React7.useState(0);
|
|
5245
|
-
const [isFullscreen, setIsFullscreen] = React7.useState(false);
|
|
5246
|
-
const [pipActive, setPipActive] = React7.useState(false);
|
|
5247
|
-
const [controlsVisible, setControlsVisible] = React7.useState(true);
|
|
5248
|
-
const [playbackRate, setPlaybackRate] = React7.useState(1);
|
|
5249
|
-
const [openMenu, setOpenMenu] = React7.useState(
|
|
5250
|
-
null
|
|
5251
|
-
);
|
|
5252
|
-
const [behindLive, setBehindLive] = React7.useState(false);
|
|
5253
|
-
const [webkitFullscreenSupported, setWebkitFullscreenSupported] = React7.useState(false);
|
|
5254
|
-
const hideTimerRef = React7.useRef(null);
|
|
5255
|
-
const pipSupported = typeof document !== "undefined" && "pictureInPictureEnabled" in document && document.pictureInPictureEnabled;
|
|
5256
|
-
const fullscreenSupported = hasStandardFullscreen() || webkitFullscreenSupported;
|
|
5257
|
-
React7.useEffect(() => {
|
|
5258
|
-
const video = videoRef.current;
|
|
5259
|
-
if (!video || !active) return;
|
|
5260
|
-
const onTimeUpdate = () => {
|
|
5261
|
-
setCurrentTime(video.currentTime);
|
|
5262
|
-
if (video.buffered.length > 0) {
|
|
5263
|
-
setBuffered(video.buffered.end(video.buffered.length - 1));
|
|
5264
|
-
}
|
|
5265
|
-
if (mode === "live" && video.seekable.length > 0) {
|
|
5266
|
-
const edge = video.seekable.end(video.seekable.length - 1);
|
|
5267
|
-
setBehindLive(edge - video.currentTime > LIVE_EDGE_THRESHOLD_S);
|
|
5268
|
-
}
|
|
5269
|
-
};
|
|
5270
|
-
const onDuration = () => setDuration(video.duration);
|
|
5271
|
-
const onVolume = () => {
|
|
5272
|
-
setIsMuted(video.muted);
|
|
5273
|
-
setVolume(video.volume);
|
|
5274
|
-
};
|
|
5275
|
-
const onRate = () => setPlaybackRate(video.playbackRate);
|
|
5276
|
-
video.addEventListener("timeupdate", onTimeUpdate);
|
|
5277
|
-
video.addEventListener("durationchange", onDuration);
|
|
5278
|
-
video.addEventListener("loadedmetadata", onDuration);
|
|
5279
|
-
video.addEventListener("volumechange", onVolume);
|
|
5280
|
-
video.addEventListener("ratechange", onRate);
|
|
5281
|
-
onVolume();
|
|
5282
|
-
return () => {
|
|
5283
|
-
video.removeEventListener("timeupdate", onTimeUpdate);
|
|
5284
|
-
video.removeEventListener("durationchange", onDuration);
|
|
5285
|
-
video.removeEventListener("loadedmetadata", onDuration);
|
|
5286
|
-
video.removeEventListener("volumechange", onVolume);
|
|
5287
|
-
video.removeEventListener("ratechange", onRate);
|
|
5288
|
-
};
|
|
5289
|
-
}, [videoRef, active, mode]);
|
|
5290
|
-
React7.useEffect(() => {
|
|
5291
|
-
const video = videoRef.current;
|
|
5292
|
-
if (!paused || !video || !isPlaying) return;
|
|
5293
|
-
video.pause();
|
|
5294
|
-
}, [paused, videoRef, isPlaying]);
|
|
5295
|
-
React7.useEffect(() => {
|
|
5296
|
-
setWebkitFullscreenSupported(hasWebkitVideoFullscreen(videoRef.current));
|
|
5297
|
-
}, [videoRef, active]);
|
|
5298
|
-
React7.useEffect(() => {
|
|
5299
|
-
const onFsChange = () => setIsFullscreen(document.fullscreenElement === containerRef.current);
|
|
5300
|
-
document.addEventListener("fullscreenchange", onFsChange);
|
|
5301
|
-
return () => document.removeEventListener("fullscreenchange", onFsChange);
|
|
5302
|
-
}, []);
|
|
5303
|
-
React7.useEffect(() => {
|
|
5304
|
-
const video = asWebkitVideo(videoRef.current);
|
|
5305
|
-
if (!video) return;
|
|
5306
|
-
const onBegin = () => setIsFullscreen(true);
|
|
5307
|
-
const onEnd = () => setIsFullscreen(false);
|
|
5308
|
-
video.addEventListener("webkitbeginfullscreen", onBegin);
|
|
5309
|
-
video.addEventListener("webkitendfullscreen", onEnd);
|
|
5310
|
-
return () => {
|
|
5311
|
-
video.removeEventListener("webkitbeginfullscreen", onBegin);
|
|
5312
|
-
video.removeEventListener("webkitendfullscreen", onEnd);
|
|
5313
|
-
};
|
|
5314
|
-
}, [videoRef, active]);
|
|
5315
|
-
React7.useEffect(() => {
|
|
5316
|
-
const video = videoRef.current;
|
|
5317
|
-
if (!video) return;
|
|
5318
|
-
const onEnter = () => setPipActive(true);
|
|
5319
|
-
const onLeave = () => setPipActive(false);
|
|
5320
|
-
video.addEventListener("enterpictureinpicture", onEnter);
|
|
5321
|
-
video.addEventListener("leavepictureinpicture", onLeave);
|
|
5322
|
-
return () => {
|
|
5323
|
-
video.removeEventListener("enterpictureinpicture", onEnter);
|
|
5324
|
-
video.removeEventListener("leavepictureinpicture", onLeave);
|
|
5325
|
-
};
|
|
5326
|
-
}, [videoRef]);
|
|
5327
|
-
const scheduleHide = React7.useCallback(() => {
|
|
5328
|
-
if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
|
|
5329
|
-
hideTimerRef.current = setTimeout(() => {
|
|
5330
|
-
setControlsVisible(false);
|
|
5331
|
-
setOpenMenu(null);
|
|
5332
|
-
}, CONTROLS_HIDE_MS);
|
|
5333
|
-
}, []);
|
|
5334
|
-
const revealControls = React7.useCallback(() => {
|
|
5335
|
-
setControlsVisible(true);
|
|
5336
|
-
scheduleHide();
|
|
5337
|
-
}, [scheduleHide]);
|
|
5338
|
-
React7.useEffect(() => {
|
|
5339
|
-
return () => {
|
|
5340
|
-
if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
|
|
5341
|
-
};
|
|
5342
|
-
}, []);
|
|
5343
|
-
function togglePlay() {
|
|
5344
|
-
const video = videoRef.current;
|
|
5345
|
-
if (!video) return;
|
|
5346
|
-
if (video.paused) void video.play();
|
|
5347
|
-
else video.pause();
|
|
5348
|
-
}
|
|
5349
|
-
function toggleMute() {
|
|
5350
|
-
const video = videoRef.current;
|
|
5351
|
-
if (!video) return;
|
|
5352
|
-
video.muted = !video.muted;
|
|
5353
|
-
}
|
|
5354
|
-
function changeVolume(next) {
|
|
5355
|
-
const video = videoRef.current;
|
|
5356
|
-
if (!video) return;
|
|
5357
|
-
video.volume = next;
|
|
5358
|
-
video.muted = next === 0;
|
|
5359
|
-
}
|
|
5360
|
-
function seekTo(next) {
|
|
5361
|
-
const video = videoRef.current;
|
|
5362
|
-
if (!video) return;
|
|
5363
|
-
video.currentTime = next;
|
|
5364
|
-
setCurrentTime(next);
|
|
5365
|
-
}
|
|
5366
|
-
function seekBy(delta) {
|
|
5367
|
-
const video = videoRef.current;
|
|
5368
|
-
if (!video) return;
|
|
5369
|
-
seekTo(Math.min(Math.max(video.currentTime + delta, 0), duration || 0));
|
|
5370
|
-
}
|
|
5371
|
-
function goToLiveEdge() {
|
|
5372
|
-
const video = videoRef.current;
|
|
5373
|
-
if (!video || video.seekable.length === 0) return;
|
|
5374
|
-
video.currentTime = video.seekable.end(video.seekable.length - 1);
|
|
5375
|
-
if (video.paused) void video.play();
|
|
5376
|
-
}
|
|
5377
|
-
function toggleFullscreen() {
|
|
5378
|
-
const el = containerRef.current;
|
|
5379
|
-
const video = asWebkitVideo(videoRef.current);
|
|
5380
|
-
if (el && document.fullscreenElement === el) {
|
|
5381
|
-
void document.exitFullscreen();
|
|
5382
|
-
return;
|
|
5383
|
-
}
|
|
5384
|
-
if (video?.webkitDisplayingFullscreen && video.webkitExitFullscreen) {
|
|
5385
|
-
video.webkitExitFullscreen();
|
|
5386
|
-
return;
|
|
5387
|
-
}
|
|
5388
|
-
if (el && hasStandardFullscreen() && typeof el.requestFullscreen === "function") {
|
|
5389
|
-
void el.requestFullscreen();
|
|
5390
|
-
return;
|
|
5391
|
-
}
|
|
5392
|
-
if (video && typeof video.webkitEnterFullscreen === "function") {
|
|
5393
|
-
video.webkitEnterFullscreen();
|
|
5394
|
-
}
|
|
5395
|
-
}
|
|
5396
|
-
function togglePip() {
|
|
5397
|
-
const video = videoRef.current;
|
|
5398
|
-
if (!video) return;
|
|
5399
|
-
if (document.pictureInPictureElement === video) {
|
|
5400
|
-
void document.exitPictureInPicture();
|
|
5401
|
-
} else {
|
|
5402
|
-
void video.requestPictureInPicture?.();
|
|
5403
|
-
}
|
|
5404
|
-
}
|
|
5405
|
-
function changeRate(rate) {
|
|
5406
|
-
const video = videoRef.current;
|
|
5407
|
-
if (video) video.playbackRate = rate;
|
|
5408
|
-
setPlaybackRate(rate);
|
|
5409
|
-
setOpenMenu(null);
|
|
5410
|
-
}
|
|
5411
|
-
function chooseLevel(index) {
|
|
5412
|
-
setLevel(index);
|
|
5413
|
-
setOpenMenu(null);
|
|
5414
|
-
}
|
|
5415
|
-
function handleKeyDown(e) {
|
|
5416
|
-
if (!active) return;
|
|
5417
|
-
switch (e.key) {
|
|
5418
|
-
case " ":
|
|
5419
|
-
case "k":
|
|
5420
|
-
e.preventDefault();
|
|
5421
|
-
togglePlay();
|
|
5422
|
-
break;
|
|
5423
|
-
case "m":
|
|
5424
|
-
toggleMute();
|
|
5425
|
-
break;
|
|
5426
|
-
case "f":
|
|
5427
|
-
toggleFullscreen();
|
|
5428
|
-
break;
|
|
5429
|
-
case "ArrowLeft":
|
|
5430
|
-
if (mode === "vod") {
|
|
5431
|
-
e.preventDefault();
|
|
5432
|
-
seekBy(-SEEK_STEP_S);
|
|
5433
|
-
}
|
|
5434
|
-
break;
|
|
5435
|
-
case "ArrowRight":
|
|
5436
|
-
if (mode === "vod") {
|
|
5437
|
-
e.preventDefault();
|
|
5438
|
-
seekBy(SEEK_STEP_S);
|
|
5439
|
-
}
|
|
5440
|
-
break;
|
|
5441
|
-
case "ArrowUp":
|
|
5442
|
-
e.preventDefault();
|
|
5443
|
-
changeVolume(Math.min((videoRef.current?.volume ?? 1) + 0.1, 1));
|
|
5444
|
-
break;
|
|
5445
|
-
case "ArrowDown":
|
|
5446
|
-
e.preventDefault();
|
|
5447
|
-
changeVolume(Math.max((videoRef.current?.volume ?? 0) - 0.1, 0));
|
|
5448
|
-
break;
|
|
5449
|
-
}
|
|
5450
|
-
revealControls();
|
|
5451
|
-
}
|
|
5452
|
-
function errorMessage(err) {
|
|
5453
|
-
switch (err.kind) {
|
|
5454
|
-
case "unavailable":
|
|
5455
|
-
return t.errorUnavailable;
|
|
5456
|
-
case "network":
|
|
5457
|
-
return t.errorNetwork;
|
|
5458
|
-
case "media":
|
|
5459
|
-
return t.errorMedia;
|
|
5460
|
-
case "unsupported":
|
|
5461
|
-
return t.errorUnsupported;
|
|
5462
|
-
case "resync":
|
|
5463
|
-
return t.errorMedia;
|
|
5464
|
-
case "generic":
|
|
5465
|
-
return t.errorGeneric;
|
|
5466
|
-
default: {
|
|
5467
|
-
const _exhaustive = err.kind;
|
|
5468
|
-
return _exhaustive;
|
|
5469
|
-
}
|
|
5470
|
-
}
|
|
5471
|
-
}
|
|
5472
|
-
const VolumeIcon = isMuted || volume === 0 ? VolumeX : volume < 0.5 ? Volume1 : Volume2;
|
|
5473
|
-
const showSpinner = active && (state === "loading" || state === "waiting");
|
|
5474
|
-
const showControls = active && state === "ready" && (controlsVisible || !isPlaying);
|
|
5475
|
-
const showPoster = !active && !!poster;
|
|
5476
|
-
const currentLevelLabel = activeLevel < 0 ? t.auto : `${qualities.find((q) => q.index === activeLevel)?.height ?? ""}p`;
|
|
5477
|
-
return /* @__PURE__ */ jsxs(
|
|
5478
|
-
"div",
|
|
5479
|
-
{
|
|
5480
|
-
ref: containerRef,
|
|
5481
|
-
"data-slot": "video-player",
|
|
5482
|
-
"data-mode": mode,
|
|
5483
|
-
"data-fit": fit,
|
|
5484
|
-
className: cn(
|
|
5485
|
-
"group relative w-full overflow-hidden rounded-lg bg-black text-white select-none",
|
|
5486
|
-
fit === "contain" ? "h-full min-h-0 aspect-auto" : "aspect-video",
|
|
5487
|
-
className
|
|
5488
|
-
),
|
|
5489
|
-
onMouseMove: revealControls,
|
|
5490
|
-
onMouseLeave: () => {
|
|
5491
|
-
if (isPlaying) setControlsVisible(false);
|
|
5492
|
-
},
|
|
5493
|
-
onKeyDown: handleKeyDown,
|
|
5494
|
-
tabIndex: 0,
|
|
5495
|
-
role: "region",
|
|
5496
|
-
"aria-label": "Video player",
|
|
5497
|
-
children: [
|
|
5498
|
-
/* @__PURE__ */ jsx(
|
|
5499
|
-
"video",
|
|
5500
|
-
{
|
|
5501
|
-
ref: videoRef,
|
|
5502
|
-
playsInline: true,
|
|
5503
|
-
className: "h-full w-full bg-black object-contain",
|
|
5504
|
-
onClick: active ? togglePlay : void 0,
|
|
5505
|
-
poster
|
|
5506
|
-
}
|
|
5507
|
-
),
|
|
5508
|
-
showPoster && /* @__PURE__ */ jsx(
|
|
5509
|
-
"button",
|
|
5510
|
-
{
|
|
5511
|
-
type: "button",
|
|
5512
|
-
"aria-label": t.play,
|
|
5513
|
-
onClick: () => {
|
|
5514
|
-
setUserStarted(true);
|
|
5515
|
-
revealControls();
|
|
5516
|
-
},
|
|
5517
|
-
className: "absolute inset-0 flex items-center justify-center bg-black bg-contain bg-center bg-no-repeat",
|
|
5518
|
-
style: poster ? { backgroundImage: `url(${poster})` } : void 0,
|
|
5519
|
-
children: /* @__PURE__ */ jsx("span", { className: "flex size-16 items-center justify-center rounded-full bg-black/50 backdrop-blur-sm transition group-hover:bg-black/70", children: /* @__PURE__ */ jsx(Play, { className: "size-8 translate-x-0.5", fill: "currentColor" }) })
|
|
5520
|
-
}
|
|
5521
|
-
),
|
|
5522
|
-
showSpinner && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-3 bg-black/50 text-sm", children: [
|
|
5523
|
-
/* @__PURE__ */ jsx(Loader2, { className: "size-8 animate-spin" }),
|
|
5524
|
-
/* @__PURE__ */ jsx("span", { children: state === "loading" ? t.loading : t.waiting })
|
|
5525
|
-
] }),
|
|
5526
|
-
state === "error" && error && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-4 bg-black/70 px-6 text-center text-sm", children: [
|
|
5527
|
-
/* @__PURE__ */ jsx(AlertCircle, { className: "text-red-400 size-8" }),
|
|
5528
|
-
/* @__PURE__ */ jsx("p", { className: "text-red-200", children: errorMessage(error) }),
|
|
5529
|
-
/* @__PURE__ */ jsxs(
|
|
5530
|
-
"button",
|
|
5531
|
-
{
|
|
5532
|
-
type: "button",
|
|
5533
|
-
onClick: retry,
|
|
5534
|
-
className: "inline-flex items-center gap-2 rounded-md bg-white/15 px-3 py-1.5 text-sm hover:bg-white/25",
|
|
5535
|
-
children: [
|
|
5536
|
-
/* @__PURE__ */ jsx(RotateCw, { className: "size-4" }),
|
|
5537
|
-
t.retry
|
|
5538
|
-
]
|
|
5539
|
-
}
|
|
5540
|
-
)
|
|
5541
|
-
] }),
|
|
5542
|
-
state === "ready" && autoplayState === "blocked" && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/40", children: /* @__PURE__ */ jsx(
|
|
5543
|
-
"button",
|
|
5544
|
-
{
|
|
5545
|
-
type: "button",
|
|
5546
|
-
"aria-label": t.play,
|
|
5547
|
-
onClick: togglePlay,
|
|
5548
|
-
className: "flex size-16 items-center justify-center rounded-full bg-white/20 backdrop-blur-sm transition hover:bg-white/30",
|
|
5549
|
-
children: /* @__PURE__ */ jsx(Play, { className: "size-8 translate-x-0.5", fill: "currentColor" })
|
|
5550
|
-
}
|
|
5551
|
-
) }),
|
|
5552
|
-
state === "ready" && autoplayState === "muted" && /* @__PURE__ */ jsxs(
|
|
5553
|
-
"button",
|
|
5554
|
-
{
|
|
5555
|
-
type: "button",
|
|
5556
|
-
onClick: toggleMute,
|
|
5557
|
-
className: "absolute top-3 right-3 flex items-center gap-1.5 rounded-full bg-black/70 px-3 py-1.5 text-xs backdrop-blur-sm transition hover:bg-black/90",
|
|
5558
|
-
children: [
|
|
5559
|
-
/* @__PURE__ */ jsx(VolumeX, { className: "size-4" }),
|
|
5560
|
-
t.tapToUnmute
|
|
5561
|
-
]
|
|
5562
|
-
}
|
|
5563
|
-
),
|
|
5564
|
-
mode === "live" && active && state === "ready" && /* @__PURE__ */ jsxs("div", { className: "absolute top-3 left-3 flex items-center gap-2", children: [
|
|
5565
|
-
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 rounded bg-red-600 px-2 py-0.5 text-xs font-semibold tracking-wide", children: [
|
|
5566
|
-
/* @__PURE__ */ jsx("span", { className: "size-1.5 animate-pulse rounded-full bg-white" }),
|
|
5567
|
-
t.live
|
|
5568
|
-
] }),
|
|
5569
|
-
typeof stats?.viewers === "number" && /* @__PURE__ */ jsx("span", { className: "rounded bg-black/60 px-2 py-0.5 text-xs", children: t.formatViewers ? t.formatViewers(stats.viewers) : `\u{1F441} ${stats.viewers}` })
|
|
5570
|
-
] }),
|
|
5571
|
-
active && /* @__PURE__ */ jsxs(
|
|
5572
|
-
"div",
|
|
5573
|
-
{
|
|
5574
|
-
className: cn(
|
|
5575
|
-
"absolute inset-x-0 bottom-0 flex flex-col gap-1 bg-gradient-to-t from-black/80 to-transparent px-3 pt-8 pb-2 transition-opacity",
|
|
5576
|
-
showControls ? "opacity-100" : "pointer-events-none opacity-0"
|
|
5577
|
-
),
|
|
5578
|
-
"data-slot": "video-controls",
|
|
5579
|
-
children: [
|
|
5580
|
-
mode === "vod" && /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
|
|
5581
|
-
/* @__PURE__ */ jsx(
|
|
5582
|
-
Slider,
|
|
5583
|
-
{
|
|
5584
|
-
"aria-label": "Seek",
|
|
5585
|
-
min: 0,
|
|
5586
|
-
max: duration || 0,
|
|
5587
|
-
step: 0.1,
|
|
5588
|
-
value: [Math.min(currentTime, duration || 0)],
|
|
5589
|
-
onValueChange: (v) => seekTo(v[0] ?? 0),
|
|
5590
|
-
className: "w-full"
|
|
5591
|
-
}
|
|
5592
|
-
),
|
|
5593
|
-
duration > 0 && buffered > currentTime && /* @__PURE__ */ jsx(
|
|
5594
|
-
"span",
|
|
5595
|
-
{
|
|
5596
|
-
"aria-hidden": "true",
|
|
5597
|
-
className: "pointer-events-none absolute top-1/2 left-0 h-1.5 -translate-y-1/2 rounded-full bg-white/25",
|
|
5598
|
-
style: {
|
|
5599
|
-
width: `${Math.min(buffered / duration * 100, 100)}%`
|
|
5600
|
-
}
|
|
5601
|
-
}
|
|
5602
|
-
)
|
|
5603
|
-
] }),
|
|
5604
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5605
|
-
/* @__PURE__ */ jsx(
|
|
5606
|
-
ControlButton,
|
|
5607
|
-
{
|
|
5608
|
-
label: isPlaying ? t.pause : t.play,
|
|
5609
|
-
onClick: togglePlay,
|
|
5610
|
-
children: isPlaying ? /* @__PURE__ */ jsx(Pause, { className: "size-5", fill: "currentColor" }) : /* @__PURE__ */ jsx(Play, { className: "size-5", fill: "currentColor" })
|
|
5611
|
-
}
|
|
5612
|
-
),
|
|
5613
|
-
/* @__PURE__ */ jsxs("div", { className: "group/vol flex items-center", children: [
|
|
5614
|
-
/* @__PURE__ */ jsx(
|
|
5615
|
-
ControlButton,
|
|
5616
|
-
{
|
|
5617
|
-
label: isMuted ? t.unmute : t.mute,
|
|
5618
|
-
onClick: toggleMute,
|
|
5619
|
-
children: /* @__PURE__ */ jsx(VolumeIcon, { className: "size-5" })
|
|
5620
|
-
}
|
|
5621
|
-
),
|
|
5622
|
-
/* @__PURE__ */ jsx("div", { className: "w-0 overflow-hidden transition-all group-hover/vol:w-20 group-hover/vol:px-1", children: /* @__PURE__ */ jsx(
|
|
5623
|
-
Slider,
|
|
5624
|
-
{
|
|
5625
|
-
"aria-label": "Volume",
|
|
5626
|
-
min: 0,
|
|
5627
|
-
max: 1,
|
|
5628
|
-
step: 0.05,
|
|
5629
|
-
value: [isMuted ? 0 : volume],
|
|
5630
|
-
onValueChange: (v) => changeVolume(v[0] ?? 0)
|
|
5631
|
-
}
|
|
5632
|
-
) })
|
|
5633
|
-
] }),
|
|
5634
|
-
mode === "vod" && /* @__PURE__ */ jsxs("span", { className: "ml-1 text-xs tabular-nums", children: [
|
|
5635
|
-
formatTime(currentTime),
|
|
5636
|
-
" / ",
|
|
5637
|
-
formatTime(duration)
|
|
5638
|
-
] }),
|
|
5639
|
-
mode === "live" && behindLive && /* @__PURE__ */ jsxs(
|
|
5640
|
-
"button",
|
|
5641
|
-
{
|
|
5642
|
-
type: "button",
|
|
5643
|
-
onClick: goToLiveEdge,
|
|
5644
|
-
className: "ml-1 flex items-center gap-1 rounded bg-white/15 px-2 py-0.5 text-xs hover:bg-white/25",
|
|
5645
|
-
children: [
|
|
5646
|
-
/* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-red-500" }),
|
|
5647
|
-
t.goLive
|
|
5648
|
-
]
|
|
5649
|
-
}
|
|
5650
|
-
),
|
|
5651
|
-
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1", children: [
|
|
5652
|
-
mode === "vod" && typeof stats?.views === "number" && /* @__PURE__ */ jsx("span", { className: "mr-1 text-xs", children: t.formatViews ? t.formatViews(stats.views) : `${stats.views} views` }),
|
|
5653
|
-
mode === "vod" && /* @__PURE__ */ jsx(
|
|
5654
|
-
MenuControl,
|
|
5655
|
-
{
|
|
5656
|
-
label: t.speed,
|
|
5657
|
-
open: openMenu === "speed",
|
|
5658
|
-
onOpenChange: (o) => setOpenMenu(o ? "speed" : null),
|
|
5659
|
-
trigger: /* @__PURE__ */ jsxs("span", { className: "text-xs font-semibold", children: [
|
|
5660
|
-
playbackRate,
|
|
5661
|
-
"\xD7"
|
|
5662
|
-
] }),
|
|
5663
|
-
children: PLAYBACK_RATES.map((rate) => /* @__PURE__ */ jsxs(
|
|
5664
|
-
MenuItem,
|
|
5665
|
-
{
|
|
5666
|
-
selected: rate === playbackRate,
|
|
5667
|
-
onSelect: () => changeRate(rate),
|
|
5668
|
-
children: [
|
|
5669
|
-
rate,
|
|
5670
|
-
"\xD7"
|
|
5671
|
-
]
|
|
5672
|
-
},
|
|
5673
|
-
rate
|
|
5674
|
-
))
|
|
5675
|
-
}
|
|
5676
|
-
),
|
|
5677
|
-
showQualitySelector && qualities.length > 1 && /* @__PURE__ */ jsxs(
|
|
5678
|
-
MenuControl,
|
|
5679
|
-
{
|
|
5680
|
-
label: t.quality,
|
|
5681
|
-
open: openMenu === "quality",
|
|
5682
|
-
onOpenChange: (o) => setOpenMenu(o ? "quality" : null),
|
|
5683
|
-
trigger: /* @__PURE__ */ jsx(Settings, { className: "size-5" }),
|
|
5684
|
-
triggerBadge: currentLevelLabel,
|
|
5685
|
-
children: [
|
|
5686
|
-
/* @__PURE__ */ jsx(
|
|
5687
|
-
MenuItem,
|
|
5688
|
-
{
|
|
5689
|
-
selected: activeLevel < 0,
|
|
5690
|
-
onSelect: () => chooseLevel(-1),
|
|
5691
|
-
children: t.auto
|
|
5692
|
-
}
|
|
5693
|
-
),
|
|
5694
|
-
qualities.map((q) => /* @__PURE__ */ jsxs(
|
|
5695
|
-
MenuItem,
|
|
5696
|
-
{
|
|
5697
|
-
selected: activeLevel === q.index,
|
|
5698
|
-
onSelect: () => chooseLevel(q.index),
|
|
5699
|
-
children: [
|
|
5700
|
-
q.height,
|
|
5701
|
-
"p"
|
|
5702
|
-
]
|
|
5703
|
-
},
|
|
5704
|
-
q.index
|
|
5705
|
-
))
|
|
5706
|
-
]
|
|
5707
|
-
}
|
|
5708
|
-
),
|
|
5709
|
-
pipSupported && /* @__PURE__ */ jsx(ControlButton, { label: t.pictureInPicture, onClick: togglePip, children: /* @__PURE__ */ jsx(
|
|
5710
|
-
PictureInPicture2,
|
|
5711
|
-
{
|
|
5712
|
-
className: cn("size-5", pipActive && "text-primary")
|
|
5713
|
-
}
|
|
5714
|
-
) }),
|
|
5715
|
-
fullscreenSupported && /* @__PURE__ */ jsx(
|
|
5716
|
-
ControlButton,
|
|
5717
|
-
{
|
|
5718
|
-
label: isFullscreen ? t.exitFullscreen : t.enterFullscreen,
|
|
5719
|
-
onClick: toggleFullscreen,
|
|
5720
|
-
children: isFullscreen ? /* @__PURE__ */ jsx(Minimize, { className: "size-5" }) : /* @__PURE__ */ jsx(Maximize, { className: "size-5" })
|
|
5721
|
-
}
|
|
5722
|
-
)
|
|
5723
|
-
] })
|
|
5724
|
-
] })
|
|
5725
|
-
]
|
|
5726
|
-
}
|
|
5727
|
-
)
|
|
5728
|
-
]
|
|
5729
|
-
}
|
|
5730
|
-
);
|
|
5731
|
-
}
|
|
5732
|
-
function ControlButton({
|
|
5733
|
-
label,
|
|
5734
|
-
onClick,
|
|
5735
|
-
children
|
|
5736
|
-
}) {
|
|
5737
|
-
return /* @__PURE__ */ jsx(
|
|
5738
|
-
"button",
|
|
5739
|
-
{
|
|
5740
|
-
type: "button",
|
|
5741
|
-
"aria-label": label,
|
|
5742
|
-
title: label,
|
|
5743
|
-
onClick,
|
|
5744
|
-
className: "flex size-8 items-center justify-center rounded-md text-white/90 outline-none hover:bg-white/15 hover:text-white focus-visible:ring-2 focus-visible:ring-white/60",
|
|
5745
|
-
children
|
|
5746
|
-
}
|
|
5747
|
-
);
|
|
5748
|
-
}
|
|
5749
|
-
function MenuControl({
|
|
5750
|
-
label,
|
|
5751
|
-
open,
|
|
5752
|
-
onOpenChange,
|
|
5753
|
-
trigger,
|
|
5754
|
-
triggerBadge,
|
|
5755
|
-
children
|
|
5756
|
-
}) {
|
|
5757
|
-
return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
5758
|
-
/* @__PURE__ */ jsxs(
|
|
5759
|
-
"button",
|
|
5760
|
-
{
|
|
5761
|
-
type: "button",
|
|
5762
|
-
"aria-label": label,
|
|
5763
|
-
"aria-haspopup": "menu",
|
|
5764
|
-
"aria-expanded": open,
|
|
5765
|
-
title: label,
|
|
5766
|
-
onClick: () => onOpenChange(!open),
|
|
5767
|
-
className: "flex h-8 items-center gap-1 rounded-md px-1.5 text-white/90 outline-none hover:bg-white/15 hover:text-white focus-visible:ring-2 focus-visible:ring-white/60",
|
|
5768
|
-
children: [
|
|
5769
|
-
trigger,
|
|
5770
|
-
triggerBadge && /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium", children: triggerBadge })
|
|
5771
|
-
]
|
|
5772
|
-
}
|
|
5773
|
-
),
|
|
5774
|
-
open && /* @__PURE__ */ jsx(
|
|
5775
|
-
"div",
|
|
5776
|
-
{
|
|
5777
|
-
role: "menu",
|
|
5778
|
-
className: "absolute right-0 bottom-full z-10 mb-2 min-w-24 rounded-md bg-black/90 p-1 text-sm shadow-lg backdrop-blur-sm",
|
|
5779
|
-
children
|
|
5780
|
-
}
|
|
5781
|
-
)
|
|
5782
|
-
] });
|
|
5783
|
-
}
|
|
5784
|
-
function MenuItem({
|
|
5785
|
-
selected,
|
|
5786
|
-
onSelect,
|
|
5787
|
-
children
|
|
5788
|
-
}) {
|
|
5789
|
-
return /* @__PURE__ */ jsxs(
|
|
5790
|
-
"button",
|
|
5791
|
-
{
|
|
5792
|
-
type: "button",
|
|
5793
|
-
role: "menuitemradio",
|
|
5794
|
-
"aria-checked": selected,
|
|
5795
|
-
onClick: onSelect,
|
|
5796
|
-
className: "flex w-full items-center justify-between gap-3 rounded px-2 py-1.5 text-left text-white/90 hover:bg-white/15",
|
|
5797
|
-
children: [
|
|
5798
|
-
children,
|
|
5799
|
-
selected && /* @__PURE__ */ jsx(Check, { className: "size-4" })
|
|
5800
|
-
]
|
|
5801
|
-
}
|
|
5802
|
-
);
|
|
5803
|
-
}
|
|
5804
|
-
|
|
5805
|
-
// src/components/video/feedPlaybackCoordinator.ts
|
|
5806
|
-
var players = /* @__PURE__ */ new Map();
|
|
5807
|
-
function registerFeedPlayer(id, pause) {
|
|
5808
|
-
players.set(id, pause);
|
|
5809
|
-
return () => {
|
|
5810
|
-
if (players.get(id) === pause) {
|
|
5811
|
-
players.delete(id);
|
|
5812
|
-
}
|
|
5813
|
-
};
|
|
5814
|
-
}
|
|
5815
|
-
function claimFeedPlaying(id) {
|
|
5816
|
-
for (const [otherId, pause] of players) {
|
|
5817
|
-
if (otherId !== id) pause();
|
|
5818
|
-
}
|
|
5819
|
-
}
|
|
5820
|
-
|
|
5821
|
-
// src/components/video/useFeedExclusivePlayback.ts
|
|
5822
|
-
var VISIBILITY_THRESHOLDS = [0, 0.5, 1];
|
|
5823
|
-
var MIN_VISIBLE_RATIO = 0.5;
|
|
5824
|
-
function videoInPictureInPicture(container) {
|
|
5825
|
-
if (typeof document === "undefined" || !container) return false;
|
|
5826
|
-
const pip = document.pictureInPictureElement;
|
|
5827
|
-
if (!pip) return false;
|
|
5828
|
-
return container.contains(pip);
|
|
5829
|
-
}
|
|
5830
|
-
function useFeedExclusivePlayback(playbackId) {
|
|
5831
|
-
const containerRef = React7.useRef(null);
|
|
5832
|
-
const [forcePaused, setForcePaused] = React7.useState(false);
|
|
5833
|
-
const requestPause = React7.useCallback(() => {
|
|
5834
|
-
if (videoInPictureInPicture(containerRef.current)) return;
|
|
5835
|
-
setForcePaused(true);
|
|
5836
|
-
}, []);
|
|
5837
|
-
React7.useEffect(() => {
|
|
5838
|
-
return registerFeedPlayer(playbackId, requestPause);
|
|
5839
|
-
}, [playbackId, requestPause]);
|
|
5840
|
-
React7.useEffect(() => {
|
|
5841
|
-
const node = containerRef.current;
|
|
5842
|
-
if (!node || typeof IntersectionObserver === "undefined") return;
|
|
5843
|
-
const observer = new IntersectionObserver(
|
|
5844
|
-
(entries) => {
|
|
5845
|
-
const entry = entries[0];
|
|
5846
|
-
if (!entry) return;
|
|
5847
|
-
if (!entry.isIntersecting || entry.intersectionRatio < MIN_VISIBLE_RATIO) {
|
|
5848
|
-
requestPause();
|
|
5849
|
-
}
|
|
5850
|
-
},
|
|
5851
|
-
{ threshold: [...VISIBILITY_THRESHOLDS] }
|
|
5852
|
-
);
|
|
5853
|
-
observer.observe(node);
|
|
5854
|
-
return () => observer.disconnect();
|
|
5855
|
-
}, [requestPause]);
|
|
5856
|
-
React7.useEffect(() => {
|
|
5857
|
-
const onVisibility = () => {
|
|
5858
|
-
if (document.hidden) requestPause();
|
|
5859
|
-
};
|
|
5860
|
-
document.addEventListener("visibilitychange", onVisibility);
|
|
5861
|
-
return () => document.removeEventListener("visibilitychange", onVisibility);
|
|
5862
|
-
}, [requestPause]);
|
|
5863
|
-
const onPlayingChange = React7.useCallback(
|
|
5864
|
-
(playing) => {
|
|
5865
|
-
if (!playing) return;
|
|
5866
|
-
setForcePaused(false);
|
|
5867
|
-
claimFeedPlaying(playbackId);
|
|
5868
|
-
},
|
|
5869
|
-
[playbackId]
|
|
5870
|
-
);
|
|
5871
|
-
return {
|
|
5872
|
-
containerRef,
|
|
5873
|
-
paused: forcePaused,
|
|
5874
|
-
onPlayingChange
|
|
5875
|
-
};
|
|
5876
|
-
}
|
|
5877
|
-
function FeedVideoPlayer({
|
|
5878
|
-
playbackId,
|
|
5879
|
-
onPlayingChange,
|
|
5880
|
-
paused: pausedProp,
|
|
5881
|
-
fit = "aspect-video",
|
|
5882
|
-
className,
|
|
5883
|
-
...playerProps
|
|
5884
|
-
}) {
|
|
5885
|
-
const {
|
|
5886
|
-
containerRef,
|
|
5887
|
-
paused: exclusivePaused,
|
|
5888
|
-
onPlayingChange: onExclusivePlayingChange
|
|
5889
|
-
} = useFeedExclusivePlayback(playbackId);
|
|
5890
|
-
const handlePlayingChange = React7.useCallback(
|
|
5891
|
-
(playing) => {
|
|
5892
|
-
onExclusivePlayingChange(playing);
|
|
5893
|
-
onPlayingChange?.(playing);
|
|
5894
|
-
},
|
|
5895
|
-
[onExclusivePlayingChange, onPlayingChange]
|
|
5896
|
-
);
|
|
5897
|
-
return /* @__PURE__ */ jsx(
|
|
5898
|
-
"div",
|
|
5899
|
-
{
|
|
5900
|
-
ref: containerRef,
|
|
5901
|
-
"data-slot": "feed-video-player",
|
|
5902
|
-
className: cn(fit === "contain" && "h-full min-h-0 w-full", className),
|
|
5903
|
-
children: /* @__PURE__ */ jsx(
|
|
5904
|
-
VideoPlayer,
|
|
5905
|
-
{
|
|
5906
|
-
...playerProps,
|
|
5907
|
-
fit,
|
|
5908
|
-
paused: pausedProp || exclusivePaused,
|
|
5909
|
-
onPlayingChange: handlePlayingChange,
|
|
5910
|
-
className: fit === "contain" ? "h-full min-h-0" : void 0
|
|
5911
|
-
}
|
|
5912
|
-
)
|
|
5913
|
-
}
|
|
5914
|
-
);
|
|
5915
|
-
}
|
|
5916
|
-
function usePlaybackStats(statsUrl, { intervalMs = 15e3, enabled = true } = {}) {
|
|
5917
|
-
const [stats, setStats] = React7.useState({});
|
|
5918
|
-
React7.useEffect(() => {
|
|
5919
|
-
if (!statsUrl || !enabled) return;
|
|
5920
|
-
let cancelled = false;
|
|
5921
|
-
const controller = new AbortController();
|
|
5922
|
-
async function poll() {
|
|
5923
|
-
try {
|
|
5924
|
-
const res = await fetch(statsUrl, {
|
|
5925
|
-
signal: controller.signal
|
|
5926
|
-
});
|
|
5927
|
-
if (!res.ok) return;
|
|
5928
|
-
const data = await res.json();
|
|
5929
|
-
if (cancelled) return;
|
|
5930
|
-
setStats({
|
|
5931
|
-
viewers: typeof data.viewers === "number" ? data.viewers : void 0,
|
|
5932
|
-
views: typeof data.views === "number" ? data.views : void 0
|
|
5933
|
-
});
|
|
5934
|
-
} catch {
|
|
5935
|
-
}
|
|
5936
|
-
}
|
|
5937
|
-
void poll();
|
|
5938
|
-
const timer = setInterval(() => void poll(), intervalMs);
|
|
5939
|
-
return () => {
|
|
5940
|
-
cancelled = true;
|
|
5941
|
-
controller.abort();
|
|
5942
|
-
clearInterval(timer);
|
|
5943
|
-
};
|
|
5944
|
-
}, [statsUrl, intervalMs, enabled]);
|
|
5945
|
-
return stats;
|
|
5946
|
-
}
|
|
5947
4651
|
function useDebouncedValue(value, delayMs) {
|
|
5948
4652
|
const [debounced, setDebounced] = useState(value);
|
|
5949
4653
|
useEffect(() => {
|
|
@@ -7274,7 +5978,7 @@ function ScrollBar({
|
|
|
7274
5978
|
);
|
|
7275
5979
|
}
|
|
7276
5980
|
var phoneInputGroupClass = "flex h-9 w-full gap-0 overflow-hidden rounded-md border border-input bg-transparent shadow-xs transition-[color,box-shadow] [--PhoneInputCountrySelect-marginRight:0] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 dark:bg-input/30 [&_.PhoneInputCountry]:mr-0";
|
|
7277
|
-
var PhoneInput =
|
|
5981
|
+
var PhoneInput = React2.forwardRef(
|
|
7278
5982
|
({ className, onChange, value, defaultCountry = "MX", ...props }, ref) => {
|
|
7279
5983
|
return /* @__PURE__ */ jsx(
|
|
7280
5984
|
RPNInput.default,
|
|
@@ -7294,7 +5998,7 @@ var PhoneInput = React7.forwardRef(
|
|
|
7294
5998
|
}
|
|
7295
5999
|
);
|
|
7296
6000
|
PhoneInput.displayName = "PhoneInput";
|
|
7297
|
-
var InputComponent =
|
|
6001
|
+
var InputComponent = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
7298
6002
|
Input,
|
|
7299
6003
|
{
|
|
7300
6004
|
className: cn(
|
|
@@ -7312,9 +6016,9 @@ var CountrySelect = ({
|
|
|
7312
6016
|
options: countryList,
|
|
7313
6017
|
onChange
|
|
7314
6018
|
}) => {
|
|
7315
|
-
const scrollAreaRef =
|
|
7316
|
-
const [searchValue, setSearchValue] =
|
|
7317
|
-
const [isOpen, setIsOpen] =
|
|
6019
|
+
const scrollAreaRef = React2.useRef(null);
|
|
6020
|
+
const [searchValue, setSearchValue] = React2.useState("");
|
|
6021
|
+
const [isOpen, setIsOpen] = React2.useState(false);
|
|
7318
6022
|
return /* @__PURE__ */ jsxs(
|
|
7319
6023
|
Popover,
|
|
7320
6024
|
{
|
|
@@ -7495,8 +6199,8 @@ var MEXICO_STATE_PATHS = {
|
|
|
7495
6199
|
};
|
|
7496
6200
|
function MexicoMap({ selected, onToggle, className }) {
|
|
7497
6201
|
const isInteractive = typeof onToggle === "function";
|
|
7498
|
-
const selectedSet =
|
|
7499
|
-
const handleKeyDown =
|
|
6202
|
+
const selectedSet = React2.useMemo(() => new Set(selected), [selected]);
|
|
6203
|
+
const handleKeyDown = React2.useCallback(
|
|
7500
6204
|
(event, id) => {
|
|
7501
6205
|
if (!onToggle) return;
|
|
7502
6206
|
if (event.key === "Enter" || event.key === " ") {
|
|
@@ -8422,7 +7126,7 @@ function ChatMessageList({
|
|
|
8422
7126
|
/* @__PURE__ */ jsx(MessageScrollerButton, {})
|
|
8423
7127
|
] }) });
|
|
8424
7128
|
}
|
|
8425
|
-
var MessageComposer =
|
|
7129
|
+
var MessageComposer = React2.forwardRef(function MessageComposer2({
|
|
8426
7130
|
className,
|
|
8427
7131
|
locale = "en",
|
|
8428
7132
|
disabled,
|
|
@@ -8452,13 +7156,13 @@ var MessageComposer = React7.forwardRef(function MessageComposer2({
|
|
|
8452
7156
|
rows = 2,
|
|
8453
7157
|
...props
|
|
8454
7158
|
}, ref) {
|
|
8455
|
-
const formRef =
|
|
8456
|
-
const fieldRef =
|
|
7159
|
+
const formRef = React2.useRef(null);
|
|
7160
|
+
const fieldRef = React2.useRef(null);
|
|
8457
7161
|
const handleEmojiSelect = useEmojiInsert(fieldRef, value, onChange);
|
|
8458
7162
|
const pickerDisabled = Boolean(disabled || readOnly);
|
|
8459
7163
|
const draft = value !== void 0 && value !== null ? String(value) : "";
|
|
8460
7164
|
const canSubmit = (allowEmptySubmit || Boolean(draft.trim())) && !sendDisabled && !disabled && !readOnly;
|
|
8461
|
-
const handleSubmit =
|
|
7165
|
+
const handleSubmit = React2.useCallback(
|
|
8462
7166
|
(event) => {
|
|
8463
7167
|
event.preventDefault();
|
|
8464
7168
|
if (!canSubmit) return;
|
|
@@ -8466,7 +7170,7 @@ var MessageComposer = React7.forwardRef(function MessageComposer2({
|
|
|
8466
7170
|
},
|
|
8467
7171
|
[canSubmit, onSubmit]
|
|
8468
7172
|
);
|
|
8469
|
-
const handleKeyDown =
|
|
7173
|
+
const handleKeyDown = React2.useCallback(
|
|
8470
7174
|
(event) => {
|
|
8471
7175
|
onKeyDown?.(event);
|
|
8472
7176
|
if (event.defaultPrevented) return;
|
|
@@ -9375,6 +8079,6 @@ function useRealtimeChannel({
|
|
|
9375
8079
|
}, [enabled, reconnectDelayMs]);
|
|
9376
8080
|
}
|
|
9377
8081
|
|
|
9378
|
-
export { AvatarEditor, AvatarEditorDialog, BrandX, Bubble, BubbleContent, BubbleGroup, BubbleReactions, ChatMessageList, ConfirmDestructiveDialog, DEFAULT_EDITABLE_SECTION_LABELS, DataList, DataListItem, DropdownMenuItem, DropdownMenuSeparator, EditableSection, EmojiInput, EmojiPicker, EmojiPickerPopover, EmojiTextarea, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, ErrorState, ExpandableText,
|
|
8082
|
+
export { AvatarEditor, AvatarEditorDialog, BrandX, Bubble, BubbleContent, BubbleGroup, BubbleReactions, ChatMessageList, ConfirmDestructiveDialog, DEFAULT_EDITABLE_SECTION_LABELS, DataList, DataListItem, DropdownMenuItem, DropdownMenuSeparator, EditableSection, EmojiInput, EmojiPicker, EmojiPickerPopover, EmojiTextarea, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, ErrorState, ExpandableText, FieldMessage, ImageLightbox, LanguageContext, LanguageSwitcher, LoadingState, MEXICO_STATES, MEXICO_STATE_PATHS, Marker, MarkerContent, MarkerIcon, MediaCarousel, MediaViewerDialog, Message, MessageAvatar, MessageComposer, MessageContent, MessageFooter, MessageGroup, MessageHeader, MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, MexicoMap, OrgSwitcher, PLATFORM_ACCOUNT_ORG_COLLAPSE_AT, PaymentIcon, PhoneInput, PlatformAccountMenu, PlatformNavbar, SafeImage, ScrollRestorationProvider, SearchCommand, SearchablePicker, SupportMessengerProvider, ThemeSwitcher, VerifiedProfessionalBadge, ZOOM_PAN_MAX_SCALE, ZOOM_PAN_MIN_SCALE, ZOOM_PAN_SWIPE_COMMIT_PX, ZOOM_PAN_SWIPE_SNAP_MS, avatarFallbackStyle, bubbleVariants, cn, createSupportChatClient, defaultLanguages, formatPaymentCardBrand, fuzzyMatch, getAvatarColors, getInitials, insertAtCaret, markerVariants, mergePlatformAccountOrgs, normalizeEmojiQuery, normalizePaymentCardBrand, paymentIconBrandFromStripe, paymentTypeFromStripeBrand, prefersReducedMotion, useDebouncedValue, useDebouncedValueStrict, useEditableSection, useLanguageContext, useMediaQuery2 as useMediaQuery, useRealtimeChannel, useRestorableState, useScrollFadeXEdges, useScrollRestorationContext, useSearchHotkey, useSupportConversation, useSupportMessenger, useSupportMessengerOptional, useZoomPanGestures, zoomToward };
|
|
9379
8083
|
//# sourceMappingURL=index.js.map
|
|
9380
8084
|
//# sourceMappingURL=index.js.map
|