@editframe/elements 0.12.0-beta.6 → 0.13.0-beta.1

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 (95) hide show
  1. package/dist/elements/EFAudio.d.ts +1 -1
  2. package/dist/elements/EFCaptions.d.ts +5 -5
  3. package/dist/elements/EFImage.d.ts +1 -1
  4. package/dist/elements/EFMedia.browsertest.d.ts +1 -1
  5. package/dist/elements/EFMedia.d.ts +6 -1
  6. package/dist/elements/{src/elements/EFMedia.js → EFMedia.js} +3 -3
  7. package/dist/elements/EFTemporal.browsertest.d.ts +1 -1
  8. package/dist/elements/EFTemporal.d.ts +145 -2
  9. package/dist/elements/{src/elements/EFTemporal.js → EFTemporal.js} +3 -0
  10. package/dist/elements/EFTimegroup.browsertest.d.ts +2 -2
  11. package/dist/elements/EFTimegroup.d.ts +8 -3
  12. package/dist/elements/{src/elements/EFTimegroup.js → EFTimegroup.js} +104 -53
  13. package/dist/elements/EFVideo.d.ts +1 -1
  14. package/dist/elements/{src/elements/EFVideo.js → EFVideo.js} +10 -2
  15. package/dist/elements/EFWaveform.d.ts +7 -4
  16. package/dist/elements/{src/elements/EFWaveform.js → EFWaveform.js} +48 -56
  17. package/dist/elements/TimegroupController.d.ts +2 -2
  18. package/dist/elements/util.d.ts +1 -1
  19. package/dist/gui/ContextMixin.browsertest.d.ts +1 -1
  20. package/dist/gui/ContextMixin.d.ts +4 -1
  21. package/dist/{elements/src/gui → gui}/ContextMixin.js +32 -44
  22. package/dist/gui/EFFilmstrip.d.ts +15 -9
  23. package/dist/{elements/src/gui → gui}/EFFilmstrip.js +64 -16
  24. package/dist/gui/EFFocusOverlay.d.ts +17 -0
  25. package/dist/gui/EFFocusOverlay.js +82 -0
  26. package/dist/gui/EFPreview.d.ts +3 -1
  27. package/dist/{elements/src/gui → gui}/EFPreview.js +24 -16
  28. package/dist/gui/EFScrubber.d.ts +1 -1
  29. package/dist/gui/EFTimeDisplay.d.ts +1 -1
  30. package/dist/gui/EFToggleLoop.d.ts +1 -1
  31. package/dist/gui/EFTogglePlay.d.ts +1 -3
  32. package/dist/{elements/src/gui → gui}/EFTogglePlay.js +1 -9
  33. package/dist/gui/EFWorkbench.d.ts +1 -1
  34. package/dist/{elements/src/gui → gui}/TWMixin.css.js +1 -1
  35. package/dist/gui/efContext.d.ts +1 -1
  36. package/dist/index.d.ts +15 -14
  37. package/dist/{elements/src/index.js → index.js} +2 -0
  38. package/dist/style.css +769 -19
  39. package/package.json +5 -4
  40. package/src/elements/EFAudio.ts +3 -3
  41. package/src/elements/EFCaptions.browsertest.ts +3 -3
  42. package/src/elements/EFCaptions.ts +9 -9
  43. package/src/elements/EFImage.browsertest.ts +2 -2
  44. package/src/elements/EFImage.ts +4 -4
  45. package/src/elements/EFMedia.browsertest.ts +6 -6
  46. package/src/elements/EFMedia.ts +14 -7
  47. package/src/elements/EFSourceMixin.ts +3 -3
  48. package/src/elements/EFTemporal.browsertest.ts +1 -1
  49. package/src/elements/EFTemporal.ts +159 -4
  50. package/src/elements/EFTimegroup.browsertest.ts +5 -5
  51. package/src/elements/EFTimegroup.ts +141 -68
  52. package/src/elements/EFVideo.ts +15 -4
  53. package/src/elements/EFWaveform.ts +82 -98
  54. package/src/elements/FetchMixin.ts +2 -2
  55. package/src/elements/TimegroupController.ts +2 -2
  56. package/src/elements/durationConverter.ts +1 -1
  57. package/src/elements/util.ts +1 -1
  58. package/src/gui/ContextMixin.browsertest.ts +3 -3
  59. package/src/gui/ContextMixin.ts +45 -52
  60. package/src/gui/EFFilmstrip.ts +92 -36
  61. package/src/gui/EFFocusOverlay.ts +79 -0
  62. package/src/gui/EFPreview.ts +35 -18
  63. package/src/gui/EFScrubber.ts +3 -3
  64. package/src/gui/EFTimeDisplay.ts +2 -2
  65. package/src/gui/EFToggleLoop.ts +4 -4
  66. package/src/gui/EFTogglePlay.ts +4 -10
  67. package/src/gui/EFWorkbench.ts +2 -2
  68. package/src/gui/efContext.ts +1 -1
  69. package/dist/assets/src/EncodedAsset.js +0 -560
  70. package/dist/assets/src/MP4File.js +0 -223
  71. package/dist/assets/src/memoize.js +0 -14
  72. package/dist/{elements/src/EF_FRAMEGEN.js → EF_FRAMEGEN.js} +0 -0
  73. package/dist/{elements/src/EF_INTERACTIVE.js → EF_INTERACTIVE.js} +0 -0
  74. package/dist/{elements/src/EF_RENDERING.js → EF_RENDERING.js} +0 -0
  75. package/dist/elements/{src/elements/CrossUpdateController.js → CrossUpdateController.js} +0 -0
  76. package/dist/elements/{src/elements/EFAudio.js → EFAudio.js} +2 -2
  77. package/dist/elements/{src/elements/EFCaptions.js → EFCaptions.js} +0 -0
  78. package/dist/elements/{src/elements/EFImage.js → EFImage.js} +0 -0
  79. package/dist/elements/{src/elements/EFSourceMixin.js → EFSourceMixin.js} +1 -1
  80. package/dist/elements/{src/elements/FetchMixin.js → FetchMixin.js} +0 -0
  81. package/dist/elements/{src/elements/TimegroupController.js → TimegroupController.js} +0 -0
  82. package/dist/elements/{src/elements/durationConverter.js → durationConverter.js} +0 -0
  83. package/dist/elements/{src/elements/parseTimeToMs.js → parseTimeToMs.js} +0 -0
  84. package/dist/{elements/src/gui → gui}/EFScrubber.js +0 -0
  85. package/dist/{elements/src/gui → gui}/EFTimeDisplay.js +0 -0
  86. package/dist/{elements/src/gui → gui}/EFToggleLoop.js +1 -1
  87. /package/dist/{elements/src/gui → gui}/EFWorkbench.js +0 -0
  88. /package/dist/{elements/src/gui → gui}/TWMixin.js +0 -0
  89. /package/dist/{elements/src/gui → gui}/apiHostContext.js +0 -0
  90. /package/dist/{elements/src/gui → gui}/efContext.js +0 -0
  91. /package/dist/{elements/src/gui → gui}/fetchContext.js +0 -0
  92. /package/dist/{elements/src/gui → gui}/focusContext.js +0 -0
  93. /package/dist/{elements/src/gui → gui}/focusedElementContext.js +0 -0
  94. /package/dist/{elements/src/gui → gui}/playingContext.js +0 -0
  95. /package/dist/{elements/src/msToTimeCode.js → msToTimeCode.js} +0 -0
