avbridge 2.9.0 → 2.11.0

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 (50) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dist/{chunk-EY6DZEDT.cjs → chunk-37UOSAVI.cjs} +55 -10
  3. package/dist/chunk-37UOSAVI.cjs.map +1 -0
  4. package/dist/{chunk-5KVLE6YI.js → chunk-EDDWAN2L.js} +3 -2
  5. package/dist/chunk-EDDWAN2L.js.map +1 -0
  6. package/dist/{chunk-SN4WZE24.js → chunk-IHNHHEA2.js} +51 -6
  7. package/dist/chunk-IHNHHEA2.js.map +1 -0
  8. package/dist/{chunk-S4WAZC2T.cjs → chunk-WRKO6Q42.cjs} +3 -2
  9. package/dist/chunk-WRKO6Q42.cjs.map +1 -0
  10. package/dist/element-browser.js +63 -4
  11. package/dist/element-browser.js.map +1 -1
  12. package/dist/element.cjs +18 -5
  13. package/dist/element.cjs.map +1 -1
  14. package/dist/element.d.cts +1 -1
  15. package/dist/element.d.ts +1 -1
  16. package/dist/element.js +17 -4
  17. package/dist/element.js.map +1 -1
  18. package/dist/index.cjs +10 -10
  19. package/dist/index.d.cts +2 -2
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +2 -2
  22. package/dist/{player-DEcidWk6.d.cts → player-DDdNVFDv.d.cts} +23 -1
  23. package/dist/{player-DEcidWk6.d.ts → player-DDdNVFDv.d.ts} +23 -1
  24. package/dist/player.cjs +329 -109
  25. package/dist/player.cjs.map +1 -1
  26. package/dist/player.d.cts +42 -0
  27. package/dist/player.d.ts +42 -0
  28. package/dist/player.js +325 -105
  29. package/dist/player.js.map +1 -1
  30. package/dist/subtitles-5H24MEBJ.js +4 -0
  31. package/dist/{subtitles-4T74JRGT.js.map → subtitles-5H24MEBJ.js.map} +1 -1
  32. package/dist/subtitles-HMVGWTU2.cjs +29 -0
  33. package/dist/{subtitles-QUH4LPI4.cjs.map → subtitles-HMVGWTU2.cjs.map} +1 -1
  34. package/package.json +1 -1
  35. package/src/element/avbridge-player.ts +235 -78
  36. package/src/element/avbridge-subtitles.ts +273 -0
  37. package/src/element/avbridge-video.ts +21 -1
  38. package/src/element/player-styles.ts +85 -35
  39. package/src/index.ts +1 -0
  40. package/src/strategies/fallback/audio-output.ts +39 -4
  41. package/src/strategies/fallback/index.ts +12 -0
  42. package/src/strategies/hybrid/index.ts +9 -0
  43. package/src/subtitles/index.ts +2 -0
  44. package/src/types.ts +25 -0
  45. package/dist/chunk-5KVLE6YI.js.map +0 -1
  46. package/dist/chunk-EY6DZEDT.cjs.map +0 -1
  47. package/dist/chunk-S4WAZC2T.cjs.map +0 -1
  48. package/dist/chunk-SN4WZE24.js.map +0 -1
  49. package/dist/subtitles-4T74JRGT.js +0 -4
  50. package/dist/subtitles-QUH4LPI4.cjs +0 -29
package/dist/player.d.cts CHANGED
@@ -287,6 +287,28 @@ interface AvbridgeVideoElementEventMap {
287
287
  fit: "contain" | "cover" | "fill";
288
288
  }>;
289
289
  }
290
+ /**
291
+ * Configuration for a custom settings section added to `<avbridge-player>`
292
+ * via {@link addSettingsSection}. Sections render in the bottom-sheet
293
+ * settings panel alongside built-in sections (Speed, Audio, Subtitles,
294
+ * Fit, Stats for Nerds). The player owns rendering — consumers describe
295
+ * data; avbridge renders it in a consistent visual style.
296
+ */
297
+ interface SettingsSectionConfig {
298
+ /** Unique id for this section. Used to update/remove later. */
299
+ id: string;
300
+ /** Display label (e.g. "Quality", "Translate"). */
301
+ label: string;
302
+ /** Items to show when the section is expanded. */
303
+ items: Array<{
304
+ id: string;
305
+ label: string;
306
+ /** Mark the currently-selected item. */
307
+ active?: boolean;
308
+ }>;
309
+ /** Called when the user picks an item. */
310
+ onSelect(itemId: string): void;
311
+ }
290
312
 
