@rixl/media-core 1.0.1 → 1.1.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 (173) hide show
  1. package/README.md +74 -84
  2. package/dist/chunks/B-QvSqjj.js +1 -0
  3. package/dist/chunks/B2DDE32u.js +1 -0
  4. package/dist/chunks/BZFoe94X.js +1 -0
  5. package/dist/chunks/DMkwQjgA.js +1 -0
  6. package/dist/index.d.ts +2005 -82
  7. package/dist/index.js +6 -73
  8. package/package.json +13 -6
  9. package/dist/api/analytics.d.ts +0 -16
  10. package/dist/api/analytics.js +0 -227
  11. package/dist/api/feed.js +0 -52
  12. package/dist/api/heatmap.d.ts +0 -6
  13. package/dist/api/heatmap.js +0 -28
  14. package/dist/api/hot-segments.d.ts +0 -7
  15. package/dist/api/hot-segments.js +0 -20
  16. package/dist/api/image.js +0 -11
  17. package/dist/api/rixl-client.d.ts +0 -3
  18. package/dist/api/rixl-client.js +0 -14
  19. package/dist/api/upload.d.ts +0 -76
  20. package/dist/api/upload.js +0 -150
  21. package/dist/api/video.js +0 -11
  22. package/dist/lib/analytics-builders.d.ts +0 -59
  23. package/dist/lib/analytics-builders.js +0 -88
  24. package/dist/lib/analytics-initialize.d.ts +0 -1
  25. package/dist/lib/analytics-initialize.js +0 -59
  26. package/dist/lib/analytics-utils.d.ts +0 -43
  27. package/dist/lib/analytics-utils.js +0 -85
  28. package/dist/lib/analytics-wire.js +0 -153
  29. package/dist/lib/buffer-progress.d.ts +0 -20
  30. package/dist/lib/buffer-progress.js +0 -37
  31. package/dist/lib/caption-styles.d.ts +0 -20
  32. package/dist/lib/caption-styles.js +0 -56
  33. package/dist/lib/caption-utils.d.ts +0 -80
  34. package/dist/lib/caption-utils.js +0 -111
  35. package/dist/lib/chapter-utils.d.ts +0 -102
  36. package/dist/lib/chapter-utils.js +0 -147
  37. package/dist/lib/click-disambiguation.d.ts +0 -44
  38. package/dist/lib/click-disambiguation.js +0 -80
  39. package/dist/lib/click-outside.d.ts +0 -68
  40. package/dist/lib/click-outside.js +0 -64
  41. package/dist/lib/colors.d.ts +0 -6
  42. package/dist/lib/colors.js +0 -16
  43. package/dist/lib/content-analytics.d.ts +0 -27
  44. package/dist/lib/content-analytics.js +0 -38
  45. package/dist/lib/environment-effects.d.ts +0 -89
  46. package/dist/lib/environment-effects.js +0 -139
  47. package/dist/lib/feed-autoscroll.d.ts +0 -10
  48. package/dist/lib/feed-autoscroll.js +0 -13
  49. package/dist/lib/feed-container-height.d.ts +0 -7
  50. package/dist/lib/feed-container-height.js +0 -11
  51. package/dist/lib/feed-description-overflow.d.ts +0 -1
  52. package/dist/lib/feed-description-overflow.js +0 -9
  53. package/dist/lib/feed-dwell.d.ts +0 -2
  54. package/dist/lib/feed-dwell.js +0 -8
  55. package/dist/lib/feed-loop.d.ts +0 -10
  56. package/dist/lib/feed-loop.js +0 -21
  57. package/dist/lib/feed-scroll.d.ts +0 -11
  58. package/dist/lib/feed-scroll.js +0 -19
  59. package/dist/lib/feed-transforms.d.ts +0 -5
  60. package/dist/lib/feed-transforms.js +0 -30
  61. package/dist/lib/filter-undefined.js +0 -11
  62. package/dist/lib/fullscreen-utils.d.ts +0 -1
  63. package/dist/lib/fullscreen-utils.js +0 -7
  64. package/dist/lib/hls-buffer-flush.js +0 -46
  65. package/dist/lib/hls-error-recovery.d.ts +0 -35
  66. package/dist/lib/hls-error-recovery.js +0 -75
  67. package/dist/lib/hls-event-handlers.js +0 -61
  68. package/dist/lib/hls-loader.js +0 -23
  69. package/dist/lib/hls-setup.d.ts +0 -72
  70. package/dist/lib/hls-setup.js +0 -144
  71. package/dist/lib/hls-utils.d.ts +0 -31
  72. package/dist/lib/hls-utils.js +0 -34
  73. package/dist/lib/image-render.d.ts +0 -29
  74. package/dist/lib/image-render.js +0 -63
  75. package/dist/lib/keyboard-config.d.ts +0 -66
  76. package/dist/lib/keyboard-config.js +0 -127
  77. package/dist/lib/keyboard-utils.d.ts +0 -4
  78. package/dist/lib/keyboard-utils.js +0 -11
  79. package/dist/lib/lifecycle-effects.d.ts +0 -67
  80. package/dist/lib/lifecycle-effects.js +0 -78
  81. package/dist/lib/loading-debounce.d.ts +0 -26
  82. package/dist/lib/loading-debounce.js +0 -38
  83. package/dist/lib/numbers.d.ts +0 -6
  84. package/dist/lib/numbers.js +0 -36
  85. package/dist/lib/pip-utils.d.ts +0 -16
  86. package/dist/lib/pip-utils.js +0 -32
  87. package/dist/lib/playback-handlers.d.ts +0 -33
  88. package/dist/lib/playback-handlers.js +0 -51
  89. package/dist/lib/playback-slider.d.ts +0 -23
  90. package/dist/lib/playback-slider.js +0 -24
  91. package/dist/lib/player-labels.d.ts +0 -23
  92. package/dist/lib/player-labels.js +0 -132
  93. package/dist/lib/player-menu-labels.d.ts +0 -80
  94. package/dist/lib/player-menu-labels.js +0 -829
  95. package/dist/lib/player-ui-actions.d.ts +0 -10
  96. package/dist/lib/player-ui-actions.js +0 -58
  97. package/dist/lib/progress-fraction.d.ts +0 -11
  98. package/dist/lib/progress-fraction.js +0 -20
  99. package/dist/lib/progress-persist.d.ts +0 -28
  100. package/dist/lib/progress-persist.js +0 -54
  101. package/dist/lib/segment-analytics.d.ts +0 -20
  102. package/dist/lib/segment-analytics.js +0 -29
  103. package/dist/lib/subtitle-logic.d.ts +0 -31
  104. package/dist/lib/subtitle-logic.js +0 -75
  105. package/dist/lib/subtitle-utils.d.ts +0 -16
  106. package/dist/lib/subtitle-utils.js +0 -37
  107. package/dist/lib/time.d.ts +0 -1
  108. package/dist/lib/time.js +0 -17
  109. package/dist/lib/track-persistence.d.ts +0 -4
  110. package/dist/lib/track-persistence.js +0 -35
  111. package/dist/lib/video-preload-config.d.ts +0 -34
  112. package/dist/lib/video-preload-config.js +0 -41
  113. package/dist/lib/video-spinner.d.ts +0 -25
  114. package/dist/lib/video-spinner.js +0 -35
  115. package/dist/lib/video-stats.d.ts +0 -28
  116. package/dist/lib/video-stats.js +0 -44
  117. package/dist/lib/volume-utils.d.ts +0 -59
  118. package/dist/lib/volume-utils.js +0 -55
  119. package/dist/progressbar.d.ts +0 -10
  120. package/dist/progressbar.js +0 -123
  121. package/dist/store/analytics-context-store.d.ts +0 -4
  122. package/dist/store/analytics-context-store.js +0 -13
  123. package/dist/store/analytics-device-store.d.ts +0 -8
  124. package/dist/store/analytics-device-store.js +0 -44
  125. package/dist/store/analytics-session-store.d.ts +0 -8
  126. package/dist/store/analytics-session-store.js +0 -40
  127. package/dist/store/analytics-store.d.ts +0 -30
  128. package/dist/store/analytics-store.js +0 -88
  129. package/dist/store/controls-store.d.ts +0 -16
  130. package/dist/store/controls-store.js +0 -53
  131. package/dist/store/feed-cache-store.d.ts +0 -24
  132. package/dist/store/feed-cache-store.js +0 -165
  133. package/dist/store/feed-interaction-store.d.ts +0 -17
  134. package/dist/store/feed-interaction-store.js +0 -32
  135. package/dist/store/feed-store.d.ts +0 -16
  136. package/dist/store/feed-store.js +0 -38
  137. package/dist/store/generic-store.js +0 -24
  138. package/dist/store/image-store.d.ts +0 -2
  139. package/dist/store/image-store.js +0 -8
  140. package/dist/store/player-container-element-store.d.ts +0 -10
  141. package/dist/store/player-container-element-store.js +0 -38
  142. package/dist/store/player-global-actions.js +0 -53
  143. package/dist/store/player-media-element-store.d.ts +0 -10
  144. package/dist/store/player-media-element-store.js +0 -38
  145. package/dist/store/player-store-actions.js +0 -197
  146. package/dist/store/player-store.d.ts +0 -94
  147. package/dist/store/player-store.js +0 -127
  148. package/dist/store/player-track-actions.js +0 -44
  149. package/dist/store/postStore.d.ts +0 -20
  150. package/dist/store/postStore.js +0 -112
  151. package/dist/store/settings/constants.d.ts +0 -15
  152. package/dist/store/settings/constants.js +0 -47
  153. package/dist/store/settings/defaults.d.ts +0 -2
  154. package/dist/store/settings/defaults.js +0 -24
  155. package/dist/store/settings/storage.d.ts +0 -3
  156. package/dist/store/settings/storage.js +0 -28
  157. package/dist/store/settings/store.d.ts +0 -32
  158. package/dist/store/settings/store.js +0 -59
  159. package/dist/store/settings/types.d.ts +0 -27
  160. package/dist/store/settings/validation.d.ts +0 -7
  161. package/dist/store/settings/validation.js +0 -55
  162. package/dist/store/settings-menu-store.d.ts +0 -19
  163. package/dist/store/settings-menu-store.js +0 -30
  164. package/dist/store/video-store.d.ts +0 -2
  165. package/dist/store/video-store.js +0 -8
  166. package/dist/types/analytics.d.ts +0 -113
  167. package/dist/types/feed.d.ts +0 -21
  168. package/dist/types/file.d.ts +0 -12
  169. package/dist/types/heatmap.d.ts +0 -5
  170. package/dist/types/hot-segments.d.ts +0 -9
  171. package/dist/types/image.d.ts +0 -9
  172. package/dist/types/uuid.d.ts +0 -1
  173. package/dist/types/video.d.ts +0 -22
