stormcloud-video-player 0.2.9 → 0.2.10

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.
@@ -76,9 +76,22 @@ function createImaController(video, options) {
76
76
  'script[data-ima="true"]'
77
77
  );
78
78
  if (existing) {
79
- return new Promise(
80
- (resolve) => existing.addEventListener("load", () => resolve())
81
- );
79
+ if (window.google?.ima) {
80
+ return Promise.resolve();
81
+ }
82
+ return new Promise((resolve, reject) => {
83
+ const timeout = setTimeout(() => {
84
+ reject(new Error("IMA SDK load timeout"));
85
+ }, 1e4);
86
+ existing.addEventListener("load", () => {
87
+ clearTimeout(timeout);
88
+ resolve();
89
+ });
90
+ existing.addEventListener("error", () => {
91
+ clearTimeout(timeout);
92
+ reject(new Error("IMA SDK load failed"));
93
+ });
94
+ });
82
95
  }
83
96
  return new Promise((resolve, reject) => {
84
97
  const script = document.createElement("script");
@@ -107,6 +120,17 @@ function createImaController(video, options) {
107
120
  adsRequest.adTagUrl = vastTagUrl;
108
121
  adsLoader.requestAds(adsRequest);
109
122
  }
123
+ function destroyAdsManager() {
124
+ if (adsManager) {
125
+ try {
126
+ console.log("[IMA] Destroying existing ads manager");
127
+ adsManager.destroy();
128
+ } catch (error) {
129
+ console.warn("[IMA] Error destroying ads manager:", error);
130
+ }
131
+ adsManager = void 0;
132
+ }
133
+ }
110
134
  return {
111
135
  initialize() {
112
136
  ensureImaLoaded().then(() => {
@@ -139,9 +163,22 @@ function createImaController(video, options) {
139
163
  },
140
164
  async requestAds(vastTagUrl) {
141
165
  console.log("[IMA] Requesting ads:", vastTagUrl);
166
+ if (adPlaying) {
167
+ console.warn(
168
+ "[IMA] Cannot request new ads while an ad is playing. Call stop() first."
169
+ );
170
+ return Promise.reject(
171
+ new Error("Ad already playing - cannot request new ads")
172
+ );
173
+ }
174
+ destroyAdsManager();
175
+ adsLoadedReject = void 0;
176
+ adsLoadedResolve = void 0;
177
+ let currentReject;
142
178
  adsLoadedPromise = new Promise((resolve, reject) => {
143
179
  adsLoadedResolve = resolve;
144
180
  adsLoadedReject = reject;
181
+ currentReject = reject;
145
182
  setTimeout(() => {
146
183
  if (adsLoadedReject) {
147
184
  adsLoadedReject(new Error("Ad request timeout"));
@@ -163,7 +200,7 @@ function createImaController(video, options) {
163
200
  container.style.top = "0";
164
201
  container.style.right = "0";
165
202
  container.style.bottom = "0";
166
- container.style.display = "flex";
203
+ container.style.display = "none";
167
204
  container.style.alignItems = "center";
168
205
  container.style.justifyContent = "center";
169
206
  container.style.pointerEvents = "none";
@@ -203,14 +240,14 @@ function createImaController(video, options) {
203
240
  AdErrorEvent.AD_ERROR,
204
241
  (errorEvent) => {
205
242
  console.error("[IMA] Ad error:", errorEvent.getError());
206
- try {
207
- adsManager?.destroy?.();
208
- } catch {
209
- }
243
+ destroyAdsManager();
210
244
  adPlaying = false;
211
245
  video.muted = originalMutedState;
212
- if (adContainerEl)
246
+ if (adContainerEl) {
213
247
  adContainerEl.style.pointerEvents = "none";
248
+ adContainerEl.style.display = "none";
249
+ console.log("[IMA] Ad container hidden after error");
250
+ }
214
251
  if (adsLoadedReject) {
215
252
  adsLoadedReject(new Error("Ad playback error"));
216
253
  adsLoadedReject = void 0;
@@ -243,10 +280,6 @@ function createImaController(video, options) {
243
280
  AdEvent.CONTENT_PAUSE_REQUESTED,
244
281
  () => {
245
282
  console.log("[IMA] Content pause requested");
246
- if (!adPlaying) {
247
- originalMutedState = video.muted;
248
- }
249
- video.muted = true;
250
283
  if (!options?.continueLiveStreamDuringAds) {
251
284
  video.pause();
252
285
  console.log("[IMA] Video paused (VOD mode)");
@@ -255,20 +288,34 @@ function createImaController(video, options) {
255
288
  "[IMA] Video continues playing but muted (Live mode)"
256
289
  );
257
290
  }
291
+ video.muted = true;
258
292
  adPlaying = true;
259
- if (adContainerEl)
260
- adContainerEl.style.pointerEvents = "auto";
261
293
  emit("content_pause");
262
294
  }
263
295
  );
296
+ adsManager.addEventListener(AdEvent.STARTED, () => {
297
+ console.log("[IMA] Ad started playing");
298
+ if (adContainerEl) {
299
+ adContainerEl.style.pointerEvents = "auto";
300
+ adContainerEl.style.display = "flex";
301
+ console.log(
302
+ "[IMA] Ad container visibility set to flex with pointer events enabled"
303
+ );
304
+ }
305
+ });
264
306
  adsManager.addEventListener(
265
307
  AdEvent.CONTENT_RESUME_REQUESTED,
266
308
  () => {
267
309
  console.log("[IMA] Content resume requested");
268
310
  adPlaying = false;
269
311
  video.muted = originalMutedState;
270
- if (adContainerEl)
312
+ if (adContainerEl) {
271
313
  adContainerEl.style.pointerEvents = "none";
314
+ adContainerEl.style.display = "none";
315
+ console.log(
316
+ "[IMA] Ad container hidden - pointer events disabled"
317
+ );
318
+ }
272
319
  if (!options?.continueLiveStreamDuringAds) {
273
320
  video.play()?.catch(() => {
274
321
  });
@@ -285,7 +332,13 @@ function createImaController(video, options) {
285
332
  console.log("[IMA] All ads completed");
286
333
  adPlaying = false;
287
334
  video.muted = originalMutedState;
288
- if (adContainerEl) adContainerEl.style.pointerEvents = "none";
335
+ if (adContainerEl) {
336
+ adContainerEl.style.pointerEvents = "none";
337
+ adContainerEl.style.display = "none";
338
+ console.log(
339
+ "[IMA] Ad container hidden after all ads completed"
340
+ );
341
+ }
289
342
  if (!options?.continueLiveStreamDuringAds) {
290
343
  video.play().catch(() => {
291
344
  });
@@ -328,6 +381,13 @@ function createImaController(video, options) {
328
381
  google.ima.AdErrorEvent.Type.AD_ERROR,
329
382
  (adErrorEvent) => {
330
383
  console.error("[IMA] Ads loader error:", adErrorEvent.getError());
384
+ adPlaying = false;
385
+ video.muted = originalMutedState;
386
+ if (adContainerEl) adContainerEl.style.pointerEvents = "none";
387
+ if (!options?.continueLiveStreamDuringAds) {
388
+ video.play().catch(() => {
389
+ });
390
+ }
331
391
  if (adsLoadedReject) {
332
392
  adsLoadedReject(new Error("Ads loader error"));
333
393
  adsLoadedReject = void 0;
@@ -343,11 +403,9 @@ function createImaController(video, options) {
343
403
  return adsLoadedPromise;
344
404
  } catch (error) {
345
405
  console.error("[IMA] Failed to request ads:", error);
346
- if (adsLoadedReject) {
347
- adsLoadedReject(error);
348
- adsLoadedReject = void 0;
349
- adsLoadedResolve = void 0;
350
- }
406
+ currentReject?.(error);
407
+ adsLoadedReject = void 0;
408
+ adsLoadedResolve = void 0;
351
409
  return Promise.reject(error);
352
410
  }
353
411
  },
@@ -384,10 +442,16 @@ function createImaController(video, options) {
384
442
  async stop() {
385
443
  adPlaying = false;
386
444
  video.muted = originalMutedState;
445
+ if (adContainerEl) {
446
+ adContainerEl.style.pointerEvents = "none";
447
+ adContainerEl.style.display = "none";
448
+ console.log("[IMA] Ad container hidden after stop");
449
+ }
387
450
  try {
388
451
  adsManager?.stop?.();
389
452
  } catch {
390
453
  }
454
+ destroyAdsManager();
391
455
  if (!options?.continueLiveStreamDuringAds) {
392
456
  video.play().catch(() => {
393
457
  });
@@ -397,12 +461,13 @@ function createImaController(video, options) {
397
461
  }
398
462
  },
399
463
  destroy() {
400
- try {
401
- adsManager?.destroy?.();
402
- } catch {
403
- }
464
+ destroyAdsManager();
404
465
  adPlaying = false;
405
466
  video.muted = originalMutedState;
467
+ if (adContainerEl) {
468
+ adContainerEl.style.pointerEvents = "none";
469
+ adContainerEl.style.display = "none";
470
+ }
406
471
  try {
407
472
  adsLoader?.destroy?.();
408
473
  } catch {
@@ -410,6 +475,9 @@ function createImaController(video, options) {
410
475
  if (adContainerEl?.parentElement) {
411
476
  adContainerEl.parentElement.removeChild(adContainerEl);
412
477
  }
478
+ adContainerEl = void 0;
479
+ adDisplayContainer = void 0;
480
+ adsLoader = void 0;
413
481
  },
414
482
  isAdPlaying() {
415
483
  return adPlaying;
@@ -887,17 +955,8 @@ var StormcloudVideoPlayer = class {
887
955
  this.video.muted = !!this.config.muted;
888
956
  this.ima.initialize();
889
957
  this.ima.on("all_ads_completed", () => {
890
- if (!this.inAdBreak) return;
891
- const remaining = this.getRemainingAdMs();
892
- if (remaining > 500 && this.adPodQueue.length > 0) {
893
- const next = this.adPodQueue.shift();
894
- this.currentAdIndex++;
895
- this.playSingleAd(next).catch(() => {
896
- });
897
- } else {
898
- this.currentAdIndex = 0;
899
- this.totalAdsInBreak = 0;
900
- this.showAds = false;
958
+ if (this.config.debugAdTiming) {
959
+ console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received");
901
960
  }
902
961
  });
903
962
  this.ima.on("ad_error", () => {
@@ -928,6 +987,26 @@ var StormcloudVideoPlayer = class {
928
987
  );
929
988
  }
930
989
  this.clearAdFailsafeTimer();
990
+ if (!this.inAdBreak) return;
991
+ const remaining = this.getRemainingAdMs();
992
+ if (remaining > 500 && this.adPodQueue.length > 0) {
993
+ const next = this.adPodQueue.shift();
994
+ this.currentAdIndex++;
995
+ if (this.config.debugAdTiming) {
996
+ console.log(
997
+ `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
998
+ );
999
+ }
1000
+ this.playSingleAd(next).catch(() => {
1001
+ });
1002
+ } else {
1003
+ if (this.config.debugAdTiming) {
1004
+ console.log("[StormcloudVideoPlayer] Ad pod completed");
1005
+ }
1006
+ this.currentAdIndex = 0;
1007
+ this.totalAdsInBreak = 0;
1008
+ this.showAds = false;
1009
+ }
931
1010
  });
932
1011
  this.video.addEventListener("timeupdate", () => {
933
1012
  this.onTimeUpdate(this.video.currentTime);
@@ -1577,21 +1656,21 @@ var StormcloudVideoPlayer = class {
1577
1656
  if (this.config.debugAdTiming) {
1578
1657
  console.log("[StormcloudVideoPlayer] Attempting to play ad:", vastTagUrl);
1579
1658
  }
1580
- this.ima.updateOriginalMutedState(this.video.muted);
1581
- if (!this.shouldContinueLiveStreamDuringAds()) {
1582
- if (this.config.debugAdTiming) {
1583
- console.log("[StormcloudVideoPlayer] Pausing video immediately for ad (VOD mode)");
1584
- }
1585
- this.video.pause();
1586
- } else {
1659
+ if (this.ima.isAdPlaying()) {
1587
1660
  if (this.config.debugAdTiming) {
1588
- console.log("[StormcloudVideoPlayer] Muting video for ad (Live mode)");
1661
+ console.warn(
1662
+ "[StormcloudVideoPlayer] Ad already playing - skipping new ad request"
1663
+ );
1589
1664
  }
1590
- this.video.muted = true;
1665
+ return;
1591
1666
  }
1667
+ this.ima.updateOriginalMutedState(this.video.muted);
1592
1668
  this.startAdFailsafeTimer();
1593
1669
  try {
1594
1670
  await this.ima.requestAds(vastTagUrl);
1671
+ if (this.config.debugAdTiming) {
1672
+ console.log("[StormcloudVideoPlayer] Ad request successful, starting playback");
1673
+ }
1595
1674
  await this.ima.play();
1596
1675
  if (this.config.debugAdTiming) {
1597
1676
  console.log("[StormcloudVideoPlayer] Ad playback started successfully");
@@ -1619,6 +1698,13 @@ var StormcloudVideoPlayer = class {
1619
1698
  this.showAds = false;
1620
1699
  this.currentAdIndex = 0;
1621
1700
  this.totalAdsInBreak = 0;
1701
+ const originalMutedState = this.ima.getOriginalMutedState();
1702
+ this.video.muted = originalMutedState;
1703
+ if (this.config.debugAdTiming) {
1704
+ console.log(
1705
+ `[StormcloudVideoPlayer] Restored mute state to: ${originalMutedState}`
1706
+ );
1707
+ }
1622
1708
  if (this.video.paused) {
1623
1709
  this.video.play()?.catch(() => {
1624
1710
  if (this.config.debugAdTiming) {
@@ -2562,39 +2648,144 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2562
2648
  left: "50%",
2563
2649
  transform: "translateX(-50%)",
2564
2650
  marginBottom: "4px",
2565
- background: "linear-gradient(135deg, rgba(0, 0, 0, 0.85) 0%, rgba(20, 20, 20, 0.9) 100%)",
2651
+ background: "linear-gradient(135deg, rgba(0, 0, 0, 0.88) 0%, rgba(20, 20, 20, 0.92) 100%)",
2566
2652
  backdropFilter: "blur(15px)",
2567
- padding: "16px 12px",
2568
- borderRadius: "12px",
2569
- border: "1px solid rgba(255, 255, 255, 0.1)",
2653
+ padding: "10px 14px",
2654
+ borderRadius: "14px",
2655
+ border: "1px solid rgba(255, 255, 255, 0.15)",
2570
2656
  display: "flex",
2571
2657
  flexDirection: "column",
2572
2658
  alignItems: "center",
2573
- height: "130px",
2574
- boxShadow: "0 12px 40px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.1)",
2575
- zIndex: 10
2659
+ justifyContent: "center",
2660
+ height: "128px",
2661
+ boxShadow: "0 12px 40px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15)",
2662
+ zIndex: 10,
2663
+ transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out"
2576
2664
  },
2577
- onMouseEnter: () => setShowVolumeSlider(true),
2578
- onMouseLeave: () => setShowVolumeSlider(false),
2579
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2580
- "input",
2665
+ onMouseEnter: (e) => {
2666
+ setShowVolumeSlider(true);
2667
+ e.currentTarget.style.transform = "translateX(-50%) translateY(-2px) scale(1.02)";
2668
+ e.currentTarget.style.boxShadow = "0 16px 48px rgba(0, 0, 0, 0.6), 0 6px 16px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 0 24px rgba(59, 130, 246, 0.3)";
2669
+ e.currentTarget.style.borderColor = "rgba(59, 130, 246, 0.4)";
2670
+ },
2671
+ onMouseLeave: (e) => {
2672
+ setShowVolumeSlider(false);
2673
+ e.currentTarget.style.transform = "translateX(-50%)";
2674
+ e.currentTarget.style.boxShadow = "0 12px 40px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15)";
2675
+ e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.15)";
2676
+ },
2677
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2678
+ "div",
2581
2679
  {
2582
- type: "range",
2583
- min: "0",
2584
- max: "1",
2585
- step: "0.01",
2586
- value: isMuted ? 0 : volume,
2587
- onChange: (e) => handleVolumeChange(parseFloat(e.target.value)),
2588
2680
  style: {
2589
- writingMode: "bt-lr",
2590
- WebkitAppearance: "slider-vertical",
2591
- width: "6px",
2592
- height: "90px",
2593
- background: "linear-gradient(180deg, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0.1) 100%)",
2594
- borderRadius: "3px",
2595
- outline: "none",
2596
- cursor: "pointer"
2597
- }
2681
+ position: "relative",
2682
+ width: "8px",
2683
+ height: "104px",
2684
+ cursor: "pointer",
2685
+ transition: "transform 0.2s ease-in-out"
2686
+ },
2687
+ onMouseEnter: (e) => {
2688
+ e.currentTarget.style.transform = "scaleX(1.2)";
2689
+ },
2690
+ onMouseLeave: (e) => {
2691
+ e.currentTarget.style.transform = "scaleX(1)";
2692
+ },
2693
+ onMouseDown: (e) => {
2694
+ e.preventDefault();
2695
+ const sliderElement = e.currentTarget;
2696
+ const handleMouseMove = (moveEvent) => {
2697
+ if (!sliderElement) return;
2698
+ const rect2 = sliderElement.getBoundingClientRect();
2699
+ const y2 = moveEvent.clientY - rect2.top;
2700
+ const percentage2 = 1 - Math.max(0, Math.min(1, y2 / rect2.height));
2701
+ handleVolumeChange(percentage2);
2702
+ };
2703
+ const handleMouseUp = () => {
2704
+ document.removeEventListener("mousemove", handleMouseMove);
2705
+ document.removeEventListener("mouseup", handleMouseUp);
2706
+ };
2707
+ document.addEventListener("mousemove", handleMouseMove);
2708
+ document.addEventListener("mouseup", handleMouseUp);
2709
+ const rect = sliderElement.getBoundingClientRect();
2710
+ const y = e.clientY - rect.top;
2711
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
2712
+ handleVolumeChange(percentage);
2713
+ },
2714
+ onClick: (e) => {
2715
+ e.stopPropagation();
2716
+ const rect = e.currentTarget.getBoundingClientRect();
2717
+ const y = e.clientY - rect.top;
2718
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
2719
+ handleVolumeChange(percentage);
2720
+ },
2721
+ children: [
2722
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2723
+ "div",
2724
+ {
2725
+ style: {
2726
+ position: "absolute",
2727
+ bottom: "0",
2728
+ left: "0",
2729
+ width: "100%",
2730
+ height: "100%",
2731
+ background: "linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0.15) 100%)",
2732
+ borderRadius: "4px",
2733
+ boxShadow: "inset 0 1px 3px rgba(0, 0, 0, 0.2)"
2734
+ }
2735
+ }
2736
+ ),
2737
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2738
+ "div",
2739
+ {
2740
+ style: {
2741
+ position: "absolute",
2742
+ bottom: "0",
2743
+ left: "0",
2744
+ width: "100%",
2745
+ height: `${(isMuted ? 0 : volume) * 100}%`,
2746
+ background: "linear-gradient(180deg, rgba(96, 165, 250, 1) 0%, rgba(59, 130, 246, 0.95) 50%, rgba(37, 99, 235, 0.9) 100%)",
2747
+ borderRadius: "4px",
2748
+ transition: "height 0.15s ease-out, box-shadow 0.2s ease-in-out",
2749
+ boxShadow: "0 0 8px rgba(59, 130, 246, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.3)"
2750
+ }
2751
+ }
2752
+ ),
2753
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2754
+ "div",
2755
+ {
2756
+ style: {
2757
+ position: "absolute",
2758
+ bottom: `calc(${(isMuted ? 0 : volume) * 100}% - 7px)`,
2759
+ left: "50%",
2760
+ transform: "translateX(-50%)",
2761
+ width: "14px",
2762
+ height: "14px",
2763
+ background: "linear-gradient(135deg, #ffffff 0%, #f0f9ff 100%)",
2764
+ borderRadius: "50%",
2765
+ boxShadow: "0 2px 6px rgba(0, 0, 0, 0.3), 0 0 0 2px rgba(59, 130, 246, 0.3), 0 0 12px rgba(59, 130, 246, 0.4)",
2766
+ transition: "bottom 0.15s ease-out, transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, width 0.2s ease-in-out, height 0.2s ease-in-out",
2767
+ cursor: "grab"
2768
+ },
2769
+ onMouseEnter: (e) => {
2770
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.3)";
2771
+ e.currentTarget.style.boxShadow = "0 3px 10px rgba(0, 0, 0, 0.4), 0 0 0 3px rgba(59, 130, 246, 0.5), 0 0 20px rgba(59, 130, 246, 0.6)";
2772
+ e.currentTarget.style.cursor = "grab";
2773
+ },
2774
+ onMouseLeave: (e) => {
2775
+ e.currentTarget.style.transform = "translateX(-50%) scale(1)";
2776
+ e.currentTarget.style.boxShadow = "0 2px 6px rgba(0, 0, 0, 0.3), 0 0 0 2px rgba(59, 130, 246, 0.3), 0 0 12px rgba(59, 130, 246, 0.4)";
2777
+ },
2778
+ onMouseDown: (e) => {
2779
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.4)";
2780
+ e.currentTarget.style.cursor = "grabbing";
2781
+ },
2782
+ onMouseUp: (e) => {
2783
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.3)";
2784
+ e.currentTarget.style.cursor = "grab";
2785
+ }
2786
+ }
2787
+ )
2788
+ ]
2598
2789
  }
2599
2790
  )
2600
2791
  }
@@ -2911,40 +3102,146 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2911
3102
  left: "50%",
2912
3103
  transform: "translateX(-50%)",
2913
3104
  marginBottom: "4px",
2914
- background: "linear-gradient(135deg, rgba(0, 0, 0, 0.95) 0%, rgba(20, 20, 20, 0.9) 100%)",
3105
+ background: "linear-gradient(135deg, rgba(0, 0, 0, 0.96) 0%, rgba(20, 20, 20, 0.92) 100%)",
2915
3106
  backdropFilter: "blur(20px)",
2916
- padding: "16px 12px",
2917
- borderRadius: "12px",
2918
- border: "2px solid rgba(255, 255, 255, 0.6)",
3107
+ padding: "10px 14px",
3108
+ borderRadius: "14px",
3109
+ border: "2px solid rgba(255, 255, 255, 0.7)",
2919
3110
  display: "flex",
2920
3111
  flexDirection: "column",
2921
3112
  alignItems: "center",
2922
- height: "130px",
2923
- boxShadow: "0 12px 40px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.3)",
2924
- zIndex: 10
3113
+ justifyContent: "center",
3114
+ height: "128px",
3115
+ boxShadow: "0 12px 40px rgba(0, 0, 0, 0.85), 0 4px 12px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.35)",
3116
+ zIndex: 10,
3117
+ transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out"
2925
3118
  },
2926
- onMouseEnter: () => setShowVolumeSlider(true),
2927
- onMouseLeave: () => setShowVolumeSlider(false),
2928
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2929
- "input",
3119
+ onMouseEnter: (e) => {
3120
+ setShowVolumeSlider(true);
3121
+ e.currentTarget.style.transform = "translateX(-50%) translateY(-2px) scale(1.02)";
3122
+ e.currentTarget.style.boxShadow = "0 16px 48px rgba(0, 0, 0, 0.9), 0 6px 16px rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 24px rgba(96, 165, 250, 0.4)";
3123
+ e.currentTarget.style.borderColor = "rgba(96, 165, 250, 0.8)";
3124
+ },
3125
+ onMouseLeave: (e) => {
3126
+ setShowVolumeSlider(false);
3127
+ e.currentTarget.style.transform = "translateX(-50%)";
3128
+ e.currentTarget.style.boxShadow = "0 12px 40px rgba(0, 0, 0, 0.85), 0 4px 12px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.35)";
3129
+ e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.7)";
3130
+ },
3131
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
3132
+ "div",
2930
3133
  {
2931
- type: "range",
2932
- min: "0",
2933
- max: "1",
2934
- step: "0.01",
2935
- value: isMuted ? 0 : volume,
2936
- onChange: (e) => handleVolumeChange(parseFloat(e.target.value)),
2937
3134
  style: {
2938
- writingMode: "bt-lr",
2939
- WebkitAppearance: "slider-vertical",
2940
- width: "6px",
2941
- height: "90px",
2942
- background: "linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.4) 100%)",
2943
- borderRadius: "3px",
2944
- outline: "none",
3135
+ position: "relative",
3136
+ width: "8px",
3137
+ height: "104px",
2945
3138
  cursor: "pointer",
2946
- border: "1px solid rgba(255, 255, 255, 0.3)"
2947
- }
3139
+ transition: "transform 0.2s ease-in-out"
3140
+ },
3141
+ onMouseEnter: (e) => {
3142
+ e.currentTarget.style.transform = "scaleX(1.2)";
3143
+ },
3144
+ onMouseLeave: (e) => {
3145
+ e.currentTarget.style.transform = "scaleX(1)";
3146
+ },
3147
+ onMouseDown: (e) => {
3148
+ e.preventDefault();
3149
+ const sliderElement = e.currentTarget;
3150
+ const handleMouseMove = (moveEvent) => {
3151
+ if (!sliderElement) return;
3152
+ const rect2 = sliderElement.getBoundingClientRect();
3153
+ const y2 = moveEvent.clientY - rect2.top;
3154
+ const percentage2 = 1 - Math.max(0, Math.min(1, y2 / rect2.height));
3155
+ handleVolumeChange(percentage2);
3156
+ };
3157
+ const handleMouseUp = () => {
3158
+ document.removeEventListener("mousemove", handleMouseMove);
3159
+ document.removeEventListener("mouseup", handleMouseUp);
3160
+ };
3161
+ document.addEventListener("mousemove", handleMouseMove);
3162
+ document.addEventListener("mouseup", handleMouseUp);
3163
+ const rect = sliderElement.getBoundingClientRect();
3164
+ const y = e.clientY - rect.top;
3165
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
3166
+ handleVolumeChange(percentage);
3167
+ },
3168
+ onClick: (e) => {
3169
+ e.stopPropagation();
3170
+ const rect = e.currentTarget.getBoundingClientRect();
3171
+ const y = e.clientY - rect.top;
3172
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
3173
+ handleVolumeChange(percentage);
3174
+ },
3175
+ children: [
3176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3177
+ "div",
3178
+ {
3179
+ style: {
3180
+ position: "absolute",
3181
+ bottom: "0",
3182
+ left: "0",
3183
+ width: "100%",
3184
+ height: "100%",
3185
+ background: "linear-gradient(180deg, rgba(255, 255, 255, 0.85) 0%, rgba(255, 255, 255, 0.5) 100%)",
3186
+ borderRadius: "4px",
3187
+ border: "1px solid rgba(255, 255, 255, 0.4)",
3188
+ boxShadow: "inset 0 1px 3px rgba(0, 0, 0, 0.3)"
3189
+ }
3190
+ }
3191
+ ),
3192
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3193
+ "div",
3194
+ {
3195
+ style: {
3196
+ position: "absolute",
3197
+ bottom: "0",
3198
+ left: "0",
3199
+ width: "100%",
3200
+ height: `${(isMuted ? 0 : volume) * 100}%`,
3201
+ background: "linear-gradient(180deg, rgba(125, 211, 252, 1) 0%, rgba(96, 165, 250, 0.98) 50%, rgba(59, 130, 246, 0.95) 100%)",
3202
+ borderRadius: "4px",
3203
+ transition: "height 0.15s ease-out, box-shadow 0.2s ease-in-out",
3204
+ boxShadow: "0 0 12px rgba(96, 165, 250, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.4)"
3205
+ }
3206
+ }
3207
+ ),
3208
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3209
+ "div",
3210
+ {
3211
+ style: {
3212
+ position: "absolute",
3213
+ bottom: `calc(${(isMuted ? 0 : volume) * 100}% - 8px)`,
3214
+ left: "50%",
3215
+ transform: "translateX(-50%)",
3216
+ width: "16px",
3217
+ height: "16px",
3218
+ background: "linear-gradient(135deg, #ffffff 0%, #f0f9ff 100%)",
3219
+ borderRadius: "50%",
3220
+ border: "2px solid rgba(96, 165, 250, 0.9)",
3221
+ boxShadow: "0 3px 8px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(96, 165, 250, 0.4), 0 0 16px rgba(96, 165, 250, 0.5)",
3222
+ transition: "bottom 0.15s ease-out, transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, width 0.2s ease-in-out, height 0.2s ease-in-out",
3223
+ cursor: "grab"
3224
+ },
3225
+ onMouseEnter: (e) => {
3226
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.35)";
3227
+ e.currentTarget.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.6), 0 0 0 3px rgba(96, 165, 250, 0.6), 0 0 24px rgba(96, 165, 250, 0.7)";
3228
+ e.currentTarget.style.cursor = "grab";
3229
+ },
3230
+ onMouseLeave: (e) => {
3231
+ e.currentTarget.style.transform = "translateX(-50%) scale(1)";
3232
+ e.currentTarget.style.boxShadow = "0 3px 8px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(96, 165, 250, 0.4), 0 0 16px rgba(96, 165, 250, 0.5)";
3233
+ },
3234
+ onMouseDown: (e) => {
3235
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.45)";
3236
+ e.currentTarget.style.cursor = "grabbing";
3237
+ },
3238
+ onMouseUp: (e) => {
3239
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.35)";
3240
+ e.currentTarget.style.cursor = "grab";
3241
+ }
3242
+ }
3243
+ )
3244
+ ]
2948
3245
  }
2949
3246
  )
2950
3247
  }