@xhub-short/sdk 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.
Files changed (3) hide show
  1. package/dist/index.d.ts +1392 -163
  2. package/dist/index.js +4038 -1409
  3. package/package.json +9 -8
package/dist/index.d.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import * as react from 'react';
2
- import { ComponentType, ReactNode, ReactElement, CSSProperties, SyntheticEvent } from 'react';
3
- import { ActionBarHeadlessProps, VideoItem, UIInteractionState, AuthorInfoHeadlessProps, UIAuthorState, UIAuthorActions, Author, CommentAuthor, CommentConfig, ProgressBarHeadlessProps, UIFeedState, UISwipeState, VideoInfoProps, VideoSlotProps, IDataSource, IInteraction, ICommentAdapter, ISessionStorage, IAnalytics, ILogger, INetworkAdapter, IVideoLoader, IPosterLoader, LocalizationConfig, UICommentState, UICommentActions, ILocalization, MessageKey, MessageValues, MessageCatalog } from '@xhub-short/contracts';
4
- export { ActionBarHeadlessProps, ActionBarProps, ActionButtonHeadlessProps, AnalyticsEvent, AnalyticsEventType, Author, AuthorInfoHeadlessProps, AuthorInfoProps, CommentAuthor, CommentConfig, CommentItem, ErrorBoundaryProps, FeedResponse, IAnalytics, ICommentAdapter, IDataSource, IInteraction, ILocalization, ILogger, INetworkAdapter, IPosterLoader, ISessionStorage, IStorage, IVideoLoader, LocalizationConfig, LogEntry, LogLevel, MessageCatalog, MessageKey, MessageValue, MessageValues, PartialMessageCatalog, ReplyItem, SessionSnapshot, SkeletonProps, UIAuthorActions, UIAuthorState, UICommentActions, UICommentState, UIFeedState, UIInteractionActions, UIInteractionState, UIPlayerControls, UIPlayerState, UIResourceState, UISwipeState, UIVideoInfoActions, UIVideoInfoState, VideoFeedHeadlessProps, VideoInfoHeadlessProps, VideoInfoProps, VideoItem, VideoPlayerHeadlessProps, VideoSlotHeadlessProps, VideoSlotProps, VideoSource, VideoStats } from '@xhub-short/contracts';
2
+ import react__default, { ComponentType, ReactNode, ReactElement, CSSProperties, SyntheticEvent } from 'react';
3
+ import { ActionBarHeadlessProps, ContentItem, VideoItem, UIInteractionState, AuthorInfoHeadlessProps, UIAuthorState, UIAuthorActions, Author, CommentConfig, ProgressBarHeadlessProps, Article, UIFeedState, UISwipeState, VideoInfoProps as VideoInfoProps$1, VideoSlotProps, MusicInfo, ContentStats, IDataSource, IPlaylistDataSource, IInteraction, ICommentAdapter, ISessionStorage, IAnalytics, ILogger, INetworkAdapter, IVideoLoader, IPosterLoader, LocalizationConfig, UICommentState, UICommentActions, ILocalization, MessageKey, MessageValues, MessageCatalog, CommentAuthor, VideoQuality, ReportReason, PlaylistData } from '@xhub-short/contracts';
4
+ export { ActionBarHeadlessProps, ActionBarProps, ActionButtonHeadlessProps, AnalyticsEvent, AnalyticsEventType, Article, ArticleSlotHeadlessProps, ArticleStats, Author, AuthorInfoHeadlessProps, AuthorInfoProps, CommentAuthor, CommentConfig, CommentItem, ErrorBoundaryProps, FeedResponse, IAnalytics, ICommentAdapter, IDataSource, IInteraction, ILocalization, ILogger, INetworkAdapter, IPosterLoader, ISessionStorage, IStorage, IVideoLoader, ImagePostSlotHeadlessProps, LocalizationConfig, LogEntry, LogLevel, MessageCatalog, MessageKey, MessageValue, MessageValues, PartialMessageCatalog, ReplyItem, SessionSnapshot, SkeletonProps, UIAuthorActions, UIAuthorState, UICommentActions, UICommentState, UIFeedState, UIInteractionActions, UIInteractionState, UIPlayerControls, UIPlayerState, UIResourceState, UISwipeState, UIVideoInfoActions, UIVideoInfoState, VideoFeedHeadlessProps, VideoInfoHeadlessProps, VideoInfoProps, VideoItem, VideoPlayerHeadlessProps, VideoSlotHeadlessProps, VideoSlotProps, VideoSource } from '@xhub-short/contracts';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
6
- import { AuthorAvatar, AuthorName, AuthorDescription, FollowButtonProps, SheetHeaderProps, CommentListProps, CommentInputProps, VideoAuthorName, VideoCaption, VideoHashtags, VideoLocation, VideoMusic, VideoPlayerHeadlessExtendedProps, ErrorMode, ErrorBoundaryProps } from '@xhub-short/ui';
6
+ import { AuthorAvatar, AuthorName, AuthorDescription, FollowButtonProps, SheetHeaderProps, CommentListProps, CommentInputProps, VideoAuthorName, VideoCaption, VideoHashtags, VideoLocation, VideoMusic, VideoPlayerHeadlessExtendedProps, ArticleSlotHeadlessProps, ErrorMode, ErrorBoundaryProps } from '@xhub-short/ui';
7
7
  export { CommentInputProps, CommentListProps, SheetHeaderProps } from '@xhub-short/ui';
8
- import { PlayerError, FeedConfig, PlayerConfig, ResourceConfig, LifecycleConfig, OptimisticConfig, RestoreResult, FeedError, FeedState, PlayerStatus, PlayerState, NetworkType, AllocationResult, PrefetchConfig, ResourceState, PendingAction, ActionType, OptimisticState, FeedManager, PlayerEngine, ResourceGovernor, LifecycleManager, OptimisticManager, LifecycleState } from '@xhub-short/core';
8
+ import { PlayerError, FeedConfig, PlayerConfig, ResourceConfig, LifecycleConfig, OptimisticConfig, RestoreResult, FeedError, FeedState, PlayerStatus, PlayerState, NetworkType, AllocationResult, PrefetchConfig, ResourceState, PendingAction, ActionType, OptimisticState, FeedManager, PlayerEngine, ResourceGovernor, LifecycleManager, OptimisticManager, PlaylistManager, PlaylistCollectionManager, LifecycleState } from '@xhub-short/core';
9
9
  export { ActionType, AllocationResult, FeedConfig, FeedError, FeedState, LifecycleConfig, LifecycleState, NetworkType, OptimisticConfig, OptimisticState, PendingAction, PlayerConfig, PlayerError, PlayerState, PlayerStatus, PrefetchConfig, ResourceConfig, ResourceState, RestoreResult } from '@xhub-short/core';
10
10
  import { BrowserAdaptersConfig } from '@xhub-short/adapters';
11
- export { BrowserAdaptersConfig } from '@xhub-short/adapters';
11
+ export { BatchAnalyticsConfig, BatchAnalyticsContext, BatchAnalyticsEventData, BatchAnalyticsEventTransformer, BrowserAdaptersConfig } from '@xhub-short/adapters';
12
12
 
13
13
  /**
14
14
  * HOC Factory for creating wired components
@@ -143,10 +143,14 @@ interface ActionBarIconSet {
143
143
  */
