@shortkitsdk/web 0.3.0 → 0.3.2

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/shortkit.cjs CHANGED
@@ -33544,16 +33544,35 @@ class PlayerPool {
33544
33544
  player._skCurrentUrl = streamingUrl;
33545
33545
  }
33546
33546
  /**
33547
- * Promote a preload player to active: raise buffer limits and uncap ABR.
33548
- * Safe to call even if the HLS instance was already created with active config.
33547
+ * Promote a preload player to active: raise buffer limits, uncap ABR, and
33548
+ * force an immediate quality upgrade.
33549
+ *
33550
+ * Preload streams start at level 0 (lowest rendition) to conserve bandwidth.
33551
+ * When attachStream() is called again with isActive=true for the same URL it
33552
+ * early-returns, so the HLS instance keeps its startLevel:0 config. The
33553
+ * already-buffered low-quality segments play through before any higher-quality
33554
+ * segments arrive — causing a visible low-res start even on fast connections.
33555
+ *
33556
+ * Fix: lock HLS to the highest level, then seek-in-place to flush the
33557
+ * low-quality buffer so the player immediately re-fetches at high quality.
33558
+ * Re-enable ABR after a few seconds so slow connections self-correct.
33549
33559
  */
33550
33560
  promoteToActive(itemId) {
33551
33561
  const hls = this.hlsInstances.get(itemId);
33552
- if (hls) {
33553
- hls.config.maxBufferLength = PlayerPool.ACTIVE_HLS_CONFIG.maxBufferLength;
33554
- hls.config.maxMaxBufferLength = PlayerPool.ACTIVE_HLS_CONFIG.maxMaxBufferLength;
33555
- hls.autoLevelCapping = -1;
33556
- hls.nextAutoLevel = -1;
33562
+ if (!hls) return;
33563
+ hls.config.maxBufferLength = PlayerPool.ACTIVE_HLS_CONFIG.maxBufferLength;
33564
+ hls.config.maxMaxBufferLength = PlayerPool.ACTIVE_HLS_CONFIG.maxMaxBufferLength;
33565
+ hls.autoLevelCapping = -1;
33566
+ const top = hls.levels ? hls.levels.length - 1 : -1;
33567
+ if (top > 0 && hls.currentLevel < top) {
33568
+ hls.currentLevel = top;
33569
+ const player = this.assignments.get(itemId);
33570
+ if (player) {
33571
+ player.currentTime = player.currentTime;
33572
+ }
33573
+ setTimeout(() => {
33574
+ if (!hls.destroyed) hls.currentLevel = -1;
33575
+ }, 4e3);
33557
33576
  }
33558
33577
  }
33559
33578
  /** Pause, destroy HLS, and remove player from DOM and pool tracking. */
@@ -33849,7 +33868,7 @@ class FeedManager {
33849
33868
  }
33850
33869
  setMuted(muted) {
33851
33870
  this.isMuted = muted;
33852
- for (const [, player] of this.pool._players) {
33871
+ for (const [, player] of this.pool.assignments) {
33853
33872
  player.muted = muted;
33854
33873
  }
33855
33874
  this._pushPlayerState({ isMuted: muted });
@@ -33862,7 +33881,7 @@ class FeedManager {
33862
33881
  }
33863
33882
  setPlaybackRate(rate) {
33864
33883
  this.playbackRate = rate;
33865
- for (const [, player] of this.pool._players) {
33884
+ for (const [, player] of this.pool.assignments) {
33866
33885
  player.playbackRate = rate;
33867
33886
  }
33868
33887
  this._pushPlayerState({ playbackRate: rate });
@@ -34339,11 +34358,10 @@ class FeedManager {
34339
34358
  const player = this.pool.getPlayer(id);
34340
34359
  if (player) {
34341
34360
  player.pause();
34342
- player.style.opacity = "0";
34343
34361
  player._skRevealedFor = null;
34344
34362
  }
34345
34363
  this._stopTimeLoop(id);
34346
- this._clearOverlay(id);
34364
+ this._detachOverlay(id);
34347
34365
  this._sk._tracker.deactivateContent();
34348
34366
  }
34349
34367
  // --- Time loop ---
@@ -34525,14 +34543,20 @@ class FeedManager {
34525
34543
  }
34526
34544
  }
34527
34545
  /** Clear the overlay container's DOM and unsubscribe tracked listeners. */
34528
- _clearOverlay(itemId) {
34546
+ /** Unsubscribe overlay event listeners without clearing DOM. */
34547
+ _detachOverlay(itemId) {
34529
34548
  const entry = this._overlayContainers.get(itemId);
34530
34549
  if (!entry) return;
34531
34550
  if (entry.unsub) {
34532
34551
  entry.unsub();
34533
34552
  entry.unsub = null;
34534
34553
  }
34535
- entry.el.innerHTML = "";
34554
+ }
34555
+ /** Unsubscribe and clear overlay DOM (used on destroy). */
34556
+ _clearOverlay(itemId) {
34557
+ this._detachOverlay(itemId);
34558
+ const entry = this._overlayContainers.get(itemId);
34559
+ if (entry) entry.el.innerHTML = "";
34536
34560
  }
34537
34561
  }
34538
34562
  class EmbeddedFeedManager {
@@ -34581,6 +34605,7 @@ class EmbeddedFeedManager {
34581
34605
  this._setupScrollEnd();
34582
34606
  this._setupVisibilityHandler();
34583
34607
  this._setupCellHeightObserver();
34608
+ this._setupDebugPanel();
34584
34609
  if (startIndex > 0 && startIndex < items.length) {
34585
34610
  const targetEl = this.itemEls.get(items[startIndex].id);
34586
34611
  if (targetEl) targetEl.scrollIntoView({ behavior: "instant", block: "start" });
@@ -34603,6 +34628,11 @@ class EmbeddedFeedManager {
34603
34628
  this._resizeObserver = null;
34604
34629
  }
34605
34630
  if (this._visHandler) document.removeEventListener("visibilitychange", this._visHandler);
34631
+ if (this._debugKeyHandler) document.removeEventListener("keydown", this._debugKeyHandler);
34632
+ if (this._debugPanel) {
34633
+ this._debugPanel.remove();
34634
+ this._debugPanel = null;
34635
+ }
34606
34636
  this.container.innerHTML = "";
34607
34637
  this.itemEls.clear();
34608
34638
  this.items = [];
@@ -34646,7 +34676,7 @@ class EmbeddedFeedManager {
34646
34676
  }
34647
34677
  setMuted(muted) {
34648
34678
  this.isMuted = muted;
34649
- for (const [, player] of this.pool._players) {
34679
+ for (const [, player] of this.pool.assignments) {
34650
34680
  player.muted = muted;
34651
34681
  }
34652
34682
  this._pushPlayerState({ isMuted: muted });
@@ -34659,7 +34689,7 @@ class EmbeddedFeedManager {
34659
34689
  }
34660
34690
  setPlaybackRate(rate) {
34661
34691
  this.playbackRate = rate;
34662
- for (const [, player] of this.pool._players) {
34692
+ for (const [, player] of this.pool.assignments) {
34663
34693
  player.playbackRate = rate;
34664
34694
  }
34665
34695
  this._pushPlayerState({ playbackRate: rate });
@@ -34931,11 +34961,52 @@ class EmbeddedFeedManager {
34931
34961
  const p = this.pool.getPlayer(id);
34932
34962
  if (p) {
34933
34963
  p.pause();
34934
- p.style.opacity = "0";
34935
34964
  p._skRevealedFor = null;
34936
34965
  }
34937
34966
  this._stopTimeLoop(id);
34938
- this._clearOverlay(id);
34967
+ this._detachOverlay(id);
34968
+ }
34969
+ // --- Debug panel (toggle with 'D' key) ---
34970
+ _setupDebugPanel() {
34971
+ const panel = document.createElement("div");
34972
+ panel.id = "sk-debug-panel";
34973
+ panel.style.cssText = "position:fixed;top:12px;right:12px;z-index:99999;background:rgba(0,0,0,.85);color:#0f0;font:12px/1.5 monospace;padding:10px 14px;border-radius:8px;pointer-events:none;display:none;min-width:220px;";
34974
+ document.body.appendChild(panel);
34975
+ this._debugPanel = panel;
34976
+ this._debugVisible = false;
34977
+ this._debugKeyHandler = (e) => {
34978
+ if (e.key === "d" || e.key === "D") {
34979
+ this._debugVisible = !this._debugVisible;
34980
+ panel.style.display = this._debugVisible ? "block" : "none";
34981
+ }
34982
+ };
34983
+ document.addEventListener("keydown", this._debugKeyHandler);
34984
+ }
34985
+ _updateDebugPanel() {
34986
+ if (!this._debugVisible || !this._debugPanel) return;
34987
+ const hls = this.pool.hlsInstances.get(this.activeItemId);
34988
+ if (!hls || !hls.levels || !hls.levels.length) {
34989
+ this._debugPanel.innerHTML = "HLS: no levels loaded";
34990
+ return;
34991
+ }
34992
+ const level = hls.levels[hls.currentLevel] || {};
34993
+ const bw = hls.bandwidthEstimate ? (hls.bandwidthEstimate / 1e6).toFixed(2) : "?";
34994
+ const lines = [
34995
+ `<b style="color:#fff">HLS Debug</b>`,
34996
+ `Level: ${hls.currentLevel} / ${hls.levels.length - 1}`,
34997
+ `Resolution: ${level.width || "?"}x${level.height || "?"}`,
34998
+ `Bitrate: ${level.bitrate ? (level.bitrate / 1e3).toFixed(0) + " kbps" : "?"}`,
34999
+ `BW estimate: ${bw} Mbps`,
35000
+ `Auto cap: ${hls.autoLevelCapping}`,
35001
+ `Next level: ${hls.nextLevel}`,
35002
+ `<span style="color:#888">Levels:</span>`
35003
+ ];
35004
+ for (let i = 0; i < hls.levels.length; i++) {
35005
+ const l = hls.levels[i];
35006
+ const marker = i === hls.currentLevel ? ' <b style="color:#0ff">◀</b>' : "";
35007
+ lines.push(` ${i}: ${l.width}x${l.height} @ ${(l.bitrate / 1e3).toFixed(0)}k${marker}`);
35008
+ }
35009
+ this._debugPanel.innerHTML = lines.join("<br>");
34939
35010
  }
34940
35011
  // --- Time loop (state emission only, no DOM updates) ---
34941
35012
  _startTimeLoop(id, el, player) {
@@ -34943,6 +35014,7 @@ class EmbeddedFeedManager {
34943
35014
  const tick = () => {
34944
35015
  if (this._destroyed) return;
34945
35016
  if (!player || player.paused) {
35017
+ this._updateDebugPanel();
34946
35018
  this.rafIds.set(id, requestAnimationFrame(tick));
34947
35019
  return;
34948
35020
  }
@@ -34968,6 +35040,7 @@ class EmbeddedFeedManager {
34968
35040
  player.play().catch(() => {
34969
35041
  });
34970
35042
  }
35043
+ this._updateDebugPanel();
34971
35044
  this.rafIds.set(id, requestAnimationFrame(tick));
34972
35045
  };
34973
35046
  this.rafIds.set(id, requestAnimationFrame(tick));
@@ -35134,14 +35207,20 @@ class EmbeddedFeedManager {
35134
35207
  } catch (e) {
35135
35208
  }
35136
35209
  }
35137
- _clearOverlay(itemId) {
35210
+ /** Unsubscribe overlay event listeners without clearing DOM. */
35211
+ _detachOverlay(itemId) {
35138
35212
  const entry = this._overlayContainers.get(itemId);
35139
35213
  if (!entry) return;
35140
35214
  if (entry.unsub) {
35141
35215
  entry.unsub();
35142
35216
  entry.unsub = null;
35143
35217
  }
35144
- entry.el.innerHTML = "";
35218
+ }
35219
+ /** Unsubscribe and clear overlay DOM (used on destroy). */
35220
+ _clearOverlay(itemId) {
35221
+ this._detachOverlay(itemId);
35222
+ const entry = this._overlayContainers.get(itemId);
35223
+ if (entry) entry.el.innerHTML = "";
35145
35224
  }
35146
35225
  }
35147
35226
  const MuteOnSvg = '<svg viewBox="0 0 24 24"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/></svg>';
@@ -35491,6 +35570,7 @@ class PlayerFeedView {
35491
35570
  this._isOpen = true;
35492
35571
  this._onClose = onClose;
35493
35572
  this._openItemId = item.id;
35573
+ this._openThumbnailUrl = item.thumbnailUrl || null;
35494
35574
  const sourceRect = sourceEl.getBoundingClientRect();
35495
35575
  const sourceRadius = parseFloat(getComputedStyle(sourceEl).borderRadius) || 12;
35496
35576
  this.overlayEl.classList.add("skp-active");
@@ -35535,6 +35615,21 @@ class PlayerFeedView {
35535
35615
  feedEl.style.transform = "none";
35536
35616
  feedEl.style.borderRadius = `${targetRadius}px`;
35537
35617
  });
35618
+ this.feedManager.startObserver();
35619
+ this.feedManager._activateItem(item.id);
35620
+ this.overlayEl.classList.add("skp-feed-ready");
35621
+ const itemOverlay = feedItemEl?.querySelector('[data-ref="overlay"]');
35622
+ const chromeEls = [...this.overlayEl.querySelectorAll(".skp-feed-close, .sk-sidebar")];
35623
+ if (itemOverlay) chromeEls.push(itemOverlay);
35624
+ chromeEls.forEach((el) => {
35625
+ el.style.opacity = "0";
35626
+ el.style.transition = "none";
35627
+ });
35628
+ this.overlayEl.getBoundingClientRect();
35629
+ chromeEls.forEach((el) => {
35630
+ el.style.transition = "opacity .35s cubic-bezier(.32,.72,0,1)";
35631
+ el.style.opacity = "1";
35632
+ });
35538
35633
  await this._waitForTransitionEnd(feedEl, 450);
35539
35634
  feedEl.classList.remove("skp-flip-animating");
35540
35635
  feedEl.style.transformOrigin = "";
@@ -35542,9 +35637,10 @@ class PlayerFeedView {
35542
35637
  feedEl.style.scrollSnapType = "";
35543
35638
  feedEl.style.transform = "";
35544
35639
  feedEl.style.borderRadius = "";
35545
- this.feedManager.startObserver();
35546
- this.feedManager._activateItem(item.id);
35547
- this.overlayEl.classList.add("skp-feed-ready");
35640
+ chromeEls.forEach((el) => {
35641
+ el.style.opacity = "";
35642
+ el.style.transition = "";
35643
+ });
35548
35644
  this._escHandler = (e) => {
35549
35645
  if (e.key === "Escape") this.close();
35550
35646
  };
@@ -35557,33 +35653,44 @@ class PlayerFeedView {
35557
35653
  const feedWrapper = feedEl.parentNode;
35558
35654
  const feedRect = feedEl.getBoundingClientRect();
35559
35655
  const activeItemId = this.feedManager?.activeItemId;
35560
- let transferBack = false, transferVideo = null, transferHls = null;
35561
- if (activeItemId === this._openItemId) {
35562
- const ejected = this.feedManager?.pool.ejectPlayer(activeItemId);
35563
- if (ejected) {
35564
- transferBack = true;
35565
- transferVideo = ejected.video;
35566
- transferHls = ejected.hls;
35567
- }
35568
- }
35656
+ const canTransfer = activeItemId === this._openItemId;
35569
35657
  let targetRect = null;
35570
35658
  if (this._onClose) targetRect = this._onClose("getSourceRect");
35571
35659
  if (feedRect && targetRect) {
35572
- if (this.feedManager?.activeItemId && !transferBack) {
35660
+ if (this.feedManager?.activeItemId && !canTransfer) {
35573
35661
  this.feedManager._deactivateItem(this.feedManager.activeItemId);
35574
35662
  }
35663
+ const targetItemId = canTransfer ? this._openItemId : activeItemId;
35664
+ if (targetItemId && this.feedManager) {
35665
+ const targetEl = this.feedManager.itemEls.get(targetItemId);
35666
+ if (targetEl) targetEl.scrollIntoView({ behavior: "instant", block: "start" });
35667
+ }
35668
+ const savedScrollTop = feedEl.scrollTop;
35575
35669
  feedEl.style.position = "fixed";
35576
35670
  feedEl.style.left = `${feedRect.left}px`;
35577
35671
  feedEl.style.top = `${feedRect.top}px`;
35578
35672
  feedEl.style.width = `${feedRect.width}px`;
35579
35673
  feedEl.style.height = `${feedRect.height}px`;
35580
- feedEl.style.zIndex = "9999";
35674
+ feedEl.style.zIndex = String(getComputedStyle(this.overlayEl).zIndex || 9999);
35581
35675
  feedEl.style.margin = "0";
35582
35676
  feedEl.style.flex = "none";
35583
35677
  feedEl.style.aspectRatio = "unset";
35584
35678
  feedEl.style.maxHeight = "none";
35585
35679
  document.body.appendChild(feedEl);
35680
+ feedEl.scrollTop = savedScrollTop;
35586
35681
  this.overlayEl.classList.remove("skp-active", "skp-feed-ready");
35682
+ let thumbOverlay = null;
35683
+ if (this._openThumbnailUrl) {
35684
+ const crossfadeItemId = canTransfer ? this._openItemId : activeItemId;
35685
+ const crossfadeEl = crossfadeItemId && this.feedManager?.itemEls.get(crossfadeItemId);
35686
+ const videoContainer = crossfadeEl?.querySelector('[data-ref="videoContainer"]');
35687
+ if (videoContainer) {
35688
+ thumbOverlay = document.createElement("img");
35689
+ thumbOverlay.src = this._openThumbnailUrl;
35690
+ thumbOverlay.style.cssText = "position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .4s cubic-bezier(.32,.72,0,1);pointer-events:none;z-index:2;";
35691
+ videoContainer.appendChild(thumbOverlay);
35692
+ }
35693
+ }
35587
35694
  const scaleX = targetRect.width / feedRect.width;
35588
35695
  const scaleY = targetRect.height / feedRect.height;
35589
35696
  const translateX = targetRect.left - feedRect.left;
@@ -35593,29 +35700,58 @@ class PlayerFeedView {
35593
35700
  feedEl.style.transformOrigin = "0 0";
35594
35701
  feedEl.style.overflow = "hidden";
35595
35702
  feedEl.style.scrollSnapType = "none";
35703
+ feedEl.style.pointerEvents = "none";
35596
35704
  feedEl.getBoundingClientRect();
35597
35705
  feedEl.classList.add("skp-flip-animating");
35598
35706
  requestAnimationFrame(() => {
35599
35707
  feedEl.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`;
35600
35708
  feedEl.style.borderRadius = `${endRadius}px`;
35709
+ if (thumbOverlay) thumbOverlay.style.opacity = "1";
35601
35710
  });
35602
35711
  await this._waitForTransitionEnd(feedEl, 450);
35712
+ if (thumbOverlay) thumbOverlay.remove();
35713
+ let transferVideo = null;
35714
+ let transferHls = null;
35715
+ if (canTransfer) {
35716
+ const ejected = this.feedManager?.pool.ejectPlayer(this._openItemId);
35717
+ if (ejected) {
35718
+ transferVideo = ejected.video;
35719
+ transferHls = ejected.hls;
35720
+ }
35721
+ }
35603
35722
  feedEl.style.visibility = "hidden";
35604
35723
  feedEl.classList.remove("skp-flip-animating");
35605
- ["position", "left", "top", "width", "height", "zIndex", "margin", "flex", "aspectRatio", "maxHeight", "transformOrigin", "overflow", "scrollSnapType", "transform", "borderRadius"].forEach((p) => feedEl.style[p] = "");
35724
+ ["position", "left", "top", "width", "height", "zIndex", "margin", "flex", "aspectRatio", "maxHeight", "transformOrigin", "overflow", "scrollSnapType", "pointerEvents", "transform", "borderRadius"].forEach((p) => feedEl.style[p] = "");
35606
35725
  const sidebarEl = feedWrapper.querySelector(".sk-sidebar");
35607
35726
  feedWrapper.insertBefore(feedEl, sidebarEl);
35608
35727
  feedEl.style.visibility = "";
35728
+ if (this._onClose) {
35729
+ this._onClose("closed", {
35730
+ transferVideo,
35731
+ transferHls,
35732
+ transferItemId: transferVideo ? this._openItemId : null
35733
+ });
35734
+ this._onClose = null;
35735
+ }
35609
35736
  } else {
35610
35737
  this.overlayEl.classList.remove("skp-active", "skp-feed-ready");
35611
- }
35612
- if (this._onClose) {
35613
- this._onClose("closed", {
35614
- transferVideo: transferBack ? transferVideo : null,
35615
- transferHls: transferBack ? transferHls : null,
35616
- transferItemId: transferBack ? this._openItemId : null
35617
- });
35618
- this._onClose = null;
35738
+ if (this._onClose) {
35739
+ let transferVideo = null;
35740
+ let transferHls = null;
35741
+ if (canTransfer) {
35742
+ const ejected = this.feedManager?.pool.ejectPlayer(this._openItemId);
35743
+ if (ejected) {
35744
+ transferVideo = ejected.video;
35745
+ transferHls = ejected.hls;
35746
+ }
35747
+ }
35748
+ this._onClose("closed", {
35749
+ transferVideo,
35750
+ transferHls,
35751
+ transferItemId: transferVideo ? this._openItemId : null
35752
+ });
35753
+ this._onClose = null;
35754
+ }
35619
35755
  }
35620
35756
  if (this.feedManager) {
35621
35757
  this.feedManager.destroy();
@@ -35667,6 +35803,7 @@ class FeedView {
35667
35803
  this._onClose = onClose;
35668
35804
  this._openSlotIndex = startIndex;
35669
35805
  this._openItemId = item.id;
35806
+ this._openThumbnailUrl = item.thumbnailUrl || null;
35670
35807
  const slotRect = slotEl.getBoundingClientRect();
35671
35808
  const slotRadius = parseFloat(getComputedStyle(slotEl).borderRadius) || 12;
35672
35809
  if (_isPreview) {
@@ -35713,6 +35850,21 @@ class FeedView {
35713
35850
  feedEl.style.transform = "none";
35714
35851
  feedEl.style.borderRadius = `${targetRadius}px`;
35715
35852
  });
35853
+ this.feedManager.startObserver();
35854
+ this.feedManager._activateItem(item.id);
35855
+ this.overlayEl.classList.add("skw-feed-ready");
35856
+ const itemOverlay = feedItemEl?.querySelector('[data-ref="overlay"]');
35857
+ const chromeEls = [...this.overlayEl.querySelectorAll(".skw-feed-close, .sk-sidebar")];
35858
+ if (itemOverlay) chromeEls.push(itemOverlay);
35859
+ chromeEls.forEach((el) => {
35860
+ el.style.opacity = "0";
35861
+ el.style.transition = "none";
35862
+ });
35863
+ this.overlayEl.getBoundingClientRect();
35864
+ chromeEls.forEach((el) => {
35865
+ el.style.transition = "opacity .35s cubic-bezier(.32,.72,0,1)";
35866
+ el.style.opacity = "1";
35867
+ });
35716
35868
  await this._waitForTransitionEnd(feedEl, 450);
35717
35869
  feedEl.classList.remove("skw-flip-animating");
35718
35870
  feedEl.style.transformOrigin = "";
@@ -35720,9 +35872,10 @@ class FeedView {
35720
35872
  feedEl.style.scrollSnapType = "";
35721
35873
  feedEl.style.transform = "";
35722
35874
  feedEl.style.borderRadius = "";
35723
- this.feedManager.startObserver();
35724
- this.feedManager._activateItem(item.id);
35725
- this.overlayEl.classList.add("skw-feed-ready");
35875
+ chromeEls.forEach((el) => {
35876
+ el.style.opacity = "";
35877
+ el.style.transition = "";
35878
+ });
35726
35879
  this._escHandler = (e) => {
35727
35880
  if (e.key === "Escape") this.close();
35728
35881
  };
@@ -35749,6 +35902,12 @@ class FeedView {
35749
35902
  if (this.feedManager?.activeItemId && !canTransfer) {
35750
35903
  this.feedManager._deactivateItem(this.feedManager.activeItemId);
35751
35904
  }
35905
+ const targetItemId = canTransfer ? this._openItemId : activeItemId;
35906
+ if (targetItemId && this.feedManager) {
35907
+ const targetEl = this.feedManager.itemEls.get(targetItemId);
35908
+ if (targetEl) targetEl.scrollIntoView({ behavior: "instant", block: "start" });
35909
+ }
35910
+ const savedScrollTop = feedEl.scrollTop;
35752
35911
  feedEl.style.position = "fixed";
35753
35912
  feedEl.style.left = `${feedRect.left}px`;
35754
35913
  feedEl.style.top = `${feedRect.top}px`;
@@ -35760,7 +35919,20 @@ class FeedView {
35760
35919
  feedEl.style.aspectRatio = "unset";
35761
35920
  feedEl.style.maxHeight = "none";
35762
35921
  document.body.appendChild(feedEl);
35922
+ feedEl.scrollTop = savedScrollTop;
35763
35923
  this.overlayEl.classList.remove("skw-active", "skw-feed-ready");
35924
+ let thumbOverlay = null;
35925
+ if (this._openThumbnailUrl) {
35926
+ const targetItemId2 = canTransfer ? this._openItemId : activeItemId;
35927
+ const targetEl = targetItemId2 && this.feedManager?.itemEls.get(targetItemId2);
35928
+ const videoContainer = targetEl?.querySelector('[data-ref="videoContainer"]');
35929
+ if (videoContainer) {
35930
+ thumbOverlay = document.createElement("img");
35931
+ thumbOverlay.src = this._openThumbnailUrl;
35932
+ thumbOverlay.style.cssText = "position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .4s cubic-bezier(.32,.72,0,1);pointer-events:none;z-index:2;";
35933
+ videoContainer.appendChild(thumbOverlay);
35934
+ }
35935
+ }
35764
35936
  const slotRadius = parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--skw-radius").trim()) || 12;
35765
35937
  const scaleX = targetSlotRect.width / feedRect.width;
35766
35938
  const scaleY = targetSlotRect.height / feedRect.height;
@@ -35770,13 +35942,16 @@ class FeedView {
35770
35942
  feedEl.style.transformOrigin = "0 0";
35771
35943
  feedEl.style.overflow = "hidden";
35772
35944
  feedEl.style.scrollSnapType = "none";
35945
+ feedEl.style.pointerEvents = "none";
35773
35946
  feedEl.getBoundingClientRect();
35774
35947
  feedEl.classList.add("skw-flip-animating");
35775
35948
  requestAnimationFrame(() => {
35776
35949
  feedEl.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`;
35777
35950
  feedEl.style.borderRadius = `${endRadius}px`;
35951
+ if (thumbOverlay) thumbOverlay.style.opacity = "1";
35778
35952
  });
35779
35953
  await this._waitForTransitionEnd(feedEl, 450);
35954
+ if (thumbOverlay) thumbOverlay.remove();
35780
35955
  let transferVideo = null;
35781
35956
  let transferHls = null;
35782
35957
  if (canTransfer) {
@@ -35801,6 +35976,7 @@ class FeedView {
35801
35976
  feedEl.style.transformOrigin = "";
35802
35977
  feedEl.style.overflow = "";
35803
35978
  feedEl.style.scrollSnapType = "";
35979
+ feedEl.style.pointerEvents = "";
35804
35980
  feedEl.style.transform = "";
35805
35981
  feedEl.style.borderRadius = "";
35806
35982
  feedWrapper.insertBefore(feedEl, feedWrapper.firstChild);
@@ -35930,11 +36106,20 @@ class WidgetPlayerPool {
35930
36106
  }
35931
36107
  promoteToActive(itemId) {
35932
36108
  const hls = this.hlsInstances.get(itemId);
35933
- if (hls) {
35934
- hls.config.maxBufferLength = WidgetPlayerPool.ACTIVE_HLS_CONFIG.maxBufferLength;
35935
- hls.config.maxMaxBufferLength = WidgetPlayerPool.ACTIVE_HLS_CONFIG.maxMaxBufferLength;
35936
- hls.autoLevelCapping = -1;
35937
- hls.nextAutoLevel = -1;
36109
+ if (!hls) return;
36110
+ hls.config.maxBufferLength = WidgetPlayerPool.ACTIVE_HLS_CONFIG.maxBufferLength;
36111
+ hls.config.maxMaxBufferLength = WidgetPlayerPool.ACTIVE_HLS_CONFIG.maxMaxBufferLength;
36112
+ hls.autoLevelCapping = -1;
36113
+ const top = hls.levels ? hls.levels.length - 1 : -1;
36114
+ if (top > 0 && hls.currentLevel < top) {
36115
+ hls.currentLevel = top;
36116
+ const player = this.assignments.get(itemId);
36117
+ if (player) {
36118
+ player.currentTime = player.currentTime;
36119
+ }
36120
+ setTimeout(() => {
36121
+ if (!hls.destroyed) hls.currentLevel = -1;
36122
+ }, 4e3);
35938
36123
  }
35939
36124
  }
35940
36125
  _destroyHls(itemId) {
@@ -36539,7 +36724,7 @@ const CSS = `
36539
36724
 
36540
36725
  /* Video container */
36541
36726
  .sk-video-container{position:absolute;inset:0;width:100%;height:100%;overflow:hidden;background-size:cover;background-position:center}
36542
- .sk-video-container video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .15s ease}
36727
+ .sk-video-container video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .35s ease}
36543
36728
 
36544
36729
  /* Tap zone */
36545
36730
  .sk-tap-zone{position:absolute;inset:0;z-index:3;cursor:pointer}
@@ -36555,7 +36740,7 @@ const CSS = `
36555
36740
 
36556
36741
  /* Widget slot */
36557
36742
  .skw-slot{position:relative;overflow:hidden;cursor:pointer}
36558
- .skw-slot video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .2s ease}
36743
+ .skw-slot video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .4s ease}
36559
36744
  .skw-slot-thumb{position:absolute;inset:0;background-size:cover;background-position:center}
36560
36745
  .skw-slot-overlay{position:absolute;inset:0;z-index:4;pointer-events:none}
36561
36746
  .skw-slot-overlay>*{pointer-events:auto}