@editframe/elements 0.26.3-beta.0 → 0.26.4-beta.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 (132) hide show
  1. package/package.json +2 -2
  2. package/scripts/build-css.js +3 -3
  3. package/tsdown.config.ts +1 -1
  4. package/src/elements/ContextProxiesController.ts +0 -124
  5. package/src/elements/CrossUpdateController.ts +0 -22
  6. package/src/elements/EFAudio.browsertest.ts +0 -706
  7. package/src/elements/EFAudio.ts +0 -56
  8. package/src/elements/EFCaptions.browsertest.ts +0 -1960
  9. package/src/elements/EFCaptions.ts +0 -823
  10. package/src/elements/EFImage.browsertest.ts +0 -120
  11. package/src/elements/EFImage.ts +0 -113
  12. package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
  13. package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
  14. package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
  15. package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
  16. package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
  17. package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
  18. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
  19. package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
  20. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
  21. package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
  22. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
  23. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
  24. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
  25. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
  26. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
  27. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
  28. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
  29. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
  30. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
  31. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
  32. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
  33. package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
  34. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
  35. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
  36. package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
  37. package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
  38. package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
  39. package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
  40. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
  41. package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
  42. package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
  43. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
  44. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
  45. package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
  46. package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
  47. package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
  48. package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
  49. package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
  50. package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
  51. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
  52. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
  53. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
  54. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
  55. package/src/elements/EFMedia.browsertest.ts +0 -872
  56. package/src/elements/EFMedia.ts +0 -341
  57. package/src/elements/EFSourceMixin.ts +0 -60
  58. package/src/elements/EFSurface.browsertest.ts +0 -151
  59. package/src/elements/EFSurface.ts +0 -142
  60. package/src/elements/EFTemporal.browsertest.ts +0 -215
  61. package/src/elements/EFTemporal.ts +0 -800
  62. package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
  63. package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
  64. package/src/elements/EFThumbnailStrip.ts +0 -906
  65. package/src/elements/EFTimegroup.browsertest.ts +0 -934
  66. package/src/elements/EFTimegroup.ts +0 -882
  67. package/src/elements/EFVideo.browsertest.ts +0 -1482
  68. package/src/elements/EFVideo.ts +0 -564
  69. package/src/elements/EFWaveform.ts +0 -547
  70. package/src/elements/FetchContext.browsertest.ts +0 -401
  71. package/src/elements/FetchMixin.ts +0 -38
  72. package/src/elements/SampleBuffer.ts +0 -94
  73. package/src/elements/TargetController.browsertest.ts +0 -230
  74. package/src/elements/TargetController.ts +0 -224
  75. package/src/elements/TimegroupController.ts +0 -26
  76. package/src/elements/durationConverter.ts +0 -35
  77. package/src/elements/parseTimeToMs.ts +0 -9
  78. package/src/elements/printTaskStatus.ts +0 -16
  79. package/src/elements/renderTemporalAudio.ts +0 -108
  80. package/src/elements/updateAnimations.browsertest.ts +0 -1884
  81. package/src/elements/updateAnimations.ts +0 -217
  82. package/src/elements/util.ts +0 -24
  83. package/src/gui/ContextMixin.browsertest.ts +0 -860
  84. package/src/gui/ContextMixin.ts +0 -562
  85. package/src/gui/Controllable.browsertest.ts +0 -258
  86. package/src/gui/Controllable.ts +0 -41
  87. package/src/gui/EFConfiguration.ts +0 -40
  88. package/src/gui/EFControls.browsertest.ts +0 -389
  89. package/src/gui/EFControls.ts +0 -195
  90. package/src/gui/EFDial.browsertest.ts +0 -84
  91. package/src/gui/EFDial.ts +0 -172
  92. package/src/gui/EFFilmstrip.browsertest.ts +0 -712
  93. package/src/gui/EFFilmstrip.ts +0 -1349
  94. package/src/gui/EFFitScale.ts +0 -152
  95. package/src/gui/EFFocusOverlay.ts +0 -79
  96. package/src/gui/EFPause.browsertest.ts +0 -202
  97. package/src/gui/EFPause.ts +0 -73
  98. package/src/gui/EFPlay.browsertest.ts +0 -202
  99. package/src/gui/EFPlay.ts +0 -73
  100. package/src/gui/EFPreview.ts +0 -74
  101. package/src/gui/EFResizableBox.browsertest.ts +0 -79
  102. package/src/gui/EFResizableBox.ts +0 -898
  103. package/src/gui/EFScrubber.ts +0 -151
  104. package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
  105. package/src/gui/EFTimeDisplay.ts +0 -55
  106. package/src/gui/EFToggleLoop.ts +0 -35
  107. package/src/gui/EFTogglePlay.ts +0 -70
  108. package/src/gui/EFWorkbench.ts +0 -115
  109. package/src/gui/PlaybackController.ts +0 -527
  110. package/src/gui/TWMixin.css +0 -6
  111. package/src/gui/TWMixin.ts +0 -61
  112. package/src/gui/TargetOrContextMixin.ts +0 -185
  113. package/src/gui/currentTimeContext.ts +0 -5
  114. package/src/gui/durationContext.ts +0 -3
  115. package/src/gui/efContext.ts +0 -6
  116. package/src/gui/fetchContext.ts +0 -5
  117. package/src/gui/focusContext.ts +0 -7
  118. package/src/gui/focusedElementContext.ts +0 -5
  119. package/src/gui/playingContext.ts +0 -5
  120. package/src/otel/BridgeSpanExporter.ts +0 -150
  121. package/src/otel/setupBrowserTracing.ts +0 -73
  122. package/src/otel/tracingHelpers.ts +0 -251
  123. package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
  124. package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
  125. package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
  126. package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
  127. package/src/transcoding/types/index.ts +0 -312
  128. package/src/transcoding/utils/MediaUtils.ts +0 -63
  129. package/src/transcoding/utils/UrlGenerator.ts +0 -68
  130. package/src/transcoding/utils/constants.ts +0 -36
  131. package/src/utils/LRUCache.test.ts +0 -274
  132. package/src/utils/LRUCache.ts +0 -696
