@editframe/elements 0.49.6 → 0.50.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/attachContextRoot.js +1 -1
- package/dist/attachContextRoot.js.map +1 -1
- package/dist/elements/EFCaptions.js +3 -3
- package/dist/elements/EFCaptions.js.map +1 -1
- package/dist/elements/EFTemporal.js +3 -1
- package/dist/elements/EFTemporal.js.map +1 -1
- package/dist/elements/EFText.js +1 -1
- package/dist/elements/EFTimegroup.d.ts +10 -0
- package/dist/elements/EFTimegroup.js +27 -2
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/dist/elements/EFVideo.js +1 -1
- package/dist/gui/ContextMixin.js +1 -1
- package/dist/gui/EFControls.js +2 -2
- package/dist/gui/EFPause.js +2 -2
- package/dist/gui/EFPlay.js +2 -2
- package/dist/gui/EFScrubber.js +1 -1
- package/dist/gui/EFTimeDisplay.js +1 -1
- package/dist/gui/EFTimelineRuler.js +1 -1
- package/dist/gui/EFTogglePlay.js +2 -2
- package/dist/gui/EFWorkbench.js +1 -1
- package/dist/gui/PlaybackController.d.ts +1 -0
- package/dist/gui/PlaybackController.js +5 -4
- package/dist/gui/PlaybackController.js.map +1 -1
- package/dist/gui/TargetOrContextMixin.js +1 -1
- package/dist/gui/timeline/EFTimeline.d.ts +16 -11
- package/dist/gui/timeline/EFTimeline.js +163 -86
- package/dist/gui/timeline/EFTimeline.js.map +1 -1
- package/dist/gui/timeline/EFTimelineRow.js +9 -9
- package/dist/gui/timeline/tracks/CaptionsTrack.js +7 -6
- package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/EFThumbnailStrip.js +24 -10
- package/dist/gui/timeline/tracks/EFThumbnailStrip.js.map +1 -1
- package/dist/gui/timeline/tracks/TextTrack.js +26 -23
- package/dist/gui/timeline/tracks/TextTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/TimegroupTrack.js +10 -21
- package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -1
- package/dist/preview/renderTimegroupToCanvas.js +37 -5
- package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
- package/dist/preview/rendering/renderToImageNative.js +19 -7
- package/dist/preview/rendering/renderToImageNative.js.map +1 -1
- package/dist/preview/rendering/serializeTimelineDirect.js +2 -0
- package/dist/preview/rendering/serializeTimelineDirect.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/dist/node_modules/@lit/reactive-element/css-tag.js +0 -40
- package/dist/node_modules/@lit/reactive-element/css-tag.js.map +0 -1
- package/dist/node_modules/@lit/reactive-element/reactive-element.js +0 -234
- package/dist/node_modules/@lit/reactive-element/reactive-element.js.map +0 -1
|
@@ -69,6 +69,7 @@ declare class EFTimeline extends EFTimeline_base {
|
|
|
69
69
|
* This should be set to the canvas element.
|
|
70
70
|
*/
|
|
71
71
|
private targetElement;
|
|
72
|
+
private _highlightedElement;
|
|
72
73
|
private currentTimeMs;
|
|
73
74
|
private isPlaying;
|
|
74
75
|
private isLooping;
|
|
@@ -81,6 +82,7 @@ declare class EFTimeline extends EFTimeline_base {
|
|
|
81
82
|
private playheadRef;
|
|
82
83
|
private playheadHandleRef;
|
|
83
84
|
private frameHighlightRef;
|
|
85
|
+
private playheadLayerRef;
|
|
84
86
|
private animationFrameId?;
|
|
85
87
|
private selectionChangeHandler?;
|
|
86
88
|
private scrollHandler?;
|
|
@@ -112,12 +114,13 @@ declare class EFTimeline extends EFTimeline_base {
|
|
|
112
114
|
private getCanvas;
|
|
113
115
|
private getCanvasSelectionContext;
|
|
114
116
|
/**
|
|
115
|
-
* Get the currently highlighted element
|
|
117
|
+
* Get the currently highlighted element.
|
|
118
|
+
* Local state is the source of truth; canvas is kept in sync as a side effect.
|
|
116
119
|
*/
|
|
117
120
|
getHighlightedElement(): HTMLElement | null;
|
|
118
121
|
/**
|
|
119
|
-
* Set the highlighted element
|
|
120
|
-
*
|
|
122
|
+
* Set the highlighted element.
|
|
123
|
+
* Always updates local state (triggers re-render); also syncs to canvas when present.
|
|
121
124
|
*/
|
|
122
125
|
setHighlightedElement(element: HTMLElement | null): void;
|
|
123
126
|
get targetTemporal(): TemporalMixinInterface | null;
|
|
@@ -170,6 +173,12 @@ declare class EFTimeline extends EFTimeline_base {
|
|
|
170
173
|
protected updated(changedProperties: PropertyValues): void;
|
|
171
174
|
private setupSelectionListener;
|
|
172
175
|
private removeSelectionListener;
|
|
176
|
+
/**
|
|
177
|
+
* Single source-of-truth for applying a scroll position change.
|
|
178
|
+
* Keeps ruler-outer, viewportScrollLeft, playhead, and clip-path all in sync
|
|
179
|
+
* immediately — without relying on the async scroll event.
|
|
180
|
+
*/
|
|
181
|
+
private applyScrollLeft;
|
|
173
182
|
private setupScrollListener;
|
|
174
183
|
private removeScrollListener;
|
|
175
184
|
private setupKeyboardListener;
|
|
@@ -251,14 +260,10 @@ declare class EFTimeline extends EFTimeline_base {
|
|
|
251
260
|
private renderTimeDisplay;
|
|
252
261
|
private renderZoomControls;
|
|
253
262
|
/**
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Render frame highlight (mechanism).
|
|
260
|
-
* Shows the current frame as a rectangle to indicate frames have duration.
|
|
261
|
-
* Only rendered when frame markers are visible (zoom level high enough).
|
|
263
|
+
* Render the frame highlight placeholder.
|
|
264
|
+
* Position and visibility are controlled exclusively by updatePlayheadPositionDirect
|
|
265
|
+
* via the frameHighlightRef so that Lit re-renders never clobber the 60fps rAF
|
|
266
|
+
* position with the throttled this.currentTimeMs value.
|
|
262
267
|
*/
|
|
263
268
|
private renderFrameHighlight;
|
|
264
269
|
private renderControls;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { TWMixin } from "../TWMixin2.js";
|
|
2
|
+
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorate.js";
|
|
2
3
|
import { currentTimeContext } from "../currentTimeContext.js";
|
|
3
4
|
import { durationContext } from "../durationContext.js";
|
|
4
5
|
import { loopContext, playingContext } from "../playingContext.js";
|
|
5
|
-
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.122.0/helpers/decorate.js";
|
|
6
6
|
import { isEFTemporal } from "../../elements/EFTemporal.js";
|
|
7
7
|
import { targetTemporalContext } from "../ContextMixin.js";
|
|
8
8
|
import { TargetController } from "../../elements/TargetController.js";
|
|
@@ -20,7 +20,6 @@ import { EFTimegroup } from "../../elements/EFTimegroup.js";
|
|
|
20
20
|
import { consume, provide } from "@lit/context";
|
|
21
21
|
import { LitElement, css, html, nothing } from "lit";
|
|
22
22
|
import { customElement, eventOptions, property, state } from "lit/decorators.js";
|
|
23
|
-
import { styleMap } from "lit/directives/style-map.js";
|
|
24
23
|
import { createRef, ref } from "lit/directives/ref.js";
|
|
25
24
|
import { repeat } from "lit/directives/repeat.js";
|
|
26
25
|
//#region src/gui/timeline/EFTimeline.ts
|
|
@@ -46,6 +45,7 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
46
45
|
this.hide = "";
|
|
47
46
|
this.show = "";
|
|
48
47
|
this.targetElement = null;
|
|
48
|
+
this._highlightedElement = null;
|
|
49
49
|
this.currentTimeMs = 0;
|
|
50
50
|
this.isPlaying = false;
|
|
51
51
|
this.isLooping = false;
|
|
@@ -67,6 +67,7 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
67
67
|
this.playheadRef = createRef();
|
|
68
68
|
this.playheadHandleRef = createRef();
|
|
69
69
|
this.frameHighlightRef = createRef();
|
|
70
|
+
this.playheadLayerRef = createRef();
|
|
70
71
|
this.isDraggingPlayhead = false;
|
|
71
72
|
this.cachedViewportWidth = 800;
|
|
72
73
|
this.saveZoomScrollDebounceTimer = null;
|
|
@@ -83,13 +84,14 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
83
84
|
width: 100%;
|
|
84
85
|
height: 100%;
|
|
85
86
|
min-height: 100px;
|
|
87
|
+
user-select: none;
|
|
86
88
|
|
|
87
89
|
/* Layout coordination via CSS custom properties */
|
|
88
90
|
--timeline-hierarchy-width: var(--ef-hierarchy-width, 200px);
|
|
89
91
|
--timeline-row-height: var(--ef-row-height, 24px);
|
|
90
92
|
--timeline-track-height: var(--ef-track-height, 24px);
|
|
91
93
|
|
|
92
|
-
/* Component tokens
|
|
94
|
+
/* Component alias tokens — resolved from ef-color-* regardless of theme */
|
|
93
95
|
--timeline-bg: var(--ef-color-bg);
|
|
94
96
|
--timeline-border: var(--ef-color-border);
|
|
95
97
|
--timeline-header-bg: var(--ef-color-bg-panel);
|
|
@@ -98,6 +100,78 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
98
100
|
--timeline-track-bg: var(--ef-color-bg-inset);
|
|
99
101
|
--timeline-track-hover: var(--ef-color-hover);
|
|
100
102
|
--timeline-playhead: var(--ef-color-playhead);
|
|
103
|
+
|
|
104
|
+
/* Filmstrip alias tokens */
|
|
105
|
+
--filmstrip-bg: var(--ef-color-bg-inset);
|
|
106
|
+
--filmstrip-item-focused: rgba(229, 57, 53, 0.3);
|
|
107
|
+
--filmstrip-border: var(--ef-color-border);
|
|
108
|
+
--filmstrip-caption-bg: rgba(76, 175, 80, 0.15);
|
|
109
|
+
--filmstrip-caption-border: rgba(76, 175, 80, 0.5);
|
|
110
|
+
--filmstrip-segment-bg: rgba(76, 175, 80, 0.3);
|
|
111
|
+
--filmstrip-segment-border: rgba(76, 175, 80, 0.6);
|
|
112
|
+
--filmstrip-timegroup-focused: var(--ef-color-focused);
|
|
113
|
+
--filmstrip-animation-bg: rgba(229, 57, 53, 0.2);
|
|
114
|
+
--filmstrip-keyframe-bg: rgba(229, 57, 53, 0.4);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
:host-context(.dark) {
|
|
118
|
+
--ef-color-bg: #0a0a0a;
|
|
119
|
+
--ef-color-bg-panel: #111111;
|
|
120
|
+
--ef-color-bg-elevated: #1a1a1a;
|
|
121
|
+
--ef-color-bg-inset: #050505;
|
|
122
|
+
--ef-color-border: rgba(255, 255, 255, 0.15);
|
|
123
|
+
--ef-color-border-subtle: rgba(255, 255, 255, 0.08);
|
|
124
|
+
--ef-color-text: #fafafa;
|
|
125
|
+
--ef-color-text-muted: #9ca3af;
|
|
126
|
+
--ef-color-text-subtle: #6b7280;
|
|
127
|
+
--ef-color-primary: #3b82f6;
|
|
128
|
+
--ef-color-primary-subtle: rgba(59, 130, 246, 0.15);
|
|
129
|
+
--ef-color-hover: rgba(255, 255, 255, 0.06);
|
|
130
|
+
--ef-color-selected: rgba(59, 130, 246, 0.2);
|
|
131
|
+
--ef-color-selected-subtle: rgba(59, 130, 246, 0.1);
|
|
132
|
+
--ef-color-focused: rgba(59, 130, 246, 0.25);
|
|
133
|
+
--ef-color-success: #059669;
|
|
134
|
+
--ef-color-error: #dc2626;
|
|
135
|
+
--ef-color-playhead: #dc2626;
|
|
136
|
+
--ef-color-type-video: #3b82f6;
|
|
137
|
+
--ef-color-type-audio: #10b981;
|
|
138
|
+
--ef-color-type-image: #8b5cf6;
|
|
139
|
+
--ef-color-type-text: #f59e0b;
|
|
140
|
+
--ef-color-type-captions: #14b8a6;
|
|
141
|
+
--ef-color-type-timegroup: #64748b;
|
|
142
|
+
--filmstrip-item-bg: rgba(17, 17, 17, 0.9);
|
|
143
|
+
--filmstrip-waveform-bg: rgba(0, 0, 0, 0.4);
|
|
144
|
+
--filmstrip-waveform-border: rgba(255, 255, 255, 0.15);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
:host-context(.light) {
|
|
148
|
+
--ef-color-bg: #ffffff;
|
|
149
|
+
--ef-color-bg-panel: #f8f8f8;
|
|
150
|
+
--ef-color-bg-elevated: #ffffff;
|
|
151
|
+
--ef-color-bg-inset: #eeeeee;
|
|
152
|
+
--ef-color-border: rgba(0, 0, 0, 0.20);
|
|
153
|
+
--ef-color-border-subtle: rgba(0, 0, 0, 0.10);
|
|
154
|
+
--ef-color-text: #0a0a0a;
|
|
155
|
+
--ef-color-text-muted: #404040;
|
|
156
|
+
--ef-color-text-subtle: #666666;
|
|
157
|
+
--ef-color-primary: #2563eb;
|
|
158
|
+
--ef-color-primary-subtle: rgba(37, 99, 235, 0.12);
|
|
159
|
+
--ef-color-hover: rgba(0, 0, 0, 0.06);
|
|
160
|
+
--ef-color-selected: rgba(37, 99, 235, 0.18);
|
|
161
|
+
--ef-color-selected-subtle: rgba(37, 99, 235, 0.10);
|
|
162
|
+
--ef-color-focused: rgba(37, 99, 235, 0.25);
|
|
163
|
+
--ef-color-success: #059669;
|
|
164
|
+
--ef-color-error: #dc2626;
|
|
165
|
+
--ef-color-playhead: #dc2626;
|
|
166
|
+
--ef-color-type-video: #2563eb;
|
|
167
|
+
--ef-color-type-audio: #059669;
|
|
168
|
+
--ef-color-type-image: #7c3aed;
|
|
169
|
+
--ef-color-type-text: #ea580c;
|
|
170
|
+
--ef-color-type-captions: #0891b2;
|
|
171
|
+
--ef-color-type-timegroup: #475569;
|
|
172
|
+
--filmstrip-item-bg: rgba(238, 238, 238, 0.95);
|
|
173
|
+
--filmstrip-waveform-bg: rgba(0, 0, 0, 0.06);
|
|
174
|
+
--filmstrip-waveform-border: rgba(0, 0, 0, 0.12);
|
|
101
175
|
}
|
|
102
176
|
|
|
103
177
|
.timeline-container {
|
|
@@ -140,8 +214,8 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
140
214
|
min-width: 32px;
|
|
141
215
|
height: 32px;
|
|
142
216
|
padding: 6px 10px;
|
|
143
|
-
background: var(--ef-color-bg-inset);
|
|
144
|
-
border: 1px solid var(--ef-color-border-subtle);
|
|
217
|
+
background: var(--ef-color-bg-inset, #050505);
|
|
218
|
+
border: 1px solid var(--ef-color-border-subtle, rgba(255, 255, 255, 0.08));
|
|
145
219
|
border-radius: 6px;
|
|
146
220
|
color: inherit;
|
|
147
221
|
font-size: 14px;
|
|
@@ -153,8 +227,8 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
153
227
|
}
|
|
154
228
|
|
|
155
229
|
.control-btn:hover:not(:disabled) {
|
|
156
|
-
background: var(--ef-color-hover);
|
|
157
|
-
border-color: var(--ef-color-border);
|
|
230
|
+
background: var(--ef-color-hover, rgba(255, 255, 255, 0.06));
|
|
231
|
+
border-color: var(--ef-color-border, rgba(255, 255, 255, 0.15));
|
|
158
232
|
}
|
|
159
233
|
|
|
160
234
|
.control-btn:disabled {
|
|
@@ -163,8 +237,8 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
163
237
|
}
|
|
164
238
|
|
|
165
239
|
.control-btn.active {
|
|
166
|
-
background: var(--ef-color-primary-subtle);
|
|
167
|
-
border-color: var(--ef-color-primary);
|
|
240
|
+
background: var(--ef-color-primary-subtle, rgba(59, 130, 246, 0.15));
|
|
241
|
+
border-color: var(--ef-color-primary, #3b82f6);
|
|
168
242
|
}
|
|
169
243
|
|
|
170
244
|
.time-display {
|
|
@@ -172,7 +246,7 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
172
246
|
font-size: 13px;
|
|
173
247
|
font-weight: 500;
|
|
174
248
|
padding: 6px 12px;
|
|
175
|
-
background: var(--ef-color-bg-elevated);
|
|
249
|
+
background: var(--ef-color-bg-elevated, #1a1a1a);
|
|
176
250
|
border-radius: 6px;
|
|
177
251
|
letter-spacing: 0.5px;
|
|
178
252
|
}
|
|
@@ -189,8 +263,8 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
189
263
|
display: flex;
|
|
190
264
|
align-items: center;
|
|
191
265
|
justify-content: center;
|
|
192
|
-
background: var(--ef-color-bg-inset);
|
|
193
|
-
border: 1px solid var(--ef-color-border-subtle);
|
|
266
|
+
background: var(--ef-color-bg-inset, #050505);
|
|
267
|
+
border: 1px solid var(--ef-color-border-subtle, rgba(255, 255, 255, 0.08));
|
|
194
268
|
border-radius: 6px;
|
|
195
269
|
color: inherit;
|
|
196
270
|
font-size: 18px;
|
|
@@ -200,8 +274,8 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
200
274
|
}
|
|
201
275
|
|
|
202
276
|
.zoom-btn:hover {
|
|
203
|
-
background: var(--ef-color-hover);
|
|
204
|
-
border-color: var(--ef-color-border);
|
|
277
|
+
background: var(--ef-color-hover, rgba(255, 255, 255, 0.06));
|
|
278
|
+
border-color: var(--ef-color-border, rgba(255, 255, 255, 0.15));
|
|
205
279
|
transform: scale(1.05);
|
|
206
280
|
}
|
|
207
281
|
|
|
@@ -220,8 +294,8 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
220
294
|
}
|
|
221
295
|
|
|
222
296
|
.zoom-label:hover {
|
|
223
|
-
background: var(--ef-color-hover);
|
|
224
|
-
border-color: var(--ef-color-border-subtle);
|
|
297
|
+
background: var(--ef-color-hover, rgba(255, 255, 255, 0.06));
|
|
298
|
+
border-color: var(--ef-color-border-subtle, rgba(255, 255, 255, 0.08));
|
|
225
299
|
}
|
|
226
300
|
|
|
227
301
|
.zoom-btn:disabled {
|
|
@@ -239,13 +313,15 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
239
313
|
overflow: hidden;
|
|
240
314
|
}
|
|
241
315
|
|
|
316
|
+
/* Clips the ruler row during horizontal scroll — scrollLeft synced via JS */
|
|
242
317
|
.ruler-row {
|
|
243
318
|
display: flex;
|
|
244
319
|
height: 24px;
|
|
245
320
|
background: var(--timeline-ruler-bg);
|
|
246
321
|
border-bottom: 1px solid var(--timeline-border);
|
|
247
322
|
flex-shrink: 0;
|
|
248
|
-
/* Sticky
|
|
323
|
+
/* Sticky inside tracks-scroll so the ruler stays visible during vertical scroll.
|
|
324
|
+
The ruler scrolls horizontally with tracks-scroll natively — no sync needed. */
|
|
249
325
|
position: sticky;
|
|
250
326
|
top: 0;
|
|
251
327
|
z-index: 10;
|
|
@@ -348,6 +424,20 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
348
424
|
display: flex;
|
|
349
425
|
flex-direction: column;
|
|
350
426
|
}
|
|
427
|
+
|
|
428
|
+
/* Fills the label column background for the full tracks height, covering
|
|
429
|
+
empty space below the last row. Sits in the same grid cell as the rows
|
|
430
|
+
layer but at z-index 0 so actual row labels (z-index: 8) paint above it. */
|
|
431
|
+
.label-column-bg {
|
|
432
|
+
align-self: stretch;
|
|
433
|
+
position: sticky;
|
|
434
|
+
left: 0;
|
|
435
|
+
width: var(--timeline-hierarchy-width, 200px);
|
|
436
|
+
background: var(--ef-color-bg-panel);
|
|
437
|
+
border-right: 1px solid var(--ef-color-border-subtle);
|
|
438
|
+
z-index: 0;
|
|
439
|
+
pointer-events: none;
|
|
440
|
+
}
|
|
351
441
|
|
|
352
442
|
/* === PLAYHEAD (sticky layer that stays visible during vertical scroll) === */
|
|
353
443
|
.playhead-layer {
|
|
@@ -358,6 +448,8 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
358
448
|
/* Below sticky labels (z-index 10-11) but above tracks */
|
|
359
449
|
z-index: 5;
|
|
360
450
|
overflow: hidden;
|
|
451
|
+
/* clip-path is set dynamically in updatePlayheadPositionDirect so it
|
|
452
|
+
tracks horizontal scroll and always aligns with the label column edge. */
|
|
361
453
|
}
|
|
362
454
|
|
|
363
455
|
.playhead {
|
|
@@ -385,8 +477,8 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
385
477
|
position: absolute;
|
|
386
478
|
top: 0;
|
|
387
479
|
bottom: 0;
|
|
388
|
-
background: var(--ef-color-primary-subtle);
|
|
389
|
-
border-left: 2px solid var(--ef-color-primary);
|
|
480
|
+
background: var(--ef-color-primary-subtle, rgba(59, 130, 246, 0.15));
|
|
481
|
+
border-left: 2px solid var(--ef-color-primary, #3b82f6);
|
|
390
482
|
pointer-events: none;
|
|
391
483
|
}
|
|
392
484
|
|
|
@@ -395,7 +487,7 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
395
487
|
align-items: center;
|
|
396
488
|
justify-content: center;
|
|
397
489
|
height: 100%;
|
|
398
|
-
color: var(--ef-color-text-subtle);
|
|
490
|
+
color: var(--ef-color-text-subtle, #6b7280);
|
|
399
491
|
font-style: italic;
|
|
400
492
|
}
|
|
401
493
|
`];
|
|
@@ -475,16 +567,18 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
475
567
|
return this.getCanvas()?.selectionContext;
|
|
476
568
|
}
|
|
477
569
|
/**
|
|
478
|
-
* Get the currently highlighted element
|
|
570
|
+
* Get the currently highlighted element.
|
|
571
|
+
* Local state is the source of truth; canvas is kept in sync as a side effect.
|
|
479
572
|
*/
|
|
480
573
|
getHighlightedElement() {
|
|
481
|
-
return this.
|
|
574
|
+
return this._highlightedElement;
|
|
482
575
|
}
|
|
483
576
|
/**
|
|
484
|
-
* Set the highlighted element
|
|
485
|
-
*
|
|
577
|
+
* Set the highlighted element.
|
|
578
|
+
* Always updates local state (triggers re-render); also syncs to canvas when present.
|
|
486
579
|
*/
|
|
487
580
|
setHighlightedElement(element) {
|
|
581
|
+
this._highlightedElement = element;
|
|
488
582
|
this.getCanvas()?.setHighlightedElement(element);
|
|
489
583
|
}
|
|
490
584
|
get targetTemporal() {
|
|
@@ -581,11 +675,7 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
581
675
|
if (typeof state.viewportScrollLeft === "number" && state.viewportScrollLeft >= 0) {
|
|
582
676
|
const scrollLeft = state.viewportScrollLeft;
|
|
583
677
|
this.updateComplete.then(() => {
|
|
584
|
-
|
|
585
|
-
if (tracksScroll) {
|
|
586
|
-
tracksScroll.scrollLeft = scrollLeft;
|
|
587
|
-
this.viewportScrollLeft = scrollLeft;
|
|
588
|
-
}
|
|
678
|
+
if (this.tracksScrollRef.value) this.applyScrollLeft(scrollLeft);
|
|
589
679
|
});
|
|
590
680
|
}
|
|
591
681
|
} catch (error) {
|
|
@@ -783,6 +873,23 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
783
873
|
this.canvasActiveRootTemporalChangeHandler = void 0;
|
|
784
874
|
}
|
|
785
875
|
}
|
|
876
|
+
/**
|
|
877
|
+
* Single source-of-truth for applying a scroll position change.
|
|
878
|
+
* Keeps ruler-outer, viewportScrollLeft, playhead, and clip-path all in sync
|
|
879
|
+
* immediately — without relying on the async scroll event.
|
|
880
|
+
*/
|
|
881
|
+
applyScrollLeft(scrollLeft) {
|
|
882
|
+
const tracksScroll = this.tracksScrollRef.value;
|
|
883
|
+
if (!tracksScroll) return;
|
|
884
|
+
tracksScroll.scrollLeft = scrollLeft;
|
|
885
|
+
const actual = tracksScroll.scrollLeft;
|
|
886
|
+
if (actual !== this.viewportScrollLeft) {
|
|
887
|
+
this.viewportScrollLeft = actual;
|
|
888
|
+
this.updatePlayheadPositionDirect(this.currentTimeMs);
|
|
889
|
+
this.updateTimelineState();
|
|
890
|
+
this.debouncedSaveTimelineState();
|
|
891
|
+
}
|
|
892
|
+
}
|
|
786
893
|
setupScrollListener() {
|
|
787
894
|
if (this.tracksScrollRef.value) {
|
|
788
895
|
this.scrollHandler = () => {
|
|
@@ -790,6 +897,7 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
790
897
|
const newScrollLeft = this.tracksScrollRef.value.scrollLeft;
|
|
791
898
|
if (newScrollLeft !== this.viewportScrollLeft) {
|
|
792
899
|
this.viewportScrollLeft = newScrollLeft;
|
|
900
|
+
this.updatePlayheadPositionDirect(this.currentTimeMs);
|
|
793
901
|
this.updateTimelineState();
|
|
794
902
|
this.debouncedSaveTimelineState();
|
|
795
903
|
}
|
|
@@ -827,6 +935,8 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
827
935
|
const rawTime = this.targetTemporal.currentTimeMs ?? 0;
|
|
828
936
|
const duration = this.targetTemporal.durationMs ?? 0;
|
|
829
937
|
const newTimeMs = Math.max(0, Math.min(rawTime, duration));
|
|
938
|
+
if (this.isPlaying && !this.isDraggingPlayhead) this.followPlayhead(newTimeMs);
|
|
939
|
+
else this.isFollowingPlayhead = false;
|
|
830
940
|
this.updatePlayheadPositionDirect(newTimeMs);
|
|
831
941
|
const newIsPlaying = this.targetTemporal.playing ?? false;
|
|
832
942
|
const newIsLooping = this.targetTemporal.loop ?? false;
|
|
@@ -843,8 +953,6 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
843
953
|
this.currentTimeMs = newTimeMs;
|
|
844
954
|
this.lastContextUpdateTime = now;
|
|
845
955
|
}
|
|
846
|
-
if (this.isPlaying && !this.isDraggingPlayhead) this.followPlayhead(newTimeMs);
|
|
847
|
-
else this.isFollowingPlayhead = false;
|
|
848
956
|
}
|
|
849
957
|
} else if (this.isPlaying) {
|
|
850
958
|
console.warn("[EFTimeline] Lost targetTemporal during playback. Stopping playback state.", "controlTarget:", this.controlTarget, "target:", this.target);
|
|
@@ -864,6 +972,9 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
864
972
|
const hierarchyWidth = this.showHierarchy ? _EFTimeline.HIERARCHY_WIDTH : 0;
|
|
865
973
|
const playheadPx = timeToPx(timeMs, this.pixelsPerMs);
|
|
866
974
|
const playheadLeft = hierarchyWidth + playheadPx;
|
|
975
|
+
const scrollLeft = this.viewportScrollLeft;
|
|
976
|
+
const playheadLayer = this.playheadLayerRef.value;
|
|
977
|
+
if (playheadLayer) playheadLayer.style.clipPath = hierarchyWidth > 0 ? `inset(0 0 0 ${hierarchyWidth + scrollLeft}px)` : "";
|
|
867
978
|
const playhead = this.playheadRef.value;
|
|
868
979
|
if (playhead) playhead.style.left = `${playheadLeft - 1}px`;
|
|
869
980
|
const handle = this.playheadHandleRef.value;
|
|
@@ -985,6 +1096,7 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
985
1096
|
return;
|
|
986
1097
|
}
|
|
987
1098
|
this.targetTemporal.play();
|
|
1099
|
+
this.isPlaying = true;
|
|
988
1100
|
}
|
|
989
1101
|
handlePause() {
|
|
990
1102
|
if (!this.targetTemporal) {
|
|
@@ -992,6 +1104,7 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
992
1104
|
return;
|
|
993
1105
|
}
|
|
994
1106
|
this.targetTemporal.pause();
|
|
1107
|
+
this.isPlaying = false;
|
|
995
1108
|
}
|
|
996
1109
|
handleToggleLoop() {
|
|
997
1110
|
if (!this.targetTemporal) {
|
|
@@ -1024,22 +1137,12 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
1024
1137
|
handleZoomOut() {
|
|
1025
1138
|
const newPixelsPerMs = Math.max(this.effectiveMinPixelsPerMs, this.pixelsPerMs / 1.25);
|
|
1026
1139
|
this.pixelsPerMs = newPixelsPerMs;
|
|
1027
|
-
if (newPixelsPerMs <= this.fitPixelsPerMs + 1e-9)
|
|
1028
|
-
const tracksScroll = this.tracksScrollRef.value;
|
|
1029
|
-
if (tracksScroll) {
|
|
1030
|
-
tracksScroll.scrollLeft = 0;
|
|
1031
|
-
this.viewportScrollLeft = 0;
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1140
|
+
if (newPixelsPerMs <= this.fitPixelsPerMs + 1e-9) this.applyScrollLeft(0);
|
|
1034
1141
|
this.updateTimelineState();
|
|
1035
1142
|
}
|
|
1036
1143
|
handleFitToViewport() {
|
|
1037
1144
|
this.pixelsPerMs = this.fitPixelsPerMs;
|
|
1038
|
-
|
|
1039
|
-
if (tracksScroll) {
|
|
1040
|
-
tracksScroll.scrollLeft = 0;
|
|
1041
|
-
this.viewportScrollLeft = 0;
|
|
1042
|
-
}
|
|
1145
|
+
this.applyScrollLeft(0);
|
|
1043
1146
|
this.updateTimelineState();
|
|
1044
1147
|
}
|
|
1045
1148
|
/**
|
|
@@ -1063,8 +1166,7 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
1063
1166
|
const newScrollLeft = timeToPx(timeAtCursor, newPixelsPerMs) - cursorXInViewport;
|
|
1064
1167
|
this.pixelsPerMs = newPixelsPerMs;
|
|
1065
1168
|
const maxScroll = Math.max(0, timeToPx(this.durationMs, newPixelsPerMs) - (rect.width - hierarchyWidth));
|
|
1066
|
-
|
|
1067
|
-
this.viewportScrollLeft = tracksScroll.scrollLeft;
|
|
1169
|
+
this.applyScrollLeft(Math.max(0, Math.min(maxScroll, newScrollLeft)));
|
|
1068
1170
|
this.updateTimelineState();
|
|
1069
1171
|
}
|
|
1070
1172
|
/**
|
|
@@ -1152,7 +1254,10 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
1152
1254
|
startTimeMs: this.currentTimeMs
|
|
1153
1255
|
});
|
|
1154
1256
|
const tracksScroll = this.tracksScrollRef.value;
|
|
1155
|
-
if (tracksScroll)
|
|
1257
|
+
if (tracksScroll) {
|
|
1258
|
+
this.viewportScrollLeft = tracksScroll.scrollLeft;
|
|
1259
|
+
this.updatePlayheadPositionDirect(this.currentTimeMs);
|
|
1260
|
+
}
|
|
1156
1261
|
const hierarchyWidth = this.showHierarchy ? _EFTimeline.HIERARCHY_WIDTH : 0;
|
|
1157
1262
|
let lastClientX = e.clientX;
|
|
1158
1263
|
let edgeScrollAnimationId = null;
|
|
@@ -1186,8 +1291,7 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
1186
1291
|
if (scrollDelta !== 0) {
|
|
1187
1292
|
const maxScroll = tracksScroll.scrollWidth - tracksScroll.clientWidth;
|
|
1188
1293
|
const newScrollLeft = Math.max(0, Math.min(maxScroll, tracksScroll.scrollLeft + scrollDelta));
|
|
1189
|
-
|
|
1190
|
-
this.viewportScrollLeft = newScrollLeft;
|
|
1294
|
+
this.applyScrollLeft(newScrollLeft);
|
|
1191
1295
|
updatePlayheadFromMouse();
|
|
1192
1296
|
}
|
|
1193
1297
|
edgeScrollAnimationId = requestAnimationFrame(edgeScrollLoop);
|
|
@@ -1254,43 +1358,13 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
1254
1358
|
`;
|
|
1255
1359
|
}
|
|
1256
1360
|
/**
|
|
1257
|
-
*
|
|
1258
|
-
*
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
if (!this.showFrameMarkers || this.durationMs <= 0) return null;
|
|
1262
|
-
const fps = this.fps;
|
|
1263
|
-
const frameDurationMs = 1e3 / fps;
|
|
1264
|
-
const frameStartMs = quantizeToFrameTimeMs(this.currentTimeMs, fps);
|
|
1265
|
-
const frameEndMs = Math.min(frameStartMs + frameDurationMs, this.durationMs);
|
|
1266
|
-
const startPx = timeToPx(frameStartMs, this.pixelsPerMs);
|
|
1267
|
-
const widthPx = timeToPx(frameEndMs, this.pixelsPerMs) - startPx;
|
|
1268
|
-
if (widthPx <= 0 || startPx < 0) return null;
|
|
1269
|
-
return {
|
|
1270
|
-
startPx,
|
|
1271
|
-
widthPx
|
|
1272
|
-
};
|
|
1273
|
-
}
|
|
1274
|
-
/**
|
|
1275
|
-
* Render frame highlight (mechanism).
|
|
1276
|
-
* Shows the current frame as a rectangle to indicate frames have duration.
|
|
1277
|
-
* Only rendered when frame markers are visible (zoom level high enough).
|
|
1361
|
+
* Render the frame highlight placeholder.
|
|
1362
|
+
* Position and visibility are controlled exclusively by updatePlayheadPositionDirect
|
|
1363
|
+
* via the frameHighlightRef so that Lit re-renders never clobber the 60fps rAF
|
|
1364
|
+
* position with the throttled this.currentTimeMs value.
|
|
1278
1365
|
*/
|
|
1279
1366
|
renderFrameHighlight() {
|
|
1280
|
-
|
|
1281
|
-
const bounds = this.calculateFrameHighlightBounds();
|
|
1282
|
-
if (!bounds) return nothing;
|
|
1283
|
-
const hierarchyWidth = this.showHierarchy ? _EFTimeline.HIERARCHY_WIDTH : 0;
|
|
1284
|
-
return html`
|
|
1285
|
-
<div
|
|
1286
|
-
${ref(this.frameHighlightRef)}
|
|
1287
|
-
class="frame-highlight"
|
|
1288
|
-
style=${styleMap({
|
|
1289
|
-
left: `${hierarchyWidth + bounds.startPx}px`,
|
|
1290
|
-
width: `${bounds.widthPx}px`
|
|
1291
|
-
})}
|
|
1292
|
-
></div>
|
|
1293
|
-
`;
|
|
1367
|
+
return html`<div ${ref(this.frameHighlightRef)} class="frame-highlight" style="display:none"></div>`;
|
|
1294
1368
|
}
|
|
1295
1369
|
renderControls() {
|
|
1296
1370
|
if (!this.showControls) return nothing;
|
|
@@ -1375,14 +1449,14 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
1375
1449
|
<div class="timeline-container" tabindex="0" ${ref(this.containerRef)}>
|
|
1376
1450
|
${this.renderControls()}
|
|
1377
1451
|
<div class="timeline-area">
|
|
1378
|
-
<!-- Tracks Viewport
|
|
1452
|
+
<!-- Tracks Viewport — single scroll container for ruler + tracks -->
|
|
1379
1453
|
<div class="tracks-viewport" part="tracks">
|
|
1380
1454
|
<div
|
|
1381
1455
|
class="tracks-scroll"
|
|
1382
1456
|
${ref(this.tracksScrollRef)}
|
|
1383
1457
|
@pointerdown=${this.handleTracksPointerDown}
|
|
1384
1458
|
>
|
|
1385
|
-
<!-- Ruler Row
|
|
1459
|
+
<!-- Ruler Row — sticky inside tracks-scroll, scrolls horizontally with it natively -->
|
|
1386
1460
|
${this.showRuler ? html`
|
|
1387
1461
|
<div class="ruler-row" style="width: ${this.contentWidthPx + hierarchyWidth}px;">
|
|
1388
1462
|
${this.showHierarchy ? html`<div class="ruler-spacer"></div>` : nothing}
|
|
@@ -1408,13 +1482,15 @@ let EFTimeline = class EFTimeline extends TWMixin(LitElement) {
|
|
|
1408
1482
|
</div>
|
|
1409
1483
|
` : nothing}
|
|
1410
1484
|
<div class="tracks-content" style="min-width: ${this.contentWidthPx + hierarchyWidth}px;">
|
|
1485
|
+
<!-- Label column background — fills full height below the last row -->
|
|
1486
|
+
${hierarchyWidth > 0 ? html`<div class="label-column-bg"></div>` : nothing}
|
|
1411
1487
|
<!-- Track rows layer -->
|
|
1412
1488
|
<div class="tracks-rows-layer">
|
|
1413
1489
|
${this.renderRows(target)}
|
|
1414
1490
|
</div>
|
|
1415
1491
|
|
|
1416
1492
|
<!-- Playhead layer - sticky to stay visible during vertical scroll -->
|
|
1417
|
-
<div class="playhead-layer">
|
|
1493
|
+
<div ${ref(this.playheadLayerRef)} class="playhead-layer">
|
|
1418
1494
|
${this.renderFrameHighlight()}
|
|
1419
1495
|
${this.showPlayhead ? html`
|
|
1420
1496
|
<div ${ref(this.playheadRef)} class="playhead" part="playhead" style="left: ${playheadLeft - 1}px;">
|
|
@@ -1478,6 +1554,7 @@ __decorate([property({
|
|
|
1478
1554
|
__decorate([property({ type: String })], EFTimeline.prototype, "hide", void 0);
|
|
1479
1555
|
__decorate([property({ type: String })], EFTimeline.prototype, "show", void 0);
|
|
1480
1556
|
__decorate([state()], EFTimeline.prototype, "targetElement", void 0);
|
|
1557
|
+
__decorate([state()], EFTimeline.prototype, "_highlightedElement", void 0);
|
|
1481
1558
|
__decorate([state()], EFTimeline.prototype, "currentTimeMs", void 0);
|
|
1482
1559
|
__decorate([state()], EFTimeline.prototype, "isPlaying", void 0);
|
|
1483
1560
|
__decorate([state()], EFTimeline.prototype, "isLooping", void 0);
|