291
313
  /**
292
314
  * `<avbridge-player>` — YouTube-style controls element.
@@ -314,6 +336,8 @@ declare class AvbridgePlayerElement extends HTMLElement {
314
336
  private _volumeInput;
315
337
  private _settingsBtn;
316
338
  private _settingsMenu;
339
+ private _settingsScrim;
340
+ private _customSections;
317
341
  private _fullscreenBtn;
318
342
  private _speedIndicator;
319
343
  private _rippleLeft;
@@ -321,6 +345,8 @@ declare class AvbridgePlayerElement extends HTMLElement {
321
345
  private _state;
322
346
  private _controlsTimer;
323
347
  private _settingsOpen;
348
+ private _activeAudioTrackId;
349
+ private _activeSubtitleTrackId;
324
350
  private _userSeeking;
325
351
  private _holdTimer;
326
352
  private _holdSpeedActive;
@@ -345,6 +371,10 @@ declare class AvbridgePlayerElement extends HTMLElement {
345
371
  private _onSeekCommit;
346
372
  /** Linear click-to-time mapping across the full track width (no edge clamping). */
347
373
  private _timeFromSeekPointer;
374
+ /** Seekbar width below which drag-to-scrub seeks in real-time (vs
375
+ * preview-only). On narrow bars precise positioning is hard, so
376
+ * immediate video feedback is more useful than a time tooltip. */
377
+ private static readonly SCRUB_WIDTH_THRESHOLD;
348
378
  private _onSeekPointerDown;
349
379
  private _onSeekHover;
350
380
  private _updateSeekTooltip;
