unified-video-framework 1.4.166 → 1.4.168
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.
|
@@ -51,9 +51,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
51
51
|
private currentPlaybackRate = 1;
|
|
52
52
|
private isDragging: boolean = false;
|
|
53
53
|
|
|
54
|
-
// Settings menu state guard to keep controls interactive while open
|
|
55
|
-
private isSettingsOpen: boolean = false;
|
|
56
|
-
|
|
57
54
|
// Settings configuration
|
|
58
55
|
private settingsConfig = {
|
|
59
56
|
enabled: true, // Show settings button
|
|
@@ -100,20 +97,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
100
97
|
// Progress bar tooltip state
|
|
101
98
|
private showTimeTooltip: boolean = false;
|
|
102
99
|
|
|
103
|
-
// Material You touch gesture state
|
|
104
|
-
private lastTapTime: number = 0;
|
|
105
|
-
private tapCount: number = 0;
|
|
106
|
-
private tapTimeout: NodeJS.Timeout | null = null;
|
|
107
|
-
private longPressTimer: NodeJS.Timeout | null = null;
|
|
108
|
-
private longPressActive: boolean = false;
|
|
109
|
-
private longPressStartTime: number = 0;
|
|
110
|
-
private originalPlaybackRate: number = 1;
|
|
111
|
-
|
|
112
|
-
// Material You dynamic theming - matches existing theme
|
|
113
|
-
private dominantColor: string = '#ff0000'; // Primary accent from theme
|
|
114
|
-
private accentColor: string = '#ff4d4f'; // Secondary accent from theme
|
|
115
|
-
private surfaceTint: string = 'rgba(255, 0, 0, 0.08)'; // Surface tint for Material containers
|
|
116
|
-
|
|
117
100
|
// Autoplay enhancement state
|
|
118
101
|
private autoplayCapabilities: {
|
|
119
102
|
canAutoplay: boolean;
|
|
@@ -359,35 +342,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
359
342
|
|
|
360
343
|
// Initialize metadata UI to hidden/empty by default
|
|
361
344
|
this.updateMetadataUI();
|
|
362
|
-
|
|
363
|
-
// Enable Material You mobile layout on mobile portrait devices
|
|
364
|
-
this.enableMaterialYouMobileIfNeeded();
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Enable Material You mobile layout if on mobile portrait device
|
|
369
|
-
*/
|
|
370
|
-
private enableMaterialYouMobileIfNeeded(): void {
|
|
371
|
-
// Check if mobile and portrait
|
|
372
|
-
const isMobile = this.isMobileDevice();
|
|
373
|
-
const isPortrait = window.innerHeight > window.innerWidth;
|
|
374
|
-
|
|
375
|
-
if (isMobile && isPortrait && this.playerWrapper) {
|
|
376
|
-
this.debugLog('Enabling Material You mobile layout');
|
|
377
|
-
this.playerWrapper.classList.add('uvf-material-you-mobile');
|
|
378
|
-
|
|
379
|
-
// Listen for orientation changes
|
|
380
|
-
window.addEventListener('resize', () => {
|
|
381
|
-
const isNowPortrait = window.innerHeight > window.innerWidth;
|
|
382
|
-
if (this.playerWrapper) {
|
|
383
|
-
if (isMobile && isNowPortrait) {
|
|
384
|
-
this.playerWrapper.classList.add('uvf-material-you-mobile');
|
|
385
|
-
} else {
|
|
386
|
-
this.playerWrapper.classList.remove('uvf-material-you-mobile');
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
345
|
}
|
|
392
346
|
|
|
393
347
|
private setupVideoEventListeners(): void {
|
|
@@ -480,9 +434,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
480
434
|
// Update time display immediately when metadata loads
|
|
481
435
|
this.updateTimeDisplay();
|
|
482
436
|
|
|
483
|
-
// Render chapter markers if enabled
|
|
484
|
-
this.renderChapterMarkersOnProgressBar();
|
|
485
|
-
|
|
486
437
|
this.emit('onLoadedMetadata', {
|
|
487
438
|
duration: this.video.duration || 0,
|
|
488
439
|
width: this.video.videoWidth || 0,
|
|
@@ -2392,8 +2343,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
2392
2343
|
--uvf-scrollbar-thumb-hover-start: rgba(255,0,0,0.5);
|
|
2393
2344
|
--uvf-scrollbar-thumb-hover-end: rgba(255,0,0,0.6);
|
|
2394
2345
|
--uvf-firefox-scrollbar-color: rgba(255,255,255,0.25);
|
|
2395
|
-
/* Material You surface tint */
|
|
2396
|
-
--uvf-surface-tint: rgba(255, 0, 0, 0.08);
|
|
2397
2346
|
}
|
|
2398
2347
|
|
|
2399
2348
|
/* Player focus styles for better UX */
|
|
@@ -4302,162 +4251,35 @@ export class WebPlayer extends BasePlayer {
|
|
|
4302
4251
|
}
|
|
4303
4252
|
}
|
|
4304
4253
|
|
|
4305
|
-
/* iOS Safari specific fixes -
|
|
4254
|
+
/* iOS Safari specific fixes - fullscreen only */
|
|
4306
4255
|
@supports (-webkit-appearance: none) {
|
|
4307
4256
|
.uvf-player-wrapper.uvf-fullscreen,
|
|
4308
4257
|
.uvf-video-container.uvf-fullscreen {
|
|
4309
4258
|
height: -webkit-fill-available;
|
|
4310
4259
|
min-height: -webkit-fill-available;
|
|
4311
4260
|
}
|
|
4312
|
-
|
|
4313
|
-
/* Handle iOS Safari's dynamic address bar */
|
|
4314
|
-
@media screen and (max-width: 767px) {
|
|
4315
|
-
.uvf-responsive-container {
|
|
4316
|
-
height: -webkit-fill-available;
|
|
4317
|
-
min-height: 100vh;
|
|
4318
|
-
}
|
|
4319
|
-
|
|
4320
|
-
.uvf-player-wrapper {
|
|
4321
|
-
height: -webkit-fill-available;
|
|
4322
|
-
min-height: 100vh;
|
|
4323
|
-
/* Fix for iOS Safari control overlay positioning */
|
|
4324
|
-
position: relative;
|
|
4325
|
-
overflow: hidden;
|
|
4326
|
-
}
|
|
4327
|
-
|
|
4328
|
-
/* iOS Safari specific fixes for control positioning */
|
|
4329
|
-
.uvf-controls-bar {
|
|
4330
|
-
position: absolute !important;
|
|
4331
|
-
bottom: 0 !important;
|
|
4332
|
-
left: 0 !important;
|
|
4333
|
-
right: 0 !important;
|
|
4334
|
-
/* Ensure hardware acceleration */
|
|
4335
|
-
-webkit-transform: translate3d(0,0,0);
|
|
4336
|
-
transform: translate3d(0,0,0);
|
|
4337
|
-
/* Prevent any webkit transforms that could cause positioning issues */
|
|
4338
|
-
-webkit-perspective: 1000;
|
|
4339
|
-
perspective: 1000;
|
|
4340
|
-
}
|
|
4341
|
-
|
|
4342
|
-
/* Ensure all control elements use hardware acceleration */
|
|
4343
|
-
.uvf-control-btn,
|
|
4344
|
-
.uvf-progress-bar,
|
|
4345
|
-
.uvf-progress-section {
|
|
4346
|
-
-webkit-transform: translateZ(0);
|
|
4347
|
-
transform: translateZ(0);
|
|
4348
|
-
}
|
|
4349
|
-
}
|
|
4350
|
-
}
|
|
4351
|
-
|
|
4352
|
-
/* Android Chrome specific fixes */
|
|
4353
|
-
@supports (display: -webkit-box) {
|
|
4354
|
-
.uvf-responsive-container {
|
|
4355
|
-
min-height: 100vh;
|
|
4356
|
-
}
|
|
4357
|
-
|
|
4358
|
-
/* Fix for Android Chrome's address bar behavior */
|
|
4359
|
-
@media screen and (max-width: 767px) {
|
|
4360
|
-
.uvf-video-container {
|
|
4361
|
-
min-height: calc(100vh - 56px); /* Chrome mobile address bar height */
|
|
4362
|
-
}
|
|
4363
|
-
}
|
|
4364
4261
|
}
|
|
4365
4262
|
|
|
4366
|
-
/* Samsung Internet Browser fixes */
|
|
4367
|
-
@media screen and (-webkit-min-device-pixel-ratio: 1) {
|
|
4368
|
-
@media screen and (max-width: 767px) {
|
|
4369
|
-
.uvf-responsive-container {
|
|
4370
|
-
position: fixed;
|
|
4371
|
-
top: 0;
|
|
4372
|
-
left: 0;
|
|
4373
|
-
width: 100vw;
|
|
4374
|
-
height: 100vh;
|
|
4375
|
-
}
|
|
4376
|
-
}
|
|
4377
|
-
}
|
|
4378
4263
|
|
|
4379
|
-
/*
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4264
|
+
/* Enhanced Responsive Media Queries with UX Best Practices */
|
|
4265
|
+
/* Mobile devices (portrait) - Material You Design (25-50-25 Layout) */
|
|
4266
|
+
@media screen and (max-width: 767px) and (orientation: portrait) {
|
|
4267
|
+
.uvf-responsive-container {
|
|
4268
|
+
padding: 0;
|
|
4269
|
+
width: 100vw !important;
|
|
4270
|
+
height: 100vh;
|
|
4271
|
+
height: 100dvh;
|
|
4272
|
+
margin: 0;
|
|
4273
|
+
position: fixed;
|
|
4274
|
+
top: 0;
|
|
4275
|
+
left: 0;
|
|
4388
4276
|
overflow: hidden;
|
|
4389
|
-
|
|
4390
|
-
/* Prevent zoom on double tap */
|
|
4391
|
-
touch-action: manipulation;
|
|
4392
|
-
}
|
|
4393
|
-
|
|
4394
|
-
.uvf-video {
|
|
4395
|
-
/* Prevent video from being selectable */
|
|
4396
|
-
-webkit-user-select: none;
|
|
4397
|
-
-moz-user-select: none;
|
|
4398
|
-
-ms-user-select: none;
|
|
4399
|
-
user-select: none;
|
|
4400
|
-
|
|
4401
|
-
/* Ensure hardware acceleration */
|
|
4402
|
-
-webkit-transform: translateZ(0);
|
|
4403
|
-
transform: translateZ(0);
|
|
4404
4277
|
}
|
|
4405
4278
|
|
|
4406
|
-
|
|
4407
|
-
.uvf-controls-bar {
|
|
4408
|
-
position: absolute !important;
|
|
4409
|
-
bottom: 0 !important;
|
|
4410
|
-
left: 0 !important;
|
|
4411
|
-
right: 0 !important;
|
|
4412
|
-
/* Remove fixed positioning that causes issues on iOS Safari */
|
|
4413
|
-
z-index: 1000 !important;
|
|
4414
|
-
transform: translateZ(0); /* Force hardware acceleration */
|
|
4415
|
-
}
|
|
4416
|
-
|
|
4417
|
-
/* Ensure controls stay above virtual keyboards */
|
|
4418
|
-
@supports (bottom: env(keyboard-inset-height)) {
|
|
4419
|
-
.uvf-controls-bar {
|
|
4420
|
-
bottom: max(0px, env(keyboard-inset-height, 0)) !important;
|
|
4421
|
-
padding-bottom: calc(16px + max(var(--uvf-safe-area-bottom, 0), env(keyboard-inset-height, 0))) !important;
|
|
4422
|
-
}
|
|
4423
|
-
}
|
|
4424
|
-
|
|
4425
|
-
/* Hide PiP button on mobile - not supported on most mobile browsers */
|
|
4426
|
-
#uvf-pip-btn {
|
|
4427
|
-
display: none !important;
|
|
4428
|
-
}
|
|
4429
|
-
|
|
4430
|
-
/* Mobile fullscreen enhancements */
|
|
4431
|
-
.uvf-player-wrapper.uvf-fullscreen {
|
|
4432
|
-
/* Ensure fullscreen covers entire viewport on mobile */
|
|
4433
|
-
position: fixed !important;
|
|
4434
|
-
top: 0 !important;
|
|
4435
|
-
left: 0 !important;
|
|
4279
|
+
.uvf-responsive-container .uvf-player-wrapper {
|
|
4436
4280
|
width: 100vw !important;
|
|
4437
|
-
height: 100vh !important;
|
|
4438
|
-
z-index: 2147483647 !important;
|
|
4439
|
-
background: #000 !important;
|
|
4440
|
-
}
|
|
4441
|
-
|
|
4442
|
-
/* iOS Safari specific fullscreen fixes */
|
|
4443
|
-
@supports (-webkit-appearance: none) {
|
|
4444
|
-
.uvf-player-wrapper.uvf-fullscreen {
|
|
4445
|
-
/* Use viewport units that work better with iOS Safari */
|
|
4446
|
-
height: -webkit-fill-available !important;
|
|
4447
|
-
}
|
|
4448
|
-
}
|
|
4449
|
-
}
|
|
4450
|
-
|
|
4451
|
-
/* Enhanced Responsive Media Queries with UX Best Practices */
|
|
4452
|
-
|
|
4453
|
-
/* Material You Mobile Portrait Layout - 25% Black + 50% Video + 25% Black */
|
|
4454
|
-
@media screen and (max-width: 767px) and (orientation: portrait) {
|
|
4455
|
-
/* Enable Material You mode with class flag */
|
|
4456
|
-
.uvf-player-wrapper.uvf-material-you-mobile {
|
|
4457
|
-
/* Full viewport height layout */
|
|
4458
4281
|
height: 100vh;
|
|
4459
4282
|
height: 100dvh;
|
|
4460
|
-
width: 100vw;
|
|
4461
4283
|
position: fixed;
|
|
4462
4284
|
top: 0;
|
|
4463
4285
|
left: 0;
|
|
@@ -4467,8 +4289,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
4467
4289
|
overflow: hidden;
|
|
4468
4290
|
}
|
|
4469
4291
|
|
|
4470
|
-
|
|
4471
|
-
|
|
4292
|
+
/* Video container occupies middle 50% */
|
|
4293
|
+
.uvf-responsive-container .uvf-video-container {
|
|
4472
4294
|
height: 50vh;
|
|
4473
4295
|
height: 50dvh;
|
|
4474
4296
|
width: 100vw;
|
|
@@ -4476,22 +4298,20 @@ export class WebPlayer extends BasePlayer {
|
|
|
4476
4298
|
margin-top: 25vh;
|
|
4477
4299
|
margin-top: 25dvh;
|
|
4478
4300
|
aspect-ratio: unset !important;
|
|
4479
|
-
/* Match existing theme background */
|
|
4480
4301
|
background: radial-gradient(ellipse at center, #1a1a2e 0%, #000 100%);
|
|
4481
|
-
/* Material Design elevation */
|
|
4482
4302
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4),
|
|
4483
4303
|
0 4px 16px rgba(0, 0, 0, 0.3),
|
|
4484
4304
|
0 2px 8px rgba(0, 0, 0, 0.2);
|
|
4485
4305
|
}
|
|
4486
4306
|
|
|
4487
|
-
.uvf-
|
|
4307
|
+
.uvf-video {
|
|
4488
4308
|
width: 100%;
|
|
4489
4309
|
height: 100%;
|
|
4490
4310
|
object-fit: contain;
|
|
4491
4311
|
}
|
|
4492
4312
|
|
|
4493
|
-
/* Top black section (25%) - Tap zone
|
|
4494
|
-
.uvf-player-wrapper
|
|
4313
|
+
/* Top black section (25%) - Tap zone */
|
|
4314
|
+
.uvf-player-wrapper::before {
|
|
4495
4315
|
content: '';
|
|
4496
4316
|
position: absolute;
|
|
4497
4317
|
top: 0;
|
|
@@ -4505,8 +4325,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
4505
4325
|
touch-action: manipulation;
|
|
4506
4326
|
}
|
|
4507
4327
|
|
|
4508
|
-
/* Bottom black section (25%) -
|
|
4509
|
-
.uvf-player-wrapper
|
|
4328
|
+
/* Bottom black section (25%) - Controls area */
|
|
4329
|
+
.uvf-player-wrapper::after {
|
|
4510
4330
|
content: '';
|
|
4511
4331
|
position: absolute;
|
|
4512
4332
|
bottom: 0;
|
|
@@ -4523,7 +4343,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
4523
4343
|
}
|
|
4524
4344
|
|
|
4525
4345
|
/* Material surface container for controls */
|
|
4526
|
-
.uvf-
|
|
4346
|
+
.uvf-controls-bar {
|
|
4527
4347
|
position: absolute;
|
|
4528
4348
|
bottom: 0;
|
|
4529
4349
|
left: 0;
|
|
@@ -4538,13 +4358,12 @@ export class WebPlayer extends BasePlayer {
|
|
|
4538
4358
|
display: flex;
|
|
4539
4359
|
flex-direction: column;
|
|
4540
4360
|
justify-content: flex-end;
|
|
4541
|
-
/* Material Design surface with tint */
|
|
4542
4361
|
backdrop-filter: blur(24px);
|
|
4543
4362
|
-webkit-backdrop-filter: blur(24px);
|
|
4544
4363
|
}
|
|
4545
4364
|
|
|
4546
4365
|
/* Material surface tint overlay */
|
|
4547
|
-
.uvf-
|
|
4366
|
+
.uvf-controls-bar::before {
|
|
4548
4367
|
content: '';
|
|
4549
4368
|
position: absolute;
|
|
4550
4369
|
inset: 0;
|
|
@@ -4555,87 +4374,53 @@ export class WebPlayer extends BasePlayer {
|
|
|
4555
4374
|
}
|
|
4556
4375
|
|
|
4557
4376
|
/* Progress bar with chapter markers */
|
|
4558
|
-
.uvf-
|
|
4377
|
+
.uvf-progress-section {
|
|
4559
4378
|
margin-bottom: 12px;
|
|
4560
4379
|
position: relative;
|
|
4561
4380
|
}
|
|
4562
4381
|
|
|
4563
|
-
.uvf-
|
|
4382
|
+
.uvf-progress-bar-wrapper {
|
|
4564
4383
|
padding: 12px 0;
|
|
4565
4384
|
position: relative;
|
|
4566
4385
|
}
|
|
4567
4386
|
|
|
4568
|
-
.uvf-
|
|
4387
|
+
.uvf-progress-bar {
|
|
4569
4388
|
height: 4px;
|
|
4570
4389
|
background: rgba(255, 255, 255, 0.2);
|
|
4571
4390
|
border-radius: 4px;
|
|
4572
4391
|
position: relative;
|
|
4573
4392
|
overflow: visible;
|
|
4574
|
-
/* Material elevation */
|
|
4575
4393
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
4576
4394
|
}
|
|
4577
4395
|
|
|
4578
|
-
.uvf-
|
|
4396
|
+
.uvf-progress-filled {
|
|
4579
4397
|
background: var(--uvf-accent-1, #ff0000);
|
|
4580
4398
|
box-shadow: 0 0 8px var(--uvf-accent-1, #ff0000);
|
|
4581
4399
|
}
|
|
4582
4400
|
|
|
4583
|
-
.uvf-
|
|
4401
|
+
.uvf-progress-handle {
|
|
4584
4402
|
width: 20px;
|
|
4585
4403
|
height: 20px;
|
|
4586
4404
|
background: var(--uvf-accent-1, #ff0000);
|
|
4587
|
-
/* Material Design state layer */
|
|
4588
4405
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3),
|
|
4589
4406
|
0 0 0 0 var(--uvf-accent-1, #ff0000);
|
|
4590
4407
|
transition: box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
4591
4408
|
}
|
|
4592
4409
|
|
|
4593
|
-
.uvf-
|
|
4410
|
+
.uvf-progress-handle:active {
|
|
4594
4411
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4),
|
|
4595
4412
|
0 0 0 12px rgba(255, 0, 0, 0.15);
|
|
4596
4413
|
transform: translate(-50%, -50%) scale(1.2);
|
|
4597
4414
|
}
|
|
4598
4415
|
|
|
4599
|
-
/* Chapter markers on progress bar */
|
|
4600
|
-
.uvf-chapter-marker {
|
|
4601
|
-
position: absolute;
|
|
4602
|
-
top: 0;
|
|
4603
|
-
height: 100%;
|
|
4604
|
-
width: 3px;
|
|
4605
|
-
background: rgba(255, 255, 255, 0.4);
|
|
4606
|
-
border-radius: 2px;
|
|
4607
|
-
transform: translateX(-50%);
|
|
4608
|
-
pointer-events: none;
|
|
4609
|
-
z-index: 1;
|
|
4610
|
-
transition: all 0.2s ease;
|
|
4611
|
-
}
|
|
4612
|
-
|
|
4613
|
-
.uvf-chapter-marker.intro {
|
|
4614
|
-
background: #4CAF50;
|
|
4615
|
-
}
|
|
4616
|
-
|
|
4617
|
-
.uvf-chapter-marker.recap {
|
|
4618
|
-
background: #FFC107;
|
|
4619
|
-
}
|
|
4620
|
-
|
|
4621
|
-
.uvf-chapter-marker.credits {
|
|
4622
|
-
background: #9C27B0;
|
|
4623
|
-
}
|
|
4624
|
-
|
|
4625
|
-
.uvf-progress-bar-wrapper:hover .uvf-chapter-marker {
|
|
4626
|
-
width: 4px;
|
|
4627
|
-
opacity: 1;
|
|
4628
|
-
}
|
|
4629
|
-
|
|
4630
4416
|
/* Material Design control buttons */
|
|
4631
|
-
.uvf-
|
|
4417
|
+
.uvf-control-btn {
|
|
4632
4418
|
width: 48px;
|
|
4633
4419
|
height: 48px;
|
|
4634
4420
|
min-width: 48px;
|
|
4635
4421
|
min-height: 48px;
|
|
4636
4422
|
background: rgba(255, 255, 255, 0.12);
|
|
4637
4423
|
border-radius: 24px;
|
|
4638
|
-
/* Material elevation level 1 */
|
|
4639
4424
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12),
|
|
4640
4425
|
0 1px 2px rgba(0, 0, 0, 0.24);
|
|
4641
4426
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
@@ -4644,7 +4429,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
4644
4429
|
}
|
|
4645
4430
|
|
|
4646
4431
|
/* Material ripple effect */
|
|
4647
|
-
.uvf-
|
|
4432
|
+
.uvf-control-btn::before {
|
|
4648
4433
|
content: '';
|
|
4649
4434
|
position: absolute;
|
|
4650
4435
|
inset: 0;
|
|
@@ -4654,24 +4439,22 @@ export class WebPlayer extends BasePlayer {
|
|
|
4654
4439
|
transition: opacity 0.2s ease;
|
|
4655
4440
|
}
|
|
4656
4441
|
|
|
4657
|
-
.uvf-
|
|
4442
|
+
.uvf-control-btn:active::before {
|
|
4658
4443
|
opacity: 1;
|
|
4659
4444
|
}
|
|
4660
4445
|
|
|
4661
|
-
.uvf-
|
|
4662
|
-
/* Material elevation level 2 */
|
|
4446
|
+
.uvf-control-btn:active {
|
|
4663
4447
|
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16),
|
|
4664
4448
|
0 3px 6px rgba(0, 0, 0, 0.23);
|
|
4665
4449
|
transform: scale(0.95);
|
|
4666
4450
|
}
|
|
4667
4451
|
|
|
4668
|
-
.uvf-
|
|
4452
|
+
.uvf-control-btn.play-pause {
|
|
4669
4453
|
width: 56px;
|
|
4670
4454
|
height: 56px;
|
|
4671
4455
|
min-width: 56px;
|
|
4672
4456
|
min-height: 56px;
|
|
4673
4457
|
border-radius: 28px;
|
|
4674
|
-
/* Material elevated button */
|
|
4675
4458
|
background: linear-gradient(135deg,
|
|
4676
4459
|
var(--uvf-accent-1, #ff0000),
|
|
4677
4460
|
var(--uvf-accent-2, #ff4d4f));
|
|
@@ -4680,21 +4463,31 @@ export class WebPlayer extends BasePlayer {
|
|
|
4680
4463
|
0 0 0 0 var(--uvf-accent-1, #ff0000);
|
|
4681
4464
|
}
|
|
4682
4465
|
|
|
4683
|
-
.uvf-
|
|
4466
|
+
.uvf-control-btn.play-pause:active {
|
|
4684
4467
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25),
|
|
4685
4468
|
0 4px 8px rgba(0, 0, 0, 0.20),
|
|
4686
4469
|
0 0 0 8px rgba(255, 0, 0, 0.12);
|
|
4687
4470
|
}
|
|
4688
4471
|
|
|
4472
|
+
.uvf-control-btn svg {
|
|
4473
|
+
width: 20px;
|
|
4474
|
+
height: 20px;
|
|
4475
|
+
}
|
|
4476
|
+
|
|
4477
|
+
.uvf-control-btn.play-pause svg {
|
|
4478
|
+
width: 24px;
|
|
4479
|
+
height: 24px;
|
|
4480
|
+
}
|
|
4481
|
+
|
|
4689
4482
|
/* Controls row with Material spacing */
|
|
4690
|
-
.uvf-
|
|
4483
|
+
.uvf-controls-row {
|
|
4691
4484
|
gap: 16px;
|
|
4692
4485
|
padding: 0;
|
|
4693
4486
|
align-items: center;
|
|
4694
4487
|
}
|
|
4695
4488
|
|
|
4696
4489
|
/* Time display with Material surface */
|
|
4697
|
-
.uvf-
|
|
4490
|
+
.uvf-time-display {
|
|
4698
4491
|
background: rgba(255, 255, 255, 0.1);
|
|
4699
4492
|
backdrop-filter: blur(8px);
|
|
4700
4493
|
border-radius: 16px;
|
|
@@ -4702,101 +4495,20 @@ export class WebPlayer extends BasePlayer {
|
|
|
4702
4495
|
font-size: 13px;
|
|
4703
4496
|
font-weight: 500;
|
|
4704
4497
|
font-feature-settings: 'tnum';
|
|
4705
|
-
/* Material elevation */
|
|
4706
4498
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
4707
4499
|
}
|
|
4708
4500
|
|
|
4709
|
-
/*
|
|
4710
|
-
.uvf-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
height: 80px;
|
|
4716
|
-
background: rgba(255, 255, 255, 0.2);
|
|
4717
|
-
backdrop-filter: blur(10px);
|
|
4718
|
-
border-radius: 40px;
|
|
4719
|
-
display: flex;
|
|
4720
|
-
align-items: center;
|
|
4721
|
-
justify-content: center;
|
|
4722
|
-
pointer-events: none;
|
|
4723
|
-
opacity: 0;
|
|
4724
|
-
z-index: 100;
|
|
4725
|
-
transition: opacity 0.3s ease;
|
|
4726
|
-
/* Material elevation level 3 */
|
|
4727
|
-
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19),
|
|
4728
|
-
0 6px 6px rgba(0, 0, 0, 0.23);
|
|
4729
|
-
}
|
|
4730
|
-
|
|
4731
|
-
.uvf-doubletap-indicator.left {
|
|
4732
|
-
left: 15%;
|
|
4733
|
-
}
|
|
4734
|
-
|
|
4735
|
-
.uvf-doubletap-indicator.right {
|
|
4736
|
-
right: 15%;
|
|
4737
|
-
}
|
|
4738
|
-
|
|
4739
|
-
.uvf-doubletap-indicator.active {
|
|
4740
|
-
opacity: 1;
|
|
4741
|
-
animation: doubletap-pulse 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
4742
|
-
}
|
|
4743
|
-
|
|
4744
|
-
@keyframes doubletap-pulse {
|
|
4745
|
-
0% {
|
|
4746
|
-
transform: translateY(-50%) scale(0.8);
|
|
4747
|
-
opacity: 0;
|
|
4748
|
-
}
|
|
4749
|
-
50% {
|
|
4750
|
-
transform: translateY(-50%) scale(1.1);
|
|
4751
|
-
opacity: 1;
|
|
4752
|
-
}
|
|
4753
|
-
100% {
|
|
4754
|
-
transform: translateY(-50%) scale(1);
|
|
4755
|
-
opacity: 1;
|
|
4756
|
-
}
|
|
4757
|
-
}
|
|
4758
|
-
|
|
4759
|
-
.uvf-doubletap-indicator svg {
|
|
4760
|
-
width: 40px;
|
|
4761
|
-
height: 40px;
|
|
4762
|
-
fill: #fff;
|
|
4763
|
-
}
|
|
4764
|
-
|
|
4765
|
-
/* Long-press 2x speed indicator */
|
|
4766
|
-
.uvf-longpress-indicator {
|
|
4767
|
-
position: absolute;
|
|
4768
|
-
top: 50%;
|
|
4769
|
-
left: 50%;
|
|
4770
|
-
transform: translate(-50%, -50%);
|
|
4771
|
-
background: rgba(0, 0, 0, 0.8);
|
|
4772
|
-
backdrop-filter: blur(16px);
|
|
4773
|
-
padding: 16px 24px;
|
|
4774
|
-
border-radius: 24px;
|
|
4775
|
-
color: #fff;
|
|
4776
|
-
font-size: 18px;
|
|
4777
|
-
font-weight: 600;
|
|
4778
|
-
pointer-events: none;
|
|
4779
|
-
opacity: 0;
|
|
4780
|
-
z-index: 100;
|
|
4781
|
-
transition: opacity 0.2s ease;
|
|
4782
|
-
/* Material elevation level 4 */
|
|
4783
|
-
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25),
|
|
4784
|
-
0 10px 10px rgba(0, 0, 0, 0.22);
|
|
4785
|
-
}
|
|
4786
|
-
|
|
4787
|
-
.uvf-longpress-indicator.active {
|
|
4788
|
-
opacity: 1;
|
|
4789
|
-
}
|
|
4790
|
-
|
|
4791
|
-
/* Hide desktop elements in Material You mode */
|
|
4792
|
-
.uvf-player-wrapper.uvf-material-you-mobile .uvf-top-controls,
|
|
4793
|
-
.uvf-player-wrapper.uvf-material-you-mobile .uvf-title-bar,
|
|
4794
|
-
.uvf-player-wrapper.uvf-material-you-mobile .uvf-volume-control {
|
|
4501
|
+
/* Hide desktop elements */
|
|
4502
|
+
.uvf-top-controls,
|
|
4503
|
+
.uvf-title-bar,
|
|
4504
|
+
.uvf-volume-control,
|
|
4505
|
+
#uvf-skip-back,
|
|
4506
|
+
#uvf-skip-forward {
|
|
4795
4507
|
display: none !important;
|
|
4796
4508
|
}
|
|
4797
4509
|
|
|
4798
4510
|
/* Optimize settings button for Material You */
|
|
4799
|
-
|
|
4511
|
+
#uvf-settings-btn {
|
|
4800
4512
|
width: 48px !important;
|
|
4801
4513
|
height: 48px !important;
|
|
4802
4514
|
min-width: 48px !important;
|
|
@@ -4804,405 +4516,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
4804
4516
|
border-radius: 24px !important;
|
|
4805
4517
|
}
|
|
4806
4518
|
}
|
|
4807
|
-
|
|
4808
|
-
/* Mobile devices (portrait) - Enhanced UX with Safe Areas */
|
|
4809
|
-
@media screen and (max-width: 767px) and (orientation: portrait) {
|
|
4810
|
-
.uvf-responsive-container {
|
|
4811
|
-
padding: 0;
|
|
4812
|
-
width: 100vw !important;
|
|
4813
|
-
height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
4814
|
-
margin: 0;
|
|
4815
|
-
position: relative;
|
|
4816
|
-
overflow: hidden;
|
|
4817
|
-
}
|
|
4818
|
-
|
|
4819
|
-
@supports (height: 100dvh) {
|
|
4820
|
-
.uvf-responsive-container {
|
|
4821
|
-
height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
4822
|
-
}
|
|
4823
|
-
}
|
|
4824
|
-
|
|
4825
|
-
.uvf-responsive-container .uvf-player-wrapper {
|
|
4826
|
-
width: 100vw !important;
|
|
4827
|
-
height: 100% !important;
|
|
4828
|
-
min-height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
4829
|
-
}
|
|
4830
|
-
|
|
4831
|
-
@supports (height: 100dvh) {
|
|
4832
|
-
.uvf-responsive-container .uvf-player-wrapper {
|
|
4833
|
-
min-height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
4834
|
-
}
|
|
4835
|
-
}
|
|
4836
|
-
|
|
4837
|
-
.uvf-responsive-container .uvf-video-container {
|
|
4838
|
-
width: 100vw !important;
|
|
4839
|
-
height: 100% !important;
|
|
4840
|
-
aspect-ratio: unset !important;
|
|
4841
|
-
min-height: inherit;
|
|
4842
|
-
}
|
|
4843
|
-
|
|
4844
|
-
/* Enhanced mobile controls bar with safe area padding - iOS Safari specific fixes */
|
|
4845
|
-
.uvf-controls-bar {
|
|
4846
|
-
position: absolute !important;
|
|
4847
|
-
bottom: 0 !important;
|
|
4848
|
-
left: 0 !important;
|
|
4849
|
-
right: 0 !important;
|
|
4850
|
-
padding: 16px 12px;
|
|
4851
|
-
padding-bottom: calc(16px + var(--uvf-safe-area-bottom, 0px));
|
|
4852
|
-
padding-left: calc(12px + var(--uvf-safe-area-left, 0px));
|
|
4853
|
-
padding-right: calc(12px + var(--uvf-safe-area-right, 0px));
|
|
4854
|
-
background: linear-gradient(to top, var(--uvf-overlay-strong) 0%, var(--uvf-overlay-medium) 80%, var(--uvf-overlay-transparent) 100%);
|
|
4855
|
-
box-sizing: border-box;
|
|
4856
|
-
z-index: 1000 !important;
|
|
4857
|
-
/* iOS Safari specific fixes */
|
|
4858
|
-
transform: translateZ(0);
|
|
4859
|
-
-webkit-transform: translateZ(0);
|
|
4860
|
-
will-change: transform;
|
|
4861
|
-
/* Ensure proper stacking */
|
|
4862
|
-
isolation: isolate;
|
|
4863
|
-
}
|
|
4864
|
-
|
|
4865
|
-
.uvf-progress-section {
|
|
4866
|
-
margin-bottom: 16px;
|
|
4867
|
-
}
|
|
4868
|
-
|
|
4869
|
-
/* Mobile-first responsive controls layout */
|
|
4870
|
-
.uvf-controls-row {
|
|
4871
|
-
gap: 8px;
|
|
4872
|
-
flex-wrap: nowrap;
|
|
4873
|
-
align-items: center;
|
|
4874
|
-
justify-content: space-between;
|
|
4875
|
-
position: relative;
|
|
4876
|
-
width: 100%;
|
|
4877
|
-
}
|
|
4878
|
-
|
|
4879
|
-
/* Left side controls group */
|
|
4880
|
-
.uvf-left-controls {
|
|
4881
|
-
display: flex;
|
|
4882
|
-
align-items: center;
|
|
4883
|
-
gap: 8px;
|
|
4884
|
-
flex-shrink: 0;
|
|
4885
|
-
}
|
|
4886
|
-
|
|
4887
|
-
/* Center controls group */
|
|
4888
|
-
.uvf-center-controls {
|
|
4889
|
-
display: flex;
|
|
4890
|
-
align-items: center;
|
|
4891
|
-
gap: 8px;
|
|
4892
|
-
flex: 1;
|
|
4893
|
-
justify-content: center;
|
|
4894
|
-
}
|
|
4895
|
-
|
|
4896
|
-
/* Mobile control groups reordering */
|
|
4897
|
-
.uvf-controls-row .uvf-control-btn.play-pause,
|
|
4898
|
-
.uvf-controls-row #uvf-skip-back,
|
|
4899
|
-
.uvf-controls-row #uvf-skip-forward {
|
|
4900
|
-
order: 1;
|
|
4901
|
-
}
|
|
4902
|
-
|
|
4903
|
-
.uvf-controls-row .uvf-volume-control {
|
|
4904
|
-
order: 2;
|
|
4905
|
-
}
|
|
4906
|
-
|
|
4907
|
-
.uvf-controls-row .uvf-time-display:not(.uvf-above-seekbar) {
|
|
4908
|
-
order: 3;
|
|
4909
|
-
margin-left: auto;
|
|
4910
|
-
margin-right: 8px;
|
|
4911
|
-
}
|
|
4912
|
-
|
|
4913
|
-
.uvf-controls-row .uvf-right-controls {
|
|
4914
|
-
order: 4;
|
|
4915
|
-
margin-left: 0;
|
|
4916
|
-
}
|
|
4917
|
-
|
|
4918
|
-
/* Touch-friendly control sizing (minimum 44px touch target) */
|
|
4919
|
-
.uvf-control-btn {
|
|
4920
|
-
width: 44px;
|
|
4921
|
-
height: 44px;
|
|
4922
|
-
min-width: 44px;
|
|
4923
|
-
min-height: 44px;
|
|
4924
|
-
border-radius: 22px;
|
|
4925
|
-
background: rgba(255,255,255,0.15);
|
|
4926
|
-
backdrop-filter: blur(8px);
|
|
4927
|
-
}
|
|
4928
|
-
|
|
4929
|
-
.uvf-control-btn.play-pause {
|
|
4930
|
-
width: 52px;
|
|
4931
|
-
height: 52px;
|
|
4932
|
-
min-width: 52px;
|
|
4933
|
-
min-height: 52px;
|
|
4934
|
-
border-radius: 26px;
|
|
4935
|
-
background: linear-gradient(135deg, var(--uvf-accent-1), var(--uvf-accent-2));
|
|
4936
|
-
box-shadow: 0 4px 12px rgba(var(--uvf-accent-1), 0.3);
|
|
4937
|
-
}
|
|
4938
|
-
|
|
4939
|
-
.uvf-control-btn svg {
|
|
4940
|
-
width: 20px;
|
|
4941
|
-
height: 20px;
|
|
4942
|
-
}
|
|
4943
|
-
|
|
4944
|
-
.uvf-control-btn.play-pause svg {
|
|
4945
|
-
width: 24px;
|
|
4946
|
-
height: 24px;
|
|
4947
|
-
}
|
|
4948
|
-
|
|
4949
|
-
/* Skip buttons with clear visual hierarchy */
|
|
4950
|
-
#uvf-skip-back,
|
|
4951
|
-
#uvf-skip-forward {
|
|
4952
|
-
background: rgba(255,255,255,0.12);
|
|
4953
|
-
}
|
|
4954
|
-
|
|
4955
|
-
#uvf-skip-back svg,
|
|
4956
|
-
#uvf-skip-forward svg {
|
|
4957
|
-
width: 22px;
|
|
4958
|
-
height: 22px;
|
|
4959
|
-
}
|
|
4960
|
-
|
|
4961
|
-
/* Mobile time display - compact but readable */
|
|
4962
|
-
.uvf-time-display:not(.uvf-above-seekbar) {
|
|
4963
|
-
font-size: 12px;
|
|
4964
|
-
font-weight: 600;
|
|
4965
|
-
padding: 0 6px;
|
|
4966
|
-
text-align: center;
|
|
4967
|
-
background: rgba(0,0,0,0.3);
|
|
4968
|
-
border-radius: 12px;
|
|
4969
|
-
text-shadow: 0 1px 3px rgba(0,0,0,0.8);
|
|
4970
|
-
flex-shrink: 0;
|
|
4971
|
-
}
|
|
4972
|
-
|
|
4973
|
-
/* Above-seekbar time display for mobile */
|
|
4974
|
-
.uvf-time-display.uvf-above-seekbar {
|
|
4975
|
-
font-size: 12px !important;
|
|
4976
|
-
font-weight: 500 !important;
|
|
4977
|
-
padding: 3px 6px !important;
|
|
4978
|
-
background: rgba(0,0,0,0.4) !important;
|
|
4979
|
-
border-radius: 10px !important;
|
|
4980
|
-
text-shadow: 0 1px 2px rgba(0,0,0,0.8) !important;
|
|
4981
|
-
backdrop-filter: blur(4px) !important;
|
|
4982
|
-
border: 1px solid rgba(255,255,255,0.1) !important;
|
|
4983
|
-
}
|
|
4984
|
-
|
|
4985
|
-
/* Simplified volume control for mobile */
|
|
4986
|
-
.uvf-volume-control {
|
|
4987
|
-
order: 3;
|
|
4988
|
-
position: relative;
|
|
4989
|
-
}
|
|
4990
|
-
|
|
4991
|
-
/* Hide volume panel on mobile - use device controls */
|
|
4992
|
-
.uvf-volume-panel {
|
|
4993
|
-
display: none;
|
|
4994
|
-
}
|
|
4995
|
-
|
|
4996
|
-
/* Mobile volume button as simple mute toggle */
|
|
4997
|
-
.uvf-volume-control .uvf-control-btn {
|
|
4998
|
-
width: 44px;
|
|
4999
|
-
height: 44px;
|
|
5000
|
-
}
|
|
5001
|
-
|
|
5002
|
-
/* Compact right controls for mobile */
|
|
5003
|
-
.uvf-right-controls {
|
|
5004
|
-
gap: 6px;
|
|
5005
|
-
display: flex;
|
|
5006
|
-
align-items: center;
|
|
5007
|
-
flex-shrink: 0;
|
|
5008
|
-
position: relative;
|
|
5009
|
-
z-index: 10;
|
|
5010
|
-
}
|
|
5011
|
-
|
|
5012
|
-
/* Ensure settings container is visible */
|
|
5013
|
-
.uvf-right-controls > div[style*="position: relative"],
|
|
5014
|
-
.uvf-settings-container {
|
|
5015
|
-
display: flex !important;
|
|
5016
|
-
position: relative !important;
|
|
5017
|
-
align-items: center !important;
|
|
5018
|
-
justify-content: center !important;
|
|
5019
|
-
min-width: 44px !important;
|
|
5020
|
-
min-height: 44px !important;
|
|
5021
|
-
}
|
|
5022
|
-
|
|
5023
|
-
/* Remove quality badge completely - not essential */
|
|
5024
|
-
.uvf-quality-badge {
|
|
5025
|
-
display: none !important;
|
|
5026
|
-
}
|
|
5027
|
-
|
|
5028
|
-
/* Settings menu - hidden by default, accessible via menu */
|
|
5029
|
-
.uvf-settings-menu {
|
|
5030
|
-
min-width: 160px;
|
|
5031
|
-
bottom: 60px;
|
|
5032
|
-
right: 12px;
|
|
5033
|
-
font-size: 14px;
|
|
5034
|
-
max-height: 50vh;
|
|
5035
|
-
}
|
|
5036
|
-
|
|
5037
|
-
.uvf-settings-option {
|
|
5038
|
-
padding: 12px 16px;
|
|
5039
|
-
font-size: 14px;
|
|
5040
|
-
min-height: 44px;
|
|
5041
|
-
display: flex;
|
|
5042
|
-
align-items: center;
|
|
5043
|
-
}
|
|
5044
|
-
|
|
5045
|
-
.uvf-settings-option:hover {
|
|
5046
|
-
background: rgba(255,255,255,0.15);
|
|
5047
|
-
padding-left: 20px;
|
|
5048
|
-
}
|
|
5049
|
-
|
|
5050
|
-
/* Simplified settings - hide complex options */
|
|
5051
|
-
.uvf-settings-group:first-child .uvf-settings-option[data-speed="0.5"],
|
|
5052
|
-
.uvf-settings-group:first-child .uvf-settings-option[data-speed="0.75"],
|
|
5053
|
-
.uvf-settings-group:first-child .uvf-settings-option[data-speed="2"] {
|
|
5054
|
-
display: none;
|
|
5055
|
-
}
|
|
5056
|
-
}
|
|
5057
|
-
|
|
5058
|
-
/* Enhanced top controls for mobile with safe area support */
|
|
5059
|
-
.uvf-top-controls {
|
|
5060
|
-
position: absolute;
|
|
5061
|
-
top: calc(12px + var(--uvf-safe-area-top));
|
|
5062
|
-
right: calc(12px + var(--uvf-safe-area-right));
|
|
5063
|
-
display: flex;
|
|
5064
|
-
align-items: center;
|
|
5065
|
-
gap: 8px;
|
|
5066
|
-
z-index: 10;
|
|
5067
|
-
}
|
|
5068
|
-
|
|
5069
|
-
/* Touch-friendly top buttons */
|
|
5070
|
-
.uvf-top-btn {
|
|
5071
|
-
width: 44px;
|
|
5072
|
-
height: 44px;
|
|
5073
|
-
min-width: 44px;
|
|
5074
|
-
min-height: 44px;
|
|
5075
|
-
background: rgba(0,0,0,0.7);
|
|
5076
|
-
backdrop-filter: blur(10px);
|
|
5077
|
-
border: 1px solid rgba(255,255,255,0.2);
|
|
5078
|
-
}
|
|
5079
|
-
|
|
5080
|
-
.uvf-top-btn svg {
|
|
5081
|
-
width: 20px;
|
|
5082
|
-
height: 20px;
|
|
5083
|
-
}
|
|
5084
|
-
|
|
5085
|
-
/* Share button - keep visible on all devices */
|
|
5086
|
-
#uvf-share-btn {
|
|
5087
|
-
display: flex;
|
|
5088
|
-
}
|
|
5089
|
-
|
|
5090
|
-
/* Enhanced title bar for mobile with safe area support */
|
|
5091
|
-
.uvf-title-bar {
|
|
5092
|
-
padding: 12px;
|
|
5093
|
-
padding-top: calc(12px + var(--uvf-safe-area-top));
|
|
5094
|
-
padding-left: calc(12px + var(--uvf-safe-area-left));
|
|
5095
|
-
padding-right: calc(12px + var(--uvf-safe-area-right));
|
|
5096
|
-
}
|
|
5097
|
-
|
|
5098
|
-
.uvf-video-title {
|
|
5099
|
-
font-size: 16px;
|
|
5100
|
-
font-weight: 700;
|
|
5101
|
-
line-height: 1.2;
|
|
5102
|
-
}
|
|
5103
|
-
|
|
5104
|
-
.uvf-video-subtitle {
|
|
5105
|
-
font-size: 12px;
|
|
5106
|
-
margin-top: 4px;
|
|
5107
|
-
opacity: 0.8;
|
|
5108
|
-
}
|
|
5109
|
-
|
|
5110
|
-
.uvf-video-thumb {
|
|
5111
|
-
width: 48px;
|
|
5112
|
-
height: 48px;
|
|
5113
|
-
border-radius: 6px;
|
|
5114
|
-
}
|
|
5115
|
-
|
|
5116
|
-
/* Touch-optimized center play button - uses same themed style as desktop */
|
|
5117
|
-
.uvf-center-play-btn {
|
|
5118
|
-
width: clamp(72px, 18vw, 96px);
|
|
5119
|
-
height: clamp(72px, 18vw, 96px);
|
|
5120
|
-
background: linear-gradient(135deg, var(--uvf-accent-1), var(--uvf-accent-2));
|
|
5121
|
-
border: 0;
|
|
5122
|
-
box-shadow: 0 10px 30px var(--uvf-accent-1-20);
|
|
5123
|
-
}
|
|
5124
|
-
|
|
5125
|
-
.uvf-center-play-btn:hover {
|
|
5126
|
-
transform: scale(1.06);
|
|
5127
|
-
filter: saturate(1.08) brightness(1.05);
|
|
5128
|
-
box-shadow: 0 14px 36px var(--uvf-accent-1-20);
|
|
5129
|
-
}
|
|
5130
|
-
|
|
5131
|
-
.uvf-center-play-btn svg {
|
|
5132
|
-
width: clamp(28px, 5.2vw, 38px);
|
|
5133
|
-
height: clamp(28px, 5.2vw, 38px);
|
|
5134
|
-
margin-left: 4px;
|
|
5135
|
-
}
|
|
5136
|
-
|
|
5137
|
-
/* Enhanced progress bar for touch */
|
|
5138
|
-
.uvf-progress-bar {
|
|
5139
|
-
height: 3px;
|
|
5140
|
-
margin-bottom: 12px;
|
|
5141
|
-
border-radius: 4px;
|
|
5142
|
-
background: rgba(255,255,255,0.15);
|
|
5143
|
-
position: relative;
|
|
5144
|
-
}
|
|
5145
|
-
|
|
5146
|
-
/* Larger touch target for progress bar */
|
|
5147
|
-
.uvf-progress-bar-wrapper::before {
|
|
5148
|
-
content: '';
|
|
5149
|
-
position: absolute;
|
|
5150
|
-
top: -10px;
|
|
5151
|
-
left: 0;
|
|
5152
|
-
right: 0;
|
|
5153
|
-
bottom: -10px;
|
|
5154
|
-
z-index: 1;
|
|
5155
|
-
}
|
|
5156
|
-
|
|
5157
|
-
.uvf-progress-handle {
|
|
5158
|
-
width: 18px;
|
|
5159
|
-
height: 18px;
|
|
5160
|
-
background: #fff;
|
|
5161
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
5162
|
-
}
|
|
5163
|
-
|
|
5164
|
-
.uvf-progress-bar-wrapper:active .uvf-progress-handle {
|
|
5165
|
-
transform: translate(-50%, -50%) scale(1.2);
|
|
5166
|
-
}
|
|
5167
|
-
|
|
5168
|
-
/* Mobile accessibility improvements */
|
|
5169
|
-
.uvf-control-btn,
|
|
5170
|
-
.uvf-top-btn {
|
|
5171
|
-
position: relative;
|
|
5172
|
-
overflow: visible;
|
|
5173
|
-
}
|
|
5174
|
-
|
|
5175
|
-
/* Enhanced focus states for mobile */
|
|
5176
|
-
.uvf-control-btn:focus,
|
|
5177
|
-
.uvf-top-btn:focus {
|
|
5178
|
-
outline: 2px solid var(--uvf-accent-1);
|
|
5179
|
-
outline-offset: 2px;
|
|
5180
|
-
}
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
/* Show PiP on all devices - modern mobile browsers support it well */
|
|
5184
|
-
#uvf-pip-btn {
|
|
5185
|
-
display: block;
|
|
5186
|
-
background: rgba(255,255,255,0.12);
|
|
5187
|
-
border: 1px solid rgba(255,255,255,0.1);
|
|
5188
|
-
}
|
|
5189
|
-
|
|
5190
|
-
/* Essential controls in right section - Settings, PiP, and Fullscreen only */
|
|
5191
|
-
.uvf-right-controls > *:not(#uvf-settings-btn):not(#uvf-fullscreen-btn):not(#uvf-pip-btn) {
|
|
5192
|
-
display: none;
|
|
5193
|
-
}
|
|
5194
|
-
|
|
5195
|
-
/* Ensure settings button is always visible and properly sized on mobile */
|
|
5196
|
-
#uvf-settings-btn {
|
|
5197
|
-
display: flex !important;
|
|
5198
|
-
width: 44px !important;
|
|
5199
|
-
height: 44px !important;
|
|
5200
|
-
min-width: 44px !important;
|
|
5201
|
-
min-height: 44px !important;
|
|
5202
|
-
backdrop-filter: blur(8px) !important;
|
|
5203
|
-
border-radius: 22px !important;
|
|
5204
|
-
align-items: center !important;
|
|
5205
|
-
justify-content: center !important;
|
|
5206
4519
|
}
|
|
5207
4520
|
|
|
5208
4521
|
#uvf-settings-btn svg {
|
|
@@ -6130,26 +5443,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
6130
5443
|
shortcutIndicator.className = 'uvf-shortcut-indicator';
|
|
6131
5444
|
shortcutIndicator.id = 'uvf-shortcut-indicator';
|
|
6132
5445
|
container.appendChild(shortcutIndicator);
|
|
6133
|
-
|
|
6134
|
-
// Add Material You double-tap indicators
|
|
6135
|
-
const doubleTapLeft = document.createElement('div');
|
|
6136
|
-
doubleTapLeft.className = 'uvf-doubletap-indicator left';
|
|
6137
|
-
doubleTapLeft.id = 'uvf-doubletap-left';
|
|
6138
|
-
doubleTapLeft.innerHTML = '<svg viewBox="0 0 24 24"><path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/></svg><div style="margin-top:4px;font-size:14px;font-weight:600;">-10s</div>';
|
|
6139
|
-
container.appendChild(doubleTapLeft);
|
|
6140
|
-
|
|
6141
|
-
const doubleTapRight = document.createElement('div');
|
|
6142
|
-
doubleTapRight.className = 'uvf-doubletap-indicator right';
|
|
6143
|
-
doubleTapRight.id = 'uvf-doubletap-right';
|
|
6144
|
-
doubleTapRight.innerHTML = '<svg viewBox="0 0 24 24"><path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/></svg><div style="margin-top:4px;font-size:14px;font-weight:600;">+10s</div>';
|
|
6145
|
-
container.appendChild(doubleTapRight);
|
|
6146
|
-
|
|
6147
|
-
// Add Material You long-press indicator
|
|
6148
|
-
const longPressIndicator = document.createElement('div');
|
|
6149
|
-
longPressIndicator.className = 'uvf-longpress-indicator';
|
|
6150
|
-
longPressIndicator.id = 'uvf-longpress-indicator';
|
|
6151
|
-
longPressIndicator.textContent = '2x';
|
|
6152
|
-
container.appendChild(longPressIndicator);
|
|
6153
5446
|
|
|
6154
5447
|
// Create controls bar
|
|
6155
5448
|
const controlsBar = document.createElement('div');
|
|
@@ -6443,9 +5736,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
6443
5736
|
// Play/Pause
|
|
6444
5737
|
centerPlay?.addEventListener('click', () => this.togglePlayPause());
|
|
6445
5738
|
playPauseBtn?.addEventListener('click', () => this.togglePlayPause());
|
|
6446
|
-
|
|
6447
|
-
// Material You touch gestures for mobile
|
|
6448
|
-
this.setupMaterialYouGestures(wrapper);
|
|
5739
|
+
this.video.addEventListener('click', () => this.togglePlayPause());
|
|
6449
5740
|
|
|
6450
5741
|
// Update play/pause icons
|
|
6451
5742
|
this.video.addEventListener('play', () => {
|
|
@@ -6820,12 +6111,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
6820
6111
|
|
|
6821
6112
|
// Force visibility if menu is active, hide if not active
|
|
6822
6113
|
if (settingsMenu) {
|
|
6823
|
-
|
|
6824
|
-
if (activating) {
|
|
6825
|
-
this.isSettingsOpen = true;
|
|
6826
|
-
// Keep controls visible and interactive while menu is open
|
|
6827
|
-
this.showControls();
|
|
6828
|
-
if (this.hideControlsTimeout) clearTimeout(this.hideControlsTimeout);
|
|
6114
|
+
if (settingsMenu.classList.contains('active')) {
|
|
6829
6115
|
settingsMenu.style.display = 'block';
|
|
6830
6116
|
settingsMenu.style.visibility = 'visible';
|
|
6831
6117
|
settingsMenu.style.opacity = '1';
|
|
@@ -6841,13 +6127,10 @@ export class WebPlayer extends BasePlayer {
|
|
|
6841
6127
|
settingsMenu.style.padding = '10px 0';
|
|
6842
6128
|
this.debugLog('Applied fallback styles to show menu');
|
|
6843
6129
|
} else {
|
|
6844
|
-
this.isSettingsOpen = false;
|
|
6845
6130
|
settingsMenu.style.display = 'none';
|
|
6846
6131
|
settingsMenu.style.visibility = 'hidden';
|
|
6847
6132
|
settingsMenu.style.opacity = '0';
|
|
6848
6133
|
this.debugLog('Applied fallback styles to hide menu');
|
|
6849
|
-
// After closing, allow auto-hide again if playing
|
|
6850
|
-
this.scheduleHideControls();
|
|
6851
6134
|
}
|
|
6852
6135
|
}
|
|
6853
6136
|
|
|
@@ -6887,19 +6170,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
6887
6170
|
}
|
|
6888
6171
|
});
|
|
6889
6172
|
|
|
6890
|
-
// Keep controls active while interacting with settings menu
|
|
6891
|
-
if (settingsMenu) {
|
|
6892
|
-
const keepAlive = () => {
|
|
6893
|
-
if (!this.isSettingsOpen) return;
|
|
6894
|
-
this.showControls();
|
|
6895
|
-
if (this.hideControlsTimeout) clearTimeout(this.hideControlsTimeout);
|
|
6896
|
-
};
|
|
6897
|
-
settingsMenu.addEventListener('mouseenter', keepAlive);
|
|
6898
|
-
settingsMenu.addEventListener('mousemove', keepAlive);
|
|
6899
|
-
settingsMenu.addEventListener('touchstart', keepAlive, { passive: true } as any);
|
|
6900
|
-
settingsMenu.addEventListener('touchmove', keepAlive, { passive: true } as any);
|
|
6901
|
-
}
|
|
6902
|
-
|
|
6903
6173
|
// Add Escape key handler for settings menu
|
|
6904
6174
|
document.addEventListener('keydown', (e) => {
|
|
6905
6175
|
if (e.key === 'Escape' && settingsMenu?.classList.contains('active')) {
|
|
@@ -7411,267 +6681,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
7411
6681
|
}
|
|
7412
6682
|
}
|
|
7413
6683
|
|
|
7414
|
-
/**
|
|
7415
|
-
* Setup Material You touch gestures for mobile
|
|
7416
|
-
*/
|
|
7417
|
-
private setupMaterialYouGestures(wrapper: HTMLElement | null): void {
|
|
7418
|
-
if (!wrapper || !this.video) return;
|
|
7419
|
-
|
|
7420
|
-
const doubleTapLeft = document.getElementById('uvf-doubletap-left');
|
|
7421
|
-
const doubleTapRight = document.getElementById('uvf-doubletap-right');
|
|
7422
|
-
const longPressIndicator = document.getElementById('uvf-longpress-indicator');
|
|
7423
|
-
|
|
7424
|
-
// Single tap, double tap, and long press handling
|
|
7425
|
-
const videoElement = this.video;
|
|
7426
|
-
const videoContainer = wrapper.querySelector('.uvf-video-container');
|
|
7427
|
-
|
|
7428
|
-
if (!videoContainer) return;
|
|
7429
|
-
|
|
7430
|
-
// Touch start for long-press detection
|
|
7431
|
-
videoContainer.addEventListener('touchstart', (e: Event) => {
|
|
7432
|
-
const touchEvent = e as TouchEvent;
|
|
7433
|
-
const touch = touchEvent.touches[0];
|
|
7434
|
-
|
|
7435
|
-
if (!touch || !videoElement) return;
|
|
7436
|
-
|
|
7437
|
-
this.longPressStartTime = Date.now();
|
|
7438
|
-
|
|
7439
|
-
// Start long-press timer
|
|
7440
|
-
this.longPressTimer = setTimeout(() => {
|
|
7441
|
-
this.longPressActive = true;
|
|
7442
|
-
this.originalPlaybackRate = videoElement.playbackRate;
|
|
7443
|
-
videoElement.playbackRate = 2.0;
|
|
7444
|
-
|
|
7445
|
-
// Show indicator
|
|
7446
|
-
if (longPressIndicator) {
|
|
7447
|
-
longPressIndicator.classList.add('active');
|
|
7448
|
-
}
|
|
7449
|
-
}, 500); // 500ms for long press
|
|
7450
|
-
}, { passive: true });
|
|
7451
|
-
|
|
7452
|
-
// Touch end to handle tap/double-tap and stop long-press
|
|
7453
|
-
videoContainer.addEventListener('touchend', (e: Event) => {
|
|
7454
|
-
const touchEvent = e as TouchEvent;
|
|
7455
|
-
const touch = touchEvent.changedTouches[0];
|
|
7456
|
-
|
|
7457
|
-
if (!touch || !videoElement) return;
|
|
7458
|
-
|
|
7459
|
-
// Clear long-press timer
|
|
7460
|
-
if (this.longPressTimer) {
|
|
7461
|
-
clearTimeout(this.longPressTimer);
|
|
7462
|
-
this.longPressTimer = null;
|
|
7463
|
-
}
|
|
7464
|
-
|
|
7465
|
-
// If long-press was active, restore playback rate
|
|
7466
|
-
if (this.longPressActive) {
|
|
7467
|
-
videoElement.playbackRate = this.originalPlaybackRate;
|
|
7468
|
-
this.longPressActive = false;
|
|
7469
|
-
|
|
7470
|
-
if (longPressIndicator) {
|
|
7471
|
-
longPressIndicator.classList.remove('active');
|
|
7472
|
-
}
|
|
7473
|
-
return; // Don't process as tap
|
|
7474
|
-
}
|
|
7475
|
-
|
|
7476
|
-
// Tap detection
|
|
7477
|
-
const now = Date.now();
|
|
7478
|
-
const timeSinceLastTap = now - this.lastTapTime;
|
|
7479
|
-
|
|
7480
|
-
if (timeSinceLastTap < 300) { // Double tap detected (within 300ms)
|
|
7481
|
-
// Clear single tap timeout
|
|
7482
|
-
if (this.tapTimeout) {
|
|
7483
|
-
clearTimeout(this.tapTimeout);
|
|
7484
|
-
this.tapTimeout = null;
|
|
7485
|
-
}
|
|
7486
|
-
|
|
7487
|
-
// Determine if left or right side
|
|
7488
|
-
const rect = videoContainer.getBoundingClientRect();
|
|
7489
|
-
const x = touch.clientX - rect.left;
|
|
7490
|
-
const isLeftSide = x < rect.width / 2;
|
|
7491
|
-
|
|
7492
|
-
if (isLeftSide) {
|
|
7493
|
-
// Skip backward 10s
|
|
7494
|
-
this.seek(videoElement.currentTime - 10);
|
|
7495
|
-
|
|
7496
|
-
// Show indicator
|
|
7497
|
-
if (doubleTapLeft) {
|
|
7498
|
-
doubleTapLeft.classList.add('active');
|
|
7499
|
-
setTimeout(() => {
|
|
7500
|
-
doubleTapLeft.classList.remove('active');
|
|
7501
|
-
}, 400);
|
|
7502
|
-
}
|
|
7503
|
-
} else {
|
|
7504
|
-
// Skip forward 10s
|
|
7505
|
-
this.seek(videoElement.currentTime + 10);
|
|
7506
|
-
|
|
7507
|
-
// Show indicator
|
|
7508
|
-
if (doubleTapRight) {
|
|
7509
|
-
doubleTapRight.classList.add('active');
|
|
7510
|
-
setTimeout(() => {
|
|
7511
|
-
doubleTapRight.classList.remove('active');
|
|
7512
|
-
}, 400);
|
|
7513
|
-
}
|
|
7514
|
-
}
|
|
7515
|
-
|
|
7516
|
-
this.tapCount = 0;
|
|
7517
|
-
this.lastTapTime = 0;
|
|
7518
|
-
} else {
|
|
7519
|
-
// First tap
|
|
7520
|
-
this.tapCount = 1;
|
|
7521
|
-
this.lastTapTime = now;
|
|
7522
|
-
|
|
7523
|
-
// Wait for potential second tap
|
|
7524
|
-
this.tapTimeout = setTimeout(() => {
|
|
7525
|
-
if (this.tapCount === 1) {
|
|
7526
|
-
// Single tap - toggle overlay controls visibility
|
|
7527
|
-
this.toggleControls();
|
|
7528
|
-
}
|
|
7529
|
-
this.tapCount = 0;
|
|
7530
|
-
}, 300); // Wait 300ms for double tap
|
|
7531
|
-
}
|
|
7532
|
-
}, { passive: true });
|
|
7533
|
-
|
|
7534
|
-
// Touch move/cancel to reset long-press
|
|
7535
|
-
videoContainer.addEventListener('touchmove', () => {
|
|
7536
|
-
if (this.longPressTimer) {
|
|
7537
|
-
clearTimeout(this.longPressTimer);
|
|
7538
|
-
this.longPressTimer = null;
|
|
7539
|
-
}
|
|
7540
|
-
}, { passive: true });
|
|
7541
|
-
|
|
7542
|
-
videoContainer.addEventListener('touchcancel', () => {
|
|
7543
|
-
if (this.longPressTimer) {
|
|
7544
|
-
clearTimeout(this.longPressTimer);
|
|
7545
|
-
this.longPressTimer = null;
|
|
7546
|
-
}
|
|
7547
|
-
|
|
7548
|
-
if (this.longPressActive && videoElement) {
|
|
7549
|
-
videoElement.playbackRate = this.originalPlaybackRate;
|
|
7550
|
-
this.longPressActive = false;
|
|
7551
|
-
|
|
7552
|
-
if (longPressIndicator) {
|
|
7553
|
-
longPressIndicator.classList.remove('active');
|
|
7554
|
-
}
|
|
7555
|
-
}
|
|
7556
|
-
}, { passive: true });
|
|
7557
|
-
}
|
|
7558
|
-
|
|
7559
|
-
/**
|
|
7560
|
-
* Toggle controls visibility
|
|
7561
|
-
*/
|
|
7562
|
-
private toggleControls(): void {
|
|
7563
|
-
const wrapper = this.container?.querySelector('.uvf-player-wrapper');
|
|
7564
|
-
if (wrapper) {
|
|
7565
|
-
if (wrapper.classList.contains('controls-visible')) {
|
|
7566
|
-
this.hideControls();
|
|
7567
|
-
} else {
|
|
7568
|
-
this.showControls();
|
|
7569
|
-
// Auto-hide after delay if playing
|
|
7570
|
-
if (this.state.isPlaying) {
|
|
7571
|
-
this.scheduleHideControls();
|
|
7572
|
-
}
|
|
7573
|
-
}
|
|
7574
|
-
}
|
|
7575
|
-
}
|
|
7576
|
-
|
|
7577
|
-
/**
|
|
7578
|
-
* Apply dynamic color theming based on video content (Material You)
|
|
7579
|
-
* This can be enhanced to extract colors from video frames
|
|
7580
|
-
*/
|
|
7581
|
-
private applyDynamicTheming(primaryColor?: string): void {
|
|
7582
|
-
if (!this.playerWrapper) return;
|
|
7583
|
-
|
|
7584
|
-
const color = primaryColor || this.dominantColor;
|
|
7585
|
-
const rgb = this.hexToRgb(color);
|
|
7586
|
-
|
|
7587
|
-
if (rgb) {
|
|
7588
|
-
// Update CSS variables for Material You theming
|
|
7589
|
-
this.playerWrapper.style.setProperty('--uvf-accent-1', color);
|
|
7590
|
-
this.playerWrapper.style.setProperty('--uvf-accent-2', this.lightenColor(color, 10));
|
|
7591
|
-
this.playerWrapper.style.setProperty('--uvf-surface-tint', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.08)`);
|
|
7592
|
-
|
|
7593
|
-
this.debugLog(`Applied dynamic theming with color: ${color}`);
|
|
7594
|
-
}
|
|
7595
|
-
}
|
|
7596
|
-
|
|
7597
|
-
/**
|
|
7598
|
-
* Convert hex color to RGB
|
|
7599
|
-
*/
|
|
7600
|
-
private hexToRgb(hex: string): { r: number, g: number, b: number } | null {
|
|
7601
|
-
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
7602
|
-
return result ? {
|
|
7603
|
-
r: parseInt(result[1], 16),
|
|
7604
|
-
g: parseInt(result[2], 16),
|
|
7605
|
-
b: parseInt(result[3], 16)
|
|
7606
|
-
} : null;
|
|
7607
|
-
}
|
|
7608
|
-
|
|
7609
|
-
/**
|
|
7610
|
-
* Lighten a hex color by a percentage
|
|
7611
|
-
*/
|
|
7612
|
-
private lightenColor(hex: string, percent: number): string {
|
|
7613
|
-
const rgb = this.hexToRgb(hex);
|
|
7614
|
-
if (!rgb) return hex;
|
|
7615
|
-
|
|
7616
|
-
const amount = Math.floor(255 * (percent / 100));
|
|
7617
|
-
const r = Math.min(255, rgb.r + amount);
|
|
7618
|
-
const g = Math.min(255, rgb.g + amount);
|
|
7619
|
-
const b = Math.min(255, rgb.b + amount);
|
|
7620
|
-
|
|
7621
|
-
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
|
|
7622
|
-
}
|
|
7623
|
-
|
|
7624
|
-
/**
|
|
7625
|
-
* Render chapter markers on progress bar for Material You layout
|
|
7626
|
-
*/
|
|
7627
|
-
private renderChapterMarkersOnProgressBar(): void {
|
|
7628
|
-
if (!this.video || !this.chapterConfig.enabled || !this.chapterConfig.showChapterMarkers) {
|
|
7629
|
-
return;
|
|
7630
|
-
}
|
|
7631
|
-
|
|
7632
|
-
const progressBar = document.querySelector('.uvf-progress-bar');
|
|
7633
|
-
if (!progressBar) return;
|
|
7634
|
-
|
|
7635
|
-
// Remove existing markers
|
|
7636
|
-
const existingMarkers = progressBar.querySelectorAll('.uvf-chapter-marker');
|
|
7637
|
-
existingMarkers.forEach(marker => marker.remove());
|
|
7638
|
-
|
|
7639
|
-
// Get chapter data
|
|
7640
|
-
const chapters = this.chapterConfig.data;
|
|
7641
|
-
if (!chapters || !Array.isArray(chapters) || chapters.length === 0) {
|
|
7642
|
-
return;
|
|
7643
|
-
}
|
|
7644
|
-
|
|
7645
|
-
const duration = this.video.duration;
|
|
7646
|
-
if (!duration || duration === 0) return;
|
|
7647
|
-
|
|
7648
|
-
// Create marker for each chapter
|
|
7649
|
-
chapters.forEach((chapter: any) => {
|
|
7650
|
-
if (!chapter.startTime && chapter.startTime !== 0) return;
|
|
7651
|
-
|
|
7652
|
-
const marker = document.createElement('div');
|
|
7653
|
-
marker.className = 'uvf-chapter-marker';
|
|
7654
|
-
|
|
7655
|
-
// Add chapter type class if available
|
|
7656
|
-
if (chapter.type) {
|
|
7657
|
-
marker.classList.add(chapter.type.toLowerCase());
|
|
7658
|
-
}
|
|
7659
|
-
|
|
7660
|
-
// Position based on startTime
|
|
7661
|
-
const percent = (chapter.startTime / duration) * 100;
|
|
7662
|
-
marker.style.left = `${percent}%`;
|
|
7663
|
-
|
|
7664
|
-
// Add title attribute for tooltip
|
|
7665
|
-
if (chapter.title) {
|
|
7666
|
-
marker.setAttribute('title', chapter.title);
|
|
7667
|
-
}
|
|
7668
|
-
|
|
7669
|
-
progressBar.appendChild(marker);
|
|
7670
|
-
});
|
|
7671
|
-
|
|
7672
|
-
this.debugLog(`Rendered ${chapters.length} chapter markers on progress bar`);
|
|
7673
|
-
}
|
|
7674
|
-
|
|
7675
6684
|
/**
|
|
7676
6685
|
* Detect if user is on a mobile device
|
|
7677
6686
|
*/
|
|
@@ -7812,8 +6821,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
7812
6821
|
|
|
7813
6822
|
private hideControls(): void {
|
|
7814
6823
|
if (!this.state.isPlaying) return;
|
|
7815
|
-
// Never hide controls while settings menu is open
|
|
7816
|
-
if (this.isSettingsOpen) return;
|
|
7817
6824
|
|
|
7818
6825
|
const wrapper = this.container?.querySelector('.uvf-player-wrapper');
|
|
7819
6826
|
if (wrapper) {
|
|
@@ -7824,14 +6831,12 @@ export class WebPlayer extends BasePlayer {
|
|
|
7824
6831
|
|
|
7825
6832
|
private scheduleHideControls(): void {
|
|
7826
6833
|
if (!this.state.isPlaying) return;
|
|
7827
|
-
// Do not schedule auto-hide while settings are open
|
|
7828
|
-
if (this.isSettingsOpen) return;
|
|
7829
6834
|
|
|
7830
6835
|
if (this.hideControlsTimeout) clearTimeout(this.hideControlsTimeout);
|
|
7831
6836
|
// Use longer timeout in fullscreen for better UX
|
|
7832
6837
|
const timeout = this.isFullscreen() ? 4000 : 3000;
|
|
7833
6838
|
this.hideControlsTimeout = setTimeout(() => {
|
|
7834
|
-
if (this.state.isPlaying && !this.controlsContainer?.matches(':hover')
|
|
6839
|
+
if (this.state.isPlaying && !this.controlsContainer?.matches(':hover')) {
|
|
7835
6840
|
this.hideControls();
|
|
7836
6841
|
}
|
|
7837
6842
|
}, timeout);
|
|
@@ -9040,7 +8045,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
9040
8045
|
if (!settingsMenu) return;
|
|
9041
8046
|
|
|
9042
8047
|
settingsMenu.classList.remove('active');
|
|
9043
|
-
this.isSettingsOpen = false;
|
|
9044
8048
|
|
|
9045
8049
|
// Apply fallback styles to ensure menu is hidden
|
|
9046
8050
|
settingsMenu.style.display = 'none';
|
|
@@ -9053,8 +8057,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
9053
8057
|
});
|
|
9054
8058
|
|
|
9055
8059
|
this.debugLog('Settings menu hidden via hideSettingsMenu()');
|
|
9056
|
-
// Resume auto-hide if appropriate
|
|
9057
|
-
this.scheduleHideControls();
|
|
9058
8060
|
}
|
|
9059
8061
|
|
|
9060
8062
|
/**
|