@editframe/elements 0.16.7-beta.0 → 0.17.6-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +30 -0
  2. package/dist/DecoderResetFrequency.test.d.ts +1 -0
  3. package/dist/DecoderResetRecovery.test.d.ts +1 -0
  4. package/dist/DelayedLoadingState.d.ts +48 -0
  5. package/dist/DelayedLoadingState.integration.test.d.ts +1 -0
  6. package/dist/DelayedLoadingState.js +113 -0
  7. package/dist/DelayedLoadingState.test.d.ts +1 -0
  8. package/dist/EF_FRAMEGEN.d.ts +10 -1
  9. package/dist/EF_FRAMEGEN.js +199 -179
  10. package/dist/EF_INTERACTIVE.js +2 -6
  11. package/dist/EF_RENDERING.js +1 -3
  12. package/dist/JitTranscodingClient.browsertest.d.ts +1 -0
  13. package/dist/JitTranscodingClient.d.ts +167 -0
  14. package/dist/JitTranscodingClient.js +373 -0
  15. package/dist/JitTranscodingClient.test.d.ts +1 -0
  16. package/dist/LoadingDebounce.test.d.ts +1 -0
  17. package/dist/LoadingIndicator.browsertest.d.ts +0 -0
  18. package/dist/ManualScrubTest.test.d.ts +1 -0
  19. package/dist/ScrubResolvedFlashing.test.d.ts +1 -0
  20. package/dist/ScrubTrackIntegration.test.d.ts +1 -0
  21. package/dist/ScrubTrackManager.d.ts +96 -0
  22. package/dist/ScrubTrackManager.js +216 -0
  23. package/dist/ScrubTrackManager.test.d.ts +1 -0
  24. package/dist/SegmentSwitchLoading.test.d.ts +1 -0
  25. package/dist/VideoSeekFlashing.browsertest.d.ts +0 -0
  26. package/dist/VideoStuckDiagnostic.test.d.ts +1 -0
  27. package/dist/elements/CrossUpdateController.js +13 -15
  28. package/dist/elements/EFAudio.browsertest.d.ts +0 -0
  29. package/dist/elements/EFAudio.d.ts +1 -1
  30. package/dist/elements/EFAudio.js +30 -43
  31. package/dist/elements/EFCaptions.js +337 -373
  32. package/dist/elements/EFImage.js +64 -90
  33. package/dist/elements/EFMedia.d.ts +98 -33
  34. package/dist/elements/EFMedia.js +1169 -678
  35. package/dist/elements/EFSourceMixin.js +31 -48
  36. package/dist/elements/EFTemporal.d.ts +1 -0
  37. package/dist/elements/EFTemporal.js +266 -360
  38. package/dist/elements/EFTimegroup.d.ts +3 -1
  39. package/dist/elements/EFTimegroup.js +262 -323
  40. package/dist/elements/EFVideo.browsertest.d.ts +0 -0
  41. package/dist/elements/EFVideo.d.ts +90 -2
  42. package/dist/elements/EFVideo.js +408 -111
  43. package/dist/elements/EFWaveform.js +375 -411
  44. package/dist/elements/FetchMixin.js +14 -24
  45. package/dist/elements/MediaController.d.ts +30 -0
  46. package/dist/elements/TargetController.js +130 -156
  47. package/dist/elements/TimegroupController.js +17 -19
  48. package/dist/elements/durationConverter.js +15 -4
  49. package/dist/elements/parseTimeToMs.js +4 -10
  50. package/dist/elements/printTaskStatus.d.ts +2 -0
  51. package/dist/elements/printTaskStatus.js +11 -0
  52. package/dist/elements/updateAnimations.js +39 -59
  53. package/dist/getRenderInfo.js +58 -67
  54. package/dist/gui/ContextMixin.js +203 -288
  55. package/dist/gui/EFConfiguration.js +27 -43
  56. package/dist/gui/EFFilmstrip.js +440 -620
  57. package/dist/gui/EFFitScale.js +112 -135
  58. package/dist/gui/EFFocusOverlay.js +45 -61
  59. package/dist/gui/EFPreview.js +30 -49
  60. package/dist/gui/EFScrubber.js +78 -99
  61. package/dist/gui/EFTimeDisplay.js +49 -70
  62. package/dist/gui/EFToggleLoop.js +17 -34
  63. package/dist/gui/EFTogglePlay.js +37 -58
  64. package/dist/gui/EFWorkbench.js +66 -88
  65. package/dist/gui/TWMixin.js +2 -48
  66. package/dist/gui/TWMixin2.js +31 -0
  67. package/dist/gui/efContext.js +2 -6
  68. package/dist/gui/fetchContext.js +1 -3
  69. package/dist/gui/focusContext.js +1 -3
  70. package/dist/gui/focusedElementContext.js +2 -6
  71. package/dist/gui/playingContext.js +1 -4
  72. package/dist/index.js +5 -30
  73. package/dist/msToTimeCode.js +11 -13
  74. package/dist/style.css +2 -1
  75. package/package.json +3 -3
  76. package/src/elements/EFAudio.browsertest.ts +569 -0
  77. package/src/elements/EFAudio.ts +4 -6
  78. package/src/elements/EFCaptions.browsertest.ts +0 -1
  79. package/src/elements/EFImage.browsertest.ts +0 -1
  80. package/src/elements/EFMedia.browsertest.ts +147 -115
  81. package/src/elements/EFMedia.ts +1339 -307
  82. package/src/elements/EFTemporal.browsertest.ts +0 -1
  83. package/src/elements/EFTemporal.ts +11 -0
  84. package/src/elements/EFTimegroup.ts +73 -10
  85. package/src/elements/EFVideo.browsertest.ts +680 -0
  86. package/src/elements/EFVideo.ts +729 -50
  87. package/src/elements/EFWaveform.ts +4 -4
  88. package/src/elements/MediaController.ts +108 -0
  89. package/src/elements/__screenshots__/EFMedia.browsertest.ts/EFMedia-JIT-audio-playback-audioBufferTask-should-work-in-JIT-mode-without-URL-errors-1.png +0 -0
  90. package/src/elements/printTaskStatus.ts +16 -0
  91. package/src/elements/updateAnimations.ts +6 -0
  92. package/src/gui/TWMixin.ts +10 -3
  93. package/test/EFVideo.frame-tasks.browsertest.ts +524 -0
  94. package/test/EFVideo.framegen.browsertest.ts +118 -0
  95. package/test/createJitTestClips.ts +293 -0
  96. package/test/useAssetMSW.ts +49 -0
  97. package/test/useMSW.ts +31 -0
  98. package/types.json +1 -1
  99. package/dist/gui/TWMixin.css.js +0 -4
  100. /package/dist/elements/{TargetController.test.d.ts → TargetController.browsertest.d.ts} +0 -0
  101. /package/src/elements/{TargetController.test.ts → TargetController.browsertest.ts} +0 -0
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # Tasks: Hierarchical Async Task System for lit-html
2
+
3
+ ## Relevant Files
4
+
5
+ - `src/task-system/types.ts` - Complete TypeScript interfaces and types for the task system
6
+ - `src/task-system/Task.ts` - Core Task class with lifecycle management, hierarchy, and event handling
7
+ - `src/task-system/Task.test.ts` - Comprehensive unit tests for Task class
8
+ - `src/task-system/TaskManager.ts` - Core task orchestration with dependency tracking, resolution, and priority scheduling
9
+ - `src/task-system/TaskManager.test.ts` - Comprehensive unit tests for TaskManager with dependency scenarios
10
+ - `src/task-system/TaskRegistry.ts` - Global registry with automatic cleanup, memory management, and monitoring
11
+ - `src/task-system/TaskRegistry.test.ts` - Comprehensive unit tests for TaskRegistry covering all cleanup scenarios
12
+ - `src/task-system/index.ts` - Public API exports with convenience functions for common task patterns
13
+ - `src/integrations/lit-html-task-mixin.ts` - Lit-html component integration mixin
14
+ - `src/integrations/lit-html-task-mixin.test.ts` - Unit tests for lit-html integration
15
+ - `src/utils/task-debug.ts` - Developer debugging utilities and task graph visualization
16
+ - `src/utils/task-debug.test.ts` - Unit tests for debugging utilities
17
+
18
+ ### Notes
19
+
20
+ - Unit tests should be placed alongside the code files they are testing
21
+ - Use `npx tsx` to execute TypeScript files for testing
22
+ - The system should be domain-agnostic and reusable across different use cases
23
+
24
+ ## Tasks
25
+
26
+ - [x] 1.0 Design and implement core task abstraction and lifecycle management
27
+ - [x] 2.0 Build hierarchical dependency tracking and resolution system
28
+ - [x] 3.0 Create task registry and cleanup mechanisms
29
+ - [ ] 4.0 Implement lit-html component integration layer
30
+ - [ ] 5.0 Add debugging and observability features
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,48 @@
1
+ interface LoadingOptions {
2
+ background?: boolean;
3
+ }
4
+ export declare class DelayedLoadingState {
5
+ private operations;
6
+ private loadingDelayMs;
7
+ private isCurrentlyLoading;
8
+ private currentMessage;
9
+ private onStateChange?;
10
+ constructor(delayMs?: number, onStateChange?: (isLoading: boolean, message: string) => void);
11
+ /**
12
+ * Start a delayed loading operation
13
+ */
14
+ startLoading(operationId: string, message: string, options?: LoadingOptions): void;
15
+ /**
16
+ * Clear a loading operation
17
+ */
18
+ clearLoading(operationId: string): void;
19
+ /**
20
+ * Clear all loading operations
21
+ */
22
+ clearAllLoading(): void;
23
+ /**
24
+ * Get current loading state
25
+ */
26
+ get isLoading(): boolean;
27
+ /**
28
+ * Get current loading message
29
+ */
30
+ get message(): string;
31
+ /**
32
+ * Check if any non-background operations are active
33
+ */
34
+ private hasActiveOperations;
35
+ /**
36
+ * Get the message for the most recent non-background operation
37
+ */
38
+ private getCurrentMessage;
39
+ /**
40
+ * Trigger loading state (called by timeout)
41
+ */
42
+ private triggerLoadingState;
43
+ /**
44
+ * Update loading state based on current operations
45
+ */
46
+ private updateLoadingState;
47
+ }
48
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,113 @@
1
+ var DelayedLoadingState = class {
2
+ constructor(delayMs = 250, onStateChange) {
3
+ this.operations = /* @__PURE__ */ new Map();
4
+ this.isCurrentlyLoading = false;
5
+ this.currentMessage = "";
6
+ this.loadingDelayMs = delayMs;
7
+ this.onStateChange = onStateChange;
8
+ }
9
+ /**
10
+ * Start a delayed loading operation
11
+ */
12
+ startLoading(operationId, message, options = {}) {
13
+ const isBackground = options.background || false;
14
+ const existingOp = this.operations.get(operationId);
15
+ if (existingOp?.timeout) clearTimeout(existingOp.timeout);
16
+ if (isBackground) {
17
+ this.operations.set(operationId, {
18
+ id: operationId,
19
+ message,
20
+ startTime: Date.now(),
21
+ isBackground: true
22
+ });
23
+ return;
24
+ }
25
+ const timeout = setTimeout(() => {
26
+ const operation = this.operations.get(operationId);
27
+ if (operation && !operation.isBackground) this.triggerLoadingState();
28
+ }, this.loadingDelayMs);
29
+ this.operations.set(operationId, {
30
+ id: operationId,
31
+ message,
32
+ startTime: Date.now(),
33
+ timeout,
34
+ isBackground: false
35
+ });
36
+ }
37
+ /**
38
+ * Clear a loading operation
39
+ */
40
+ clearLoading(operationId) {
41
+ const operation = this.operations.get(operationId);
42
+ if (!operation) return;
43
+ if (operation.timeout) clearTimeout(operation.timeout);
44
+ this.operations.delete(operationId);
45
+ this.updateLoadingState();
46
+ }
47
+ /**
48
+ * Clear all loading operations
49
+ */
50
+ clearAllLoading() {
51
+ for (const operation of this.operations.values()) if (operation.timeout) clearTimeout(operation.timeout);
52
+ this.operations.clear();
53
+ this.updateLoadingState();
54
+ }
55
+ /**
56
+ * Get current loading state
57
+ */
58
+ get isLoading() {
59
+ return this.isCurrentlyLoading;
60
+ }
61
+ /**
62
+ * Get current loading message
63
+ */
64
+ get message() {
65
+ return this.currentMessage;
66
+ }
67
+ /**
68
+ * Check if any non-background operations are active
69
+ */
70
+ hasActiveOperations() {
71
+ for (const operation of this.operations.values()) if (!operation.isBackground) return true;
72
+ return false;
73
+ }
74
+ /**
75
+ * Get the message for the most recent non-background operation
76
+ */
77
+ getCurrentMessage() {
78
+ let latestTime = 0;
79
+ let latestMessage = "";
80
+ for (const operation of this.operations.values()) if (!operation.isBackground && operation.startTime > latestTime) {
81
+ latestTime = operation.startTime;
82
+ latestMessage = operation.message;
83
+ }
84
+ return latestMessage;
85
+ }
86
+ /**
87
+ * Trigger loading state (called by timeout)
88
+ */
89
+ triggerLoadingState() {
90
+ if (this.hasActiveOperations() && !this.isCurrentlyLoading) {
91
+ this.isCurrentlyLoading = true;
92
+ this.currentMessage = this.getCurrentMessage();
93
+ this.onStateChange?.(true, this.currentMessage);
94
+ }
95
+ }
96
+ /**
97
+ * Update loading state based on current operations
98
+ */
99
+ updateLoadingState() {
100
+ const shouldBeLoading = this.hasActiveOperations();
101
+ if (shouldBeLoading !== this.isCurrentlyLoading) {
102
+ this.isCurrentlyLoading = shouldBeLoading;
103
+ if (shouldBeLoading) {
104
+ this.currentMessage = this.getCurrentMessage();
105
+ this.onStateChange?.(true, this.currentMessage);
106
+ } else {
107
+ this.currentMessage = "";
108
+ this.onStateChange?.(false, "");
109
+ }
110
+ }
111
+ }
112
+ };
113
+ export { DelayedLoadingState };
@@ -0,0 +1 @@
1
+ export {};
@@ -6,6 +6,7 @@ interface Bridge {
6
6
  onTriggerCanvas(callback: () => void): void;
7
7
  frameReady(frameNumber: number, audioSamples: ArrayBuffer): void;
8
8
  error(error: Error): void;
9
+ syncLog(sequence: number, message: string, callback: () => void): void;
9
10
  }
