@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.
- package/package.json +2 -2
- package/scripts/build-css.js +3 -3
- package/tsdown.config.ts +1 -1
- package/src/elements/ContextProxiesController.ts +0 -124
- package/src/elements/CrossUpdateController.ts +0 -22
- package/src/elements/EFAudio.browsertest.ts +0 -706
- package/src/elements/EFAudio.ts +0 -56
- package/src/elements/EFCaptions.browsertest.ts +0 -1960
- package/src/elements/EFCaptions.ts +0 -823
- package/src/elements/EFImage.browsertest.ts +0 -120
- package/src/elements/EFImage.ts +0 -113
- package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
- package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
- package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
- package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
- package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
- package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
- package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
- package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
- package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
- package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
- package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
- package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
- package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
- package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
- package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
- package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
- package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
- package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
- package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
- package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
- package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
- package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
- package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
- package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
- package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
- package/src/elements/EFMedia.browsertest.ts +0 -872
- package/src/elements/EFMedia.ts +0 -341
- package/src/elements/EFSourceMixin.ts +0 -60
- package/src/elements/EFSurface.browsertest.ts +0 -151
- package/src/elements/EFSurface.ts +0 -142
- package/src/elements/EFTemporal.browsertest.ts +0 -215
- package/src/elements/EFTemporal.ts +0 -800
- package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
- package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
- package/src/elements/EFThumbnailStrip.ts +0 -906
- package/src/elements/EFTimegroup.browsertest.ts +0 -934
- package/src/elements/EFTimegroup.ts +0 -882
- package/src/elements/EFVideo.browsertest.ts +0 -1482
- package/src/elements/EFVideo.ts +0 -564
- package/src/elements/EFWaveform.ts +0 -547
- package/src/elements/FetchContext.browsertest.ts +0 -401
- package/src/elements/FetchMixin.ts +0 -38
- package/src/elements/SampleBuffer.ts +0 -94
- package/src/elements/TargetController.browsertest.ts +0 -230
- package/src/elements/TargetController.ts +0 -224
- package/src/elements/TimegroupController.ts +0 -26
- package/src/elements/durationConverter.ts +0 -35
- package/src/elements/parseTimeToMs.ts +0 -9
- package/src/elements/printTaskStatus.ts +0 -16
- package/src/elements/renderTemporalAudio.ts +0 -108
- package/src/elements/updateAnimations.browsertest.ts +0 -1884
- package/src/elements/updateAnimations.ts +0 -217
- package/src/elements/util.ts +0 -24
- package/src/gui/ContextMixin.browsertest.ts +0 -860
- package/src/gui/ContextMixin.ts +0 -562
- package/src/gui/Controllable.browsertest.ts +0 -258
- package/src/gui/Controllable.ts +0 -41
- package/src/gui/EFConfiguration.ts +0 -40
- package/src/gui/EFControls.browsertest.ts +0 -389
- package/src/gui/EFControls.ts +0 -195
- package/src/gui/EFDial.browsertest.ts +0 -84
- package/src/gui/EFDial.ts +0 -172
- package/src/gui/EFFilmstrip.browsertest.ts +0 -712
- package/src/gui/EFFilmstrip.ts +0 -1349
- package/src/gui/EFFitScale.ts +0 -152
- package/src/gui/EFFocusOverlay.ts +0 -79
- package/src/gui/EFPause.browsertest.ts +0 -202
- package/src/gui/EFPause.ts +0 -73
- package/src/gui/EFPlay.browsertest.ts +0 -202
- package/src/gui/EFPlay.ts +0 -73
- package/src/gui/EFPreview.ts +0 -74
- package/src/gui/EFResizableBox.browsertest.ts +0 -79
- package/src/gui/EFResizableBox.ts +0 -898
- package/src/gui/EFScrubber.ts +0 -151
- package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
- package/src/gui/EFTimeDisplay.ts +0 -55
- package/src/gui/EFToggleLoop.ts +0 -35
- package/src/gui/EFTogglePlay.ts +0 -70
- package/src/gui/EFWorkbench.ts +0 -115
- package/src/gui/PlaybackController.ts +0 -527
- package/src/gui/TWMixin.css +0 -6
- package/src/gui/TWMixin.ts +0 -61
- package/src/gui/TargetOrContextMixin.ts +0 -185
- package/src/gui/currentTimeContext.ts +0 -5
- package/src/gui/durationContext.ts +0 -3
- package/src/gui/efContext.ts +0 -6
- package/src/gui/fetchContext.ts +0 -5
- package/src/gui/focusContext.ts +0 -7
- package/src/gui/focusedElementContext.ts +0 -5
- package/src/gui/playingContext.ts +0 -5
- package/src/otel/BridgeSpanExporter.ts +0 -150
- package/src/otel/setupBrowserTracing.ts +0 -73
- package/src/otel/tracingHelpers.ts +0 -251
- package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
- package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
- package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
- package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
- package/src/transcoding/types/index.ts +0 -312
- package/src/transcoding/utils/MediaUtils.ts +0 -63
- package/src/transcoding/utils/UrlGenerator.ts +0 -68
- package/src/transcoding/utils/constants.ts +0 -36
- package/src/utils/LRUCache.test.ts +0 -274
- package/src/utils/LRUCache.ts +0 -696
package/src/gui/EFScrubber.ts
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { consume } from "@lit/context";
|
|
2
|
-
import { css, html, LitElement } from "lit";
|
|
3
|
-
import { customElement, state } from "lit/decorators.js";
|
|
4
|
-
|
|
5
|
-
import { ref } from "lit/directives/ref.js";
|
|
6
|
-
import type { ControllableInterface } from "./Controllable.js";
|
|
7
|
-
import { currentTimeContext } from "./currentTimeContext.js";
|
|
8
|
-
import { durationContext } from "./durationContext.js";
|
|
9
|
-
import { efContext } from "./efContext.js";
|
|
10
|
-
import { playingContext } from "./playingContext.js";
|
|
11
|
-
import { TargetOrContextMixin } from "./TargetOrContextMixin.js";
|
|
12
|
-
|
|
13
|
-
@customElement("ef-scrubber")
|
|
14
|
-
export class EFScrubber extends TargetOrContextMixin(LitElement, efContext) {
|
|
15
|
-
static styles = [
|
|
16
|
-
css`
|
|
17
|
-
:host {
|
|
18
|
-
--ef-scrubber-height: 4px;
|
|
19
|
-
--ef-scrubber-background: rgb(209 213 219);
|
|
20
|
-
--ef-scrubber-progress-color: rgb(37 99 235);
|
|
21
|
-
--ef-scrubber-handle-size: 12px;
|
|
22
|
-
width: 100%;
|
|
23
|
-
display: flex;
|
|
24
|
-
align-items: center;
|
|
25
|
-
justify-content: center;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.scrubber {
|
|
29
|
-
width: 100%;
|
|
30
|
-
height: var(--ef-scrubber-height);
|
|
31
|
-
background: var(--ef-scrubber-background);
|
|
32
|
-
position: relative;
|
|
33
|
-
cursor: pointer;
|
|
34
|
-
border-radius: 2px;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.progress {
|
|
38
|
-
position: absolute;
|
|
39
|
-
height: 100%;
|
|
40
|
-
background: var(--ef-scrubber-progress-color);
|
|
41
|
-
border-radius: 2px;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.handle {
|
|
45
|
-
position: absolute;
|
|
46
|
-
width: var(--ef-scrubber-handle-size);
|
|
47
|
-
height: var(--ef-scrubber-handle-size);
|
|
48
|
-
background: var(--ef-scrubber-progress-color);
|
|
49
|
-
border-radius: 50%;
|
|
50
|
-
top: 50%;
|
|
51
|
-
transform: translate(-50%, -50%);
|
|
52
|
-
cursor: grab;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/* Add CSS Shadow Parts */
|
|
56
|
-
::part(scrubber) { }
|
|
57
|
-
::part(progress) { }
|
|
58
|
-
::part(handle) { }
|
|
59
|
-
`,
|
|
60
|
-
];
|
|
61
|
-
|
|
62
|
-
@consume({ context: playingContext, subscribe: true })
|
|
63
|
-
playing = false;
|
|
64
|
-
|
|
65
|
-
@consume({ context: currentTimeContext, subscribe: true })
|
|
66
|
-
currentTimeMs = Number.NaN;
|
|
67
|
-
|
|
68
|
-
@consume({ context: durationContext, subscribe: true })
|
|
69
|
-
durationMs = 0;
|
|
70
|
-
|
|
71
|
-
get context(): ControllableInterface | null {
|
|
72
|
-
return this.effectiveContext;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
@state()
|
|
76
|
-
private scrubProgress = 0;
|
|
77
|
-
|
|
78
|
-
@state()
|
|
79
|
-
private isMoving = false;
|
|
80
|
-
|
|
81
|
-
private scrubberRef?: HTMLElement;
|
|
82
|
-
|
|
83
|
-
private updateProgress(e: MouseEvent) {
|
|
84
|
-
if (!this.context || !this.scrubberRef) return;
|
|
85
|
-
|
|
86
|
-
const rect = this.scrubberRef.getBoundingClientRect();
|
|
87
|
-
const x = e.clientX - rect.left;
|
|
88
|
-
const progress = Math.max(0, Math.min(1, x / rect.width));
|
|
89
|
-
|
|
90
|
-
this.scrubProgress = progress;
|
|
91
|
-
this.context.currentTimeMs = progress * this.durationMs;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
private boundHandlePointerDown = (e: MouseEvent) => {
|
|
95
|
-
this.isMoving = true;
|
|
96
|
-
e.preventDefault();
|
|
97
|
-
this.updateProgress(e);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
private boundHandlePointerMove = (e: MouseEvent) => {
|
|
101
|
-
if (this.isMoving) {
|
|
102
|
-
this.updateProgress(e);
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
private boundHandlePointerUp = () => {
|
|
107
|
-
this.isMoving = false;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
render() {
|
|
111
|
-
// Calculate progress from currentTimeMs and duration
|
|
112
|
-
const currentProgress =
|
|
113
|
-
this.durationMs > 0 ? (this.currentTimeMs ?? 0) / this.durationMs : 0;
|
|
114
|
-
|
|
115
|
-
const displayProgress = this.isMoving
|
|
116
|
-
? this.scrubProgress
|
|
117
|
-
: currentProgress;
|
|
118
|
-
|
|
119
|
-
return html`
|
|
120
|
-
<div
|
|
121
|
-
${ref((el) => {
|
|
122
|
-
this.scrubberRef = el as HTMLElement;
|
|
123
|
-
})}
|
|
124
|
-
part="scrubber"
|
|
125
|
-
class="scrubber"
|
|
126
|
-
@mousedown=${this.boundHandlePointerDown}
|
|
127
|
-
>
|
|
128
|
-
<div class="progress" style="width: ${displayProgress * 100}%"></div>
|
|
129
|
-
<div class="handle" style="left: ${displayProgress * 100}%"></div>
|
|
130
|
-
</div>
|
|
131
|
-
`;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
connectedCallback() {
|
|
135
|
-
super.connectedCallback();
|
|
136
|
-
window.addEventListener("pointerup", this.boundHandlePointerUp);
|
|
137
|
-
window.addEventListener("pointermove", this.boundHandlePointerMove);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
disconnectedCallback() {
|
|
141
|
-
super.disconnectedCallback();
|
|
142
|
-
window.removeEventListener("pointerup", this.boundHandlePointerUp);
|
|
143
|
-
window.removeEventListener("pointermove", this.boundHandlePointerMove);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
declare global {
|
|
148
|
-
interface HTMLElementTagNameMap {
|
|
149
|
-
"ef-scrubber": EFScrubber;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
import { html, render as litRender, type TemplateResult } from "lit";
|
|
2
|
-
import { assert, beforeEach, describe, test, vi } from "vitest";
|
|
3
|
-
import { EFTimeDisplay } from "./EFTimeDisplay.js";
|
|
4
|
-
import "./EFTimeDisplay.js";
|
|
5
|
-
import type { EFTimegroup } from "../elements/EFTimegroup.js";
|
|
6
|
-
import "../elements/EFTimegroup.js";
|
|
7
|
-
import "./EFPreview.js";
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
11
|
-
const key = localStorage.key(i);
|
|
12
|
-
if (typeof key !== "string") continue;
|
|
13
|
-
localStorage.removeItem(key);
|
|
14
|
-
}
|
|
15
|
-
while (document.body.children.length) {
|
|
16
|
-
document.body.children[0]?.remove();
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const renderTimeDisplay = (result: TemplateResult) => {
|
|
21
|
-
const container = document.createElement("div");
|
|
22
|
-
litRender(result, container);
|
|
23
|
-
const timeDisplay = container.querySelector("ef-time-display");
|
|
24
|
-
if (!timeDisplay) {
|
|
25
|
-
throw new Error("No ef-time-display found");
|
|
26
|
-
}
|
|
27
|
-
if (!(timeDisplay instanceof EFTimeDisplay)) {
|
|
28
|
-
throw new Error("Element is not an EFTimeDisplay");
|
|
29
|
-
}
|
|
30
|
-
document.body.appendChild(container);
|
|
31
|
-
return { timeDisplay, container };
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
describe("EFTimeDisplay", () => {
|
|
35
|
-
test("shows 0:00 / 0:00 when context is null", async () => {
|
|
36
|
-
const { timeDisplay } = renderTimeDisplay(
|
|
37
|
-
html`<ef-time-display></ef-time-display>`,
|
|
38
|
-
);
|
|
39
|
-
await timeDisplay.updateComplete;
|
|
40
|
-
|
|
41
|
-
const timeText = timeDisplay.shadowRoot?.textContent?.trim();
|
|
42
|
-
assert.equal(timeText, "0:00 / 0:00");
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("works correctly with real EFTimegroup context", async () => {
|
|
46
|
-
const { timeDisplay } = renderTimeDisplay(
|
|
47
|
-
html`<ef-preview>
|
|
48
|
-
<ef-timegroup mode="fixed" duration="5s">
|
|
49
|
-
<ef-time-display></ef-time-display>
|
|
50
|
-
</ef-timegroup>
|
|
51
|
-
</ef-preview>`,
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
await timeDisplay.updateComplete;
|
|
55
|
-
|
|
56
|
-
const timeText = timeDisplay.shadowRoot?.textContent?.trim();
|
|
57
|
-
assert.equal(
|
|
58
|
-
timeText,
|
|
59
|
-
"0:00 / 0:05",
|
|
60
|
-
"Should work with real timegroup context",
|
|
61
|
-
);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("handles undefined currentTimeMs gracefully", async () => {
|
|
65
|
-
const { timeDisplay } = renderTimeDisplay(
|
|
66
|
-
html`<ef-preview>
|
|
67
|
-
<ef-timegroup mode="fixed" duration="5s"></ef-timegroup>
|
|
68
|
-
<ef-time-display></ef-time-display>
|
|
69
|
-
</ef-preview>`,
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
// const context = timeDisplay.closest("ef-preview") ;
|
|
73
|
-
await timeDisplay.updateComplete;
|
|
74
|
-
|
|
75
|
-
const timeText = timeDisplay.shadowRoot?.textContent?.trim();
|
|
76
|
-
assert.equal(
|
|
77
|
-
timeText,
|
|
78
|
-
"0:00 / 0:05",
|
|
79
|
-
"Should show 0:00 when currentTimeMs is undefined",
|
|
80
|
-
);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test("handles NaN currentTimeMs gracefully", async () => {
|
|
84
|
-
const { timeDisplay } = renderTimeDisplay(
|
|
85
|
-
html`<ef-preview>
|
|
86
|
-
<ef-timegroup mode="fixed" duration="5s"></ef-timegroup>
|
|
87
|
-
<ef-time-display></ef-time-display>
|
|
88
|
-
</ef-preview>`,
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
const context = timeDisplay.closest("ef-preview")!;
|
|
92
|
-
context.currentTimeMs = Number.NaN;
|
|
93
|
-
await timeDisplay.updateComplete;
|
|
94
|
-
|
|
95
|
-
const timeText = timeDisplay.shadowRoot?.textContent?.trim();
|
|
96
|
-
assert.equal(
|
|
97
|
-
timeText,
|
|
98
|
-
"0:00 / 0:05",
|
|
99
|
-
"Should show 0:00 when currentTimeMs is NaN",
|
|
100
|
-
);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test("formats time correctly with valid values", async () => {
|
|
104
|
-
const { timeDisplay } = renderTimeDisplay(
|
|
105
|
-
html`<ef-preview>
|
|
106
|
-
<ef-timegroup mode="fixed" duration="3s"></ef-timegroup>
|
|
107
|
-
<ef-time-display></ef-time-display>
|
|
108
|
-
</ef-preview>`,
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
const context = timeDisplay.closest("ef-preview")!;
|
|
112
|
-
const timegroup = context.querySelector("ef-timegroup") as EFTimegroup;
|
|
113
|
-
|
|
114
|
-
await timegroup.updateComplete;
|
|
115
|
-
await timegroup.seek(1500); // 1.5 seconds = 0:01
|
|
116
|
-
|
|
117
|
-
// Wait for context to update and propagate to time display
|
|
118
|
-
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
119
|
-
await context.updateComplete;
|
|
120
|
-
await timeDisplay.updateComplete;
|
|
121
|
-
|
|
122
|
-
const timeText = timeDisplay.shadowRoot?.textContent?.trim();
|
|
123
|
-
assert.equal(timeText, "0:01 / 0:03");
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("formats minutes correctly", async () => {
|
|
127
|
-
const { timeDisplay } = renderTimeDisplay(
|
|
128
|
-
html`<ef-preview>
|
|
129
|
-
<ef-timegroup mode="fixed" duration="125s"></ef-timegroup>
|
|
130
|
-
<ef-time-display></ef-time-display>
|
|
131
|
-
</ef-preview>`,
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
const context = timeDisplay.closest("ef-preview")!;
|
|
135
|
-
|
|
136
|
-
// Create a mock timegroup with longer duration
|
|
137
|
-
const mockTimegroup = document.createElement("ef-timegroup") as EFTimegroup;
|
|
138
|
-
mockTimegroup.setAttribute("mode", "fixed");
|
|
139
|
-
mockTimegroup.setAttribute("duration", "125s"); // 2:05
|
|
140
|
-
|
|
141
|
-
context.currentTimeMs = 75000; // 75 seconds = 1:15
|
|
142
|
-
await vi.waitUntil(
|
|
143
|
-
() => timeDisplay.shadowRoot?.textContent?.trim() === "1:15 / 2:05",
|
|
144
|
-
);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test("updates display when time changes", async () => {
|
|
148
|
-
const { timeDisplay } = renderTimeDisplay(
|
|
149
|
-
html`<ef-preview>
|
|
150
|
-
<ef-timegroup mode="fixed" duration="10s"></ef-timegroup>
|
|
151
|
-
<ef-time-display></ef-time-display>
|
|
152
|
-
</ef-preview>`,
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
const context = timeDisplay.closest("ef-preview")!;
|
|
156
|
-
const timegroup = context.querySelector("ef-timegroup") as EFTimegroup;
|
|
157
|
-
|
|
158
|
-
await timegroup.updateComplete;
|
|
159
|
-
|
|
160
|
-
// Initial time
|
|
161
|
-
await timegroup.seek(2000); // 2 seconds = 0:02
|
|
162
|
-
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
163
|
-
await context.updateComplete;
|
|
164
|
-
await timeDisplay.updateComplete;
|
|
165
|
-
|
|
166
|
-
let timeText = timeDisplay.shadowRoot?.textContent?.trim();
|
|
167
|
-
assert.equal(timeText, "0:02 / 0:10");
|
|
168
|
-
|
|
169
|
-
// Update time
|
|
170
|
-
await timegroup.seek(7000); // 7 seconds = 0:07
|
|
171
|
-
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
172
|
-
await context.updateComplete;
|
|
173
|
-
await timeDisplay.updateComplete;
|
|
174
|
-
|
|
175
|
-
timeText = timeDisplay.shadowRoot?.textContent?.trim();
|
|
176
|
-
assert.equal(timeText, "0:07 / 0:10", "Should update when time changes");
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
test("handles zero duration", async () => {
|
|
180
|
-
const { timeDisplay } = renderTimeDisplay(
|
|
181
|
-
html`<ef-preview>
|
|
182
|
-
<ef-timegroup mode="fixed" duration="10s"></ef-timegroup>
|
|
183
|
-
<ef-time-display></ef-time-display>
|
|
184
|
-
</ef-preview>`,
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
const context = timeDisplay.closest("ef-preview")!;
|
|
188
|
-
const timegroup = context.targetTemporal!;
|
|
189
|
-
|
|
190
|
-
(timegroup as unknown as Element).setAttribute("duration", "0s");
|
|
191
|
-
assert.equal(timegroup.durationMs, 0);
|
|
192
|
-
|
|
193
|
-
await timegroup.updateComplete;
|
|
194
|
-
await timeDisplay.updateComplete;
|
|
195
|
-
await vi.waitUntil(
|
|
196
|
-
() => timeDisplay.shadowRoot?.textContent?.trim() === "0:00 / 0:00",
|
|
197
|
-
);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
test("handles context changes correctly", async () => {
|
|
201
|
-
const { timeDisplay } = renderTimeDisplay(
|
|
202
|
-
html`<ef-preview>
|
|
203
|
-
<ef-timegroup mode="fixed" duration="10s"></ef-timegroup>
|
|
204
|
-
<ef-time-display></ef-time-display>
|
|
205
|
-
</ef-preview>`,
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
const context = timeDisplay.closest("ef-preview")!;
|
|
209
|
-
|
|
210
|
-
// Set initial values
|
|
211
|
-
context.currentTimeMs = 1000;
|
|
212
|
-
await timeDisplay.updateComplete;
|
|
213
|
-
|
|
214
|
-
let timeText = timeDisplay.shadowRoot?.textContent?.trim();
|
|
215
|
-
assert.equal(timeText, "0:01 / 0:10");
|
|
216
|
-
|
|
217
|
-
// Move to a different context (using renderTimeDisplay to get proper TestContext)
|
|
218
|
-
const { timeDisplay: newTimeDisplay, container: newContainer } =
|
|
219
|
-
renderTimeDisplay(
|
|
220
|
-
html`<ef-preview>
|
|
221
|
-
<ef-timegroup mode="fixed" duration="10s"></ef-timegroup>
|
|
222
|
-
<ef-time-display></ef-time-display>
|
|
223
|
-
</ef-preview>`,
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
const newContext = newTimeDisplay.closest("ef-preview")!;
|
|
227
|
-
|
|
228
|
-
// Set different values in new context
|
|
229
|
-
newContext.currentTimeMs = 3000;
|
|
230
|
-
await newTimeDisplay.updateComplete;
|
|
231
|
-
|
|
232
|
-
timeText = newTimeDisplay.shadowRoot?.textContent?.trim();
|
|
233
|
-
assert.equal(timeText, "0:03 / 0:10", "Should work with new context");
|
|
234
|
-
|
|
235
|
-
newContainer.remove();
|
|
236
|
-
});
|
|
237
|
-
});
|
package/src/gui/EFTimeDisplay.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { consume } from "@lit/context";
|
|
2
|
-
import { css, html, LitElement } from "lit";
|
|
3
|
-
import { customElement } from "lit/decorators.js";
|
|
4
|
-
import { currentTimeContext } from "./currentTimeContext.js";
|
|
5
|
-
import { durationContext } from "./durationContext.js";
|
|
6
|
-
import { efContext } from "./efContext.js";
|
|
7
|
-
import { TargetOrContextMixin } from "./TargetOrContextMixin.js";
|
|
8
|
-
|
|
9
|
-
@customElement("ef-time-display")
|
|
10
|
-
export class EFTimeDisplay extends TargetOrContextMixin(LitElement, efContext) {
|
|
11
|
-
static styles = css`
|
|
12
|
-
:host {
|
|
13
|
-
display: inline-block;
|
|
14
|
-
font-family: var(--ef-font-family, system-ui);
|
|
15
|
-
font-size: var(--ef-font-size-xs, 0.75rem);
|
|
16
|
-
color: var(--ef-text-color, rgb(75 85 99));
|
|
17
|
-
white-space: nowrap;
|
|
18
|
-
}
|
|
19
|
-
`;
|
|
20
|
-
|
|
21
|
-
@consume({ context: currentTimeContext, subscribe: true })
|
|
22
|
-
currentTimeMs = Number.NaN;
|
|
23
|
-
|
|
24
|
-
@consume({ context: durationContext, subscribe: true })
|
|
25
|
-
durationMs = 0;
|
|
26
|
-
|
|
27
|
-
private formatTime(ms: number): string {
|
|
28
|
-
// Handle NaN, undefined, null, or negative values
|
|
29
|
-
if (!Number.isFinite(ms) || ms < 0) {
|
|
30
|
-
return "0:00";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const totalSeconds = Math.floor(ms / 1000);
|
|
34
|
-
const minutes = Math.floor(totalSeconds / 60);
|
|
35
|
-
const seconds = totalSeconds % 60;
|
|
36
|
-
return `${minutes}:${seconds.toString().padStart(2, "0")}`;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
render() {
|
|
40
|
-
const currentTime = this.currentTimeMs;
|
|
41
|
-
const totalTime = this.durationMs;
|
|
42
|
-
|
|
43
|
-
return html`
|
|
44
|
-
<span part="time">
|
|
45
|
-
${this.formatTime(currentTime)} / ${this.formatTime(totalTime)}
|
|
46
|
-
</span>
|
|
47
|
-
`;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
declare global {
|
|
52
|
-
interface HTMLElementTagNameMap {
|
|
53
|
-
"ef-time-display": EFTimeDisplay;
|
|
54
|
-
}
|
|
55
|
-
}
|
package/src/gui/EFToggleLoop.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { css, html, LitElement } from "lit";
|
|
2
|
-
import { customElement } from "lit/decorators.js";
|
|
3
|
-
|
|
4
|
-
import type { ControllableInterface } from "./Controllable.js";
|
|
5
|
-
import { efContext } from "./efContext.js";
|
|
6
|
-
import { TargetOrContextMixin } from "./TargetOrContextMixin.js";
|
|
7
|
-
|
|
8
|
-
@customElement("ef-toggle-loop")
|
|
9
|
-
export class EFToggleLoop extends TargetOrContextMixin(LitElement, efContext) {
|
|
10
|
-
static styles = [
|
|
11
|
-
css`
|
|
12
|
-
:host {}
|
|
13
|
-
`,
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
get context(): ControllableInterface | null {
|
|
17
|
-
return this.effectiveContext;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
render() {
|
|
21
|
-
return html`
|
|
22
|
-
<slot @click=${() => {
|
|
23
|
-
if (this.context) {
|
|
24
|
-
this.context.loop = !this.context.loop;
|
|
25
|
-
}
|
|
26
|
-
}}></slot>
|
|
27
|
-
`;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
declare global {
|
|
32
|
-
interface HTMLElementTagNameMap {
|
|
33
|
-
"ef-toggle-loop": EFToggleLoop;
|
|
34
|
-
}
|
|
35
|
-
}
|
package/src/gui/EFTogglePlay.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { consume } from "@lit/context";
|
|
2
|
-
import { css, html, LitElement } from "lit";
|
|
3
|
-
import { customElement, state } from "lit/decorators.js";
|
|
4
|
-
import { attachContextRoot } from "../attachContextRoot.js";
|
|
5
|
-
import type { ControllableInterface } from "./Controllable.js";
|
|
6
|
-
import { efContext } from "./efContext.js";
|
|
7
|
-
import { playingContext } from "./playingContext.js";
|
|
8
|
-
import { TargetOrContextMixin } from "./TargetOrContextMixin.js";
|
|
9
|
-
|
|
10
|
-
attachContextRoot();
|
|
11
|
-
|
|
12
|
-
@customElement("ef-toggle-play")
|
|
13
|
-
export class EFTogglePlay extends TargetOrContextMixin(LitElement, efContext) {
|
|
14
|
-
static styles = [
|
|
15
|
-
css`
|
|
16
|
-
:host {}
|
|
17
|
-
div {
|
|
18
|
-
all: inherit;
|
|
19
|
-
}
|
|
20
|
-
`,
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
@consume({ context: playingContext, subscribe: true })
|
|
24
|
-
@state()
|
|
25
|
-
playing = false;
|
|
26
|
-
|
|
27
|
-
get efContext(): ControllableInterface | null {
|
|
28
|
-
return this.effectiveContext;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Attach click listener to host
|
|
32
|
-
connectedCallback() {
|
|
33
|
-
super.connectedCallback();
|
|
34
|
-
this.addEventListener("click", this.togglePlay);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Detach click listener from host
|
|
38
|
-
disconnectedCallback() {
|
|
39
|
-
super.disconnectedCallback();
|
|
40
|
-
this.removeEventListener("click", this.togglePlay);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
render() {
|
|
44
|
-
return html`
|
|
45
|
-
<div>
|
|
46
|
-
${
|
|
47
|
-
this.playing
|
|
48
|
-
? html`<slot name="pause"></slot>`
|
|
49
|
-
: html`<slot name="play"></slot>`
|
|
50
|
-
}
|
|
51
|
-
</div>
|
|
52
|
-
`;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
togglePlay = () => {
|
|
56
|
-
if (this.efContext) {
|
|
57
|
-
if (this.playing) {
|
|
58
|
-
this.efContext.pause();
|
|
59
|
-
} else {
|
|
60
|
-
this.efContext.play();
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
declare global {
|
|
67
|
-
interface HTMLElementTagNameMap {
|
|
68
|
-
"ef-toggle-play": EFTogglePlay;
|
|
69
|
-
}
|
|
70
|
-
}
|
package/src/gui/EFWorkbench.ts
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { css, html, LitElement, type PropertyValueMap } from "lit";
|
|
2
|
-
import { customElement, eventOptions, property } from "lit/decorators.js";
|
|
3
|
-
import { createRef, ref } from "lit/directives/ref.js";
|
|
4
|
-
|
|
5
|
-
import { ContextMixin } from "./ContextMixin.js";
|
|
6
|
-
import { TWMixin } from "./TWMixin.js";
|
|
7
|
-
|
|
8
|
-
@customElement("ef-workbench")
|
|
9
|
-
export class EFWorkbench extends ContextMixin(TWMixin(LitElement)) {
|
|
10
|
-
static styles = [
|
|
11
|
-
css`
|
|
12
|
-
:host {
|
|
13
|
-
display: block;
|
|
14
|
-
width: 100%;
|
|
15
|
-
height: 100%;
|
|
16
|
-
}
|
|
17
|
-
`,
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
@property({ type: Boolean })
|
|
21
|
-
rendering = false;
|
|
22
|
-
|
|
23
|
-
focusOverlay = createRef<HTMLDivElement>();
|
|
24
|
-
|
|
25
|
-
@eventOptions({ passive: false, capture: true })
|
|
26
|
-
handleStageWheel(event: WheelEvent) {
|
|
27
|
-
event.preventDefault();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
connectedCallback(): void {
|
|
31
|
-
document.body.style.width = "100%";
|
|
32
|
-
document.body.style.height = "100%";
|
|
33
|
-
document.documentElement.style.width = "100%";
|
|
34
|
-
document.documentElement.style.height = "100%";
|
|
35
|
-
super.connectedCallback();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
disconnectedCallback(): void {
|
|
39
|
-
super.disconnectedCallback();
|
|
40
|
-
document.body.style.width = "";
|
|
41
|
-
document.body.style.height = "";
|
|
42
|
-
document.documentElement.style.width = "";
|
|
43
|
-
document.documentElement.style.height = "";
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
update(
|
|
47
|
-
changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>,
|
|
48
|
-
): void {
|
|
49
|
-
super.update(changedProperties);
|
|
50
|
-
|
|
51
|
-
if (changedProperties.has("focusedElement")) {
|
|
52
|
-
this.drawOverlays();
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
drawOverlays = () => {
|
|
57
|
-
const focusOverlay = this.focusOverlay.value;
|
|
58
|
-
if (focusOverlay) {
|
|
59
|
-
if (this.focusedElement) {
|
|
60
|
-
focusOverlay.style.display = "block";
|
|
61
|
-
const rect = this.focusedElement.getBoundingClientRect();
|
|
62
|
-
Object.assign(focusOverlay.style, {
|
|
63
|
-
position: "fixed",
|
|
64
|
-
top: `${rect.top}px`,
|
|
65
|
-
left: `${rect.left}px`,
|
|
66
|
-
width: `${rect.width}px`,
|
|
67
|
-
height: `${rect.height}px`,
|
|
68
|
-
});
|
|
69
|
-
requestAnimationFrame(this.drawOverlays);
|
|
70
|
-
} else {
|
|
71
|
-
focusOverlay.style.display = "none";
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
render() {
|
|
77
|
-
// TODO: this.rendering is not correctly set when using the framegen bridge
|
|
78
|
-
// so to hack we're checking for the existence of EF_RENDERING on the window
|
|
79
|
-
if (
|
|
80
|
-
this.rendering ||
|
|
81
|
-
(typeof window !== "undefined" && window.EF_RENDERING?.() === true)
|
|
82
|
-
) {
|
|
83
|
-
return html`
|
|
84
|
-
<slot class="fixed inset-0 h-full w-full" name="canvas"></slot>
|
|
85
|
-
`;
|
|
86
|
-
}
|
|
87
|
-
return html`
|
|
88
|
-
<div
|
|
89
|
-
class="grid h-full w-full bg-slate-800"
|
|
90
|
-
style="grid-template-rows: 1fr 300px; grid-template-columns: 100%;"
|
|
91
|
-
>
|
|
92
|
-
<div
|
|
93
|
-
class="relative h-full w-full overflow-hidden"
|
|
94
|
-
@wheel=${this.handleStageWheel}
|
|
95
|
-
>
|
|
96
|
-
<ef-fit-scale class="h-full grid place-content-center">
|
|
97
|
-
<slot name="canvas" class="contents"></slot>
|
|
98
|
-
</ef-fit-scale>
|
|
99
|
-
<div
|
|
100
|
-
class="border border-blue-500 bg-blue-200 bg-opacity-20 absolute"
|
|
101
|
-
${ref(this.focusOverlay)}
|
|
102
|
-
></div>
|
|
103
|
-
</div>
|
|
104
|
-
|
|
105
|
-
<slot class="overflow inline-block" name="timeline"></slot>
|
|
106
|
-
</div>
|
|
107
|
-
`;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
declare global {
|
|
112
|
-
interface HTMLElementTagNameMap {
|
|
113
|
-
"ef-workbench": EFWorkbench;
|
|
114
|
-
}
|
|
115
|
-
}
|