@xhub-reels/sdk 0.1.17 → 0.1.19

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/index.cjs CHANGED
@@ -741,9 +741,12 @@ var OptimisticManager = class {
741
741
  }
742
742
  };
743
743
  var DEFAULT_RESOURCE_CONFIG = {
744
- maxAllocations: 11,
745
- bufferWindow: 3,
746
- warmWindow: 4,
744
+ // bufferWindow=2 → ±2 hot slots + 1 active = 5 HLS instances max.
745
+ // Reduced from 3 to eliminate media pipeline thread contention on iOS/Android WebView
746
+ // that caused jank after scrolling 4-5 videos (7 concurrent HLS instances was too many).
747
+ maxAllocations: 8,
748
+ bufferWindow: 2,
749
+ warmWindow: 3,
747
750
  // 0ms debounce — setFocusedIndexImmediate is already used post-snap.
748
751
  focusDebounceMs: 0,
749
752
  preloadLookAhead: 3
@@ -1393,7 +1396,7 @@ function mapHlsError(data) {
1393
1396
  }
1394
1397
  }
1395
1398
  function useHls(options) {
1396
- const { src, videoRef, isActive, isPrefetch, bufferTier = "active", hlsConfig, onError } = options;
1399
+ const { src, videoRef, isActive, isPrefetch, bufferTier = "active", hlsConfig, onError, isDragging = false } = options;
1397
1400
  const isHlsSupported = typeof window !== "undefined" && Hls__default.default.isSupported();
1398
1401
  const isNative = supportsNativeHls();
1399
1402
  const isHlsJs = isHlsSupported && !isNative;
@@ -1549,6 +1552,21 @@ function useHls(options) {
1549
1552
  }
1550
1553
  }
1551
1554
  }, [bufferTier]);
1555
+ react.useEffect(() => {
1556
+ const hls = hlsRef.current;
1557
+ if (!hls || isActive) return;
1558
+ if (isDragging) {
1559
+ try {
1560
+ hls.stopLoad();
1561
+ } catch {
1562
+ }
1563
+ } else {
1564
+ try {
1565
+ hls.startLoad(-1);
1566
+ } catch {
1567
+ }
1568
+ }
1569
+ }, [isDragging, isActive]);
1552
1570
  return {
1553
1571
  isHlsJs,
1554
1572
  isReady,
@@ -1705,7 +1723,8 @@ function DefaultPauseAction() {
1705
1723
  }
1706
1724
  );
1707
1725
  }
1708
- var PLAY_AHEAD_MAX_CONCURRENT = 2;
1726
+ var PLAY_AHEAD_MAX_CONCURRENT = 3;
1727
+ var PLAY_AHEAD_STAGGER_MS = 50;
1709
1728
  var _playAheadActive = 0;
1710
1729
  var _playAheadQueue = [];
1711
1730
  function acquirePlayAhead() {
@@ -1713,7 +1732,12 @@ function acquirePlayAhead() {
1713
1732
  _playAheadActive++;
1714
1733
  return Promise.resolve();
1715
1734
  }
1716
- return new Promise((resolve) => _playAheadQueue.push(resolve));
1735
+ return new Promise((resolve) => {
1736
+ const queuePosition = _playAheadQueue.length;
1737
+ _playAheadQueue.push(() => {
1738
+ setTimeout(resolve, PLAY_AHEAD_STAGGER_MS * queuePosition);
1739
+ });
1740
+ });
1717
1741
  }