@@ -26,53 +26,39 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
26
26
  this.mode = "bars";
27
27
  this.color = "currentColor";
28
28
  this.targetSelector = "";
29
- this.lastFrameTime = 0;
30
29
  this.frameTask = new Task(this, {
31
30
  autoRun: EF_INTERACTIVE,
32
31
  args: () => [this.targetElement.audioBufferTask.status],
33
32
  task: async () => {
34
- const currentTime = performance.now();
35
- const timeSinceLastFrame = this.lastFrameTime ? currentTime - this.lastFrameTime : 0;
36
- console.log(`Time since last frame: ${timeSinceLastFrame.toFixed(2)}ms`);
37
- this.lastFrameTime = currentTime;
38
33
  await this.targetElement.audioBufferTask.taskComplete;
39
34
  this.ctx ||= this.initCanvas();
40
35
  const ctx = this.ctx;
41
36
  if (!ctx) return;
42
37
  if (!this.targetElement.audioBufferTask.value) return;
43
38
  if (this.targetElement.trimAdjustedOwnCurrentTimeMs > 0) {
44
- const FRAMES_TO_ANALYZE = 4;
45
- const FRAME_DURATION_MS = 48e3 / 100;
46
- const multiFrameData = [];
47
- for (let i = 0; i < FRAMES_TO_ANALYZE; i++) {
48
- const frameOffset = i - Math.floor(FRAMES_TO_ANALYZE / 2);
49
- const audioContext = new OfflineAudioContext(2, 48e3 / 25, 48e3);
50
- const audioBufferSource = audioContext.createBufferSource();
51
- audioBufferSource.buffer = this.targetElement.audioBufferTask.value.buffer;
52
- const analyser = audioContext.createAnalyser();
53
- analyser.fftSize = 128 / 2;
54
- analyser.smoothingTimeConstant = 0.1;
55
- audioBufferSource.connect(analyser);
56
- const startTime = Math.max(
57
- 0,
58
- (this.targetElement.trimAdjustedOwnCurrentTimeMs - this.targetElement.audioBufferTask.value.startOffsetMs) / 1e3 + frameOffset * FRAME_DURATION_MS / 1e3
59
- );
60
- audioBufferSource.start(0, startTime, FRAME_DURATION_MS / 1e3);
61
- await audioContext.startRendering();
62
- const frameData = new Uint8Array(analyser.frequencyBinCount);
63
- analyser.getByteFrequencyData(frameData);
64
- multiFrameData.push(frameData);
65
- }
66
- const dataLength = multiFrameData[0]?.length ?? 0;
67
- const smoothedData = new Uint8Array(dataLength);
68
- for (let i = 0; i < smoothedData.length; i++) {
69
- let sum = 0;
70
- for (const frameData of multiFrameData) {
71
- sum += frameData[i] ?? 0;
72
- }
73
- let avg = sum / FRAMES_TO_ANALYZE;
74
- avg = (avg / 255) ** 1.2 * 255;
75
- smoothedData[i] = Math.round(avg);
39
+ const FRAME_DURATION_MS = 48e3 / 1e3;
40
+ const FRAME_SMEAR_MS = FRAME_DURATION_MS * 1;
41
+ const FRAME_SMEAR_S = FRAME_SMEAR_MS / 1e3;
42
+ const audioContext = new OfflineAudioContext(2, 48e3 / 25, 48e3);
43
+ const audioBufferSource = audioContext.createBufferSource();
44
+ audioBufferSource.buffer = this.targetElement.audioBufferTask.value.buffer;
45
+ const analyser = audioContext.createAnalyser();
46
+ analyser.fftSize = 128 * 8;
47
+ audioBufferSource.connect(analyser);
48
+ const startTime = Math.max(
49
+ 0,
50
+ (this.targetElement.trimAdjustedOwnCurrentTimeMs - this.targetElement.audioBufferTask.value.startOffsetMs) / 1e3
51
+ );
52
+ audioBufferSource.start(0, startTime, FRAME_SMEAR_S);
53
+ await audioContext.startRendering();
54
+ const frameData = new Uint8Array(analyser.frequencyBinCount);
55
+ analyser.getByteFrequencyData(frameData);
56
+ const smoothedData = frameData.slice(0, frameData.length / 2);
57
+ if (this.color === "currentColor") {
58
+ const computedStyle = getComputedStyle(this);
59
+ const currentColor = computedStyle.color;
60
+ ctx.strokeStyle = currentColor;
61
+ ctx.fillStyle = currentColor;
76
62
  }
77
63
  switch (this.mode) {
78
64
  case "bars":
@@ -118,9 +104,31 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
118
104
  if (this.targetElement) {
119
105
  new CrossUpdateController(this.targetElement, this);
120
106
  }
107
+ this.resizeObserver = new ResizeObserver(() => {
108
+ this.resizeCanvas();
109
+ });
110
+ this.resizeObserver.observe(this);
111
+ this.mutationObserver = new MutationObserver((mutationsList) => {
112
+ for (const mutation of mutationsList) {
113
+ if (mutation.type === "attributes") {
114
+ this.frameTask.run();
115
+ }
116
+ }
117
+ });
118
+ this.mutationObserver.observe(this, { attributes: true });
119
+ }
120
+ disconnectedCallback() {
121
+ super.disconnectedCallback();
122
+ this.resizeObserver?.disconnect();
123
+ this.mutationObserver?.disconnect();
124
+ }
125
+ resizeCanvas() {
126
+ this.ctx = this.initCanvas();
127
+ if (this.ctx) {
128
+ this.frameTask.run();
129
+ }
121
130
  }
122
131
  initCanvas() {
123
- console.count("initCanvas");
124
132
  const canvas = this.canvasRef.value;
125
133
  if (!canvas) return null;
126
134
  const rect = this.getBoundingClientRect();
@@ -135,8 +143,6 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
135
143
  return ctx;
136
144
  }
137
145
  drawBars(ctx, frequencyData) {
138
- ctx.strokeStyle = this.color;
139
- ctx.fillStyle = this.color;
140
146
  const canvas = ctx.canvas;
141
147
  const waveWidth = canvas.width / devicePixelRatio;
142
148
  const waveHeight = canvas.height / devicePixelRatio;
@@ -151,8 +157,6 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
151
157
  });
152
158
  }
153
159
  drawBricks(ctx, frequencyData) {
154
- ctx.strokeStyle = this.color;
155
- ctx.fillStyle = this.color;
156
160
  const canvas = ctx.canvas;
157
161
  const waveWidth = canvas.width / devicePixelRatio;
158
162
  const waveHeight = canvas.height / devicePixelRatio;
@@ -176,8 +180,6 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
176
180
  });
177
181
  }
178
182
  drawLine(ctx, frequencyData) {
179
- ctx.strokeStyle = this.color;
180
- ctx.fillStyle = this.color;
181
183
  const canvas = ctx.canvas;
182
184
  const waveWidth = canvas.width / devicePixelRatio;
183
185
  const waveHeight = canvas.height / devicePixelRatio;
@@ -196,8 +198,6 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
196
198
  ctx.stroke();
197
199
  }
198
200
  drawRoundBars(ctx, frequencyData) {
199
- ctx.strokeStyle = this.color;
200
- ctx.fillStyle = this.color;
201
201
  const canvas = ctx.canvas;
202
202
  const waveWidth = canvas.width / devicePixelRatio;
203
203
  const waveHeight = canvas.height / devicePixelRatio;
@@ -216,8 +216,6 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
216
216
  });