144
144
  interface ActionBarProps extends Omit<ActionBarHeadlessProps, 'interactionState' | 'interactionActions'> {
145
145
  /**
146
- * Video item to get interaction state for
147
- * Required - ActionBar needs video data for likes/comments/shares
146
+ * Content item (Video or Article) to get interaction state for
147
+ * Required - ActionBar needs data for likes/comments/shares
148
148
  */
149
- video: VideoItem;
149
+ content: ContentItem;
150
+ /**
151
+ * @deprecated Use 'content' instead. Will be removed in v3.0
152
+ */
153
+ video?: VideoItem;
150
154
  /**
151
155
  * Icon set for buttons
152
156
  * Host app provides these to customize appearance
@@ -166,11 +170,11 @@ interface ActionBarProps extends Omit<ActionBarHeadlessProps, 'interactionState'
166
170
  * Custom callback when sharing
167
171
  * Default: no-op (host app should implement share sheet)
168
172
  */
169
- onShare?: (video: VideoItem) => void | Promise<void>;
173
+ onShare?: (content: ContentItem) => void | Promise<void>;
170
174
  /**
171
175
  * Custom callback when like is toggled (in addition to SDK action)
172
176
  */
173
- onLikeToggle?: (isLiked: boolean, videoId: string) => void;
177
+ onLikeToggle?: (isLiked: boolean, contentId: string) => void;
174
178
  /**
175
179
  * Whether to show bookmark button (not auto-wired, host manages state)
176
180
  * @default false
@@ -183,7 +187,7 @@ interface ActionBarProps extends Omit<ActionBarHeadlessProps, 'interactionState'
183
187
  /**
184
188
  * Callback when bookmark is toggled
185
189
  */
186
- onBookmarkToggle?: (isBookmarked: boolean, videoId: string) => void;
190
+ onBookmarkToggle?: (isBookmarked: boolean, contentId: string) => void;
187
191
  /**
188
192
  * Layout direction
189
193
  * @default 'vertical'
@@ -209,7 +213,7 @@ interface ActionBarProps extends Omit<ActionBarHeadlessProps, 'interactionState'
209
213
  * Auto-connects Like, Comment, Share buttons to SDK hooks.
210
214
  * Uses optimistic UI for Like with automatic rollback on failure.
211
215
  */
212
- declare function ActionBar({ video, icons, interactionState: overrideState, onOpenComments, onShare, onLikeToggle, showBookmark, isBookmarked, onBookmarkToggle, direction, buttonVariant, className, children, testId, }: ActionBarProps): React.ReactElement;
216
+ declare function ActionBar({ content: contentProp, video: videoLegacy, icons, interactionState: overrideState, onOpenComments, onShare, onLikeToggle, showBookmark, isBookmarked, onBookmarkToggle, direction, buttonVariant, className, children, testId, }: ActionBarProps): React.ReactElement;
213
217
  declare namespace ActionBar {
214
218
  var displayName: string;
215
219
  }
@@ -219,10 +223,14 @@ declare namespace ActionBar {
219
223
  */
220
224
  interface AuthorInfoProps extends Omit<AuthorInfoHeadlessProps, 'authorState' | 'authorActions'> {
221
225
  /**
222
- * Video item to get author from
223
- * Required - AuthorInfo needs video data for author + follow state
226
+ * Content item (Video or Article) to get author from
227
+ * Required - AuthorInfo needs data for author + follow state
224
228
  */
225
- video: VideoItem;
229
+ content: ContentItem;
230
+ /**
231
+ * @deprecated Use 'content' instead. Will be removed in v3.0
232
+ */
233
+ video?: VideoItem;
226
234
  /**
227
235
  * Override author state (for custom scenarios)
228
236
  * If provided, replaces auto-derived state from video
@@ -277,7 +285,7 @@ interface AuthorInfoProps extends Omit<AuthorInfoHeadlessProps, 'authorState' |
277
285
  * Auto-connects Follow button to SDK hooks.
278
286
  * Uses optimistic UI for Follow with automatic rollback on failure.
279
287
  */
280
- declare function AuthorInfoRoot({ video, authorState: overrideState, authorActions: overrideActions, onOpenProfile, onFollowToggle, showFollowButton, variant, overlay, className, children, testId, }: AuthorInfoProps): React.ReactElement;
288
+ declare function AuthorInfoRoot({ content: contentProp, video: videoLegacy, authorState: overrideState, authorActions: overrideActions, onOpenProfile, onFollowToggle, showFollowButton, variant, overlay, className, children, testId, }: AuthorInfoProps): React.ReactElement;
281
289
  declare namespace AuthorInfoRoot {
282
290
  var displayName: string;
283
291
  }
@@ -349,8 +357,6 @@ interface CommentSheetProps {
349
357
  isOpen: boolean;
350
358
  /** Close callback */
351
359
  onClose: () => void;
352
- /** Current user info (required for optimistic UI when posting) */
353
- currentUser?: CommentAuthor;
354
360
  /** Config overrides */
355
361
  config?: Partial<CommentConfig>;
356
362
  /** Initial count from video data (displayed before API loads) */
@@ -368,7 +374,7 @@ interface CommentSheetProps {
368
374
  /** Close on escape key (default: true) */
369
375
  closeOnEscape?: boolean;
370
376
  }
371
- declare function CommentSheetInner({ videoId, isOpen, onClose, currentUser, config, initialCount, autoLoad, children, className, backdropClassName, closeOnBackdropClick, closeOnEscape, }: CommentSheetProps): ReactElement;
377
+ declare function CommentSheetInner({ videoId, isOpen, onClose, config, initialCount, autoLoad, children, className, backdropClassName, closeOnBackdropClick, closeOnEscape, }: CommentSheetProps): ReactElement;
372
378
  /**
373
379
  * Translated SheetHeader - auto-injects i18n strings
374
380
  */
@@ -486,6 +492,54 @@ declare namespace ProgressBar {
486
492
  var displayName: string;
487
493
  }
488
494
 
495
+ /**
496
+ * Lightweight Touch Drag Hook
497
+ *
498
+ * Custom replacement for @use-gesture/react's useDrag.
499
+ * ~1KB instead of ~20KB gzipped.
500
+ *
501
+ * Features:
502
+ * - Touch and mouse support
503
+ * - Axis locking (vertical/horizontal)
504
+ * - Velocity calculation
505
+ * - Direction detection
506
+ * - First/Last event detection
507
+ *
508
+ * @internal This is an internal implementation detail.
509
+ * Use useSwipeGesture for the public API.
510
+ */
511
+ type DragAxis = 'x' | 'y' | 'both';
512
+ interface DragState {
513
+ /** Movement from start [x, y] in pixels */
514
+ movement: [number, number];
515
+ /** Velocity [vx, vy] in px/ms */
516
+ velocity: [number, number];
517
+ /** Direction [-1, 0, 1] for each axis */
518
+ direction: [number, number];
519
+ /** True on first event of gesture */
520
+ first: boolean;
521
+ /** True on last event (release) */
522
+ last: boolean;
523
+ /** True while gesture is active */
524
+ active: boolean;
525
+ /** Cancel function to abort gesture */
526
+ cancel: () => void;
527
+ /** Original event */
528
+ event: TouchEvent | MouseEvent | PointerEvent;
529
+ }
530
+ interface DragConfig {
531
+ /** Axis to track: 'x', 'y', or 'both' */
532
+ axis?: DragAxis;
533
+ /** Filter out taps (short movements) */
534
+ filterTaps?: boolean;
535
+ /** Tap threshold in pixels */
536
+ tapThreshold?: number;
537
+ /** Enable touch events */
538
+ touch?: boolean;
539
+ /** Enable mouse/pointer events */
540
+ mouse?: boolean;
541
+ }
542
+ type DragHandler = (state: DragState) => void;
489
543
  interface DragBind {
490
544
  onPointerDown?: (e: React.PointerEvent) => void;
491
545
  onTouchStart?: (e: React.TouchEvent) => void;
@@ -493,6 +547,25 @@ interface DragBind {
493
547
  touchAction: string;
494
548
  };
495
549
  }
550
+ /**
551
+ * Lightweight drag hook - replacement for @use-gesture/react useDrag
552
+ *
553
+ * @param handler - Callback for drag events
554
+ * @param config - Configuration options
555
+ * @returns Bind function to spread on target element
556
+ *
557
+ * @example
558
+ * ```tsx
559
+ * const bind = useTouchDrag((state) => {
560
+ * if (state.first) console.log('Started dragging');
561
+ * if (state.last) console.log('Released', state.velocity);
562
+ * console.log('Movement:', state.movement);
563
+ * }, { axis: 'y' });
564
+ *
565
+ * return <div {...bind()}>Drag me</div>;
566
+ * ```
567
+ */
568
+ declare function useTouchDrag(handler: DragHandler, config?: DragConfig): () => DragBind;
496
569
 
497
570
  /**
498
571
  * Swipe Gesture Hook
@@ -506,10 +579,6 @@ interface DragBind {
506
579
  * - Velocity-based snap decisions
507
580
  * - Integration with ResourceGovernor
508
581
  *
509
- * PERFORMANCE CRITICAL (ADR 005):
510
- * - KHÔNG sử dụng useState cho dragOffset trong scroll loop
511
- * - Dùng useRef + direct DOM manipulation
512
- * - Chỉ sync state khi Touch End (Snap)
513
582
  *
514
583
  * AXIS SUPPORT:
515
584
  * - `vertical`: Full support - index navigation + gesture
@@ -549,6 +618,19 @@ interface SwipeConfig {
549
618
  * @default true
550
619
  */
551
620
  enableMouse?: boolean;
621
+ /**
622
+ * Temporarily disable swipe gestures.
623
+ * Use this to block swipe during other gestures (e.g., zoom).
624
+ *
625
+ * @default false
626
+ * @example
627
+ * ```tsx
628
+ * // Block swipe during zoom
629
+ * const { isSwipeLocked } = useZoomGesture();
630
+ * const swipe = useSwipeGesture({ disabled: isSwipeLocked });
631
+ * ```
632
+ */
633
+ disabled?: boolean;
552
634
  /**
553
635
  * Called when swipe completes and snaps
554
636
  */
@@ -700,37 +782,40 @@ interface UseSwipeGestureReturn {
700
782
  declare function useSwipeGesture(config?: SwipeConfig): UseSwipeGestureReturn;
701
783
 
702
784
  /**
703
- * Wired VideoFeed Component
785
+ * Wired Feed Component
704
786
  *
705
787
  * Pre-wired version of VideoFeedHeadless that auto-injects SDK hooks.
706
- * This is the recommended way to use VideoFeed in host applications.
788
+ * Supports mixed content (VideoItem | Article) in a single feed.
789
+ * This is the recommended way to use Feed in host applications.
707
790
  *
708
791
  * Features:
709
792
  * - Auto-connects to FeedManager via useFeed()
710
793
  * - Auto-handles swipe gestures via useSwipeGesture()
711
794
  * - Direct DOM manipulation for 60fps swipe (ADR 005)
712
- * - All props from VideoFeedHeadless are still available for customization
795
+ * - Mixed content support (videos + articles)
796
+ * - All props from FeedHeadless are still available for customization
713
797
  *
714
798
  * ## Gesture Scope
715
799
  *
716
- * Gestures are attached to the outer container div (not VideoFeedHeadless).
800
+ * Gestures are attached to the outer container div (not FeedHeadless).
717
801
  * If you wrap this component with additional DOM, gestures will still work
718
802
  * correctly as they're attached to the inner container.
719
803
  *
720
804
  * @example
721
805
  * ```tsx
722
- * import { VideoFeed } from '@xhub-short/sdk';
806
+ * import { Feed, isVideoItem, isArticle } from '@xhub-short/sdk';
723
807
  *
724
808
  * function App() {
725
809
  * return (
726
810
  * <ShortVideoProvider>
727
- * <VideoFeed
728
- * renderSlot={(video, index) => (
729
- * <div>
730
- * <img src={video.poster} alt={video.title} />
731
- * <span>{video.title}</span>
732
- * </div>
733
- * )}
811
+ * <Feed
812
+ * renderSlot={(item, index) => {
813
+ * // Type guard for mixed content
814
+ * if (isArticle(item)) {
815
+ * return <DefaultArticleSlot article={item} index={index} />;
816
+ * }
817
+ * return <DefaultVideoSlot video={item} index={index} />;
818
+ * }}
734
819
  * onIndexChange={(index) => console.log('Active:', index)}
735
820
  * />
736
821
  * </ShortVideoProvider>
@@ -740,31 +825,111 @@ declare function useSwipeGesture(config?: SwipeConfig): UseSwipeGestureReturn;
740
825
  */
741
826
 
742
827
  /**
743
- * Props for wired VideoFeed component
828
+ * Custom renderers for each content type.
829
+ * Allows host apps to override rendering per type without using explicit type guards.
830
+ */
831
+ interface SlotRenderers {
832
+ /** Custom renderer for video items */
833
+ video?: (props: {
834
+ video: VideoItem;
835
+ index: number;
836
+ isActive: boolean;
837
+ }) => ReactNode;
838
+ /** Custom renderer for article items */
839
+ article?: (props: {
840
+ article: Article;
841
+ index: number;
842
+ isActive: boolean;
843
+ }) => ReactNode;
844
+ }
845
+ /**
846
+ * Props for wired Feed component
847
+ *
848
+ * Supports mixed content (VideoItem | Article).
849
+ * Use type guards (isVideoItem, isArticle) in renderSlot to handle different content types.
744
850
  *
745
- * Extends VideoFeedHeadlessProps but feedState and swipeState are auto-injected.
851
+ * feedState and swipeState are auto-injected by SDK hooks.
746
852
  * You can still override them if needed.
747
853
  */
748
- interface VideoFeedProps {
749
- /** Container height (default: 100vh via CSS) */
854
+ interface FeedProps {
855
+ /**
856
+ * Container height (default: 100vh via CSS)
857
+ *
858
+ * @default 100vh
859
+ */
750
860
  height?: number | string;
751
- /** Additional CSS classes */
861
+ /**
862
+ * Additional CSS classes
863
+ * @default ''
864
+ */
752
865
  className?: string;
753
- /** Render function for each video slot */
754
- renderSlot: (video: VideoItem, index: number) => ReactNode;
755
- /** Called when active index changes */
866
+ /**
867
+ * Render function for each content slot (video or article)
868
+ *
869
+ * @deprecated Use `renderers` prop for a cleaner, type-safe mapping pattern.
870
+ * If not provided, RendererFactory will automatically select DefaultVideoSlot
871
+ * for videos and DefaultArticleSlot for articles.
872
+ *
873
+ * @param item - The content item (VideoItem | Article)
874
+ * @param index - The index of the slot
875
+ * @param isActive - Whether this slot is currently active/visible
876
+ * @returns The React node to render
877
+ */
878
+ renderSlot?: (item: ContentItem, index: number, isActive: boolean) => ReactNode;
879
+ /**
880
+ * Custom renderers for each content type.
881
+ * Recommended for advanced customization without explicit type guards.
882
+ *
883
+ * @example
884
+ * ```tsx
885
+ * <Feed
886
+ * renderers={{
887
+ * video: (props) => <MyCustomVideo {...props} showBookmark={false} />,
888
+ * article: (props) => <MyCustomArticle {...props} />
889
+ * }}
890
+ * />
891
+ * ```
892
+ */
893
+ renderers?: SlotRenderers;
894
+ /**
895
+ * Common props to pass to both default slots (if no renderers or renderSlot provided)
896
+ * or to use as a base for custom renderers.
897
+ */
898
+ slotProps?: Record<string, unknown>;
899
+ /**
900
+ * Called when active index changes
901
+ * @param index - The new active index
902
+ */
756
903
  onIndexChange?: (index: number) => void;
757
- /** Called when reaching near end of feed */
904
+ /**
905
+ * Called when reaching near end of feed
906
+ * @returns The React node to render
907
+ */
758
908
  onEndReached?: () => void;
759
- /** Threshold for triggering onEndReached (default: 2) */
909
+ /**
910
+ * Threshold for triggering onEndReached
911
+ * @default 2
912
+ */
760
913
  endReachedThreshold?: number;
761
- /** Number of slots to render before/after active (default: 1) */
914
+ /**
915
+ * Number of slots to render before/after active
916
+ * @default 1
917
+ */
762
918
  bufferSize?: number;
763
- /** Loading text */
919
+ /**
920
+ * Loading text
921
+ * @default 'Loading...'
922
+ */
764
923
  loadingText?: string;
765
- /** Empty text */
924
+ /**
925
+ * Empty text
926
+ * @default 'No videos'
927
+ */
766
928
  emptyText?: string;
767
- /** End of feed text */
929
+ /**
930
+ * End of feed text
931
+ * @default 'End of feed'
932
+ */
768
933
  endText?: string;
769
934
  /**
770
935
  * Whether to auto-load initial feed on mount
@@ -838,11 +1003,59 @@ interface VideoFeedProps {
838
1003
  * @default { touchAction: 'none' }
839
1004
  */
840
1005
  containerStyle?: CSSProperties;
1006
+ /**
1007
+ * Playlist ID to load.
1008
+ * If provided, feed will switch to Playlist mode.
1009
+ * If undefined/null, feed will switch to Recommendation mode.
1010
+ */
1011
+ playlistId?: string;
1012
+ /**
1013
+ * Externally disable swipe gestures.
1014
+ * Use this to block swipe during other gestures (e.g., zoom).
1015
+ *
1016
+ * @default false
1017
+ * @example
1018
+ * ```tsx
1019
+ * // With useZoomGesture hook
1020
+ * function MyFeed() {
1021
+ * const zoom = useZoomGesture();
1022
+ *
1023
+ * return (
1024
+ * <VideoFeed
1025
+ * swipeDisabled={zoom.isSwipeLocked}
1026
+ * renderSlot={(video, index) => (
1027
+ * <div {...zoom.handlers} style={{ transform: zoom.transform }}>
1028
+ * <video src={video.url} />
1029
+ * {!zoom.isZooming && <ActionBar />}
1030
+ * </div>
1031
+ * )}
1032
+ * />
1033
+ * );
1034
+ * }
1035
+ * ```
1036
+ */
1037
+ swipeDisabled?: boolean;
1038
+ /**
1039
+ * Custom empty state component.
1040
+ * Receives `text` prop (localized empty text).
1041
+ */
1042
+ renderEmpty?: (text: string) => ReactNode;
1043
+ /**
1044
+ * Custom loading state component.
1045
+ * Receives `text` prop (localized loading text).
1046
+ */
1047
+ renderLoading?: (text: string) => ReactNode;
1048
+ /**
1049
+ * Custom end of feed component.
1050
+ * Receives `text` prop (localized end text).
1051
+ */
1052
+ renderEnd?: (text: string) => ReactNode;
841
1053
  }
842
1054
  /**
843
- * Wired VideoFeed Component
1055
+ * Wired Feed Component
844
1056
  *
845
1057
  * Automatically connects to SDK's FeedManager and SwipeGesture.
1058
+ * Supports mixed content (VideoItem | Article) in a single feed.
846
1059
  * Provides smooth 60fps swipe navigation using ref-based DOM manipulation.
847
1060
  *
848
1061
  * ## Performance Notes
@@ -850,11 +1063,31 @@ interface VideoFeedProps {
850
1063
  * This component uses direct DOM manipulation during swipe to avoid
851
1064
  * React re-renders. The `dragOffset` in swipeState is always 0 because
852
1065
  * real-time offset is stored in a ref and applied directly to DOM elements.
1066
+ *
1067
+ * ## Mixed Content Support
1068
+ *
1069
+ * Use type guards in renderSlot to handle different content types:
1070
+ * ```tsx
1071
+ * <Feed
1072
+ * renderSlot={(item, index) => {
1073
+ * if (isArticle(item)) return <ArticleSlot article={item} />;
1074
+ * return <VideoSlot video={item} />;
1075
+ * }}
1076
+ * />
1077
+ * ```
853
1078
  */
854
- declare function VideoFeed({ height, className, renderSlot, onIndexChange, onEndReached, endReachedThreshold, bufferSize, loadingText: loadingTextProp, emptyText: emptyTextProp, endText: endTextProp, autoLoad, autoLoadMore, swipeThreshold, velocityThreshold, snapDuration, onSwipe, feedState: feedStateProp, swipeState: swipeStateProp, containerStyle, }: VideoFeedProps): React.ReactElement;
855
- declare namespace VideoFeed {
1079
+ declare function Feed({ height, className, renderSlot, renderers, slotProps, onIndexChange, onEndReached, endReachedThreshold, bufferSize, loadingText: loadingTextProp, emptyText: emptyTextProp, endText: endTextProp, autoLoad, autoLoadMore, swipeThreshold, velocityThreshold, snapDuration, onSwipe, feedState: feedStateProp, swipeState: swipeStateProp, containerStyle, swipeDisabled, playlistId, renderEmpty, renderLoading, renderEnd, }: FeedProps): React.ReactElement;
1080
+ declare namespace Feed {
856
1081
  var displayName: string;
857
1082
  }
1083
+ /**
1084
+ * @deprecated Use `Feed` instead. `VideoFeed` is an alias for backward compatibility.
1085
+ */
1086
+ declare const VideoFeed: typeof Feed;
1087
+ /**
1088
+ * @deprecated Use `FeedProps` instead. `VideoFeedProps` is an alias for backward compatibility.
1089
+ */
1090
+ type VideoFeedProps = FeedProps;
858
1091
 
859
1092
  /**
860
1093
  * VideoInfo - Wired SDK Component
@@ -875,9 +1108,9 @@ declare namespace VideoFeed {
875
1108
  */
876
1109
 
877
1110
  /**
878
- * Extended props for wired VideoInfo component
1111
+ * VideoInfo wired component props
879
1112
  */
880
- interface VideoInfoWiredProps extends VideoInfoProps {
1113
+ interface VideoInfoProps extends VideoInfoProps$1 {
881
1114
  /** Called when author name is clicked */
882
1115
  onOpenProfile?: (author: Author) => void;
883
1116
  /** Called when hashtag is clicked */
@@ -891,10 +1124,12 @@ interface VideoInfoWiredProps extends VideoInfoProps {
891
1124
  /** Test ID for testing */
892
1125
  testId?: string;
893
1126
  }
1127
+ /** @deprecated Use VideoInfoProps instead. Will be removed in v3.0 */
1128
+ type VideoInfoWiredProps = VideoInfoProps;
894
1129
  /**
895
1130
  * VideoInfo - Wired SDK component
896
1131
  */
897
- declare function VideoInfoRoot({ video, videoInfoState: overrideState, videoInfoActions: overrideActions, onOpenProfile, onHashtagClick, onMusicClick, onLocationClick, overlay, className, children, testId, }: VideoInfoWiredProps): ReactElement;
1132
+ declare function VideoInfoRoot({ content: contentProp, video: videoLegacy, videoInfoState: overrideState, videoInfoActions: overrideActions, onOpenProfile, onHashtagClick, onMusicClick, onLocationClick, overlay, className, children, testId, }: VideoInfoProps): ReactElement;
898
1133
  /**
899
1134
  * Compound component with sub-components attached
900
1135
  */
@@ -1107,10 +1342,29 @@ interface VideoSlotWiredProps extends VideoSlotProps {
1107
1342
  * Must be provided by host app if needed
1108
1343
  */
1109
1344
  onDoubleTap?: () => void;
1345
+ /**
1346
+ * Handle long press (hold > 500ms)
1347
+ * Typically used to open context menu (AdvanceMenu)
1348
+ */
1349
+ onLongPress?: () => void;
1350
+ /**
1351
+ * Disable tap gesture handling.
1352
+ * When true, single tap won't trigger play/pause toggle or PlayIndicator.
1353
+ * Useful during zoom gesture to prevent phantom taps from touchend events.
1354
+ */
1355
+ disableTap?: boolean;
1110
1356
  /** Custom error UI (receives wrapped error with metadata) */
1111
1357
  renderError?: (error: Error, retry: () => void) => ReactNode;
1112
1358
  /** Custom loading UI */
1113
1359
  renderLoading?: () => ReactNode;
1360
+ /** Title for reported overlay */
1361
+ reportedTitle?: string;
1362
+ /** Message for reported overlay */
1363
+ reportedMessage?: string;
1364
+ /** Button text for reported overlay */
1365
+ reportedButtonText?: string;
1366
+ /** Custom styles */
1367
+ style?: React.CSSProperties;
1114
1368
  }
1115
1369
  /**
1116
1370
  * SDK Player Error - Preserves error metadata from PlayerEngine
@@ -1160,88 +1414,489 @@ declare class SDKPlayerError extends Error {
1160
1414
  * Host app must trigger these manually or wait for future IntersectionObserver
1161
1415
  * implementation in VideoSlotHeadless.
1162
1416
  */
1163
- declare function VideoSlot({ video, index, resourceState: resourceStateOverride, playerState: playerStateOverride, playerControls: playerControlsOverride, className, children, onVisible, onHidden, onTap, onDoubleTap, renderError, renderLoading, }: VideoSlotWiredProps): React.ReactElement;
1417
+ declare function VideoSlot({ video, index, resourceState: resourceStateOverride, playerState: playerStateOverride, playerControls: playerControlsOverride, className, children, onVisible, onHidden, onTap, onDoubleTap, onLongPress, disableTap, renderError, renderLoading, reportedTitle, reportedMessage, reportedButtonText, style, }: VideoSlotWiredProps): React.ReactElement;
1164
1418
  declare namespace VideoSlot {
1165
1419
  var displayName: string;
1166
1420
  }
1167
1421
 
1168
- interface DefaultSlotProps {
1422
+ /**
1423
+ * Wired ArticleSlot Component
1424
+ *
1425
+ * Pre-wired version of ArticleSlotHeadless that auto-injects SDK hooks.
1426
+ * This is the recommended way to use ArticleSlot in host applications.
1427
+ *
1428
+ * Features:
1429
+ * - Auto-connects to useArticle hook for state management
1430
+ * - Auto-connects to SDK context for interactions
1431
+ * - Handles like animation and double-tap
1432
+ * - Integrates with DetailView for "Read More" functionality
1433
+ * - Music playback support
1434
+ */
1435
+
1436
+ interface ArticleSlotWiredProps extends Omit<ArticleSlotHeadlessProps, 'currentImageIndex' | 'onImageIndexChange' | 'isMusicPlaying' | 'onToggleMusic' | 'onDoubleTap' | 'onReadMoreClick'> {
1437
+ /** Slot index in the feed */
1438
+ index: number;
1439
+ /** Music info for this post */
1440
+ music?: MusicInfo;
1441
+ /** Whether this post is active (visible) */
1442
+ isActive?: boolean;
1443
+ /** Whether to show detail view on "Read More" click */
1444
+ enableDetailView?: boolean;
1445
+ /** Custom double tap handler (default: toggle like) */
1446
+ onDoubleTap?: () => void;
1447
+ /** Custom "Read More" handler (default: open detail view) */
1448
+ onReadMoreClick?: () => void;
1449
+ /** Custom long press handler */
1450
+ onLongPress?: () => void;
1451
+ /** Custom single tap handler (default: toggle music if tapToToggleMusic=true) */
1452
+ onTap?: () => void;
1453
+ /** Enable tap-to-toggle music (default: true if music exists) */
1454
+ tapToToggleMusic?: boolean;
1455
+ /**
1456
+ * Callback when image index changes in carousel.
1457
+ * Useful for syncing with Detail View.
1458
+ */
1459
+ onImageIndexChange?: (index: number) => void;
1460
+ /** Children (overlay content) */
1461
+ children?: ReactNode;
1462
+ /** Custom detail view content */
1463
+ renderDetailView?: (props: {
1464
+ article: Article;
1465
+ onClose: () => void;
1466
+ isOpen: boolean;
1467
+ }) => ReactNode;
1468
+ /** Detail view header title */
1469
+ detailViewTitle?: string;
1470
+ /** Whether to show follow button in detail view */
1471
+ showFollowButton?: boolean;
1472
+ /** Current user follows this author */
1473
+ isFollowing?: boolean;
1474
+ /** Follow button click handler */
1475
+ onFollowClick?: () => void;
1476
+ /** Custom styles */
1477
+ style?: React.CSSProperties;
1478
+ }
1479
+ declare function ArticleSlot({ article, index, music, isActive, enableDetailView, onDoubleTap: customDoubleTap, onReadMoreClick: customReadMore, onLongPress, onTap: customTap, tapToToggleMusic, onImageIndexChange: onIndexChangeProp, children, renderDetailView, detailViewTitle, showFollowButton, isFollowing, onFollowClick, ...restProps }: ArticleSlotWiredProps): ReactElement;
1480
+ declare namespace ArticleSlot {
1481
+ var displayName: string;
1482
+ }
1483
+ /** @deprecated Use ArticleSlot instead */
1484
+ declare const ImagePostSlot: typeof ArticleSlot;
1485
+ /** @deprecated Use ArticleSlotWiredProps instead */
1486
+ type ImagePostSlotWiredProps = ArticleSlotWiredProps;
1487
+
1488
+ interface DefaultArticleSlotProps {
1489
+ /** Article data */
1490
+ article: Article;
1491
+ /** Index in the feed */
1492
+ index: number;
1493
+ /** Whether this post is currently active/visible (for music autoplay) */
1494
+ isActive?: boolean;
1495
+ showLikeAnimation?: boolean;
1496
+ showBookmark?: boolean;
1497
+ showShare?: boolean;
1498
+ showComment?: boolean;
1499
+ showMusic?: boolean;
1500
+ showPlayIndicator?: boolean;
1501
+ showAuthorFollow?: boolean;
1502
+ enableDetailView?: boolean;
1503
+ enableZoom?: boolean;
1504
+ showCounter?: boolean;
1505
+ showAdvanceMenu?: boolean;
1506
+ showCleanModeOverlay?: boolean;
1507
+ showReportSheet?: boolean;
1508
+ detailViewTitle?: string;
1509
+ onShare?: (article: Article) => void;
1510
+ onOpenComments?: (postId: string) => void;
1511
+ onAuthorTap?: (article: Article) => void;
1512
+ onFollow?: (article: Article) => void;
1513
+ onHashtagTap?: (hashtag: string, article: Article) => void;
1514
+ onContentHidden?: (contentId: string) => void;
1515
+ onLikeToggle?: (isLiked: boolean) => void;
1516
+ onBookmarkToggle?: (isBookmarked: boolean) => void;
1517
+ renderActionBar?: (props: ActionBarProps) => ReactNode;
1518
+ renderAuthorInfo?: (props: AuthorInfoProps) => ReactNode;
1519
+ children?: ReactNode;
1520
+ /** Additional className */
1521
+ className?: string;
1522
+ }
1523
+ declare const DefaultArticleSlot: react.NamedExoticComponent<DefaultArticleSlotProps>;
1524
+ /** @deprecated Use DefaultArticleSlot instead */
1525
+ declare const DefaultImagePostSlot: react.NamedExoticComponent<DefaultArticleSlotProps>;
1526
+ /** @deprecated Use DefaultArticleSlotProps instead */
1527
+ type DefaultImagePostSlotProps = DefaultArticleSlotProps;
1528
+
1529
+ interface AdvanceMenuProps {
1530
+ /** Content item (Video or Article) */
1531
+ content: ContentItem;
1532
+ /** @deprecated Use content instead. Will be removed in v3.0 */
1533
+ video?: VideoItem;
1534
+ /** Whether menu is open */
1535
+ isOpen: boolean;
1536
+ /** Close handler */
1537
+ onClose: () => void;
1538
+ /** Callback when report is clicked (opens report sheet) */
1539
+ onOpenReport?: () => void;
1540
+ /** Show quality picker (default: true) */
1541
+ showQuality?: boolean;
1542
+ /** Show auto-scroll toggle (default: true) */
1543
+ showAutoScroll?: boolean;
1544
+ /** Show clean mode option (default: true) */
1545
+ showCleanMode?: boolean;
1546
+ /** Show not interested option (default: true) */
1547
+ showNotInterested?: boolean;
1548
+ /** Show report option (default: true) */
1549
+ showReport?: boolean;
1550
+ /** Custom class name */
1551
+ className?: string;
1552
+ /** Speed label */
1553
+ speedLabel?: string;
1554
+ /** Quality label */
1555
+ qualityLabel?: string;
1556
+ /** Auto-scroll label */
1557
+ autoScrollLabel?: string;
1558
+ /** Clean mode label */
1559
+ cleanModeLabel?: string;
1560
+ /** Not interested label */
1561
+ notInterestedLabel?: string;
1562
+ /** Report label */
1563
+ reportLabel?: string;
1564
+ /** Custom speed icon */
1565
+ speedIcon?: ReactNode;
1566
+ /** Custom quality icon */
1567
+ qualityIcon?: ReactNode;
1568
+ /** Custom auto-scroll icon */
1569
+ autoScrollIcon?: ReactNode;
1570
+ /** Custom clean mode icon */
1571
+ cleanModeIcon?: ReactNode;
1572
+ /** Custom not interested icon */
1573
+ notInterestedIcon?: ReactNode;
1574
+ /** Custom report icon */
1575
+ reportIcon?: ReactNode;
1576
+ }
1577
+ declare function AdvanceMenuComponent({ content: contentProp, video: videoLegacy, isOpen, onClose, onOpenReport, showQuality, showAutoScroll, showCleanMode, showNotInterested, showReport, className, speedLabel, qualityLabel, autoScrollLabel, cleanModeLabel, notInterestedLabel, reportLabel, speedIcon, qualityIcon, autoScrollIcon, cleanModeIcon, notInterestedIcon, reportIcon, }: AdvanceMenuProps): ReactElement;
1578
+ declare const AdvanceMenu: react.MemoExoticComponent<typeof AdvanceMenuComponent>;
1579
+ /** @deprecated Use AdvanceMenu instead */
1580
+ declare const AdvanceMenuWired: react.MemoExoticComponent<typeof AdvanceMenuComponent>;
1581
+ /** @deprecated Use AdvanceMenuProps instead */
1582
+ type AdvanceMenuWiredProps = AdvanceMenuProps;
1583
+
1584
+ interface ReportSheetProps {
1585
+ /** Content ID to report */
1586
+ contentId: string;
1587
+ /** Current video index in feed (required for goToNextOnSuccess) */
1588
+ currentIndex?: number;
1589
+ /** Whether sheet is open */
1590
+ isOpen: boolean;
1591
+ /** Close handler */
1592
+ onClose: () => void;
1593
+ /** Callback when report succeeds */
1594
+ onSuccess?: () => void;
1595
+ /** Callback when report fails */
1596
+ onError?: (error: Error) => void;
1597
+ /**
1598
+ * Auto navigate to next video when Done is clicked after successful report
1599
+ * @default true
1600
+ */
1601
+ goToNextOnSuccess?: boolean;
1602
+ /** Custom class name */
1603
+ className?: string;
1604
+ /** Sheet title for step 1 - reason selection */
1605
+ selectTitle?: string;
1606
+ /** Sheet title for step 2 - report detail */
1607
+ title?: string;
1608
+ /** Text input placeholder */
1609
+ inputPlaceholder?: string;
1610
+ /** Submit button text */
1611
+ submitText?: string;
1612
+ /** Loading text */
1613
+ loadingText?: string;
1614
+ /** Success title */
1615
+ successTitle?: string;
1616
+ /** Success message */
1617
+ successMessage?: string;
1618
+ /** Done button text (on success screen) */
1619
+ doneText?: string;
1620
+ /** Error message */
1621
+ errorMessage?: string;
1622
+ /** Error description (retry hint) */
1623
+ errorDescription?: string;
1624
+ /**
1625
+ * Custom button element to trigger the report sheet
1626
+ */
1627
+ buttonElement?: ReactElement;
1628
+ }
1629
+ declare function ReportSheetComponent({ contentId, currentIndex, isOpen, onClose, onSuccess, onError, goToNextOnSuccess, className, selectTitle, title, inputPlaceholder, submitText, loadingText, successTitle, successMessage, doneText, errorMessage, errorDescription, buttonElement, }: ReportSheetProps): ReactElement;
1630
+ declare const ReportSheet: react.MemoExoticComponent<typeof ReportSheetComponent>;
1631
+ /** @deprecated Use ReportSheet instead */
1632
+ declare const ReportSheetWired: react.MemoExoticComponent<typeof ReportSheetComponent>;
1633
+ /** @deprecated Use ReportSheetProps instead */
1634
+ type ReportSheetWiredProps = ReportSheetProps;
1635
+
1636
+ interface CleanModeOverlayProps {
1637
+ /** Auto-hide delay in ms (default: 3000) */
1638
+ hideDelay?: number;
1639
+ /** Custom exit icon */
1640
+ exitIcon?: ReactNode;
1641
+ /** Custom play icon */
1642
+ playIcon?: ReactNode;
1643
+ /** Custom pause icon */
1644
+ pauseIcon?: ReactNode;
1645
+ /** Custom class name */
1646
+ className?: string;
1647
+ }
1648
+ declare function CleanModeOverlayComponent({ hideDelay, exitIcon, playIcon, pauseIcon, className, }: CleanModeOverlayProps): ReactElement | null;
1649
+ declare const CleanModeOverlay: react.MemoExoticComponent<typeof CleanModeOverlayComponent>;
1650
+
1651
+ interface DefaultVideoSlotProps {
1169
1652
  /** Video item data */
1170
1653
  video: VideoItem;
1171
1654
  /** Index in the feed */
1172
1655
  index: number;
1173
- /** Show poster before video plays (default: true) */
1174
1656
  showPoster?: boolean;
1175
- /** Show play/pause indicator on tap (default: true) */
1176
1657
  showPlayIndicator?: boolean;
1177
- /** Keep play icon visible while video is paused - YouTube style (default: true) */
1178
1658
  persistPlayIndicatorWhenPaused?: boolean;
1179
- /** Show like animation on double-tap (default: true) */
1180
1659
  showLikeAnimation?: boolean;
1181
- /** Show author info - avatar + follow badge (default: true) */
1182
1660
  showAuthorInfo?: boolean;
1183
- /** Show action bar - like, comment, share (default: true) */
1184
1661
  showActionBar?: boolean;
1185
- /** Show video info - author name, caption, hashtags (default: true) */
1186
1662
  showVideoInfo?: boolean;
1187
- /** Show progress bar (default: true) */
1188
1663
  showProgressBar?: boolean;
1189
- /** Show time display in progress bar (default: true) */
1190
1664
  showTime?: boolean;
1191
- /** Show tooltip when seeking (default: true) */
1192
1665
  showTooltip?: boolean;
1193
- /** Show bookmark button (default: false) */
1194
1666
  showBookmark?: boolean;
1195
- /** Show comment sheet when comment button is clicked (default: true) */
1196
1667
  showCommentSheet?: boolean;
1197
- /** Show debug info (default: false) */
1198
1668
  showDebug?: boolean;
1199
- /** Called when comments button is clicked */
1200
- onOpenComments?: (videoId: string) => void;
1201
- /** Called when share button is clicked */
1202
- onShare?: (video: VideoItem) => void;
1203
- /** Called when like is toggled */
1204
- onLikeToggle?: (isNowLiked: boolean, videoId: string) => void;
1205
- /** Called when bookmark is toggled */
1206
- onBookmarkToggle?: (isNowBookmarked: boolean, videoId: string) => void;
1207
- /** Called when author profile is clicked */
1669
+ showCleanModeOverlay?: boolean;
1670
+ showAdvanceMenu?: boolean;
1671
+ showReportSheet?: boolean;
1672
+ onOpenComments?: () => void;
1673
+ onShare?: (content: VideoItem) => void;
1674
+ onLikeToggle?: (isLiked: boolean) => void;
1675
+ onBookmarkToggle?: (isBookmarked: boolean) => void;
1208
1676
  onOpenProfile?: (author: VideoItem['author']) => void;
1209
- /** Called when follow is toggled */
1210
- onFollowToggle?: (isNowFollowing: boolean, authorId: string) => void;
1211
- /** Called when a hashtag is clicked */
1677
+ onFollowToggle?: (isFollowing: boolean, authorId: string) => void;
1212
1678
  onHashtagClick?: (hashtag: string) => void;
1213
- /** Custom VideoPlayer renderer */
1679
+ onContentHidden?: (contentId: string) => void;
1680
+ onReportSuccess?: (contentId: string) => void;
1681
+ onReportError?: (error: Error) => void;
1682
+ onZoomStateChange?: (isZooming: boolean) => void;
1214
1683
  renderPlayer?: (props: VideoPlayerProps) => ReactNode;
1215
- /** Custom ActionBar renderer */
1216
1684
  renderActionBar?: (props: ActionBarProps) => ReactNode;
1217
- /** Custom AuthorInfo renderer */
1218
1685
  renderAuthorInfo?: (props: AuthorInfoProps) => ReactNode;
1219
- /** Custom VideoInfo renderer */
1220
- renderVideoInfo?: (props: VideoInfoWiredProps) => ReactNode;
1221
- /** Custom ProgressBar renderer */
1686
+ renderVideoInfo?: (props: VideoInfoProps) => ReactNode;
1222
1687
  renderProgressBar?: (props: ProgressBarProps) => ReactNode;
1223
- /** Custom CommentSheet renderer */
1224
- renderCommentSheet?: (props: CommentSheetProps) => ReactNode;
1225
- /** Custom Error renderer */
1688
+ renderCommentSheet?: (props: {
1689
+ videoId: string;
1690
+ isOpen: boolean;
1691
+ onClose: () => void;
1692
+ config?: Partial<CommentConfig>;
1693
+ }) => ReactNode;
1694
+ renderAdvanceMenu?: (props: {
1695
+ content: VideoItem;
1696
+ isOpen: boolean;
1697
+ onClose: () => void;
1698
+ onOpenReport?: () => void;
1699
+ }) => ReactNode;
1700
+ renderReportSheet?: (props: {
1701
+ contentId: string;
1702
+ isOpen: boolean;
1703
+ onClose: () => void;
1704
+ onSuccess?: () => void;
1705
+ onError?: (error: Error) => void;
1706
+ }) => ReactNode;
1226
1707
  renderError?: (error: Error, retry: () => void) => ReactNode;
1227
- /** Current user info for optimistic UI when posting comments */
1228
- currentUser?: CommentAuthor;
1229
- /** Comment configuration (pageSize, maxLength, etc.) */
1230
1708
  commentConfig?: Partial<CommentConfig>;
1231
- /** Like animation style (default: 'float') */
1232
1709
  likeAnimationStyle?: 'float' | 'burst' | 'scale' | 'bounce';
1233
- /** Like animation duration in ms (default: 1200) */
1234
1710
  likeAnimationDuration?: number;
1235
- /** Max hearts in animation (default: 15) */
1236
1711
  likeAnimationMaxHearts?: number;
1237
- /** Heart size in px (default: 70) */
1238
1712
  likeAnimationSize?: number;
1239
- /** Heart color (default: '#fe2c55') */
1240
1713
  likeAnimationColor?: string;
1241
- /** Additional class name */
1714
+ enableZoom?: boolean;
1715
+ zoomMaxScale?: number;
1716
+ zoomSnapBackAnimation?: 'none' | 'ease' | 'spring';
1242
1717
  className?: string;
1243
1718
  }
1244
- declare const DefaultSlot: react.NamedExoticComponent<DefaultSlotProps>;
1719
+ declare const DefaultVideoSlot: react.NamedExoticComponent<DefaultVideoSlotProps>;
1720
+
1721
+ /**
1722
+ * Minimum requirements for content used in SlotComposer
1723
+ */
1724
+ interface SlotContent {
1725
+ /** Unique content ID */
1726
+ id: string;
1727
+ /** Author information */
1728
+ author: Author;
1729
+ /** Content statistics */
1730
+ stats: ContentStats;
1731
+ /** Content type discriminator */
1732
+ type: 'video' | 'article';
1733
+ }
1734
+ /**
1735
+ * State provided by SlotComposer to its children
1736
+ */
1737
+ interface ComposerState {
1738
+ /** Whether clean mode is active */
1739
+ cleanMode: boolean;
1740
+ /** Whether pinch-to-zoom is currently active */
1741
+ isZoomActive: boolean;
1742
+ /** Whether content is liked (reactive) */
1743
+ isLiked: boolean;
1744
+ /** Whether content is bookmarked (local state) */
1745
+ isBookmarked: boolean;
1746
+ /** Whether comment sheet is open */
1747
+ isCommentSheetOpen: boolean;
1748
+ /** Whether advance menu is open */
1749
+ isMenuOpen: boolean;
1750
+ /** Whether report sheet is open */
1751
+ isReportSheetOpen: boolean;
1752
+ }
1753
+ /**
1754
+ * Handlers provided by SlotComposer
1755
+ */
1756
+ interface ComposerHandlers {
1757
+ /** Toggle like status */
1758
+ handleLike: () => void;
1759
+ /** Trigger share action */
1760
+ handleShare: () => void;
1761
+ /** Trigger comment action */
1762
+ handleComment: () => void;
1763
+ /** Toggle bookmark status */
1764
+ handleBookmark: () => void;
1765
+ /** Handle double-tap like (like only) */
1766
+ handleDoubleTap: () => void;
1767
+ /** Handle open report sheet */
1768
+ handleOpenReport: () => void;
1769
+ /** Close all transient UI */
1770
+ handleCloseAll: () => void;
1771
+ /** Set visibility states */
1772
+ setCommentSheetOpen: (open: boolean) => void;
1773
+ setMenuOpen: (open: boolean) => void;
1774
+ setReportSheetOpen: (open: boolean) => void;
1775
+ }
1776
+ /**
1777
+ * Pre-configured wired components
1778
+ */
1779
+ interface ComposerComponents {
1780
+ /** Pre-wired ActionBar */
1781
+ ActionBarComponent: (props: {
1782
+ icons: ActionBarIconSet;
1783
+ onOpenComments?: () => void;
1784
+ onShare?: (content: ContentItem) => void;
1785
+ }) => ReactNode;
1786
+ /** Pre-wired CommentSheet */
1787
+ CommentSheetComponent: (props: {
1788
+ isOpen: boolean;
1789
+ onClose: () => void;
1790
+ initialCount?: number;
1791
+ }) => ReactNode;
1792
+ /** Pre-wired AdvanceMenu */
1793
+ AdvanceMenuComponent: (props: {
1794
+ isOpen: boolean;
1795
+ onClose: () => void;
1796
+ onOpenReport?: () => void;
1797
+ }) => ReactNode;
1798
+ /** Pre-wired ReportSheet */
1799
+ ReportSheetComponent: (props: {
1800
+ isOpen: boolean;
1801
+ onClose: () => void;
1802
+ }) => ReactNode;
1803
+ }
1804
+ /**
1805
+ * Context provided to the children render function
1806
+ */
1807
+ interface ComposerContext<TContent extends SlotContent> {
1808
+ /** Passed content data */
1809
+ content: TContent;
1810
+ /** Aggregate state from SDK hooks */
1811
+ state: ComposerState;
1812
+ /** Aggregate handlers from SDK hooks */
1813
+ handlers: ComposerHandlers;
1814
+ /** Pre-configured wired components */
1815
+ components: ComposerComponents;
1816
+ }
1817
+ interface SlotComposerProps<TContent extends SlotContent> {
1818
+ /** Content data (VideoItem or Article) */
1819
+ content: TContent;
1820
+ /** Render prop function */
1821
+ children: (context: ComposerContext<TContent>) => ReactNode;
1822
+ /** Callback when like is toggled */
1823
+ onLikeToggle?: (isLiked: boolean) => void;
1824
+ /** Callback when bookmark is toggled */
1825
+ onBookmarkToggle?: (isBookmarked: boolean) => void;
1826
+ /** Callback when comments are opened */
1827
+ onOpenComments?: () => void;
1828
+ /** Callback when share is triggered */
1829
+ onShare?: (content: ContentItem) => void;
1830
+ /** Callback when zoom state changes */
1831
+ onZoomStateChange?: (isZooming: boolean) => void;
1832
+ /** Whether to show ActionBar (default: true) */
1833
+ showActionBar?: boolean;
1834
+ /** Whether to show CommentSheet (default: true) */
1835
+ showCommentSheet?: boolean;
1836
+ /** Whether to show AdvanceMenu (default: true) */
1837
+ showAdvanceMenu?: boolean;
1838
+ /** Zoom max scale (default: 5) */
1839
+ zoomMaxScale?: number;
1840
+ /** Snap-back animation style (default: 'ease') */
1841
+ zoomSnapBackAnimation?: 'none' | 'ease' | 'spring';
1842
+ }
1843
+ /**
1844
+ * SlotComposer - Logic-only component for wiring slots
1845
+ *
1846
+ * extraction of shared behavior between DefaultVideoSlot and DefaultArticleSlot.
1847
+ */
1848
+ declare function SlotComposer<TContent extends SlotContent>({ content: initialContent, children, onLikeToggle, onBookmarkToggle, onOpenComments, onShare, onZoomStateChange, showActionBar, showCommentSheet, showAdvanceMenu, zoomMaxScale, zoomSnapBackAnimation, }: SlotComposerProps<TContent>): react_jsx_runtime.JSX.Element;
1849
+ declare namespace SlotComposer {
1850
+ var displayName: string;
1851
+ }
1852
+
1853
+ interface PlaylistBarProps {
1854
+ /** Custom playlist icon */
1855
+ icon?: React.ReactNode;
1856
+ /** Custom arrow icon */
1857
+ arrowIcon?: React.ReactNode;
1858
+ /** Optional label text */
1859
+ label?: string;
1860
+ }
1861
+ /**
1862
+ * Wired PlaylistBar component
1863
+ * Automatically connects to PlaylistContext to display current playlist info.
1864
+ */
1865
+ declare function PlaylistBar({ icon, arrowIcon, label }: PlaylistBarProps): react_jsx_runtime.JSX.Element | null;
1866
+
1867
+ interface PlaylistCollectionProps {
1868
+ /** View type: grid or list */
1869
+ viewType?: 'grid' | 'list';
1870
+ /** Optional callback for navigation when a playlist is selected */
1871
+ onNavigate?: (id: string) => void;
1872
+ /** Custom class name */
1873
+ className?: string;
1874
+ }
1875
+ /**
1876
+ * Wired PlaylistCollection component
1877
+ *
1878
+ * Connects to usePlaylistCollection hook and renders PlaylistCollectionHeadless.
1879
+ * Automatically handles data fetching and i18n.
1880
+ */
1881
+ declare function PlaylistCollection({ viewType, onNavigate, className, }: PlaylistCollectionProps): react_jsx_runtime.JSX.Element;
1882
+
1883
+ interface PlaylistSheetProps {
1884
+ /** Optional custom header accessory */
1885
+ headerAccessory?: ReactNode;
1886
+ /** Optional custom translations */
1887
+ translations?: {
1888
+ title?: string;
1889
+ emptyState?: string;
1890
+ nowPlaying?: string;
1891
+ };
1892
+ }
1893
+ /**
1894
+ * Wired Playlist Sheet Component
1895
+ *
1896
+ * Connects the headless playlist sheet to the SDK store and context.
1897
+ * Automatically handles item selection and visibility state via Global UI Store.
1898
+ */
1899
+ declare function PlaylistSheet({ headerAccessory, translations }: PlaylistSheetProps): react_jsx_runtime.JSX.Element;
1245
1900
 
1246
1901
  /**
1247
1902
  * SDK Configuration Types
@@ -1256,6 +1911,8 @@ declare const DefaultSlot: react.NamedExoticComponent<DefaultSlotProps>;
1256
1911
  interface AdaptersConfig {
1257
1912
  /** Data source adapter for fetching video feed */
1258
1913
  dataSource?: IDataSource;
1914
+ /** Playlist data source adapter for fetching playlists */
1915
+ playlist?: IPlaylistDataSource;
1259
1916
  /** Interaction adapter for like/follow/comment actions */
1260
1917
  interaction?: IInteraction;
1261
1918
  /** Comment adapter for comment operations (read/write/like) */
@@ -1273,6 +1930,64 @@ interface AdaptersConfig {
1273
1930
  /** Poster loader adapter for preloading thumbnails */
1274
1931
  posterLoader?: IPosterLoader;
1275
1932
  }
1933
+ /**
1934
+ * User information for SDK features
1935
+ *
1936
+ * Used by:
1937
+ * - Analytics: Set user context for events
1938
+ * - Comments: Author info for posted comments
1939
+ * - Any feature requiring user identification
1940
+ */
1941
+ interface SDKUser {
1942
+ /** User ID (required) */
1943
+ id: string;
1944
+ /** Display name */
1945
+ name?: string;
1946
+ /** Avatar URL */
1947
+ avatar?: string;
1948
+ /** Additional user properties (for analytics) */
1949
+ properties?: Record<string, unknown>;
1950
+ }
1951
+ /**
1952
+ * User configuration
1953
+ */
1954
+ interface UserConfig {
1955
+ /**
1956
+ * Current user info
1957
+ *
1958
+ * If provided, SDK will:
1959
+ * - Set analytics user context automatically
1960
+ * - Use as default author for comments
1961
+ *
1962
+ * @example
1963
+ * ```tsx
1964
+ * <ShortVideoProvider config={{
1965
+ * user: {
1966
+ * current: {
1967
+ * id: 'user-123',
1968
+ * name: 'John Doe',
1969
+ * avatar: 'https://example.com/avatar.jpg',
1970
+ * },
1971
+ * },
1972
+ * }}>
1973
+ * ```
1974
+ */
1975
+ current?: SDKUser | null;
1976
+ /**
1977
+ * Callback to get user dynamically
1978
+ * Called when user info is needed but `current` is not set
1979
+ *
1980
+ * @example
1981
+ * ```tsx
1982
+ * <ShortVideoProvider config={{
1983
+ * user: {
1984
+ * getUser: () => authStore.getCurrentUser(),
1985
+ * },
1986
+ * }}>
1987
+ * ```
1988
+ */
1989
+ getUser?: () => SDKUser | null | Promise<SDKUser | null>;
1990
+ }
1276
1991
  /**
1277
1992
  * Actions that require authentication
1278
1993
  *
@@ -1287,7 +2002,7 @@ type GuestAction = 'like' | 'unlike' | 'comment' | 'reply' | 'follow' | 'unfollo
1287
2002
  */
1288
2003
  interface GuestActionContext {
1289
2004
  /** Type of resource */
1290
- resourceType: 'video' | 'comment' | 'author';
2005
+ resourceType: 'video' | 'article' | 'comment' | 'author';
1291
2006
  /** Resource ID */
1292
2007
  resourceId: string;
1293
2008
  /** Additional metadata (e.g., videoId for comment, authorId for follow) */
@@ -1451,7 +2166,9 @@ interface SDKConfig {
1451
2166
  prefetchCache?: {
1452
2167
  /** Enable instant loading from cache (default: true) */
1453
2168
  enabled?: boolean;
1454
- /** Number of videos to cache (default: 10) */
2169
+ /** Number of items to cache (default: 10) */
2170
+ maxItems?: number;
2171
+ /** @deprecated Use maxItems instead. Will be removed in v3.0 */
1455
2172
  maxVideos?: number;
1456
2173
  /**
1457
2174
  * Enable dynamic cache eviction (default: true)
@@ -1485,6 +2202,29 @@ interface SDKConfig {
1485
2202
  * ```
1486
2203
  */
1487
2204
  localization?: LocalizationConfig;
2205
+ /**
2206
+ * User configuration
2207
+ *
2208
+ * Provides user info for all SDK features:
2209
+ * - Analytics: User context for events
2210
+ * - Comments: Author info for posted comments
2211
+ *
2212
+ * @example
2213
+ * ```tsx
2214
+ * <ShortVideoProvider config={{
2215
+ * user: {
2216
+ * current: {
2217
+ * id: 'user-123',
2218
+ * name: 'John Doe',
2219
+ * avatar: 'https://example.com/avatar.jpg',
2220
+ * },
2221
+ * },
2222
+ * }}>
2223
+ * <App />
2224
+ * </ShortVideoProvider>
2225
+ * ```
2226
+ */
2227
+ user?: UserConfig;
1488
2228
  /**
1489
2229
  * Enable debug mode
1490
2230
  * When true, enables verbose logging
@@ -1691,15 +2431,26 @@ declare namespace ShortVideoRoot {
1691
2431
  var displayName: string;
1692
2432
  }
1693
2433
 
1694
- declare function ShortVideoProvider({ children, config, fallback, }: ShortVideoProviderProps): ReactElement;
2434
+ declare function ShortVideoProvider({ children, config }: ShortVideoProviderProps): ReactElement;
1695
2435
 
1696
2436
  /**
1697
2437
  * Feed Hooks
1698
2438
  *
1699
- * React hooks for managing video feed state and actions.
2439
+ * React hooks for managing content feed state and actions.
1700
2440
  * Provides SSR-safe subscriptions to FeedManager via useSyncExternalStore.
1701
2441
  */
1702
2442
 
2443
+ /**
2444
+ * Options for useFeed hook
2445
+ */
2446
+ interface UseFeedOptions {
2447
+ /**
2448
+ * Playlist ID to load.
2449
+ * If provided, feed will switch to Playlist mode.
2450
+ * If undefined/null, feed will switch to Recommendation mode.
2451
+ */
2452
+ playlistId?: string;
2453
+ }
1703
2454
  /**
1704
2455
  * Selector-based hook for FeedManager state
1705
2456
  *
@@ -1726,7 +2477,9 @@ declare function useFeedSelector<TSelected>(selector: (state: FeedState) => TSel
1726
2477
  * Return type for useFeed hook
1727
2478
  */
1728
2479
  interface UseFeedReturn {
1729
- /** Array of videos in display order */
2480
+ /** Array of items in display order */
2481
+ items: ContentItem[];
2482
+ /** @deprecated Use items instead. Will be removed in v3.0 */
1730
2483
  videos: VideoItem[];
1731
2484
  /** Whether initial load is in progress */
1732
2485
  loading: boolean;
@@ -1734,19 +2487,23 @@ interface UseFeedReturn {
1734
2487
  loadingMore: boolean;
1735
2488
  /** Current error, if any */
1736
2489
  error: FeedError | null;
1737
- /** Whether more videos can be loaded */
2490
+ /** Whether more items can be loaded */
1738
2491
  hasMore: boolean;
1739
2492
  /** Current pagination cursor */
1740
2493
  cursor: string | null;
1741
2494
  /** Load initial feed data */
1742
2495
  loadInitial: () => Promise<void>;
1743
- /** Load more videos (paginate) */
2496
+ /** Load more items (paginate) */
1744
2497
  loadMore: () => Promise<void>;
1745
2498
  /** Revalidate current feed data */
1746
2499
  revalidate: () => Promise<void>;
1747
- /** Find video by ID */
2500
+ /** Find item by ID */
2501
+ findItem: (id: string) => ContentItem | undefined;
2502
+ /** @deprecated Use findItem instead */
1748
2503
  findVideo: (id: string) => VideoItem | undefined;
1749
- /** Update a single video (for optimistic updates) */
2504
+ /** Update a single item (for optimistic updates) */
2505
+ updateItem: (id: string, updates: Partial<ContentItem>) => void;
2506
+ /** @deprecated Use updateItem instead */
1750
2507
  updateVideo: (id: string, updates: Partial<VideoItem>) => void;
1751
2508
  /** Check if feed data is stale */
1752
2509
  isStale: () => boolean;
@@ -1756,15 +2513,15 @@ interface UseFeedReturn {
1756
2513
  /**
1757
2514
  * Full-featured hook for Feed management
1758
2515
  *
1759
- * Returns state and methods for controlling the video feed.
2516
+ * Returns state and methods for controlling the content feed.
1760
2517
  * Includes request spam prevention for loadMore.
1761
2518
  *
1762
2519
  * @returns Feed state and actions
1763
2520
  *
1764
2521
  * @example
1765
2522
  * ```tsx
1766
- * function VideoFeed() {
1767
- * const { videos, loading, loadMore, hasMore } = useFeed();
2523
+ * function Feed({ playlistId }: { playlistId?: string }) {
2524
+ * const { items, loading, loadMore, hasMore } = useFeed({ playlistId });
1768
2525
  *
1769
2526
  * useEffect(() => {
1770
2527
  * loadInitial();
@@ -1773,8 +2530,8 @@ interface UseFeedReturn {
1773
2530
  * return (
1774
2531
  * <div>
1775
2532
  * {loading && <Spinner />}
1776
- * {videos.map(video => (
1777
- * <VideoCard key={video.id} video={video} />
2533
+ * {items.map(item => (
2534
+ * <ContentCard key={item.id} item={item} />
1778
2535
  * ))}
1779
2536
  * {hasMore && (
1780
2537
  * <button onClick={loadMore}>Load More</button>
@@ -1784,7 +2541,7 @@ interface UseFeedReturn {
1784
2541
  * }
1785
2542
  * ```
1786
2543
  */
1787
- declare function useFeed(): UseFeedReturn;
2544
+ declare function useFeed(options?: UseFeedOptions): UseFeedReturn;
1788
2545
 
1789
2546
  /**
1790
2547
  * Player Hooks
@@ -2247,11 +3004,62 @@ declare function useOptimistic(): {
2247
3004
  };
2248
3005
  type UseOptimisticReturn = ReturnType<typeof useOptimistic>;
2249
3006
 
3007
+ /**
3008
+ * UI State Store
3009
+ *
3010
+ * Simple vanilla store for shared UI state that needs to be synced
3011
+ * across multiple components (e.g., cleanMode, menuState).
3012
+ *
3013
+ * This is separate from Core managers because it's purely UI state
3014
+ * that doesn't affect playback/feed logic.
3015
+ *
3016
+ * Uses the same pattern as Zustand's createStore for compatibility
3017
+ * with React's useSyncExternalStore.
3018
+ */
3019
+ /** What triggered clean mode */
3020
+ type CleanModeTrigger = 'menu' | 'zoom' | 'gesture' | 'api' | null;
3021
+ interface UIState {
3022
+ cleanMode: boolean;
3023
+ cleanModeTrigger: CleanModeTrigger;
3024
+ isMenuOpen: boolean;
3025
+ menuVideoId: string | null;
3026
+ reportedVideoIds: Set<string>;
3027
+ dismissedReportOverlays: Set<string>;
3028
+ isPlaylistSheetOpen: boolean;
3029
+ isGestureLocked: boolean;
3030
+ overlaysVisible: boolean;
3031
+ enableAutoHide: boolean;
3032
+ autoScrollEnabled: boolean;
3033
+ }
3034
+ interface UIActions {
3035
+ enterCleanMode: (trigger: CleanModeTrigger) => void;
3036
+ exitCleanMode: () => void;
3037
+ toggleCleanMode: () => void;
3038
+ openMenu: (videoId: string) => void;
3039
+ closeMenu: () => void;
3040
+ markVideoReported: (videoId: string) => void;
3041
+ isVideoReported: (videoId: string) => boolean;
3042
+ dismissReportOverlay: (videoId: string) => void;
3043
+ isReportOverlayDismissed: (videoId: string) => boolean;
3044
+ openPlaylistSheet: () => void;
3045
+ closePlaylistSheet: () => void;
3046
+ togglePlaylistSheet: () => void;
3047
+ setGestureLock: (isLocked: boolean) => void;
3048
+ setEnableAutoHide: (enabled: boolean) => void;
3049
+ triggerInteraction: () => void;
3050
+ setAutoScrollEnabled: (enabled: boolean) => void;
3051
+ }
3052
+ type UIStore = UIState & UIActions;
3053
+ interface UIStoreApi {
3054
+ getState: () => UIStore;
3055
+ subscribe: (listener: () => void) => () => void;
3056
+ }
3057
+
2250
3058
  /**
2251
3059
  * SDK instance containing all managers
2252
3060
  */
2253
3061
  interface SDKInstance {
2254
- /** Feed manager for video list management */
3062
+ /** Feed manager for item list management */
2255
3063
  feedManager: FeedManager;
2256
3064
  /** Player engine for playback control */
2257
3065
  playerEngine: PlayerEngine;
@@ -2261,6 +3069,12 @@ interface SDKInstance {
2261
3069
  lifecycleManager: LifecycleManager;
2262
3070
  /** Optimistic manager for UI updates */
2263
3071
  optimisticManager: OptimisticManager;
3072
+ /** UI state store (clean mode, menu state, etc.) */
3073
+ uiStore: UIStoreApi;
3074
+ /** Playlist manager */
3075
+ playlistManager: PlaylistManager;
3076
+ /** Playlist collection manager */
3077
+ playlistCollectionManager: PlaylistCollectionManager;
2264
3078
  /** Adapters used by the SDK */
2265
3079
  adapters: {
2266
3080
  dataSource: IDataSource;
@@ -2271,6 +3085,8 @@ interface SDKInstance {
2271
3085
  network: INetworkAdapter;
2272
3086
  videoLoader: IVideoLoader;
2273
3087
  posterLoader: IPosterLoader;
3088
+ /** Playlist adapter (optional) */
3089
+ playlist?: IPlaylistDataSource;
2274
3090
  /** Comment adapter (optional - only if comment feature is enabled) */
2275
3091
  comment?: ICommentAdapter;
2276
3092
  };
@@ -2281,54 +3097,21 @@ interface SDKInstance {
2281
3097
  /** Guest mode config from SDKConfig */
2282
3098
  config?: GuestModeConfig;
2283
3099
  };
3100
+ /** User management */
3101
+ user: {
3102
+ /** Get current user */
3103
+ getUser: () => SDKUser | null;
3104
+ /** Set current user (updates analytics and other features) */
3105
+ setUser: (user: SDKUser | null) => void;
3106
+ /** User config from SDKConfig */
3107
+ config?: UserConfig;
3108
+ };
2284
3109
  /** Destroy all managers and cleanup resources */
2285
3110
  destroy: () => void;
2286
3111
  }
2287
3112
  declare function createSDK(config?: SDKConfig): SDKInstance;
2288
- /**
2289
- * Get or create singleton SDK instance
2290
- *
2291
- * @deprecated Use `createSDK()` with `ShortVideoProvider` instead.
2292
- * Singleton pattern causes issues with:
2293
- * - **SSR**: Instance shared between requests
2294
- * - **Testing**: State persists between tests
2295
- * - **Multiple instances**: Not supported
2296
- *
2297
- * @param config - SDK configuration (only used on first call)
2298
- * @returns Singleton SDK instance
2299
- *
2300
- * @example
2301
- * ```tsx
2302
- * // ❌ DEPRECATED - Don't use this
2303
- * const sdk = getSDK({ feed: { pageSize: 20 } });
2304
- *
2305
- * // ✅ RECOMMENDED - Use createSDK with Provider
2306
- * <ShortVideoProvider config={{ feed: { pageSize: 20 } }}>
2307
- * <App />
2308
- * </ShortVideoProvider>
2309
- * ```
2310
- */
2311
3113
  declare function getSDK(config?: SDKConfig): SDKInstance;
2312
- /**
2313
- * Destroy singleton SDK instance
2314
- *
2315
- * Call this before creating a new instance with different config,
2316
- * or when cleaning up (e.g., in tests).
2317
- */
2318
3114
  declare function destroySDK(): void;
2319
- /**
2320
- * Prefetch feed data (Standalone functionality)
2321
- *
2322
- * Allows host app to fetch data before mounting the SDK components.
2323
- * Data is stored in static in-memory cache and automatically consumed
2324
- * when SDK initializes with the same configuration.
2325
- *
2326
- * @example
2327
- * ```ts
2328
- * // In routing logic or hover handler
2329
- * await prefetchFeed(config);
2330
- * ```
2331
- */
2332
3115
  declare function prefetchFeed(config: SDKConfig, options?: {
2333
3116
  ttl?: number;
2334
3117
  }): Promise<void>;
@@ -2462,8 +3245,6 @@ interface UseCommentOptions {
2462
3245
  videoId: string;
2463
3246
  /** Auto load comments when hook mounts (default: false) */
2464
3247
  autoLoad?: boolean;
2465
- /** Current user info (required for posting) */
2466
- currentUser?: CommentAuthor;
2467
3248
  /** Config overrides */
2468
3249
  config?: Partial<CommentConfig>;
2469
3250
  }
@@ -2762,4 +3543,452 @@ interface UseGuestModeReturn {
2762
3543
  */
2763
3544
  declare function useGuestMode(guestConfigOverride?: GuestModeConfig): UseGuestModeReturn;
2764
3545
 
2765
- export { ActionBar, type ActionBarIconSet, type ActionBarProps as ActionBarWiredProps, type AdaptersConfig, AuthorInfo, type AuthorInfoProps as AuthorInfoWiredProps, CommentSheet, type CommentSheetProps, DEFAULT_LOCALE, DefaultSlot, type DefaultSlotProps, type GuestAction, type GuestActionContext, type GuestModeConfig, type HookReturn, LocalizationAdapter, LocalizationContext, LocalizationProvider, type LocalizationProviderProps, ProgressBar, type ProgressBarProps, type SDKConfig, type SDKInstance, SDKPlayerError, SUPPORTED_LOCALES, ShortVideoProvider, type ShortVideoProviderProps, ShortVideoRoot, type ShortVideoRootProps, type SupportedLocale, type SwipeConfig, type SwipeDirection, type SwipeState, type TranslateFn, type UseCommentOptions, type UseCommentReturn, type UseFeedReturn, type UseGuestModeReturn, type UseLifecycleOptions, type UseLifecycleReturn, type UseOptimisticReturn, type UsePlayerCoreReturn, type UsePlayerForVideoReturn, type UsePlayerReturn, type UseResourceAllocationReturn, type UseResourceReturn, type UseSwipeGestureReturn, type UseTranslationReturn, type VideoElementHandlers, VideoFeed, type VideoFeedProps, VideoInfo, type VideoInfoWiredProps, VideoPlayer, type VideoPlayerProps, VideoSlot, type VideoSlotWiredProps, type WireConfig, createSDK, createSimpleWireConfig, createWiredComponent, destroySDK, enMessages, getMessages, getSDK, isLocaleSupported, messageCatalogs, parseMessage, prefetchFeed, resetCommentManager, useComment, useFeed, useFeedSelector, useGuestMode, useLifecycle, useLifecycleSelector, useLocalization, useOptimistic, useOptimisticSelector, useOptionalLocalization, useOptionalTranslation, usePlayer, usePlayerForVideo, usePlayerSelector, useResource, useResourceAllocation, useResourceSelector, useSDK, useSwipeGesture, useTranslation, viMessages };
3546
+ /**
3547
+ * useUser - Hook for managing current user
3548
+ *
3549
+ * Provides access to current user and ability to update user info.
3550
+ * Changes are automatically propagated to Analytics, Comments, etc.
3551
+ *
3552
+ * @example
3553
+ * ```tsx
3554
+ * function UserProfile() {
3555
+ * const { user, setUser, isLoggedIn } = useUser();
3556
+ *
3557
+ * const handleLogin = (userData) => {
3558
+ * setUser({
3559
+ * id: userData.id,
3560
+ * name: userData.name,
3561
+ * avatar: userData.avatar,
3562
+ * });
3563
+ * };
3564
+ *
3565
+ * const handleLogout = () => {
3566
+ * setUser(null);
3567
+ * };
3568
+ *
3569
+ * return (
3570
+ * <div>
3571
+ * {isLoggedIn ? (
3572
+ * <span>Welcome, {user.name}!</span>
3573
+ * ) : (
3574
+ * <span>Guest</span>
3575
+ * )}
3576
+ * </div>
3577
+ * );
3578
+ * }
3579
+ * ```
3580
+ */
3581
+
3582
+ interface UseUserReturn {
3583
+ /** Current user info (null if not logged in) */
3584
+ user: SDKUser | null;
3585
+ /** Whether user is logged in */
3586
+ isLoggedIn: boolean;
3587
+ /**
3588
+ * Set current user
3589
+ * Pass null to logout/clear user
3590
+ *
3591
+ * This will:
3592
+ * - Update analytics user context
3593
+ * - Update default author for comments
3594
+ */
3595
+ setUser: (user: SDKUser | null) => void;
3596
+ /**
3597
+ * Get user as CommentAuthor format
3598
+ * Returns anonymous user if not logged in
3599
+ */
3600
+ getCommentAuthor: () => CommentAuthor;
3601
+ }
3602
+ /**
3603
+ * Hook for managing current user
3604
+ */
3605
+ declare function useUser(): UseUserReturn;
3606
+
3607
+ /**
3608
+ * useAdvancedControls - Umbrella hook for advanced video controls
3609
+ *
3610
+ * Consolidates speed, quality, clean mode, auto-scroll, report, and menu state
3611
+ * into a single hook for simplified SDK API surface.
3612
+ *
3613
+ * @example
3614
+ * ```tsx
3615
+ * function VideoControls({ videoId }: { videoId: string }) {
3616
+ * const {
3617
+ * speed, setSpeed,
3618
+ * cleanMode, enterCleanMode, exitCleanMode,
3619
+ * isMenuOpen, openMenu, closeMenu,
3620
+ * report, notInterested,
3621
+ * } = useAdvancedControls();
3622
+ *
3623
+ * return (
3624
+ * <div>
3625
+ * <button onClick={() => setSpeed(2)}>2x</button>
3626
+ * <button onClick={openMenu}>Menu</button>
3627
+ * </div>
3628
+ * );
3629
+ * }
3630
+ * ```
3631
+ */
3632
+
3633
+ /**
3634
+ * Speed preset options
3635
+ */
3636
+ declare const SPEED_PRESETS: readonly [0.5, 0.75, 1, 1.25, 1.5, 2];
3637
+ type SpeedPreset = (typeof SPEED_PRESETS)[number];
3638
+ /**
3639
+ * Return type for useAdvancedControls hook
3640
+ */
3641
+ interface UseAdvancedControlsReturn {
3642
+ /** Current playback speed (from PlayerEngine) */
3643
+ speed: number;
3644
+ /** Set playback speed */
3645
+ setSpeed: (speed: number) => void;
3646
+ /** Available speed presets */
3647
+ speedPresets: readonly number[];
3648
+ /** Whether clean mode is active */
3649
+ cleanMode: boolean;
3650
+ /** What triggered clean mode */
3651
+ cleanModeTrigger: CleanModeTrigger;
3652
+ /** Enter clean mode */
3653
+ enterCleanMode: (trigger: CleanModeTrigger) => void;
3654
+ /** Exit clean mode */
3655
+ exitCleanMode: () => void;
3656
+ /** Toggle clean mode */
3657
+ toggleCleanMode: () => void;
3658
+ /** Whether auto-scroll is enabled */
3659
+ autoScroll: boolean;
3660
+ /** Toggle auto-scroll on/off */
3661
+ toggleAutoScroll: () => void;
3662
+ /** Set auto-scroll state */
3663
+ setAutoScroll: (enabled: boolean) => void;
3664
+ /** Current quality level (-1 = auto) */
3665
+ quality: number;
3666
+ /** Set quality level (-1 for auto) */
3667
+ setQuality: (level: number) => void;
3668
+ /** Available quality levels (from HLS.js) */
3669
+ qualityLevels: VideoQuality[];
3670
+ /** Whether quality is in auto mode */
3671
+ isAutoQuality: boolean;
3672
+ /**
3673
+ * Report content
3674
+ * @param contentId - ID of content to report
3675
+ * @param reason - Report reason code
3676
+ * @param description - Optional additional description
3677
+ */
3678
+ report: (contentId: string, reason: string, description?: string) => Promise<void>;
3679
+ /**
3680
+ * Get available report reasons
3681
+ */
3682
+ getReportReasons: () => Promise<ReportReason[]>;
3683
+ /**
3684
+ * Mark content as "not interested"
3685
+ * @param contentId - ID of content
3686
+ */
3687
+ notInterested: (contentId: string) => Promise<void>;
3688
+ /**
3689
+ * Download content (delegated to host app via callback)
3690
+ * @param content - Content item to download
3691
+ */
3692
+ download: (content: ContentItem) => Promise<void>;
3693
+ /** Whether advanced menu is open */
3694
+ isMenuOpen: boolean;
3695
+ /** Open advanced menu */
3696
+ openMenu: () => void;
3697
+ /** Close advanced menu */
3698
+ closeMenu: () => void;
3699
+ /** Toggle menu open/close */
3700
+ toggleMenu: () => void;
3701
+ /** Whether report is in progress */
3702
+ isReporting: boolean;
3703
+ /** Whether not-interested action is in progress */
3704
+ isMarkingNotInterested: boolean;
3705
+ }
3706
+ /**
3707
+ * Configuration for useAdvancedControls
3708
+ */
3709
+ interface UseAdvancedControlsConfig {
3710
+ /** Callback when download is requested (handled by host app) */
3711
+ onDownload?: (content: ContentItem) => Promise<void>;
3712
+ /** Callback when content is hidden via "not interested" */
3713
+ onContentHidden?: (contentId: string) => void;
3714
+ }
3715
+ /**
3716
+ * Advanced controls hook
3717
+ *
3718
+ * Combines multiple control features into a single hook:
3719
+ * - Speed control (uses PlayerEngine)
3720
+ * - Clean mode (local state)
3721
+ * - Auto-scroll (persisted to storage)
3722
+ * - Quality control (placeholder for HLS.js integration)
3723
+ * - Report/Not interested actions (uses IInteraction adapter)
3724
+ * - Menu state (local state)
3725
+ */
3726
+ declare function useAdvancedControls(config?: UseAdvancedControlsConfig): UseAdvancedControlsReturn;
3727
+
3728
+ /**
3729
+ * Snap-back animation type
3730
+ * - 'none': Instant snap (no animation)
3731
+ * - 'ease': Smooth ease-out transition
3732
+ * - 'spring': Spring-like bounce effect
3733
+ */
3734
+ type SnapBackAnimation = 'none' | 'ease' | 'spring';
3735
+ /**
3736
+ * Configuration for useZoomGesture
3737
+ */
3738
+ interface UseZoomGestureConfig {
3739
+ /** Minimum scale factor (default: 1) */
3740
+ minScale?: number;
3741
+ /** Maximum scale factor (default: 5) */
3742
+ maxScale?: number;
3743
+ /** Whether to lock vertical swipe during zoom (default: true) */
3744
+ lockVerticalSwipe?: boolean;
3745
+ /**
3746
+ * Snap-back animation type (default: 'ease')
3747
+ * - 'none': Instant snap, onZoomEnd called immediately
3748
+ * - 'ease': Smooth ease-out, onZoomEnd called after duration
3749
+ * - 'spring': Spring bounce effect, onZoomEnd called after duration
3750
+ */
3751
+ snapBackAnimation?: SnapBackAnimation;
3752
+ /** Snap-back animation duration in ms (default: 200) */
3753
+ snapBackDuration?: number;
3754
+ /** Callback when zoom starts (scale > 1) */
3755
+ onZoomStart?: () => void;
3756
+ /** Callback when zoom ends (after snap-back animation completes) */
3757
+ onZoomEnd?: (finalScale: number) => void;
3758
+ /** Callback on scale change during gesture */
3759
+ onZoomChange?: (scale: number) => void;
3760
+ /** Whether zoom is enabled (default: true) */
3761
+ enabled?: boolean;
3762
+ }
3763
+ /**
3764
+ * Touch event handlers
3765
+ */
3766
+ interface ZoomGestureHandlers {
3767
+ onTouchStart: (e: react__default.TouchEvent) => void;
3768
+ onTouchMove: (e: react__default.TouchEvent) => void;
3769
+ onTouchEnd: (e: react__default.TouchEvent) => void;
3770
+ }
3771
+ /**
3772
+ * Return type for useZoomGesture
3773
+ */
3774
+ interface UseZoomGestureReturn {
3775
+ /** Current scale factor (1 = normal) */
3776
+ scale: number;
3777
+ /** Current X translation in pixels */
3778
+ translateX: number;
3779
+ /** Current Y translation in pixels */
3780
+ translateY: number;
3781
+ /** Whether actively zooming (2+ fingers) */
3782
+ isZooming: boolean;
3783
+ /** Whether currently panning while zoomed */
3784
+ isPanning: boolean;
3785
+ /** Whether snap-back animation is playing */
3786
+ isAnimating: boolean;
3787
+ /** Whether vertical swipe is locked */
3788
+ isSwipeLocked: boolean;
3789
+ /** Touch event handlers to spread on container */
3790
+ handlers: ZoomGestureHandlers;
3791
+ /** Manually reset to default state */
3792
+ reset: () => void;
3793
+ /** CSS transform string for convenience */
3794
+ transform: string;
3795
+ /** CSS transform-origin string */
3796
+ transformOrigin: string;
3797
+ /**
3798
+ * CSS styles for container element.
3799
+ * Includes `touchAction: 'none'` for proper gesture handling on mobile.
3800
+ * Always spread this on your container for best compatibility.
3801
+ */
3802
+ containerStyle: react__default.CSSProperties;
3803
+ /**
3804
+ * CSS transition string for snap-back animation.
3805
+ * Apply this to the transform property for smooth animations.
3806
+ */
3807
+ transitionStyle: react__default.CSSProperties;
3808
+ }
3809
+ /**
3810
+ * useZoomGesture - Pinch-to-zoom gesture handler
3811
+ *
3812
+ * Handles pinch gestures for video zoom with pan support.
3813
+ * Always snaps back to scale=1 on release (Instagram Reels behavior).
3814
+ *
3815
+ * @example
3816
+ * ```tsx
3817
+ * function VideoPlayer({ video }: { video: VideoItem }) {
3818
+ * const {
3819
+ * transform,
3820
+ * transformOrigin,
3821
+ * containerStyle,
3822
+ * handlers,
3823
+ * isZooming,
3824
+ * } = useZoomGesture({
3825
+ * onZoomStart: () => enterCleanMode('zoom'),
3826
+ * onZoomEnd: () => exitCleanMode(),
3827
+ * snapBackAnimation: 'ease', // smooth snap-back
3828
+ * });
3829
+ *
3830
+ * return (
3831
+ * <div
3832
+ * style={{
3833
+ * ...containerStyle, // includes touchAction: 'none' for proper gesture handling
3834
+ * transform,
3835
+ * transformOrigin,
3836
+ * }}
3837
+ * {...handlers}
3838
+ * >
3839
+ * <video src={video.url} />
3840
+ * </div>
3841
+ * );
3842
+ * }
3843
+ * ```
3844
+ *
3845
+ * @remarks
3846
+ * **Important:** Always spread `containerStyle` on your container element.
3847
+ * This sets `touchAction: 'none'` which is required for `preventDefault()` to work
3848
+ * on mobile browsers (Chrome 56+, Safari) where touch events are passive by default.
3849
+ */
3850
+ declare function useZoomGesture(config?: UseZoomGestureConfig): UseZoomGestureReturn;
3851
+
3852
+ /**
3853
+ * useReportedVideo - Hook to check/manage reported video state
3854
+ *
3855
+ * Provides methods to check if a video has been reported and
3856
+ * whether the overlay has been dismissed by the user.
3857
+ */
3858
+ interface UseReportedVideoReturn {
3859
+ /** Whether this video has been reported by user */
3860
+ isReported: boolean;
3861
+ /** Whether user dismissed the overlay (wants to watch anyway) */
3862
+ isOverlayDismissed: boolean;
3863
+ /** Should show the reported overlay (reported AND not dismissed) */
3864
+ shouldShowOverlay: boolean;
3865
+ /** Mark video as reported */
3866
+ markReported: () => void;
3867
+ /** Dismiss the overlay (user wants to watch) */
3868
+ dismissOverlay: () => void;
3869
+ }
3870
+ /**
3871
+ * Hook to manage reported video state for a specific video
3872
+ *
3873
+ * @param videoId - The video ID to check
3874
+ * @returns Object with reported state and actions
3875
+ */
3876
+ declare function useReportedVideo(videoId: string): UseReportedVideoReturn;
3877
+
3878
+ /**
3879
+ * useArticle - Hook for managing Article state and interactions
3880
+ *
3881
+ * Features:
3882
+ * - Article carousel navigation
3883
+ * - Music playback control
3884
+ * - Detail view state
3885
+ * - Like/unlike
3886
+ * - Share
3887
+ */
3888
+
3889
+ interface UseArticleConfig {
3890
+ /** The article data */
3891
+ article: Article;
3892
+ /** Initial image index (default: 0) */
3893
+ initialIndex?: number;
3894
+ /** Music info for this article (optional) */
3895
+ music?: MusicInfo;
3896
+ /** Whether this article is currently active (visible) */
3897
+ isActive?: boolean;
3898
+ }
3899
+ interface UseArticleReturn {
3900
+ /** Current image index in carousel */
3901
+ currentIndex: number;
3902
+ /** Set current image index */
3903
+ setCurrentIndex: (index: number) => void;
3904
+ /** Go to next image */
3905
+ next: () => void;
3906
+ /** Go to previous image */
3907
+ prev: () => void;
3908
+ /** Total number of images */
3909
+ totalImages: number;
3910
+ /** Whether can go to next image */
3911
+ hasNext: boolean;
3912
+ /** Whether can go to previous image */
3913
+ hasPrev: boolean;
3914
+ /** Whether music is playing */
3915
+ isMusicPlaying: boolean;
3916
+ /** Toggle music playback */
3917
+ toggleMusic: () => void;
3918
+ /** Play music */
3919
+ playMusic: () => void;
3920
+ /** Pause music */
3921
+ pauseMusic: () => void;
3922
+ /** Timestamp of last user music toggle (for PlayIndicator) */
3923
+ lastMusicToggleTime: number;
3924
+ /** Whether detail view is open */
3925
+ isDetailOpen: boolean;
3926
+ /** Open detail view */
3927
+ openDetail: () => void;
3928
+ /** Close detail view */
3929
+ closeDetail: () => void;
3930
+ /** Whether article is liked by current user */
3931
+ isLiked: boolean;
3932
+ /** Like the article */
3933
+ handleLike: () => Promise<void>;
3934
+ /** Unlike the article */
3935
+ handleUnlike: () => Promise<void>;
3936
+ /** Toggle like state */
3937
+ toggleLike: () => Promise<void>;
3938
+ /** Share the article */
3939
+ handleShare: () => Promise<void>;
3940
+ }
3941
+ declare function useArticle({ article, initialIndex, music, isActive, }: UseArticleConfig): UseArticleReturn;
3942
+ /** @deprecated Use useArticle instead */
3943
+ declare const useImagePost: typeof useArticle;
3944
+ /** @deprecated Use UseArticleConfig instead */
3945
+ type UseImagePostConfig = UseArticleConfig;
3946
+ /** @deprecated Use UseArticleReturn instead */
3947
+ type UseImagePostReturn = UseArticleReturn;
3948
+
3949
+ /**
3950
+ * Playlist Context Value
3951
+ */
3952
+ interface PlaylistContextValue {
3953
+ /** Current active playlist ID */
3954
+ playlistId: string | null;
3955
+ /** List of items in the playlist */
3956
+ items: ContentItem[];
3957
+ /** Current active item index */
3958
+ currentIndex: number;
3959
+ /** Loading status */
3960
+ status: 'idle' | 'loading' | 'error';
3961
+ /** Error information */
3962
+ error: Error | null;
3963
+ /** Playlist metadata */
3964
+ metadata: {
3965
+ title: string;
3966
+ description?: string;
3967
+ cover?: string;
3968
+ totalItems: number;
3969
+ } | null;
3970
+ /** Load a playlist by ID */
3971
+ loadPlaylist: (id: string) => Promise<void>;
3972
+ /** Set playlist data directly */
3973
+ setPlaylist: (playlist: PlaylistData) => void;
3974
+ /** Go to next item */
3975
+ next: () => void;
3976
+ /** Go to previous item */
3977
+ prev: () => void;
3978
+ /** Jump to a specific index */
3979
+ jumpTo: (index: number) => void;
3980
+ /** Reset store state */
3981
+ reset: () => void;
3982
+ }
3983
+ /**
3984
+ * PlaylistProvider - Connects PlaylistManager to React Context
3985
+ */
3986
+ declare function PlaylistProvider({ children }: {
3987
+ children: ReactNode;
3988
+ }): react_jsx_runtime.JSX.Element;
3989
+ /**
3990
+ * Hook to access Playlist state and actions
3991
+ */
3992
+ declare function usePlaylist(): PlaylistContextValue;
3993
+
3994
+ export { ActionBar, type ActionBarIconSet, type ActionBarProps as ActionBarWiredProps, type AdaptersConfig, AdvanceMenu, type AdvanceMenuProps, AdvanceMenuWired, type AdvanceMenuWiredProps, ArticleSlot, type ArticleSlotWiredProps, AuthorInfo, type AuthorInfoProps as AuthorInfoWiredProps, CleanModeOverlay as CleanModeOverlayWired, type CleanModeOverlayProps as CleanModeOverlayWiredProps, type CleanModeTrigger, CommentSheet, type CommentSheetProps, type ComposerContext, DEFAULT_LOCALE, DefaultArticleSlot, type DefaultArticleSlotProps, DefaultImagePostSlot, type DefaultImagePostSlotProps, DefaultVideoSlot as DefaultSlot, type DefaultVideoSlotProps as DefaultSlotProps, type DragAxis, type DragBind, type DragConfig, type DragHandler, type DragState, Feed, type FeedProps, type GuestAction, type GuestActionContext, type GuestModeConfig, type HookReturn, ImagePostSlot, type ImagePostSlotWiredProps, LocalizationAdapter, LocalizationContext, LocalizationProvider, type LocalizationProviderProps, PlaylistBar, PlaylistCollection, type PlaylistCollectionProps, type PlaylistContextValue, PlaylistProvider, PlaylistSheet, ProgressBar, type ProgressBarProps, ReportSheet, type ReportSheetProps, ReportSheetWired, type ReportSheetWiredProps, type SDKConfig, type SDKInstance, SDKPlayerError, type SDKUser, SPEED_PRESETS, SUPPORTED_LOCALES, ShortVideoProvider, type ShortVideoProviderProps, ShortVideoRoot, type ShortVideoRootProps, SlotComposer, type SlotComposerProps, type SpeedPreset, type SupportedLocale, type SwipeConfig, type SwipeDirection, type SwipeState, type TranslateFn, type UseAdvancedControlsConfig, type UseAdvancedControlsReturn, type UseArticleConfig, type UseArticleReturn, type UseCommentOptions, type UseCommentReturn, type UseFeedReturn, type UseGuestModeReturn, type UseImagePostConfig, type UseImagePostReturn, type UseLifecycleOptions, type UseLifecycleReturn, type UseOptimisticReturn, type UsePlayerCoreReturn, type UsePlayerForVideoReturn, type UsePlayerReturn, type UseReportedVideoReturn, type UseResourceAllocationReturn, type UseResourceReturn, type UseSwipeGestureReturn, type UseTranslationReturn, type UseUserReturn, type UseZoomGestureConfig, type UseZoomGestureReturn, type UserConfig, type VideoElementHandlers, VideoFeed, type VideoFeedProps, VideoInfo, type VideoInfoWiredProps, VideoPlayer, type VideoPlayerProps, VideoSlot, type VideoSlotWiredProps, type WireConfig, type ZoomGestureHandlers, createSDK, createSimpleWireConfig, createWiredComponent, destroySDK, enMessages, getMessages, getSDK, isLocaleSupported, messageCatalogs, parseMessage, prefetchFeed, resetCommentManager, useAdvancedControls, useArticle, useComment, useFeed, useFeedSelector, useGuestMode, useImagePost, useLifecycle, useLifecycleSelector, useLocalization, useOptimistic, useOptimisticSelector, useOptionalLocalization, useOptionalTranslation, usePlayer, usePlayerForVideo, usePlayerSelector, usePlaylist, useReportedVideo, useResource, useResourceAllocation, useResourceSelector, useSDK, useSwipeGesture, useTouchDrag, useTranslation, useUser, useZoomGesture, viMessages };