unified-video-framework 1.4.136 → 1.4.138

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.
@@ -2402,6 +2402,141 @@ export class WebPlayer extends BasePlayer {
2402
2402
  transform: translateY(0);
2403
2403
  }
2404
2404
 
2405
+ /* Improved Accordion Styles */
2406
+ .uvf-settings-accordion {
2407
+ padding: 8px 0;
2408
+ }
2409
+
2410
+ .uvf-accordion-item {
2411
+ margin-bottom: 2px;
2412
+ border-radius: 8px;
2413
+ overflow: hidden;
2414
+ background: rgba(255,255,255,0.03);
2415
+ }
2416
+
2417
+ .uvf-accordion-item:last-child {
2418
+ margin-bottom: 0;
2419
+ }
2420
+
2421
+ .uvf-accordion-header {
2422
+ display: flex;
2423
+ align-items: center;
2424
+ justify-content: space-between;
2425
+ padding: 12px 16px;
2426
+ cursor: pointer;
2427
+ transition: all 0.2s ease;
2428
+ background: rgba(255,255,255,0.05);
2429
+ border-bottom: 1px solid rgba(255,255,255,0.08);
2430
+ }
2431
+
2432
+ .uvf-accordion-header:hover {
2433
+ background: rgba(255,255,255,0.1);
2434
+ }
2435
+
2436
+ .uvf-accordion-item.expanded .uvf-accordion-header {
2437
+ background: rgba(255,255,255,0.08);
2438
+ border-bottom-color: rgba(255,255,255,0.12);
2439
+ }
2440
+
2441
+ .uvf-accordion-title {
2442
+ display: flex;
2443
+ align-items: center;
2444
+ gap: 8px;
2445
+ font-size: 13px;
2446
+ font-weight: 500;
2447
+ color: #fff;
2448
+ flex: 1;
2449
+ }
2450
+
2451
+ .uvf-accordion-icon {
2452
+ display: flex;
2453
+ align-items: center;
2454
+ justify-content: center;
2455
+ opacity: 0.9;
2456
+ width: 16px;
2457
+ height: 16px;
2458
+ }
2459
+
2460
+ .uvf-accordion-icon svg {
2461
+ width: 14px;
2462
+ height: 14px;
2463
+ fill: currentColor;
2464
+ }
2465
+
2466
+ .uvf-accordion-current {
2467
+ font-size: 11px;
2468
+ color: var(--uvf-accent-1);
2469
+ background: rgba(255,255,255,0.08);
2470
+ padding: 2px 8px;
2471
+ border-radius: 8px;
2472
+ font-weight: 600;
2473
+ margin-right: 8px;
2474
+ }
2475
+
2476
+ .uvf-accordion-arrow {
2477
+ font-size: 10px;
2478
+ color: rgba(255,255,255,0.7);
2479
+ transition: transform 0.25s ease;
2480
+ width: 16px;
2481
+ text-align: center;
2482
+ }
2483
+
2484
+ .uvf-accordion-item.expanded .uvf-accordion-arrow {
2485
+ transform: rotate(180deg);
2486
+ }
2487
+
2488
+ .uvf-accordion-content {
2489
+ max-height: 0;
2490
+ overflow: hidden;
2491
+ transition: max-height 0.25s cubic-bezier(0.4, 0, 0.2, 1);
2492
+ background: rgba(0,0,0,0.2);
2493
+ }
2494
+
2495
+ .uvf-accordion-item.expanded .uvf-accordion-content {
2496
+ max-height: 250px;
2497
+ }
2498
+
2499
+ /* Settings options within accordion */
2500
+ .uvf-accordion-content .uvf-settings-option {
2501
+ color: #fff;
2502
+ font-size: 13px;
2503
+ padding: 10px 16px;
2504
+ cursor: pointer;
2505
+ transition: all 0.2s ease;
2506
+ border-bottom: 1px solid rgba(255,255,255,0.05);
2507
+ display: flex;
2508
+ align-items: center;
2509
+ justify-content: space-between;
2510
+ }
2511
+
2512
+ .uvf-accordion-content .uvf-settings-option:last-child {
2513
+ border-bottom: none;
2514
+ }
2515
+
2516
+ .uvf-accordion-content .uvf-settings-option:hover {
2517
+ background: rgba(255,255,255,0.06);
2518
+ padding-left: 20px;
2519
+ }
2520
+
2521
+ .uvf-accordion-content .uvf-settings-option.active {
2522
+ color: var(--uvf-accent-1);
2523
+ background: rgba(255,255,255,0.08);
2524
+ font-weight: 600;
2525
+ }
2526
+
2527
+ .uvf-accordion-content .uvf-settings-option.active::after {
2528
+ content: '✓';
2529
+ font-size: 12px;
2530
+ opacity: 0.8;
2531
+ }
2532
+
2533
+ .uvf-settings-empty {
2534
+ padding: 20px;
2535
+ text-align: center;
2536
+ color: rgba(255,255,255,0.6);
2537
+ font-size: 14px;
2538
+ }
2539
+
2405
2540
  .uvf-settings-group {
2406
2541
  padding: 10px 0;
2407
2542
  border-bottom: 1px solid rgba(255,255,255,0.1);
@@ -5091,11 +5226,26 @@ export class WebPlayer extends BasePlayer {
5091
5226
  stopCastBtn?.addEventListener('click', () => this.stopCasting());
5092
5227
  shareBtn?.addEventListener('click', () => this.shareVideo());
5093
5228
 
5094
- // Hide settings menu when clicking outside
5229
+ // Hide settings menu when clicking outside or pressing Escape
5095
5230
  document.addEventListener('click', (e) => {
5096
5231
  if (!(e.target as HTMLElement).closest('#uvf-settings-btn') &&
5097
5232
  !(e.target as HTMLElement).closest('#uvf-settings-menu')) {
5098
5233
  settingsMenu?.classList.remove('active');
5234
+ // Also close any expanded accordions
5235
+ settingsMenu?.querySelectorAll('.uvf-accordion-item.expanded').forEach(item => {
5236
+ item.classList.remove('expanded');
5237
+ });
5238
+ }
5239
+ });
5240
+
5241
+ // Add Escape key handler for settings menu
5242
+ document.addEventListener('keydown', (e) => {
5243
+ if (e.key === 'Escape' && settingsMenu?.classList.contains('active')) {
5244
+ settingsMenu.classList.remove('active');
5245
+ // Also close any expanded accordions
5246
+ settingsMenu.querySelectorAll('.uvf-accordion-item.expanded').forEach(item => {
5247
+ item.classList.remove('expanded');
5248
+ });
5099
5249
  }
5100
5250
  });
5101
5251
  }
@@ -6338,53 +6488,113 @@ export class WebPlayer extends BasePlayer {
6338
6488
  this.debugLog('Available subtitles:', this.availableSubtitles);
6339
6489
  this.debugLog('Settings config:', this.settingsConfig);
6340
6490
 
6341
- let menuHTML = '';
6491
+ // Generate accordion-style menu
6492
+ this.generateAccordionMenu();
6493
+ }
6494
+
6495
+ /**
6496
+ * Generate accordion-style settings menu
6497
+ */
6498
+ private generateAccordionMenu(): void {
6499
+ const settingsMenu = document.getElementById('uvf-settings-menu');
6500
+ if (!settingsMenu) return;
6342
6501
 
6343
- // Playback Speed Section (only if enabled in config)
6502
+ let menuHTML = '<div class="uvf-settings-accordion">';
6503
+
6504
+ // Playback Speed Accordion Section (only if enabled in config)
6344
6505
  if (this.settingsConfig.speed) {
6506
+ const currentSpeedLabel = this.currentPlaybackRate === 1 ? 'Normal' : `${this.currentPlaybackRate}x`;
6345
6507
  menuHTML += `
6346
- <div class="uvf-settings-group">
6347
- <div class="uvf-settings-label">Playback Speed</div>
6348
- <div class="uvf-settings-option speed-option" data-speed="0.25">0.25x</div>
6349
- <div class="uvf-settings-option speed-option" data-speed="0.5">0.5x</div>
6350
- <div class="uvf-settings-option speed-option" data-speed="0.75">0.75x</div>
6351
- <div class="uvf-settings-option speed-option ${this.currentPlaybackRate === 1 ? 'active' : ''}" data-speed="1">Normal</div>
6352
- <div class="uvf-settings-option speed-option" data-speed="1.25">1.25x</div>
6353
- <div class="uvf-settings-option speed-option" data-speed="1.5">1.5x</div>
6354
- <div class="uvf-settings-option speed-option" data-speed="1.75">1.75x</div>
6355
- <div class="uvf-settings-option speed-option" data-speed="2">2x</div>
6508
+ <div class="uvf-accordion-item">
6509
+ <div class="uvf-accordion-header" data-section="speed">
6510
+ <div class="uvf-accordion-title">
6511
+ <span class="uvf-accordion-icon">
6512
+ <svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor">
6513
+ <path d="M13,24l11-12L13,0V7.5C5.7,7.5,0,13.2,0,20.5C0,22.4,0.6,24.2,1.6,25.7C4.8,21.8,8.7,19.5,13,19.5V24z"/>
6514
+ </svg>
6515
+ </span>
6516
+ <span>Playback Speed</span>
6517
+ </div>
6518
+ <div class="uvf-accordion-current">${currentSpeedLabel}</div>
6519
+ <div class="uvf-accordion-arrow">▼</div>
6520
+ </div>
6521
+ <div class="uvf-accordion-content" data-section="speed">
6522
+ <div class="uvf-settings-option speed-option ${this.currentPlaybackRate === 0.25 ? 'active' : ''}" data-speed="0.25">0.25x</div>
6523
+ <div class="uvf-settings-option speed-option ${this.currentPlaybackRate === 0.5 ? 'active' : ''}" data-speed="0.5">0.5x</div>
6524
+ <div class="uvf-settings-option speed-option ${this.currentPlaybackRate === 0.75 ? 'active' : ''}" data-speed="0.75">0.75x</div>
6525
+ <div class="uvf-settings-option speed-option ${this.currentPlaybackRate === 1 ? 'active' : ''}" data-speed="1">Normal</div>
6526
+ <div class="uvf-settings-option speed-option ${this.currentPlaybackRate === 1.25 ? 'active' : ''}" data-speed="1.25">1.25x</div>
6527
+ <div class="uvf-settings-option speed-option ${this.currentPlaybackRate === 1.5 ? 'active' : ''}" data-speed="1.5">1.5x</div>
6528
+ <div class="uvf-settings-option speed-option ${this.currentPlaybackRate === 1.75 ? 'active' : ''}" data-speed="1.75">1.75x</div>
6529
+ <div class="uvf-settings-option speed-option ${this.currentPlaybackRate === 2 ? 'active' : ''}" data-speed="2">2x</div>
6530
+ </div>
6356
6531
  </div>`;
6357
6532
  }
6358
6533
 
6359
- // Quality Section (only if enabled in config and qualities detected)
6534
+ // Quality Accordion Section (only if enabled in config and qualities detected)
6360
6535
  if (this.settingsConfig.quality && this.availableQualities.length > 0) {
6361
- menuHTML += `<div class="uvf-settings-group">
6362
- <div class="uvf-settings-label">Quality</div>`;
6536
+ const currentQuality = this.availableQualities.find(q => q.value === this.currentQuality);
6537
+ const currentQualityLabel = currentQuality ? currentQuality.label : 'Auto';
6538
+
6539
+ menuHTML += `
6540
+ <div class="uvf-accordion-item">
6541
+ <div class="uvf-accordion-header" data-section="quality">
6542
+ <div class="uvf-accordion-title">
6543
+ <span class="uvf-accordion-icon">
6544
+ <svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor">
6545
+ <path d="M9,4v3h5V4h4V16H15v-3H9v3H5V4H9z M8,5H6v10h2V11h6v4h2V5h-2v4H8V5z"/>
6546
+ </svg>
6547
+ </span>
6548
+ <span>Quality</span>
6549
+ </div>
6550
+ <div class="uvf-accordion-current">${currentQualityLabel}</div>
6551
+ <div class="uvf-accordion-arrow">▼</div>
6552
+ </div>
6553
+ <div class="uvf-accordion-content" data-section="quality">`;
6363
6554
 
6364
6555
  this.availableQualities.forEach(quality => {
6365
6556
  const isActive = quality.value === this.currentQuality ? 'active' : '';
6366
6557
  menuHTML += `<div class="uvf-settings-option quality-option ${isActive}" data-quality="${quality.value}">${quality.label}</div>`;
6367
6558
  });
6368
6559
 
6369
- menuHTML += `</div>`;
6560
+ menuHTML += `</div></div>`;
6370
6561
  }
6371
6562
 
6372
- // Subtitles Section (only if enabled in config and subtitles available)
6563
+ // Subtitles Accordion Section (only if enabled in config and subtitles available)
6373
6564
  if (this.settingsConfig.subtitles && this.availableSubtitles.length > 0) {
6374
- menuHTML += `<div class="uvf-settings-group">
6375
- <div class="uvf-settings-label">Subtitles/Captions</div>`;
6565
+ const currentSubtitle = this.availableSubtitles.find(s => s.value === this.currentSubtitle);
6566
+ const currentSubtitleLabel = currentSubtitle ? currentSubtitle.label : 'Off';
6567
+
6568
+ menuHTML += `
6569
+ <div class="uvf-accordion-item">
6570
+ <div class="uvf-accordion-header" data-section="subtitles">
6571
+ <div class="uvf-accordion-title">
6572
+ <span class="uvf-accordion-icon">
6573
+ <svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor">
6574
+ <path d="M20,4H4C2.89,4 2,4.89 2,6V18C2,19.11 2.89,20 4,20H20C21.11,20 22,19.11 22,18V6C22,4.89 21.11,4 20,4M20,18H4V6H20V18M6,10H8V12H6V10M6,14H14V16H6V14M16,14H18V16H16V14M10,10H18V12H10V10Z"/>
6575
+ </svg>
6576
+ </span>
6577
+ <span>Subtitles</span>
6578
+ </div>
6579
+ <div class="uvf-accordion-current">${currentSubtitleLabel}</div>
6580
+ <div class="uvf-accordion-arrow">▼</div>
6581
+ </div>
6582
+ <div class="uvf-accordion-content" data-section="subtitles">`;
6376
6583
 
6377
6584
  this.availableSubtitles.forEach(subtitle => {
6378
6585
  const isActive = subtitle.value === this.currentSubtitle ? 'active' : '';
6379
6586
  menuHTML += `<div class="uvf-settings-option subtitle-option ${isActive}" data-subtitle="${subtitle.value}">${subtitle.label}</div>`;
6380
6587
  });
6381
6588
 
6382
- menuHTML += `</div>`;
6589
+ menuHTML += `</div></div>`;
6383
6590
  }
6384
6591
 
6592
+ // Close accordion container
6593
+ menuHTML += '</div>';
6594
+
6385
6595
  // If no sections are enabled or available, show a message
6386
- if (!menuHTML.trim()) {
6387
- menuHTML = '<div class="uvf-settings-group"><div class="uvf-settings-label">No settings available</div></div>';
6596
+ if (menuHTML === '<div class="uvf-settings-accordion"></div>') {
6597
+ menuHTML = '<div class="uvf-settings-accordion"><div class="uvf-settings-empty">No settings available</div></div>';
6388
6598
  }
6389
6599
 
6390
6600
  this.debugLog('Generated menu HTML length:', menuHTML.length);
@@ -6475,18 +6685,33 @@ export class WebPlayer extends BasePlayer {
6475
6685
  }
6476
6686
 
6477
6687
  /**
6478
- * Setup event listeners for settings menu options
6688
+ * Setup event listeners for accordion-style settings menu
6479
6689
  */
6480
6690
  private setupSettingsEventListeners(): void {
6481
6691
  const settingsMenu = document.getElementById('uvf-settings-menu');
6482
6692
  if (!settingsMenu) return;
6483
6693
 
6694
+ // Accordion header click handlers
6695
+ settingsMenu.querySelectorAll('.uvf-accordion-header').forEach(header => {
6696
+ header.addEventListener('click', (e) => {
6697
+ e.preventDefault();
6698
+ e.stopPropagation();
6699
+
6700
+ const accordionItem = header.parentElement;
6701
+ const section = header.getAttribute('data-section');
6702
+
6703
+ if (accordionItem && section) {
6704
+ this.toggleAccordionSection(accordionItem, section);
6705
+ }
6706
+ });
6707
+ });
6708
+
6484
6709
  // Speed options
6485
6710
  settingsMenu.querySelectorAll('.speed-option').forEach(option => {
6486
6711
  option.addEventListener('click', (e) => {
6487
6712
  const speed = parseFloat((e.target as HTMLElement).dataset.speed || '1');
6488
6713
  this.setPlaybackRateFromSettings(speed);
6489
- this.updateSettingsActiveStates('speed-option', e.target as HTMLElement);
6714
+ this.updateAccordionAfterSelection('speed');
6490
6715
  });
6491
6716
  });
6492
6717
 
@@ -6495,7 +6720,7 @@ export class WebPlayer extends BasePlayer {
6495
6720
  option.addEventListener('click', (e) => {
6496
6721
  const quality = (e.target as HTMLElement).dataset.quality || 'auto';
6497
6722
  this.setQualityFromSettings(quality);
6498
- this.updateSettingsActiveStates('quality-option', e.target as HTMLElement);
6723
+ this.updateAccordionAfterSelection('quality');
6499
6724
  });
6500
6725
  });
6501
6726
 
@@ -6504,10 +6729,47 @@ export class WebPlayer extends BasePlayer {
6504
6729
  option.addEventListener('click', (e) => {
6505
6730
  const subtitle = (e.target as HTMLElement).dataset.subtitle || 'off';
6506
6731
  this.setSubtitle(subtitle);
6507
- this.updateSettingsActiveStates('subtitle-option', e.target as HTMLElement);
6732
+ this.updateAccordionAfterSelection('subtitles');
6508
6733
  });
6509
6734
  });
6510
6735
  }
6736
+
6737
+ /**
6738
+ * Toggle accordion section
6739
+ */
6740
+ private toggleAccordionSection(accordionItem: Element, section: string): void {
6741
+ const isExpanded = accordionItem.classList.contains('expanded');
6742
+
6743
+ // If clicking the same section that's already expanded, just close it
6744
+ if (isExpanded) {
6745
+ accordionItem.classList.remove('expanded');
6746
+ return;
6747
+ }
6748
+
6749
+ // Otherwise, close all sections and open the clicked one
6750
+ const settingsMenu = document.getElementById('uvf-settings-menu');
6751
+ if (settingsMenu) {
6752
+ settingsMenu.querySelectorAll('.uvf-accordion-item.expanded').forEach(item => {
6753
+ item.classList.remove('expanded');
6754
+ });
6755
+ }
6756
+
6757
+ // Open the clicked section
6758
+ accordionItem.classList.add('expanded');
6759
+ }
6760
+
6761
+ /**
6762
+ * Update accordion after user makes a selection
6763
+ */
6764
+ private updateAccordionAfterSelection(section: string): void {
6765
+ // Just update the current values without closing
6766
+ // User can manually close or it will close when they click outside
6767
+ setTimeout(() => {
6768
+ // Refresh the menu to update current values
6769
+ this.generateAccordionMenu();
6770
+ this.setupSettingsEventListeners();
6771
+ }, 100);
6772
+ }
6511
6773
 
6512
6774
  /**
6513
6775
  * Update active states in settings menu