unified-video-framework 1.4.365 → 1.4.366

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.
Files changed (40) hide show
  1. package/package.json +1 -1
  2. package/packages/core/dist/interfaces/IVideoPlayer.d.ts +0 -1
  3. package/packages/core/dist/interfaces/IVideoPlayer.d.ts.map +1 -1
  4. package/packages/core/dist/interfaces.d.ts +1 -1
  5. package/packages/core/dist/interfaces.d.ts.map +1 -1
  6. package/packages/core/src/interfaces/IVideoPlayer.ts +1 -2
  7. package/packages/core/src/interfaces.ts +4 -1
  8. package/packages/web/dist/WebPlayer.d.ts.map +1 -1
  9. package/packages/web/dist/WebPlayer.js +1 -33
  10. package/packages/web/dist/WebPlayer.js.map +1 -1
  11. package/packages/web/dist/chapters/ChapterManager.d.ts +4 -3
  12. package/packages/web/dist/chapters/ChapterManager.d.ts.map +1 -1
  13. package/packages/web/dist/chapters/ChapterManager.js +52 -34
  14. package/packages/web/dist/chapters/ChapterManager.js.map +1 -1
  15. package/packages/web/dist/chapters/CreditsButtonController.d.ts +44 -0
  16. package/packages/web/dist/chapters/CreditsButtonController.d.ts.map +1 -0
  17. package/packages/web/dist/chapters/CreditsButtonController.js +243 -0
  18. package/packages/web/dist/chapters/CreditsButtonController.js.map +1 -0
  19. package/packages/web/dist/chapters/SkipButtonController.d.ts +1 -4
  20. package/packages/web/dist/chapters/SkipButtonController.d.ts.map +1 -1
  21. package/packages/web/dist/chapters/SkipButtonController.js +22 -118
  22. package/packages/web/dist/chapters/SkipButtonController.js.map +1 -1
  23. package/packages/web/dist/chapters/types/ChapterTypes.d.ts +21 -12
  24. package/packages/web/dist/chapters/types/ChapterTypes.d.ts.map +1 -1
  25. package/packages/web/dist/chapters/types/ChapterTypes.js.map +1 -1
  26. package/packages/web/dist/react/WebPlayerView.d.ts +0 -10
  27. package/packages/web/dist/react/WebPlayerView.d.ts.map +1 -1
  28. package/packages/web/dist/react/WebPlayerView.js +1 -5
  29. package/packages/web/dist/react/WebPlayerView.js.map +1 -1
  30. package/packages/web/dist/react/components/SkipButton.d.ts +0 -2
  31. package/packages/web/dist/react/components/SkipButton.d.ts.map +1 -1
  32. package/packages/web/dist/react/components/SkipButton.js +13 -30
  33. package/packages/web/dist/react/components/SkipButton.js.map +1 -1
  34. package/packages/web/src/WebPlayer.ts +7475 -7507
  35. package/packages/web/src/chapters/ChapterManager.ts +76 -57
  36. package/packages/web/src/chapters/CreditsButtonController.ts +392 -0
  37. package/packages/web/src/chapters/SkipButtonController.ts +33 -148
  38. package/packages/web/src/chapters/types/ChapterTypes.ts +34 -19
  39. package/packages/web/src/react/WebPlayerView.tsx +1 -14
  40. package/packages/web/src/react/components/SkipButton.tsx +24 -82