@@ -1,147 +0,0 @@
1
- // src/lib/chapter-utils.ts
2
- /**
3
- * Check if a single chapter is within the video duration
4
- * @param chapter - The chapter to validate
5
- * @param videoDuration - The total video duration in seconds
6
- * @param tolerance - Tolerance in seconds for rounding errors (default: 1)
7
- * @returns Whether the chapter is within valid bounds
8
- */
9
- function isChapterWithinDuration(chapter, videoDuration, tolerance = 1) {
10
- return chapter.start_time_sec <= videoDuration + tolerance && chapter.end_time_sec <= videoDuration + tolerance;
11
- }
12
- /**
13
- * Validate chapters for use with chaptered progress bar
14
- * Returns false if:
15
- * - chapters is undefined or empty
16
- * - less than 2 chapters (need multiple for chaptered bar)
17
- * - any chapter exceeds video duration
18
- *
19
- * @param chapters - Array of chapters to validate
20
- * @param videoDuration - The total video duration in seconds
21
- * @param tolerance - Tolerance in seconds for rounding errors (default: 1)
22
- * @returns Whether chapters are valid for chaptered progress bar
23
- */
24
- function validateChapters(chapters, videoDuration, tolerance = 1) {
25
- if (!chapters || chapters.length <= 1) return false;
26
- if (videoDuration <= 0) return false;
27
- return chapters.every((chapter) => isChapterWithinDuration(chapter, videoDuration, tolerance));
28
- }
29
- /**
30
- * Calculate chapter end times based on next chapter start or video duration
31
- */
32
- function calculateChaptersWithEnd(chapters, duration) {
33
- return chapters.map((chapter, index) => {
34
- const nextChapter = chapters[index + 1];
35
- const endTime = nextChapter ? nextChapter.start_time_sec : duration;
36
- return {
37
- ...chapter,
38
- endTime,
39
- durationSec: endTime - chapter.start_time_sec
40
- };
41
- });
42
- }
43
- function sliceHeatmapData({ heatmapData, chapterStartTime, chapterEndTime, videoDuration }) {
44
- if (!heatmapData?.length || videoDuration <= 0) return [];
45
- const startPercent = chapterStartTime / videoDuration;
46
- const endPercent = chapterEndTime / videoDuration;
47
- const startIndex = Math.floor(startPercent * heatmapData.length);
48
- const endIndex = Math.ceil(endPercent * heatmapData.length);
49
- return heatmapData.slice(startIndex, Math.max(startIndex + 1, endIndex));
50
- }
51
- /**
52
- * Calculate all heatmap slices for chapters
53
- */
54
- function calculateHeatmapSlices(heatmapData, chapters, duration) {
55
- if (!heatmapData?.length) return chapters.map(() => []);
56
- return chapters.map((chapter) => sliceHeatmapData({
57
- heatmapData,
58
- chapterStartTime: chapter.start_time_sec,
59
- chapterEndTime: chapter.endTime,
60
- videoDuration: duration
61
- }));
62
- }
63
- /**
64
- * Calculate global max value from heatmap data for consistent normalization
65
- */
66
- function calculateGlobalMax(heatmapData) {
67
- if (!heatmapData?.length) return 0;
68
- return Math.max(...heatmapData);
69
- }
70
- /**
71
- * Calculate progress percentage within a chapter segment
72
- * @returns 0-100 percentage
73
- */
74
- function calculateSegmentProgress(currentPosition, chapterStart, chapterEnd) {
75
- if (currentPosition <= chapterStart) return 0;
76
- if (currentPosition >= chapterEnd) return 100;
77
- return (currentPosition - chapterStart) / (chapterEnd - chapterStart) * 100;
78
- }
79
- function calculateSegmentBufferRanges({ globalBufferRanges, chapterStart, chapterEnd, videoDuration }) {
80
- if (chapterEnd - chapterStart <= 0 || videoDuration <= 0) return [];
81
- const chapterStartPercent = chapterStart / videoDuration * 100;
82
- const chapterEndPercent = chapterEnd / videoDuration * 100;
83
- return globalBufferRanges.map((range) => {
84
- if (range.end <= chapterStartPercent || range.start >= chapterEndPercent) return null;
85
- const clampedStart = Math.max(range.start, chapterStartPercent);
86
- const clampedEnd = Math.min(range.end, chapterEndPercent);
87
- return {
88
- start: (clampedStart - chapterStartPercent) / (chapterEndPercent - chapterStartPercent) * 100,
89
- end: (clampedEnd - chapterStartPercent) / (chapterEndPercent - chapterStartPercent) * 100
90
- };
91
- }).filter((r) => r !== null);
92
- }
93
- /**
94
- * Calculate flex grow ratio for a chapter based on its duration
95
- */
96
- function calculateFlexGrow(chapterDuration, videoDuration) {
97
- if (videoDuration <= 0) return 0;
98
- return chapterDuration / videoDuration;
99
- }
100
- function getCurrentPosition({ isDragging, progress, duration, currentTime }) {
101
- return isDragging ? progress * duration : currentTime;
102
- }
103
- /**
104
- * Find which segment is hovered based on mouse X position
105
- * @param segments - Array of segment DOM elements
106
- * @param mouseX - Mouse X coordinate (clientX)
107
- * @returns Index of hovered segment, or null if none
108
- */
109
- function findHoveredSegmentIndex(segments, mouseX) {
110
- for (let i = 0; i < segments.length; i++) {
111
- const segRef = segments[i];
112
- if (!segRef) continue;
113
- const segRect = segRef.getBoundingClientRect();
114
- if (mouseX >= segRect.left && mouseX <= segRect.right) return i;
115
- }
116
- return null;
117
- }
118
- /**
119
- * Start time of the first chapter after the current position, or null when
120
- * already in the last chapter. Mirrors React useChapterNavigation.
121
- */
122
- function getNextChapterStart(chapters, currentTime) {
123
- if (!chapters?.length) return null;
124
- const next = chapters.find((ch) => ch.start_time_sec > currentTime);
125
- return next ? next.start_time_sec : null;
126
- }
127
- /** Seconds into a chapter after which Shift+P restarts it instead of jumping back. */
128
- const CHAPTER_RESTART_THRESHOLD_SECONDS = 3;
129
- /**
130
- * Target for "previous chapter": restart the current chapter when more than
131
- * 3s in (or when it is the first), otherwise jump to the previous chapter.
132
- * Mirrors React useChapterNavigation.
133
- */
134
- function getPrevChapterTarget(chapters, currentTime) {
135
- if (!chapters?.length) return null;
136
- let currentChapterIndex = -1;
137
- for (let i = chapters.length - 1; i >= 0; i--) if (chapters[i].start_time_sec <= currentTime) {
138
- currentChapterIndex = i;
139
- break;
140
- }
141
- if (currentChapterIndex < 0) return null;
142
- const currentChapter = chapters[currentChapterIndex];
143
- if (currentTime - currentChapter.start_time_sec > CHAPTER_RESTART_THRESHOLD_SECONDS || currentChapterIndex === 0) return currentChapter.start_time_sec;
144
- return chapters[currentChapterIndex - 1].start_time_sec;
145
- }
146
-
147
- export { calculateChaptersWithEnd, calculateFlexGrow, calculateGlobalMax, calculateHeatmapSlices, calculateSegmentBufferRanges, calculateSegmentProgress, findHoveredSegmentIndex, getCurrentPosition, getNextChapterStart, getPrevChapterTarget, isChapterWithinDuration, sliceHeatmapData, validateChapters };
@@ -1,44 +0,0 @@
1
- /**
2
- * Delay in milliseconds for single-click detection
3
- * Used to distinguish between single click (toggle play) and double click (fullscreen)
4
- */
5
- export declare const SINGLE_CLICK_DELAY_MS = 250;
6
- /**
7
- * Maximum time between clicks to be considered a double-click
8
- */
9
- export declare const DOUBLE_CLICK_THRESHOLD_MS = 400;
10
- export interface ClickDisambiguationState {
11
- singleClickTimerId: ReturnType<typeof setTimeout> | null;
12
- lastClickTime: number;
13
- }
14
- /**
15
- * Create initial click disambiguation state
16
- */
17
- export declare function createClickDisambiguationState(): ClickDisambiguationState;
18
- export interface HandleClickOptions {
19
- state: ClickDisambiguationState;
20
- hideUI: boolean;
21
- allowFullscreen: boolean;
22
- isExcludedTarget: boolean;
23
- onTogglePlay: () => void;
24
- }
25
- /**
26
- * Handle a click event with disambiguation logic
27
- * Returns true if the click was handled, false if ignored
28
- */
29
- export declare function handleClick(options: HandleClickOptions): boolean;
30
- export interface HandleDoubleClickOptions {
31
- state: ClickDisambiguationState;
32
- allowFullscreen: boolean;
33
- isExcludedTarget: boolean;
34
- onToggleFullscreen: () => void;
35
- }
36
- /**
37
- * Handle a double-click event
38
- * Returns true if the double-click was handled, false if ignored
39
- */
40
- export declare function handleDoubleClick(options: HandleDoubleClickOptions): boolean;
41
- /**
42
- * Clean up click disambiguation state (clear any pending timers)
43
- */
44
- export declare function cleanupClickDisambiguation(state: ClickDisambiguationState): void;
@@ -1,80 +0,0 @@
1
- // src/lib/click-disambiguation.ts
2
- /**
3
- * Delay in milliseconds for single-click detection
4
- * Used to distinguish between single click (toggle play) and double click (fullscreen)
5
- */
6
- const SINGLE_CLICK_DELAY_MS = 250;
7
- /**
8
- * Maximum time between clicks to be considered a double-click
9
- */
10
- const DOUBLE_CLICK_THRESHOLD_MS = 400;
11
- /**
12
- * Create initial click disambiguation state
13
- */
14
- function createClickDisambiguationState() {
15
- return {
16
- singleClickTimerId: null,
17
- lastClickTime: 0
18
- };
19
- }
20
- /**
21
- * Clear any pending single-click timer
22
- */
23
- function clearPendingSingleClick(state) {
24
- if (state.singleClickTimerId !== null) {
25
- clearTimeout(state.singleClickTimerId);
26
- state.singleClickTimerId = null;
27
- }
28
- }
29
- /**
30
- * Schedule a single-click action after a delay
31
- * Allows time for a potential double-click to cancel it
32
- */
33
- function scheduleSingleClick(state, onSingleClick) {
34
- clearPendingSingleClick(state);
35
- state.singleClickTimerId = setTimeout(() => {
36
- onSingleClick();
37
- state.singleClickTimerId = null;
38
- }, 250);
39
- }
40
- /**
41
- * Handle a click event with disambiguation logic
42
- * Returns true if the click was handled, false if ignored
43
- */
44
- function handleClick(options) {
45
- const { state, hideUI, allowFullscreen, isExcludedTarget, onTogglePlay } = options;
46
- if (hideUI || isExcludedTarget) return false;
47
- const now = Date.now();
48
- const timeSinceLastClick = now - state.lastClickTime;
49
- state.lastClickTime = now;
50
- if (timeSinceLastClick > 0 && timeSinceLastClick < 400) {
51
- clearPendingSingleClick(state);
52
- return true;
53
- }
54
- if (!allowFullscreen) {
55
- onTogglePlay();
56
- return true;
57
- }
58
- scheduleSingleClick(state, onTogglePlay);
59
- return true;
60
- }
61
- /**
62
- * Handle a double-click event
63
- * Returns true if the double-click was handled, false if ignored
64
- */
65
- function handleDoubleClick(options) {
66
- const { state, allowFullscreen, isExcludedTarget, onToggleFullscreen } = options;
67
- if (!allowFullscreen || isExcludedTarget) return false;
68
- clearPendingSingleClick(state);
69
- state.lastClickTime = 0;
70
- onToggleFullscreen();
71
- return true;
72
- }
73
- /**
74
- * Clean up click disambiguation state (clear any pending timers)
75
- */
76
- function cleanupClickDisambiguation(state) {
77
- clearPendingSingleClick(state);
78
- }
79
-
80
- export { DOUBLE_CLICK_THRESHOLD_MS, SINGLE_CLICK_DELAY_MS, cleanupClickDisambiguation, clearPendingSingleClick, createClickDisambiguationState, handleClick, handleDoubleClick, scheduleSingleClick };
@@ -1,68 +0,0 @@
1
- /**
2
- * Generic "click outside" detection with an optional exclusion attribute.
3
- *
4
- * Use case: a settings menu that should close when the user clicks outside it,
5
- * but NOT when the user clicks the button that opens it (otherwise the button
6
- * both opens and immediately closes the menu in the same pointer cycle).
7
- *
8
- * Pure, DOM-only, framework-agnostic. React/Lit/Vue wrappers call
9
- * `bindClickOutside` from their lifecycle hooks.
10
- */
11
- export interface ClickOutsideOptions {
12
- /**
13
- * Function that returns the "inside" element. Called on every pointer event,
14
- * so the caller can pass a ref-like getter that may be null until the
15
- * referenced element is mounted.
16
- */
17
- getContainer: () => HTMLElement | null;
18
- /**
19
- * Optional attribute name (e.g. `"data-settings-button"`). Any pointer event
20
- * whose target is inside an element carrying this attribute is treated as
21
- * "inside" and does NOT trigger `onOutsideClick`.
22
- *
23
- * This is what lets the button that toggles the menu coexist with a
24
- * close-on-outside-click listener: the button's own clicks are ignored here,
25
- * letting the button's own onClick decide whether to toggle.
26
- */
27
- excludeAttribute?: string;
28
- /**
29
- * Invoked when a pointer event lands outside both the container and (if
30
- * configured) any element carrying `excludeAttribute`.
31
- */
32
- onOutsideClick: (event: MouseEvent) => void;
33
- }
34
- /**
35
- * Returns `true` when the given event target is inside an element carrying
36
- * the supplied attribute. When `attribute` is undefined, always returns false.
37
- */
38
- export declare function isInsideExcludedElement(target: EventTarget | null, attribute: string | undefined): boolean;
39
- /**
40
- * Returns `true` when the event lands outside both the container and any
41
- * excluded element.
42
- *
43
- * Checks the composed path as well as the plain target: document-level
44
- * listeners see shadow-DOM events retargeted to the host, and
45
- * `document.querySelector` cannot see into shadow roots — the composed path
46
- * is the only reliable record of where the click really landed.
47
- */
48
- export declare function isClickOutside(event: MouseEvent, container: HTMLElement | null, excludeAttribute: string | undefined): boolean;
49
- /**
50
- * Bind a `mousedown` listener on the document that invokes `onOutsideClick`
51
- * whenever the event lands outside the container and outside any element
52
- * carrying `excludeAttribute`.
53
- *
54
- * Returns a teardown function that removes the listener.
55
- *
56
- * The container is looked up via `getContainer()` on every event so the caller
57
- * can wire it to a ref that may be null at bind time.
58
- */
59
- export declare function bindClickOutside({ getContainer, excludeAttribute, onOutsideClick }: ClickOutsideOptions): () => void;
60
- /**
61
- * Bind a `visibilitychange` listener that fires `onHidden` when the document
62
- * is hidden. Returns a teardown function.
63
- *
64
- * Settings menus use this to close themselves when the user tabs away.
65
- * Lives next to `bindClickOutside` because the two are almost always wired
66
- * together for "dismissible overlay" behaviour.
67
- */
68
- export declare function bindDocumentHidden(onHidden: () => void): () => void;
@@ -1,64 +0,0 @@
1
- // src/lib/click-outside.ts
2
- /**
3
- * Returns `true` when the given event target is inside an element carrying
4
- * the supplied attribute. When `attribute` is undefined, always returns false.
5
- */
6
- function isInsideExcludedElement(target, attribute) {
7
- if (!attribute) return false;
8
- if (!(target instanceof Node)) return false;
9
- const excluded = document.querySelector(`[${attribute}]`);
10
- return Boolean(excluded?.contains(target));
11
- }
12
- /**
13
- * Returns `true` when the event lands outside both the container and any
14
- * excluded element.
15
- *
16
- * Checks the composed path as well as the plain target: document-level
17
- * listeners see shadow-DOM events retargeted to the host, and
18
- * `document.querySelector` cannot see into shadow roots — the composed path
19
- * is the only reliable record of where the click really landed.
20
- */
21
- function isClickOutside(event, container, excludeAttribute) {
22
- if (!container) return false;
23
- const target = event.target;
24
- if (!(target instanceof Node)) return false;
25
- const path = event.composedPath();
26
- if (path.includes(container) || container.contains(target)) return false;
27
- if (excludeAttribute && path.some((node) => node instanceof Element && node.hasAttribute(excludeAttribute))) return false;
28
- if (isInsideExcludedElement(target, excludeAttribute)) return false;
29
- return true;
30
- }
31
- /**
32
- * Bind a `mousedown` listener on the document that invokes `onOutsideClick`
33
- * whenever the event lands outside the container and outside any element
34
- * carrying `excludeAttribute`.
35
- *
36
- * Returns a teardown function that removes the listener.
37
- *
38
- * The container is looked up via `getContainer()` on every event so the caller
39
- * can wire it to a ref that may be null at bind time.
40
- */
41
- function bindClickOutside({ getContainer, excludeAttribute, onOutsideClick }) {
42
- const handler = (event) => {
43
- if (isClickOutside(event, getContainer(), excludeAttribute)) onOutsideClick(event);
44
- };
45
- document.addEventListener("mousedown", handler);
46
- return () => document.removeEventListener("mousedown", handler);
47
- }
48
- /**
49
- * Bind a `visibilitychange` listener that fires `onHidden` when the document
50
- * is hidden. Returns a teardown function.
51
- *
52
- * Settings menus use this to close themselves when the user tabs away.
53
- * Lives next to `bindClickOutside` because the two are almost always wired
54
- * together for "dismissible overlay" behaviour.
55
- */
56
- function bindDocumentHidden(onHidden) {
57
- const handler = () => {
58
- if (document.visibilityState === "hidden") onHidden();
59
- };
60
- document.addEventListener("visibilitychange", handler);
61
- return () => document.removeEventListener("visibilitychange", handler);
62
- }
63
-
64
- export { bindClickOutside, bindDocumentHidden, isClickOutside, isInsideExcludedElement };
@@ -1,6 +0,0 @@
1
- export declare function hexToRGB(hex: string): {
2
- r: number;
3
- g: number;
4
- b: number;
5
- };
6
- export declare function hexToRGBAString(hex: string, alpha?: number): string;
@@ -1,16 +0,0 @@
1
- // src/lib/colors.ts
2
- function hexToRGB(hex) {
3
- hex = hex.replace("#", "");
4
- return {
5
- r: parseInt(hex.substring(0, 2), 16),
6
- g: parseInt(hex.substring(2, 4), 16),
7
- b: parseInt(hex.substring(4, 6), 16)
8
- };
9
- }
10
- function hexToRGBAString(hex, alpha = 100) {
11
- const { r, g, b } = hexToRGB(hex);
12
- alpha /= 100;
13
- return `rgba(${r}, ${g}, ${b}, ${alpha})`;
14
- }
15
-
16
- export { hexToRGB, hexToRGBAString };
@@ -1,27 +0,0 @@
1
- import { BuildEventParams } from "./analytics-utils.js";
2
- import { ContentType, Segment, ViewType } from "../types/analytics.js";
3
- export interface VideoAnalyticsPayload {
4
- videoPositionMs?: number;
5
- videoTotalDurationMs?: number;
6
- segments?: Segment[];
7
- }
8
- export interface VideoSnapshot {
9
- currentTime: number;
10
- duration: number;
11
- }
12
- export interface BuildVideoAnalyticsPayloadParams {
13
- contentType: ContentType;
14
- viewType: ViewType;
15
- video?: VideoSnapshot | null;
16
- playerId?: string;
17
- }
18
- /**
19
- * Build and POST a single content-view event.
20
- */
21
- export declare function postContentEvent(params: BuildEventParams): void;
22
- /**
23
- * Compute the video position / duration / segments to attach to a
24
- * content-view event. Returns an empty payload for non-video content
25
- * or when the video snapshot is unavailable.
26
- */
27
- export declare function buildVideoAnalyticsPayload({ contentType, viewType, video, playerId }: BuildVideoAnalyticsPayloadParams): VideoAnalyticsPayload;
@@ -1,38 +0,0 @@
1
- import { buildContentViewEvent, secondsToMs, shouldIncludeSegments } from "./analytics-utils.js";
2
- import { postAnalytics } from "../api/analytics.js";
3
- import { analyticsStoreActions } from "../store/analytics-store.js";
4
-
5
- // src/lib/content-analytics.ts
6
- /**
7
- * Pure content-analytics helpers extracted from useContentAnalytics.
8
- *
9
- * - postContentEvent: build a content-view event and POST it.
10
- * - buildVideoAnalyticsPayload: derive the video position/duration/segments
11
- * fields included in a content-view event. Reads currentTime/duration off
12
- * a video-shaped snapshot; no React refs, no DOM dependencies beyond
13
- * HTMLVideoElement properties.
14
- */
15
- /**
16
- * Build and POST a single content-view event.
17
- */
18
- function postContentEvent(params) {
19
- postAnalytics([buildContentViewEvent(params)]);
20
- }
21
- /**
22
- * Compute the video position / duration / segments to attach to a
23
- * content-view event. Returns an empty payload for non-video content
24
- * or when the video snapshot is unavailable.
25
- */
26
- function buildVideoAnalyticsPayload({ contentType, viewType, video, playerId }) {
27
- if (contentType !== "video" || !video) return {};
28
- const videoPositionMs = secondsToMs(video.currentTime);
29
- const videoTotalDurationMs = secondsToMs(video.duration);
30
- const segments = playerId && shouldIncludeSegments(viewType) ? analyticsStoreActions.getSegmentsWithCurrent(playerId, videoPositionMs) : void 0;
31
- return {
32
- videoPositionMs,
33
- videoTotalDurationMs,
34
- segments: segments && segments.length > 0 ? segments : void 0
35
- };
36
- }
37
-
38
- export { buildVideoAnalyticsPayload, postContentEvent };
@@ -1,89 +0,0 @@
1
- import { Viewport } from "./caption-utils.js";
2
- /**
3
- * Pure helpers for video-element environment effects.
4
- *
5
- * "Environment effects" = things the player has to keep in sync with the
6
- * surrounding document/window: fullscreen state, picture-in-picture state,
7
- * tab visibility, viewport size, and the volume/mute state derived from
8
- * player + user-interaction inputs.
9
- *
10
- * Everything in this module is framework-agnostic. React/Lit/Vue wrappers
11
- * supply the DOM listeners and the store-update callbacks; this module
12
- * supplies the predicates, the state derivations, and the listener factories.
13
- */
14
- /**
15
- * Bind the `document` `fullscreenchange` event to a callback that receives the
16
- * current fullscreen state.
17
- *
18
- * Returns a teardown function that removes the listener.
19
- */
20
- export declare function bindFullscreenChangeListener(onChange: (isFullscreen: boolean) => void): () => void;
21
- /**
22
- * Bind `enterpictureinpicture` and `leavepictureinpicture` events on a
23
- * video element to a callback that receives the current PiP state.
24
- *
25
- * Returns a teardown function.
26
- */
27
- export declare function bindPictureInPictureChangeListener(videoElement: HTMLVideoElement | null, onChange: (isPip: boolean) => void): () => void;
28
- export interface PipReentryOptions {
29
- videoElement: HTMLVideoElement | null;
30
- isCurrentlyInPip: boolean;
31
- }
32
- /**
33
- * Decide whether to re-request PiP when the document becomes hidden.
34
- *
35
- * Returns `true` when the caller should re-enter PiP on the supplied
36
- * video element. The actual `requestPictureInPicture()` call is left to the
37
- * caller (since it returns a promise and the caller chooses how to handle
38
- * errors).
39
- */
40
- export declare function shouldReenterPictureInPicture({ videoElement, isCurrentlyInPip }: PipReentryOptions): boolean;
41
- /**
42
- * Bind a `visibilitychange` handler that re-enters PiP whenever the document
43
- * becomes hidden and the player believes itself to be in PiP but the browser
44
- * has dropped out.
45
- *
46
- * Returns a teardown function.
47
- */
48
- export declare function bindPipVisibilityReentry(getOptions: () => PipReentryOptions, onError?: (error: unknown) => void): () => void;
49
- export interface AppliedVolumeSync {
50
- /** The `muted` value that should be assigned to the video element. */
51
- muted: boolean;
52
- /** The `volume` value that should be assigned, or `null` if not changing. */
53
- volume: number | null;
54
- /** When non-null, the store's current `volume` should be updated to this value. */
55
- storeVolumeUpdate: number | null;
56
- }
57
- export interface DeriveVolumeSyncOptions {
58
- /** Hard mute applied by the consumer (e.g. theme override). */
59
- forcedMuted: boolean;
60
- /** The player's persisted `muted` state. */
61
- playerMuted: boolean;
62
- /** Whether the user has interacted with the player yet. */
63
- playerInteracted: boolean;
64
- /** The player's persisted `volume`. */
65
- playerVolume: number;
66
- /** The player's persisted `previousVolume` (used to restore on unmute). */
67
- playerPreviousVolume: number;
68
- }
69
- /**
70
- * Pure derivation of the values that should be written to the video element
71
- * (and back to the store) given the current player state.
72
- *
73
- * Mirrors the behaviour previously implemented inside `useVolumeEffect`:
74
- *
75
- * 1. When the caller forces mute, nothing else is touched.
76
- * 2. Otherwise, `shouldBeMuted` decides the effective mute state.
77
- * 3. When unmuted, `getRestoredVolume` resolves the volume to assign, and the
78
- * store is updated only when the current persisted volume is `0`.
79
- */
80
- export declare function deriveVolumeSync({ forcedMuted, playerMuted, playerInteracted, playerVolume, playerPreviousVolume }: DeriveVolumeSyncOptions): AppliedVolumeSync | null;
81
- /**
82
- * Observe a container element's viewport size, invoking the callback with the
83
- * current `{width, height}` whenever it changes (via `ResizeObserver`) or
84
- * the window resizes.
85
- *
86
- * Returns a teardown function.
87
- */
88
- export declare function observeViewport(container: HTMLElement | null, onChange: (viewport: Viewport) => void): () => void;
89
- export type { Viewport };