@@ -353,6 +383,7 @@ declare class AvbridgePlayerElement extends HTMLElement {
353
383
  private _toggleMute;
354
384
  private _updateVolume;
355
385
  private _toggleSettings;
386
+ private _fitSettingsToPlayer;
356
387
  private _closeSettings;
357
388
  private _buildSettingsMenu;
358
389
  private _toggleStats;
@@ -374,8 +405,14 @@ declare class AvbridgePlayerElement extends HTMLElement {
374
405
  showControls(durationMs?: number): void;
375
406
  private _showControls;
376
407
  private _scheduleHide;
408
+ /** Read the controls-timeout attribute. 0 or negative = never hide.
409
+ * Unset = default 3000ms. */
410
+ private _getControlsTimeout;
377
411
  /** Track whether the last interaction was touch so click handler can skip. */
378
412
  private _lastPointerTypeWasTouch;
413
+ /** True for ~50ms after a touch double-tap was handled, so the
414
+ * synthetic dblclick from the browser doesn't also fire fullscreen. */
415
+ private _touchDoubleTapConsumed;
379
416
  /** True if the event's composed path passes through consumer-slotted
380
417
  * content (toolbar or content-overlay). Slotted content lives in the
381
418
  * light DOM so `.closest(".avp-toolbar-top")` on the event target won't
@@ -387,6 +424,9 @@ declare class AvbridgePlayerElement extends HTMLElement {
387
424
  private _onPointerUp;
388
425
  private _cancelHold;
389
426
  private _doDoubleTap;
427
+ /** Duration of one frame in seconds, derived from diagnostics fps or
428
+ * a 30fps default. Used for frame-step shortcuts (`,` / `.`). */
429
+ private _frameDuration;
390
430
  private _onKeydown;
391
431
  private _clearTimers;
392
432
  get src(): string;
@@ -437,6 +477,8 @@ declare class AvbridgePlayerElement extends HTMLElement {
437
477
  load(): Promise<void>;
438
478
  destroy(): Promise<void>;
439
479
  setAudioTrack(id: number): Promise<void>;
480
+ addSettingsSection(config: SettingsSectionConfig): void;
481
+ removeSettingsSection(id: string): void;
440
482
  setSubtitleTrack(id: number | null): Promise<void>;
441
483
  getDiagnostics(): unknown;
442
484
  canPlayType(mime: string): string;
package/dist/player.d.ts CHANGED
@@ -287,6 +287,28 @@ interface AvbridgeVideoElementEventMap {
287
287
  fit: "contain" | "cover" | "fill";
288
288
  }>;
289
289
  }
290
+ /**
291
+ * Configuration for a custom settings section added to `<avbridge-player>`
292
+ * via {@link addSettingsSection}. Sections render in the bottom-sheet
293
+ * settings panel alongside built-in sections (Speed, Audio, Subtitles,
294
+ * Fit, Stats for Nerds). The player owns rendering — consumers describe
295
+ * data; avbridge renders it in a consistent visual style.
296
+ */
297
+ interface SettingsSectionConfig {
298
+ /** Unique id for this section. Used to update/remove later. */
299
+ id: string;
300
+ /** Display label (e.g. "Quality", "Translate"). */
301
+ label: string;
302
+ /** Items to show when the section is expanded. */
303
+ items: Array<{
304
+ id: string;
305
+ label: string;
306
+ /** Mark the currently-selected item. */
307
+ active?: boolean;
308
+ }>;
309
+ /** Called when the user picks an item. */
310
+ onSelect(itemId: string): void;
311
+ }
290
312
 
291
313
  /**
292
314
  * `<avbridge-player>` — YouTube-style controls element.
@@ -314,6 +336,8 @@ declare class AvbridgePlayerElement extends HTMLElement {
314
336
  private _volumeInput;
315
337
  private _settingsBtn;
316
338
  private _settingsMenu;
339
+ private _settingsScrim;
340
+ private _customSections;
317
341
  private _fullscreenBtn;
318
342
  private _speedIndicator;
319
343
  private _rippleLeft;
@@ -321,6 +345,8 @@ declare class AvbridgePlayerElement extends HTMLElement {
321
345
  private _state;
322
346
  private _controlsTimer;
323
347
  private _settingsOpen;
348
+ private _activeAudioTrackId;
349
+ private _activeSubtitleTrackId;
324
350
  private _userSeeking;
325
351
  private _holdTimer;
326
352
  private _holdSpeedActive;
@@ -345,6 +371,10 @@ declare class AvbridgePlayerElement extends HTMLElement {
345
371
  private _onSeekCommit;
346
372
  /** Linear click-to-time mapping across the full track width (no edge clamping). */
347
373
  private _timeFromSeekPointer;
374
+ /** Seekbar width below which drag-to-scrub seeks in real-time (vs
375
+ * preview-only). On narrow bars precise positioning is hard, so
376
+ * immediate video feedback is more useful than a time tooltip. */
377
+ private static readonly SCRUB_WIDTH_THRESHOLD;
348
378
  private _onSeekPointerDown;
349
379
  private _onSeekHover;
350
380
  private _updateSeekTooltip;
@@ -353,6 +383,7 @@ declare class AvbridgePlayerElement extends HTMLElement {
353
383
  private _toggleMute;
354
384
  private _updateVolume;
355
385
  private _toggleSettings;
386
+ private _fitSettingsToPlayer;
356
387
  private _closeSettings;
357
388
  private _buildSettingsMenu;
358
389
  private _toggleStats;
@@ -374,8 +405,14 @@ declare class AvbridgePlayerElement extends HTMLElement {
374
405
  showControls(durationMs?: number): void;
375
406
  private _showControls;
376
407
  private _scheduleHide;
408
+ /** Read the controls-timeout attribute. 0 or negative = never hide.
409
+ * Unset = default 3000ms. */
410
+ private _getControlsTimeout;
377
411
  /** Track whether the last interaction was touch so click handler can skip. */
378
412
  private _lastPointerTypeWasTouch;
413
+ /** True for ~50ms after a touch double-tap was handled, so the
414
+ * synthetic dblclick from the browser doesn't also fire fullscreen. */
415
+ private _touchDoubleTapConsumed;
379
416
  /** True if the event's composed path passes through consumer-slotted
380
417
  * content (toolbar or content-overlay). Slotted content lives in the
381
418
  * light DOM so `.closest(".avp-toolbar-top")` on the event target won't
@@ -387,6 +424,9 @@ declare class AvbridgePlayerElement extends HTMLElement {
387
424
  private _onPointerUp;
388
425
  private _cancelHold;
389
426
  private _doDoubleTap;
427
+ /** Duration of one frame in seconds, derived from diagnostics fps or
428
+ * a 30fps default. Used for frame-step shortcuts (`,` / `.`). */
429
+ private _frameDuration;
390
430
  private _onKeydown;
391
431
  private _clearTimers;
392
432
  get src(): string;
@@ -437,6 +477,8 @@ declare class AvbridgePlayerElement extends HTMLElement {
437
477
  load(): Promise<void>;
438
478
  destroy(): Promise<void>;
439
479
  setAudioTrack(id: number): Promise<void>;
480
+ addSettingsSection(config: SettingsSectionConfig): void;
481
+ removeSettingsSection(id: string): void;
440
482
  setSubtitleTrack(id: number | null): Promise<void>;
441
483
  getDiagnostics(): unknown;
442
484
  canPlayType(mime: string): string;