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