@xhub-short/ui 0.1.0-beta.1 → 0.1.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CommentSheet.css-BeCrEaUG.d.ts +221 -0
- package/dist/{chunk-2PTMP65P.js → chunk-2FSDVYER.js} +8 -9
- package/dist/{chunk-WKX2WBVO.js → chunk-3XPJHUYL.js} +1 -39
- package/dist/{chunk-ANGBSV7L.js → chunk-AC2IFAJR.js} +10 -5
- package/dist/{chunk-4YDIRPIN.js → chunk-ANCP53F3.js} +3 -3
- package/dist/chunk-AQHD6LPS.js +430 -0
- package/dist/{chunk-HW4LXTFT.js → chunk-CL6BS7GB.js} +7 -5
- package/dist/{chunk-YW23IBKF.js → chunk-ECR42RKK.js} +46 -5
- package/dist/chunk-EDWS2IPH.js +1 -0
- package/dist/chunk-FNXTPQ6L.js +2573 -0
- package/dist/{chunk-DHQJBXQW.js → chunk-KWHMZ6H5.js} +1 -1
- package/dist/{chunk-UXMA4KJZ.js → chunk-RMLTPW5S.js} +3 -2
- package/dist/{chunk-SSJDO24Q.js → chunk-SZXFH334.js} +1 -1
- package/dist/{chunk-4MN72OZH.js → chunk-UNV3NWN6.js} +4 -4
- package/dist/{chunk-ZZDQKP4R.js → chunk-WCRDTBCZ.js} +94 -155
- package/dist/{chunk-XAOEHLOX.js → chunk-XDIH66C4.js} +245 -52
- package/dist/components/ActionBar/index.js +1 -1
- package/dist/components/AuthorInfo/index.d.ts +5 -1
- package/dist/components/AuthorInfo/index.js +1 -1
- package/dist/components/BlurhashPlaceholder/index.d.ts +67 -0
- package/dist/components/BlurhashPlaceholder/index.js +150 -0
- package/dist/components/CommentSheet/index.d.ts +164 -0
- package/dist/components/CommentSheet/index.js +1 -0
- package/dist/components/ErrorBoundary/index.js +1 -1
- package/dist/components/OfflineIndicator/index.d.ts +56 -0
- package/dist/components/OfflineIndicator/index.js +151 -0
- package/dist/components/ProgressBar/index.d.ts +30 -2
- package/dist/components/ProgressBar/index.js +1 -1
- package/dist/components/Skeleton/index.js +1 -1
- package/dist/components/SubtitleDisplay/index.d.ts +94 -0
- package/dist/components/SubtitleDisplay/index.js +165 -0
- package/dist/components/VideoFeed/index.d.ts +11 -0
- package/dist/components/VideoFeed/index.js +1 -1
- package/dist/components/VideoInfo/index.js +1 -1
- package/dist/components/VideoPlayer/index.d.ts +14 -41
- package/dist/components/VideoPlayer/index.js +1 -1
- package/dist/components/VideoSlot/index.d.ts +124 -64
- package/dist/components/VideoSlot/index.js +1 -1
- package/dist/components/VirtualSlider/index.d.ts +339 -0
- package/dist/components/VirtualSlider/index.js +1 -0
- package/dist/components/icons/index.js +1 -1
- package/dist/index.d.ts +76 -93
- package/dist/index.js +75 -27
- package/package.json +53 -8
- package/dist/use-gesture-react.esm-3SV4QLEJ.js +0 -1893
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { HeartFilledIcon } from './chunk-
|
|
2
|
-
import {
|
|
3
|
-
import { injectComponentCSS } from './chunk-
|
|
4
|
-
import { createContext, memo,
|
|
1
|
+
import { PauseIcon, PlayIcon, HeartFilledIcon } from './chunk-ANCP53F3.js';
|
|
2
|
+
import { clsx2 } from './chunk-EDWS2IPH.js';
|
|
3
|
+
import { injectComponentCSS } from './chunk-RMLTPW5S.js';
|
|
4
|
+
import { createContext, memo, useMemo, useState, useCallback, useEffect, useRef, useInsertionEffect, useContext } from 'react';
|
|
5
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
7
|
var DEFAULT_DOUBLE_TAP_DELAY = 300;
|
|
@@ -21,6 +21,7 @@ function useDoubleTap({
|
|
|
21
21
|
const lastPositionRef = useRef({ x: 50, y: 50 });
|
|
22
22
|
const lastExecutionTimeRef = useRef(0);
|
|
23
23
|
const pointerStartRef = useRef(null);
|
|
24
|
+
const cumulativeMovementRef = useRef(0);
|
|
24
25
|
useEffect(() => {
|
|
25
26
|
return () => {
|
|
26
27
|
if (tapTimeoutRef.current) {
|
|
@@ -72,6 +73,9 @@ function useDoubleTap({
|
|
|
72
73
|
}, [disabled, doubleTapDelay, onSingleTap, onDoubleTap]);
|
|
73
74
|
const isSwipe = useCallback(
|
|
74
75
|
(clientX, clientY) => {
|
|
76
|
+
if (cumulativeMovementRef.current > movementThreshold * 2) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
75
79
|
const start = pointerStartRef.current;
|
|
76
80
|
if (!start) return false;
|
|
77
81
|
const dx = Math.abs(clientX - start.x);
|
|
@@ -101,6 +105,11 @@ function useDoubleTap({
|
|
|
101
105
|
const handlePointerDown = useCallback((e) => {
|
|
102
106
|
if (e.button !== 0) return;
|
|
103
107
|
pointerStartRef.current = { x: e.clientX, y: e.clientY };
|
|
108
|
+
cumulativeMovementRef.current = 0;
|
|
109
|
+
}, []);
|
|
110
|
+
const handlePointerMove = useCallback((e) => {
|
|
111
|
+
if (!pointerStartRef.current) return;
|
|
112
|
+
cumulativeMovementRef.current += Math.abs(e.movementX) + Math.abs(e.movementY);
|
|
104
113
|
}, []);
|
|
105
114
|
const handlePointerUp = useCallback(
|
|
106
115
|
(e) => {
|
|
@@ -120,6 +129,7 @@ function useDoubleTap({
|
|
|
120
129
|
return {
|
|
121
130
|
handlers: {
|
|
122
131
|
onPointerDown: handlePointerDown,
|
|
132
|
+
onPointerMove: handlePointerMove,
|
|
123
133
|
onPointerUp: handlePointerUp,
|
|
124
134
|
onClick: handleClick
|
|
125
135
|
},
|
|
@@ -133,17 +143,18 @@ var VIDEO_SLOT_CSS = `
|
|
|
133
143
|
* Z-INDEX LAYERING CONTRACT
|
|
134
144
|
*
|
|
135
145
|
* Override these variables to customize layer stacking:
|
|
136
|
-
* --sv-z-poster:
|
|
137
|
-
* --sv-z-
|
|
138
|
-
* --sv-z-indicator:
|
|
139
|
-
* --sv-z-progress:
|
|
146
|
+
* --sv-z-poster: Poster/Thumbnail layer (default: 3)
|
|
147
|
+
* --sv-z-slot-spinner: Loading spinner layer (default: 2)
|
|
148
|
+
* --sv-z-indicator: Error/Play indicator layer (default: 4)
|
|
149
|
+
* --sv-z-progress: Progress bar layer (default: 5)
|
|
140
150
|
*
|
|
141
151
|
* Layer Stack (bottom to top):
|
|
142
152
|
* \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
143
|
-
* \u2502 z-index:
|
|
144
|
-
* \u2502 z-index:
|
|
145
|
-
* \u2502 z-index:
|
|
146
|
-
* \u2502 z-index:
|
|
153
|
+
* \u2502 z-index: 5 - Progress Bar (topmost) \u2502
|
|
154
|
+
* \u2502 z-index: 4 - Error UI / Play Indicator \u2502
|
|
155
|
+
* \u2502 z-index: 3 - Poster / Thumbnail \u2502
|
|
156
|
+
* \u2502 z-index: 2 - Loading Spinner \u2502
|
|
157
|
+
* \u2502 z-index: 1 - Video Element \u2502
|
|
147
158
|
* \u2502 z-index: 0 - Slot Container (base) \u2502
|
|
148
159
|
* \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
149
160
|
* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
|
|
@@ -249,7 +260,8 @@ var VIDEO_SLOT_CSS = `
|
|
|
249
260
|
align-items: center;
|
|
250
261
|
justify-content: center;
|
|
251
262
|
background-color: var(--sv-bg-primary, #000);
|
|
252
|
-
z-index:
|
|
263
|
+
/* Poster should be ABOVE spinner (z-index: 2) so thumbnail shows instead of spinner */
|
|
264
|
+
z-index: var(--sv-z-poster, 3);
|
|
253
265
|
/* Smooth fade transition */
|
|
254
266
|
transition: opacity var(--sv-transition-duration, 300ms) ease-out;
|
|
255
267
|
pointer-events: none;
|
|
@@ -453,7 +465,8 @@ var VIDEO_SLOT_CSS = `
|
|
|
453
465
|
top: 50%;
|
|
454
466
|
left: 50%;
|
|
455
467
|
transform: translate(-50%, -50%);
|
|
456
|
-
z-index
|
|
468
|
+
/* Use slot-specific z-index (not global --sv-z-overlay which may conflict) */
|
|
469
|
+
z-index: var(--sv-z-slot-spinner, 2);
|
|
457
470
|
}
|
|
458
471
|
|
|
459
472
|
.sv-video-slot__spinner-circle {
|
|
@@ -485,20 +498,29 @@ var VIDEO_SLOT_CSS = `
|
|
|
485
498
|
display: flex;
|
|
486
499
|
align-items: center;
|
|
487
500
|
justify-content: center;
|
|
488
|
-
background-color: rgba(0, 0, 0, 0.5);
|
|
489
501
|
border-radius: 50%;
|
|
490
502
|
z-index: var(--sv-z-indicator, 3);
|
|
491
503
|
opacity: 0;
|
|
492
|
-
transition: opacity 200ms ease;
|
|
504
|
+
transition: opacity 200ms ease-out;
|
|
493
505
|
pointer-events: none;
|
|
494
506
|
}
|
|
495
507
|
|
|
508
|
+
/* Show indicator */
|
|
496
509
|
.sv-video-slot__play-indicator--visible {
|
|
497
|
-
opacity:
|
|
498
|
-
animation: sv-slot-play-indicator 400ms ease-out forwards;
|
|
510
|
+
opacity: 0.65;
|
|
499
511
|
}
|
|
500
512
|
|
|
501
|
-
|
|
513
|
+
/* Persist mode: stay visible, no animation */
|
|
514
|
+
.sv-video-slot__play-indicator--persist {
|
|
515
|
+
opacity: 0.65;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/* Animating out: scale up + fade out */
|
|
519
|
+
.sv-video-slot__play-indicator--animating-out {
|
|
520
|
+
animation: sv-slot-indicator-out var(--sv-indicator-duration, 200ms) ease-out forwards;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
@keyframes sv-slot-indicator-out {
|
|
502
524
|
0% {
|
|
503
525
|
opacity: 1;
|
|
504
526
|
transform: translate(-50%, -50%) scale(1);
|
|
@@ -512,6 +534,11 @@ var VIDEO_SLOT_CSS = `
|
|
|
512
534
|
.sv-video-slot__play-indicator-icon {
|
|
513
535
|
color: #fff;
|
|
514
536
|
font-size: 28px;
|
|
537
|
+
width: 100%;
|
|
538
|
+
height: 100%;
|
|
539
|
+
display: flex;
|
|
540
|
+
align-items: center;
|
|
541
|
+
justify-content: center;
|
|
515
542
|
}
|
|
516
543
|
|
|
517
544
|
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
@@ -628,23 +655,25 @@ var SLOT_PAUSED_CLASS = "sv-video-slot--paused";
|
|
|
628
655
|
var Z_INDEX = {
|
|
629
656
|
/** Base layer - slot container */
|
|
630
657
|
SLOT: 0,
|
|
631
|
-
/**
|
|
632
|
-
|
|
633
|
-
/**
|
|
634
|
-
|
|
658
|
+
/** Video element */
|
|
659
|
+
VIDEO: 1,
|
|
660
|
+
/** Loading spinner */
|
|
661
|
+
SPINNER: 2,
|
|
662
|
+
/** Poster/thumbnail (above spinner so thumbnail shows during loading) */
|
|
663
|
+
POSTER: 3,
|
|
635
664
|
/** Error UI and play/pause indicator */
|
|
636
|
-
INDICATOR:
|
|
665
|
+
INDICATOR: 4,
|
|
637
666
|
/** Progress bar (always on top) */
|
|
638
|
-
PROGRESS:
|
|
667
|
+
PROGRESS: 5
|
|
639
668
|
};
|
|
640
669
|
var Z_INDEX_CSS_VARS = {
|
|
641
670
|
POSTER: "--sv-z-poster",
|
|
642
|
-
|
|
671
|
+
SPINNER: "--sv-z-slot-spinner",
|
|
643
672
|
INDICATOR: "--sv-z-indicator",
|
|
644
673
|
PROGRESS: "--sv-z-progress"
|
|
645
674
|
};
|
|
646
675
|
var ANIMATION_SELECTOR = '[data-like-animation="true"]';
|
|
647
|
-
function
|
|
676
|
+
function VideoSlotHeadlessBase({
|
|
648
677
|
video,
|
|
649
678
|
resourceState,
|
|
650
679
|
playerState,
|
|
@@ -660,7 +689,8 @@ function VideoSlotHeadless({
|
|
|
660
689
|
onTap,
|
|
661
690
|
onDoubleTap,
|
|
662
691
|
renderError,
|
|
663
|
-
renderLoading
|
|
692
|
+
renderLoading,
|
|
693
|
+
restoreFrame
|
|
664
694
|
}) {
|
|
665
695
|
useInsertionEffect(() => {
|
|
666
696
|
return injectComponentCSS("video-slot", VIDEO_SLOT_CSS);
|
|
@@ -669,6 +699,7 @@ function VideoSlotHeadless({
|
|
|
669
699
|
const animationElementRef = useRef(null);
|
|
670
700
|
const prevVideoIdRef = useRef(video.id);
|
|
671
701
|
const [firstFrame, setFirstFrame] = useState(null);
|
|
702
|
+
const [lastUserToggleTime, setLastUserToggleTime] = useState(0);
|
|
672
703
|
if (prevVideoIdRef.current !== video.id) {
|
|
673
704
|
prevVideoIdRef.current = video.id;
|
|
674
705
|
animationElementRef.current = null;
|
|
@@ -702,6 +733,7 @@ function VideoSlotHeadless({
|
|
|
702
733
|
return animationElement?.__showWhenAlreadyLiked ?? true;
|
|
703
734
|
}, [getAnimationElement]);
|
|
704
735
|
const handleSingleTap = useCallback(() => {
|
|
736
|
+
setLastUserToggleTime(Date.now());
|
|
705
737
|
if (onTap) {
|
|
706
738
|
onTap();
|
|
707
739
|
} else {
|
|
@@ -732,9 +764,11 @@ function VideoSlotHeadless({
|
|
|
732
764
|
playerState,
|
|
733
765
|
playerControls,
|
|
734
766
|
firstFrame,
|
|
735
|
-
setFirstFrame
|
|
767
|
+
setFirstFrame,
|
|
768
|
+
lastUserToggleTime,
|
|
769
|
+
setLastUserToggleTime
|
|
736
770
|
}),
|
|
737
|
-
[video, resourceState, playerState, playerControls, firstFrame]
|
|
771
|
+
[video, resourceState, playerState, playerControls, firstFrame, lastUserToggleTime]
|
|
738
772
|
);
|
|
739
773
|
const stateClasses = useMemo(() => {
|
|
740
774
|
const classes = [SLOT_CLASS];
|
|
@@ -764,7 +798,7 @@ function VideoSlotHeadless({
|
|
|
764
798
|
"div",
|
|
765
799
|
{
|
|
766
800
|
ref: containerRef,
|
|
767
|
-
className:
|
|
801
|
+
className: clsx2(...stateClasses, className),
|
|
768
802
|
...tapHandlers,
|
|
769
803
|
...{
|
|
770
804
|
[VIDEO_ID_ATTR]: video.id,
|
|
@@ -773,8 +807,26 @@ function VideoSlotHeadless({
|
|
|
773
807
|
[LOADING_ATTR]: playerState.isLoading ? "true" : "false"
|
|
774
808
|
},
|
|
775
809
|
children: [
|
|
810
|
+
restoreFrame && !playerState.isPlaying && !playerState.error && /* @__PURE__ */ jsx(
|
|
811
|
+
"div",
|
|
812
|
+
{
|
|
813
|
+
className: "sv-video-slot__restore-frame",
|
|
814
|
+
style: {
|
|
815
|
+
position: "absolute",
|
|
816
|
+
inset: 0,
|
|
817
|
+
zIndex: 5,
|
|
818
|
+
// Above video, below overlay
|
|
819
|
+
backgroundImage: `url(${restoreFrame})`,
|
|
820
|
+
backgroundSize: "contain",
|
|
821
|
+
backgroundPosition: "center",
|
|
822
|
+
backgroundRepeat: "no-repeat",
|
|
823
|
+
backgroundColor: "#000"
|
|
824
|
+
},
|
|
825
|
+
"aria-hidden": "true"
|
|
826
|
+
}
|
|
827
|
+
),
|
|
776
828
|
playerState.error && (renderError ? renderError(playerState.error, handleRetry) : /* @__PURE__ */ jsx(DefaultErrorUI, { error: playerState.error, onRetry: handleRetry })),
|
|
777
|
-
playerState.isLoading && !playerState.error && (renderLoading ? renderLoading() : /* @__PURE__ */ jsx(DefaultLoadingUI, {})),
|
|
829
|
+
playerState.isLoading && !playerState.error && !restoreFrame && (renderLoading ? renderLoading() : /* @__PURE__ */ jsx(DefaultLoadingUI, {})),
|
|
778
830
|
children
|
|
779
831
|
]
|
|
780
832
|
}
|
|
@@ -830,23 +882,28 @@ function DefaultErrorUI({
|
|
|
830
882
|
function DefaultLoadingUI() {
|
|
831
883
|
return /* @__PURE__ */ jsx("div", { className: "sv-video-slot__spinner", children: /* @__PURE__ */ jsx("div", { className: "sv-video-slot__spinner-circle", "aria-label": "Loading video" }) });
|
|
832
884
|
}
|
|
885
|
+
var VideoSlotHeadless = memo(VideoSlotHeadlessBase);
|
|
833
886
|
VideoSlotHeadless.displayName = "VideoSlotHeadless";
|
|
834
|
-
function
|
|
887
|
+
var VideoSlotPosterInner = memo(function VideoSlotPosterInner2({
|
|
835
888
|
className,
|
|
836
889
|
forceSkeleton = false,
|
|
837
890
|
posterUrl,
|
|
838
891
|
loading = "eager",
|
|
839
892
|
alt,
|
|
840
|
-
onError
|
|
893
|
+
onError,
|
|
894
|
+
videoPoster,
|
|
895
|
+
videoTitle,
|
|
896
|
+
firstFrame,
|
|
897
|
+
isLoading,
|
|
898
|
+
currentTime
|
|
841
899
|
}) {
|
|
842
|
-
const { video, firstFrame, playerState } = useVideoSlotContext();
|
|
843
900
|
const imageSource = useMemo(() => {
|
|
844
901
|
if (forceSkeleton) return null;
|
|
845
902
|
if (posterUrl) return posterUrl;
|
|
846
903
|
if (firstFrame) return firstFrame;
|
|
847
|
-
if (
|
|
904
|
+
if (videoPoster) return videoPoster;
|
|
848
905
|
return null;
|
|
849
|
-
}, [forceSkeleton, posterUrl, firstFrame,
|
|
906
|
+
}, [forceSkeleton, posterUrl, firstFrame, videoPoster]);
|
|
850
907
|
const sourceKey = imageSource ?? "skeleton";
|
|
851
908
|
const [errorForSource, setErrorForSource] = useState(null);
|
|
852
909
|
const imageError = errorForSource === sourceKey;
|
|
@@ -860,12 +917,12 @@ function VideoSlotPoster({
|
|
|
860
917
|
if (imageSource === firstFrame) return "first-frame";
|
|
861
918
|
return "poster";
|
|
862
919
|
}, [showSkeleton, imageSource, firstFrame]);
|
|
863
|
-
const imageAlt = alt ??
|
|
864
|
-
const isVisible =
|
|
920
|
+
const imageAlt = alt ?? videoTitle ?? "";
|
|
921
|
+
const isVisible = isLoading && currentTime === 0;
|
|
865
922
|
return /* @__PURE__ */ jsx(
|
|
866
923
|
"div",
|
|
867
924
|
{
|
|
868
|
-
className:
|
|
925
|
+
className: clsx2(
|
|
869
926
|
"sv-video-slot__poster",
|
|
870
927
|
`sv-video-slot__poster--${posterType}`,
|
|
871
928
|
!isVisible && "sv-video-slot__poster--hidden",
|
|
@@ -888,20 +945,35 @@ function VideoSlotPoster({
|
|
|
888
945
|
)
|
|
889
946
|
}
|
|
890
947
|
);
|
|
948
|
+
});
|
|
949
|
+
VideoSlotPosterInner.displayName = "VideoSlotPosterInner";
|
|
950
|
+
function VideoSlotPoster(props) {
|
|
951
|
+
const { video, firstFrame, playerState } = useVideoSlotContext();
|
|
952
|
+
return /* @__PURE__ */ jsx(
|
|
953
|
+
VideoSlotPosterInner,
|
|
954
|
+
{
|
|
955
|
+
...props,
|
|
956
|
+
videoPoster: video.poster,
|
|
957
|
+
videoTitle: video.title,
|
|
958
|
+
firstFrame,
|
|
959
|
+
isLoading: playerState.isLoading,
|
|
960
|
+
currentTime: playerState.currentTime
|
|
961
|
+
}
|
|
962
|
+
);
|
|
891
963
|
}
|
|
964
|
+
VideoSlotPoster.displayName = "VideoSlotPoster";
|
|
892
965
|
function VideoSlotSkeleton({
|
|
893
966
|
className
|
|
894
967
|
}) {
|
|
895
968
|
return /* @__PURE__ */ jsx(
|
|
896
969
|
"div",
|
|
897
970
|
{
|
|
898
|
-
className:
|
|
971
|
+
className: clsx2("sv-video-slot__poster", "sv-video-slot__poster--skeleton", className),
|
|
899
972
|
"aria-hidden": "true",
|
|
900
973
|
children: /* @__PURE__ */ jsx("div", { className: "sv-video-slot__poster-skeleton" })
|
|
901
974
|
}
|
|
902
975
|
);
|
|
903
976
|
}
|
|
904
|
-
VideoSlotPoster.displayName = "VideoSlotPoster";
|
|
905
977
|
VideoSlotSkeleton.displayName = "VideoSlotSkeleton";
|
|
906
978
|
function VideoSlotOverlayRoot({
|
|
907
979
|
children,
|
|
@@ -911,7 +983,7 @@ function VideoSlotOverlayRoot({
|
|
|
911
983
|
return /* @__PURE__ */ jsx(
|
|
912
984
|
"div",
|
|
913
985
|
{
|
|
914
|
-
className:
|
|
986
|
+
className: clsx2(
|
|
915
987
|
"sv-video-slot__overlay",
|
|
916
988
|
noGradient && "sv-video-slot__overlay--no-gradient",
|
|
917
989
|
className
|
|
@@ -924,37 +996,37 @@ function VideoSlotOverlayTop({
|
|
|
924
996
|
children,
|
|
925
997
|
className
|
|
926
998
|
}) {
|
|
927
|
-
return /* @__PURE__ */ jsx("div", { className:
|
|
999
|
+
return /* @__PURE__ */ jsx("div", { className: clsx2("sv-video-slot__overlay-top", className), children });
|
|
928
1000
|
}
|
|
929
1001
|
function VideoSlotOverlayBottom({
|
|
930
1002
|
children,
|
|
931
1003
|
className
|
|
932
1004
|
}) {
|
|
933
|
-
return /* @__PURE__ */ jsx("div", { className:
|
|
1005
|
+
return /* @__PURE__ */ jsx("div", { className: clsx2("sv-video-slot__overlay-bottom", className), children });
|
|
934
1006
|
}
|
|
935
1007
|
function VideoSlotOverlayLeft({
|
|
936
1008
|
children,
|
|
937
1009
|
className
|
|
938
1010
|
}) {
|
|
939
|
-
return /* @__PURE__ */ jsx("div", { className:
|
|
1011
|
+
return /* @__PURE__ */ jsx("div", { className: clsx2("sv-video-slot__overlay-left", className), children });
|
|
940
1012
|
}
|
|
941
1013
|
function VideoSlotOverlayRight({
|
|
942
1014
|
children,
|
|
943
1015
|
className
|
|
944
1016
|
}) {
|
|
945
|
-
return /* @__PURE__ */ jsx("div", { className:
|
|
1017
|
+
return /* @__PURE__ */ jsx("div", { className: clsx2("sv-video-slot__overlay-right", className), children });
|
|
946
1018
|
}
|
|
947
1019
|
function VideoSlotOverlayActions({
|
|
948
1020
|
children,
|
|
949
1021
|
className
|
|
950
1022
|
}) {
|
|
951
|
-
return /* @__PURE__ */ jsx("div", { className:
|
|
1023
|
+
return /* @__PURE__ */ jsx("div", { className: clsx2("sv-video-slot__actions", className), children });
|
|
952
1024
|
}
|
|
953
1025
|
function VideoSlotOverlayAuthor({
|
|
954
1026
|
children,
|
|
955
1027
|
className
|
|
956
1028
|
}) {
|
|
957
|
-
return /* @__PURE__ */ jsx("div", { className:
|
|
1029
|
+
return /* @__PURE__ */ jsx("div", { className: clsx2("sv-video-slot__author", className), children });
|
|
958
1030
|
}
|
|
959
1031
|
VideoSlotOverlayRoot.displayName = "VideoSlotOverlay";
|
|
960
1032
|
VideoSlotOverlayTop.displayName = "VideoSlotOverlay.Top";
|
|
@@ -1214,7 +1286,7 @@ var SingleHeart = memo(function SingleHeart2({
|
|
|
1214
1286
|
return /* @__PURE__ */ jsx(
|
|
1215
1287
|
"div",
|
|
1216
1288
|
{
|
|
1217
|
-
className:
|
|
1289
|
+
className: clsx2("sv-like-heart", `sv-like-heart--${animation}`),
|
|
1218
1290
|
style: {
|
|
1219
1291
|
"--sv-heart-x": `${heart.x}%`,
|
|
1220
1292
|
"--sv-heart-y": `${heart.y}%`,
|
|
@@ -1303,7 +1375,7 @@ function VideoSlotLikeAnimation({
|
|
|
1303
1375
|
"div",
|
|
1304
1376
|
{
|
|
1305
1377
|
ref: containerRef,
|
|
1306
|
-
className:
|
|
1378
|
+
className: clsx2("sv-like-animation-container", className),
|
|
1307
1379
|
"data-like-animation": "true",
|
|
1308
1380
|
"aria-hidden": "true",
|
|
1309
1381
|
children: hearts.map((heart) => /* @__PURE__ */ jsx(
|
|
@@ -1322,5 +1394,126 @@ function VideoSlotLikeAnimation({
|
|
|
1322
1394
|
);
|
|
1323
1395
|
}
|
|
1324
1396
|
VideoSlotLikeAnimation.displayName = "VideoSlotLikeAnimation";
|
|
1397
|
+
function DefaultPlayIcon({ size }) {
|
|
1398
|
+
return /* @__PURE__ */ jsx(PlayIcon, { size });
|
|
1399
|
+
}
|
|
1400
|
+
function DefaultPauseIcon({ size }) {
|
|
1401
|
+
return /* @__PURE__ */ jsx(PauseIcon, { size });
|
|
1402
|
+
}
|
|
1403
|
+
var VideoSlotPlayIndicatorInner = memo(function VideoSlotPlayIndicatorInner2({
|
|
1404
|
+
playIcon,
|
|
1405
|
+
pauseIcon,
|
|
1406
|
+
size = 48,
|
|
1407
|
+
duration = 600,
|
|
1408
|
+
persistWhenPaused = true,
|
|
1409
|
+
className,
|
|
1410
|
+
testId,
|
|
1411
|
+
lastUserToggleTime,
|
|
1412
|
+
isPlaying
|
|
1413
|
+
}) {
|
|
1414
|
+
const [showIndicator, setShowIndicator] = useState(false);
|
|
1415
|
+
const [isPersisting, setIsPersisting] = useState(false);
|
|
1416
|
+
const [isAnimatingOut, setIsAnimatingOut] = useState(false);
|
|
1417
|
+
const [iconType, setIconType] = useState("play");
|
|
1418
|
+
const hideTimeoutRef = useRef(null);
|
|
1419
|
+
const lastHandledToggleTimeRef = useRef(0);
|
|
1420
|
+
const isPlayingRef = useRef(isPlaying);
|
|
1421
|
+
isPlayingRef.current = isPlaying;
|
|
1422
|
+
const persistWhenPausedRef = useRef(persistWhenPaused);
|
|
1423
|
+
persistWhenPausedRef.current = persistWhenPaused;
|
|
1424
|
+
const prevIsPlayingRef = useRef(isPlaying);
|
|
1425
|
+
const clearHideTimeout = () => {
|
|
1426
|
+
if (hideTimeoutRef.current) {
|
|
1427
|
+
clearTimeout(hideTimeoutRef.current);
|
|
1428
|
+
hideTimeoutRef.current = null;
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
useEffect(() => {
|
|
1432
|
+
const wasPlaying = prevIsPlayingRef.current;
|
|
1433
|
+
prevIsPlayingRef.current = isPlaying;
|
|
1434
|
+
if (!wasPlaying && isPlaying && isPersisting) {
|
|
1435
|
+
const timeSinceLastToggle = Date.now() - lastUserToggleTime;
|
|
1436
|
+
const isAutoResume = timeSinceLastToggle > 100;
|
|
1437
|
+
if (isAutoResume) {
|
|
1438
|
+
if (hideTimeoutRef.current) {
|
|
1439
|
+
clearTimeout(hideTimeoutRef.current);
|
|
1440
|
+
hideTimeoutRef.current = null;
|
|
1441
|
+
}
|
|
1442
|
+
setShowIndicator(false);
|
|
1443
|
+
setIsPersisting(false);
|
|
1444
|
+
setIsAnimatingOut(false);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}, [isPlaying, isPersisting, lastUserToggleTime]);
|
|
1448
|
+
useEffect(() => {
|
|
1449
|
+
if (lastUserToggleTime === 0 || lastUserToggleTime === lastHandledToggleTimeRef.current) {
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
lastHandledToggleTimeRef.current = lastUserToggleTime;
|
|
1453
|
+
if (isPersisting && isPlaying) {
|
|
1454
|
+
setIconType("pause");
|
|
1455
|
+
setIsPersisting(false);
|
|
1456
|
+
setIsAnimatingOut(true);
|
|
1457
|
+
clearHideTimeout();
|
|
1458
|
+
hideTimeoutRef.current = setTimeout(() => {
|
|
1459
|
+
setShowIndicator(false);
|
|
1460
|
+
setIsAnimatingOut(false);
|
|
1461
|
+
hideTimeoutRef.current = null;
|
|
1462
|
+
}, duration);
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
setShowIndicator(true);
|
|
1466
|
+
setIsPersisting(false);
|
|
1467
|
+
setIsAnimatingOut(false);
|
|
1468
|
+
setIconType("play");
|
|
1469
|
+
clearHideTimeout();
|
|
1470
|
+
hideTimeoutRef.current = setTimeout(() => {
|
|
1471
|
+
const currentIsPlaying = isPlayingRef.current;
|
|
1472
|
+
const shouldPersist = persistWhenPausedRef.current;
|
|
1473
|
+
if (shouldPersist && !currentIsPlaying) {
|
|
1474
|
+
setIsPersisting(true);
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
setShowIndicator(false);
|
|
1478
|
+
hideTimeoutRef.current = null;
|
|
1479
|
+
}, duration);
|
|
1480
|
+
return clearHideTimeout;
|
|
1481
|
+
}, [lastUserToggleTime, duration, isPersisting, isPlaying]);
|
|
1482
|
+
if (!showIndicator && !isPersisting) return null;
|
|
1483
|
+
const iconToShow = iconType === "pause" ? pauseIcon ?? /* @__PURE__ */ jsx(DefaultPauseIcon, { size }) : playIcon ?? /* @__PURE__ */ jsx(DefaultPlayIcon, { size });
|
|
1484
|
+
const indicatorClass = [
|
|
1485
|
+
"sv-video-slot__play-indicator",
|
|
1486
|
+
showIndicator && "sv-video-slot__play-indicator--visible",
|
|
1487
|
+
isPersisting && "sv-video-slot__play-indicator--persist",
|
|
1488
|
+
isAnimatingOut && "sv-video-slot__play-indicator--animating-out",
|
|
1489
|
+
className
|
|
1490
|
+
].filter(Boolean).join(" ");
|
|
1491
|
+
return /* @__PURE__ */ jsx(
|
|
1492
|
+
"div",
|
|
1493
|
+
{
|
|
1494
|
+
className: indicatorClass,
|
|
1495
|
+
style: { "--sv-indicator-duration": `${duration}ms` },
|
|
1496
|
+
"aria-hidden": "true",
|
|
1497
|
+
"data-testid": testId,
|
|
1498
|
+
children: /* @__PURE__ */ jsx("span", { className: "sv-video-slot__play-indicator-icon", children: iconToShow })
|
|
1499
|
+
}
|
|
1500
|
+
);
|
|
1501
|
+
});
|
|
1502
|
+
VideoSlotPlayIndicatorInner.displayName = "VideoSlotPlayIndicatorInner";
|
|
1503
|
+
function VideoSlotPlayIndicator(props) {
|
|
1504
|
+
const context = useOptionalVideoSlotContext();
|
|
1505
|
+
if (!context) return null;
|
|
1506
|
+
const lastUserToggleTime = context.lastUserToggleTime ?? 0;
|
|
1507
|
+
const isPlaying = context.playerState?.isPlaying ?? false;
|
|
1508
|
+
return /* @__PURE__ */ jsx(
|
|
1509
|
+
VideoSlotPlayIndicatorInner,
|
|
1510
|
+
{
|
|
1511
|
+
...props,
|
|
1512
|
+
lastUserToggleTime,
|
|
1513
|
+
isPlaying
|
|
1514
|
+
}
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1517
|
+
VideoSlotPlayIndicator.displayName = "VideoSlotPlayIndicator";
|
|
1325
1518
|
|
|
1326
|
-
export { LOADING_ATTR, LOADING_DATASET_KEY, PLAYER_STATE_ATTR, PLAYER_STATE_DATASET_KEY, SLOT_ACTIVE_ATTR, SLOT_ACTIVE_CLASS, SLOT_ACTIVE_DATASET_KEY, SLOT_CLASS, SLOT_ERROR_CLASS, SLOT_LOADING_CLASS, SLOT_PAUSED_CLASS, SLOT_PLAYING_CLASS, VIDEO_ID_ATTR, VIDEO_ID_DATASET_KEY, VIDEO_SLOT_CSS, VIDEO_SLOT_LIKE_ANIMATION_CSS, VideoSlotContext, VideoSlotHeadless, VideoSlotLikeAnimation, VideoSlotOverlay, VideoSlotPoster, VideoSlotSkeleton, Z_INDEX, Z_INDEX_CSS_VARS, useDoubleTap, useOptionalVideoSlotContext, useVideoSlotContext, useVideoSlotIsActive, useVideoSlotIsPlaying, useVideoSlotPlayer, useVideoSlotResource, useVideoSlotVideo };
|
|
1519
|
+
export { LOADING_ATTR, LOADING_DATASET_KEY, PLAYER_STATE_ATTR, PLAYER_STATE_DATASET_KEY, SLOT_ACTIVE_ATTR, SLOT_ACTIVE_CLASS, SLOT_ACTIVE_DATASET_KEY, SLOT_CLASS, SLOT_ERROR_CLASS, SLOT_LOADING_CLASS, SLOT_PAUSED_CLASS, SLOT_PLAYING_CLASS, VIDEO_ID_ATTR, VIDEO_ID_DATASET_KEY, VIDEO_SLOT_CSS, VIDEO_SLOT_LIKE_ANIMATION_CSS, VideoSlotContext, VideoSlotHeadless, VideoSlotLikeAnimation, VideoSlotOverlay, VideoSlotPlayIndicator, VideoSlotPoster, VideoSlotSkeleton, Z_INDEX, Z_INDEX_CSS_VARS, useDoubleTap, useOptionalVideoSlotContext, useVideoSlotContext, useVideoSlotIsActive, useVideoSlotIsPlaying, useVideoSlotPlayer, useVideoSlotResource, useVideoSlotVideo };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { ACTION_BAR_CSS, CompoundBookmark as ActionBarBookmark, CompoundComment as ActionBarComment, ActionBarHeadless, CompoundLike as ActionBarLike, CompoundShare as ActionBarShare, ActionButton, BookmarkButton, CommentButton, LikeButton, ShareButton } from '../../chunk-
|
|
1
|
+
export { ACTION_BAR_CSS, CompoundBookmark as ActionBarBookmark, CompoundComment as ActionBarComment, ActionBarHeadless, CompoundLike as ActionBarLike, CompoundShare as ActionBarShare, ActionButton, BookmarkButton, CommentButton, LikeButton, ShareButton } from '../../chunk-AC2IFAJR.js';
|
|
@@ -137,6 +137,10 @@ interface FollowButtonProps {
|
|
|
137
137
|
className?: string;
|
|
138
138
|
/** Children (for fully custom content) */
|
|
139
139
|
children?: ReactNode;
|
|
140
|
+
/** Aria-label for follow state (default: "Follow") */
|
|
141
|
+
followAriaLabel?: string;
|
|
142
|
+
/** Aria-label for unfollow state (default: "Unfollow") */
|
|
143
|
+
unfollowAriaLabel?: string;
|
|
140
144
|
}
|
|
141
145
|
/**
|
|
142
146
|
* FollowButton - Follow/unfollow toggle button
|
|
@@ -155,7 +159,7 @@ interface FollowButtonProps {
|
|
|
155
159
|
* <AuthorInfoHeadless.FollowButton size="small" />
|
|
156
160
|
* </AuthorInfoHeadless>
|
|
157
161
|
*/
|
|
158
|
-
declare function FollowButton({ isFollowing: isFollowingProp, isPending: isPendingProp, onClick, size, iconOnly, followIcon, followingIcon, followText, followingText, disabled, className, children, }: FollowButtonProps): react_jsx_runtime.JSX.Element;
|
|
162
|
+
declare function FollowButton({ isFollowing: isFollowingProp, isPending: isPendingProp, onClick, size, iconOnly, followIcon, followingIcon, followText, followingText, disabled, className, children, followAriaLabel, unfollowAriaLabel, }: FollowButtonProps): react_jsx_runtime.JSX.Element;
|
|
159
163
|
|
|
160
164
|
/**
|
|
161
165
|
* Layout variant for AuthorInfo
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { AUTHOR_INFO_CSS, AuthorAvatar, AuthorDescription, AuthorInfoContext, AuthorInfoHeadless, AuthorName, FollowButton, useAuthorInfoContext, useOptionalAuthorInfoContext } from '../../chunk-
|
|
1
|
+
export { AUTHOR_INFO_CSS, AuthorAvatar, AuthorDescription, AuthorInfoContext, AuthorInfoHeadless, AuthorName, FollowButton, useAuthorInfoContext, useOptionalAuthorInfoContext } from '../../chunk-2FSDVYER.js';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlurhashPlaceholderHeadless - Blur placeholder from blurhash string
|
|
3
|
+
*
|
|
4
|
+
* Lazy loads the blurhash decoder and caches decoded images.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Props for BlurhashPlaceholderHeadless
|
|
10
|
+
*/
|
|
11
|
+
interface BlurhashPlaceholderHeadlessProps {
|
|
12
|
+
/** Blurhash string to decode */
|
|
13
|
+
blurhash: string;
|
|
14
|
+
/** Width of decoded image (default: 32) */
|
|
15
|
+
width?: number;
|
|
16
|
+
/** Height of decoded image (default: 32) */
|
|
17
|
+
height?: number;
|
|
18
|
+
/** Punch factor for color intensity (default: 1) */
|
|
19
|
+
punch?: number;
|
|
20
|
+
/** Whether to hide (fade out) */
|
|
21
|
+
isHidden?: boolean;
|
|
22
|
+
/** Additional CSS class */
|
|
23
|
+
className?: string;
|
|
24
|
+
/** Test ID */
|
|
25
|
+
testId?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* BlurhashPlaceholderHeadless - Display decoded blurhash as placeholder
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* <BlurhashPlaceholderHeadless
|
|
33
|
+
* blurhash="LEHV6nWB2yk8pyo0adR*.7kCMdnj"
|
|
34
|
+
* width={32}
|
|
35
|
+
* height={32}
|
|
36
|
+
* isHidden={isVideoReady}
|
|
37
|
+
* />
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare function BlurhashPlaceholderHeadless({ blurhash, width, height, punch, isHidden, className, testId, }: BlurhashPlaceholderHeadlessProps): React.ReactElement | null;
|
|
41
|
+
/**
|
|
42
|
+
* Clear the blurhash image cache
|
|
43
|
+
*/
|
|
44
|
+
declare function clearBlurhashCache(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Get cache size
|
|
47
|
+
*/
|
|
48
|
+
declare function getBlurhashCacheSize(): number;
|
|
49
|
+
/**
|
|
50
|
+
* Preload a blurhash into cache
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* // Preload blurhash before component renders
|
|
55
|
+
* await preloadBlurhash('LEHV6nWB2yk8pyo0adR*.7kCMdnj');
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare function preloadBlurhash(blurhash: string, width?: number, height?: number, punch?: number): Promise<string | null>;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* BlurhashPlaceholder CSS
|
|
62
|
+
*
|
|
63
|
+
* Styles for blurhash placeholder images.
|
|
64
|
+
*/
|
|
65
|
+
declare const BLURHASH_PLACEHOLDER_CSS = "\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * BLURHASH PLACEHOLDER\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-blurhash-placeholder {\n position: absolute;\n inset: 0;\n z-index: var(--sv-z-placeholder, 1);\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n transition: opacity 200ms ease-out;\n}\n\n.sv-blurhash-placeholder--hidden {\n opacity: 0;\n pointer-events: none;\n}\n\n/* Canvas used for decoding - always hidden */\n.sv-blurhash-placeholder__canvas {\n display: none !important;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n * REDUCE MOTION\n * \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n@media (prefers-reduced-motion: reduce) {\n .sv-blurhash-placeholder {\n transition: none;\n }\n}\n";
|
|
66
|
+
|
|
67
|
+
export { BLURHASH_PLACEHOLDER_CSS, BlurhashPlaceholderHeadless, type BlurhashPlaceholderHeadlessProps, clearBlurhashCache, getBlurhashCacheSize, preloadBlurhash };
|