@xhub-short/ui 0.1.0-beta.1 → 0.1.0-beta.11

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.
Files changed (76) hide show
  1. package/dist/CommentSheet.css-DyEc3Sro.d.ts +217 -0
  2. package/dist/VideoSlotPlayIndicator-DPs8Xt5C.d.ts +51 -0
  3. package/dist/chunk-3OB3OVYR.js +349 -0
  4. package/dist/{chunk-WKX2WBVO.js → chunk-3XPJHUYL.js} +1 -39
  5. package/dist/chunk-4RIMQOBR.js +58 -0
  6. package/dist/chunk-4TUBNA2X.js +180 -0
  7. package/dist/{chunk-4YDIRPIN.js → chunk-ANCP53F3.js} +3 -3
  8. package/dist/{chunk-UXMA4KJZ.js → chunk-CAWE42LH.js} +5 -3
  9. package/dist/{chunk-ANGBSV7L.js → chunk-CIIZ3IHV.js} +10 -5
  10. package/dist/chunk-DR7KR7OT.js +103 -0
  11. package/dist/chunk-DXLCQ4FH.js +102 -0
  12. package/dist/chunk-EDWS2IPH.js +1 -0
  13. package/dist/chunk-FR7UQSZP.js +570 -0
  14. package/dist/chunk-IWSBYOSS.js +91 -0
  15. package/dist/chunk-JEY6R4KJ.js +334 -0
  16. package/dist/chunk-KMJ3PQ7M.js +1262 -0
  17. package/dist/chunk-MFJS65C5.js +368 -0
  18. package/dist/{chunk-HW4LXTFT.js → chunk-OM4L7RE5.js} +18 -6
  19. package/dist/chunk-PBIH2F2Q.js +344 -0
  20. package/dist/chunk-PJ4NMVMY.js +326 -0
  21. package/dist/chunk-Q6MG7AVG.js +531 -0
  22. package/dist/chunk-QCKVF2DR.js +713 -0
  23. package/dist/chunk-QCRRF76W.js +75 -0
  24. package/dist/chunk-QUEJHA24.js +508 -0
  25. package/dist/chunk-VXW7AOGM.js +285 -0
  26. package/dist/chunk-YB7AXTX7.js +430 -0
  27. package/dist/chunk-ZGWSJ6Z5.js +601 -0
  28. package/dist/components/ActionBar/index.js +1 -1
  29. package/dist/components/AdvanceMenu/index.d.ts +78 -0
  30. package/dist/components/AdvanceMenu/index.js +1 -0
  31. package/dist/components/AuthorInfo/index.d.ts +5 -1
  32. package/dist/components/AuthorInfo/index.js +1 -1
  33. package/dist/components/BottomSheet/index.d.ts +82 -0
  34. package/dist/components/BottomSheet/index.js +1 -0
  35. package/dist/components/CleanModeOverlay/index.d.ts +60 -0
  36. package/dist/components/CleanModeOverlay/index.js +1 -0
  37. package/dist/components/CommentSheet/index.d.ts +164 -0
  38. package/dist/components/CommentSheet/index.js +1 -0
  39. package/dist/components/DetailView/index.d.ts +311 -0
  40. package/dist/components/DetailView/index.js +1 -0
  41. package/dist/components/ErrorBoundary/index.js +1 -1
  42. package/dist/components/ImageCarousel/index.d.ts +50 -0
  43. package/dist/components/ImageCarousel/index.js +1 -0
  44. package/dist/components/ImagePostSlot/index.d.ts +207 -0
  45. package/dist/components/ImagePostSlot/index.js +1 -0
  46. package/dist/components/ProgressBar/index.d.ts +30 -2
  47. package/dist/components/ProgressBar/index.js +1 -1
  48. package/dist/components/QualityPicker/index.d.ts +35 -0
  49. package/dist/components/QualityPicker/index.js +1 -0
  50. package/dist/components/ReportSheet/index.d.ts +68 -0
  51. package/dist/components/ReportSheet/index.js +1 -0
  52. package/dist/components/Skeleton/index.js +1 -1
  53. package/dist/components/SpeedPicker/index.d.ts +32 -0
  54. package/dist/components/SpeedPicker/index.js +1 -0
  55. package/dist/components/VideoFeed/index.d.ts +12 -1
  56. package/dist/components/VideoFeed/index.js +1 -1
  57. package/dist/components/VideoInfo/index.d.ts +4 -2
  58. package/dist/components/VideoInfo/index.js +1 -1
  59. package/dist/components/VideoPlayer/index.d.ts +14 -41
  60. package/dist/components/VideoPlayer/index.js +1 -1
  61. package/dist/components/VideoSlot/index.d.ts +84 -65
  62. package/dist/components/VideoSlot/index.js +2 -1
  63. package/dist/components/VirtualSlider/index.d.ts +339 -0
  64. package/dist/components/VirtualSlider/index.js +1 -0
  65. package/dist/components/icons/index.js +1 -1
  66. package/dist/index.d.ts +107 -95
  67. package/dist/index.js +84 -27
  68. package/package.json +51 -7
  69. package/dist/chunk-2PTMP65P.js +0 -738
  70. package/dist/chunk-4MN72OZH.js +0 -148
  71. package/dist/chunk-DHQJBXQW.js +0 -562
  72. package/dist/chunk-SSJDO24Q.js +0 -204
  73. package/dist/chunk-XAOEHLOX.js +0 -1326
  74. package/dist/chunk-YW23IBKF.js +0 -530
  75. package/dist/chunk-ZZDQKP4R.js +0 -418
  76. package/dist/use-gesture-react.esm-3SV4QLEJ.js +0 -1893