10
11
  declare global {
11
12
  interface Window {
@@ -27,15 +28,23 @@ declare class TriggerCanvas {
27
28
  export declare class EFFramegen {
28
29
  time: number;
29
30
  frameDurationMs: number;
30
- initialBusyTasks: Promise<unknown[]>;
31
31
  audioBufferPromise?: Promise<AudioBuffer>;
32
32
  renderOptions?: VideoRenderOptions;
33
33
  frameBox: HTMLDivElement;
34
34
  BRIDGE: typeof window.FRAMEGEN_BRIDGE;
35
35
  triggerCanvas: TriggerCanvas;
36
+ verificationCanvas?: HTMLCanvasElement;
37
+ verificationCtx?: CanvasRenderingContext2D;
38
+ private logSequence;
39
+ frameTasksInProgress: boolean;
40
+ currentFrameNumber: number;
36
41
  trace(...args: any[]): void;
42
+ syncLog(...args: any[]): Promise<void>;
43
+ private initializeVerificationCanvas;
44
+ private drawVerificationStrip;
37
45
  constructor();
38
46
  connectToBridge(): void;
47
+ get showFrameBox(): boolean;
39
48
  initialize(renderOptions: VideoRenderOptions): Promise<void>;
40
49
  beginFrame(frameNumber: number, isLast: boolean): Promise<string | ArrayBuffer | null | undefined>;
41
50
  }
@@ -1,182 +1,202 @@
1
- import { TaskStatus } from "@lit/task";
2
- import { deepGetElementsWithFrameTasks } from "./elements/EFTemporal.js";
3
1
  import { shallowGetTimegroups } from "./elements/EFTimegroup.js";
4
- class TriggerCanvas {
5
- constructor() {
6
- this.canvasInitialized = false;
7
- this.canvas = document.createElement("canvas");
8
- const ctx = this.canvas.getContext("2d", { willReadFrequently: true });
9
- if (!ctx) throw new Error("Canvas 2d context not ready");
10
- this.ctx = ctx;
11
- this.ctx.fillStyle = "transparent";
12
- }
13
- initialize() {
14
- if (this.canvasInitialized) return;
15
- this.canvasInitialized = true;
16
- this.canvas.width = 1;
17
- this.canvas.height = 1;
18
- Object.assign(this.canvas.style, {
19
- position: "fixed",
20
- top: "0px",
21
- left: "0px",
22
- width: "100%",
23
- height: "100%",
24
- zIndex: "100000"
25
- });
26
- document.body.appendChild(this.canvas);
27
- }
28
- trigger() {
29
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
30
- }
31
- }
32
- class EFFramegen {
33
- constructor() {
34
- this.time = 0;
35
- this.frameDurationMs = 0;
36
- this.initialBusyTasks = Promise.resolve([]);
37
- this.frameBox = document.createElement("div");
38
- this.triggerCanvas = new TriggerCanvas();
39
- this.BRIDGE = window.FRAMEGEN_BRIDGE;
40
- if (this.BRIDGE) {
41
- console.log("trace: BRIDGE.constructor (connecting to bridge)");
42
- this.connectToBridge();
43
- }
44
- }
45
- trace(...args) {
46
- console.trace("[EF_FRAMEGEN]", ...args);
47
- }
48
- connectToBridge() {
49
- const BRIDGE = this.BRIDGE;
50
- if (!BRIDGE) {
51
- throw new Error("No BRIDGE when attempting to connect to bridge");
52
- }
53
- console.log("trace: BRIDGE.connectToBridge");
54
- BRIDGE.onInitialize(async (renderOptions) => {
55
- console.log("trace: BRIDGE.onInitialize", renderOptions);
56
- await this.initialize(renderOptions);
57
- BRIDGE.initialized();
58
- });
59
- BRIDGE.onBeginFrame((frameNumber, isLast) => {
60
- console.log("trace: BRIDGE.onBeginFrame", frameNumber, isLast);
61
- this.beginFrame(frameNumber, isLast);
62
- });
63
- }
64
- async initialize(renderOptions) {
65
- this.renderOptions = renderOptions;
66
- const workbench = document.querySelector("ef-workbench");
67
- if (!workbench) {
68
- throw new Error("No workbench found");
69
- }
70
- workbench.rendering = true;
71
- const timegroups = shallowGetTimegroups(workbench);
72
- const temporals = deepGetElementsWithFrameTasks(workbench);
73
- const firstGroup = timegroups[0];
74
- if (!firstGroup) {
75
- throw new Error("No temporal elements found");
76
- }
77
- firstGroup.currentTimeMs = renderOptions.encoderOptions.fromMs;
78
- this.frameDurationMs = 1e3 / renderOptions.encoderOptions.video.framerate;
79
- this.initialBusyTasks = Promise.all(
80
- temporals.filter((temporal) => temporal.frameTask.status < TaskStatus.COMPLETE).map((temporal) => temporal.frameTask).map((task) => task.taskComplete)
81
- );
82
- this.time = 0;
83
- if (renderOptions.showFrameBox) {
84
- Object.assign(this.frameBox.style, {
85
- width: "200px",
86
- height: "100px",
87
- font: "30px Arial",
88
- backgroundColor: "white",
89
- position: "absolute",
90
- top: "0px",
91
- left: "0px",
92
- zIndex: "100000"
93
- });
94
- document.body.prepend(this.frameBox);
95
- }
96
- this.audioBufferPromise = firstGroup.renderAudio(
97
- renderOptions.encoderOptions.alignedFromUs / 1e3,
98
- renderOptions.encoderOptions.alignedToUs / 1e3
99
- );
100
- console.log("trace: Initialized");
101
- }
102
- async beginFrame(frameNumber, isLast) {
103
- if (this.renderOptions === void 0) {
104
- throw new Error("No renderOptions");
105
- }
106
- if (this.renderOptions.showFrameBox) {
107
- this.frameBox.innerHTML = `
108
- <div>Frame #${frameNumber}</div>
109
- <div>${this.time.toFixed(4)}</div>
2
+ var TriggerCanvas = class {
3
+ constructor() {
4
+ this.canvasInitialized = false;
5
+ this.canvas = document.createElement("canvas");
6
+ const ctx = this.canvas.getContext("2d", { willReadFrequently: true });
7
+ if (!ctx) throw new Error("Canvas 2d context not ready");
8
+ this.ctx = ctx;
9
+ this.ctx.fillStyle = "transparent";
10
+ }
11
+ initialize() {
12
+ if (this.canvasInitialized) return;
13
+ this.canvasInitialized = true;
14
+ this.canvas.width = 1;
15
+ this.canvas.height = 1;
16
+ Object.assign(this.canvas.style, {
17
+ position: "fixed",
18
+ top: "0px",
19
+ left: "0px",
20
+ width: "100%",
21
+ height: "100%",
22
+ zIndex: "100000"
23
+ });
24
+ document.body.appendChild(this.canvas);
25
+ }
26
+ trigger() {
27
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
28
+ }
29
+ };
30
+ var EFFramegen = class {
31
+ trace(...args) {
32
+ console.trace("[EF_FRAMEGEN]", ...args);
33
+ }
34
+ async syncLog(...args) {
35
+ if (!this.BRIDGE) {
36
+ console.log("[EF_FRAMEGEN]", ...args);
37
+ return;
38
+ }
39
+ const sequence = ++this.logSequence;
40
+ const message = args.map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)).join(" ");
41
+ return new Promise((resolve) => {
42
+ this.BRIDGE.syncLog(sequence, message, () => {
43
+ resolve();
44
+ });
45
+ });
46
+ }
47
+ initializeVerificationCanvas() {
48
+ if (this.verificationCanvas) return;
49
+ this.verificationCanvas = document.createElement("canvas");
50
+ const ctx = this.verificationCanvas.getContext("2d");
51
+ if (!ctx) throw new Error("Verification canvas 2d context not ready");
52
+ this.verificationCtx = ctx;
53
+ const workbench = document.querySelector("ef-workbench");
54
+ if (workbench) {
55
+ this.verificationCanvas.width = workbench.clientWidth;
56
+ this.verificationCanvas.height = 1;
57
+ Object.assign(this.verificationCanvas.style, {
58
+ position: "fixed",
59
+ left: "0px",
60
+ bottom: "0px",
61
+ width: `${workbench.clientWidth}px`,
62
+ height: "1px",
63
+ zIndex: "99999"
64
+ });
65
+ document.body.appendChild(this.verificationCanvas);
66
+ }
67
+ }
68
+ drawVerificationStrip(frameNumber) {
69
+ this.initializeVerificationCanvas();
70
+ if (!this.verificationCanvas || !this.verificationCtx) return;
71
+ const width = this.verificationCanvas.width;
72
+ const height = this.verificationCanvas.height;
73
+ this.verificationCtx.clearRect(0, 0, width, height);
74
+ const red = Math.floor(frameNumber / (256 * 256)) % 256;
75
+ const green = Math.floor(frameNumber / 256) % 256;
76
+ const blue = frameNumber % 256;
77
+ this.verificationCtx.fillStyle = `rgb(${red}, ${green}, ${blue})`;
78
+ this.verificationCtx.fillRect(0, 0, width, height);
79
+ }
80
+ constructor() {
81
+ this.time = 0;
82
+ this.frameDurationMs = 0;
83
+ this.frameBox = document.createElement("div");
84
+ this.triggerCanvas = new TriggerCanvas();
85
+ this.logSequence = 0;
86
+ this.frameTasksInProgress = false;
87
+ this.currentFrameNumber = 0;
88
+ this.BRIDGE = window.FRAMEGEN_BRIDGE;
89
+ if (this.BRIDGE) this.connectToBridge();
90
+ }
91
+ connectToBridge() {
92
+ const BRIDGE = this.BRIDGE;
93
+ if (!BRIDGE) throw new Error("No BRIDGE when attempting to connect to bridge");
94
+ BRIDGE.onInitialize(async (renderOptions) => {
95
+ await this.initialize(renderOptions);
96
+ BRIDGE.initialized();
97
+ });
98
+ BRIDGE.onBeginFrame((frameNumber, isLast) => {
99
+ this.beginFrame(frameNumber, isLast);
100
+ });
101
+ BRIDGE.onTriggerCanvas(() => {
102
+ this.triggerCanvas.trigger();
103
+ });
104
+ }
105
+ get showFrameBox() {
106
+ return true;
107
+ }
108
+ async initialize(renderOptions) {
109
+ this.renderOptions = renderOptions;
110
+ const alignedFromMs = renderOptions.encoderOptions.alignedFromUs / 1e3;
111
+ const alignedToMs = renderOptions.encoderOptions.alignedToUs / 1e3;
112
+ const alignedDurationMs = alignedToMs - alignedFromMs;
113
+ await this.syncLog("[EF_FRAMEGEN.initialize] Aligned boundary parameters:", {
114
+ alignedFromUs: renderOptions.encoderOptions.alignedFromUs,
115
+ alignedToUs: renderOptions.encoderOptions.alignedToUs,
116
+ alignedFromMs: alignedFromMs.toFixed(3),
117
+ alignedToMs: alignedToMs.toFixed(3),
118
+ alignedDurationMs: alignedDurationMs.toFixed(3),
119
+ sequenceNumber: renderOptions.encoderOptions.sequenceNumber,
120
+ originalFromMs: renderOptions.encoderOptions.fromMs,
121
+ originalToMs: renderOptions.encoderOptions.toMs,
122
+ originalDurationMs: (renderOptions.encoderOptions.toMs - renderOptions.encoderOptions.fromMs).toFixed(3)
123
+ });
124
+ const workbench = document.querySelector("ef-workbench");
125
+ if (!workbench) throw new Error("No workbench found");
126
+ workbench.rendering = true;
127
+ workbench.playing = false;
128
+ const timegroups = shallowGetTimegroups(workbench);
129
+ const firstGroup = timegroups[0];
130
+ if (!firstGroup) throw new Error("No temporal elements found");
131
+ const startingTimeMs = renderOptions.encoderOptions.fromMs;
132
+ await firstGroup.waitForMediaDurations();
133
+ await firstGroup.waitForFrameTasks();
134
+ this.frameDurationMs = 1e3 / renderOptions.encoderOptions.video.framerate;
135
+ this.time = startingTimeMs;
136
+ if (this.showFrameBox) {
137
+ Object.assign(this.frameBox.style, {
138
+ width: "200px",
139
+ height: "100px",
140
+ font: "10px Arial",
141
+ backgroundColor: "white",
142
+ position: "absolute",
143
+ top: "0px",
144
+ right: "0px",
145
+ zIndex: "100000"
146
+ });
147
+ document.body.prepend(this.frameBox);
148
+ }
149
+ this.triggerCanvas.initialize();
150
+ await this.syncLog("[EF_FRAMEGEN.initialize] About to call renderAudio with:", {
151
+ fromMs: alignedFromMs.toFixed(3),
152
+ toMs: alignedToMs.toFixed(3),
153
+ durationMs: alignedDurationMs.toFixed(3)
154
+ });
155
+ this.audioBufferPromise = firstGroup.renderAudio(renderOptions.encoderOptions.alignedFromUs / 1e3, renderOptions.encoderOptions.alignedToUs / 1e3);
156
+ }
157
+ async beginFrame(frameNumber, isLast) {
158
+ if (this.renderOptions === void 0) throw new Error("No renderOptions");
159
+ const workbench = document.querySelector("ef-workbench");
160
+ if (!workbench) throw new Error("No workbench found");
161
+ workbench.rendering = true;
162
+ const timegroups = shallowGetTimegroups(workbench);
163
+ const firstGroup = timegroups[0];
164
+ if (!firstGroup) throw new Error("No temporal elements found");
165
+ const frameTime = this.renderOptions.encoderOptions.fromMs + frameNumber * this.frameDurationMs;
166
+ firstGroup.currentTimeMs = Number(Number(frameTime).toFixed(5));
167
+ await firstGroup.waitForFrameTasks();
168
+ if (this.showFrameBox) this.frameBox.innerHTML = `
169
+ <div>🖼️ Frame: ${frameNumber}</div>
170
+ <div>🕛 Segment: ${this.time.toFixed(4)}</div>
171
+ <div>🕛 Frame: ${firstGroup.currentTimeMs.toFixed(4)}</div>
172
+ <div> from-to: ${this.renderOptions.encoderOptions.fromMs.toFixed(4)} - ${this.renderOptions.encoderOptions.toMs.toFixed(4)}</div>
110
173
  `;
111
- }
112
- const workbench = document.querySelector("ef-workbench");
113
- if (!workbench) {
114
- throw new Error("No workbench found");
115
- }
116
- workbench.rendering = true;
117
- const timegroups = shallowGetTimegroups(workbench);
118
- const temporals = deepGetElementsWithFrameTasks(workbench);
119
- const firstGroup = timegroups[0];
120
- if (!firstGroup) {
121
- throw new Error("No temporal elements found");
122
- }
123
- this.time = this.renderOptions.encoderOptions.fromMs + frameNumber * this.frameDurationMs;
124
- firstGroup.currentTimeMs = this.time;
125
- await this.initialBusyTasks;
126
- await new Promise(queueMicrotask);
127
- const now = performance.now();
128
- await Promise.all(
129
- temporals.filter((temporal) => temporal.frameTask.status < TaskStatus.COMPLETE).map((temporal) => {
130
- return temporal.frameTask;
131
- }).map((task) => task.taskComplete)
132
- );
133
- console.log(
134
- `trace: frame:${frameNumber} All tasks complete ${performance.now() - now}ms`
135
- );
136
- this.triggerCanvas.trigger();
137
- if (isLast && this.audioBufferPromise) {
138
- const renderedAudio = await this.audioBufferPromise;
139
- const channelCount = renderedAudio.numberOfChannels;
140
- const interleavedSamples = new Float32Array(
141
- channelCount * renderedAudio.length
142
- );
143
- for (let i = 0; i < renderedAudio.length; i++) {
144
- for (let j = 0; j < channelCount; j++) {
145
- interleavedSamples.set(
146
- renderedAudio.getChannelData(j).slice(i, i + 1),
147
- i * channelCount + j
148
- );
149
- }
150
- }
151
- if (this.BRIDGE) {
152
- this.BRIDGE.frameReady(frameNumber, interleavedSamples.buffer);
153
- } else {
154
- const fileReader = new FileReader();
155
- fileReader.readAsDataURL(new Blob([interleavedSamples.buffer]));
156
- await new Promise((resolve, reject) => {
157
- fileReader.onload = resolve;
158
- fileReader.onerror = reject;
159
- });
160
- return fileReader.result;
161
- }
162
- } else {
163
- if (this.BRIDGE) {
164
- this.BRIDGE.frameReady(frameNumber, new ArrayBuffer(0));
165
- } else {
166
- const fileReader = new FileReader();
167
- fileReader.readAsDataURL(new Blob([]));
168
- await new Promise((resolve, reject) => {
169
- fileReader.onload = resolve;
170
- fileReader.onerror = reject;
171
- });
172
- return fileReader.result;
173
- }
174
- }
175
- }
176
- }
177
- if (typeof window !== "undefined") {
178
- window.EF_FRAMEGEN = new EFFramegen();
179
- }
180
- export {
181
- EFFramegen
174
+ this.drawVerificationStrip(frameNumber);
175
+ if (isLast && this.audioBufferPromise) {
176
+ const renderedAudio = await this.audioBufferPromise;
177
+ const channelCount = renderedAudio.numberOfChannels;
178
+ const interleavedSamples = new Float32Array(channelCount * renderedAudio.length);
179
+ for (let i = 0; i < renderedAudio.length; i++) for (let j = 0; j < channelCount; j++) interleavedSamples.set(renderedAudio.getChannelData(j).slice(i, i + 1), i * channelCount + j);
180
+ if (this.BRIDGE) this.BRIDGE.frameReady(frameNumber, interleavedSamples.buffer);
181
+ else {
182
+ const fileReader = new FileReader();
183
+ fileReader.readAsDataURL(new Blob([interleavedSamples.buffer]));
184
+ await new Promise((resolve, reject) => {
185
+ fileReader.onload = resolve;
186
+ fileReader.onerror = reject;
187
+ });
188
+ return fileReader.result;
189
+ }
190
+ } else if (this.BRIDGE) this.BRIDGE.frameReady(frameNumber, /* @__PURE__ */ new ArrayBuffer(0));
191
+ else {
192
+ const fileReader = new FileReader();
193
+ fileReader.readAsDataURL(new Blob([]));
194
+ await new Promise((resolve, reject) => {
195
+ fileReader.onload = resolve;
196
+ fileReader.onerror = reject;
197
+ });
198
+ return fileReader.result;
199
+ }
200
+ }
182
201
  };
202
+ if (typeof window !== "undefined") window.EF_FRAMEGEN = new EFFramegen();
@@ -1,7 +1,3 @@
1
1
  let EF_INTERACTIVE = false;
2
- if (typeof window !== "undefined") {
3
- EF_INTERACTIVE = !window.location?.search.includes("EF_NONINTERACTIVE");
4
- }
5
- export {
6
- EF_INTERACTIVE
7
- };
2
+ if (typeof window !== "undefined") EF_INTERACTIVE = !window.location?.search.includes("EF_NONINTERACTIVE");
3
+ export { EF_INTERACTIVE };
@@ -1,4 +1,2 @@
1
1
  const EF_RENDERING = () => typeof window !== "undefined" && "FRAMEGEN_BRIDGE" in window;
2
- export {
3
- EF_RENDERING
4
- };
2
+ export { EF_RENDERING };
@@ -0,0 +1 @@
1
+ export {};