217
217
  }
218
218
  drawEqualizer(ctx, frequencyData) {
219
- ctx.strokeStyle = this.color;
220
- ctx.fillStyle = this.color;
221
219
  const canvas = ctx.canvas;
222
220
  const waveWidth = canvas.width / devicePixelRatio;
223
221
  const waveHeight = canvas.height / devicePixelRatio;
@@ -237,8 +235,6 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
237
235
  });
238
236
  }
239
237
  drawCurve(ctx, frequencyData) {
240
- ctx.strokeStyle = this.color;
241
- ctx.fillStyle = this.color;
242
238
  const canvas = ctx.canvas;
243
239
  const waveWidth = canvas.width / devicePixelRatio;
244
240
  const waveHeight = canvas.height / devicePixelRatio;
@@ -257,8 +253,6 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
257
253
  ctx.stroke();
258
254
  }
259
255
  drawPixel(ctx, frequencyData) {
260
- ctx.strokeStyle = this.color;
261
- ctx.fillStyle = this.color;
262
256
  const canvas = ctx.canvas;
263
257
  const waveWidth = canvas.width / devicePixelRatio;
264
258
  const waveHeight = canvas.height / devicePixelRatio;
@@ -273,8 +267,6 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
273
267
  });
274
268
  }
275
269
  drawWave(ctx, frequencyData) {
276
- ctx.strokeStyle = this.color;
277
- ctx.fillStyle = this.color;
278
270
  const canvas = ctx.canvas;
279
271
  const waveWidth = canvas.width / devicePixelRatio;
280
272
  const waveHeight = canvas.height / devicePixelRatio;
@@ -285,7 +277,7 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
285
277
  ctx.moveTo(0, baseline);
286
278
  ctx.lineTo(waveWidth, baseline);
287
279
  ctx.strokeStyle = this.color;
288
- ctx.lineWidth = 2;
280
+ ctx.lineWidth = 1;
289
281
  ctx.stroke();
290
282
  frequencyData.forEach((value, i) => {
291
283
  const x = i * (waveWidth / frequencyData.length);
@@ -306,7 +298,7 @@ let EFWaveform = class extends EFTemporal(TWMixin(LitElement)) {
306
298
  }
307
299
  updated(changedProperties) {
308
300
  super.updated(changedProperties);
309
- if (changedProperties.has("color") || changedProperties.has("mode")) {
301
+ if (changedProperties.size > 0) {
310
302
  this.frameTask.run();
311
303
  }
312
304
  }
@@ -1,5 +1,5 @@
1
- import { ReactiveController, LitElement } from 'lit';
2
- import { EFTimegroup } from './EFTimegroup.ts';
1
+ import { LitElement, ReactiveController } from 'lit';
2
+ import { EFTimegroup } from './EFTimegroup.js';
3
3
  export declare class TimegroupController implements ReactiveController {
4
4
  private host;
5
5
  private child;
@@ -1,3 +1,3 @@
1
- import { EFTimegroup } from './EFTimegroup.ts';
1
+ import { EFTimegroup } from './EFTimegroup.js';
2
2
  export declare const getRootTimeGroup: (element: Element) => EFTimegroup | null;
3
3
  export declare const getStartTimeMs: (element: Element) => number;
@@ -1,5 +1,5 @@
1
1
  import { LitElement } from 'lit';
2
- declare const TestContext_base: (new (...args: any[]) => import('./ContextMixin.ts').ContextMixinInterface) & typeof LitElement;
2
+ declare const TestContext_base: (new (...args: any[]) => import('./ContextMixin.js').ContextMixinInterface) & typeof LitElement;
3
3
  declare class TestContext extends TestContext_base {
4
4
  }
5
5
  declare global {
@@ -1,6 +1,9 @@
1
1
  import { LitElement } from 'lit';
2
2
  import { createRef } from 'lit/directives/ref.js';
3
- import { EFTimegroup } from '../elements/EFTimegroup.ts';
3
+ import { EFTimegroup } from '../elements/EFTimegroup.js';
4
+ export declare const targetTimegroupContext: {
5
+ __context__: EFTimegroup | null;
6
+ };
4
7
  export declare class ContextMixinInterface extends LitElement {
5
8
  signingURL?: string;
6
9
  apiHost?: string;
@@ -1,4 +1,4 @@
1
- import { provide } from "@lit/context";
1
+ import { createContext, provide } from "@lit/context";
2
2
  import { state, property } from "lit/decorators.js";
3
3
  import { createRef } from "lit/directives/ref.js";
4
4
  import { apiHostContext } from "./apiHostContext.js";
@@ -16,6 +16,9 @@ var __decorateClass = (decorators, target, key, kind) => {
16
16
  if (result) __defProp(target, key, result);
17
17
  return result;
18
18
  };
19
+ const targetTimegroupContext = createContext(
20
+ "target-timegroup"
21
+ );
19
22
  const contextMixinSymbol = Symbol("contextMixin");
20
23
  function isContextMixin(value) {
21
24
  return typeof value === "object" && value !== null && contextMixinSymbol in value.constructor;
@@ -27,6 +30,7 @@ function ContextMixin(superClass) {
27
30
  super(...arguments);
28
31
  this.focusContext = this;
29
32
  this.efContext = this;
33
+ this.targetTimegroup = null;
30
34
  this.fetch = async (url, init = {}) => {
31
35
  init.headers ||= {};
32
36
  Object.assign(init.headers, {
@@ -58,48 +62,22 @@ function ContextMixin(superClass) {
58
62
  this.#URLTokens = {};
59
63
  this.playing = false;
60
64
  this.loop = false;
61
- this.stageScale = 1;
62
65
  this.rendering = false;
63
66
  this.currentTimeMs = 0;
64
67
  this.stageRef = createRef();
65
68
  this.canvasRef = createRef();
66
69
  this.#FPS = 30;
67
70
  this.#MS_PER_FRAME = 1e3 / this.#FPS;
68
- this.setStageScale = () => {
69
- if (this.isConnected && !this.rendering) {
70
- const canvasElement = this.canvasRef.value;
71
- const stageElement = this.stageRef.value;
72
- const canvasChild = canvasElement?.assignedElements()[0];
73
- if (stageElement && canvasElement && canvasChild) {
74
- canvasElement.style.width = `${canvasChild.clientWidth}px`;
75
- canvasElement.style.height = `${canvasChild.clientHeight}px`;
76
- const stageWidth = stageElement.clientWidth;
77
- const stageHeight = stageElement.clientHeight;
78
- const canvasWidth = canvasElement.clientWidth;
79
- const canvasHeight = canvasElement.clientHeight;
80
- const stageRatio = stageWidth / stageHeight;
81
- const canvasRatio = canvasWidth / canvasHeight;
82
- if (stageRatio > canvasRatio) {
83
- const scale = stageHeight / canvasHeight;
84
- if (this.stageScale !== scale) {
85
- canvasElement.style.transform = `scale(${scale})`;
86
- canvasElement.style.transformOrigin = "center left";
87
- }
88
- this.stageScale = scale;
89
- } else {
90
- const scale = stageWidth / canvasWidth;
91
- if (this.stageScale !== scale) {
92
- canvasElement.style.transform = `scale(${scale})`;
93
- canvasElement.style.transformOrigin = "center left";
94
- }
95
- this.stageScale = scale;
71
+ this.#timegroupObserver = new MutationObserver((mutations) => {
72
+ for (const mutation of mutations) {
73
+ if (mutation.type === "childList") {
74
+ const newTimegroup = this.querySelector("ef-timegroup");
75
+ if (newTimegroup !== this.targetTimegroup) {
76
+ this.targetTimegroup = newTimegroup;
96
77
  }
97
78
  }
98
79
  }
99
- if (this.isConnected) {
100
- requestAnimationFrame(this.setStageScale);
101
- }
102
- };
80
+ });
103
81
  this.#playbackAudioContext = null;
104
82
  this.#playbackAnimationFrameRequest = null;
105
83
  this.#AUDIO_PLAYBACK_SLICE_MS = 1e3;
@@ -110,15 +88,22 @@ function ContextMixin(superClass) {
110
88
  #URLTokens;
111
89
  #FPS;
112
90
  #MS_PER_FRAME;
91
+ #timegroupObserver;
113
92
  connectedCallback() {
114
93
  super.connectedCallback();
115
- requestAnimationFrame(this.setStageScale);
94
+ this.targetTimegroup = this.querySelector("ef-timegroup");
95
+ this.#timegroupObserver.observe(this, {
96
+ childList: true,
97
+ subtree: true,
98
+ attributes: true
99
+ });
116
100
  if (this.playing) {
117
101
  this.startPlayback();
118
102
  }
119
103
  }
120
104
  disconnectedCallback() {
121
105
  super.disconnectedCallback();
106
+ this.#timegroupObserver.disconnect();
122
107
  this.stopPlayback();
123
108
  }
124
109
  update(changedProperties) {
@@ -146,9 +131,6 @@ function ContextMixin(superClass) {
146
131
  }
147
132
  super.update(changedProperties);
148
133
  }
149
- get targetTimegroup() {
150
- return this.querySelector("ef-timegroup");
151
- }
152
134
  play() {
153
135
  this.playing = true;
154
136
  }
@@ -187,6 +169,12 @@ function ContextMixin(superClass) {
187
169
  }
188
170
  await timegroup.waitForMediaDurations();
189
171
  let currentMs = timegroup.currentTimeMs;
172
+ const fromMs = currentMs;
173
+ const toMs = timegroup.endTimeMs;
174
+ if (fromMs >= toMs) {
175
+ this.pause();
176
+ return;
177
+ }
190
178
  let bufferCount = 0;
191
179
  this.#playbackAudioContext = new AudioContext({
192
180
  latencyHint: "playback"
@@ -213,8 +201,6 @@ function ContextMixin(superClass) {
213
201
  fillBuffer();
214
202
  }
215
203
  };
216
- const fromMs = currentMs;
217
- const toMs = timegroup.endTimeMs;
218
204
  const queueBufferSource = async () => {
219
205
  if (currentMs >= toMs) {
220
206
  return false;
@@ -264,6 +250,10 @@ function ContextMixin(superClass) {
264
250
  __decorateClass([
265
251
  provide({ context: efContext })
266
252
  ], ContextElement.prototype, "efContext");
253
+ __decorateClass([
254
+ provide({ context: targetTimegroupContext }),
255
+ state()
256
+ ], ContextElement.prototype, "targetTimegroup");
267
257
  __decorateClass([
268
258
  provide({ context: fetchContext })
269
259
  ], ContextElement.prototype, "fetch");
@@ -278,9 +268,6 @@ function ContextMixin(superClass) {
278
268
  provide({ context: loopContext }),
279
269
  property({ type: Boolean, reflect: true })
280
270
  ], ContextElement.prototype, "loop");
281
- __decorateClass([
282
- state()
283
- ], ContextElement.prototype, "stageScale");
284
271
  __decorateClass([
285
272
  property({ type: Boolean })
286
273
  ], ContextElement.prototype, "rendering");
@@ -291,5 +278,6 @@ function ContextMixin(superClass) {
291
278
  }
292
279
  export {
293
280
  ContextMixin,
294
- isContextMixin
281
+ isContextMixin,
282
+ targetTimegroupContext
295
283
  };
@@ -1,11 +1,11 @@
1
1
  import { LitElement, PropertyValueMap, ReactiveController, TemplateResult, nothing } from 'lit';
2
- import { EFAudio } from '../elements/EFAudio.ts';
3
- import { EFImage } from '../elements/EFImage.ts';
4
- import { TemporalMixinInterface } from '../elements/EFTemporal.ts';
5
- import { EFTimegroup } from '../elements/EFTimegroup.ts';
6
- import { EFVideo } from '../elements/EFVideo.ts';
7
- import { TimegroupController } from '../elements/TimegroupController.ts';
8
- import { FocusContext } from './focusContext.ts';
2
+ import { EFAudio } from '../elements/EFAudio.js';
3
+ import { EFImage } from '../elements/EFImage.js';
4
+ import { TemporalMixinInterface } from '../elements/EFTemporal.js';
5
+ import { EFTimegroup } from '../elements/EFTimegroup.js';
6
+ import { EFVideo } from '../elements/EFVideo.js';
7
+ import { TimegroupController } from '../elements/TimegroupController.js';
8
+ import { FocusContext } from './focusContext.js';
9
9
  declare class ElementFilmstripController implements ReactiveController {
10
10
  private host;
11
11
  private filmstrip;
@@ -69,7 +69,7 @@ declare class EFHierarchyItem<ElementType extends HTMLElement = HTMLElement> ext
69
69
  get isFocused(): boolean;
70
70
  displayLabel(): TemplateResult<1> | string | typeof nothing;
71
71
  render(): TemplateResult<1>;
72
- renderChildren(): Array<TemplateResult<1>> | typeof nothing;
72
+ renderChildren(): Array<TemplateResult<1> | typeof nothing> | typeof nothing;
73
73
  }
74
74
  declare class EFTimegroupHierarchyItem extends EFHierarchyItem<EFTimegroup> {
75
75
  get icon(): string;
@@ -103,6 +103,7 @@ declare class EFHTMLHierarchyItem extends EFHierarchyItem {
103
103
  declare const EFFilmstrip_base: typeof LitElement;
104
104
  export declare class EFFilmstrip extends EFFilmstrip_base {
105
105
  #private;
106
+ static styles: import('lit').CSSResult[];
106
107
  pixelsPerMs: number;
107
108
  scrubbing: boolean;
108
109
  timelineScrolltop: number;
@@ -110,8 +111,11 @@ export declare class EFFilmstrip extends EFFilmstrip_base {
110
111
  loop?: boolean;
111
112
  timegroupController?: TimegroupController;
112
113
  currentTimeMs: number;
114
+ autoScale: boolean;
115
+ private resizeObserver;
113
116
  connectedCallback(): void;
114
117
  disconnectedCallback(): void;
118
+ updatePixelsPerMs(): void;
115
119
  syncGutterScroll(): void;
116
120
  syncHierarchyScroll(): void;
117
121
  scrub(e: MouseEvent): void;
@@ -123,7 +127,9 @@ export declare class EFFilmstrip extends EFFilmstrip_base {
123
127
  get gutter(): HTMLDivElement | undefined;
124
128
  render(): TemplateResult<1>;
125
129
  updated(changes: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
126
- get targetTimegroup(): EFTimegroup | null | undefined;
130
+ target?: string;
131
+ targetTimegroup?: EFTimegroup | null;
132
+ protected willUpdate(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
127
133
  }
128
134
  declare global {
129
135
  interface HTMLElementTagNameMap {
@@ -11,6 +11,7 @@ import { EFVideo } from "../elements/EFVideo.js";
11
11
  import { EFWaveform } from "../elements/EFWaveform.js";
12
12
  import { TimegroupController } from "../elements/TimegroupController.js";
13
13
  import { msToTimeCode } from "../msToTimeCode.js";
14
+ import { targetTimegroupContext } from "./ContextMixin.js";
14
15
  import { TWMixin } from "./TWMixin.js";
15
16
  import { focusContext } from "./focusContext.js";
16
17
  import { focusedElementContext } from "./focusedElementContext.js";
@@ -410,6 +411,9 @@ EFHTMLHierarchyItem = __decorateClass([
410
411
  ], EFHTMLHierarchyItem);
411
412
  const renderHierarchyChildren = (children) => {
412
413
  return children.map((child) => {
414
+ if (child instanceof HTMLElement && child.dataset?.efHidden) {
415
+ return nothing;
416
+ }
413
417
  if (child instanceof EFTimegroup) {
414
418
  return html`<ef-timegroup-hierarchy-item
415
419
  .element=${child}
@@ -452,6 +456,9 @@ const renderHierarchyChildren = (children) => {
452
456
  };
453
457
  const renderFilmstripChildren = (children, pixelsPerMs) => {
454
458
  return children.map((child) => {
459
+ if (child instanceof HTMLElement && child.dataset?.efHidden) {
460
+ return nothing;
461
+ }
455
462
  if (child instanceof EFTimegroup) {
456
463
  return html`<ef-timegroup-filmstrip
457
464
  .element=${child}
@@ -503,6 +510,12 @@ let EFFilmstrip = class extends TWMixin(LitElement) {
503
510
  this.scrubbing = false;
504
511
  this.timelineScrolltop = 0;
505
512
  this.currentTimeMs = 0;
513
+ this.autoScale = false;
514
+ this.resizeObserver = new ResizeObserver(() => {
515
+ if (this.autoScale) {
516
+ this.updatePixelsPerMs();
517
+ }
518
+ });
506
519
  __privateAdd(this, _handleKeyPress, (event) => {
507
520
  if (event.key === " ") {
508
521
  const [target] = event.composedPath();
@@ -527,10 +540,19 @@ let EFFilmstrip = class extends TWMixin(LitElement) {
527
540
  super.connectedCallback();
528
541
  __privateMethod(this, _EFFilmstrip_instances, bindToTargetTimegroup_fn).call(this);
529
542
  window.addEventListener("keypress", __privateGet(this, _handleKeyPress));
543
+ this.resizeObserver.observe(this);
530
544
  }
531
545
  disconnectedCallback() {
532
546
  super.disconnectedCallback();
533
547
  window.removeEventListener("keypress", __privateGet(this, _handleKeyPress));
548
+ this.resizeObserver.disconnect();
549
+ }
550
+ updatePixelsPerMs() {
551
+ const target = this.targetTimegroup;
552
+ const gutter = this.gutterRef.value;
553
+ if (target && gutter && gutter.clientWidth > 0) {
554
+ this.pixelsPerMs = gutter.clientWidth / (target.durationMs || 1);
555
+ }
534
556
  }
535
557
  syncGutterScroll() {
536
558
  if (this.gutter && this.hierarchyRef.value) {
@@ -585,7 +607,9 @@ let EFFilmstrip = class extends TWMixin(LitElement) {
585
607
  }
586
608
  scrollScrub(e) {
587
609
  if (this.targetTimegroup && this.gutter && !this.playing) {
588
- e.preventDefault();
610
+ if (e.deltaX !== 0) {
611
+ e.preventDefault();
612
+ }
589
613
  if (this.gutterRef.value && this.gutterRef.value.scrollLeft === 0 && e.deltaX < 0) {
590
614
  this.gutter.scrollBy(0, e.deltaY);
591
615
  return;
@@ -615,38 +639,38 @@ let EFFilmstrip = class extends TWMixin(LitElement) {
615
639
  <div
616
640
  class="z-20 col-span-2 border-b-slate-600 bg-slate-100 shadow shadow-slate-300"
617
641
  >
618
- <input
619
- type="range"
620
- .value=${this.pixelsPerMs}
621
- min="0.01"
622
- max="0.1"
623
- step="0.001"
624
- @input=${(e) => {
642
+ ${!this.autoScale ? html`<input
643
+ type="range"
644
+ .value=${this.pixelsPerMs}
645
+ min="0.01"
646
+ max="0.1"
647
+ step="0.001"
648
+ @input=${(e) => {
625
649
  const target2 = e.target;
626
650
  this.pixelsPerMs = Number.parseFloat(target2.value);
627
651
  }}
628
- />
652
+ />` : nothing}
629
653
  <code>${msToTimeCode(this.currentTimeMs, true)} </code> /
630
654
  <code>${msToTimeCode(target?.durationMs ?? 0, true)}</code>
631
655
  <ef-toggle-play class="inline-block mx-2">
632
656
  <div slot="pause">
633
- <button class="bg-white">&#9654;</button>
657
+ <button>⏸️</button>
634
658
  </div>
635
659
  <div slot="play">
636
- <button>⏸️</button>
660
+ <button>▶️</button>
637
661
  </div>
638
662
  </ef-toggle-play>
639
- <ef-toggle-loop><button>${this.loop ? "🔁" : html`<span class="opacity-50">🔁</span>`}</button></ef-toggle-loop>
663
+ <ef-toggle-loop><button>${this.loop ? "🔁" : html`<span class="opacity-50 line-through">🔁</span>`}</button></ef-toggle-loop>
640
664
  </div>
641
665
  <div
642
- class="z-10 pl-1 pr-1 pt-2 shadow shadow-slate-600 overflow-auto"
666
+ class="z-10 pl-1 pr-1 pt-[8px] shadow shadow-slate-600 overflow-auto"
643
667
  ${ref(this.hierarchyRef)}
644
668
  @scroll=${this.syncHierarchyScroll}
645
669
  >
646
670
  ${renderHierarchyChildren(target ? [target] : [])}
647
671
  </div>
648
672
  <div
649
- class="h-full w-full cursor-crosshair overflow-auto bg-slate-200 pt-2"
673
+ class="flex h-full w-full cursor-crosshair overflow-auto bg-slate-200 pt-[8px]"
650
674
  id="gutter"
651
675
  ${ref(this.gutterRef)}
652
676
  @scroll=${this.syncGutterScroll}
@@ -682,8 +706,14 @@ let EFFilmstrip = class extends TWMixin(LitElement) {
682
706
  }
683
707
  }
684
708
  }
685
- get targetTimegroup() {
686
- return __privateGet(this, _EFFilmstrip_instances, contextElement_get)?.targetTimegroup;
709
+ willUpdate(changedProperties) {
710
+ if (changedProperties.has("targetTimegroup")) {
711
+ __privateMethod(this, _EFFilmstrip_instances, bindToTargetTimegroup_fn).call(this);
712
+ }
713
+ if (this.autoScale) {
714
+ this.updatePixelsPerMs();
715
+ }
716
+ super.willUpdate(changedProperties);
687
717
  }
688
718
  };
689
719
  _EFFilmstrip_instances = /* @__PURE__ */ new WeakSet();
@@ -701,6 +731,14 @@ _handleKeyPress = /* @__PURE__ */ new WeakMap();
701
731
  contextElement_get = function() {
702
732
  return this.closest("ef-workbench, ef-preview");
703
733
  };
734
+ EFFilmstrip.styles = [
735
+ css`
736
+ :host {
737
+ display: block;
738
+ overflow: hidden;
739
+ }
740
+ `
741
+ ];
704
742
  __decorateClass([
705
743
  property({ type: Number })
706
744
  ], EFFilmstrip.prototype, "pixelsPerMs", 2);
@@ -721,6 +759,9 @@ __decorateClass([
721
759
  __decorateClass([
722
760
  state()
723
761
  ], EFFilmstrip.prototype, "currentTimeMs", 2);
762
+ __decorateClass([
763
+ property({ type: Boolean, reflect: true, attribute: "auto-scale" })
764
+ ], EFFilmstrip.prototype, "autoScale", 2);
724
765
  __decorateClass([
725
766
  eventOptions({ passive: false })
726
767
  ], EFFilmstrip.prototype, "syncGutterScroll", 1);
@@ -736,6 +777,13 @@ __decorateClass([
736
777
  __decorateClass([
737
778
  eventOptions({ passive: false })
738
779
  ], EFFilmstrip.prototype, "scrollScrub", 1);
780
+ __decorateClass([
781
+ property({ type: String })
782
+ ], EFFilmstrip.prototype, "target", 2);
783
+ __decorateClass([
784
+ consume({ context: targetTimegroupContext, subscribe: true }),
785
+ state()
786
+ ], EFFilmstrip.prototype, "targetTimegroup", 2);
739
787
  EFFilmstrip = __decorateClass([
740
788
  customElement("ef-filmstrip")
741
789
  ], EFFilmstrip);
@@ -0,0 +1,17 @@
1
+ import { LitElement } from 'lit';
2
+ export declare class EFFocusOverlay extends LitElement {
3
+ static styles: import('lit').CSSResult;
4
+ focusedElement?: HTMLElement | null;
5
+ overlay: import('lit-html/directives/ref.js').Ref<HTMLDivElement>;
6
+ private animationFrame?;
7
+ drawOverlay: () => void;
8
+ render(): import('lit-html').TemplateResult<1>;
9
+ connectedCallback(): void;
10
+ disconnectedCallback(): void;
11
+ protected updated(): void;
12
+ }
13
+ declare global {
14
+ interface HTMLElementTagNameMap {
15
+ "ef-focus-overlay": EFFocusOverlay;
16
+ }
17
+ }