@editframe/elements 0.19.2-beta.0 → 0.20.0-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.
- package/dist/elements/ContextProxiesController.d.ts +40 -0
- package/dist/elements/ContextProxiesController.js +69 -0
- package/dist/elements/EFCaptions.d.ts +45 -6
- package/dist/elements/EFCaptions.js +220 -26
- package/dist/elements/EFImage.js +4 -1
- package/dist/elements/EFMedia/AssetIdMediaEngine.d.ts +2 -1
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +9 -0
- package/dist/elements/EFMedia/AssetMediaEngine.d.ts +1 -0
- package/dist/elements/EFMedia/AssetMediaEngine.js +11 -0
- package/dist/elements/EFMedia/BaseMediaEngine.d.ts +13 -1
- package/dist/elements/EFMedia/BaseMediaEngine.js +9 -0
- package/dist/elements/EFMedia/JitMediaEngine.d.ts +7 -1
- package/dist/elements/EFMedia/JitMediaEngine.js +24 -0
- package/dist/elements/EFMedia/shared/GlobalInputCache.d.ts +39 -0
- package/dist/elements/EFMedia/shared/GlobalInputCache.js +57 -0
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.d.ts +27 -0
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +106 -0
- package/dist/elements/EFMedia.js +25 -1
- package/dist/elements/EFSurface.browsertest.d.ts +0 -0
- package/dist/elements/EFSurface.d.ts +30 -0
- package/dist/elements/EFSurface.js +96 -0
- package/dist/elements/EFTemporal.js +7 -6
- package/dist/elements/EFThumbnailStrip.browsertest.d.ts +0 -0
- package/dist/elements/EFThumbnailStrip.d.ts +86 -0
- package/dist/elements/EFThumbnailStrip.js +490 -0
- package/dist/elements/EFThumbnailStrip.media-engine.browsertest.d.ts +0 -0
- package/dist/elements/EFTimegroup.d.ts +7 -7
- package/dist/elements/EFTimegroup.js +59 -16
- package/dist/elements/updateAnimations.browsertest.d.ts +13 -0
- package/dist/elements/updateAnimations.d.ts +5 -0
- package/dist/elements/updateAnimations.js +37 -13
- package/dist/getRenderInfo.js +1 -1
- package/dist/gui/ContextMixin.js +27 -14
- package/dist/gui/EFControls.browsertest.d.ts +0 -0
- package/dist/gui/EFControls.d.ts +38 -0
- package/dist/gui/EFControls.js +51 -0
- package/dist/gui/EFFilmstrip.d.ts +40 -1
- package/dist/gui/EFFilmstrip.js +240 -3
- package/dist/gui/EFPreview.js +2 -1
- package/dist/gui/EFScrubber.d.ts +6 -5
- package/dist/gui/EFScrubber.js +31 -21
- package/dist/gui/EFTimeDisplay.browsertest.d.ts +0 -0
- package/dist/gui/EFTimeDisplay.d.ts +2 -6
- package/dist/gui/EFTimeDisplay.js +13 -23
- package/dist/gui/TWMixin.js +1 -1
- package/dist/gui/currentTimeContext.d.ts +3 -0
- package/dist/gui/currentTimeContext.js +3 -0
- package/dist/gui/durationContext.d.ts +3 -0
- package/dist/gui/durationContext.js +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -1
- package/dist/style.css +1 -1
- package/dist/transcoding/types/index.d.ts +11 -0
- package/dist/utils/LRUCache.d.ts +46 -0
- package/dist/utils/LRUCache.js +382 -1
- package/dist/utils/LRUCache.test.d.ts +1 -0
- package/package.json +2 -2
- package/src/elements/ContextProxiesController.ts +123 -0
- package/src/elements/EFCaptions.browsertest.ts +1820 -0
- package/src/elements/EFCaptions.ts +373 -36
- package/src/elements/EFImage.ts +4 -1
- package/src/elements/EFMedia/AssetIdMediaEngine.ts +30 -1
- package/src/elements/EFMedia/AssetMediaEngine.ts +33 -0
- package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +3 -8
- package/src/elements/EFMedia/BaseMediaEngine.ts +35 -0
- package/src/elements/EFMedia/JitMediaEngine.ts +48 -0
- package/src/elements/EFMedia/shared/GlobalInputCache.ts +77 -0
- package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +227 -0
- package/src/elements/EFMedia.ts +38 -1
- package/src/elements/EFSurface.browsertest.ts +155 -0
- package/src/elements/EFSurface.ts +141 -0
- package/src/elements/EFTemporal.ts +14 -8
- package/src/elements/EFThumbnailStrip.browsertest.ts +591 -0
- package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +713 -0
- package/src/elements/EFThumbnailStrip.ts +905 -0
- package/src/elements/EFTimegroup.browsertest.ts +56 -7
- package/src/elements/EFTimegroup.ts +88 -18
- package/src/elements/updateAnimations.browsertest.ts +361 -12
- package/src/elements/updateAnimations.ts +68 -19
- package/src/gui/ContextMixin.browsertest.ts +0 -25
- package/src/gui/ContextMixin.ts +44 -20
- package/src/gui/EFControls.browsertest.ts +175 -0
- package/src/gui/EFControls.ts +84 -0
- package/src/gui/EFFilmstrip.ts +323 -4
- package/src/gui/EFPreview.ts +2 -1
- package/src/gui/EFScrubber.ts +29 -25
- package/src/gui/EFTimeDisplay.browsertest.ts +237 -0
- package/src/gui/EFTimeDisplay.ts +12 -40
- package/src/gui/currentTimeContext.ts +5 -0
- package/src/gui/durationContext.ts +3 -0
- package/src/transcoding/types/index.ts +13 -0
- package/src/utils/LRUCache.test.ts +272 -0
- package/src/utils/LRUCache.ts +543 -0
- package/types.json +1 -1
- package/dist/transcoding/cache/CacheManager.d.ts +0 -73
- package/src/transcoding/cache/CacheManager.ts +0 -208
package/src/gui/EFFilmstrip.ts
CHANGED
|
@@ -18,7 +18,11 @@ import { createRef, ref } from "lit/directives/ref.js";
|
|
|
18
18
|
import { styleMap } from "lit/directives/style-map.js";
|
|
19
19
|
|
|
20
20
|
import { EFAudio } from "../elements/EFAudio.js";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
type Caption,
|
|
23
|
+
EFCaptions,
|
|
24
|
+
EFCaptionsActiveWord,
|
|
25
|
+
} from "../elements/EFCaptions.js";
|
|
22
26
|
import { EFImage } from "../elements/EFImage.js";
|
|
23
27
|
import type { TemporalMixinInterface } from "../elements/EFTemporal.js";
|
|
24
28
|
import { EFTimegroup } from "../elements/EFTimegroup.js";
|
|
@@ -223,8 +227,295 @@ export class EFVideoFilmstrip extends FilmstripItem {
|
|
|
223
227
|
|
|
224
228
|
@customElement("ef-captions-filmstrip")
|
|
225
229
|
export class EFCaptionsFilmstrip extends FilmstripItem {
|
|
226
|
-
|
|
227
|
-
|
|
230
|
+
render() {
|
|
231
|
+
const captions = this.element as EFCaptions;
|
|
232
|
+
const captionsData = captions.unifiedCaptionsDataTask.value;
|
|
233
|
+
|
|
234
|
+
return html`<div style=${styleMap(this.gutterStyles)}>
|
|
235
|
+
<div
|
|
236
|
+
class="bg-slate-300 relative"
|
|
237
|
+
?data-focused=${this.isFocused}
|
|
238
|
+
@mouseenter=${() => {
|
|
239
|
+
if (this.focusContext) {
|
|
240
|
+
this.focusContext.focusedElement = this.element;
|
|
241
|
+
}
|
|
242
|
+
}}
|
|
243
|
+
@mouseleave=${() => {
|
|
244
|
+
if (this.focusContext) {
|
|
245
|
+
this.focusContext.focusedElement = null;
|
|
246
|
+
}
|
|
247
|
+
}}
|
|
248
|
+
>
|
|
249
|
+
<div
|
|
250
|
+
?data-focused=${this.isFocused}
|
|
251
|
+
class="border-outset relative mb-[1px] block h-[1.1rem] text-nowrap border border-slate-500 bg-blue-200 text-sm data-[focused]:bg-slate-400 overflow-hidden"
|
|
252
|
+
style=${styleMap(this.trimPortionStyles)}
|
|
253
|
+
>
|
|
254
|
+
📝 ${this.renderCaptionsData(captionsData)}
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
${this.renderChildren()}
|
|
258
|
+
</div>`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
renderCaptionsData(captionsData: Caption | null | undefined) {
|
|
262
|
+
if (!captionsData) {
|
|
263
|
+
return html``;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Get current time for highlighting active elements
|
|
267
|
+
const captions = this.element as EFCaptions;
|
|
268
|
+
const rootTimegroup = captions.rootTimegroup;
|
|
269
|
+
const currentTimeMs = rootTimegroup?.currentTimeMs || 0;
|
|
270
|
+
const captionsLocalTimeMs = currentTimeMs - captions.startTimeMs;
|
|
271
|
+
const captionsLocalTimeSec = captionsLocalTimeMs / 1000;
|
|
272
|
+
|
|
273
|
+
// Show all segments with text content, let them clip naturally
|
|
274
|
+
const segmentElements = captionsData.segments.map((segment) => {
|
|
275
|
+
const isActive =
|
|
276
|
+
captionsLocalTimeSec >= segment.start &&
|
|
277
|
+
captionsLocalTimeSec < segment.end;
|
|
278
|
+
|
|
279
|
+
return html`<div
|
|
280
|
+
class="absolute border border-slate-600 text-xs overflow-hidden flex items-center ${isActive ? "bg-green-200 border-green-500 font-bold z-[5]" : "bg-slate-100"}"
|
|
281
|
+
style=${styleMap({
|
|
282
|
+
left: `${this.pixelsPerMs * segment.start * 1000}px`,
|
|
283
|
+
width: `${this.pixelsPerMs * (segment.end - segment.start) * 1000}px`,
|
|
284
|
+
height: "100%",
|
|
285
|
+
top: "0px",
|
|
286
|
+
})}
|
|
287
|
+
title="Segment: '${segment.text}' (${segment.start}s - ${segment.end}s)"
|
|
288
|
+
>
|
|
289
|
+
<span class="px-0.5 text-[8px] ${isActive ? "font-bold" : ""}">${segment.text}</span>
|
|
290
|
+
</div>`;
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return html`${segmentElements}`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
renderChildren(): Array<TemplateResult<1> | typeof nothing> | typeof nothing {
|
|
297
|
+
// Also render normal DOM children (like ef-captions-active-word elements)
|
|
298
|
+
return super.renderChildren();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
@customElement("ef-captions-active-word-filmstrip")
|
|
303
|
+
export class EFCaptionsActiveWordFilmstrip extends FilmstripItem {
|
|
304
|
+
get captionsTrackStyles() {
|
|
305
|
+
const parentCaptions = this.element.closest("ef-captions") as EFCaptions;
|
|
306
|
+
return {
|
|
307
|
+
position: "relative",
|
|
308
|
+
left: `${this.pixelsPerMs * (parentCaptions?.startTimeWithinParentMs || 0)}px`,
|
|
309
|
+
width: `${this.pixelsPerMs * (parentCaptions?.durationMs || 0)}px`,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
render() {
|
|
314
|
+
// Get parent captions element and its data
|
|
315
|
+
const parentCaptions = this.element.closest("ef-captions") as EFCaptions;
|
|
316
|
+
const captionsData = parentCaptions?.unifiedCaptionsDataTask.value;
|
|
317
|
+
|
|
318
|
+
if (!captionsData) {
|
|
319
|
+
return html`<div style=${styleMap(this.captionsTrackStyles)}>
|
|
320
|
+
<div class="bg-slate-300 border border-slate-500 h-[1.1rem] mb-[1px] text-xs">
|
|
321
|
+
🗣️ Active Word
|
|
322
|
+
</div>
|
|
323
|
+
</div>`;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Get current time for highlighting
|
|
327
|
+
const rootTimegroup = parentCaptions.rootTimegroup;
|
|
328
|
+
const currentTimeMs = rootTimegroup?.currentTimeMs || 0;
|
|
329
|
+
const captionsLocalTimeMs = currentTimeMs - parentCaptions.startTimeMs;
|
|
330
|
+
const captionsLocalTimeSec = captionsLocalTimeMs / 1000;
|
|
331
|
+
|
|
332
|
+
return html`<div style=${styleMap(this.captionsTrackStyles)}>
|
|
333
|
+
<div class="bg-slate-300 relative border border-slate-500 h-[1.1rem] mb-[1px] w-full">
|
|
334
|
+
${captionsData.word_segments.map((word) => {
|
|
335
|
+
const isCurrentlyActive =
|
|
336
|
+
captionsLocalTimeSec >= word.start &&
|
|
337
|
+
captionsLocalTimeSec < word.end;
|
|
338
|
+
|
|
339
|
+
return html`<div
|
|
340
|
+
class="absolute border text-xs overflow-visible flex items-center ${isCurrentlyActive ? "bg-yellow-200 border-yellow-500 font-bold z-[5]" : "bg-blue-50 border-blue-200"}"
|
|
341
|
+
style=${styleMap({
|
|
342
|
+
left: `${this.pixelsPerMs * word.start * 1000}px`,
|
|
343
|
+
width: `${this.pixelsPerMs * (word.end - word.start) * 1000}px`,
|
|
344
|
+
height: "100%",
|
|
345
|
+
top: "0px",
|
|
346
|
+
})}
|
|
347
|
+
title="Word: '${word.text}' (${word.start}s - ${word.end}s)"
|
|
348
|
+
>
|
|
349
|
+
${isCurrentlyActive ? html`<span class="px-0.5 text-[8px] font-bold whitespace-nowrap bg-yellow-200">${word.text.trim()}</span>` : ""}
|
|
350
|
+
</div>`;
|
|
351
|
+
})}
|
|
352
|
+
</div>
|
|
353
|
+
</div>`;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
@customElement("ef-captions-segment-filmstrip")
|
|
358
|
+
export class EFCaptionsSegmentFilmstrip extends FilmstripItem {
|
|
359
|
+
get captionsTrackStyles() {
|
|
360
|
+
const parentCaptions = this.element.closest("ef-captions") as EFCaptions;
|
|
361
|
+
return {
|
|
362
|
+
position: "relative",
|
|
363
|
+
left: `${this.pixelsPerMs * (parentCaptions?.startTimeWithinParentMs || 0)}px`,
|
|
364
|
+
width: `${this.pixelsPerMs * (parentCaptions?.durationMs || 0)}px`,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
render() {
|
|
369
|
+
// Get parent captions element and its data
|
|
370
|
+
const parentCaptions = this.element.closest("ef-captions") as EFCaptions;
|
|
371
|
+
const captionsData = parentCaptions?.unifiedCaptionsDataTask.value;
|
|
372
|
+
|
|
373
|
+
if (!captionsData) {
|
|
374
|
+
return html`<div style=${styleMap(this.captionsTrackStyles)}>
|
|
375
|
+
<div class="bg-slate-300 border border-slate-500 h-[1.1rem] mb-[1px] text-xs">
|
|
376
|
+
📄 Segment
|
|
377
|
+
</div>
|
|
378
|
+
</div>`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Get current time for highlighting
|
|
382
|
+
const rootTimegroup = parentCaptions.rootTimegroup;
|
|
383
|
+
const currentTimeMs = rootTimegroup?.currentTimeMs || 0;
|
|
384
|
+
const captionsLocalTimeMs = currentTimeMs - parentCaptions.startTimeMs;
|
|
385
|
+
const captionsLocalTimeSec = captionsLocalTimeMs / 1000;
|
|
386
|
+
|
|
387
|
+
return html`<div style=${styleMap(this.captionsTrackStyles)}>
|
|
388
|
+
<div class="bg-slate-300 relative border border-slate-500 h-[1.1rem] mb-[1px] w-full">
|
|
389
|
+
${captionsData.segments.map((segment) => {
|
|
390
|
+
const isCurrentlyActive =
|
|
391
|
+
captionsLocalTimeSec >= segment.start &&
|
|
392
|
+
captionsLocalTimeSec < segment.end;
|
|
393
|
+
|
|
394
|
+
return html`<div
|
|
395
|
+
class="absolute border text-xs overflow-visible flex items-center ${isCurrentlyActive ? "bg-green-200 border-green-500 font-bold z-[5]" : "bg-green-50 border-green-200"}"
|
|
396
|
+
style=${styleMap({
|
|
397
|
+
left: `${this.pixelsPerMs * segment.start * 1000}px`,
|
|
398
|
+
width: `${this.pixelsPerMs * (segment.end - segment.start) * 1000}px`,
|
|
399
|
+
height: "100%",
|
|
400
|
+
top: "0px",
|
|
401
|
+
})}
|
|
402
|
+
title="Segment: '${segment.text}' (${segment.start}s - ${segment.end}s)"
|
|
403
|
+
>
|
|
404
|
+
${isCurrentlyActive ? html`<span class="px-0.5 text-[8px] font-bold whitespace-nowrap bg-green-200">${segment.text}</span>` : ""}
|
|
405
|
+
</div>`;
|
|
406
|
+
})}
|
|
407
|
+
</div>
|
|
408
|
+
</div>`;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
@customElement("ef-captions-before-word-filmstrip")
|
|
413
|
+
export class EFCaptionsBeforeWordFilmstrip extends FilmstripItem {
|
|
414
|
+
get captionsTrackStyles() {
|
|
415
|
+
const parentCaptions = this.element.closest("ef-captions") as EFCaptions;
|
|
416
|
+
return {
|
|
417
|
+
position: "relative",
|
|
418
|
+
left: `${this.pixelsPerMs * (parentCaptions?.startTimeWithinParentMs || 0)}px`,
|
|
419
|
+
width: `${this.pixelsPerMs * (parentCaptions?.durationMs || 0)}px`,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
render() {
|
|
424
|
+
// Get parent captions element and its data
|
|
425
|
+
const parentCaptions = this.element.closest("ef-captions") as EFCaptions;
|
|
426
|
+
const captionsData = parentCaptions?.unifiedCaptionsDataTask.value;
|
|
427
|
+
|
|
428
|
+
if (!captionsData) {
|
|
429
|
+
return html`<div style=${styleMap(this.captionsTrackStyles)}>
|
|
430
|
+
<div class="bg-slate-300 border border-slate-500 h-[1.1rem] mb-[1px] text-xs">
|
|
431
|
+
⬅️ Before
|
|
432
|
+
</div>
|
|
433
|
+
</div>`;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Get current time for highlighting
|
|
437
|
+
const rootTimegroup = parentCaptions.rootTimegroup;
|
|
438
|
+
const currentTimeMs = rootTimegroup?.currentTimeMs || 0;
|
|
439
|
+
const captionsLocalTimeMs = currentTimeMs - parentCaptions.startTimeMs;
|
|
440
|
+
const captionsLocalTimeSec = captionsLocalTimeMs / 1000;
|
|
441
|
+
|
|
442
|
+
return html`<div style=${styleMap(this.captionsTrackStyles)}>
|
|
443
|
+
<div class="bg-slate-300 relative border border-slate-500 h-[1.1rem] mb-[1px] w-full">
|
|
444
|
+
${captionsData.word_segments.map((word) => {
|
|
445
|
+
const isCurrentlyActive =
|
|
446
|
+
captionsLocalTimeSec >= word.start &&
|
|
447
|
+
captionsLocalTimeSec < word.end;
|
|
448
|
+
|
|
449
|
+
return html`<div
|
|
450
|
+
class="absolute border text-xs overflow-visible flex items-center ${isCurrentlyActive ? "bg-yellow-200 border-yellow-500 font-bold z-[5]" : "bg-purple-50 border-purple-200"}"
|
|
451
|
+
style=${styleMap({
|
|
452
|
+
left: `${this.pixelsPerMs * word.start * 1000}px`,
|
|
453
|
+
width: `${this.pixelsPerMs * (word.end - word.start) * 1000}px`,
|
|
454
|
+
height: "100%",
|
|
455
|
+
top: "0px",
|
|
456
|
+
})}
|
|
457
|
+
title="Word: '${word.text}' (${word.start}s - ${word.end}s)"
|
|
458
|
+
>
|
|
459
|
+
<!-- No text for before tracks - they're redundant -->
|
|
460
|
+
</div>`;
|
|
461
|
+
})}
|
|
462
|
+
</div>
|
|
463
|
+
</div>`;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
@customElement("ef-captions-after-word-filmstrip")
|
|
468
|
+
export class EFCaptionsAfterWordFilmstrip extends FilmstripItem {
|
|
469
|
+
get captionsTrackStyles() {
|
|
470
|
+
const parentCaptions = this.element.closest("ef-captions") as EFCaptions;
|
|
471
|
+
return {
|
|
472
|
+
position: "relative",
|
|
473
|
+
left: `${this.pixelsPerMs * (parentCaptions?.startTimeWithinParentMs || 0)}px`,
|
|
474
|
+
width: `${this.pixelsPerMs * (parentCaptions?.durationMs || 0)}px`,
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
render() {
|
|
479
|
+
// Get parent captions element and its data
|
|
480
|
+
const parentCaptions = this.element.closest("ef-captions") as EFCaptions;
|
|
481
|
+
const captionsData = parentCaptions?.unifiedCaptionsDataTask.value;
|
|
482
|
+
|
|
483
|
+
if (!captionsData) {
|
|
484
|
+
return html`<div style=${styleMap(this.captionsTrackStyles)}>
|
|
485
|
+
<div class="bg-slate-300 border border-slate-500 h-[1.1rem] mb-[1px] text-xs">
|
|
486
|
+
➡️ After
|
|
487
|
+
</div>
|
|
488
|
+
</div>`;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Get current time for highlighting
|
|
492
|
+
const rootTimegroup = parentCaptions.rootTimegroup;
|
|
493
|
+
const currentTimeMs = rootTimegroup?.currentTimeMs || 0;
|
|
494
|
+
const captionsLocalTimeMs = currentTimeMs - parentCaptions.startTimeMs;
|
|
495
|
+
const captionsLocalTimeSec = captionsLocalTimeMs / 1000;
|
|
496
|
+
|
|
497
|
+
return html`<div style=${styleMap(this.captionsTrackStyles)}>
|
|
498
|
+
<div class="bg-slate-300 relative border border-slate-500 h-[1.1rem] mb-[1px] w-full">
|
|
499
|
+
${captionsData.word_segments.map((word) => {
|
|
500
|
+
const isCurrentlyActive =
|
|
501
|
+
captionsLocalTimeSec >= word.start &&
|
|
502
|
+
captionsLocalTimeSec < word.end;
|
|
503
|
+
|
|
504
|
+
return html`<div
|
|
505
|
+
class="absolute border text-xs overflow-visible flex items-center ${isCurrentlyActive ? "bg-yellow-200 border-yellow-500 font-bold z-[5]" : "bg-purple-50 border-purple-200"}"
|
|
506
|
+
style=${styleMap({
|
|
507
|
+
left: `${this.pixelsPerMs * word.start * 1000}px`,
|
|
508
|
+
width: `${this.pixelsPerMs * (word.end - word.start) * 1000}px`,
|
|
509
|
+
height: "100%",
|
|
510
|
+
top: "0px",
|
|
511
|
+
})}
|
|
512
|
+
title="Word: '${word.text}' (${word.start}s - ${word.end}s)"
|
|
513
|
+
>
|
|
514
|
+
<!-- No text for after tracks - they're redundant -->
|
|
515
|
+
</div>`;
|
|
516
|
+
})}
|
|
517
|
+
</div>
|
|
518
|
+
</div>`;
|
|
228
519
|
}
|
|
229
520
|
}
|
|
230
521
|
|
|
@@ -496,6 +787,30 @@ const renderFilmstripChildren = (
|
|
|
496
787
|
.pixelsPerMs=${pixelsPerMs}
|
|
497
788
|
></ef-captions-filmstrip>`;
|
|
498
789
|
}
|
|
790
|
+
if (child instanceof EFCaptionsActiveWord) {
|
|
791
|
+
return html`<ef-captions-active-word-filmstrip
|
|
792
|
+
.element=${child}
|
|
793
|
+
.pixelsPerMs=${pixelsPerMs}
|
|
794
|
+
></ef-captions-active-word-filmstrip>`;
|
|
795
|
+
}
|
|
796
|
+
if (child.tagName === "EF-CAPTIONS-SEGMENT") {
|
|
797
|
+
return html`<ef-captions-segment-filmstrip
|
|
798
|
+
.element=${child}
|
|
799
|
+
.pixelsPerMs=${pixelsPerMs}
|
|
800
|
+
></ef-captions-segment-filmstrip>`;
|
|
801
|
+
}
|
|
802
|
+
if (child.tagName === "EF-CAPTIONS-BEFORE-ACTIVE-WORD") {
|
|
803
|
+
return html`<ef-captions-before-word-filmstrip
|
|
804
|
+
.element=${child}
|
|
805
|
+
.pixelsPerMs=${pixelsPerMs}
|
|
806
|
+
></ef-captions-before-word-filmstrip>`;
|
|
807
|
+
}
|
|
808
|
+
if (child.tagName === "EF-CAPTIONS-AFTER-ACTIVE-WORD") {
|
|
809
|
+
return html`<ef-captions-after-word-filmstrip
|
|
810
|
+
.element=${child}
|
|
811
|
+
.pixelsPerMs=${pixelsPerMs}
|
|
812
|
+
></ef-captions-after-word-filmstrip>`;
|
|
813
|
+
}
|
|
499
814
|
if (child instanceof EFWaveform) {
|
|
500
815
|
return html`<ef-waveform-filmstrip
|
|
501
816
|
.element=${child}
|
|
@@ -769,7 +1084,7 @@ export class EFFilmstrip extends TWMixin(LitElement) {
|
|
|
769
1084
|
@mousedown=${this.startScrub}
|
|
770
1085
|
>
|
|
771
1086
|
<div
|
|
772
|
-
class="border-red pointer-events-none absolute z-
|
|
1087
|
+
class="border-red pointer-events-none absolute z-[20] h-full w-[2px] border-r-2 border-red-700"
|
|
773
1088
|
style=${styleMap({
|
|
774
1089
|
left: `${this.pixelsPerMs * this.currentTimeMs}px`,
|
|
775
1090
|
top: `${this.timelineScrolltop}px`,
|
|
@@ -833,6 +1148,10 @@ declare global {
|
|
|
833
1148
|
"ef-audio-filmstrip": EFAudioFilmstrip;
|
|
834
1149
|
"ef-video-filmstrip": EFVideoFilmstrip;
|
|
835
1150
|
"ef-captions-filmstrip": EFCaptionsFilmstrip;
|
|
1151
|
+
"ef-captions-active-word-filmstrip": EFCaptionsActiveWordFilmstrip;
|
|
1152
|
+
"ef-captions-segment-filmstrip": EFCaptionsSegmentFilmstrip;
|
|
1153
|
+
"ef-captions-before-word-filmstrip": EFCaptionsBeforeWordFilmstrip;
|
|
1154
|
+
"ef-captions-after-word-filmstrip": EFCaptionsAfterWordFilmstrip;
|
|
836
1155
|
"ef-waveform-filmstrip": EFWaveformFilmstrip;
|
|
837
1156
|
"ef-image-filmstrip": EFImageFilmstrip;
|
|
838
1157
|
"ef-html-filmstrip": EFHTMLFilmstrip;
|
package/src/gui/EFPreview.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { provide } from "@lit/context";
|
|
2
2
|
import { css, html, LitElement } from "lit";
|
|
3
3
|
import { customElement } from "lit/decorators.js";
|
|
4
|
+
import { EFTargetable } from "../elements/TargetController.js";
|
|
4
5
|
import { ContextMixin } from "./ContextMixin.js";
|
|
5
6
|
import { focusedElementContext } from "./focusedElementContext.js";
|
|
6
7
|
import { TWMixin } from "./TWMixin.js";
|
|
7
8
|
|
|
8
9
|
@customElement("ef-preview")
|
|
9
|
-
export class EFPreview extends ContextMixin(TWMixin(LitElement)) {
|
|
10
|
+
export class EFPreview extends EFTargetable(ContextMixin(TWMixin(LitElement))) {
|
|
10
11
|
static styles = [
|
|
11
12
|
css`
|
|
12
13
|
:host {
|
package/src/gui/EFScrubber.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { customElement, state } from "lit/decorators.js";
|
|
|
4
4
|
|
|
5
5
|
import { ref } from "lit/directives/ref.js";
|
|
6
6
|
import type { ContextMixinInterface } from "./ContextMixin.js";
|
|
7
|
+
import { currentTimeContext } from "./currentTimeContext.js";
|
|
8
|
+
import { durationContext } from "./durationContext.js";
|
|
7
9
|
import { efContext } from "./efContext.js";
|
|
8
10
|
import { playingContext } from "./playingContext.js";
|
|
9
11
|
|
|
@@ -16,8 +18,10 @@ export class EFScrubber extends LitElement {
|
|
|
16
18
|
--ef-scrubber-background: rgb(209 213 219);
|
|
17
19
|
--ef-scrubber-progress-color: rgb(37 99 235);
|
|
18
20
|
--ef-scrubber-handle-size: 12px;
|
|
19
|
-
display: block;
|
|
20
21
|
width: 100%;
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
justify-content: center;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
.scrubber {
|
|
@@ -60,14 +64,17 @@ export class EFScrubber extends LitElement {
|
|
|
60
64
|
@consume({ context: playingContext, subscribe: true })
|
|
61
65
|
playing = false;
|
|
62
66
|
|
|
63
|
-
@
|
|
64
|
-
|
|
67
|
+
@consume({ context: currentTimeContext, subscribe: true })
|
|
68
|
+
currentTimeMs = Number.NaN;
|
|
69
|
+
|
|
70
|
+
@consume({ context: durationContext, subscribe: true })
|
|
71
|
+
durationMs = 0;
|
|
65
72
|
|
|
66
73
|
@state()
|
|
67
74
|
private scrubProgress = 0;
|
|
68
75
|
|
|
69
76
|
@state()
|
|
70
|
-
private
|
|
77
|
+
private isMoving = false;
|
|
71
78
|
|
|
72
79
|
private scrubberRef?: HTMLElement;
|
|
73
80
|
|
|
@@ -79,30 +86,33 @@ export class EFScrubber extends LitElement {
|
|
|
79
86
|
const progress = Math.max(0, Math.min(1, x / rect.width));
|
|
80
87
|
|
|
81
88
|
this.scrubProgress = progress;
|
|
82
|
-
this.context.currentTimeMs =
|
|
83
|
-
progress * (this.context.targetTimegroup?.durationMs ?? 0);
|
|
89
|
+
this.context.currentTimeMs = progress * this.durationMs;
|
|
84
90
|
}
|
|
85
91
|
|
|
86
|
-
private
|
|
87
|
-
this.
|
|
92
|
+
private boundHandlePointerDown = (e: MouseEvent) => {
|
|
93
|
+
this.isMoving = true;
|
|
88
94
|
e.preventDefault();
|
|
89
95
|
this.updateProgress(e);
|
|
90
96
|
};
|
|
91
97
|
|
|
92
|
-
private
|
|
93
|
-
if (this.
|
|
98
|
+
private boundHandlePointerMove = (e: MouseEvent) => {
|
|
99
|
+
if (this.isMoving) {
|
|
94
100
|
this.updateProgress(e);
|
|
95
101
|
}
|
|
96
102
|
};
|
|
97
103
|
|
|
98
|
-
private
|
|
99
|
-
this.
|
|
104
|
+
private boundHandlePointerUp = () => {
|
|
105
|
+
this.isMoving = false;
|
|
100
106
|
};
|
|
101
107
|
|
|
102
108
|
render() {
|
|
103
|
-
|
|
109
|
+
// Calculate progress from currentTimeMs and duration
|
|
110
|
+
const currentProgress =
|
|
111
|
+
this.durationMs > 0 ? (this.currentTimeMs ?? 0) / this.durationMs : 0;
|
|
112
|
+
|
|
113
|
+
const displayProgress = this.isMoving
|
|
104
114
|
? this.scrubProgress
|
|
105
|
-
:
|
|
115
|
+
: currentProgress;
|
|
106
116
|
|
|
107
117
|
return html`
|
|
108
118
|
<div
|
|
@@ -111,7 +121,7 @@ export class EFScrubber extends LitElement {
|
|
|
111
121
|
})}
|
|
112
122
|
part="scrubber"
|
|
113
123
|
class="scrubber"
|
|
114
|
-
@mousedown=${this.
|
|
124
|
+
@mousedown=${this.boundHandlePointerDown}
|
|
115
125
|
>
|
|
116
126
|
<div class="progress" style="width: ${displayProgress * 100}%"></div>
|
|
117
127
|
<div class="handle" style="left: ${displayProgress * 100}%"></div>
|
|
@@ -121,20 +131,14 @@ export class EFScrubber extends LitElement {
|
|
|
121
131
|
|
|
122
132
|
connectedCallback() {
|
|
123
133
|
super.connectedCallback();
|
|
124
|
-
window.addEventListener("
|
|
125
|
-
window.addEventListener("
|
|
126
|
-
|
|
127
|
-
if (this.context) {
|
|
128
|
-
this.context.addEventListener("timeupdate", (e: Event) => {
|
|
129
|
-
this.lastTimeUpdateProgress = (e as CustomEvent).detail.progress;
|
|
130
|
-
});
|
|
131
|
-
}
|
|
134
|
+
window.addEventListener("pointerup", this.boundHandlePointerUp);
|
|
135
|
+
window.addEventListener("pointermove", this.boundHandlePointerMove);
|
|
132
136
|
}
|
|
133
137
|
|
|
134
138
|
disconnectedCallback() {
|
|
135
139
|
super.disconnectedCallback();
|
|
136
|
-
window.removeEventListener("
|
|
137
|
-
window.removeEventListener("
|
|
140
|
+
window.removeEventListener("pointerup", this.boundHandlePointerUp);
|
|
141
|
+
window.removeEventListener("pointermove", this.boundHandlePointerMove);
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
144
|
|