@@ -0,0 +1,207 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ReactElement } from 'react';
3
+ import { ImagePost, MusicInfo } from '@xhub-short/contracts';
4
+ import { b as VideoSlotPlayIndicatorInnerProps } from '../../VideoSlotPlayIndicator-DPs8Xt5C.js';
5
+
6
+ /** CSS class for image post slot */
7
+ declare const IMAGE_POST_SLOT_CLASS = "sv-image-post-slot";
8
+ /** CSS class for active state */
9
+ declare const IMAGE_POST_SLOT_ACTIVE_CLASS = "sv-image-post-slot--active";
10
+ /** CSS class for clean mode */
11
+ declare const IMAGE_POST_SLOT_CLEAN_MODE_CLASS = "sv-image-post-slot--clean-mode";
12
+ /** Data attribute for image post ID */
13
+ declare const IMAGE_POST_ID_ATTR = "data-image-post-id";
14
+ /** Data attribute for active state */
15
+ declare const IMAGE_POST_ACTIVE_ATTR = "data-active";
16
+ interface ImagePostSlotContextValue {
17
+ /** The image post data */
18
+ imagePost: ImagePost;
19
+ /** Current image index in carousel */
20
+ currentImageIndex: number;
21
+ /** Total number of images */
22
+ totalImages: number;
23
+ /** Whether this slot is active (visible) */
24
+ isActive: boolean;
25
+ /** Whether in clean mode */
26
+ isCleanMode: boolean;
27
+ /** Timestamp of last user music toggle (for PlayIndicator) */
28
+ lastMusicToggleTime: number;
29
+ /** Music info (if any) */
30
+ music?: MusicInfo;
31
+ /** Whether music is playing */
32
+ isMusicPlaying: boolean;
33
+ /** Toggle music playback */
34
+ toggleMusic: () => void;
35
+ /** Navigate to specific image */
36
+ goToImage: (index: number) => void;
37
+ /** Go to next image */
38
+ nextImage: () => void;
39
+ /** Go to previous image */
40
+ prevImage: () => void;
41
+ /** Expand caption / open detail view */
42
+ expandCaption: () => void;
43
+ }
44
+ /**
45
+ * Hook to access ImagePostSlot context
46
+ * @throws if used outside ImagePostSlotHeadless
47
+ */
48
+ declare function useImagePostSlotContext(): ImagePostSlotContextValue;
49
+ /**
50
+ * Hook to optionally access ImagePostSlot context
51
+ * Returns null if used outside ImagePostSlotHeadless
52
+ */
53
+ declare function useOptionalImagePostSlotContext(): ImagePostSlotContextValue | null;
54
+ interface ImagePostSlotHeadlessProps {
55
+ /** The image post data */
56
+ imagePost: ImagePost;
57
+ /** Whether this slot is active (visible in viewport) */
58
+ isActive?: boolean;
59
+ /** Current image index (controlled) */
60
+ currentImageIndex?: number;
61
+ /** Called when image index changes */
62
+ onImageIndexChange?: (index: number) => void;
63
+ /** Music info for this post */
64
+ music?: MusicInfo;
65
+ /** Whether music is playing */
66
+ isMusicPlaying?: boolean;
67
+ /** Toggle music playback */
68
+ onToggleMusic?: () => void;
69
+ /**
70
+ * Last music toggle timestamp (controlled from parent for PlayIndicator sync).
71
+ * If provided, overrides internal tracking.
72
+ */
73
+ lastMusicToggleTime?: number;
74
+ /** Whether in clean mode (hide overlay) */
75
+ isCleanMode?: boolean;
76
+ /** Handle single tap */
77
+ onTap?: () => void;
78
+ /** Handle double tap (usually for like) */
79
+ onDoubleTap?: () => void;
80
+ /** Handle long press */
81
+ onLongPress?: () => void;
82
+ /** Called when "Read More" is clicked */
83
+ onReadMoreClick?: () => void;
84
+ /** Disable tap gestures */
85
+ disableTap?: boolean;
86
+ /** Enable pinch-to-zoom on images */
87
+ enableZoom?: boolean;
88
+ /** Show image counter (1/5) */
89
+ showCounter?: boolean;
90
+ /** Show pagination dots */
91
+ showIndicators?: boolean;
92
+ /** Additional class name */
93
+ className?: string;
94
+ /** Children (render prop or ReactNode) */
95
+ children?: ReactNode | ((context: ImagePostSlotContextValue) => ReactNode);
96
+ }
97
+ declare function ImagePostSlotHeadlessBase({ imagePost, isActive, currentImageIndex: controlledIndex, onImageIndexChange, music, isMusicPlaying, onToggleMusic, lastMusicToggleTime: controlledToggleTime, isCleanMode, onTap, onDoubleTap, onLongPress, onReadMoreClick, disableTap, enableZoom, showCounter, showIndicators, className, children, }: ImagePostSlotHeadlessProps): ReactElement;
98
+ declare const ImagePostSlotHeadless: react.MemoExoticComponent<typeof ImagePostSlotHeadlessBase>;
99
+ /**
100
+ * Overlay container for author, caption, actions
101
+ */
102
+ interface ImagePostSlotOverlayProps {
103
+ children?: ReactNode;
104
+ className?: string;
105
+ }
106
+ declare function ImagePostSlotOverlay({ children, className, }: ImagePostSlotOverlayProps): ReactElement;
107
+ /**
108
+ * Bottom content area (author, caption, read more)
109
+ */
110
+ interface ImagePostSlotBottomProps {
111
+ children?: ReactNode;
112
+ className?: string;
113
+ /** Click handler */
114
+ onClick?: () => void;
115
+ }
116
+ declare function ImagePostSlotBottom({ children, className, onClick, }: ImagePostSlotBottomProps): ReactElement;
117
+ /**
118
+ * Actions container (right side)
119
+ */
120
+ interface ImagePostSlotActionsProps {
121
+ children?: ReactNode;
122
+ className?: string;
123
+ }
124
+ declare function ImagePostSlotActions({ children, className, }: ImagePostSlotActionsProps): ReactElement;
125
+ /**
126
+ * Caption component with optional expansion
127
+ */
128
+ interface ImagePostSlotCaptionProps {
129
+ /** Maximum lines to show (default: 2) */
130
+ maxLines?: number;
131
+ /** Whether caption is expanded */
132
+ expanded?: boolean;
133
+ /** Class name */
134
+ className?: string;
135
+ }
136
+ declare function ImagePostSlotCaption({ maxLines, expanded, className, }: ImagePostSlotCaptionProps): ReactElement;
137
+ /**
138
+ * Read More button
139
+ */
140
+ interface ImagePostSlotReadMoreProps {
141
+ /** Button label */
142
+ label?: string;
143
+ /** Class name */
144
+ className?: string;
145
+ /** Click handler (overrides context) */
146
+ onClick?: () => void;
147
+ }
148
+ declare function ImagePostSlotReadMore({ label, className, onClick, }: ImagePostSlotReadMoreProps): ReactElement;
149
+ /**
150
+ * Music bar component
151
+ */
152
+ interface ImagePostSlotMusicProps {
153
+ /** Class name */
154
+ className?: string;
155
+ /**
156
+ * Click handler
157
+ */
158
+ onClick?: () => void;
159
+ /**
160
+ * Custom fallback element when cover image fails to load or is not provided.
161
+ * If not provided, a default vinyl disc SVG will be used.
162
+ *
163
+ * @example
164
+ * ```tsx
165
+ * <ImagePostSlotMusic
166
+ * fallbackCover={<MyCustomMusicIcon className="my-icon" />}
167
+ * />
168
+ * ```
169
+ */
170
+ fallbackCover?: ReactNode;
171
+ }
172
+ declare function ImagePostSlotMusic({ className, onClick, fallbackCover, }: ImagePostSlotMusicProps): ReactElement | null;
173
+ /**
174
+ * Props for ImagePostSlotPlayIndicator
175
+ * Uses same props as VideoSlotPlayIndicatorInner, minus context-derived props
176
+ */
177
+ type ImagePostSlotPlayIndicatorProps = Omit<VideoSlotPlayIndicatorInnerProps, 'lastUserToggleTime' | 'isPlaying'>;
178
+ /**
179
+ * ImagePostSlotPlayIndicator - Shows play/pause icon when user toggles music
180
+ *
181
+ * Reuses the same visual component as VideoSlot but reads from ImagePostSlot context.
182
+ * This ensures UI consistency across Video and Image posts.
183
+ *
184
+ * @example
185
+ * ```tsx
186
+ * <ImagePostSlotHeadless imagePost={imagePost}>
187
+ * <ImagePostSlotPlayIndicator persistWhenPaused />
188
+ * </ImagePostSlotHeadless>
189
+ * ```
190
+ */
191
+ declare function ImagePostSlotPlayIndicator(props: ImagePostSlotPlayIndicatorProps): ReactElement | null;
192
+ declare namespace ImagePostSlotPlayIndicator {
193
+ var displayName: string;
194
+ }
195
+
196
+ /**
197
+ * ImagePostSlot CSS - Injected at runtime
198
+ *
199
+ * Features:
200
+ * - Full-screen image post display
201
+ * - Overlay for author, caption, actions
202
+ * - Music bar (optional)
203
+ * - Read more button
204
+ */
205
+ declare const IMAGE_POST_SLOT_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 IMAGE POST SLOT - Container\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-image-post-slot {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n background: var(--sv-image-post-bg, #000);\n touch-action: pan-y;\n user-select: none;\n -webkit-user-select: none;\n}\n\n.sv-image-post-slot--active {\n z-index: 1;\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 IMAGE CONTAINER\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-image-post-slot__images {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\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 OVERLAY (Author, Caption, Actions)\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-image-post-slot__overlay {\n position: absolute;\n inset: 0;\n pointer-events: none;\n display: flex;\n flex-direction: column;\n z-index: 2;\n}\n\n.sv-image-post-slot__overlay > * {\n pointer-events: auto;\n}\n\n/* Gradient background for better text visibility */\n.sv-image-post-slot__gradient {\n position: absolute;\n left: 0;\n right: 0;\n pointer-events: none;\n}\n\n.sv-image-post-slot__gradient--top {\n top: 0;\n height: 120px;\n background: linear-gradient(\n to bottom,\n rgba(0, 0, 0, 0.5) 0%,\n rgba(0, 0, 0, 0.3) 50%,\n transparent 100%\n );\n}\n\n.sv-image-post-slot__gradient--bottom {\n bottom: 0;\n height: 200px;\n background: linear-gradient(\n to top,\n rgba(0, 0, 0, 0.7) 0%,\n rgba(0, 0, 0, 0.4) 50%,\n transparent 100%\n );\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 TOP BAR (Counter, Badge)\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-image-post-slot__top-bar {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n padding: var(--sv-image-post-top-padding, 16px);\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n z-index: 10;\n}\n\n.sv-image-post-slot__counter {\n padding: 4px 10px;\n background: var(--sv-image-post-counter-bg, rgba(0, 0, 0, 0.6));\n border-radius: 12px;\n font-size: var(--sv-image-post-counter-font-size, 12px);\n font-weight: 500;\n color: var(--sv-image-post-counter-color, #fff);\n}\n\n/* Image badge (e.g., \"Photo\") */\n.sv-image-post-slot__badge {\n padding: 4px 8px;\n background: var(--sv-image-post-badge-bg, rgba(255, 255, 255, 0.2));\n border-radius: 4px;\n font-size: var(--sv-image-post-badge-font-size, 11px);\n font-weight: 500;\n color: var(--sv-image-post-badge-color, #fff);\n text-transform: uppercase;\n letter-spacing: 0.5px;\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 BOTTOM CONTENT (Author, Caption, Music, Read More)\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-image-post-slot__bottom {\n position: absolute;\n bottom: 0;\n left: 0;\n right: var(--sv-image-post-action-bar-width, 72px);\n padding: var(--sv-image-post-bottom-padding, 16px);\n display: flex;\n flex-direction: column;\n gap: 12px;\n z-index: 5;\n}\n\n/* Author section */\n.sv-image-post-slot__author {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.sv-image-post-slot__author-avatar {\n width: var(--sv-image-post-avatar-size, 40px);\n height: var(--sv-image-post-avatar-size, 40px);\n border-radius: 50%;\n object-fit: cover;\n border: 2px solid var(--sv-image-post-avatar-border, rgba(255, 255, 255, 0.3));\n}\n\n.sv-image-post-slot__author-name {\n font-size: var(--sv-image-post-author-font-size, 15px);\n font-weight: 600;\n color: var(--sv-image-post-author-color, #fff);\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);\n}\n\n/* Caption section */\n.sv-image-post-slot__caption {\n font-size: var(--sv-image-post-caption-font-size, 14px);\n line-height: 1.4;\n color: var(--sv-image-post-caption-color, #fff);\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);\n display: -webkit-box;\n -webkit-line-clamp: var(--sv-image-post-caption-lines, 2);\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.sv-image-post-slot__caption--expanded {\n -webkit-line-clamp: unset;\n overflow: visible;\n}\n\n/* Read more button */\n.sv-image-post-slot__read-more {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 8px 16px;\n background: var(--sv-image-post-read-more-bg, rgba(255, 255, 255, 0.2));\n border: none;\n border-radius: 20px;\n font-size: var(--sv-image-post-read-more-font-size, 13px);\n font-weight: 500;\n color: var(--sv-image-post-read-more-color, #fff);\n cursor: pointer;\n transition: background 0.2s ease;\n width: fit-content;\n}\n\n.sv-image-post-slot__read-more:hover {\n background: var(--sv-image-post-read-more-hover-bg, rgba(255, 255, 255, 0.3));\n}\n\n.sv-image-post-slot__read-more svg {\n width: 16px;\n height: 16px;\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 ACTION BAR (Right side)\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-image-post-slot__actions {\n position: absolute;\n right: 0;\n bottom: 0;\n width: var(--sv-image-post-action-bar-width, 72px);\n padding: var(--sv-image-post-actions-padding, 16px 8px);\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 20px;\n z-index: 5;\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 MUSIC BAR\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-image-post-slot__music {\n overflow: hidden;\n aspect-ratio: 1/1;\n background: transparent;\n width: fit-content;\n height: fit-content;\n border: none;\n}\n\n.sv-image-post-slot__music-cover {\n width: 44px;\n height: 44px;\n object-fit: cover;\n border-radius: 999px;\n border: 2px solid var(--sv-image-post-music-border, rgba(255, 255, 255, 0.3));\n}\n\n.sv-image-post-slot__music-cover--playing {\n animation: sv-music-rotate 4s linear infinite;\n}\n\n@keyframes sv-music-rotate {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\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 LIKE ANIMATION\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-image-post-slot__like-animation {\n position: absolute;\n inset: 0;\n pointer-events: none;\n z-index: 20;\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 CLEAN MODE (Hide overlay)\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-image-post-slot--clean-mode .sv-image-post-slot__overlay,\n.sv-image-post-slot--clean-mode .sv-image-post-slot__top-bar,\n.sv-image-post-slot--clean-mode .sv-image-post-slot__bottom,\n.sv-image-post-slot--clean-mode .sv-image-post-slot__actions,\n.sv-image-post-slot--clean-mode .sv-image-post-slot__gradient {\n opacity: 0;\n transition: opacity 0.3s ease;\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 LOADING STATE\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-image-post-slot--loading .sv-image-post-slot__skeleton {\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0.05) 25%,\n rgba(255, 255, 255, 0.1) 50%,\n rgba(255, 255, 255, 0.05) 75%\n );\n background-size: 200% 100%;\n animation: sv-image-post-shimmer 1.5s infinite;\n}\n\n@keyframes sv-image-post-shimmer {\n 0% {\n background-position: 200% 0;\n }\n 100% {\n background-position: -200% 0;\n }\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 ACCESSIBILITY\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-image-post-slot:focus-visible {\n outline: 2px solid var(--sv-image-post-focus-color, #4dabff);\n outline-offset: -2px;\n}\n\n.sv-image-post-slot__sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n";
206
+
207
+ export { IMAGE_POST_ACTIVE_ATTR, IMAGE_POST_ID_ATTR, IMAGE_POST_SLOT_ACTIVE_CLASS, IMAGE_POST_SLOT_CLASS, IMAGE_POST_SLOT_CLEAN_MODE_CLASS, IMAGE_POST_SLOT_CSS, ImagePostSlotActions, type ImagePostSlotActionsProps, ImagePostSlotBottom, type ImagePostSlotBottomProps, ImagePostSlotCaption, type ImagePostSlotCaptionProps, type ImagePostSlotContextValue, ImagePostSlotHeadless, type ImagePostSlotHeadlessProps, ImagePostSlotMusic, type ImagePostSlotMusicProps, ImagePostSlotOverlay, type ImagePostSlotOverlayProps, ImagePostSlotPlayIndicator, type ImagePostSlotPlayIndicatorProps, ImagePostSlotReadMore, type ImagePostSlotReadMoreProps, useImagePostSlotContext, useOptionalImagePostSlotContext };
@@ -0,0 +1 @@
1
+ export { IMAGE_POST_ACTIVE_ATTR, IMAGE_POST_ID_ATTR, IMAGE_POST_SLOT_ACTIVE_CLASS, IMAGE_POST_SLOT_CLASS, IMAGE_POST_SLOT_CLEAN_MODE_CLASS, IMAGE_POST_SLOT_CSS, ImagePostSlotActions, ImagePostSlotBottom, ImagePostSlotCaption, ImagePostSlotHeadless, ImagePostSlotMusic, ImagePostSlotOverlay, ImagePostSlotPlayIndicator, ImagePostSlotReadMore, useImagePostSlotContext, useOptionalImagePostSlotContext } from '../../chunk-JEY6R4KJ.js';
@@ -1,3 +1,4 @@
1
+ import * as react from 'react';
1
2
  import { ProgressBarHeadlessProps } from '@xhub-short/contracts';
2
3
 
3
4
  interface ProgressBarHeadlessExtendedProps extends ProgressBarHeadlessProps {
@@ -16,6 +17,23 @@ interface ProgressBarHeadlessExtendedProps extends ProgressBarHeadlessProps {
16
17
  * @default false
17
18
  */
18
19
  minimal?: boolean;
20
+ /**
21
+ * Auto-hide progress bar when video is playing
22
+ *
23
+ * When enabled:
24
+ * - Hidden when video is playing
25
+ * - Visible when video is paused
26
+ * - Visible when user hovers (if seekable)
27
+ *
28
+ * @default true
29
+ */
30
+ autoHide?: boolean;
31
+ /**
32
+ * Whether progress bar should be visible (controlled by parent)
33
+ * Used by wired component to control visibility based on player state.
34
+ * Takes precedence over autoHide behavior.
35
+ */
36
+ visible?: boolean;
19
37
  /**
20
38
  * Ref to the fill element for direct DOM manipulation (ADR 005)
21
39
  * Used by wired component for 60fps smooth updates
@@ -64,8 +82,18 @@ declare function calculateProgress(currentTime: number, duration: number): numbe
64
82
  * - Headless version updates via React state (acceptable for demos)
65
83
  * - Wired version (P11) will use Direct DOM Update for 60fps smooth
66
84
  * - Fill element uses will-change: transform for GPU compositing
85
+ *
86
+ * Wrapped with React.memo to prevent unnecessary re-renders from parent.
87
+ * Re-render is expected during seek (local state changes).
88
+ */
89
+ declare function ProgressBarHeadlessBase({ currentTime, duration, buffered, seekable, className, onSeek, onSeekStart, onSeekEnd, showTime, showTooltip, minimal, autoHide, visible, fillRef: externalFillRef, bufferedRef: externalBufferedRef, handleRef: externalHandleRef, currentTimeRef: externalCurrentTimeRef, durationRef: externalDurationRef, }: ProgressBarHeadlessExtendedProps): React.ReactElement;
90
+ /**
91
+ * ProgressBarHeadless - Memoized for performance
92
+ *
93
+ * Custom compare function excludes frequently-changing time values since
94
+ * the wired component uses Direct DOM Update for those.
67
95
  */
68
- declare function ProgressBarHeadless({ currentTime, duration, buffered, seekable, className, onSeek, onSeekStart, onSeekEnd, showTime, showTooltip, minimal, fillRef: externalFillRef, bufferedRef: externalBufferedRef, handleRef: externalHandleRef, currentTimeRef: externalCurrentTimeRef, durationRef: externalDurationRef, }: ProgressBarHeadlessExtendedProps): React.ReactElement;
96
+ declare const ProgressBarHeadless: react.MemoExoticComponent<typeof ProgressBarHeadlessBase>;
69
97
 
70
98
  /**
71
99
  * CSS for ProgressBar Component
@@ -79,6 +107,6 @@ declare function ProgressBarHeadless({ currentTime, duration, buffered, seekable
79
107
  * - Avoid width changes (causes layout thrashing)
80
108
  * 5. Support touch/click seek interaction
81
109
  */
82
- declare const PROGRESS_BAR_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 * ProgressBar Container\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-progress-bar {\n /* Layout */\n position: relative;\n width: 100%;\n \n /* Interactive area - larger than visual for easier touch */\n height: var(--sv-progress-bar-touch-height, 24px);\n display: flex;\n align-items: center;\n \n /* Cursor */\n cursor: pointer;\n \n /* Prevent text selection during seek */\n user-select: none;\n -webkit-user-select: none;\n touch-action: none;\n}\n\n.sv-progress-bar--disabled {\n cursor: default;\n pointer-events: none;\n}\n\n/* \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\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\n * Track Wrapper (contains track + tooltip)\n * \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\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 */\n\n.sv-progress-bar__track-wrapper {\n position: relative;\n flex: 1;\n display: flex;\n align-items: center;\n}\n\n/* \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\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\n * Track (background bar)\n * \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\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 */\n\n.sv-progress-bar__track {\n position: relative;\n width: 100%;\n height: var(--sv-progress-bar-height, 3px);\n \n /* Visual */\n background: var(--sv-progress-bar-track-bg, rgba(255, 255, 255, 0.2));\n border-radius: var(--sv-progress-bar-radius, 1.5px);\n overflow: hidden;\n}\n\n/* Expand track on hover/active for better visibility */\n.sv-progress-bar:hover .sv-progress-bar__track,\n.sv-progress-bar--seeking .sv-progress-bar__track {\n height: var(--sv-progress-bar-height-active, 5px);\n}\n\n/* Disable transition during seeking for immediate feedback */\n.sv-progress-bar--seeking .sv-progress-bar__fill,\n.sv-progress-bar--seeking .sv-progress-bar__handle {\n transition: none;\n}\n\n/* \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\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\n * Buffered Fill (shows buffered progress)\n * \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\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 */\n\n.sv-progress-bar__buffered {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n \n /* Visual */\n background: var(--sv-progress-bar-buffered-bg, rgba(255, 255, 255, 0.3));\n border-radius: inherit;\n \n /* Performance: Use scaleX for GPU acceleration */\n transform-origin: left center;\n transform: scaleX(0);\n \n /* Smooth transition for buffered updates */\n transition: transform 0.3s ease-out;\n}\n\n/* \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\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\n * Progress Fill (shows playback progress)\n * IMPORTANT: Designed for Direct DOM Update (ADR 005)\n * - Uses scaleX() for 60fps smooth animation\n * - Wired component will update transform directly via DOM manipulation\n * \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\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 */\n\n.sv-progress-bar__fill {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n \n /* Visual */\n background: var(--sv-progress-bar-fill-bg, var(--sv-color-accent, #fe2c55));\n border-radius: inherit;\n \n /* Performance: Use scaleX for GPU acceleration */\n transform-origin: left center;\n transform: scaleX(0);\n \n /* Smooth transition for headless mode (timeupdate ~250ms intervals)\n * For RAF-based Direct DOM Update (wired mode), this has minimal impact\n * because transform updates every 16ms anyway */\n transition: var(--sv-progress-bar-fill-transition, transform 0.25s linear);\n will-change: transform;\n}\n\n/* \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\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\n * Seek Handle (thumb indicator)\n * \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\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 */\n\n.sv-progress-bar__handle {\n position: absolute;\n top: 50%;\n \n /* Size */\n width: var(--sv-progress-bar-handle-size, 12px);\n height: var(--sv-progress-bar-handle-size, 12px);\n \n /* Positioning - will be set via left % */\n left: 0;\n transform: translate(-50%, -50%) scale(0);\n \n /* Visual */\n background: var(--sv-progress-bar-handle-bg, #fff);\n border-radius: 50%;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n \n /* Animation - smooth position + show/hide */\n transition: left 0.25s linear, transform 0.15s ease-out;\n}\n\n/* Show handle on hover/seeking */\n.sv-progress-bar:hover .sv-progress-bar__handle,\n.sv-progress-bar--seeking .sv-progress-bar__handle {\n transform: translate(-50%, -50%) scale(1);\n}\n\n/* \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\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\n * Time Display (optional, shows current/duration)\n * \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\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 */\n\n.sv-progress-bar__time {\n display: flex;\n justify-content: space-between;\n padding: var(--sv-spacing-xs, 4px) 0;\n \n font-family: var(--sv-font-family, 'Urbanist', sans-serif);\n font-size: var(--sv-font-size-xs, 11px);\n font-variant-numeric: tabular-nums;\n color: var(--sv-text-secondary, rgba(255, 255, 255, 0.7));\n}\n\n.sv-progress-bar__time-current,\n.sv-progress-bar__time-duration {\n /* Ensure consistent width for changing numbers */\n min-width: 2.5em;\n}\n\n.sv-progress-bar__time-current {\n text-align: left;\n}\n\n.sv-progress-bar__time-duration {\n text-align: right;\n}\n\n/* \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\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\n * Seek Tooltip (shows time at cursor position during hover)\n * \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\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 */\n\n.sv-progress-bar__tooltip {\n position: absolute;\n /* Position above the container (not inside track to avoid overflow:hidden) */\n top: 0;\n left: 0;\n transform: translate(-50%, -100%);\n margin-top: -8px;\n \n /* Visual */\n padding: 4px 8px;\n background: var(--sv-bg-overlay, rgba(0, 0, 0, 0.8));\n border-radius: var(--sv-border-radius-sm, 4px);\n \n /* Typography */\n font-family: var(--sv-font-family, 'Urbanist', sans-serif);\n font-size: var(--sv-font-size-xs, 11px);\n font-variant-numeric: tabular-nums;\n color: var(--sv-text-primary, #fff);\n white-space: nowrap;\n \n /* Hidden by default */\n opacity: 0;\n visibility: hidden;\n transition: opacity 0.15s ease, visibility 0.15s ease;\n \n /* Prevent tooltip from capturing pointer events */\n pointer-events: none;\n \n /* Ensure tooltip is above other elements */\n z-index: 10;\n}\n\n.sv-progress-bar:hover .sv-progress-bar__tooltip,\n.sv-progress-bar--seeking .sv-progress-bar__tooltip {\n opacity: 1;\n visibility: visible;\n}\n\n/* \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\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\n * Minimal Variant (for use inside VideoSlot overlay)\n * \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\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 */\n\n.sv-progress-bar--minimal {\n height: auto;\n}\n\n.sv-progress-bar--minimal .sv-progress-bar__track {\n height: var(--sv-progress-bar-height-minimal, 2px);\n}\n\n.sv-progress-bar--minimal:hover .sv-progress-bar__track {\n height: var(--sv-progress-bar-height-minimal, 2px);\n}\n\n.sv-progress-bar--minimal .sv-progress-bar__handle {\n display: none;\n}\n\n/* \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\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\n * Reduced Motion (Accessibility)\n * \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\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 */\n\n@media (prefers-reduced-motion: reduce) {\n .sv-progress-bar__fill,\n .sv-progress-bar__buffered,\n .sv-progress-bar__handle,\n .sv-progress-bar__tooltip {\n transition: none;\n }\n}\n";
110
+ declare const PROGRESS_BAR_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 * ProgressBar Container\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-progress-bar {\n /* Layout */\n position: relative;\n width: 100%;\n \n /* Interactive area - larger than visual for easier touch */\n height: var(--sv-progress-bar-touch-height, 24px);\n display: flex;\n align-items: center;\n \n /* Cursor */\n cursor: pointer;\n \n /* Prevent text selection during seek */\n user-select: none;\n -webkit-user-select: none;\n touch-action: none;\n}\n\n.sv-progress-bar--disabled {\n cursor: default;\n pointer-events: none;\n}\n\n/* \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\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\n * Track Wrapper (contains track + tooltip)\n * \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\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 */\n\n.sv-progress-bar__track-wrapper {\n position: relative;\n flex: 1;\n display: flex;\n align-items: center;\n}\n\n/* \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\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\n * Track (background bar)\n * \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\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 */\n\n.sv-progress-bar__track {\n position: relative;\n width: 100%;\n height: var(--sv-progress-bar-height, 3px);\n \n /* Visual */\n background: var(--sv-progress-bar-track-bg, rgba(255, 255, 255, 0.2));\n border-radius: var(--sv-progress-bar-radius, 1.5px);\n overflow: hidden;\n}\n\n/* Expand track on hover/active for better visibility */\n.sv-progress-bar:hover .sv-progress-bar__track,\n.sv-progress-bar--seeking .sv-progress-bar__track {\n height: var(--sv-progress-bar-height-active, 5px);\n}\n\n/* Disable transition during seeking for immediate feedback */\n.sv-progress-bar--seeking .sv-progress-bar__fill,\n.sv-progress-bar--seeking .sv-progress-bar__handle {\n transition: none;\n}\n\n/* \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\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\n * Buffered Fill (shows buffered progress)\n * \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\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 */\n\n.sv-progress-bar__buffered {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n \n /* Visual */\n background: var(--sv-progress-bar-buffered-bg, rgba(255, 255, 255, 0.3));\n border-radius: inherit;\n \n /* Performance: Use scaleX for GPU acceleration */\n transform-origin: left center;\n transform: scaleX(0);\n \n /* Smooth transition for buffered updates */\n transition: transform 0.3s ease-out;\n}\n\n/* \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\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\n * Progress Fill (shows playback progress)\n * IMPORTANT: Designed for Direct DOM Update (ADR 005)\n * - Uses scaleX() for 60fps smooth animation\n * - Wired component will update transform directly via DOM manipulation\n * \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\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 */\n\n.sv-progress-bar__fill {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n \n /* Visual */\n background: var(--sv-progress-bar-fill-bg, var(--sv-color-accent, #fe2c55));\n border-radius: inherit;\n \n /* Performance: Use scaleX for GPU acceleration */\n transform-origin: left center;\n transform: scaleX(0);\n \n /* Smooth transition for headless mode (timeupdate ~250ms intervals)\n * For RAF-based Direct DOM Update (wired mode), this has minimal impact\n * because transform updates every 16ms anyway */\n transition: var(--sv-progress-bar-fill-transition, transform 0.25s linear);\n will-change: transform;\n}\n\n/* \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\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\n * Seek Handle (thumb indicator)\n * \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\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 */\n\n.sv-progress-bar__handle {\n position: absolute;\n top: 50%;\n \n /* Size */\n width: var(--sv-progress-bar-handle-size, 12px);\n height: var(--sv-progress-bar-handle-size, 12px);\n \n /* Positioning - will be set via left % */\n left: 0;\n transform: translate(-50%, -50%) scale(0);\n \n /* Visual */\n background: var(--sv-progress-bar-handle-bg, #fff);\n border-radius: 50%;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n \n /* Animation - smooth position + show/hide */\n transition: left 0.25s linear, transform 0.15s ease-out;\n}\n\n/* Show handle on hover/seeking */\n.sv-progress-bar:hover .sv-progress-bar__handle,\n.sv-progress-bar--seeking .sv-progress-bar__handle {\n transform: translate(-50%, -50%) scale(1);\n}\n\n/* \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\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\n * Time Display (optional, shows current/duration)\n * \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\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 */\n\n.sv-progress-bar__time {\n display: flex;\n justify-content: space-between;\n padding: var(--sv-spacing-xs, 4px) 0;\n \n font-family: var(--sv-font-family, 'Urbanist', sans-serif);\n font-size: var(--sv-font-size-xs, 11px);\n font-variant-numeric: tabular-nums;\n color: var(--sv-text-secondary, rgba(255, 255, 255, 0.7));\n}\n\n.sv-progress-bar__time-current,\n.sv-progress-bar__time-duration {\n /* Ensure consistent width for changing numbers */\n min-width: 2.5em;\n}\n\n.sv-progress-bar__time-current {\n text-align: left;\n}\n\n.sv-progress-bar__time-duration {\n text-align: right;\n}\n\n/* \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\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\n * Seek Tooltip (shows time at cursor position during hover)\n * \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\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 */\n\n.sv-progress-bar__tooltip {\n position: absolute;\n /* Position above the container (not inside track to avoid overflow:hidden) */\n top: 0;\n left: 0;\n transform: translate(-50%, -100%);\n margin-top: -8px;\n \n /* Visual */\n padding: 4px 8px;\n background: var(--sv-bg-overlay, rgba(0, 0, 0, 0.8));\n border-radius: var(--sv-border-radius-sm, 4px);\n \n /* Typography */\n font-family: var(--sv-font-family, 'Urbanist', sans-serif);\n font-size: var(--sv-font-size-xs, 11px);\n font-variant-numeric: tabular-nums;\n color: var(--sv-text-primary, #fff);\n white-space: nowrap;\n \n /* Hidden by default */\n opacity: 0;\n visibility: hidden;\n transition: opacity 0.15s ease, visibility 0.15s ease;\n \n /* Prevent tooltip from capturing pointer events */\n pointer-events: none;\n \n /* Ensure tooltip is above other elements */\n z-index: 10;\n}\n\n.sv-progress-bar:hover .sv-progress-bar__tooltip,\n.sv-progress-bar--seeking .sv-progress-bar__tooltip {\n opacity: 1;\n visibility: visible;\n}\n\n/* \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\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\n * Minimal Variant (for use inside VideoSlot overlay)\n * \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\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 */\n\n.sv-progress-bar--minimal {\n height: auto;\n}\n\n.sv-progress-bar--minimal .sv-progress-bar__track {\n height: var(--sv-progress-bar-height-minimal, 2px);\n}\n\n.sv-progress-bar--minimal:hover .sv-progress-bar__track {\n height: var(--sv-progress-bar-height-minimal, 2px);\n}\n\n.sv-progress-bar--minimal .sv-progress-bar__handle {\n display: none;\n}\n\n/* \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\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\n * Auto-hide Variant\n * Shows progress bar only when video is paused or user is seeking/hovering\n * \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\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 */\n\n.sv-progress-bar--auto-hide {\n /* Smooth transition for show/hide */\n transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s ease;\n opacity: 1;\n visibility: visible;\n transform: translateY(0);\n}\n\n.sv-progress-bar--auto-hide.sv-progress-bar--hidden {\n opacity: 0;\n visibility: hidden;\n transform: translateY(8px);\n pointer-events: none;\n}\n\n/* Show on hover even when auto-hide is enabled */\n.sv-progress-bar--auto-hide:hover {\n opacity: 1;\n visibility: visible;\n transform: translateY(0);\n}\n\n/* Always show when seeking */\n.sv-progress-bar--auto-hide.sv-progress-bar--seeking {\n opacity: 1;\n visibility: visible;\n transform: translateY(0);\n}\n\n/* \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\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\n * Reduced Motion (Accessibility)\n * \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\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 */\n\n@media (prefers-reduced-motion: reduce) {\n .sv-progress-bar__fill,\n .sv-progress-bar__buffered,\n .sv-progress-bar__handle,\n .sv-progress-bar__tooltip {\n transition: none;\n }\n}\n";
83
111
 
84
112
  export { PROGRESS_BAR_CSS, ProgressBarHeadless, type ProgressBarHeadlessExtendedProps, calculateProgress, formatTime };
@@ -1 +1 @@
1
- export { PROGRESS_BAR_CSS, ProgressBarHeadless, calculateProgress, formatTime } from '../../chunk-YW23IBKF.js';
1
+ export { PROGRESS_BAR_CSS, ProgressBarHeadless, calculateProgress, formatTime } from '../../chunk-VXW7AOGM.js';
@@ -0,0 +1,35 @@
1
+ import * as react from 'react';
2
+ import { ReactElement } from 'react';
3
+ import { VideoQuality } from '@xhub-short/contracts';
4
+
5
+ interface QualityPickerHeadlessProps {
6
+ /** Current quality level (-1 = auto) */
7
+ quality: number;
8
+ /** Available quality levels from HLS.js */
9
+ levels: VideoQuality[];
10
+ /** Quality change callback (level index, -1 for auto) */
11
+ onQualityChange: (level: number) => void;
12
+ /** Title text (default: "Quality") */
13
+ title?: string;
14
+ /** Custom class name */
15
+ className?: string;
16
+ /** Close callback (optional, for integration with menu) */
17
+ onClose?: () => void;
18
+ /** Auto-close after selection (default: true) */
19
+ autoClose?: boolean;
20
+ /** Show "Auto" option (default: true) */
21
+ showAuto?: boolean;
22
+ /** Custom label for auto (default: "Auto") */
23
+ autoLabel?: string;
24
+ /** Empty state message */
25
+ emptyMessage?: string;
26
+ }
27
+ declare function QualityPickerHeadlessComponent({ quality, levels, onQualityChange, title, className, onClose, autoClose, showAuto, autoLabel, emptyMessage, }: QualityPickerHeadlessProps): ReactElement;
28
+ declare const QualityPickerHeadless: react.MemoExoticComponent<typeof QualityPickerHeadlessComponent>;
29
+
30
+ /**
31
+ * QualityPicker CSS
32
+ */
33
+ declare const QUALITY_PICKER_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\n CSS VARIABLES\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 */\n\n.sv-quality-picker {\n /* Colors */\n --sv-quality-picker-bg: rgba(28, 28, 30, 0.95);\n --sv-quality-picker-text: #ffffff;\n --sv-quality-picker-text-secondary: rgba(255, 255, 255, 0.6);\n --sv-quality-picker-selected: #007aff;\n --sv-quality-picker-item-hover: rgba(255, 255, 255, 0.1);\n --sv-quality-picker-check: #007aff;\n --sv-quality-picker-badge: rgba(255, 255, 255, 0.15);\n \n /* Dimensions */\n --sv-quality-picker-item-height: 48px;\n --sv-quality-picker-radius: 12px;\n --sv-quality-picker-padding: 8px;\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\n CONTAINER\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 */\n\n.sv-quality-picker {\n padding: var(--sv-quality-picker-padding);\n}\n\n.sv-quality-picker-title {\n padding: 8px 16px;\n font-size: 14px;\n font-weight: 600;\n color: var(--sv-quality-picker-text-secondary);\n text-align: center;\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\n OPTIONS\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 */\n\n.sv-quality-picker-options {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.sv-quality-picker-option {\n display: flex;\n align-items: center;\n gap: 12px;\n height: var(--sv-quality-picker-item-height);\n padding: 0 16px;\n border: none;\n background: transparent;\n color: var(--sv-quality-picker-text);\n font-size: 16px;\n font-weight: 400;\n cursor: pointer;\n border-radius: var(--sv-quality-picker-radius);\n transition: background-color 150ms ease;\n -webkit-tap-highlight-color: transparent;\n}\n\n.sv-quality-picker-option:hover {\n background: var(--sv-quality-picker-item-hover);\n}\n\n.sv-quality-picker-option[data-selected=\"true\"] {\n color: var(--sv-quality-picker-selected);\n font-weight: 500;\n}\n\n.sv-quality-picker-option-label {\n flex: 1;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.sv-quality-picker-option-badge {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n padding: 2px 6px;\n background: var(--sv-quality-picker-badge);\n border-radius: 4px;\n color: var(--sv-quality-picker-text-secondary);\n}\n\n.sv-quality-picker-option[data-selected=\"true\"] .sv-quality-picker-option-badge {\n background: var(--sv-quality-picker-selected);\n color: #ffffff;\n}\n\n.sv-quality-picker-option-check {\n width: 20px;\n height: 20px;\n color: var(--sv-quality-picker-check);\n opacity: 0;\n transition: opacity 150ms ease;\n}\n\n.sv-quality-picker-option[data-selected=\"true\"] .sv-quality-picker-option-check {\n opacity: 1;\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\n EMPTY STATE\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 */\n\n.sv-quality-picker-empty {\n padding: 24px 16px;\n text-align: center;\n color: var(--sv-quality-picker-text-secondary);\n font-size: 14px;\n}\n";
34
+
35
+ export { QUALITY_PICKER_CSS, QualityPickerHeadless, type QualityPickerHeadlessProps };
@@ -0,0 +1 @@
1
+ export { QUALITY_PICKER_CSS, QualityPickerHeadless } from '../../chunk-DR7KR7OT.js';
@@ -0,0 +1,68 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ReactElement } from 'react';
3
+ import { ReportReason } from '@xhub-short/contracts';
4
+
5
+ type ReportSheetStep = 'select' | 'detail';
6
+ type ReportSheetState = 'form' | 'loading' | 'success' | 'error';
7
+ interface ReportSheetHeadlessProps {
8
+ /** Whether the sheet is open */
9
+ isOpen: boolean;
10
+ /** Close handler */
11
+ onClose: () => void;
12
+ /** Submit handler */
13
+ onSubmit: (reasonId: string, description?: string) => Promise<void>;
14
+ /** Available report reasons */
15
+ reasons?: ReportReason[];
16
+ /** Whether reasons are being loaded from API */
17
+ isLoadingReasons?: boolean;
18
+ /** Max characters for text input (default: 500) */
19
+ maxLength?: number;
20
+ /** Content ID being reported (for display) */
21
+ contentId?: string;
22
+ /**
23
+ * If true, show success screen immediately (video was already reported)
24
+ * Used to prevent re-reporting the same video
25
+ */
26
+ alreadyReported?: boolean;
27
+ /** Sheet title for step 1 - reason selection */
28
+ selectTitle?: string;
29
+ /** Sheet title for step 2 - report detail */
30
+ title?: string;
31
+ /** Text input placeholder */
32
+ inputPlaceholder?: string;
33
+ /** Submit button text */
34
+ submitText?: string;
35
+ /** Loading text */
36
+ loadingText?: string;
37
+ /** Success title */
38
+ successTitle?: string;
39
+ /** Success message */
40
+ successMessage?: string;
41
+ /** Done button text (on success screen) */
42
+ doneText?: string;
43
+ /** Error message */
44
+ errorMessage?: string;
45
+ /** Error description (retry hint) */
46
+ errorDescription?: string;
47
+ /** Custom success icon */
48
+ successIcon?: ReactNode;
49
+ /** Custom class name */
50
+ className?: string;
51
+ }
52
+ declare function ReportSheetHeadlessComponent({ isOpen, onClose, onSubmit, reasons, isLoadingReasons, maxLength, alreadyReported, selectTitle, title, inputPlaceholder, submitText, loadingText, successTitle, successMessage, doneText, errorMessage, errorDescription, successIcon, className, }: ReportSheetHeadlessProps): ReactElement | null;
53
+ declare const ReportSheetHeadless: react.MemoExoticComponent<typeof ReportSheetHeadlessComponent>;
54
+
55
+ /**
56
+ * ReportSheet CSS-in-TS
57
+ *
58
+ * Report form bottom sheet styling (2-step flow).
59
+ * Built on BottomSheetHeadless base.
60
+ */
61
+ declare const REPORT_SHEET_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\n CSS VARIABLES\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 */\n\n.sv-report-sheet {\n /* Colors */\n --sv-report-bg: #1a1a1a;\n --sv-report-text: rgba(255, 255, 255, 0.95);\n --sv-report-text-muted: rgba(255, 255, 255, 0.6);\n --sv-report-border: rgba(255, 255, 255, 0.1);\n --sv-report-error: #ef4444;\n --sv-report-success: #22c55e;\n --sv-report-primary: #3b82f6;\n --sv-report-item-hover: rgba(255, 255, 255, 0.05);\n \n /* Spacing */\n --sv-report-padding: 16px;\n --sv-report-gap: 12px;\n --sv-report-radius: 8px;\n \n /* Override BottomSheet */\n --sv-sheet-bg: var(--sv-report-bg);\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\n HEADER\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 */\n\n.sv-report-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: var(--sv-report-padding);\n border-bottom: 1px solid var(--sv-report-border);\n min-height: 56px;\n}\n\n.sv-report-title {\n flex: 1;\n font-size: 17px;\n font-weight: 600;\n color: var(--sv-report-text);\n margin: 0;\n text-align: center;\n}\n\n/* When back button is present, title should still be centered */\n.sv-report-header .sv-report-back + .sv-report-title {\n margin-left: -32px; /* Compensate for back button */\n}\n\n.sv-report-close,\n.sv-report-back {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n flex-shrink: 0;\n \n background: transparent;\n border: none;\n border-radius: 50%;\n \n color: var(--sv-report-text-muted);\n cursor: pointer;\n transition: background 150ms ease, color 150ms ease;\n}\n\n.sv-report-close:hover,\n.sv-report-back:hover {\n background: var(--sv-report-item-hover);\n color: var(--sv-report-text);\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\n CONTENT\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 */\n\n.sv-report-content {\n padding: 0;\n max-height: 50vh;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.sv-report-content--detail {\n padding: var(--sv-report-padding);\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\n STEP 1: REASON LIST\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 */\n\n.sv-report-reasons {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.sv-report-reason-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n width: 100%;\n padding: 16px var(--sv-report-padding);\n \n background: transparent;\n border: none;\n border-bottom: 1px solid var(--sv-report-border);\n \n color: var(--sv-report-text);\n font-size: 15px;\n text-align: left;\n \n cursor: pointer;\n transition: background 150ms ease;\n}\n\n.sv-report-reason-item:last-child {\n border-bottom: none;\n}\n\n.sv-report-reason-item:hover {\n background: var(--sv-report-item-hover);\n}\n\n.sv-report-reason-item:active {\n background: rgba(255, 255, 255, 0.08);\n}\n\n.sv-report-reason-item .sv-report-reason-label {\n flex: 1;\n}\n\n.sv-report-reason-item svg {\n flex-shrink: 0;\n color: var(--sv-report-text-muted);\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\n SKELETON LOADING\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 */\n\n.sv-report-reasons--loading {\n pointer-events: none;\n}\n\n.sv-report-reason-skeleton {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 16px var(--sv-report-padding);\n border-bottom: 1px solid var(--sv-report-border);\n}\n\n.sv-report-reason-skeleton:last-child {\n border-bottom: none;\n}\n\n.sv-report-skeleton-text {\n flex: 1;\n height: 16px;\n max-width: 200px;\n background: linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0.06) 25%,\n rgba(255, 255, 255, 0.12) 50%,\n rgba(255, 255, 255, 0.06) 75%\n );\n background-size: 200% 100%;\n border-radius: 4px;\n animation: sv-report-skeleton-shimmer 1.5s infinite;\n}\n\n.sv-report-skeleton-icon {\n width: 20px;\n height: 20px;\n background: rgba(255, 255, 255, 0.06);\n border-radius: 4px;\n}\n\n@keyframes sv-report-skeleton-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\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\n STEP 2: REASON DETAIL\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 */\n\n.sv-report-reason-title {\n margin: 0 0 var(--sv-report-gap);\n font-size: 16px;\n font-weight: 600;\n color: var(--sv-report-text);\n}\n\n.sv-report-reason-description {\n margin: 0 0 var(--sv-report-gap);\n padding: 12px 16px;\n \n background: rgba(255, 255, 255, 0.05);\n border-radius: var(--sv-report-radius);\n \n font-size: 14px;\n color: var(--sv-report-text-muted);\n line-height: 1.5;\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\n TEXT INPUT\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 */\n\n.sv-report-input-section {\n margin-top: var(--sv-report-gap);\n}\n\n.sv-report-textarea {\n width: 100%;\n min-height: 120px;\n padding: 12px;\n \n background: rgba(255, 255, 255, 0.05);\n border: 1px solid var(--sv-report-border);\n border-radius: var(--sv-report-radius);\n \n color: var(--sv-report-text);\n font-size: 14px;\n font-family: inherit;\n line-height: 1.5;\n resize: vertical;\n \n transition: border-color 150ms ease;\n}\n\n.sv-report-textarea::placeholder {\n color: var(--sv-report-text-muted);\n}\n\n.sv-report-textarea:focus {\n outline: none;\n border-color: var(--sv-report-primary);\n}\n\n.sv-report-char-count {\n margin-top: 4px;\n font-size: 12px;\n color: var(--sv-report-text-muted);\n text-align: right;\n}\n\n.sv-report-char-count[data-over=\"true\"] {\n color: var(--sv-report-error);\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\n FOOTER\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 */\n\n.sv-report-footer {\n display: flex;\n gap: 12px;\n padding: var(--sv-report-padding);\n border-top: 1px solid var(--sv-report-border);\n}\n\n.sv-report-footer--single {\n /* Single button layout */\n}\n\n.sv-report-button {\n flex: 1;\n padding: 14px 20px;\n \n border: none;\n border-radius: var(--sv-report-radius);\n \n font-size: 15px;\n font-weight: 600;\n \n cursor: pointer;\n transition: background 150ms ease, opacity 150ms ease;\n}\n\n.sv-report-button-submit {\n background: var(--sv-report-primary);\n color: white;\n}\n\n.sv-report-button-submit:hover:not(:disabled) {\n background: #2563eb;\n}\n\n.sv-report-button-submit:active:not(:disabled) {\n background: #1d4ed8;\n}\n\n.sv-report-button-submit:disabled {\n opacity: 0.5;\n cursor: not-allowed;\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\n LOADING & SUCCESS STATES\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 */\n\n.sv-report-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--sv-report-text-muted);\n}\n\n.sv-report-spinner {\n width: 24px;\n height: 24px;\n border: 2px solid var(--sv-report-border);\n border-top-color: var(--sv-report-primary);\n border-radius: 50%;\n animation: sv-report-spin 0.8s linear infinite;\n}\n\n@keyframes sv-report-spin {\n to { transform: rotate(360deg); }\n}\n\n.sv-report-success {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n padding: 48px 24px;\n text-align: center;\n}\n\n.sv-report-success-icon {\n width: 48px;\n height: 48px;\n color: var(--sv-report-success);\n}\n\n.sv-report-success-title {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--sv-report-text);\n}\n\n.sv-report-success-message {\n margin: 0;\n font-size: 14px;\n color: var(--sv-report-text-muted);\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\n ERROR STATE\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 */\n\n.sv-report-error-message {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px;\n margin-bottom: var(--sv-report-gap);\n \n background: rgba(239, 68, 68, 0.1);\n border-radius: var(--sv-report-radius);\n \n color: var(--sv-report-error);\n font-size: 14px;\n}\n";
62
+ /**
63
+ * Inject ReportSheet CSS into document
64
+ * Note: layered=false to prevent host app CSS from overriding critical styles
65
+ */
66
+ declare function injectReportSheetCSS(): void;
67
+
68
+ export { REPORT_SHEET_CSS, ReportSheetHeadless, type ReportSheetHeadlessProps, type ReportSheetState, type ReportSheetStep, injectReportSheetCSS };
@@ -0,0 +1 @@
1
+ export { REPORT_SHEET_CSS, ReportSheetHeadless, injectReportSheetCSS } from '../../chunk-QCKVF2DR.js';
@@ -1 +1 @@
1
- export { Skeleton, SkeletonAvatar, SkeletonText, SkeletonVideo } from '../../chunk-4MN72OZH.js';
1
+ export { Skeleton, SkeletonAvatar, SkeletonText, SkeletonVideo } from '../../chunk-4RIMQOBR.js';
@@ -0,0 +1,32 @@
1
+ import * as react from 'react';
2
+ import { ReactElement } from 'react';
3
+
4
+ /** Default speed presets */
5
+ declare const DEFAULT_SPEEDS: readonly [0.5, 0.75, 1, 1.25, 1.5, 2];
6
+ interface SpeedPickerHeadlessProps {
7
+ /** Current speed */
8
+ speed: number;
9
+ /** Speed change callback */
10
+ onSpeedChange: (speed: number) => void;
11
+ /** Available speeds (default: DEFAULT_SPEEDS) */
12
+ speeds?: readonly number[];
13
+ /** Title text (default: "Playback Speed") */
14
+ title?: string;
15
+ /** Custom class name */
16
+ className?: string;
17
+ /** Format speed label (default: "1x", "0.5x") */
18
+ formatLabel?: (speed: number) => string;
19
+ /** Close callback (optional, for integration with menu) */
20
+ onClose?: () => void;
21
+ /** Auto-close after selection (default: true) */
22
+ autoClose?: boolean;
23
+ }
24
+ declare function SpeedPickerHeadlessComponent({ speed, onSpeedChange, speeds, title, className, formatLabel, onClose, autoClose, }: SpeedPickerHeadlessProps): ReactElement;
25
+ declare const SpeedPickerHeadless: react.MemoExoticComponent<typeof SpeedPickerHeadlessComponent>;
26
+
27
+ /**
28
+ * SpeedPicker CSS
29
+ */
30
+ declare const SPEED_PICKER_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\n CSS VARIABLES\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 */\n\n.sv-speed-picker {\n /* Colors */\n --sv-speed-picker-bg: rgba(28, 28, 30, 0.95);\n --sv-speed-picker-text: #ffffff;\n --sv-speed-picker-text-secondary: rgba(255, 255, 255, 0.6);\n --sv-speed-picker-selected: #007aff;\n --sv-speed-picker-item-hover: rgba(255, 255, 255, 0.1);\n --sv-speed-picker-check: #007aff;\n \n /* Dimensions */\n --sv-speed-picker-item-height: 48px;\n --sv-speed-picker-radius: 12px;\n --sv-speed-picker-padding: 8px;\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\n CONTAINER\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 */\n\n.sv-speed-picker {\n padding: var(--sv-speed-picker-padding);\n}\n\n.sv-speed-picker-title {\n padding: 8px 16px;\n font-size: 14px;\n font-weight: 600;\n color: var(--sv-speed-picker-text-secondary);\n text-align: center;\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\n OPTIONS\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 */\n\n.sv-speed-picker-options {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.sv-speed-picker-option {\n display: flex;\n align-items: center;\n justify-content: space-between;\n height: var(--sv-speed-picker-item-height);\n padding: 0 16px;\n border: none;\n background: transparent;\n color: var(--sv-speed-picker-text);\n font-size: 16px;\n font-weight: 400;\n cursor: pointer;\n border-radius: var(--sv-speed-picker-radius);\n transition: background-color 150ms ease;\n -webkit-tap-highlight-color: transparent;\n}\n\n.sv-speed-picker-option:hover {\n background: var(--sv-speed-picker-item-hover);\n}\n\n.sv-speed-picker-option[data-selected=\"true\"] {\n color: var(--sv-speed-picker-selected);\n font-weight: 500;\n}\n\n.sv-speed-picker-option-label {\n flex: 1;\n}\n\n.sv-speed-picker-option-check {\n width: 20px;\n height: 20px;\n color: var(--sv-speed-picker-check);\n opacity: 0;\n transition: opacity 150ms ease;\n}\n\n.sv-speed-picker-option[data-selected=\"true\"] .sv-speed-picker-option-check {\n opacity: 1;\n}\n";
31
+
32
+ export { DEFAULT_SPEEDS, SPEED_PICKER_CSS, SpeedPickerHeadless, type SpeedPickerHeadlessProps };
@@ -0,0 +1 @@
1
+ export { DEFAULT_SPEEDS, SPEED_PICKER_CSS, SpeedPickerHeadless } from '../../chunk-QCRRF76W.js';
@@ -8,6 +8,11 @@ import { ReactNode } from 'react';
8
8
  * Position-based virtual scroller for video feed (TikTok-style).
9
9
  * Receives state via props (headless pattern).
10
10
  *
11
+ * This component wraps VirtualSlider<VideoItem> and adds:
12
+ * - Video-specific context (VideoFeedContext)
13
+ * - Video-specific CSS classes (sv-video-feed prefix)
14
+ * - Backwards-compatible API
15
+ *
11
16
  * Features:
12
17
  * - Virtual scrolling (only renders visible + buffer slots)
13
18
  * - CSS transform-based scrolling (60fps)
@@ -39,7 +44,7 @@ interface VideoFeedHeadlessProps {
39
44
  /** Additional CSS classes */
40
45
  className?: string;
41
46
  /** Render function for each video slot */
42
- renderSlot: (video: VideoItem, index: number) => ReactNode;
47
+ renderSlot: (video: VideoItem, index: number, isActive: boolean) => ReactNode;
43
48
  /** Called when active index changes */
44
49
  onIndexChange?: (index: number) => void;
45
50
  /** Called when reaching near end of feed */
@@ -72,8 +77,14 @@ interface VideoFeedHeadlessProps {
72
77
  *
73
78
  * Renders a vertical video feed with virtual scrolling.
74
79
  * Position-based: each video takes full viewport height.
80
+ *
81
+ * Internally uses VirtualSlider<VideoItem> for the core slider logic,
82
+ * adding video-specific context and styling.
75
83
  */
76
84
  declare function VideoFeedHeadless({ feedState, swipeState, height, className, renderSlot, onIndexChange, onEndReached, endReachedThreshold, bufferSize, loadingText, emptyText, endText, disableInlineTransforms, }: VideoFeedHeadlessProps): React.ReactElement;
85
+ declare namespace VideoFeedHeadless {
86
+ var displayName: string;
87
+ }
77
88
 
78
89
  /**
79
90
  * VideoFeed context value
@@ -1 +1 @@
1
- export { SLOT_ACTIVE_ATTR, SLOT_ACTIVE_DATASET_KEY, SLOT_INDEX_ATTR, SLOT_INDEX_DATASET_KEY, VIDEO_FEED_CLASS_PREFIX, VIDEO_FEED_CSS, VideoFeedContext, VideoFeedHeadless, getSlotIndexFromPosition, useOptionalVideoFeedContext, useVideoFeedContext, useVideoFeedPosition } from '../../chunk-ZZDQKP4R.js';
1
+ export { SLOT_ACTIVE_ATTR, SLOT_ACTIVE_DATASET_KEY, SLOT_INDEX_ATTR, SLOT_INDEX_DATASET_KEY, VIDEO_FEED_CLASS_PREFIX, VIDEO_FEED_CSS, VideoFeedContext, VideoFeedHeadless, getSlotIndexFromPosition, useOptionalVideoFeedContext, useVideoFeedContext, useVideoFeedPosition } from '../../chunk-4TUBNA2X.js';