@xhub-short/ui 0.1.0-beta.9 → 1.0.0-beta.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CommentSheet.css-DuBy01rU.d.ts +219 -0
- package/dist/VideoSlotPlayIndicator-DPs8Xt5C.d.ts +51 -0
- package/dist/chunk-3OB3OVYR.js +349 -0
- package/dist/chunk-4RIMQOBR.js +58 -0
- package/dist/chunk-5MKYDI4X.js +1 -0
- package/dist/chunk-5Y43HNRG.js +296 -0
- package/dist/chunk-7WXAQHJI.js +350 -0
- package/dist/chunk-BADA7OLG.js +564 -0
- package/dist/chunk-BEJAJFV6.js +191 -0
- package/dist/chunk-BNI7CYRI.js +334 -0
- package/dist/{chunk-RMLTPW5S.js → chunk-CAWE42LH.js} +4 -3
- package/dist/chunk-DGKMO3AE.js +717 -0
- package/dist/chunk-DR7KR7OT.js +103 -0
- package/dist/chunk-GSNIZ6DF.js +605 -0
- package/dist/chunk-HXQPEZRG.js +105 -0
- package/dist/{chunk-FNXTPQ6L.js → chunk-IC2KUU4V.js} +31 -1340
- package/dist/chunk-IWSBYOSS.js +91 -0
- package/dist/chunk-MFJS65C5.js +368 -0
- package/dist/{chunk-AC2IFAJR.js → chunk-NJXIYSDZ.js} +12 -1
- package/dist/{chunk-CL6BS7GB.js → chunk-OM4L7RE5.js} +12 -2
- package/dist/chunk-QCRRF76W.js +75 -0
- package/dist/chunk-QUEJHA24.js +508 -0
- package/dist/chunk-UYBQTE4M.js +337 -0
- package/dist/chunk-VJ744W5N.js +603 -0
- package/dist/chunk-VXW7AOGM.js +285 -0
- package/dist/{chunk-AQHD6LPS.js → chunk-YB7AXTX7.js} +1 -1
- package/dist/components/ActionBar/index.d.ts +7 -2
- package/dist/components/ActionBar/index.js +1 -1
- package/dist/components/AdvanceMenu/index.d.ts +80 -0
- package/dist/components/AdvanceMenu/index.js +1 -0
- package/dist/components/ArticleSlot/index.d.ts +213 -0
- package/dist/components/ArticleSlot/index.js +1 -0
- package/dist/components/AuthorInfo/index.js +1 -1
- package/dist/components/BottomSheet/index.d.ts +87 -0
- package/dist/components/BottomSheet/index.js +1 -0
- package/dist/components/CleanModeOverlay/index.d.ts +60 -0
- package/dist/components/CleanModeOverlay/index.js +1 -0
- package/dist/components/CommentSheet/index.d.ts +1 -1
- package/dist/components/CommentSheet/index.js +1 -1
- package/dist/components/DetailView/index.d.ts +314 -0
- package/dist/components/DetailView/index.js +1 -0
- package/dist/components/ErrorBoundary/index.js +1 -1
- package/dist/components/{VideoFeed → Feed}/index.d.ts +64 -45
- package/dist/components/Feed/index.js +1 -0
- package/dist/components/ImageCarousel/index.d.ts +50 -0
- package/dist/components/ImageCarousel/index.js +1 -0
- package/dist/components/Playlist/index.d.ts +117 -0
- package/dist/components/Playlist/index.js +1 -0
- package/dist/components/ProgressBar/index.js +1 -1
- package/dist/components/QualityPicker/index.d.ts +35 -0
- package/dist/components/QualityPicker/index.js +1 -0
- package/dist/components/ReportSheet/index.d.ts +74 -0
- package/dist/components/ReportSheet/index.js +1 -0
- package/dist/components/Skeleton/index.js +1 -1
- package/dist/components/SpeedPicker/index.d.ts +32 -0
- package/dist/components/SpeedPicker/index.js +1 -0
- package/dist/components/VideoInfo/index.d.ts +7 -3
- package/dist/components/VideoInfo/index.js +1 -1
- package/dist/components/VideoPlayer/index.js +1 -1
- package/dist/components/VideoSlot/index.d.ts +31 -68
- package/dist/components/VideoSlot/index.js +2 -1
- package/dist/components/VirtualSlider/index.js +1 -1
- package/dist/index.d.ts +40 -10
- package/dist/index.js +23 -13
- package/package.json +8 -4
- package/dist/CommentSheet.css-BeCrEaUG.d.ts +0 -221
- package/dist/chunk-2FSDVYER.js +0 -737
- package/dist/chunk-ECR42RKK.js +0 -571
- package/dist/chunk-KWHMZ6H5.js +0 -562
- package/dist/chunk-SZXFH334.js +0 -204
- package/dist/chunk-UNV3NWN6.js +0 -148
- package/dist/chunk-WCRDTBCZ.js +0 -357
- package/dist/chunk-XDIH66C4.js +0 -1519
- package/dist/components/VideoFeed/index.js +0 -1
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactElement, ReactNode } from 'react';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
import { Article, MusicInfo } from '@xhub-short/contracts';
|
|
5
|
+
|
|
6
|
+
interface DetailViewContextValue {
|
|
7
|
+
/** The article data */
|
|
8
|
+
article: Article;
|
|
9
|
+
/** Current image index */
|
|
10
|
+
currentImageIndex: number;
|
|
11
|
+
/** Set current image index */
|
|
12
|
+
setCurrentImageIndex: (index: number) => void;
|
|
13
|
+
/** Close detail view */
|
|
14
|
+
close: () => void;
|
|
15
|
+
/** Whether view is animating */
|
|
16
|
+
isAnimating: boolean;
|
|
17
|
+
/** Actions */
|
|
18
|
+
actions: {
|
|
19
|
+
onLike?: () => void;
|
|
20
|
+
onComment?: () => void;
|
|
21
|
+
onShare?: () => void;
|
|
22
|
+
onBookmark?: () => void;
|
|
23
|
+
onFollow?: () => void;
|
|
24
|
+
onGestureLock?: (isLocked: boolean) => void;
|
|
25
|
+
};
|
|
26
|
+
/** States */
|
|
27
|
+
states: {
|
|
28
|
+
isLiked: boolean;
|
|
29
|
+
isBookmarked: boolean;
|
|
30
|
+
isFollowing: boolean;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Hook to access DetailView context
|
|
35
|
+
* @throws if used outside DetailViewHeadless
|
|
36
|
+
*/
|
|
37
|
+
declare function useDetailViewContext(): DetailViewContextValue;
|
|
38
|
+
declare function BackIcon(): react_jsx_runtime.JSX.Element;
|
|
39
|
+
declare function HeartIcon({ filled }: {
|
|
40
|
+
filled?: boolean;
|
|
41
|
+
}): react_jsx_runtime.JSX.Element;
|
|
42
|
+
declare function CommentIcon(): react_jsx_runtime.JSX.Element;
|
|
43
|
+
declare function BookmarkIcon({ filled }: {
|
|
44
|
+
filled?: boolean;
|
|
45
|
+
}): react_jsx_runtime.JSX.Element;
|
|
46
|
+
declare function ShareIcon(): react_jsx_runtime.JSX.Element;
|
|
47
|
+
interface DetailViewHeadlessProps {
|
|
48
|
+
/** Whether the detail view is open */
|
|
49
|
+
isOpen: boolean;
|
|
50
|
+
/** Close handler */
|
|
51
|
+
onClose: () => void;
|
|
52
|
+
/** The article to display */
|
|
53
|
+
article: Article;
|
|
54
|
+
/** Initial image index */
|
|
55
|
+
initialImageIndex?: number;
|
|
56
|
+
/** Animation duration in ms */
|
|
57
|
+
animationDuration?: number;
|
|
58
|
+
/** Enable swipe down to close */
|
|
59
|
+
enableSwipeToClose?: boolean;
|
|
60
|
+
/** Swipe threshold (percentage) to trigger close */
|
|
61
|
+
swipeCloseThreshold?: number;
|
|
62
|
+
/** Custom header title (fallback if no author) */
|
|
63
|
+
headerTitle?: string;
|
|
64
|
+
/** Show drag indicator */
|
|
65
|
+
showDragIndicator?: boolean;
|
|
66
|
+
/** Whether post is liked */
|
|
67
|
+
isLiked?: boolean;
|
|
68
|
+
/** Whether post is bookmarked */
|
|
69
|
+
isBookmarked?: boolean;
|
|
70
|
+
/** Whether following author */
|
|
71
|
+
isFollowing?: boolean;
|
|
72
|
+
/** Like button handler */
|
|
73
|
+
onLike?: () => void;
|
|
74
|
+
/** Comment button handler */
|
|
75
|
+
onComment?: () => void;
|
|
76
|
+
/** Share button handler */
|
|
77
|
+
onShare?: () => void;
|
|
78
|
+
/** Bookmark button handler */
|
|
79
|
+
onBookmark?: () => void;
|
|
80
|
+
/** Follow button handler */
|
|
81
|
+
onFollow?: () => void;
|
|
82
|
+
/** Callback to lock/unlock global gestures */
|
|
83
|
+
onGestureLock?: (isLocked: boolean) => void;
|
|
84
|
+
/**
|
|
85
|
+
* Custom bottom bar renderer.
|
|
86
|
+
* When provided, replaces the entire default bottom bar.
|
|
87
|
+
* Use compound components (DetailViewBottomBar, DetailViewActionButton, etc.)
|
|
88
|
+
* to build custom layouts.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```tsx
|
|
92
|
+
* <DetailViewHeadless
|
|
93
|
+
* renderBottomBar={() => (
|
|
94
|
+
* <DetailViewBottomBar>
|
|
95
|
+
* <DetailViewActionButton type="like" />
|
|
96
|
+
* <DetailViewActionButton type="share" />
|
|
97
|
+
* </DetailViewBottomBar>
|
|
98
|
+
* )}
|
|
99
|
+
* />
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
renderBottomBar?: () => ReactNode;
|
|
103
|
+
/**
|
|
104
|
+
* Hide the default bottom bar completely.
|
|
105
|
+
* Use when you want to render your own bottom bar via children.
|
|
106
|
+
*/
|
|
107
|
+
hideBottomBar?: boolean;
|
|
108
|
+
/** Additional class name */
|
|
109
|
+
className?: string;
|
|
110
|
+
/** Children */
|
|
111
|
+
children?: ReactNode;
|
|
112
|
+
}
|
|
113
|
+
declare function formatCount(count: number): string;
|
|
114
|
+
declare function DetailViewHeadlessBase({ isOpen, onClose, article, initialImageIndex, animationDuration, enableSwipeToClose, swipeCloseThreshold, headerTitle, showDragIndicator, isLiked, isBookmarked, isFollowing, onLike, onComment, onShare, onBookmark, onFollow, renderBottomBar, hideBottomBar, onGestureLock, className, children, }: DetailViewHeadlessProps): ReactElement | null;
|
|
115
|
+
declare const DetailViewHeadless: react.MemoExoticComponent<typeof DetailViewHeadlessBase>;
|
|
116
|
+
/**
|
|
117
|
+
* Image Gallery for detail view
|
|
118
|
+
*/
|
|
119
|
+
interface DetailViewGalleryProps {
|
|
120
|
+
/** Enable zoom on images */
|
|
121
|
+
enableZoom?: boolean;
|
|
122
|
+
/** Class name */
|
|
123
|
+
className?: string;
|
|
124
|
+
}
|
|
125
|
+
declare function DetailViewGallery({ enableZoom, className, }: DetailViewGalleryProps): ReactElement;
|
|
126
|
+
/**
|
|
127
|
+
* Author section for detail view
|
|
128
|
+
*/
|
|
129
|
+
interface DetailViewAuthorProps {
|
|
130
|
+
/** Whether user is following this author */
|
|
131
|
+
isFollowing?: boolean;
|
|
132
|
+
/** Follow button click handler */
|
|
133
|
+
onFollowClick?: () => void;
|
|
134
|
+
/** Show follow button */
|
|
135
|
+
showFollowButton?: boolean;
|
|
136
|
+
/** Format date function */
|
|
137
|
+
formatDate?: (date: Date | string) => string;
|
|
138
|
+
/** Follow button label */
|
|
139
|
+
followLabel?: string;
|
|
140
|
+
/** Following button label */
|
|
141
|
+
followingLabel?: string;
|
|
142
|
+
/** Class name */
|
|
143
|
+
className?: string;
|
|
144
|
+
}
|
|
145
|
+
declare function DetailViewAuthor({ isFollowing, onFollowClick, showFollowButton, formatDate, followLabel, followingLabel, className, }: DetailViewAuthorProps): ReactElement | null;
|
|
146
|
+
/**
|
|
147
|
+
* Caption display for detail view
|
|
148
|
+
*/
|
|
149
|
+
interface DetailViewCaptionProps {
|
|
150
|
+
/** Render hashtags as links */
|
|
151
|
+
renderHashtag?: (hashtag: string) => ReactNode;
|
|
152
|
+
/** Class name */
|
|
153
|
+
className?: string;
|
|
154
|
+
}
|
|
155
|
+
declare function DetailViewCaption({ renderHashtag, className, }: DetailViewCaptionProps): ReactElement | null;
|
|
156
|
+
/**
|
|
157
|
+
* Stats display for detail view
|
|
158
|
+
*/
|
|
159
|
+
interface DetailViewStatsProps {
|
|
160
|
+
/** Format number function */
|
|
161
|
+
formatNumber?: (num: number) => string;
|
|
162
|
+
/** Labels */
|
|
163
|
+
labels?: {
|
|
164
|
+
likes?: string;
|
|
165
|
+
comments?: string;
|
|
166
|
+
shares?: string;
|
|
167
|
+
views?: string;
|
|
168
|
+
};
|
|
169
|
+
/** Class name */
|
|
170
|
+
className?: string;
|
|
171
|
+
}
|
|
172
|
+
declare function DetailViewStats({ formatNumber, labels, className, }: DetailViewStatsProps): ReactElement;
|
|
173
|
+
/**
|
|
174
|
+
* Music bar for detail view
|
|
175
|
+
*/
|
|
176
|
+
interface DetailViewMusicProps {
|
|
177
|
+
/** Music info */
|
|
178
|
+
music?: MusicInfo;
|
|
179
|
+
/** Whether music is playing */
|
|
180
|
+
isPlaying?: boolean;
|
|
181
|
+
/** Click handler */
|
|
182
|
+
onClick?: () => void;
|
|
183
|
+
/** Class name */
|
|
184
|
+
className?: string;
|
|
185
|
+
}
|
|
186
|
+
declare function DetailViewMusic({ music, isPlaying, onClick, className, }: DetailViewMusicProps): ReactElement | null;
|
|
187
|
+
/**
|
|
188
|
+
* Bottom bar container
|
|
189
|
+
*
|
|
190
|
+
* Use this to wrap custom bottom bar content.
|
|
191
|
+
* Handles event propagation and styling.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```tsx
|
|
195
|
+
* <DetailViewHeadless
|
|
196
|
+
* renderBottomBar={() => (
|
|
197
|
+
* <DetailViewBottomBar>
|
|
198
|
+
* <DetailViewCommentInput />
|
|
199
|
+
* <DetailViewActions>
|
|
200
|
+
* <DetailViewActionButton type="like" />
|
|
201
|
+
* <DetailViewActionButton type="share" />
|
|
202
|
+
* </DetailViewActions>
|
|
203
|
+
* </DetailViewBottomBar>
|
|
204
|
+
* )}
|
|
205
|
+
* />
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
interface DetailViewBottomBarProps {
|
|
209
|
+
/** Children */
|
|
210
|
+
children?: ReactNode;
|
|
211
|
+
/** Class name */
|
|
212
|
+
className?: string;
|
|
213
|
+
}
|
|
214
|
+
declare function DetailViewBottomBar({ children, className, }: DetailViewBottomBarProps): ReactElement;
|
|
215
|
+
/**
|
|
216
|
+
* Comment input for bottom bar
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```tsx
|
|
220
|
+
* <DetailViewCommentInput
|
|
221
|
+
* placeholder="Write a comment..."
|
|
222
|
+
* onSubmit={(value) => submitComment(value)}
|
|
223
|
+
* />
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
interface DetailViewCommentInputProps {
|
|
227
|
+
/** Placeholder text */
|
|
228
|
+
placeholder?: string;
|
|
229
|
+
/** Called when user submits comment (presses Enter) */
|
|
230
|
+
onSubmit?: (value: string) => void;
|
|
231
|
+
/** Class name */
|
|
232
|
+
className?: string;
|
|
233
|
+
}
|
|
234
|
+
declare function DetailViewCommentInput({ placeholder, onSubmit, className, }: DetailViewCommentInputProps): ReactElement;
|
|
235
|
+
/**
|
|
236
|
+
* Actions container for bottom bar
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```tsx
|
|
240
|
+
* <DetailViewActions>
|
|
241
|
+
* <DetailViewActionButton type="like" />
|
|
242
|
+
* <DetailViewActionButton type="comment" />
|
|
243
|
+
* </DetailViewActions>
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
interface DetailViewActionsProps {
|
|
247
|
+
/** Children (action buttons) */
|
|
248
|
+
children?: ReactNode;
|
|
249
|
+
/** Class name */
|
|
250
|
+
className?: string;
|
|
251
|
+
}
|
|
252
|
+
declare function DetailViewActions({ children, className }: DetailViewActionsProps): ReactElement;
|
|
253
|
+
/**
|
|
254
|
+
* Action button types
|
|
255
|
+
*/
|
|
256
|
+
type DetailViewActionType = 'like' | 'comment' | 'bookmark' | 'share';
|
|
257
|
+
/**
|
|
258
|
+
* Single action button for bottom bar
|
|
259
|
+
*
|
|
260
|
+
* Can be used with predefined types (like, comment, bookmark, share)
|
|
261
|
+
* or with custom icon/content.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```tsx
|
|
265
|
+
* // Using predefined type
|
|
266
|
+
* <DetailViewActionButton type="like" isActive={isLiked} count={123} onClick={handleLike} />
|
|
267
|
+
*
|
|
268
|
+
* // Using custom icon
|
|
269
|
+
* <DetailViewActionButton icon={<MyCustomIcon />} onClick={handleCustomAction} />
|
|
270
|
+
* ```
|
|
271
|
+
*/
|
|
272
|
+
interface DetailViewActionButtonProps {
|
|
273
|
+
/** Predefined action type */
|
|
274
|
+
type?: DetailViewActionType;
|
|
275
|
+
/** Custom icon (overrides type) */
|
|
276
|
+
icon?: ReactNode;
|
|
277
|
+
/** Whether button is active (e.g., liked, bookmarked) */
|
|
278
|
+
isActive?: boolean;
|
|
279
|
+
/** Count to display */
|
|
280
|
+
count?: number;
|
|
281
|
+
/** Click handler */
|
|
282
|
+
onClick?: () => void;
|
|
283
|
+
/** Aria label */
|
|
284
|
+
ariaLabel?: string;
|
|
285
|
+
/** Class name */
|
|
286
|
+
className?: string;
|
|
287
|
+
/** Children (additional content) */
|
|
288
|
+
children?: ReactNode;
|
|
289
|
+
}
|
|
290
|
+
declare function DetailViewActionButton({ type, icon, isActive, count, onClick, ariaLabel, className, children, }: DetailViewActionButtonProps): ReactElement;
|
|
291
|
+
/**
|
|
292
|
+
* Export icons for host apps that want to build custom bottom bars
|
|
293
|
+
* but still use consistent icons.
|
|
294
|
+
*/
|
|
295
|
+
declare const DetailViewIcons: {
|
|
296
|
+
Back: typeof BackIcon;
|
|
297
|
+
Heart: typeof HeartIcon;
|
|
298
|
+
Comment: typeof CommentIcon;
|
|
299
|
+
Bookmark: typeof BookmarkIcon;
|
|
300
|
+
Share: typeof ShareIcon;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* DetailView CSS - Injected at runtime
|
|
305
|
+
*
|
|
306
|
+
* Features:
|
|
307
|
+
* - Full-screen detail view
|
|
308
|
+
* - Expand/collapse animation
|
|
309
|
+
* - Scrollable content
|
|
310
|
+
* - Back gesture (swipe down)
|
|
311
|
+
*/
|
|
312
|
+
declare const DETAIL_VIEW_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 DETAIL VIEW - 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-detail-view {\n position: fixed;\n inset: 0;\n z-index: var(--sv-detail-view-z-index, 1000);\n background: var(--sv-detail-view-bg, #000);\n overflow: hidden;\n touch-action: none;\n}\n\n/* States */\n.sv-detail-view--entering {\n animation: sv-detail-view-enter 0.3s cubic-bezier(0.32, 0.72, 0, 1) forwards;\n}\n\n.sv-detail-view--exiting {\n animation: sv-detail-view-exit 0.3s cubic-bezier(0.32, 0.72, 0, 1) forwards;\n}\n\n.sv-detail-view--dragging {\n transition: none;\n}\n\n@keyframes sv-detail-view-enter {\n from {\n opacity: 0;\n transform: translateY(100%) scale(0.9);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n@keyframes sv-detail-view-exit {\n from {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n to {\n opacity: 0;\n transform: translateY(100%) scale(0.9);\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 BACKDROP\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-detail-view__backdrop {\n position: absolute;\n inset: 0;\n background: var(--sv-detail-view-backdrop-bg, rgba(0, 0, 0, 0.8));\n opacity: 1;\n transition: opacity 0.3s ease;\n}\n\n.sv-detail-view--exiting .sv-detail-view__backdrop {\n opacity: 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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n CONTENT 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-detail-view__container {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n will-change: transform;\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 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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-detail-view__header {\n position: sticky;\n top: 0;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--sv-detail-view-header-padding, 12px 16px);\n background: var(--sv-detail-view-header-bg, rgba(0, 0, 0, 0.8));\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n}\n\n.sv-detail-view__back-button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: var(--sv-detail-view-back-bg, rgba(255, 255, 255, 0.1));\n color: var(--sv-detail-view-back-color, #fff);\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.sv-detail-view__back-button:hover {\n background: var(--sv-detail-view-back-hover-bg, rgba(255, 255, 255, 0.2));\n}\n\n.sv-detail-view__back-button svg {\n width: 24px;\n height: 24px;\n}\n\n.sv-detail-view__title {\n font-size: var(--sv-detail-view-title-font-size, 16px);\n font-weight: 600;\n color: var(--sv-detail-view-title-color, #fff);\n}\n\n.sv-detail-view__actions {\n display: flex;\n gap: 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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n DRAG INDICATOR\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-detail-view__drag-indicator {\n position: absolute;\n top: 8px;\n left: 50%;\n transform: translateX(-50%);\n width: 36px;\n height: 5px;\n background: var(--sv-detail-view-drag-indicator-bg, rgba(255, 255, 255, 0.3));\n border-radius: 3px;\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 SCROLL 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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.sv-detail-view__scroll {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch;\n overscroll-behavior-y: contain;\n /* Allow vertical scroll but block horizontal (prevents Feed swipe) */\n touch-action: pan-y;\n /* Padding bottom for bottom bar */\n padding-bottom: 70px;\n}\n\n.sv-detail-view__content {\n min-height: 100%;\n /* No horizontal padding - gallery should be full width */\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 GALLERY\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-detail-view__gallery {\n position: relative;\n width: 100%;\n aspect-ratio: 1;\n background: var(--sv-detail-view-gallery-bg, #111);\n overflow: hidden;\n}\n\n.sv-detail-view__gallery-image {\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n\n/* Gallery pagination */\n.sv-detail-view__gallery-pagination {\n position: absolute;\n bottom: 12px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n gap: 6px;\n padding: 6px 12px;\n background: rgba(0, 0, 0, 0.5);\n border-radius: 20px;\n}\n\n.sv-detail-view__gallery-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.5);\n border: none;\n padding: 0;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.sv-detail-view__gallery-dot--active {\n background: #fff;\n transform: scale(1.2);\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 AUTHOR SECTION\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-detail-view__author {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 0;\n border-bottom: 1px solid var(--sv-detail-view-border-color, rgba(255, 255, 255, 0.1));\n}\n\n.sv-detail-view__author-avatar {\n width: 44px;\n height: 44px;\n border-radius: 50%;\n object-fit: cover;\n}\n\n.sv-detail-view__author-info {\n flex: 1;\n}\n\n.sv-detail-view__author-name {\n font-size: 15px;\n font-weight: 600;\n color: var(--sv-detail-view-text-color, #fff);\n}\n\n.sv-detail-view__author-date {\n font-size: 13px;\n color: var(--sv-detail-view-secondary-color, rgba(255, 255, 255, 0.6));\n margin-top: 2px;\n}\n\n.sv-detail-view__follow-button {\n padding: 8px 20px;\n background: var(--sv-detail-view-follow-bg, #fe2c55);\n border: none;\n border-radius: 4px;\n font-size: 14px;\n font-weight: 600;\n color: #fff;\n cursor: pointer;\n transition: opacity 0.2s ease;\n}\n\n.sv-detail-view__follow-button:hover {\n opacity: 0.9;\n}\n\n.sv-detail-view__follow-button--following {\n background: var(--sv-detail-view-following-bg, rgba(255, 255, 255, 0.1));\n color: var(--sv-detail-view-following-color, #fff);\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 CAPTION SECTION\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-detail-view__caption {\n padding: 16px;\n font-size: var(--sv-detail-view-caption-font-size, 15px);\n line-height: 1.5;\n color: var(--sv-detail-view-text-color, #fff);\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* Hashtags in caption */\n.sv-detail-view__hashtag {\n color: var(--sv-detail-view-hashtag-color, #4dabff);\n cursor: pointer;\n}\n\n.sv-detail-view__hashtag:hover {\n text-decoration: underline;\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 STATS SECTION\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-detail-view__stats {\n display: flex;\n gap: 24px;\n padding: 16px 0;\n border-top: 1px solid var(--sv-detail-view-border-color, rgba(255, 255, 255, 0.1));\n}\n\n.sv-detail-view__stat {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 14px;\n color: var(--sv-detail-view-secondary-color, rgba(255, 255, 255, 0.6));\n}\n\n.sv-detail-view__stat-value {\n font-weight: 600;\n color: var(--sv-detail-view-text-color, #fff);\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 (Bottom)\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-detail-view__action-bar {\n position: sticky;\n bottom: 0;\n display: flex;\n justify-content: space-around;\n padding: 12px 16px;\n background: var(--sv-detail-view-action-bar-bg, rgba(0, 0, 0, 0.9));\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n border-top: 1px solid var(--sv-detail-view-border-color, rgba(255, 255, 255, 0.1));\n}\n\n.sv-detail-view__action {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n padding: 8px 16px;\n border: none;\n background: transparent;\n color: var(--sv-detail-view-action-color, #fff);\n cursor: pointer;\n transition: opacity 0.2s ease;\n}\n\n.sv-detail-view__action:hover {\n opacity: 0.8;\n}\n\n.sv-detail-view__action--active {\n color: var(--sv-detail-view-action-active-color, #fe2c55);\n}\n\n.sv-detail-view__action svg {\n width: 24px;\n height: 24px;\n}\n\n.sv-detail-view__action-label {\n font-size: 12px;\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-detail-view__music {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n margin: 0 16px 16px;\n background: var(--sv-detail-view-music-bg, rgba(255, 255, 255, 0.05));\n border-radius: 8px;\n border: none;\n width: calc(100% - 32px);\n cursor: pointer;\n text-align: left;\n}\n\n.sv-detail-view__music-cover {\n width: 48px;\n height: 48px;\n border-radius: 4px;\n object-fit: cover;\n}\n\n.sv-detail-view__music-info {\n flex: 1;\n overflow: hidden;\n}\n\n.sv-detail-view__music-title {\n font-size: 14px;\n font-weight: 500;\n color: var(--sv-detail-view-text-color, #fff);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sv-detail-view__music-artist {\n font-size: 12px;\n color: var(--sv-detail-view-secondary-color, rgba(255, 255, 255, 0.6));\n margin-top: 2px;\n}\n\n.sv-detail-view__music-icon {\n width: 24px;\n height: 24px;\n color: var(--sv-detail-view-secondary-color, rgba(255, 255, 255, 0.6));\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 HEADER - Author Info (Plan Layout)\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-detail-view__header-author {\n display: flex;\n align-items: center;\n gap: 10px;\n flex: 1;\n min-width: 0;\n margin-left: 12px;\n}\n\n.sv-detail-view__header-avatar {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n object-fit: cover;\n flex-shrink: 0;\n}\n\n.sv-detail-view__header-name {\n font-size: 15px;\n font-weight: 600;\n color: var(--sv-detail-view-text-color, #fff);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sv-detail-view__follow-btn {\n padding: 6px 16px;\n font-size: 13px;\n font-weight: 600;\n border: none;\n border-radius: 4px;\n background: var(--sv-detail-view-follow-bg, #fe2c55);\n color: #fff;\n cursor: pointer;\n transition: background 0.2s ease, opacity 0.2s ease;\n flex-shrink: 0;\n}\n\n.sv-detail-view__follow-btn:hover {\n opacity: 0.9;\n}\n\n.sv-detail-view__follow-btn--following {\n background: var(--sv-detail-view-following-bg, rgba(255, 255, 255, 0.12));\n color: var(--sv-detail-view-text-color, #fff);\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 ACTION BAR (Plan Layout)\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-detail-view__bottom-bar {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n padding-bottom: max(10px, env(safe-area-inset-bottom));\n background: var(--sv-detail-view-bottom-bar-bg, rgba(0, 0, 0, 0.95));\n border-top: 1px solid var(--sv-detail-view-border-color, rgba(255, 255, 255, 0.1));\n z-index: 20;\n}\n\n.sv-detail-view__comment-input {\n flex: 1;\n display: flex;\n align-items: center;\n background: var(--sv-detail-view-input-bg, rgba(255, 255, 255, 0.08));\n border-radius: 18px;\n min-width: 0;\n overflow: hidden;\n}\n\n.sv-detail-view__bottom-bar .sv-detail-view__comment-input input,\n.sv-detail-view__comment-field,\ninput.sv-detail-view__comment-field {\n flex: 1;\n width: 100%;\n height: auto;\n padding: 10px 14px;\n margin: 0;\n border: none;\n border-radius: 0;\n background: transparent;\n background-color: transparent;\n font-family: inherit;\n font-size: 14px;\n line-height: 1.4;\n color: var(--sv-detail-view-text-color, #fff);\n outline: none;\n box-shadow: none;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.sv-detail-view__bottom-bar .sv-detail-view__comment-input input::placeholder,\n.sv-detail-view__comment-field::placeholder,\ninput.sv-detail-view__comment-field::placeholder {\n color: var(--sv-detail-view-placeholder-color, rgba(255, 255, 255, 0.5));\n opacity: 1;\n}\n\n.sv-detail-view__bottom-bar .sv-detail-view__comment-input input:focus,\n.sv-detail-view__comment-field:focus,\ninput.sv-detail-view__comment-field:focus {\n outline: none;\n box-shadow: none;\n border: none;\n}\n\n.sv-detail-view__bottom-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n}\n\n.sv-detail-view__action-btn {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n min-width: 40px;\n padding: 4px 6px;\n background: none;\n border: none;\n color: var(--sv-detail-view-text-color, #fff);\n cursor: pointer;\n transition: transform 0.15s ease, color 0.15s ease;\n}\n\n.sv-detail-view__action-btn:hover {\n transform: scale(1.05);\n}\n\n.sv-detail-view__action-btn:active {\n transform: scale(0.95);\n}\n\n.sv-detail-view__action-btn--active {\n color: var(--sv-detail-view-active-color, #fe2c55);\n}\n\n.sv-detail-view__action-btn svg {\n width: 22px;\n height: 22px;\n}\n\n.sv-detail-view__action-count {\n font-size: 11px;\n margin-top: 2px;\n color: var(--sv-detail-view-secondary-color, rgba(255, 255, 255, 0.7));\n}\n\n.sv-detail-view__action-btn--active .sv-detail-view__action-count {\n color: var(--sv-detail-view-active-color, #fe2c55);\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-detail-view:focus-visible {\n outline: 2px solid var(--sv-detail-view-focus-color, #4dabff);\n outline-offset: -2px;\n}\n\n.sv-detail-view__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";
|
|
313
|
+
|
|
314
|
+
export { DETAIL_VIEW_CSS, DetailViewActionButton, type DetailViewActionButtonProps, type DetailViewActionType, DetailViewActions, type DetailViewActionsProps, DetailViewAuthor, type DetailViewAuthorProps, DetailViewBottomBar, type DetailViewBottomBarProps, DetailViewCaption, type DetailViewCaptionProps, DetailViewCommentInput, type DetailViewCommentInputProps, type DetailViewContextValue, DetailViewGallery, type DetailViewGalleryProps, DetailViewHeadless, type DetailViewHeadlessProps, DetailViewIcons, DetailViewMusic, type DetailViewMusicProps, DetailViewStats, type DetailViewStatsProps, formatCount as formatDetailViewCount, useDetailViewContext };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DETAIL_VIEW_CSS, DetailViewActionButton, DetailViewActions, DetailViewAuthor, DetailViewBottomBar, DetailViewCaption, DetailViewCommentInput, DetailViewGallery, DetailViewHeadless, DetailViewIcons, DetailViewMusic, DetailViewStats, formatCount as formatDetailViewCount, useDetailViewContext } from '../../chunk-BADA7OLG.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { ErrorBoundary } from '../../chunk-
|
|
1
|
+
export { ErrorBoundary } from '../../chunk-IWSBYOSS.js';
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { UIFeedState, UISwipeState, VideoItem } from '@xhub-short/contracts';
|
|
1
|
+
import { UIFeedState, UISwipeState, ContentItem, VideoItem } from '@xhub-short/contracts';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* FeedHeadless Component
|
|
7
7
|
*
|
|
8
|
-
* Position-based virtual scroller for
|
|
8
|
+
* Position-based virtual scroller for mixed content feed (Videos, Articles, etc.).
|
|
9
9
|
* Receives state via props (headless pattern).
|
|
10
10
|
*
|
|
11
|
-
* This component wraps VirtualSlider<
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
11
|
+
* This component wraps VirtualSlider<ContentItem> and adds:
|
|
12
|
+
* - Feed-specific context (FeedContext)
|
|
13
|
+
* - Feed-specific CSS classes (sv-feed prefix)
|
|
14
14
|
* - Backwards-compatible API
|
|
15
15
|
*
|
|
16
16
|
* Features:
|
|
@@ -22,19 +22,19 @@ import { ReactNode } from 'react';
|
|
|
22
22
|
*
|
|
23
23
|
* @example
|
|
24
24
|
* ```tsx
|
|
25
|
-
* <
|
|
25
|
+
* <FeedHeadless
|
|
26
26
|
* feedState={feedState}
|
|
27
27
|
* swipeState={swipeState}
|
|
28
|
-
* renderSlot={(
|
|
28
|
+
* renderSlot={(item, index) => <ArticleSlot item={item} />}
|
|
29
29
|
* onEndReached={() => feedState.loadMore()}
|
|
30
30
|
* />
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Props for
|
|
35
|
+
* Props for FeedHeadless
|
|
36
36
|
*/
|
|
37
|
-
interface
|
|
37
|
+
interface FeedHeadlessProps {
|
|
38
38
|
/** Feed state (videos, loading, etc.) */
|
|
39
39
|
feedState: UIFeedState;
|
|
40
40
|
/** Swipe/scroll state */
|
|
@@ -43,8 +43,8 @@ interface VideoFeedHeadlessProps {
|
|
|
43
43
|
height?: number | string;
|
|
44
44
|
/** Additional CSS classes */
|
|
45
45
|
className?: string;
|
|
46
|
-
/** Render function for each
|
|
47
|
-
renderSlot: (
|
|
46
|
+
/** Render function for each content slot */
|
|
47
|
+
renderSlot: (item: ContentItem, index: number, isActive: boolean) => ReactNode;
|
|
48
48
|
/** Called when active index changes */
|
|
49
49
|
onIndexChange?: (index: number) => void;
|
|
50
50
|
/** Called when reaching near end of feed */
|
|
@@ -62,8 +62,8 @@ interface VideoFeedHeadlessProps {
|
|
|
62
62
|
/**
|
|
63
63
|
* Disable inline transform styles on slots.
|
|
64
64
|
*
|
|
65
|
-
* When true,
|
|
66
|
-
* to slot elements. This allows parent components (like
|
|
65
|
+
* When true, FeedHeadless will NOT apply inline `transform` styles
|
|
66
|
+
* to slot elements. This allows parent components (like Feed wired)
|
|
67
67
|
* to control transforms entirely via direct DOM manipulation.
|
|
68
68
|
*
|
|
69
69
|
* Use this when implementing 60fps swipe with ref-based transforms.
|
|
@@ -71,26 +71,45 @@ interface VideoFeedHeadlessProps {
|
|
|
71
71
|
* @default false
|
|
72
72
|
*/
|
|
73
73
|
disableInlineTransforms?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Custom empty state component.
|
|
76
|
+
* Receives `text` prop (localized empty text).
|
|
77
|
+
*/
|
|
78
|
+
renderEmpty?: (text: string) => ReactNode;
|
|
79
|
+
/**
|
|
80
|
+
* Custom loading state component.
|
|
81
|
+
* Receives `text` prop (localized loading text).
|
|
82
|
+
*/
|
|
83
|
+
renderLoading?: (text: string) => ReactNode;
|
|
84
|
+
/**
|
|
85
|
+
* Custom end of feed component.
|
|
86
|
+
* Receives `text` prop (localized end text).
|
|
87
|
+
*/
|
|
88
|
+
renderEnd?: (text: string) => ReactNode;
|
|
74
89
|
}
|
|
90
|
+
/** @deprecated Use FeedHeadlessProps instead. Will be removed in v3.0 */
|
|
91
|
+
type VideoFeedHeadlessProps = FeedHeadlessProps;
|
|
75
92
|
/**
|
|
76
|
-
*
|
|
93
|
+
* Feed Headless Component
|
|
77
94
|
*
|
|
78
|
-
* Renders a vertical
|
|
79
|
-
* Position-based: each
|
|
95
|
+
* Renders a vertical feed with virtual scrolling.
|
|
96
|
+
* Position-based: each item takes full viewport height.
|
|
80
97
|
*
|
|
81
|
-
* Internally uses VirtualSlider<
|
|
82
|
-
* adding
|
|
98
|
+
* Internally uses VirtualSlider<ContentItem> for the core slider logic,
|
|
99
|
+
* adding feed-specific context and styling.
|
|
83
100
|
*/
|
|
84
|
-
declare function
|
|
85
|
-
declare namespace
|
|
101
|
+
declare function FeedHeadless({ feedState, swipeState, height, className, renderSlot, onIndexChange, onEndReached, endReachedThreshold, bufferSize, renderEmpty, renderLoading, renderEnd, loadingText, emptyText, endText, disableInlineTransforms, }: FeedHeadlessProps): React.ReactElement;
|
|
102
|
+
declare namespace FeedHeadless {
|
|
86
103
|
var displayName: string;
|
|
87
104
|
}
|
|
88
105
|
|
|
89
106
|
/**
|
|
90
|
-
*
|
|
107
|
+
* Feed context value
|
|
91
108
|
*/
|
|
92
|
-
interface
|
|
93
|
-
/** List of
|
|
109
|
+
interface FeedContextValue {
|
|
110
|
+
/** List of items */
|
|
111
|
+
items: ContentItem[];
|
|
112
|
+
/** @deprecated Use items instead. Will be removed in v3.0 */
|
|
94
113
|
videos: VideoItem[];
|
|
95
114
|
/** Current active index */
|
|
96
115
|
activeIndex: number;
|
|
@@ -102,28 +121,28 @@ interface VideoFeedContextValue {
|
|
|
102
121
|
containerHeight: number;
|
|
103
122
|
/** Go to specific index */
|
|
104
123
|
goToIndex: (index: number) => void;
|
|
105
|
-
/** Total
|
|
124
|
+
/** Total item count */
|
|
106
125
|
totalCount: number;
|
|
107
126
|
}
|
|
108
127
|
/**
|
|
109
|
-
*
|
|
128
|
+
* Feed context (internal use)
|
|
110
129
|
*/
|
|
111
|
-
declare const
|
|
130
|
+
declare const FeedContext: react.Context<FeedContextValue | null>;
|
|
112
131
|
/**
|
|
113
|
-
* Hook to access
|
|
132
|
+
* Hook to access Feed context
|
|
114
133
|
*
|
|
115
|
-
* @throws Error if used outside
|
|
134
|
+
* @throws Error if used outside Feed
|
|
116
135
|
*/
|
|
117
|
-
declare function
|
|
136
|
+
declare function useFeedContext(): FeedContextValue;
|
|
118
137
|
/**
|
|
119
|
-
* Hook to check if inside
|
|
138
|
+
* Hook to check if inside Feed (doesn't throw)
|
|
120
139
|
*/
|
|
121
|
-
declare function
|
|
140
|
+
declare function useOptionalFeedContext(): FeedContextValue | null;
|
|
122
141
|
|
|
123
142
|
/**
|
|
124
|
-
*
|
|
143
|
+
* useFeedPosition Hook
|
|
125
144
|
*
|
|
126
|
-
* Calculates position and visibility for
|
|
145
|
+
* Calculates position and visibility for content slots in the feed.
|
|
127
146
|
* Uses position-based virtual scrolling (TikTok-style).
|
|
128
147
|
*
|
|
129
148
|
* Architecture:
|
|
@@ -165,8 +184,8 @@ interface SlotPosition {
|
|
|
165
184
|
/** Opacity for transition effect */
|
|
166
185
|
opacity: number;
|
|
167
186
|
}
|
|
168
|
-
interface
|
|
169
|
-
/** Total number of
|
|
187
|
+
interface UseFeedPositionOptions {
|
|
188
|
+
/** Total number of items */
|
|
170
189
|
totalCount: number;
|
|
171
190
|
/** Current active index */
|
|
172
191
|
activeIndex: number;
|
|
@@ -184,7 +203,7 @@ interface UseVideoFeedPositionOptions {
|
|
|
184
203
|
/** Number of slots to render before/after active (default: 1) */
|
|
185
204
|
bufferSize?: number;
|
|
186
205
|
}
|
|
187
|
-
interface
|
|
206
|
+
interface UseFeedPositionReturn {
|
|
188
207
|
/** Positions for all slots that should render */
|
|
189
208
|
slots: SlotPosition[];
|
|
190
209
|
/** Current scroll offset (for container transform) */
|
|
@@ -211,8 +230,8 @@ interface UseVideoFeedPositionReturn {
|
|
|
211
230
|
*
|
|
212
231
|
* @example
|
|
213
232
|
* ```tsx
|
|
214
|
-
* const { slots, isAtEnd } =
|
|
215
|
-
* totalCount:
|
|
233
|
+
* const { slots, isAtEnd } = useFeedPosition({
|
|
234
|
+
* totalCount: items.length,
|
|
216
235
|
* activeIndex,
|
|
217
236
|
* containerHeight: 800,
|
|
218
237
|
* dragOffset,
|
|
@@ -225,7 +244,7 @@ interface UseVideoFeedPositionReturn {
|
|
|
225
244
|
* });
|
|
226
245
|
* ```
|
|
227
246
|
*/
|
|
228
|
-
declare function
|
|
247
|
+
declare function useFeedPosition(options: UseFeedPositionOptions): UseFeedPositionReturn;
|
|
229
248
|
/**
|
|
230
249
|
* Calculate the slot index from a Y position
|
|
231
250
|
* Useful for snap-to-slot logic
|
|
@@ -238,9 +257,9 @@ declare function useVideoFeedPosition(options: UseVideoFeedPositionOptions): Use
|
|
|
238
257
|
declare function getSlotIndexFromPosition(yPosition: number, containerHeight: number, totalCount: number): number;
|
|
239
258
|
|
|
240
259
|
/**
|
|
241
|
-
*
|
|
260
|
+
* Feed CSS
|
|
242
261
|
*/
|
|
243
|
-
declare const VIDEO_FEED_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 * VideoFeed Styles\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-video-feed {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n background: var(--sv-bg-primary, #000);\n touch-action: pan-y;\n user-select: none;\n -webkit-user-select: 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 * Feed Container (holds all slots)\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-video-feed__container {\n position: relative;\n width: 100%;\n height: 100%;\n will-change: transform;\n}\n\n/* Smooth transition when not swiping */\n.sv-video-feed__container--snapping {\n transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);\n}\n\n/* No transition during swipe for immediate feedback */\n.sv-video-feed__container--swiping {\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 * Video Slot Wrapper\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-video-feed__slot {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n will-change: transform, opacity;\n backface-visibility: hidden;\n -webkit-backface-visibility: hidden;\n}\n\n/* Active slot */\n.sv-video-feed__slot--active {\n z-index: 2;\n}\n\n/* Adjacent slots (for preview) */\n.sv-video-feed__slot--adjacent {\n z-index: 1;\n pointer-events: none;\n}\n\n/* Transition for smooth snap animations (when not swiping) */\n.sv-video-feed__slot--transitioning {\n transition: transform 0.175s cubic-bezier(0.25, 0.46, 0.45, 0.94),\n opacity 0.2s ease;\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 * Loading State\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-video-feed__loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--sv-spacing-md, 16px);\n color: var(--sv-text-secondary, #999);\n}\n\n.sv-video-feed__loading-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid rgba(255, 255, 255, 0.2);\n border-top-color: var(--sv-color-primary, #fe2c55);\n border-radius: 50%;\n animation: sv-feed-spinner 0.8s linear infinite;\n}\n\n@keyframes sv-feed-spinner {\n to {\n transform: rotate(360deg);\n }\n}\n\n.sv-video-feed__loading-text {\n font-size: var(--sv-font-size-sm, 13px);\n font-family: var(--sv-font-family, 'Urbanist', sans-serif);\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 * Empty State\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-video-feed__empty {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n text-align: center;\n color: var(--sv-text-secondary, #999);\n font-family: var(--sv-font-family, 'Urbanist', sans-serif);\n}\n\n.sv-video-feed__empty-icon {\n font-size: 48px;\n margin-bottom: var(--sv-spacing-md, 16px);\n}\n\n.sv-video-feed__empty-text {\n font-size: var(--sv-font-size-md, 14px);\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 * End of Feed\n * Note: Positioned outside container but above slots. Uses safe-area-inset\n * to avoid being covered by device notch/home 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-video-feed__end {\n position: absolute;\n /* Safe bottom positioning - accounts for device UI */\n bottom: calc(var(--sv-spacing-xl, 32px) + env(safe-area-inset-bottom, 0px));\n left: 50%;\n transform: translateX(-50%);\n z-index: 10; /* Above slots (z-index 1-2) */\n padding: var(--sv-spacing-sm, 8px) var(--sv-spacing-md, 16px);\n background: rgba(0, 0, 0, 0.7);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n border-radius: var(--sv-border-radius-md, 8px);\n color: var(--sv-text-secondary, #999);\n font-size: var(--sv-font-size-sm, 13px);\n font-family: var(--sv-font-family, 'Urbanist', sans-serif);\n pointer-events: none; /* Don't block interactions */\n animation: sv-feed-end-fade-in 0.3s ease-out;\n}\n\n@keyframes sv-feed-end-fade-in {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\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\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-video-feed__container--snapping {\n transition: none;\n }\n \n .sv-video-feed__loading-spinner {\n animation: none;\n }\n}\n";
|
|
262
|
+
declare const FEED_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 * Feed Styles\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-feed {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n background: var(--sv-bg-primary, #000);\n touch-action: pan-y;\n user-select: none;\n -webkit-user-select: 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 * Feed Container (holds all slots)\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-feed__container {\n position: relative;\n width: 100%;\n height: 100%;\n will-change: transform;\n}\n\n/* Smooth transition when not swiping */\n.sv-feed__container--snapping {\n transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);\n}\n\n/* No transition during swipe for immediate feedback */\n.sv-feed__container--swiping {\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 * Content Slot Wrapper\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-feed__slot {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n will-change: transform, opacity;\n backface-visibility: hidden;\n -webkit-backface-visibility: hidden;\n}\n\n/* Active slot */\n.sv-feed__slot--active {\n z-index: 2;\n}\n\n/* Adjacent slots (for preview) */\n.sv-feed__slot--adjacent {\n z-index: 1;\n pointer-events: none;\n}\n\n/* Transition for smooth snap animations (when not swiping) */\n.sv-feed__slot--transitioning {\n transition: transform 0.175s cubic-bezier(0.25, 0.46, 0.45, 0.94),\n opacity 0.2s ease;\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 * Loading State\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-feed__loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--sv-spacing-md, 16px);\n color: var(--sv-text-secondary, #999);\n}\n\n.sv-feed__loading-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid rgba(255, 255, 255, 0.2);\n border-top-color: var(--sv-color-primary, #fe2c55);\n border-radius: 50%;\n animation: sv-feed-spinner 0.8s linear infinite;\n}\n\n@keyframes sv-feed-spinner {\n to {\n transform: rotate(360deg);\n }\n}\n\n.sv-feed__loading-text {\n font-size: var(--sv-font-size-sm, 13px);\n font-family: var(--sv-font-family, 'Urbanist', sans-serif);\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 * Empty State\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-feed__empty {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n text-align: center;\n color: var(--sv-text-secondary, #999);\n font-family: var(--sv-font-family, 'Urbanist', sans-serif);\n}\n\n.sv-feed__empty-icon {\n font-size: 48px;\n margin-bottom: var(--sv-spacing-md, 16px);\n}\n\n.sv-feed__empty-text {\n font-size: var(--sv-font-size-md, 14px);\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 * End of Feed\n * Note: Positioned outside container but above slots. Uses safe-area-inset\n * to avoid being covered by device notch/home 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-feed__end {\n position: absolute;\n /* Safe bottom positioning - accounts for device UI */\n bottom: calc(var(--sv-spacing-xl, 32px) + env(safe-area-inset-bottom, 0px));\n left: 50%;\n transform: translateX(-50%);\n z-index: 10; /* Above slots (z-index 1-2) */\n padding: var(--sv-spacing-sm, 8px) var(--sv-spacing-md, 16px);\n background: rgba(0, 0, 0, 0.7);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n border-radius: var(--sv-border-radius-md, 8px);\n color: var(--sv-text-secondary, #999);\n font-size: var(--sv-font-size-sm, 13px);\n font-family: var(--sv-font-family, 'Urbanist', sans-serif);\n pointer-events: none; /* Don't block interactions */\n animation: sv-feed-end-fade-in 0.3s ease-out;\n}\n\n@keyframes sv-feed-end-fade-in {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\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\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-feed__container--snapping {\n transition: none;\n }\n \n .sv-feed__loading-spinner {\n animation: none;\n }\n}\n";
|
|
244
263
|
|
|
245
264
|
/**
|
|
246
265
|
* VideoFeed Constants
|
|
@@ -278,8 +297,8 @@ declare const SLOT_INDEX_DATASET_KEY = "slotIndex";
|
|
|
278
297
|
*/
|
|
279
298
|
declare const SLOT_ACTIVE_DATASET_KEY = "slotActive";
|
|
280
299
|
/**
|
|
281
|
-
* CSS class prefix for
|
|
300
|
+
* CSS class prefix for Feed components.
|
|
282
301
|
*/
|
|
283
|
-
declare const
|
|
302
|
+
declare const FEED_CLASS_PREFIX = "sv-feed";
|
|
284
303
|
|
|
285
|
-
export { SLOT_ACTIVE_ATTR, SLOT_ACTIVE_DATASET_KEY, SLOT_INDEX_ATTR, SLOT_INDEX_DATASET_KEY, type SlotPosition, type
|
|
304
|
+
export { FEED_CLASS_PREFIX, FEED_CSS, FeedContext, type FeedContextValue, FeedHeadless, type FeedHeadlessProps, SLOT_ACTIVE_ATTR, SLOT_ACTIVE_DATASET_KEY, SLOT_INDEX_ATTR, SLOT_INDEX_DATASET_KEY, type SlotPosition, type UseFeedPositionOptions, type UseFeedPositionReturn, FEED_CLASS_PREFIX as VIDEO_FEED_CLASS_PREFIX, FEED_CSS as VIDEO_FEED_CSS, FeedContext as VideoFeedContext, type FeedContextValue as VideoFeedContextValue, FeedHeadless as VideoFeedHeadless, type VideoFeedHeadlessProps, getSlotIndexFromPosition, useFeedContext, useFeedPosition, useOptionalFeedContext, useOptionalFeedContext as useOptionalVideoFeedContext, useFeedContext as useVideoFeedContext, useFeedPosition as useVideoFeedPosition };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FEED_CLASS_PREFIX, FEED_CSS, FeedContext, FeedHeadless, SLOT_ACTIVE_ATTR, SLOT_ACTIVE_DATASET_KEY, SLOT_INDEX_ATTR, SLOT_INDEX_DATASET_KEY, FEED_CLASS_PREFIX as VIDEO_FEED_CLASS_PREFIX, FEED_CSS as VIDEO_FEED_CSS, FeedContext as VideoFeedContext, FeedHeadless as VideoFeedHeadless, getSlotIndexFromPosition, useFeedContext, useFeedPosition, useOptionalFeedContext, useOptionalFeedContext as useOptionalVideoFeedContext, useFeedContext as useVideoFeedContext, useFeedPosition as useVideoFeedPosition } from '../../chunk-BEJAJFV6.js';
|