@@ -12,21 +12,18 @@ import {
12
12
 
13
13
  export class SkipButtonController {
14
14
  private skipButton: HTMLElement | null = null;
15
- private secondaryButton: HTMLElement | null = null;
16
- private container: HTMLElement | null = null;
17
15
  private currentSegment: VideoSegment | null = null;
18
16
  private autoSkipTimeout: NodeJS.Timeout | null = null;
19
17
  private hideTimeout: NodeJS.Timeout | null = null;
20
18
  private countdownInterval: NodeJS.Timeout | null = null;
21
19
  private state: SkipButtonState;
22
-
20
+
23
21
  constructor(
24
22
  private playerContainer: HTMLElement,
25
23
  private config: ChapterConfig,
26
24
  private onSkip: (segment: VideoSegment) => void,
27
25
  private onButtonShown: (segment: VideoSegment) => void,
28
- private onButtonHidden: (segment: VideoSegment, reason: string) => void,
29
- private onSecondaryAction?: (segment: VideoSegment) => void
26
+ private onButtonHidden: (segment: VideoSegment, reason: string) => void
30
27
  ) {
31
28
  this.state = {
32
29
  visible: false,
@@ -53,13 +50,9 @@ export class SkipButtonController {
53
50
  this.state.segment = segment;
54
51
 
55
52
  // Create button if it doesn't exist
56
- if (!this.container) {
57
- console.log('[SkipButtonController] Creating skip button container');
58
- const containerElement = this.createSkipButton();
59
- this.playerContainer.appendChild(containerElement);
60
- console.log('[SkipButtonController] Container appended to player. Container:', this.container);
61
- } else {
62
- console.log('[SkipButtonController] Container already exists, reusing it');
53
+ if (!this.skipButton) {
54
+ this.skipButton = this.createSkipButton();
55
+ this.playerContainer.appendChild(this.skipButton);
63
56
  }
64
57
 
65
58
  // Update button content and show it
@@ -126,13 +119,7 @@ export class SkipButtonController {
126
119
  */
127
120
  public destroy(): void {
128
121
  this.clearTimeouts();
129
- if (this.container) {
130
- this.container.remove();
131
- this.container = null;
132
- this.skipButton = null;
133
- this.secondaryButton = null;
134
- } else if (this.skipButton) {
135
- // Fallback if no container (shouldn't happen with new logic but safe to keep)
122
+ if (this.skipButton) {
136
123
  this.skipButton.remove();
137
124
  this.skipButton = null;
138
125
  }
@@ -142,70 +129,31 @@ export class SkipButtonController {
142
129
  }
143
130
 
144
131
  /**
145
- * Create the skip button DOM element (container and buttons)
132
+ * Create the skip button DOM element
146
133
  */
147
134
  private createSkipButton(): HTMLElement {
148
- // Create container
149
- const container = document.createElement('div');
150
- container.className = 'uvf-skip-container';
151
- this.container = container;
152
-
153
- // Create secondary button (Watch Credits) - initially hidden
154
- const secondaryBtn = document.createElement('button');
155
- secondaryBtn.className = 'uvf-skip-button uvf-skip-secondary';
156
- secondaryBtn.setAttribute('type', 'button');
157
- secondaryBtn.textContent = 'Watch Credits';
158
- secondaryBtn.style.display = 'none'; // Hidden by default
159
- secondaryBtn.style.marginRight = '10px';
160
- secondaryBtn.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
161
- secondaryBtn.style.border = '1px solid rgba(255, 255, 255, 0.5)';
162
-
163
- // Explicit click listener for secondary button
164
- secondaryBtn.addEventListener('click', (e) => {
165
- e.stopPropagation(); // prevent triggering other things
166
- if (this.currentSegment && this.onSecondaryAction) {
167
- this.clearTimeouts(); // Stop auto-skip
168
- this.onSecondaryAction(this.currentSegment);
169
- // Hide the container after action
170
- this.hideSkipButton('user-action');
171
- }
172
- });
173
- this.secondaryButton = secondaryBtn;
174
- container.appendChild(secondaryBtn);
175
-
176
- // Create primary button
177
135
  const button = document.createElement('button');
178
136
  button.className = 'uvf-skip-button';
179
137
  button.setAttribute('type', 'button');
180
138
  button.setAttribute('aria-label', 'Skip segment');
181
139
 
140
+ // Apply position styles
141
+ this.applyPositionStyles(button, this.state.position);
142
+
182
143
  // Add click handler
183
144
  button.addEventListener('click', () => {
184
- console.log('[SkipButtonController] Primary button (Next Episode) clicked! Current segment:', this.currentSegment?.type);
185
145
  if (this.currentSegment) {
186
146
  this.onSkip(this.currentSegment);
187
147
  this.hideSkipButton('user-action');
188
148
  }
189
149
  });
190
150
 
191
- this.skipButton = button;
192
- container.appendChild(button);
193
-
194
- // Apply custom styles if provided (to the primary button mostly)
151
+ // Apply custom styles if provided
195
152
  if (this.config.customStyles?.skipButton) {
196
153
  Object.assign(button.style, this.config.customStyles.skipButton);
197
154
  }
198
155
 
199
- // Apply position styles to CONTAINER
200
- this.applyPositionStyles(container, this.state.position);
201
-
202
- // Add flex layout for side-by-side buttons
203
- container.style.display = 'flex';
204
- container.style.flexDirection = 'row';
205
- container.style.alignItems = 'center';
206
- container.style.gap = '10px';
207
-
208
- return container;
156
+ return button;
209
157
  }
210
158
 
211
159
  /**
@@ -215,75 +163,33 @@ export class SkipButtonController {
215
163
  if (!this.skipButton) return;
216
164
 
217
165
  // Set button text
218
- let skipLabel = segment.skipLabel || DEFAULT_SKIP_LABELS[segment.type];
219
-
220
- // Check for Next Episode override
221
- let showSecondary = false;
222
- // We infer it's next episode scenario if it's credits and we have a config for it
223
- // AND secondary action is available.
224
- // Ideally we checked for `nextEpisode` config in ChapterManager, but here we only know if onSecondaryAction exists.
225
- // Let's assume onSecondaryAction implies we support it.
226
- if (segment.type === 'credits' && this.onSecondaryAction) {
227
- console.log('[SkipButtonController] Credits segment detected, nextEpisode config:', this.config.nextEpisode);
228
- if (this.config.nextEpisode?.title) {
229
- skipLabel = `Next Episode: ${this.config.nextEpisode.title}`;
230
- } else {
231
- skipLabel = "Next Episode";
232
- }
233
- showSecondary = true;
234
- } else {
235
- console.log('[SkipButtonController] Not showing secondary button. segment.type:', segment.type, 'onSecondaryAction:', !!this.onSecondaryAction);
236
- }
237
-
166
+ const skipLabel = segment.skipLabel || DEFAULT_SKIP_LABELS[segment.type];
238
167
  this.skipButton.textContent = skipLabel;
239
168
 
240
- // Handle Secondary Button Visibility
241
- if (this.secondaryButton) {
242
- console.log('[SkipButtonController] Secondary button exists. showSecondary:', showSecondary, 'onSecondaryAction:', !!this.onSecondaryAction);
243
- if (showSecondary && this.onSecondaryAction) {
244
- console.log('[SkipButtonController] Showing Watch Credits button');
245
- this.secondaryButton.style.display = 'inline-block';
246
- if (this.config.customStyles?.skipButton) {
247
- // Inherit some basic styles like font size if available
248
- if (this.config.customStyles.skipButton.fontSize) {
249
- this.secondaryButton.style.fontSize = this.config.customStyles.skipButton.fontSize;
250
- }
251
- if (this.config.customStyles.skipButton.padding) {
252
- this.secondaryButton.style.padding = this.config.customStyles.skipButton.padding;
253
- }
254
- }
255
- } else {
256
- console.log('[SkipButtonController] Hiding Watch Credits button');
257
- this.secondaryButton.style.display = 'none';
258
- }
259
- } else {
260
- console.log('[SkipButtonController] Secondary button element does not exist!');
261
- }
262
-
263
169
  // Update aria-label for accessibility
264
170
  this.skipButton.setAttribute('aria-label', `${skipLabel} - ${segment.title || segment.type}`);
265
171
 
266
172
  // Add segment type class for styling
267
173
  this.skipButton.className = `uvf-skip-button uvf-skip-${segment.type}`;
268
-
269
- // Position class is on container now, so update it just in case position changed
270
- this.updatePosition(this.state.position);
174
+
175
+ // Apply position class
176
+ this.skipButton.classList.add(`uvf-skip-button-${this.state.position}`);
271
177
  }
272
178
 
273
179
  /**
274
- * Apply position styles to element (container or button)
180
+ * Apply position styles to skip button
275
181
  */
276
- private applyPositionStyles(element: HTMLElement, position: SkipButtonPosition): void {
182
+ private applyPositionStyles(button: HTMLElement, position: SkipButtonPosition): void {
277
183
  // Reset position classes
278
- element.classList.remove(
184
+ button.classList.remove(
279
185
  'uvf-skip-button-bottom-right',
280
- 'uvf-skip-button-bottom-left',
186
+ 'uvf-skip-button-bottom-left',
281
187
  'uvf-skip-button-top-right',
282
188
  'uvf-skip-button-top-left'
283
189
  );
284
190
 
285
191
  // Add new position class
286
- element.classList.add(`uvf-skip-button-${position}`);
192
+ button.classList.add(`uvf-skip-button-${position}`);
287
193
 
288
194
  // Apply CSS styles based on position
289
195
  const styles: Partial<CSSStyleDeclaration> = {
@@ -295,58 +201,39 @@ export class SkipButtonController {
295
201
  case 'bottom-right':
296
202
  Object.assign(styles, {
297
203
  bottom: '100px',
298
- right: '30px',
299
- top: 'auto',
300
- left: 'auto'
204
+ right: '30px'
301
205
  });
302
206
  break;
303
207
  case 'bottom-left':
304
208
  Object.assign(styles, {
305
209
  bottom: '100px',
306
- left: '30px',
307
- top: 'auto',
308
- right: 'auto'
210
+ left: '30px'
309
211
  });
310
212
  break;
311
213
  case 'top-right':
312
214
  Object.assign(styles, {
313
215
  top: '30px',
314
- right: '30px',
315
- bottom: 'auto',
316
- left: 'auto'
216
+ right: '30px'
317
217
  });
318
218
  break;
319
219
  case 'top-left':
320
220
  Object.assign(styles, {
321
221
  top: '30px',
322
- left: '30px',
323
- bottom: 'auto',
324
- right: 'auto'
222
+ left: '30px'
325
223
  });
326
224
  break;
327
225
  }
328
226
 
329
- Object.assign(element.style, styles);
227
+ Object.assign(button.style, styles);
330
228
  }
331
229
 
332
230
  /**
333
231
  * Show the skip button with animation
334
232
  */
335
233
  private showButton(): void {
336
- console.log('[SkipButtonController] showButton called. Container exists:', !!this.container);
337
- if (!this.container) {
338
- console.error('[SkipButtonController] Cannot show button - container is null!');
339
- return;
340
- }
234
+ if (!this.skipButton) return;
341
235
 
342
- this.container.classList.add('visible');
343
- // Ensure visibility with inline styles as fallback
344
- this.container.style.display = 'flex';
345
- this.container.style.opacity = '1';
346
- this.container.style.visibility = 'visible';
347
- this.container.style.pointerEvents = 'auto';
348
- console.log('[SkipButtonController] Container visibility set. Display:', this.container.style.display);
349
- // Also ensure buttons are visible (opacity handled by CSS on container usually, but let's be safe)
236
+ this.skipButton.classList.add('visible');
350
237
  this.state.visible = true;
351
238
  }
352
239
 
@@ -354,12 +241,10 @@ export class SkipButtonController {
354
241
  * Hide the skip button with animation
355
242
  */
356
243
  private hideButton(): void {
357
- if (!this.container) return;
244
+ if (!this.skipButton) return;
358
245
 
359
- this.container.classList.remove('visible');
360
- if (this.skipButton) {
361
- this.skipButton.classList.remove('auto-skip', 'countdown');
362
- }
246
+ this.skipButton.classList.remove('visible');
247
+ this.skipButton.classList.remove('auto-skip', 'countdown');
363
248
  }
364
249
 
365
250
  /**
@@ -400,10 +285,10 @@ export class SkipButtonController {
400
285
 
401
286
  // Update button text with countdown
402
287
  const originalText = this.skipButton.textContent || '';
403
-
288
+
404
289
  // Start countdown animation
405
290
  this.skipButton.classList.add('countdown');
406
-
291
+
407
292
  // Update countdown every second
408
293
  this.countdownInterval = setInterval(() => {
409
294
  remainingTime -= 1;
@@ -39,6 +39,15 @@ export interface VideoSegment {
39
39
 
40
40
  /** Whether to show skip button for this segment */
41
41
  showSkipButton?: boolean;
42
+
43
+ /** URL to navigate to for next episode (used in credits segments) */
44
+ nextEpisodeUrl?: string;
45
+
46
+ /** Custom label for "Watch Credits" button */
47
+ watchCreditsLabel?: string;
48
+
49
+ /** Custom label for "Next Episode/Play Next" button */
50
+ nextEpisodeLabel?: string;
42
51
  }
43
52
 
44
53
  /**
@@ -103,21 +112,6 @@ export interface ChapterConfig {
103
112
 
104
113
  /** User preferences */
105
114
  userPreferences?: ChapterPreferences;
106
-
107
- /** Next Episode Configuration */
108
- nextEpisode?: NextEpisodeConfig;
109
- }
110
-
111
- /**
112
- * Configuration for the next episode
113
- */
114
- export interface NextEpisodeConfig {
115
- enabled: boolean;
116
- url: string;
117
- title?: string;
118
- thumbnail?: string;
119
- autoPlayDelay?: number; // seconds, overrides segment autoSkipDelay if present
120
- description?: string;
121
115
  }
122
116
 
123
117
  /**
@@ -197,10 +191,31 @@ export interface ChapterEvents {
197
191
  url?: string;
198
192
  };
199
193
 
200
- /** When next episode should be played */
201
- nextEpisode: {
202
- config: NextEpisodeConfig;
203
- autoPlay: boolean;
194
+ /** When user clicks "Watch Credits" button */
195
+ creditsWatched: {
196
+ segment: VideoSegment;
197
+ currentTime: number;
198
+ };
199
+
200
+ /** When user manually clicks "Next Episode" button */
201
+ nextEpisodeClicked: {
202
+ segment: VideoSegment;
203
+ nextEpisodeUrl: string;
204
+ currentTime: number;
205
+ };
206
+
207
+ /** When auto-redirect timer triggers */
208
+ creditsAutoRedirect: {
209
+ segment: VideoSegment;
210
+ nextEpisodeUrl: string;
211
+ currentTime: number;
212
+ };
213
+
214
+ /** When credits segment ends after user watched them */
215
+ creditsFullyWatched: {
216
+ segment: VideoSegment;
217
+ nextEpisodeUrl?: string;
218
+ currentTime: number;
204
219
  };
205
220
  }
206
221
 
@@ -370,16 +370,9 @@ export type WebPlayerViewProps = {
370
370
  autoSkipCredits?: boolean; // Auto-skip credits segments (default: false)
371
371
  showSkipButtons?: boolean; // Show skip buttons (default: true)
372
372
  skipButtonTimeout?: number; // Button timeout in milliseconds (default: 5000)
373
-
374
373
  rememberChoices?: boolean; // Remember user preferences (default: true)
375
374
  resumePlaybackAfterSkip?: boolean; // Resume playback after skipping (default: true)
376
375
  };
377
- nextEpisode?: { // Next episode configuration
378
- title: string;
379
- url: string;
380
- thumbnail?: string;
381
- autoPlayDelay?: number;
382
- };
383
376
  };
384
377
 
385
378
  // Navigation Configuration
@@ -436,7 +429,6 @@ export type WebPlayerViewProps = {
436
429
  onChapterSkipButtonHidden?: (data: { segment: any; reason: string }) => void; // Skip button hidden
437
430
  onChaptersLoaded?: (data: { segmentCount: number; chapters: any[] }) => void; // Chapters loaded
438
431
  onChaptersLoadError?: (data: { error: Error; url?: string }) => void; // Chapters load error
439
- onNextEpisode?: (data: { config: any; autoPlay: boolean }) => void; // Next episode triggered
440
432
 
441
433
  // Flash News Ticker
442
434
  flashNewsTicker?: FlashNewsTickerConfig; // Flash news ticker configuration
@@ -993,9 +985,7 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
993
985
  skipButtonTimeout: chaptersConfig.userPreferences?.skipButtonTimeout ?? 5000,
994
986
  rememberChoices: chaptersConfig.userPreferences?.rememberChoices ?? true,
995
987
  resumePlaybackAfterSkip: chaptersConfig.userPreferences?.resumePlaybackAfterSkip ?? true,
996
-
997
- },
998
- nextEpisode: chaptersConfig.nextEpisode
988
+ }
999
989
  } : { enabled: false }
1000
990
  };
1001
991
 
@@ -1131,9 +1121,6 @@ export const WebPlayerView: React.FC<WebPlayerViewProps> = (props) => {
1131
1121
  if (props.onChaptersLoadError && typeof (player as any).on === 'function') {
1132
1122
  (player as any).on('chaptersLoadError', props.onChaptersLoadError);
1133
1123
  }
1134
- if (props.onNextEpisode && typeof (player as any).on === 'function') {
1135
- (player as any).on('nextEpisode', props.onNextEpisode);
1136
- }
1137
1124
 
1138
1125
  // Navigation event listeners
1139
1126
  if (props.onNavigationBackClicked && typeof (player as any).on === 'function') {
@@ -45,12 +45,6 @@ export interface SkipButtonProps {
45
45
 
46
46
  /** Auto-skip delay in seconds */
47
47
  autoSkipDelay?: number;
48
-
49
- /** Secondary action label (e.g., "Watch Credits") */
50
- secondaryLabel?: string;
51
-
52
- /** Callback for secondary action */
53
- onSecondaryAction?: (segment: VideoSegment) => void;
54
48
  }
55
49
 
56
50
  export const SkipButton: React.FC<SkipButtonProps> = ({
@@ -65,9 +59,7 @@ export const SkipButton: React.FC<SkipButtonProps> = ({
65
59
  className = '',
66
60
  style = {},
67
61
  enableAutoSkip = false,
68
- autoSkipDelay = 10,
69
- secondaryLabel,
70
- onSecondaryAction
62
+ autoSkipDelay = 10
71
63
  }) => {
72
64
  // State
73
65
  const [isVisible, setIsVisible] = useState(visible);
@@ -138,33 +130,6 @@ export const SkipButton: React.FC<SkipButtonProps> = ({
138
130
  handleHide('user-action');
139
131
  };
140
132
 
141
- /**
142
- * Handle secondary button click
143
- */
144
- const handleSecondary = (e: React.MouseEvent) => {
145
- e.stopPropagation(); // Prevent bubbling
146
- if (!segment) return;
147
-
148
- // Stop auto-skip and countdown
149
- clearTimeouts();
150
- setIsAutoSkip(false);
151
- setCountdown(null);
152
-
153
- // Call callback
154
- onSecondaryAction?.(segment);
155
-
156
- // We DON'T hide the button immediately for "Watch Credits",
157
- // we just stop the auto-skip countdown. User can still click "Next Episode" later.
158
- // But typically "Watch Credits" implies dismissing the "Next Episode" urgency.
159
- // If we want to hide it:
160
- // handleHide('user-secondary-action');
161
-
162
- // For now, let's keep it visible but stop the countdown so they can click 'Next' later if they want,
163
- // OR we can hide it if that's the desired UX.
164
- // Based on Netflix UX: "Watch Credits" usually minimizes the overlay.
165
- // Here we'll just stop the auto-skip.
166
- };
167
-
168
133
  /**
169
134
  * Handle button show
170
135
  */
@@ -283,54 +248,31 @@ export const SkipButton: React.FC<SkipButtonProps> = ({
283
248
  }
284
249
 
285
250
  return (
286
- <div className={`uvf-skip-container ${positionClass} ${className}`} style={defaultStyles}>
287
- {/* Secondary Button (Watch Credits) */}
288
- {secondaryLabel && (
289
- <button
290
- type="button"
291
- className="uvf-skip-button uvf-skip-secondary"
292
- onClick={handleSecondary}
251
+ <button
252
+ type="button"
253
+ className={buttonClasses}
254
+ style={defaultStyles}
255
+ onClick={handleSkip}
256
+ aria-label={`${buttonText} - ${segment.title || segment.type}`}
257
+ >
258
+ {displayText}
259
+
260
+ {/* Progress bar for auto-skip countdown */}
261
+ {isAutoSkip && countdown !== null && (
262
+ <div
263
+ className="uvf-skip-countdown-progress"
293
264
  style={{
294
- marginRight: '10px',
295
- backgroundColor: 'rgba(0, 0, 0, 0.7)',
296
- border: '1px solid rgba(255, 255, 255, 0.5)',
297
- color: '#fff',
298
- ...style
265
+ position: 'absolute',
266
+ bottom: 0,
267
+ left: 0,
268
+ height: '3px',
269
+ backgroundColor: 'currentColor',
270
+ width: `${((autoSkipDelay - countdown) / autoSkipDelay) * 100}%`,
271
+ transition: 'width 1s linear',
272
+ borderRadius: '0 0 6px 6px'
299
273
  }}
300
- >
301
- {secondaryLabel}
302
- </button>
274
+ />
303
275
  )}
304
-
305
- {/* Primary Button (Next Episode / Skip) */}
306
- <button
307
- type="button"
308
- className={buttonClasses}
309
- // style is applied to container now, but we keep button specific classes
310
- // We need to adjust styles since we are wrapping in a div now
311
- style={{ pointerEvents: 'auto' }}
312
- onClick={handleSkip}
313
- aria-label={`${buttonText} - ${segment.title || segment.type}`}
314
- >
315
- {displayText}
316
-
317
- {/* Progress bar for auto-skip countdown */}
318
- {isAutoSkip && countdown !== null && (
319
- <div
320
- className="uvf-skip-countdown-progress"
321
- style={{
322
- position: 'absolute',
323
- bottom: 0,
324
- left: 0,
325
- height: '3px',
326
- backgroundColor: 'currentColor',
327
- width: `${((autoSkipDelay - countdown) / autoSkipDelay) * 100}%`,
328
- transition: 'width 1s linear',
329
- borderRadius: '0 0 6px 6px'
330
- }}
331
- />
332
- )}
333
- </button>
334
- </div>
276
+ </button>
335
277
  );
336
278
  };