1718
1742
  function releasePlayAhead() {
1719
1743
  _playAheadActive = Math.max(0, _playAheadActive - 1);
@@ -1734,6 +1758,7 @@ function VideoSlot({
1734
1758
  onToggleMute,
1735
1759
  onAutoplayBlocked,
1736
1760
  showFps = false,
1761
+ isDragging = false,
1737
1762
  renderOverlay,
1738
1763
  renderActions,
1739
1764
  renderPauseAction
@@ -1771,6 +1796,7 @@ function VideoSlot({
1771
1796
  onToggleMute,
1772
1797
  onAutoplayBlocked,
1773
1798
  showFps,
1799
+ isDragging,
1774
1800
  renderOverlay,
1775
1801
  renderActions,
1776
1802
  renderPauseAction,
@@ -1790,6 +1816,7 @@ function VideoSlotInner({
1790
1816
  onToggleMute,
1791
1817
  onAutoplayBlocked,
1792
1818
  showFps,
1819
+ isDragging,
1793
1820
  renderOverlay,
1794
1821
  renderActions,
1795
1822
  renderPauseAction,
@@ -1811,6 +1838,7 @@ function VideoSlotInner({
1811
1838
  // so useHls creates the HLS instance and starts buffering
1812
1839
  isPrefetch: isPrefetch || isPreloaded,
1813
1840
  bufferTier,
1841
+ isDragging,
1814
1842
  onError: (code, message) => {
1815
1843
  console.error(`[VideoSlot] HLS error: ${code} \u2014 ${message}`);
1816
1844
  }
@@ -1947,6 +1975,7 @@ function VideoSlotInner({
1947
1975
  video.muted = isActive ? isMuted : true;
1948
1976
  }, [isMuted, isActive]);
1949
1977
  const showPosterOverlay = !isReady && !hasPlayedAhead;
1978
+ const isPreDecoded = hasPlayedAhead;
1950
1979
  const [showMuteIndicator, setShowMuteIndicator] = react.useState(false);
1951
1980
  const muteIndicatorTimer = react.useRef(null);
1952
1981
  const handleToggleMute = react.useCallback(() => {
@@ -2062,13 +2091,14 @@ function VideoSlotInner({
2062
2091
  width: "100%",
2063
2092
  height: "100%",
2064
2093
  objectFit: "cover",
2065
- // Hide video until ready to avoid black frame flash
2094
+ // Hide video until ready to avoid black frame flash.
2095
+ // When pre-decoded, skip transition — first frame is already on canvas.
2066
2096
  opacity: showPosterOverlay ? 0 : 1,
2067
- transition: "opacity 0.15s ease"
2097
+ transition: isPreDecoded ? "none" : "opacity 0.15s ease"
2068
2098
  }
2069
2099
  }
2070
2100
  ),
2071
- item.poster && /* @__PURE__ */ jsxRuntime.jsx(
2101
+ item.poster && !isPreDecoded && /* @__PURE__ */ jsxRuntime.jsx(
2072
2102
  "div",
2073
2103
  {
2074
2104
  style: {
@@ -2198,6 +2228,7 @@ function FpsCounter() {
2198
2228
  );
2199
2229
  }
2200
2230
  var RENDER_WINDOW_RADIUS = 3;
2231
+ var PLACEHOLDER_EXTRA = 2;
2201
2232
  var centerStyle = {
2202
2233
  height: "100dvh",
2203
2234
  display: "flex",
@@ -2275,9 +2306,20 @@ function ReelsFeed({
2275
2306
  }
2276
2307
  };
2277
2308
  rebuild();
2278
- const observer = new MutationObserver(rebuild);
2309
+ let rebuildTimer = null;
2310
+ const debouncedRebuild = () => {
2311
+ if (rebuildTimer !== null) return;
2312
+ rebuildTimer = setTimeout(() => {
2313
+ rebuildTimer = null;
2314
+ rebuild();
2315
+ }, 16);
2316
+ };
2317
+ const observer = new MutationObserver(debouncedRebuild);
2279
2318
  observer.observe(container, { childList: true, subtree: true });
2280
- return () => observer.disconnect();
2319
+ return () => {
2320
+ observer.disconnect();
2321
+ if (rebuildTimer !== null) clearTimeout(rebuildTimer);
2322
+ };
2281
2323
  }, [items.length, focusedIndex]);
2282
2324
  const containerHeight = react.useRef(
2283
2325
  typeof window !== "undefined" ? window.innerHeight : 800
@@ -2403,6 +2445,15 @@ function ReelsFeed({
2403
2445
  const handleToggleMute = react.useCallback(() => {
2404
2446
  setIsMuted((prev) => !prev);
2405
2447
  }, []);
2448
+ const windowedItems = react.useMemo(() => {
2449
+ const start = Math.max(0, focusedIndex - RENDER_WINDOW_RADIUS - PLACEHOLDER_EXTRA);
2450
+ const end = Math.min(items.length - 1, focusedIndex + RENDER_WINDOW_RADIUS + PLACEHOLDER_EXTRA);
2451
+ const result = [];
2452
+ for (let i = start; i <= end; i++) {
2453
+ result.push({ item: items[i], index: i });
2454
+ }
2455
+ return result;
2456
+ }, [items, focusedIndex]);
2406
2457
  if (loading && items.length === 0) {
2407
2458
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...centerStyle, flexDirection: "column", gap: 0 }, children: renderLoading ? renderLoading() : /* @__PURE__ */ jsxRuntime.jsx(DefaultSkeleton, {}) });
2408
2459
  }
@@ -2439,7 +2490,7 @@ function ReelsFeed({
2439
2490
  to { transform: rotate(360deg); }
2440
2491
  }
2441
2492
  ` }),
2442
- items.map((item, index) => {
2493
+ windowedItems.map(({ item, index }) => {
2443
2494
  const distFromFocus = Math.abs(index - focusedIndex);
2444
2495
  const wrapperStyle = {
2445
2496
  position: "absolute",
@@ -2474,6 +2525,7 @@ function ReelsFeed({
2474
2525
  onToggleMute: handleToggleMute,
2475
2526
  onAutoplayBlocked,
2476
2527
  showFps: showFps && isActive,
2528
+ isDragging: isDragMuted,
2477
2529
  renderOverlay,
2478
2530
  renderActions,
2479
2531
  renderPauseAction
package/dist/index.d.cts CHANGED
@@ -577,7 +577,10 @@ declare class OptimisticManager {
577
577
  * Tier 4 (Cold): preloadLookAhead beyond warm — metadata/manifest prefetch only (no DOM)
578
578
  *
579
579
  * Total DOM nodes = 1 + 2×bufferWindow + warmWindow (forward) + 1 (backward)
580
- * Default: 1 + 6 + 4 = 11 DOM nodes, ~112 MB memory (fits comfortably in 1 GB budget)
580
+ * Default: 1 + 4 + 3 = 8 DOM nodes, ~80 MB memory (fits comfortably in 1 GB budget)
581
+ *
582
+ * bufferWindow reduced from 3→2 to cut concurrent HLS instances from 7→5,
583
+ * eliminating media pipeline thread contention on iOS/Android WebView.
581
584
  */
582
585
 
583
586
  interface ResourceState {
@@ -831,6 +834,13 @@ interface UseHlsOptions {
831
834
  hlsConfig?: Partial<HlsConfig>;
832
835
  /** Called when hls.js encounters a fatal error. Maps to PlayerEngine error codes. */
833
836
  onError?: (code: 'NETWORK_ERROR' | 'MEDIA_ERROR' | 'DECODE_ERROR' | 'UNKNOWN', message: string) => void;
837
+ /**
838
+ * Whether the user is currently dragging/swiping.
839
+ * When true, non-active slots pause HLS network fetching (hls.stopLoad) to
840
+ * free bandwidth for the active slot and reduce main-thread contention.
841
+ * Active slot is never paused regardless of this flag.
842
+ */
843
+ isDragging?: boolean;
834
844
  }
835
845
  interface UseHlsReturn {
836
846
  /** Whether hls.js is being used (false = native HLS on Safari) */
@@ -854,11 +864,16 @@ interface VideoSlotProps {
854
864
  /** Called when unmuted autoplay fails and SDK falls back to muted playback */
855
865
  onAutoplayBlocked?: () => void;
856
866
  showFps?: boolean;
867
+ /**
868
+ * Whether the user is currently dragging. Passed to useHls to pause
869
+ * non-active HLS fetching during gesture, freeing bandwidth + main thread.
870
+ */
871
+ isDragging?: boolean;
857
872
  renderOverlay?: (item: ContentItem, actions: SlotActions) => react.ReactNode;
858
873
  renderActions?: (item: ContentItem, actions: SlotActions) => react.ReactNode;
859
874
  renderPauseAction?: (item: ContentItem, actions: PauseSlotActions) => react.ReactNode;
860
875
  }
861
- declare function VideoSlot({ item, index, isActive, isPrefetch, isPreloaded, bufferTier, isMuted, onToggleMute, onAutoplayBlocked, showFps, renderOverlay, renderActions, renderPauseAction, }: VideoSlotProps): react_jsx_runtime.JSX.Element;
876
+ declare function VideoSlot({ item, index, isActive, isPrefetch, isPreloaded, bufferTier, isMuted, onToggleMute, onAutoplayBlocked, showFps, isDragging, renderOverlay, renderActions, renderPauseAction, }: VideoSlotProps): react_jsx_runtime.JSX.Element;
862
877
 
863
878
  declare function DefaultOverlay({ item }: {
864
879
  item: ContentItem;
package/dist/index.d.ts CHANGED
@@ -577,7 +577,10 @@ declare class OptimisticManager {
577
577
  * Tier 4 (Cold): preloadLookAhead beyond warm — metadata/manifest prefetch only (no DOM)
578
578
  *
579
579
  * Total DOM nodes = 1 + 2×bufferWindow + warmWindow (forward) + 1 (backward)
580
- * Default: 1 + 6 + 4 = 11 DOM nodes, ~112 MB memory (fits comfortably in 1 GB budget)
580
+ * Default: 1 + 4 + 3 = 8 DOM nodes, ~80 MB memory (fits comfortably in 1 GB budget)
581
+ *
582
+ * bufferWindow reduced from 3→2 to cut concurrent HLS instances from 7→5,
583
+ * eliminating media pipeline thread contention on iOS/Android WebView.
581
584
  */
582
585
 
583
586
  interface ResourceState {
@@ -831,6 +834,13 @@ interface UseHlsOptions {
831
834
  hlsConfig?: Partial<HlsConfig>;
832
835
  /** Called when hls.js encounters a fatal error. Maps to PlayerEngine error codes. */
833
836
  onError?: (code: 'NETWORK_ERROR' | 'MEDIA_ERROR' | 'DECODE_ERROR' | 'UNKNOWN', message: string) => void;
837
+ /**
838
+ * Whether the user is currently dragging/swiping.
839
+ * When true, non-active slots pause HLS network fetching (hls.stopLoad) to
840
+ * free bandwidth for the active slot and reduce main-thread contention.
841
+ * Active slot is never paused regardless of this flag.
842
+ */
843
+ isDragging?: boolean;
834
844
  }
835
845
  interface UseHlsReturn {
836
846
  /** Whether hls.js is being used (false = native HLS on Safari) */
@@ -854,11 +864,16 @@ interface VideoSlotProps {
854
864
  /** Called when unmuted autoplay fails and SDK falls back to muted playback */
855
865
  onAutoplayBlocked?: () => void;
856
866
  showFps?: boolean;
867
+ /**
868
+ * Whether the user is currently dragging. Passed to useHls to pause
869
+ * non-active HLS fetching during gesture, freeing bandwidth + main thread.
870
+ */
871
+ isDragging?: boolean;
857
872
  renderOverlay?: (item: ContentItem, actions: SlotActions) => react.ReactNode;
858
873
  renderActions?: (item: ContentItem, actions: SlotActions) => react.ReactNode;
859
874
  renderPauseAction?: (item: ContentItem, actions: PauseSlotActions) => react.ReactNode;
860
875
  }
861
- declare function VideoSlot({ item, index, isActive, isPrefetch, isPreloaded, bufferTier, isMuted, onToggleMute, onAutoplayBlocked, showFps, renderOverlay, renderActions, renderPauseAction, }: VideoSlotProps): react_jsx_runtime.JSX.Element;
876
+ declare function VideoSlot({ item, index, isActive, isPrefetch, isPreloaded, bufferTier, isMuted, onToggleMute, onAutoplayBlocked, showFps, isDragging, renderOverlay, renderActions, renderPauseAction, }: VideoSlotProps): react_jsx_runtime.JSX.Element;
862
877
 
863
878
  declare function DefaultOverlay({ item }: {
864
879
  item: ContentItem;
package/dist/index.js CHANGED
@@ -735,9 +735,12 @@ var OptimisticManager = class {
735
735
  }
736
736
  };
737
737
  var DEFAULT_RESOURCE_CONFIG = {
738
- maxAllocations: 11,
739
- bufferWindow: 3,
740
- warmWindow: 4,
738
+ // bufferWindow=2 → ±2 hot slots + 1 active = 5 HLS instances max.
739
+ // Reduced from 3 to eliminate media pipeline thread contention on iOS/Android WebView
740
+ // that caused jank after scrolling 4-5 videos (7 concurrent HLS instances was too many).
741
+ maxAllocations: 8,
742
+ bufferWindow: 2,
743
+ warmWindow: 3,
741
744
  // 0ms debounce — setFocusedIndexImmediate is already used post-snap.
742
745
  focusDebounceMs: 0,
743
746
  preloadLookAhead: 3
@@ -1387,7 +1390,7 @@ function mapHlsError(data) {
1387
1390
  }
1388
1391
  }
1389
1392
  function useHls(options) {
1390
- const { src, videoRef, isActive, isPrefetch, bufferTier = "active", hlsConfig, onError } = options;
1393
+ const { src, videoRef, isActive, isPrefetch, bufferTier = "active", hlsConfig, onError, isDragging = false } = options;
1391
1394
  const isHlsSupported = typeof window !== "undefined" && Hls.isSupported();
1392
1395
  const isNative = supportsNativeHls();
1393
1396
  const isHlsJs = isHlsSupported && !isNative;
@@ -1543,6 +1546,21 @@ function useHls(options) {
1543
1546
  }
1544
1547
  }
1545
1548
  }, [bufferTier]);
1549
+ useEffect(() => {
1550
+ const hls = hlsRef.current;
1551
+ if (!hls || isActive) return;
1552
+ if (isDragging) {
1553
+ try {
1554
+ hls.stopLoad();
1555
+ } catch {
1556
+ }
1557
+ } else {
1558
+ try {
1559
+ hls.startLoad(-1);
1560
+ } catch {
1561
+ }
1562
+ }
1563
+ }, [isDragging, isActive]);
1546
1564
  return {
1547
1565
  isHlsJs,
1548
1566
  isReady,
@@ -1699,7 +1717,8 @@ function DefaultPauseAction() {
1699
1717
  }
1700
1718
  );
1701
1719
  }
1702
- var PLAY_AHEAD_MAX_CONCURRENT = 2;
1720
+ var PLAY_AHEAD_MAX_CONCURRENT = 3;
1721
+ var PLAY_AHEAD_STAGGER_MS = 50;
1703
1722
  var _playAheadActive = 0;
1704
1723
  var _playAheadQueue = [];
1705
1724
  function acquirePlayAhead() {
@@ -1707,7 +1726,12 @@ function acquirePlayAhead() {
1707
1726
  _playAheadActive++;
1708
1727
  return Promise.resolve();
1709
1728
  }
1710
- return new Promise((resolve) => _playAheadQueue.push(resolve));
1729
+ return new Promise((resolve) => {
1730
+ const queuePosition = _playAheadQueue.length;
1731
+ _playAheadQueue.push(() => {
1732
+ setTimeout(resolve, PLAY_AHEAD_STAGGER_MS * queuePosition);
1733
+ });
1734
+ });
1711
1735
  }
1712
1736
  function releasePlayAhead() {
1713
1737
  _playAheadActive = Math.max(0, _playAheadActive - 1);
@@ -1728,6 +1752,7 @@ function VideoSlot({
1728
1752
  onToggleMute,
1729
1753
  onAutoplayBlocked,
1730
1754
  showFps = false,
1755
+ isDragging = false,
1731
1756
  renderOverlay,
1732
1757
  renderActions,
1733
1758
  renderPauseAction
@@ -1765,6 +1790,7 @@ function VideoSlot({
1765
1790
  onToggleMute,
1766
1791
  onAutoplayBlocked,
1767
1792
  showFps,
1793
+ isDragging,
1768
1794
  renderOverlay,
1769
1795
  renderActions,
1770
1796
  renderPauseAction,
@@ -1784,6 +1810,7 @@ function VideoSlotInner({
1784
1810
  onToggleMute,
1785
1811
  onAutoplayBlocked,
1786
1812
  showFps,
1813
+ isDragging,
1787
1814
  renderOverlay,
1788
1815
  renderActions,
1789
1816
  renderPauseAction,
@@ -1805,6 +1832,7 @@ function VideoSlotInner({
1805
1832
  // so useHls creates the HLS instance and starts buffering
1806
1833
  isPrefetch: isPrefetch || isPreloaded,
1807
1834
  bufferTier,
1835
+ isDragging,
1808
1836
  onError: (code, message) => {
1809
1837
  console.error(`[VideoSlot] HLS error: ${code} \u2014 ${message}`);
1810
1838
  }
@@ -1941,6 +1969,7 @@ function VideoSlotInner({
1941
1969
  video.muted = isActive ? isMuted : true;
1942
1970
  }, [isMuted, isActive]);
1943
1971
  const showPosterOverlay = !isReady && !hasPlayedAhead;
1972
+ const isPreDecoded = hasPlayedAhead;
1944
1973
  const [showMuteIndicator, setShowMuteIndicator] = useState(false);
1945
1974
  const muteIndicatorTimer = useRef(null);
1946
1975
  const handleToggleMute = useCallback(() => {
@@ -2056,13 +2085,14 @@ function VideoSlotInner({
2056
2085
  width: "100%",
2057
2086
  height: "100%",
2058
2087
  objectFit: "cover",
2059
- // Hide video until ready to avoid black frame flash
2088
+ // Hide video until ready to avoid black frame flash.
2089
+ // When pre-decoded, skip transition — first frame is already on canvas.
2060
2090
  opacity: showPosterOverlay ? 0 : 1,
2061
- transition: "opacity 0.15s ease"
2091
+ transition: isPreDecoded ? "none" : "opacity 0.15s ease"
2062
2092
  }
2063
2093
  }
2064
2094
  ),
2065
- item.poster && /* @__PURE__ */ jsx(
2095
+ item.poster && !isPreDecoded && /* @__PURE__ */ jsx(
2066
2096
  "div",
2067
2097
  {
2068
2098
  style: {
@@ -2192,6 +2222,7 @@ function FpsCounter() {
2192
2222
  );
2193
2223
  }
2194
2224
  var RENDER_WINDOW_RADIUS = 3;
2225
+ var PLACEHOLDER_EXTRA = 2;
2195
2226
  var centerStyle = {
2196
2227
  height: "100dvh",
2197
2228
  display: "flex",
@@ -2269,9 +2300,20 @@ function ReelsFeed({
2269
2300
  }
2270
2301
  };
2271
2302
  rebuild();
2272
- const observer = new MutationObserver(rebuild);
2303
+ let rebuildTimer = null;
2304
+ const debouncedRebuild = () => {
2305
+ if (rebuildTimer !== null) return;
2306
+ rebuildTimer = setTimeout(() => {
2307
+ rebuildTimer = null;
2308
+ rebuild();
2309
+ }, 16);
2310
+ };
2311
+ const observer = new MutationObserver(debouncedRebuild);
2273
2312
  observer.observe(container, { childList: true, subtree: true });
2274
- return () => observer.disconnect();
2313
+ return () => {
2314
+ observer.disconnect();
2315
+ if (rebuildTimer !== null) clearTimeout(rebuildTimer);
2316
+ };
2275
2317
  }, [items.length, focusedIndex]);
2276
2318
  const containerHeight = useRef(
2277
2319
  typeof window !== "undefined" ? window.innerHeight : 800
@@ -2397,6 +2439,15 @@ function ReelsFeed({
2397
2439
  const handleToggleMute = useCallback(() => {
2398
2440
  setIsMuted((prev) => !prev);
2399
2441
  }, []);
2442
+ const windowedItems = useMemo(() => {
2443
+ const start = Math.max(0, focusedIndex - RENDER_WINDOW_RADIUS - PLACEHOLDER_EXTRA);
2444
+ const end = Math.min(items.length - 1, focusedIndex + RENDER_WINDOW_RADIUS + PLACEHOLDER_EXTRA);
2445
+ const result = [];
2446
+ for (let i = start; i <= end; i++) {
2447
+ result.push({ item: items[i], index: i });
2448
+ }
2449
+ return result;
2450
+ }, [items, focusedIndex]);
2400
2451
  if (loading && items.length === 0) {
2401
2452
  return /* @__PURE__ */ jsx("div", { style: { ...centerStyle, flexDirection: "column", gap: 0 }, children: renderLoading ? renderLoading() : /* @__PURE__ */ jsx(DefaultSkeleton, {}) });
2402
2453
  }
@@ -2433,7 +2484,7 @@ function ReelsFeed({
2433
2484
  to { transform: rotate(360deg); }
2434
2485
  }
2435
2486
  ` }),
2436
- items.map((item, index) => {
2487
+ windowedItems.map(({ item, index }) => {
2437
2488
  const distFromFocus = Math.abs(index - focusedIndex);
2438
2489
  const wrapperStyle = {
2439
2490
  position: "absolute",
@@ -2468,6 +2519,7 @@ function ReelsFeed({
2468
2519
  onToggleMute: handleToggleMute,
2469
2520
  onAutoplayBlocked,
2470
2521
  showFps: showFps && isActive,
2522
+ isDragging: isDragMuted,
2471
2523
  renderOverlay,
2472
2524
  renderActions,
2473
2525
  renderPauseAction
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xhub-reels/sdk",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "High-performance Short Video / Reels SDK for React — optimized for Flutter WebView",
5
5
  "license": "MIT",
6
6
  "type": "module",