@@ -1,585 +0,0 @@
1
- import { html, render } from "lit";
2
- import { beforeEach, describe } from "vitest";
3
-
4
- import { test as baseTest } from "../../test/useMSW.js";
5
- import "../gui/EFPreview.js";
6
- import "../gui/EFWorkbench.js";
7
- import "./EFThumbnailStrip.js";
8
- import type { EFThumbnailStrip } from "./EFThumbnailStrip.js";
9
- import "./EFTimegroup.js";
10
- import "./EFVideo.js";
11
-
12
- import type { EFTimegroup } from "./EFTimegroup.js";
13
- import type { EFVideo } from "./EFVideo.js";
14
-
15
- beforeEach(async () => {
16
- localStorage.clear();
17
- await fetch("/@ef-clear-cache", { method: "DELETE" });
18
- });
19
-
20
- // IMPLEMENTATION GUIDELINES: Test initial thumbnail loading without requiring resize events
21
-
22
- interface ThumbnailStripFixture {
23
- video: EFVideo;
24
- thumbnailStrip: EFThumbnailStrip;
25
- timegroup: EFTimegroup;
26
- container: HTMLElement;
27
- }
28
-
29
- const test = baseTest.extend<{
30
- thumbnailStripSetup: ThumbnailStripFixture;
31
- alternateSetup: ThumbnailStripFixture;
32
- }>({
33
- thumbnailStripSetup: async ({}, use) => {
34
- const container = document.createElement("div");
35
- render(
36
- html`
37
- <ef-configuration api-host="http://localhost:63315" signing-url="">
38
- <div style="width: 600px; height: 400px;">
39
- <ef-preview class="w-[600px] h-[300px]">
40
- <ef-timegroup mode="contain" class="w-full h-full bg-black">
41
- <ef-video src="http://web:3000/head-moov-480p.mp4" id="test-video" class="size-full object-contain"></ef-video>
42
- </ef-timegroup>
43
- </ef-preview>
44
- <ef-thumbnail-strip
45
- target="test-video"
46
- thumbnail-width="80"
47
- class="w-full"
48
- style="height: 48px;"
49
- ></ef-thumbnail-strip>
50
- </div>
51
- </ef-configuration>
52
- `,
53
- container,
54
- );
55
- document.body.appendChild(container);
56
-
57
- const timegroup = container.querySelector("ef-timegroup") as EFTimegroup;
58
- const video = container.querySelector("ef-video") as EFVideo;
59
- const thumbnailStrip = container.querySelector(
60
- "ef-thumbnail-strip",
61
- ) as EFThumbnailStrip;
62
-
63
- await Promise.all([
64
- timegroup.updateComplete,
65
- video.updateComplete,
66
- thumbnailStrip.updateComplete,
67
- ]);
68
-
69
- await use({ video, thumbnailStrip, timegroup, container });
70
- container.remove();
71
- },
72
- alternateSetup: async ({}, use) => {
73
- const container = document.createElement("div");
74
- render(
75
- html`
76
- <ef-configuration api-host="http://localhost:63315" signing-url="">
77
- <div style="width: 400px; height: 300px;">
78
- <ef-preview class="w-full h-[200px]">
79
- <ef-timegroup mode="contain" class="w-full h-full bg-black">
80
- <ef-video src="http://web:3000/head-moov-480p.mp4" id="alt-video" class="size-full object-contain"></ef-video>
81
- </ef-timegroup>
82
- </ef-preview>
83
- <ef-thumbnail-strip
84
- target="alt-video"
85
- thumbnail-width="80"
86
- class="w-full"
87
- style="height: 48px;"
88
- ></ef-thumbnail-strip>
89
- </div>
90
- </ef-configuration>
91
- `,
92
- container,
93
- );
94
- document.body.appendChild(container);
95
-
96
- const timegroup = container.querySelector("ef-timegroup") as EFTimegroup;
97
- const video = container.querySelector("ef-video") as EFVideo;
98
- const thumbnailStrip = container.querySelector(
99
- "ef-thumbnail-strip",
100
- ) as EFThumbnailStrip;
101
-
102
- await Promise.all([
103
- timegroup.updateComplete,
104
- video.updateComplete,
105
- thumbnailStrip.updateComplete,
106
- ]);
107
-
108
- await use({ video, thumbnailStrip, timegroup, container });
109
- container.remove();
110
- },
111
- });
112
-
113
- const awaitThumbnailLayout = async (thumbnailStrip: EFThumbnailStrip) => {
114
- // @ts-expect-error missing implementation
115
- await thumbnailStrip.thumbnailLayoutTask.taskComplete;
116
- };
117
-
118
- describe("EFThumbnailStrip", () => {
119
- describe("initialization", () => {
120
- test("should detect dimensions and target element on connection", async ({
121
- expect,
122
- thumbnailStripSetup,
123
- }) => {
124
- const { video, thumbnailStrip } = thumbnailStripSetup;
125
-
126
- await video.mediaEngineTask.taskComplete;
127
-
128
- expect(thumbnailStrip.targetElement).toBe(video);
129
- // @ts-expect-error testing private property
130
- expect(thumbnailStrip.stripWidth).toBeGreaterThan(0);
131
-
132
- const canvas = thumbnailStrip.shadowRoot?.querySelector("canvas");
133
- expect(canvas).toBeTruthy();
134
- expect(canvas?.width).toBeGreaterThan(0);
135
- expect(canvas?.height).toBeGreaterThan(0);
136
- }, 1000);
137
-
138
- test("should select target element by ID", async ({
139
- expect,
140
- thumbnailStripSetup,
141
- }) => {
142
- const { video, thumbnailStrip } = thumbnailStripSetup;
143
-
144
- expect(thumbnailStrip.targetElement).toBe(video);
145
- expect(thumbnailStrip.target).toBe("test-video");
146
- }, 1000);
147
- });
148
-
149
- describe("trimmed duration behavior", () => {
150
- test("should show thumbnails from trimmed time range by default", async ({
151
- expect,
152
- thumbnailStripSetup,
153
- }) => {
154
- const { video, thumbnailStrip } = thumbnailStripSetup;
155
-
156
- // Set trim properties on the video
157
- video.setAttribute("trimstart", "2s");
158
- video.setAttribute("trimend", "1s");
159
- await video.updateComplete;
160
- await video.mediaEngineTask.taskComplete;
161
-
162
- // Wait for thumbnail layout to complete
163
- await awaitThumbnailLayout(thumbnailStrip);
164
-
165
- // Video should have trimmed duration
166
- expect(video.sourceStartMs).toBe(2000); // trimstart 2s
167
- expect(video.durationMs).toBe(7000); // 10s - 2s trimstart - 1s trimend
168
-
169
- // Thumbnails should be generated for the trimmed range by default (not intrinsic)
170
- expect(thumbnailStrip.useIntrinsicDuration).toBe(false);
171
- }, 1000);
172
-
173
- test("should recalculate thumbnails when trim properties change dynamically", async ({
174
- expect,
175
- thumbnailStripSetup,
176
- }) => {
177
- const { video, thumbnailStrip } = thumbnailStripSetup;
178
-
179
- await video.mediaEngineTask.taskComplete;
180
-
181
- // Start with no trimming - should use full duration
182
- expect(video.sourceStartMs).toBe(0);
183
- expect(video.durationMs).toBe(10000); // full 10s duration
184
-
185
- // Wait for initial thumbnail layout
186
- await awaitThumbnailLayout(thumbnailStrip);
187
-
188
- // Now add trim properties dynamically
189
- video.setAttribute("trimstart", "3s");
190
- video.setAttribute("trimend", "2s");
191
- await video.updateComplete;
192
-
193
- // @ts-expect-error testing private task
194
- await thumbnailStrip.thumbnailLayoutTask.taskComplete;
195
-
196
- // Wait for the thumbnail update to complete
197
- await awaitThumbnailLayout(thumbnailStrip);
198
-
199
- // Video should now have updated trimmed values
200
- expect(video.sourceStartMs).toBe(3000); // trimstart 3s
201
- expect(video.durationMs).toBe(5000); // 10s - 3s trimstart - 2s trimend
202
-
203
- // Change trim properties again
204
- video.setAttribute("trimstart", "1s");
205
- video.setAttribute("trimend", "1s");
206
- await video.updateComplete;
207
-
208
- // Wait for the second thumbnail update
209
- await awaitThumbnailLayout(thumbnailStrip);
210
-
211
- // Video should reflect the new trim values
212
- expect(video.sourceStartMs).toBe(1000); // trimstart 1s
213
- expect(video.durationMs).toBe(8000); // 10s - 1s trimstart - 1s trimend
214
- }, 2000);
215
-
216
- test("should recalculate thumbnails when sourcein/sourceout properties change", async ({
217
- expect,
218
- thumbnailStripSetup,
219
- }) => {
220
- const { video, thumbnailStrip } = thumbnailStripSetup;
221
-
222
- await video.mediaEngineTask.taskComplete;
223
-
224
- // Start with sourcein/sourceout properties
225
- video.setAttribute("sourcein", "2s");
226
- video.setAttribute("sourceout", "8s");
227
- await video.updateComplete;
228
-
229
- // Wait for thumbnail layout
230
- await awaitThumbnailLayout(thumbnailStrip);
231
-
232
- // Video should have source-based duration
233
- expect(video.sourceStartMs).toBe(2000); // sourcein 2s
234
- expect(video.durationMs).toBe(6000); // sourceout 8s - sourcein 2s
235
-
236
- // Change the source properties
237
- video.setAttribute("sourcein", "1s");
238
- video.setAttribute("sourceout", "9s");
239
- await video.updateComplete;
240
-
241
- // Wait for thumbnail update
242
- await awaitThumbnailLayout(thumbnailStrip);
243
-
244
- // Video should reflect new source values
245
- expect(video.sourceStartMs).toBe(1000); // sourcein 1s
246
- expect(video.durationMs).toBe(8000); // sourceout 9s - sourcein 1s
247
- }, 2000);
248
-
249
- test("should show thumbnails from full duration when useIntrinsicDuration is true", async ({
250
- expect,
251
- thumbnailStripSetup,
252
- }) => {
253
- const { video, thumbnailStrip } = thumbnailStripSetup;
254
-
255
- // Set trim properties and useIntrinsicDuration
256
- video.setAttribute("trimstart", "2s");
257
- video.setAttribute("trimend", "1s");
258
- thumbnailStrip.setAttribute("use-intrinsic-duration", "true");
259
-
260
- await Promise.all([video.updateComplete, thumbnailStrip.updateComplete]);
261
- await video.mediaEngineTask.taskComplete;
262
-
263
- // Wait for thumbnail layout to complete
264
- await awaitThumbnailLayout(thumbnailStrip);
265
-
266
- // Video should have trimmed duration but thumbnail strip uses intrinsic
267
- expect(video.sourceStartMs).toBe(2000); // trimstart 2s
268
- expect(video.durationMs).toBe(7000); // trimmed duration (10s - 2s - 1s)
269
- expect(video.intrinsicDurationMs).toBe(10000); // full duration
270
- expect(thumbnailStrip.useIntrinsicDuration).toBe(true);
271
- }, 1000);
272
-
273
- test("should ignore all trim properties when useIntrinsicDuration is true", async ({
274
- expect,
275
- thumbnailStripSetup,
276
- }) => {
277
- const { video, thumbnailStrip } = thumbnailStripSetup;
278
-
279
- await video.mediaEngineTask.taskComplete;
280
-
281
- // First verify default trimmed behavior
282
- video.setAttribute("trimstart", "3s");
283
- video.setAttribute("trimend", "2s");
284
- await video.updateComplete;
285
-
286
- await awaitThumbnailLayout(thumbnailStrip);
287
-
288
- // Should respect trim by default
289
- expect(video.sourceStartMs).toBe(3000); // trimstart 3s
290
- expect(video.durationMs).toBe(5000); // 10s - 3s - 2s
291
-
292
- // Now enable useIntrinsicDuration
293
- thumbnailStrip.setAttribute("use-intrinsic-duration", "true");
294
- await thumbnailStrip.updateComplete;
295
-
296
- await awaitThumbnailLayout(thumbnailStrip);
297
-
298
- // Video properties should still reflect trim settings
299
- expect(video.sourceStartMs).toBe(3000); // trimstart 3s
300
- expect(video.durationMs).toBe(5000); // trimmed duration
301
- expect(video.intrinsicDurationMs).toBe(10000); // full duration
302
-
303
- // But thumbnail strip should ignore trims and use full duration
304
- expect(thumbnailStrip.useIntrinsicDuration).toBe(true);
305
-
306
- // The layout calculation should use 0 to intrinsicDurationMs instead of trimmed range
307
- // We can verify this by checking that the implementation correctly handles the flag
308
- }, 2000);
309
-
310
- test("should handle custom start-time-ms/end-time-ms relative to correct timeline", async ({
311
- expect,
312
- thumbnailStripSetup,
313
- }) => {
314
- const { video, thumbnailStrip } = thumbnailStripSetup;
315
-
316
- // Set up trim: source 0-10s becomes trimmed 0-7s (source 2-9s)
317
- video.setAttribute("trimstart", "2s");
318
- video.setAttribute("trimend", "1s");
319
- await video.updateComplete;
320
- // CRITICAL: Wait for media engine task completion AFTER setting trim attributes
321
- await video.mediaEngineTask.taskComplete;
322
-
323
- // Verify base setup
324
- expect(video.sourceStartMs).toBe(2000); // Trim starts at 2s in source
325
- expect(video.durationMs).toBe(7000); // 7s trimmed duration
326
-
327
- // Test custom time range in TRIMMED mode (default)
328
- // start-time-ms="1000" should mean 1s into the trimmed portion = 3s in source
329
- // end-time-ms="5000" should mean 5s into the trimmed portion = 7s in source
330
- thumbnailStrip.setAttribute("start-time-ms", "1000"); // 1s into trimmed timeline
331
- thumbnailStrip.setAttribute("end-time-ms", "5000"); // 5s into trimmed timeline
332
- await thumbnailStrip.updateComplete;
333
-
334
- // Verify the properties were set
335
- expect(thumbnailStrip.startTimeMs).toBe(1000);
336
- expect(thumbnailStrip.endTimeMs).toBe(5000);
337
-
338
- // Force thumbnail layout to run with new properties
339
- // @ts-expect-error missing implementation
340
- thumbnailStrip.thumbnailLayoutTask.run();
341
- await awaitThumbnailLayout(thumbnailStrip);
342
-
343
- // Get layout and check timestamps
344
- // @ts-expect-error missing implementation
345
- const layout = thumbnailStrip.thumbnailLayoutTask.value;
346
- expect(layout).toBeTruthy();
347
-
348
- if (layout) {
349
- const allTimestamps = layout.segments.flatMap((segment) =>
350
- segment.thumbnails.map((thumb) => thumb.timeMs),
351
- );
352
-
353
- expect(allTimestamps.length).toBeGreaterThan(0);
354
-
355
- // Thumbnails should be from 3000ms to 7000ms in source timeline
356
- // (trimstart 2000ms + custom start 1000ms = 3000ms to trimstart 2000ms + custom end 5000ms = 7000ms)
357
- const minTime = Math.min(...allTimestamps);
358
- const maxTime = Math.max(...allTimestamps);
359
-
360
- expect(minTime).toBeGreaterThanOrEqual(3000); // Should start from 3s in source
361
- expect(minTime).toBeLessThan(3500); // Should be close to 3s
362
- expect(maxTime).toBeLessThanOrEqual(7000); // Should end at 7s in source
363
- expect(maxTime).toBeGreaterThan(6500); // Should be close to 7s
364
- }
365
-
366
- // Test INTRINSIC mode with custom times
367
- // start-time-ms="1000" should mean 1s in source timeline
368
- // end-time-ms="8000" should mean 8s in source timeline
369
- thumbnailStrip.setAttribute("use-intrinsic-duration", "true");
370
- thumbnailStrip.setAttribute("start-time-ms", "1000"); // 1s in source timeline
371
- thumbnailStrip.setAttribute("end-time-ms", "8000"); // 8s in source timeline
372
- await thumbnailStrip.updateComplete;
373
-
374
- // Verify the properties were set correctly for intrinsic mode
375
- expect(thumbnailStrip.useIntrinsicDuration).toBe(true);
376
- expect(thumbnailStrip.startTimeMs).toBe(1000);
377
- expect(thumbnailStrip.endTimeMs).toBe(8000);
378
-
379
- // Force thumbnail layout to run with intrinsic mode properties
380
- // @ts-expect-error missing implementation
381
- thumbnailStrip.thumbnailLayoutTask.run();
382
- await awaitThumbnailLayout(thumbnailStrip);
383
-
384
- // @ts-expect-error missing implementation
385
- const intrinsicLayout = thumbnailStrip.thumbnailLayoutTask.value;
386
- expect(intrinsicLayout).toBeTruthy();
387
-
388
- if (intrinsicLayout) {
389
- const intrinsicTimestamps = intrinsicLayout.segments.flatMap(
390
- (segment) => segment.thumbnails.map((thumb) => thumb.timeMs),
391
- );
392
-
393
- expect(intrinsicTimestamps.length).toBeGreaterThan(0);
394
-
395
- // In intrinsic mode, should be 1000ms to 8000ms directly in source timeline
396
- const intrinsicMin = Math.min(...intrinsicTimestamps);
397
- const intrinsicMax = Math.max(...intrinsicTimestamps);
398
-
399
- expect(intrinsicMin).toBeGreaterThanOrEqual(1000); // Should start from 1s in source
400
- expect(intrinsicMin).toBeLessThan(1500); // Should be close to 1s
401
- expect(intrinsicMax).toBeLessThanOrEqual(8000); // Should end at 8s in source
402
- expect(intrinsicMax).toBeGreaterThan(7500); // Should be close to 8s
403
- }
404
- }, 3000);
405
-
406
- test("should correctly parse use-intrinsic-duration string values", async ({
407
- expect,
408
- thumbnailStripSetup,
409
- }) => {
410
- const { thumbnailStrip } = thumbnailStripSetup;
411
-
412
- // Test default value
413
- expect(thumbnailStrip.useIntrinsicDuration).toBe(false);
414
-
415
- // Test "true" string value
416
- thumbnailStrip.setAttribute("use-intrinsic-duration", "true");
417
- await thumbnailStrip.updateComplete;
418
- expect(thumbnailStrip.useIntrinsicDuration).toBe(true);
419
-
420
- // Test "false" string value (this is the key fix)
421
- thumbnailStrip.setAttribute("use-intrinsic-duration", "false");
422
- await thumbnailStrip.updateComplete;
423
- expect(thumbnailStrip.useIntrinsicDuration).toBe(false); // Should be false, not true!
424
-
425
- // Test removing attribute
426
- thumbnailStrip.removeAttribute("use-intrinsic-duration");
427
- await thumbnailStrip.updateComplete;
428
- expect(thumbnailStrip.useIntrinsicDuration).toBe(false);
429
-
430
- // Test setting via property
431
- thumbnailStrip.useIntrinsicDuration = true;
432
- await thumbnailStrip.updateComplete;
433
- expect(thumbnailStrip.getAttribute("use-intrinsic-duration")).toBe(
434
- "true",
435
- );
436
-
437
- thumbnailStrip.useIntrinsicDuration = false;
438
- await thumbnailStrip.updateComplete;
439
- expect(thumbnailStrip.hasAttribute("use-intrinsic-duration")).toBe(false);
440
- }, 1000);
441
-
442
- test("should show trimmed thumbnails when use-intrinsic-duration='false'", async ({
443
- expect,
444
- thumbnailStripSetup,
445
- }) => {
446
- const { video, thumbnailStrip } = thumbnailStripSetup;
447
-
448
- // Set trim and explicitly set use-intrinsic-duration="false"
449
- video.setAttribute("trimstart", "2s");
450
- thumbnailStrip.setAttribute("use-intrinsic-duration", "false"); // This should be false!
451
-
452
- await video.updateComplete;
453
- await thumbnailStrip.updateComplete;
454
- await video.mediaEngineTask.taskComplete;
455
-
456
- await awaitThumbnailLayout(thumbnailStrip);
457
-
458
- // Verify the boolean parsing worked correctly
459
- expect(thumbnailStrip.useIntrinsicDuration).toBe(false); // Should be false, not true!
460
- expect(thumbnailStrip.getAttribute("use-intrinsic-duration")).toBe(
461
- "false",
462
- );
463
-
464
- // Verify thumbnail behavior: should use trimmed timeline, starting from 2s
465
- expect(video.sourceStartMs).toBe(2000); // trimstart 2s
466
- expect(video.durationMs).toBe(8000); // 10s - 2s = 8s trimmed duration
467
-
468
- // @ts-expect-error testing private task
469
- const layout = thumbnailStrip.thumbnailLayoutTask.value;
470
- if (layout) {
471
- const allTimestamps = layout.segments.flatMap((segment) =>
472
- segment.thumbnails.map((thumb) => thumb.timeMs),
473
- );
474
- expect(allTimestamps.length).toBeGreaterThan(0);
475
-
476
- // Thumbnails should start from 2000ms (trimstart), not 0ms
477
- const firstTimestamp = Math.min(...allTimestamps);
478
- expect(firstTimestamp).toBeGreaterThanOrEqual(2000);
479
- expect(firstTimestamp).toBeLessThan(2500);
480
- }
481
- }, 1000);
482
-
483
- test("should align first thumbnail with video currentTime=0 frame", async ({
484
- expect,
485
- thumbnailStripSetup,
486
- }) => {
487
- const { video, thumbnailStrip } = thumbnailStripSetup;
488
-
489
- // Set trim to 2s - both video and thumbnails should show same frame
490
- video.setAttribute("trimstart", "2s");
491
- video.setAttribute("current-time", "0"); // 0 in trimmed timeline = 2s in source
492
-
493
- await video.updateComplete;
494
- await video.mediaEngineTask.taskComplete;
495
-
496
- // Wait for thumbnail calculations
497
- // @ts-expect-error testing private task
498
- await thumbnailStrip.thumbnailLayoutTask.taskComplete;
499
-
500
- // Get the video's current source time (what frame it's showing)
501
- const videoSourceTime = video.sourceStartMs + (video.currentTimeMs || 0);
502
- expect(videoSourceTime).toBe(2000); // Should be at 2s in source (frame 61)
503
-
504
- // Get the first thumbnail timestamp
505
- // @ts-expect-error testing private task
506
- const layout = thumbnailStrip.thumbnailLayoutTask.value;
507
- if (layout) {
508
- const allTimestamps = layout.segments.flatMap((segment) =>
509
- segment.thumbnails.map((thumb) => thumb.timeMs),
510
- );
511
- expect(allTimestamps.length).toBeGreaterThan(0);
512
-
513
- const firstThumbnailTime = Math.min(...allTimestamps);
514
-
515
- // The first thumbnail should be at exactly the same source time as video currentTime=0
516
- // This ensures frame 61 shows in both video and thumbnail
517
- expect(firstThumbnailTime).toBe(videoSourceTime); // Should be exactly 2000ms
518
- }
519
- }, 1000);
520
- });
521
-
522
- describe("layout behavior", () => {
523
- test("should calculate layout immediately after initialization", async ({
524
- expect,
525
- alternateSetup,
526
- }) => {
527
- const { video, thumbnailStrip } = alternateSetup;
528
-
529
- await video.mediaEngineTask.taskComplete;
530
-
531
- // @ts-expect-error testing private property
532
- expect(thumbnailStrip.stripWidth).toBe(400); // Uses container inner width
533
- expect(thumbnailStrip.targetElement).toBe(video);
534
- }, 1000);
535
-
536
- test("should remain stable when video properties change", async ({
537
- expect,
538
- thumbnailStripSetup,
539
- }) => {
540
- const { video, thumbnailStrip } = thumbnailStripSetup;
541
-
542
- await video.mediaEngineTask.taskComplete;
543
-
544
- const initialState = {
545
- // @ts-expect-error testing private property
546
- stripWidth: thumbnailStrip.stripWidth,
547
- targetElement: thumbnailStrip.targetElement,
548
- };
549
-
550
- video.setAttribute("trimstart", "2s");
551
- video.setAttribute("trimend", "2s");
552
- await video.updateComplete;
553
-
554
- // @ts-expect-error testing private property
555
- expect(thumbnailStrip.stripWidth).toBe(initialState.stripWidth);
556
- expect(thumbnailStrip.targetElement).toBe(initialState.targetElement);
557
- expect(video.getAttribute("trimstart")).toBe("2s");
558
- expect(video.getAttribute("trimend")).toBe("2s");
559
- }, 1000);
560
-
561
- test("should update dimensions when container resizes", async ({
562
- expect,
563
- alternateSetup,
564
- }) => {
565
- const { video, thumbnailStrip } = alternateSetup;
566
-
567
- await video.mediaEngineTask.taskComplete;
568
-
569
- // @ts-expect-error testing private property
570
- const initialWidth = thumbnailStrip.stripWidth;
571
- expect(initialWidth).toBe(400); // Container inner width
572
-
573
- // Wait for any pending thumbnail layout tasks to complete
574
- await awaitThumbnailLayout(thumbnailStrip);
575
-
576
- // Simulate resize by directly setting the internal width and triggering update
577
- (thumbnailStrip as any)._stripWidth = 800;
578
- // @ts-expect-error testing private property
579
- const finalWidth = thumbnailStrip.stripWidth;
580
-
581
- expect(finalWidth).toBe(800);
582
- expect(finalWidth).toBeGreaterThan(initialWidth);
583
- }, 1000);
584
- });
585
- });