@editframe/elements 0.36.0-beta → 0.36.1-beta
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/canvas/EFCanvas.d.ts +4 -4
- package/dist/elements/EFAudio.d.ts +4 -4
- package/dist/elements/EFCaptions.d.ts +0 -4
- package/dist/elements/EFCaptions.js +12 -32
- package/dist/elements/EFCaptions.js.map +1 -1
- package/dist/elements/EFPanZoom.d.ts +4 -4
- package/dist/elements/EFSurface.d.ts +4 -4
- package/dist/elements/EFText.d.ts +4 -4
- package/dist/elements/EFTextSegment.d.ts +4 -4
- package/dist/elements/EFThumbnailStrip.d.ts +4 -4
- package/dist/elements/EFVideo.d.ts +6 -6
- package/dist/elements/EFWaveform.d.ts +4 -4
- package/dist/elements/updateAnimations.js +75 -0
- package/dist/elements/updateAnimations.js.map +1 -1
- package/dist/gui/EFActiveRootTemporal.d.ts +4 -4
- package/dist/gui/EFConfiguration.d.ts +4 -4
- package/dist/gui/EFControls.d.ts +2 -2
- package/dist/gui/EFDial.d.ts +4 -4
- package/dist/gui/EFFilmstrip.d.ts +2 -2
- package/dist/gui/EFFitScale.d.ts +3 -3
- package/dist/gui/EFFocusOverlay.d.ts +4 -4
- package/dist/gui/EFPause.d.ts +4 -4
- package/dist/gui/EFPlay.d.ts +4 -4
- package/dist/gui/EFPreview.d.ts +4 -4
- package/dist/gui/EFResizableBox.d.ts +4 -4
- package/dist/gui/EFScrubber.d.ts +4 -4
- package/dist/gui/EFTimeDisplay.d.ts +4 -4
- package/dist/gui/EFToggleLoop.d.ts +4 -4
- package/dist/gui/EFTogglePlay.d.ts +4 -4
- package/dist/gui/EFTransformHandles.d.ts +4 -4
- package/dist/gui/EFWorkbench.d.ts +6 -6
- package/dist/gui/EFWorkbench.js +28 -0
- package/dist/gui/EFWorkbench.js.map +1 -1
- package/dist/gui/hierarchy/EFHierarchy.d.ts +4 -4
- package/dist/gui/hierarchy/EFHierarchyItem.d.ts +2 -2
- package/dist/gui/timeline/tracks/ImageTrack.d.ts +2 -2
- package/dist/gui/timeline/tracks/TimegroupTrack.d.ts +5 -5
- package/dist/gui/timeline/tracks/VideoTrack.d.ts +4 -4
- package/dist/gui/tree/EFTree.d.ts +4 -4
- package/dist/gui/tree/EFTreeItem.d.ts +4 -4
- package/dist/style.css +33 -0
- package/package.json +2 -2
|
@@ -5,9 +5,9 @@ import { PanZoomTransform } from "../elements/EFPanZoom.js";
|
|
|
5
5
|
import "./overlays/SelectionOverlay.js";
|
|
6
6
|
import "../gui/EFOverlayLayer.js";
|
|
7
7
|
import "../gui/EFTransformHandles.js";
|
|
8
|
-
import * as
|
|
8
|
+
import * as lit3 from "lit";
|
|
9
9
|
import { LitElement } from "lit";
|
|
10
|
-
import * as
|
|
10
|
+
import * as lit_html2 from "lit-html";
|
|
11
11
|
|
|
12
12
|
//#region src/canvas/EFCanvas.d.ts
|
|
13
13
|
declare const EFCanvas_base: typeof LitElement;
|
|
@@ -87,7 +87,7 @@ declare const EFCanvas_base: typeof LitElement;
|
|
|
87
87
|
* Manages existing elements (EF* elements, divs, etc.) and provides selection functionality.
|
|
88
88
|
*/
|
|
89
89
|
declare class EFCanvas extends EFCanvas_base {
|
|
90
|
-
static styles:
|
|
90
|
+
static styles: lit3.CSSResult[];
|
|
91
91
|
panZoomTransform?: PanZoomTransform;
|
|
92
92
|
elementIdAttribute: string;
|
|
93
93
|
enableTransformHandles: boolean;
|
|
@@ -302,7 +302,7 @@ declare class EFCanvas extends EFCanvas_base {
|
|
|
302
302
|
* Cleanup transform handles.
|
|
303
303
|
*/
|
|
304
304
|
private cleanupTransformHandles;
|
|
305
|
-
render():
|
|
305
|
+
render(): lit_html2.TemplateResult<1>;
|
|
306
306
|
}
|
|
307
307
|
declare global {
|
|
308
308
|
interface HTMLElementTagNameMap {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FrameRenderable, FrameState, FrameTask } from "../preview/FrameController.js";
|
|
2
2
|
import { EFMedia } from "./EFMedia.js";
|
|
3
|
-
import * as
|
|
4
|
-
import * as
|
|
3
|
+
import * as lit_html8 from "lit-html";
|
|
4
|
+
import * as lit_html_directives_ref_js2 from "lit-html/directives/ref.js";
|
|
5
5
|
|
|
6
6
|
//#region src/elements/EFAudio.d.ts
|
|
7
7
|
declare const EFAudio_base: typeof EFMedia;
|
|
@@ -17,9 +17,9 @@ declare class EFAudio extends EFAudio_base implements FrameRenderable {
|
|
|
17
17
|
* @domAttribute "volume"
|
|
18
18
|
*/
|
|
19
19
|
volume: number;
|
|
20
|
-
audioElementRef:
|
|
20
|
+
audioElementRef: lit_html_directives_ref_js2.Ref<HTMLAudioElement>;
|
|
21
21
|
protected updated(changedProperties: Map<PropertyKey, unknown>): void;
|
|
22
|
-
render():
|
|
22
|
+
render(): lit_html8.TemplateResult<1>;
|
|
23
23
|
/**
|
|
24
24
|
* @deprecated Use FrameRenderable methods (prepareFrame, renderFrame) via FrameController instead.
|
|
25
25
|
* This is a compatibility wrapper that delegates to the new system.
|
|
@@ -30,7 +30,6 @@ interface Caption {
|
|
|
30
30
|
*/
|
|
31
31
|
declare class EFCaptionsActiveWord extends HTMLElement {
|
|
32
32
|
#private;
|
|
33
|
-
constructor();
|
|
34
33
|
set wordText(text: string);
|
|
35
34
|
get wordText(): string;
|
|
36
35
|
set wordIndex(index: number);
|
|
@@ -42,7 +41,6 @@ declare class EFCaptionsActiveWord extends HTMLElement {
|
|
|
42
41
|
*/
|
|
43
42
|
declare class EFCaptionsSegment extends HTMLElement {
|
|
44
43
|
#private;
|
|
45
|
-
constructor();
|
|
46
44
|
set segmentText(text: string);
|
|
47
45
|
get segmentText(): string;
|
|
48
46
|
}
|
|
@@ -51,7 +49,6 @@ declare class EFCaptionsSegment extends HTMLElement {
|
|
|
51
49
|
* Uses light DOM for simplicity - parent sets textContent directly.
|
|
52
50
|
*/
|
|
53
51
|
declare class EFCaptionsBeforeActiveWord extends EFCaptionsSegment {
|
|
54
|
-
constructor();
|
|
55
52
|
set segmentText(text: string);
|
|
56
53
|
}
|
|
57
54
|
/**
|
|
@@ -59,7 +56,6 @@ declare class EFCaptionsBeforeActiveWord extends EFCaptionsSegment {
|
|
|
59
56
|
* Uses light DOM for simplicity - parent sets textContent directly.
|
|
60
57
|
*/
|
|
61
58
|
declare class EFCaptionsAfterActiveWord extends EFCaptionsSegment {
|
|
62
|
-
constructor();
|
|
63
59
|
set segmentText(text: string);
|
|
64
60
|
}
|
|
65
61
|
declare const EFCaptions_base: (new (...args: any[]) => EFSourceMixinInterface) & (new (...args: any[]) => TemporalMixinInterface) & (new (...args: any[]) => FetchMixinInterface) & typeof LitElement;
|
|
@@ -22,20 +22,14 @@ const stopWords = new Set([
|
|
|
22
22
|
let EFCaptionsActiveWord = class EFCaptionsActiveWord$1 extends HTMLElement {
|
|
23
23
|
#wordText = "";
|
|
24
24
|
#wordIndex = 0;
|
|
25
|
-
constructor() {
|
|
26
|
-
super();
|
|
27
|
-
this.style.display = "inline-block";
|
|
28
|
-
this.style.whiteSpace = "normal";
|
|
29
|
-
this.style.lineHeight = "1";
|
|
30
|
-
}
|
|
31
25
|
set wordText(text) {
|
|
32
26
|
this.#wordText = text;
|
|
33
27
|
if (!text || stopWords.has(text)) {
|
|
34
|
-
this.
|
|
28
|
+
this.hidden = true;
|
|
35
29
|
this.textContent = "";
|
|
36
30
|
} else {
|
|
37
|
-
this.
|
|
38
|
-
this.textContent = text;
|
|
31
|
+
this.hidden = false;
|
|
32
|
+
this.textContent = text + " ";
|
|
39
33
|
}
|
|
40
34
|
}
|
|
41
35
|
get wordText() {
|
|
@@ -53,19 +47,13 @@ let EFCaptionsActiveWord = class EFCaptionsActiveWord$1 extends HTMLElement {
|
|
|
53
47
|
EFCaptionsActiveWord = __decorate([customElement("ef-captions-active-word")], EFCaptionsActiveWord);
|
|
54
48
|
let EFCaptionsSegment = class EFCaptionsSegment$1 extends HTMLElement {
|
|
55
49
|
#segmentText = "";
|
|
56
|
-
constructor() {
|
|
57
|
-
super();
|
|
58
|
-
this.style.display = "inline-block";
|
|
59
|
-
this.style.whiteSpace = "normal";
|
|
60
|
-
this.style.lineHeight = "1";
|
|
61
|
-
}
|
|
62
50
|
set segmentText(text) {
|
|
63
51
|
this.#segmentText = text;
|
|
64
52
|
if (!text || stopWords.has(text)) {
|
|
65
|
-
this.
|
|
53
|
+
this.hidden = true;
|
|
66
54
|
this.textContent = "";
|
|
67
55
|
} else {
|
|
68
|
-
this.
|
|
56
|
+
this.hidden = false;
|
|
69
57
|
this.textContent = text;
|
|
70
58
|
}
|
|
71
59
|
}
|
|
@@ -75,35 +63,27 @@ let EFCaptionsSegment = class EFCaptionsSegment$1 extends HTMLElement {
|
|
|
75
63
|
};
|
|
76
64
|
EFCaptionsSegment = __decorate([customElement("ef-captions-segment")], EFCaptionsSegment);
|
|
77
65
|
let EFCaptionsBeforeActiveWord = class EFCaptionsBeforeActiveWord$1 extends EFCaptionsSegment {
|
|
78
|
-
constructor() {
|
|
79
|
-
super();
|
|
80
|
-
this.style.whiteSpace = "pre";
|
|
81
|
-
}
|
|
82
66
|
set segmentText(text) {
|
|
83
67
|
const hasActiveWord = (this.closest("ef-captions")?.querySelector("ef-captions-active-word"))?.wordText;
|
|
84
68
|
const finalText = text && hasActiveWord ? text + " " : text;
|
|
85
69
|
if (!finalText || stopWords.has(finalText)) {
|
|
86
|
-
this.
|
|
70
|
+
this.hidden = true;
|
|
87
71
|
this.textContent = "";
|
|
88
72
|
} else {
|
|
89
|
-
this.
|
|
73
|
+
this.hidden = false;
|
|
90
74
|
this.textContent = finalText;
|
|
91
75
|
}
|
|
92
76
|
}
|
|
93
77
|
};
|
|
94
78
|
EFCaptionsBeforeActiveWord = __decorate([customElement("ef-captions-before-active-word")], EFCaptionsBeforeActiveWord);
|
|
95
79
|
let EFCaptionsAfterActiveWord = class EFCaptionsAfterActiveWord$1 extends EFCaptionsSegment {
|
|
96
|
-
constructor() {
|
|
97
|
-
super();
|
|
98
|
-
this.style.whiteSpace = "pre";
|
|
99
|
-
}
|
|
100
80
|
set segmentText(text) {
|
|
101
|
-
const finalText = text
|
|
81
|
+
const finalText = text;
|
|
102
82
|
if (!finalText || stopWords.has(finalText)) {
|
|
103
|
-
this.
|
|
83
|
+
this.hidden = true;
|
|
104
84
|
this.textContent = "";
|
|
105
85
|
} else {
|
|
106
|
-
this.
|
|
86
|
+
this.hidden = false;
|
|
107
87
|
this.textContent = finalText;
|
|
108
88
|
}
|
|
109
89
|
}
|
|
@@ -127,13 +107,13 @@ let EFCaptions = class EFCaptions$1 extends EFSourceMixin(EFTemporal(FetchMixin(
|
|
|
127
107
|
static {
|
|
128
108
|
this.styles = [css`
|
|
129
109
|
:host {
|
|
130
|
-
display:
|
|
110
|
+
display: block;
|
|
131
111
|
white-space: normal;
|
|
132
112
|
line-height: 1;
|
|
133
113
|
gap: 0;
|
|
134
114
|
}
|
|
135
115
|
::slotted(*) {
|
|
136
|
-
display: inline
|
|
116
|
+
display: inline;
|
|
137
117
|
margin: 0;
|
|
138
118
|
padding: 0;
|
|
139
119
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EFCaptions.js","names":["EFCaptionsActiveWord","#wordText","#wordIndex","EFCaptionsSegment","#segmentText","EFCaptionsBeforeActiveWord","EFCaptionsAfterActiveWord","EFCaptions","#captionsDataLoaded","#captionsDataValue","#captionsDataPromise","#doLoadCaptionsData","#transcriptionData","#loadTranscriptionFragment","#rootTimegroupUpdateController","#cachedIntrinsicDurationMs","captionsData: Caption | null","result: number"],"sources":["../../src/elements/EFCaptions.ts"],"sourcesContent":["import { css, html, LitElement, type PropertyValueMap } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport type { ReactiveController } from \"lit\";\nimport type { GetISOBMFFFileTranscriptionResult } from \"../../../api/src/index.js\";\nimport {\n type FrameRenderable,\n type FrameState,\n createFrameTaskWrapper,\n PRIORITY_CAPTIONS,\n} from \"../preview/FrameController.js\";\nimport { AsyncValue } from \"./EFMedia.js\";\nimport { CrossUpdateController } from \"./CrossUpdateController.js\";\nimport { EFAudio } from \"./EFAudio.js\";\nimport { EFSourceMixin } from \"./EFSourceMixin.js\";\nimport { EFTemporal, flushStartTimeMsCache } from \"./EFTemporal.js\";\nimport {\n flushSequenceDurationCache,\n EFTimegroup,\n} from \"./EFTimegroup.js\";\nimport { EFVideo } from \"./EFVideo.js\";\nimport { FetchMixin } from \"./FetchMixin.js\";\n\nexport interface WordSegment {\n text: string;\n start: number;\n end: number;\n}\n\nexport interface Segment {\n start: number;\n end: number;\n text: string;\n}\n\nexport interface Caption {\n segments: Segment[];\n word_segments: WordSegment[];\n}\n\nconst stopWords = new Set([\"\", \".\", \"!\", \"?\", \",\"]);\n\n/**\n * Caption active word element - displays the currently spoken word.\n * Uses light DOM for simplicity - parent sets textContent directly.\n */\n@customElement(\"ef-captions-active-word\")\nexport class EFCaptionsActiveWord extends HTMLElement {\n #wordText = \"\";\n #wordIndex = 0;\n \n constructor() {\n super();\n // Apply default styles via inline style\n this.style.display = \"inline-block\";\n this.style.whiteSpace = \"normal\";\n this.style.lineHeight = \"1\";\n }\n \n set wordText(text: string) {\n this.#wordText = text;\n // Hide element if no content or only stop words\n if (!text || stopWords.has(text)) {\n this.style.display = \"none\";\n this.textContent = \"\";\n } else {\n this.style.display = \"inline-block\";\n this.textContent = text;\n }\n }\n \n get wordText(): string {\n return this.#wordText;\n }\n \n set wordIndex(index: number) {\n this.#wordIndex = index;\n // Set deterministic --ef-word-seed value based on word index\n const seed = (index * 9007) % 233; // Prime numbers for better distribution\n const seedValue = seed / 233; // Normalize to 0-1 range\n this.style.setProperty(\"--ef-word-seed\", seedValue.toString());\n }\n \n get wordIndex(): number {\n return this.#wordIndex;\n }\n}\n\n/**\n * Caption segment element - displays a full caption segment.\n * Uses light DOM for simplicity - parent sets textContent directly.\n */\n@customElement(\"ef-captions-segment\")\nexport class EFCaptionsSegment extends HTMLElement {\n #segmentText = \"\";\n \n constructor() {\n super();\n // Apply default styles via inline style\n this.style.display = \"inline-block\";\n this.style.whiteSpace = \"normal\";\n this.style.lineHeight = \"1\";\n }\n \n set segmentText(text: string) {\n this.#segmentText = text;\n // Hide element if no content or only stop words\n if (!text || stopWords.has(text)) {\n this.style.display = \"none\";\n this.textContent = \"\";\n } else {\n this.style.display = \"inline-block\";\n this.textContent = text;\n }\n }\n \n get segmentText(): string {\n return this.#segmentText;\n }\n}\n\n/**\n * Caption before-active-word element - displays words before the current word.\n * Uses light DOM for simplicity - parent sets textContent directly.\n */\n@customElement(\"ef-captions-before-active-word\")\nexport class EFCaptionsBeforeActiveWord extends EFCaptionsSegment {\n constructor() {\n super();\n // Override whiteSpace to preserve spacing\n this.style.whiteSpace = \"pre\";\n }\n \n set segmentText(text: string) {\n // Check if there's an active word by looking for sibling active word element\n const activeWord = this.closest(\"ef-captions\")?.querySelector(\n \"ef-captions-active-word\",\n ) as EFCaptionsActiveWord;\n const hasActiveWord = activeWord?.wordText;\n \n // Add trailing space if there's an active word coming after us\n const finalText = text && hasActiveWord ? text + \" \" : text;\n \n // Hide element if no content or only stop words\n if (!finalText || stopWords.has(finalText)) {\n this.style.display = \"none\";\n this.textContent = \"\";\n } else {\n this.style.display = \"inline-block\";\n this.textContent = finalText;\n }\n }\n}\n\n/**\n * Caption after-active-word element - displays words after the current word.\n * Uses light DOM for simplicity - parent sets textContent directly.\n */\n@customElement(\"ef-captions-after-active-word\")\nexport class EFCaptionsAfterActiveWord extends EFCaptionsSegment {\n constructor() {\n super();\n // Override whiteSpace to preserve spacing\n this.style.whiteSpace = \"pre\";\n }\n \n set segmentText(text: string) {\n // Add leading space if there's text\n const finalText = text ? \" \" + text : text;\n \n // Hide element if no content or only stop words\n if (!finalText || stopWords.has(finalText)) {\n this.style.display = \"none\";\n this.textContent = \"\";\n } else {\n this.style.display = \"inline-block\";\n this.textContent = finalText;\n }\n }\n}\n\n@customElement(\"ef-captions\")\nexport class EFCaptions extends EFSourceMixin(\n EFTemporal(FetchMixin(LitElement)),\n { assetType: \"caption_files\" },\n) implements FrameRenderable {\n static styles = [\n css`\n :host {\n display: inline-flex;\n white-space: normal;\n line-height: 1;\n gap: 0;\n }\n ::slotted(*) {\n display: inline-block;\n margin: 0;\n padding: 0;\n }\n `,\n ];\n\n @property({ type: String, attribute: \"target\", reflect: true })\n targetSelector = \"\";\n\n set target(value: string) {\n this.targetSelector = value;\n }\n\n @property({ attribute: \"word-style\" })\n wordStyle = \"\";\n\n /**\n * URL or path to a JSON file containing custom captions data.\n * The JSON should conform to the Caption interface with 'segments' and 'word_segments' arrays.\n */\n @property({ type: String, attribute: \"captions-src\", reflect: true })\n captionsSrc = \"\";\n\n /**\n * Direct captions data object. Takes priority over captions-src and captions-script.\n * Should conform to the Caption interface with 'segments' and 'word_segments' arrays.\n */\n @property({ type: Object, attribute: false })\n captionsData: Caption | null = null;\n\n /**\n * ID of a <script> element containing JSON captions data.\n * The script's textContent should be valid JSON conforming to the Caption interface.\n */\n @property({ type: String, attribute: \"captions-script\", reflect: true })\n captionsScript = \"\";\n\n activeWordContainers = this.getElementsByTagName(\"ef-captions-active-word\");\n segmentContainers = this.getElementsByTagName(\"ef-captions-segment\");\n beforeActiveWordContainers = this.getElementsByTagName(\n \"ef-captions-before-active-word\",\n );\n afterActiveWordContainers = this.getElementsByTagName(\n \"ef-captions-after-active-word\",\n );\n\n // Cache for intrinsicDurationMs to avoid expensive O(n) recalculation every frame\n #cachedIntrinsicDurationMs: number | undefined | null = null; // null = not computed, undefined = no duration\n\n render() {\n return html`<slot></slot>`;\n }\n\n transcriptionsPath() {\n if (!this.targetElement) {\n return null;\n }\n if (this.targetElement.assetId) {\n return `${this.apiHost}/api/v1/isobmff_files/${this.targetElement.assetId}/transcription`;\n }\n return null;\n }\n\n captionsPath() {\n if (!this.targetElement) {\n return null;\n }\n if (this.targetElement.assetId) {\n return `${this.apiHost}/api/v1/caption_files/${this.targetElement.assetId}`;\n }\n const targetSrc = this.targetElement.src;\n // Normalize the path: remove leading slash and any double slashes\n let normalizedSrc = targetSrc.startsWith(\"/\")\n ? targetSrc.slice(1)\n : targetSrc;\n normalizedSrc = normalizedSrc.replace(/^\\/+/, \"\");\n // Use production API format for local files\n return `/api/v1/assets/local/captions?src=${encodeURIComponent(normalizedSrc)}`;\n }\n\n // ============================================================================\n // Captions Data Loading - async methods instead of Tasks\n // ============================================================================\n\n #captionsDataLoaded = false;\n #captionsDataPromise: Promise<Caption | null> | null = null;\n #captionsDataValue: Caption | null = null;\n #transcriptionData: GetISOBMFFFileTranscriptionResult | null = null;\n\n /**\n * AsyncValue wrapper for backwards compatibility\n */\n unifiedCaptionsDataTask = new AsyncValue<Caption | null>();\n\n /**\n * Load captions data from all possible sources\n */\n async loadCaptionsData(signal?: AbortSignal): Promise<Caption | null> {\n // Return cached if already loaded\n if (this.#captionsDataLoaded && this.#captionsDataValue) {\n return this.#captionsDataValue;\n }\n\n // Return in-flight promise\n if (this.#captionsDataPromise) {\n return this.#captionsDataPromise;\n }\n\n this.unifiedCaptionsDataTask.startPending();\n this.#captionsDataPromise = this.#doLoadCaptionsData(signal);\n\n try {\n this.#captionsDataValue = await this.#captionsDataPromise;\n this.#captionsDataLoaded = true;\n if (this.#captionsDataValue) {\n this.unifiedCaptionsDataTask.setValue(this.#captionsDataValue);\n }\n return this.#captionsDataValue;\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n console.error(\"Failed to load captions data:\", error);\n return null;\n } finally {\n this.#captionsDataPromise = null;\n }\n }\n\n async #doLoadCaptionsData(signal?: AbortSignal): Promise<Caption | null> {\n // Priority 1: Direct captionsData property\n if (this.captionsData) {\n return this.captionsData;\n }\n\n // Priority 2: Script element reference\n if (this.captionsScript) {\n const scriptElement = document.getElementById(this.captionsScript);\n if (scriptElement?.textContent) {\n try {\n return JSON.parse(scriptElement.textContent) as Caption;\n } catch (error) {\n console.error(`Failed to parse captions from script #${this.captionsScript}:`, error);\n }\n }\n }\n\n // Priority 3: External captions file\n if (this.captionsSrc) {\n try {\n const response = await this.fetch(this.captionsSrc, { signal });\n return await response.json() as Caption;\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n console.error(`Failed to load captions from ${this.captionsSrc}:`, error);\n }\n }\n\n // Priority 4: Transcription from target element\n if (this.targetElement && !this.hasCustomCaptionsData) {\n const transcriptionPath = this.transcriptionsPath();\n if (transcriptionPath) {\n try {\n const response = await this.fetch(transcriptionPath, { signal });\n this.#transcriptionData = await response.json() as GetISOBMFFFileTranscriptionResult;\n signal?.throwIfAborted();\n\n // Load fragment for current time\n if (this.#transcriptionData) {\n return this.#loadTranscriptionFragment(signal);\n }\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n // Transcription not available - not an error\n }\n }\n }\n\n return null;\n }\n\n async #loadTranscriptionFragment(signal?: AbortSignal): Promise<Caption | null> {\n if (!this.#transcriptionData) return null;\n\n const fragmentIndex = Math.floor(this.ownCurrentTimeMs / this.#transcriptionData.work_slice_ms);\n const fragmentPath = `${this.apiHost}/api/v1/transcriptions/${this.#transcriptionData.id}/fragments/${fragmentIndex}`;\n\n try {\n const response = await this.fetch(fragmentPath, { signal });\n return await response.json() as Caption;\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n console.error(\"Failed to load transcription fragment:\", error);\n return null;\n }\n }\n\n /**\n * @deprecated Use FrameRenderable methods (prepareFrame, renderFrame) via FrameController instead.\n * This is a compatibility wrapper that delegates to the new system.\n */\n frameTask = createFrameTaskWrapper(this);\n\n // ============================================================================\n // FrameRenderable Implementation\n // Centralized frame control - no Lit Tasks\n // ============================================================================\n\n /**\n * Query readiness state for a given time.\n * @implements FrameRenderable\n */\n getFrameState(_timeMs: number): FrameState {\n // Check if captions data is loaded\n const hasData = this.#captionsDataLoaded && this.#captionsDataValue !== null;\n\n return {\n needsPreparation: !hasData,\n isReady: hasData,\n priority: PRIORITY_CAPTIONS,\n };\n }\n\n /**\n * Async preparation - waits for captions data to load.\n * @implements FrameRenderable\n */\n async prepareFrame(_timeMs: number, signal: AbortSignal): Promise<void> {\n await this.loadCaptionsData(signal);\n signal.throwIfAborted();\n }\n\n /**\n * Synchronous render - updates caption text containers.\n * Sets textContent directly on child elements (light DOM).\n * @implements FrameRenderable\n */\n renderFrame(_timeMs: number): void {\n // Update text containers by setting properties\n // Child elements update their textContent directly (light DOM)\n this.updateTextContainers();\n }\n\n // ============================================================================\n // End FrameRenderable Implementation\n // ============================================================================\n\n #rootTimegroupUpdateController?: ReactiveController;\n\n connectedCallback() {\n super.connectedCallback();\n\n // Start loading captions data\n this.loadCaptionsData().catch(() => {});\n\n // Try to get target element safely\n const target = this.targetSelector\n ? document.getElementById(this.targetSelector)\n : null;\n if (target && (target instanceof EFAudio || target instanceof EFVideo)) {\n new CrossUpdateController(target, this);\n }\n // For standalone captions with custom data, ensure proper timeline sync\n else if (this.hasCustomCaptionsData && this.rootTimegroup) {\n new CrossUpdateController(this.rootTimegroup, this);\n }\n\n // Ensure captions update when root timegroup's currentTimeMs changes\n if (this.rootTimegroup) {\n this.#rootTimegroupUpdateController = {\n hostUpdated: () => {\n Promise.resolve().then(() => {\n this.updateTextContainers();\n });\n },\n hostDisconnected: () => {\n this.#rootTimegroupUpdateController = undefined;\n },\n };\n this.rootTimegroup.addController(this.#rootTimegroupUpdateController);\n }\n\n // Prevent display:none from being set on the parent caption element.\n // IMPORTANT: This only applies to the parent <ef-captions> element, NOT to\n // caption child elements (<ef-captions-segment>, <ef-captions-active-word>, etc.).\n // Child elements MUST respect display:none for proper temporal visibility\n // in video rendering. Video export relies on display:none to hide elements\n // outside their time range.\n const observer = new MutationObserver(() => {\n if (this.style.display === \"none\") {\n this.style.removeProperty(\"display\");\n this.style.opacity = \"0\";\n this.style.pointerEvents = \"none\";\n } else if (!this.style.display || this.style.display === \"\") {\n this.style.removeProperty(\"opacity\");\n this.style.removeProperty(\"pointer-events\");\n }\n });\n observer.observe(this, { attributes: true, attributeFilter: [\"style\"] });\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this.#rootTimegroupUpdateController && this.rootTimegroup) {\n this.rootTimegroup.removeController(this.#rootTimegroupUpdateController);\n this.#rootTimegroupUpdateController = undefined;\n }\n }\n\n protected updated(\n changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>,\n ): void {\n // Set up root timegroup controller if rootTimegroup is now available\n if (this.rootTimegroup && !this.#rootTimegroupUpdateController) {\n this.#rootTimegroupUpdateController = {\n hostUpdated: () => {\n Promise.resolve().then(() => {\n this.updateTextContainers();\n });\n },\n hostDisconnected: () => {\n this.#rootTimegroupUpdateController = undefined;\n },\n };\n this.rootTimegroup.addController(this.#rootTimegroupUpdateController);\n }\n\n // Clean up controller if rootTimegroup changed\n if (\n changedProperties.has(\"rootTimegroup\") &&\n this.#rootTimegroupUpdateController\n ) {\n const oldRootTimegroup = changedProperties.get(\"rootTimegroup\") as\n | EFTimegroup\n | undefined;\n if (oldRootTimegroup && oldRootTimegroup !== this.rootTimegroup) {\n oldRootTimegroup.removeController(this.#rootTimegroupUpdateController);\n this.#rootTimegroupUpdateController = undefined;\n }\n }\n\n this.updateTextContainers();\n\n // Force duration recalculation when custom captions data changes\n if (\n changedProperties.has(\"captionsData\") ||\n changedProperties.has(\"captionsSrc\") ||\n changedProperties.has(\"captionsScript\")\n ) {\n // Invalidate caches and reload\n this.#cachedIntrinsicDurationMs = null;\n this.#captionsDataLoaded = false;\n this.#captionsDataValue = null;\n this.loadCaptionsData().catch(() => {});\n\n this.requestUpdate(\"intrinsicDurationMs\");\n\n flushSequenceDurationCache();\n flushStartTimeMsCache();\n\n if (this.parentTimegroup) {\n this.parentTimegroup.requestUpdate(\"durationMs\");\n this.parentTimegroup.requestUpdate(\"currentTime\");\n }\n }\n\n // Update captions when timeline position changes\n if (changedProperties.has(\"ownCurrentTimeMs\")) {\n this.updateTextContainers();\n }\n }\n\n updateTextContainers() {\n const captionsData = this.#captionsDataValue;\n if (!captionsData) {\n return;\n }\n\n // For captions with custom data, try to use the video's source time\n let currentTimeMs = this.ownCurrentTimeMs;\n if (this.hasCustomCaptionsData && this.parentTimegroup) {\n const videoElement = Array.from(this.parentTimegroup.children).find(\n (child): child is EFVideo => child instanceof EFVideo,\n );\n if (videoElement) {\n const sourceInMs = videoElement.sourceInMs ?? 0;\n currentTimeMs = videoElement.currentSourceTimeMs - sourceInMs;\n currentTimeMs = Math.max(0, Math.min(currentTimeMs, this.durationMs));\n }\n }\n\n const currentTimeSec = currentTimeMs / 1000;\n\n // Find the current word from word_segments\n const currentWord = captionsData.word_segments.find(\n (word) => currentTimeSec >= word.start && currentTimeSec < word.end,\n );\n\n // Find the current segment\n const currentSegment = captionsData.segments.find(\n (segment) =>\n currentTimeSec >= segment.start && currentTimeSec < segment.end,\n );\n\n for (const wordContainer of this.activeWordContainers) {\n if (currentWord) {\n const wordIndex = captionsData.word_segments.findIndex(\n (w) =>\n w.start === currentWord.start &&\n w.end === currentWord.end &&\n w.text === currentWord.text,\n );\n wordContainer.wordIndex = wordIndex >= 0 ? wordIndex : 0;\n wordContainer.wordText = currentWord.text; // Sets textContent directly\n } else {\n wordContainer.wordText = \"\"; // Hides element\n }\n }\n\n for (const segmentContainer of this.segmentContainers) {\n if (currentSegment) {\n segmentContainer.segmentText = currentSegment.text; // Sets textContent directly\n } else {\n segmentContainer.segmentText = \"\"; // Hides element\n }\n }\n\n // Process context for both word and segment cases\n if (currentWord && currentSegment) {\n const segmentWords = captionsData.word_segments.filter(\n (word) =>\n word.start >= currentSegment.start && word.end <= currentSegment.end,\n );\n\n const currentWordIndex = segmentWords.findIndex(\n (word) =>\n word.start === currentWord.start && word.end === currentWord.end,\n );\n\n if (currentWordIndex !== -1) {\n const beforeWords = segmentWords\n .slice(0, currentWordIndex)\n .map((w) => w.text.trim())\n .join(\" \");\n\n const afterWords = segmentWords\n .slice(currentWordIndex + 1)\n .map((w) => w.text.trim())\n .join(\" \");\n\n for (const container of this.beforeActiveWordContainers) {\n container.segmentText = beforeWords; // Sets textContent directly\n }\n\n for (const container of this.afterActiveWordContainers) {\n container.segmentText = afterWords; // Sets textContent directly\n }\n }\n } else if (currentSegment) {\n const segmentWords = captionsData.word_segments.filter(\n (word) =>\n word.start >= currentSegment.start && word.end <= currentSegment.end,\n );\n\n const firstWord = segmentWords[0];\n const isBeforeFirstWord = firstWord && currentTimeSec < firstWord.start;\n\n if (isBeforeFirstWord) {\n const allWords = segmentWords.map((w) => w.text.trim()).join(\" \");\n\n for (const container of this.beforeActiveWordContainers) {\n container.segmentText = \"\"; // Hides element\n }\n\n for (const container of this.afterActiveWordContainers) {\n container.segmentText = allWords; // Sets textContent directly\n }\n } else {\n const allCompletedWords = segmentWords\n .map((w) => w.text.trim())\n .join(\" \");\n\n for (const container of this.beforeActiveWordContainers) {\n container.segmentText = allCompletedWords; // Sets textContent directly\n }\n\n for (const container of this.afterActiveWordContainers) {\n container.segmentText = \"\"; // Hides element\n }\n }\n } else {\n for (const container of this.beforeActiveWordContainers) {\n container.segmentText = \"\"; // Hides element\n }\n\n for (const container of this.afterActiveWordContainers) {\n container.segmentText = \"\"; // Hides element\n }\n }\n }\n\n get targetElement() {\n const target = document.getElementById(this.targetSelector ?? \"\");\n if (target instanceof EFAudio || target instanceof EFVideo) {\n return target;\n }\n if (this.hasCustomCaptionsData) {\n return null;\n }\n return null;\n }\n\n get hasCustomCaptionsData(): boolean {\n return !!(this.captionsData || this.captionsSrc || this.captionsScript);\n }\n\n get intrinsicDurationMs(): number | undefined {\n if (this.#cachedIntrinsicDurationMs !== null) {\n return this.#cachedIntrinsicDurationMs;\n }\n\n let captionsData: Caption | null = null;\n\n if (this.captionsData) {\n captionsData = this.captionsData;\n } else if (this.captionsScript) {\n const scriptElement = document.getElementById(this.captionsScript);\n if (scriptElement?.textContent) {\n try {\n captionsData = JSON.parse(scriptElement.textContent) as Caption;\n } catch {\n // Invalid JSON\n }\n }\n } else if (this.#captionsDataValue) {\n captionsData = this.#captionsDataValue;\n }\n\n if (!captionsData) {\n if (!this.captionsData && !this.captionsScript && !this.captionsSrc) {\n this.#cachedIntrinsicDurationMs = undefined;\n }\n return undefined;\n }\n\n let result: number;\n if (\n captionsData.segments.length === 0 &&\n captionsData.word_segments.length === 0\n ) {\n result = 0;\n } else {\n const maxSegmentEnd =\n captionsData.segments.length > 0\n ? captionsData.segments.reduce(\n (max, s) => (s.end > max ? s.end : max),\n 0,\n )\n : 0;\n const maxWordEnd =\n captionsData.word_segments.length > 0\n ? captionsData.word_segments.reduce(\n (max, w) => (w.end > max ? w.end : max),\n 0,\n )\n : 0;\n\n result = Math.max(maxSegmentEnd, maxWordEnd) * 1000;\n }\n\n this.#cachedIntrinsicDurationMs = result;\n return result;\n }\n\n get hasOwnDuration(): boolean {\n return !!(\n this.captionsData ||\n this.captionsScript ||\n this.#captionsDataValue\n );\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-captions\": EFCaptions;\n \"ef-captions-active-word\": EFCaptionsActiveWord;\n \"ef-captions-segment\": EFCaptionsSegment;\n \"ef-captions-before-active-word\": EFCaptionsBeforeActiveWord;\n \"ef-captions-after-active-word\": EFCaptionsAfterActiveWord;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EFCaptions.js","names":["EFCaptionsActiveWord","#wordText","#wordIndex","EFCaptionsSegment","#segmentText","EFCaptionsBeforeActiveWord","EFCaptionsAfterActiveWord","EFCaptions","#captionsDataLoaded","#captionsDataValue","#captionsDataPromise","#doLoadCaptionsData","#transcriptionData","#loadTranscriptionFragment","#rootTimegroupUpdateController","#cachedIntrinsicDurationMs","captionsData: Caption | null","result: number"],"sources":["../../src/elements/EFCaptions.ts"],"sourcesContent":["import { css, html, LitElement, type PropertyValueMap } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport type { ReactiveController } from \"lit\";\nimport type { GetISOBMFFFileTranscriptionResult } from \"../../../api/src/index.js\";\nimport {\n type FrameRenderable,\n type FrameState,\n createFrameTaskWrapper,\n PRIORITY_CAPTIONS,\n} from \"../preview/FrameController.js\";\nimport { AsyncValue } from \"./EFMedia.js\";\nimport { CrossUpdateController } from \"./CrossUpdateController.js\";\nimport { EFAudio } from \"./EFAudio.js\";\nimport { EFSourceMixin } from \"./EFSourceMixin.js\";\nimport { EFTemporal, flushStartTimeMsCache } from \"./EFTemporal.js\";\nimport {\n flushSequenceDurationCache,\n EFTimegroup,\n} from \"./EFTimegroup.js\";\nimport { EFVideo } from \"./EFVideo.js\";\nimport { FetchMixin } from \"./FetchMixin.js\";\n\nexport interface WordSegment {\n text: string;\n start: number;\n end: number;\n}\n\nexport interface Segment {\n start: number;\n end: number;\n text: string;\n}\n\nexport interface Caption {\n segments: Segment[];\n word_segments: WordSegment[];\n}\n\nconst stopWords = new Set([\"\", \".\", \"!\", \"?\", \",\"]);\n\n/**\n * Caption active word element - displays the currently spoken word.\n * Uses light DOM for simplicity - parent sets textContent directly.\n */\n@customElement(\"ef-captions-active-word\")\nexport class EFCaptionsActiveWord extends HTMLElement {\n #wordText = \"\";\n #wordIndex = 0;\n \n set wordText(text: string) {\n this.#wordText = text;\n // Hide element if no content or only stop words\n if (!text || stopWords.has(text)) {\n this.hidden = true;\n this.textContent = \"\";\n } else {\n this.hidden = false;\n // Add trailing space to maintain consistent spacing with surrounding words\n this.textContent = text + \" \";\n }\n }\n \n get wordText(): string {\n return this.#wordText;\n }\n \n set wordIndex(index: number) {\n this.#wordIndex = index;\n // Set deterministic --ef-word-seed value based on word index\n const seed = (index * 9007) % 233; // Prime numbers for better distribution\n const seedValue = seed / 233; // Normalize to 0-1 range\n this.style.setProperty(\"--ef-word-seed\", seedValue.toString());\n }\n \n get wordIndex(): number {\n return this.#wordIndex;\n }\n}\n\n/**\n * Caption segment element - displays a full caption segment.\n * Uses light DOM for simplicity - parent sets textContent directly.\n */\n@customElement(\"ef-captions-segment\")\nexport class EFCaptionsSegment extends HTMLElement {\n #segmentText = \"\";\n \n set segmentText(text: string) {\n this.#segmentText = text;\n // Hide element if no content or only stop words\n if (!text || stopWords.has(text)) {\n this.hidden = true;\n this.textContent = \"\";\n } else {\n this.hidden = false;\n this.textContent = text;\n }\n }\n \n get segmentText(): string {\n return this.#segmentText;\n }\n}\n\n/**\n * Caption before-active-word element - displays words before the current word.\n * Uses light DOM for simplicity - parent sets textContent directly.\n */\n@customElement(\"ef-captions-before-active-word\")\nexport class EFCaptionsBeforeActiveWord extends EFCaptionsSegment {\n set segmentText(text: string) {\n // Check if there's an active word by looking for sibling active word element\n const activeWord = this.closest(\"ef-captions\")?.querySelector(\n \"ef-captions-active-word\",\n ) as EFCaptionsActiveWord;\n const hasActiveWord = activeWord?.wordText;\n \n // Add trailing space if there's an active word coming after us\n const finalText = text && hasActiveWord ? text + \" \" : text;\n \n // Hide element if no content or only stop words\n if (!finalText || stopWords.has(finalText)) {\n this.hidden = true;\n this.textContent = \"\";\n } else {\n this.hidden = false;\n this.textContent = finalText;\n }\n }\n}\n\n/**\n * Caption after-active-word element - displays words after the current word.\n * Uses light DOM for simplicity - parent sets textContent directly.\n */\n@customElement(\"ef-captions-after-active-word\")\nexport class EFCaptionsAfterActiveWord extends EFCaptionsSegment {\n set segmentText(text: string) {\n // No leading space - active word will add trailing space\n const finalText = text;\n \n // Hide element if no content or only stop words\n if (!finalText || stopWords.has(finalText)) {\n this.hidden = true;\n this.textContent = \"\";\n } else {\n this.hidden = false;\n this.textContent = finalText;\n }\n }\n}\n\n@customElement(\"ef-captions\")\nexport class EFCaptions extends EFSourceMixin(\n EFTemporal(FetchMixin(LitElement)),\n { assetType: \"caption_files\" },\n) implements FrameRenderable {\n static styles = [\n css`\n :host {\n display: block;\n white-space: normal;\n line-height: 1;\n gap: 0;\n }\n ::slotted(*) {\n display: inline;\n margin: 0;\n padding: 0;\n }\n `,\n ];\n\n @property({ type: String, attribute: \"target\", reflect: true })\n targetSelector = \"\";\n\n set target(value: string) {\n this.targetSelector = value;\n }\n\n @property({ attribute: \"word-style\" })\n wordStyle = \"\";\n\n /**\n * URL or path to a JSON file containing custom captions data.\n * The JSON should conform to the Caption interface with 'segments' and 'word_segments' arrays.\n */\n @property({ type: String, attribute: \"captions-src\", reflect: true })\n captionsSrc = \"\";\n\n /**\n * Direct captions data object. Takes priority over captions-src and captions-script.\n * Should conform to the Caption interface with 'segments' and 'word_segments' arrays.\n */\n @property({ type: Object, attribute: false })\n captionsData: Caption | null = null;\n\n /**\n * ID of a <script> element containing JSON captions data.\n * The script's textContent should be valid JSON conforming to the Caption interface.\n */\n @property({ type: String, attribute: \"captions-script\", reflect: true })\n captionsScript = \"\";\n\n activeWordContainers = this.getElementsByTagName(\"ef-captions-active-word\");\n segmentContainers = this.getElementsByTagName(\"ef-captions-segment\");\n beforeActiveWordContainers = this.getElementsByTagName(\n \"ef-captions-before-active-word\",\n );\n afterActiveWordContainers = this.getElementsByTagName(\n \"ef-captions-after-active-word\",\n );\n\n // Cache for intrinsicDurationMs to avoid expensive O(n) recalculation every frame\n #cachedIntrinsicDurationMs: number | undefined | null = null; // null = not computed, undefined = no duration\n\n render() {\n return html`<slot></slot>`;\n }\n\n transcriptionsPath() {\n if (!this.targetElement) {\n return null;\n }\n if (this.targetElement.assetId) {\n return `${this.apiHost}/api/v1/isobmff_files/${this.targetElement.assetId}/transcription`;\n }\n return null;\n }\n\n captionsPath() {\n if (!this.targetElement) {\n return null;\n }\n if (this.targetElement.assetId) {\n return `${this.apiHost}/api/v1/caption_files/${this.targetElement.assetId}`;\n }\n const targetSrc = this.targetElement.src;\n // Normalize the path: remove leading slash and any double slashes\n let normalizedSrc = targetSrc.startsWith(\"/\")\n ? targetSrc.slice(1)\n : targetSrc;\n normalizedSrc = normalizedSrc.replace(/^\\/+/, \"\");\n // Use production API format for local files\n return `/api/v1/assets/local/captions?src=${encodeURIComponent(normalizedSrc)}`;\n }\n\n // ============================================================================\n // Captions Data Loading - async methods instead of Tasks\n // ============================================================================\n\n #captionsDataLoaded = false;\n #captionsDataPromise: Promise<Caption | null> | null = null;\n #captionsDataValue: Caption | null = null;\n #transcriptionData: GetISOBMFFFileTranscriptionResult | null = null;\n\n /**\n * AsyncValue wrapper for backwards compatibility\n */\n unifiedCaptionsDataTask = new AsyncValue<Caption | null>();\n\n /**\n * Load captions data from all possible sources\n */\n async loadCaptionsData(signal?: AbortSignal): Promise<Caption | null> {\n // Return cached if already loaded\n if (this.#captionsDataLoaded && this.#captionsDataValue) {\n return this.#captionsDataValue;\n }\n\n // Return in-flight promise\n if (this.#captionsDataPromise) {\n return this.#captionsDataPromise;\n }\n\n this.unifiedCaptionsDataTask.startPending();\n this.#captionsDataPromise = this.#doLoadCaptionsData(signal);\n\n try {\n this.#captionsDataValue = await this.#captionsDataPromise;\n this.#captionsDataLoaded = true;\n if (this.#captionsDataValue) {\n this.unifiedCaptionsDataTask.setValue(this.#captionsDataValue);\n }\n return this.#captionsDataValue;\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n console.error(\"Failed to load captions data:\", error);\n return null;\n } finally {\n this.#captionsDataPromise = null;\n }\n }\n\n async #doLoadCaptionsData(signal?: AbortSignal): Promise<Caption | null> {\n // Priority 1: Direct captionsData property\n if (this.captionsData) {\n return this.captionsData;\n }\n\n // Priority 2: Script element reference\n if (this.captionsScript) {\n const scriptElement = document.getElementById(this.captionsScript);\n if (scriptElement?.textContent) {\n try {\n return JSON.parse(scriptElement.textContent) as Caption;\n } catch (error) {\n console.error(`Failed to parse captions from script #${this.captionsScript}:`, error);\n }\n }\n }\n\n // Priority 3: External captions file\n if (this.captionsSrc) {\n try {\n const response = await this.fetch(this.captionsSrc, { signal });\n return await response.json() as Caption;\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n console.error(`Failed to load captions from ${this.captionsSrc}:`, error);\n }\n }\n\n // Priority 4: Transcription from target element\n if (this.targetElement && !this.hasCustomCaptionsData) {\n const transcriptionPath = this.transcriptionsPath();\n if (transcriptionPath) {\n try {\n const response = await this.fetch(transcriptionPath, { signal });\n this.#transcriptionData = await response.json() as GetISOBMFFFileTranscriptionResult;\n signal?.throwIfAborted();\n\n // Load fragment for current time\n if (this.#transcriptionData) {\n return this.#loadTranscriptionFragment(signal);\n }\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n // Transcription not available - not an error\n }\n }\n }\n\n return null;\n }\n\n async #loadTranscriptionFragment(signal?: AbortSignal): Promise<Caption | null> {\n if (!this.#transcriptionData) return null;\n\n const fragmentIndex = Math.floor(this.ownCurrentTimeMs / this.#transcriptionData.work_slice_ms);\n const fragmentPath = `${this.apiHost}/api/v1/transcriptions/${this.#transcriptionData.id}/fragments/${fragmentIndex}`;\n\n try {\n const response = await this.fetch(fragmentPath, { signal });\n return await response.json() as Caption;\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n console.error(\"Failed to load transcription fragment:\", error);\n return null;\n }\n }\n\n /**\n * @deprecated Use FrameRenderable methods (prepareFrame, renderFrame) via FrameController instead.\n * This is a compatibility wrapper that delegates to the new system.\n */\n frameTask = createFrameTaskWrapper(this);\n\n // ============================================================================\n // FrameRenderable Implementation\n // Centralized frame control - no Lit Tasks\n // ============================================================================\n\n /**\n * Query readiness state for a given time.\n * @implements FrameRenderable\n */\n getFrameState(_timeMs: number): FrameState {\n // Check if captions data is loaded\n const hasData = this.#captionsDataLoaded && this.#captionsDataValue !== null;\n\n return {\n needsPreparation: !hasData,\n isReady: hasData,\n priority: PRIORITY_CAPTIONS,\n };\n }\n\n /**\n * Async preparation - waits for captions data to load.\n * @implements FrameRenderable\n */\n async prepareFrame(_timeMs: number, signal: AbortSignal): Promise<void> {\n await this.loadCaptionsData(signal);\n signal.throwIfAborted();\n }\n\n /**\n * Synchronous render - updates caption text containers.\n * Sets textContent directly on child elements (light DOM).\n * @implements FrameRenderable\n */\n renderFrame(_timeMs: number): void {\n // Update text containers by setting properties\n // Child elements update their textContent directly (light DOM)\n this.updateTextContainers();\n }\n\n // ============================================================================\n // End FrameRenderable Implementation\n // ============================================================================\n\n #rootTimegroupUpdateController?: ReactiveController;\n\n connectedCallback() {\n super.connectedCallback();\n\n // Start loading captions data\n this.loadCaptionsData().catch(() => {});\n\n // Try to get target element safely\n const target = this.targetSelector\n ? document.getElementById(this.targetSelector)\n : null;\n if (target && (target instanceof EFAudio || target instanceof EFVideo)) {\n new CrossUpdateController(target, this);\n }\n // For standalone captions with custom data, ensure proper timeline sync\n else if (this.hasCustomCaptionsData && this.rootTimegroup) {\n new CrossUpdateController(this.rootTimegroup, this);\n }\n\n // Ensure captions update when root timegroup's currentTimeMs changes\n if (this.rootTimegroup) {\n this.#rootTimegroupUpdateController = {\n hostUpdated: () => {\n Promise.resolve().then(() => {\n this.updateTextContainers();\n });\n },\n hostDisconnected: () => {\n this.#rootTimegroupUpdateController = undefined;\n },\n };\n this.rootTimegroup.addController(this.#rootTimegroupUpdateController);\n }\n\n // Prevent display:none from being set on the parent caption element.\n // IMPORTANT: This only applies to the parent <ef-captions> element, NOT to\n // caption child elements (<ef-captions-segment>, <ef-captions-active-word>, etc.).\n // Child elements MUST respect display:none for proper temporal visibility\n // in video rendering. Video export relies on display:none to hide elements\n // outside their time range.\n const observer = new MutationObserver(() => {\n if (this.style.display === \"none\") {\n this.style.removeProperty(\"display\");\n this.style.opacity = \"0\";\n this.style.pointerEvents = \"none\";\n } else if (!this.style.display || this.style.display === \"\") {\n this.style.removeProperty(\"opacity\");\n this.style.removeProperty(\"pointer-events\");\n }\n });\n observer.observe(this, { attributes: true, attributeFilter: [\"style\"] });\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this.#rootTimegroupUpdateController && this.rootTimegroup) {\n this.rootTimegroup.removeController(this.#rootTimegroupUpdateController);\n this.#rootTimegroupUpdateController = undefined;\n }\n }\n\n protected updated(\n changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>,\n ): void {\n // Set up root timegroup controller if rootTimegroup is now available\n if (this.rootTimegroup && !this.#rootTimegroupUpdateController) {\n this.#rootTimegroupUpdateController = {\n hostUpdated: () => {\n Promise.resolve().then(() => {\n this.updateTextContainers();\n });\n },\n hostDisconnected: () => {\n this.#rootTimegroupUpdateController = undefined;\n },\n };\n this.rootTimegroup.addController(this.#rootTimegroupUpdateController);\n }\n\n // Clean up controller if rootTimegroup changed\n if (\n changedProperties.has(\"rootTimegroup\") &&\n this.#rootTimegroupUpdateController\n ) {\n const oldRootTimegroup = changedProperties.get(\"rootTimegroup\") as\n | EFTimegroup\n | undefined;\n if (oldRootTimegroup && oldRootTimegroup !== this.rootTimegroup) {\n oldRootTimegroup.removeController(this.#rootTimegroupUpdateController);\n this.#rootTimegroupUpdateController = undefined;\n }\n }\n\n this.updateTextContainers();\n\n // Force duration recalculation when custom captions data changes\n if (\n changedProperties.has(\"captionsData\") ||\n changedProperties.has(\"captionsSrc\") ||\n changedProperties.has(\"captionsScript\")\n ) {\n // Invalidate caches and reload\n this.#cachedIntrinsicDurationMs = null;\n this.#captionsDataLoaded = false;\n this.#captionsDataValue = null;\n this.loadCaptionsData().catch(() => {});\n\n this.requestUpdate(\"intrinsicDurationMs\");\n\n flushSequenceDurationCache();\n flushStartTimeMsCache();\n\n if (this.parentTimegroup) {\n this.parentTimegroup.requestUpdate(\"durationMs\");\n this.parentTimegroup.requestUpdate(\"currentTime\");\n }\n }\n\n // Update captions when timeline position changes\n if (changedProperties.has(\"ownCurrentTimeMs\")) {\n this.updateTextContainers();\n }\n }\n\n updateTextContainers() {\n const captionsData = this.#captionsDataValue;\n if (!captionsData) {\n return;\n }\n\n // For captions with custom data, try to use the video's source time\n let currentTimeMs = this.ownCurrentTimeMs;\n if (this.hasCustomCaptionsData && this.parentTimegroup) {\n const videoElement = Array.from(this.parentTimegroup.children).find(\n (child): child is EFVideo => child instanceof EFVideo,\n );\n if (videoElement) {\n const sourceInMs = videoElement.sourceInMs ?? 0;\n currentTimeMs = videoElement.currentSourceTimeMs - sourceInMs;\n currentTimeMs = Math.max(0, Math.min(currentTimeMs, this.durationMs));\n }\n }\n\n const currentTimeSec = currentTimeMs / 1000;\n\n // Find the current word from word_segments\n const currentWord = captionsData.word_segments.find(\n (word) => currentTimeSec >= word.start && currentTimeSec < word.end,\n );\n\n // Find the current segment\n const currentSegment = captionsData.segments.find(\n (segment) =>\n currentTimeSec >= segment.start && currentTimeSec < segment.end,\n );\n\n for (const wordContainer of this.activeWordContainers) {\n if (currentWord) {\n const wordIndex = captionsData.word_segments.findIndex(\n (w) =>\n w.start === currentWord.start &&\n w.end === currentWord.end &&\n w.text === currentWord.text,\n );\n wordContainer.wordIndex = wordIndex >= 0 ? wordIndex : 0;\n wordContainer.wordText = currentWord.text; // Sets textContent directly\n } else {\n wordContainer.wordText = \"\"; // Hides element\n }\n }\n\n for (const segmentContainer of this.segmentContainers) {\n if (currentSegment) {\n segmentContainer.segmentText = currentSegment.text; // Sets textContent directly\n } else {\n segmentContainer.segmentText = \"\"; // Hides element\n }\n }\n\n // Process context for both word and segment cases\n if (currentWord && currentSegment) {\n const segmentWords = captionsData.word_segments.filter(\n (word) =>\n word.start >= currentSegment.start && word.end <= currentSegment.end,\n );\n\n const currentWordIndex = segmentWords.findIndex(\n (word) =>\n word.start === currentWord.start && word.end === currentWord.end,\n );\n\n if (currentWordIndex !== -1) {\n const beforeWords = segmentWords\n .slice(0, currentWordIndex)\n .map((w) => w.text.trim())\n .join(\" \");\n\n const afterWords = segmentWords\n .slice(currentWordIndex + 1)\n .map((w) => w.text.trim())\n .join(\" \");\n\n for (const container of this.beforeActiveWordContainers) {\n container.segmentText = beforeWords; // Sets textContent directly\n }\n\n for (const container of this.afterActiveWordContainers) {\n container.segmentText = afterWords; // Sets textContent directly\n }\n }\n } else if (currentSegment) {\n const segmentWords = captionsData.word_segments.filter(\n (word) =>\n word.start >= currentSegment.start && word.end <= currentSegment.end,\n );\n\n const firstWord = segmentWords[0];\n const isBeforeFirstWord = firstWord && currentTimeSec < firstWord.start;\n\n if (isBeforeFirstWord) {\n const allWords = segmentWords.map((w) => w.text.trim()).join(\" \");\n\n for (const container of this.beforeActiveWordContainers) {\n container.segmentText = \"\"; // Hides element\n }\n\n for (const container of this.afterActiveWordContainers) {\n container.segmentText = allWords; // Sets textContent directly\n }\n } else {\n const allCompletedWords = segmentWords\n .map((w) => w.text.trim())\n .join(\" \");\n\n for (const container of this.beforeActiveWordContainers) {\n container.segmentText = allCompletedWords; // Sets textContent directly\n }\n\n for (const container of this.afterActiveWordContainers) {\n container.segmentText = \"\"; // Hides element\n }\n }\n } else {\n for (const container of this.beforeActiveWordContainers) {\n container.segmentText = \"\"; // Hides element\n }\n\n for (const container of this.afterActiveWordContainers) {\n container.segmentText = \"\"; // Hides element\n }\n }\n }\n\n get targetElement() {\n const target = document.getElementById(this.targetSelector ?? \"\");\n if (target instanceof EFAudio || target instanceof EFVideo) {\n return target;\n }\n if (this.hasCustomCaptionsData) {\n return null;\n }\n return null;\n }\n\n get hasCustomCaptionsData(): boolean {\n return !!(this.captionsData || this.captionsSrc || this.captionsScript);\n }\n\n get intrinsicDurationMs(): number | undefined {\n if (this.#cachedIntrinsicDurationMs !== null) {\n return this.#cachedIntrinsicDurationMs;\n }\n\n let captionsData: Caption | null = null;\n\n if (this.captionsData) {\n captionsData = this.captionsData;\n } else if (this.captionsScript) {\n const scriptElement = document.getElementById(this.captionsScript);\n if (scriptElement?.textContent) {\n try {\n captionsData = JSON.parse(scriptElement.textContent) as Caption;\n } catch {\n // Invalid JSON\n }\n }\n } else if (this.#captionsDataValue) {\n captionsData = this.#captionsDataValue;\n }\n\n if (!captionsData) {\n if (!this.captionsData && !this.captionsScript && !this.captionsSrc) {\n this.#cachedIntrinsicDurationMs = undefined;\n }\n return undefined;\n }\n\n let result: number;\n if (\n captionsData.segments.length === 0 &&\n captionsData.word_segments.length === 0\n ) {\n result = 0;\n } else {\n const maxSegmentEnd =\n captionsData.segments.length > 0\n ? captionsData.segments.reduce(\n (max, s) => (s.end > max ? s.end : max),\n 0,\n )\n : 0;\n const maxWordEnd =\n captionsData.word_segments.length > 0\n ? captionsData.word_segments.reduce(\n (max, w) => (w.end > max ? w.end : max),\n 0,\n )\n : 0;\n\n result = Math.max(maxSegmentEnd, maxWordEnd) * 1000;\n }\n\n this.#cachedIntrinsicDurationMs = result;\n return result;\n }\n\n get hasOwnDuration(): boolean {\n return !!(\n this.captionsData ||\n this.captionsScript ||\n this.#captionsDataValue\n );\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-captions\": EFCaptions;\n \"ef-captions-active-word\": EFCaptionsActiveWord;\n \"ef-captions-segment\": EFCaptionsSegment;\n \"ef-captions-before-active-word\": EFCaptionsBeforeActiveWord;\n \"ef-captions-after-active-word\": EFCaptionsAfterActiveWord;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as lit6 from "lit";
|
|
2
2
|
import { LitElement } from "lit";
|
|
3
|
-
import * as
|
|
3
|
+
import * as lit_html6 from "lit-html";
|
|
4
4
|
|
|
5
5
|
//#region src/elements/EFPanZoom.d.ts
|
|
6
6
|
interface PanZoomTransform {
|
|
@@ -9,7 +9,7 @@ interface PanZoomTransform {
|
|
|
9
9
|
scale: number;
|
|
10
10
|
}
|
|
11
11
|
declare class EFPanZoom extends LitElement {
|
|
12
|
-
static styles:
|
|
12
|
+
static styles: lit6.CSSResult[];
|
|
13
13
|
x: number;
|
|
14
14
|
y: number;
|
|
15
15
|
scale: number;
|
|
@@ -89,7 +89,7 @@ declare class EFPanZoom extends LitElement {
|
|
|
89
89
|
* @param padding - Padding factor (0-1), e.g., 0.1 = 10% padding on each side. Default: 0.05
|
|
90
90
|
*/
|
|
91
91
|
fitToContent(padding?: number): void;
|
|
92
|
-
render():
|
|
92
|
+
render(): lit_html6.TemplateResult<1>;
|
|
93
93
|
}
|
|
94
94
|
//#endregion
|
|
95
95
|
export { EFPanZoom, PanZoomTransform };
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { FrameRenderable, FrameState } from "../preview/FrameController.js";
|
|
2
2
|
import { ContextMixinInterface } from "../gui/ContextMixin.js";
|
|
3
|
-
import * as
|
|
3
|
+
import * as lit28 from "lit";
|
|
4
4
|
import { LitElement } from "lit";
|
|
5
|
-
import * as
|
|
5
|
+
import * as lit_html27 from "lit-html";
|
|
6
6
|
import * as lit_html_directives_ref3 from "lit-html/directives/ref";
|
|
7
7
|
|
|
8
8
|
//#region src/elements/EFSurface.d.ts
|
|
9
9
|
declare class EFSurface extends LitElement implements FrameRenderable {
|
|
10
10
|
#private;
|
|
11
|
-
static styles:
|
|
11
|
+
static styles: lit28.CSSResult[];
|
|
12
12
|
canvasRef: lit_html_directives_ref3.Ref<HTMLCanvasElement>;
|
|
13
13
|
targetElement: ContextMixinInterface | null;
|
|
14
14
|
target: string;
|
|
15
|
-
render():
|
|
15
|
+
render(): lit_html27.TemplateResult<1>;
|
|
16
16
|
get rootTimegroup(): any;
|
|
17
17
|
get currentTimeMs(): number;
|
|
18
18
|
get durationMs(): number;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { TemporalMixinInterface } from "./EFTemporal.js";
|
|
2
2
|
import { EFTextSegment } from "./EFTextSegment.js";
|
|
3
|
-
import * as
|
|
3
|
+
import * as lit8 from "lit";
|
|
4
4
|
import { LitElement, PropertyValueMap } from "lit";
|
|
5
|
-
import * as
|
|
5
|
+
import * as lit_html9 from "lit-html";
|
|
6
6
|
|
|
7
7
|
//#region src/elements/EFText.d.ts
|
|
8
8
|
type SplitMode = "line" | "word" | "char";
|
|
9
9
|
declare const EFText_base: (new (...args: any[]) => TemporalMixinInterface) & typeof LitElement;
|
|
10
10
|
declare class EFText extends EFText_base {
|
|
11
11
|
#private;
|
|
12
|
-
static styles:
|
|
12
|
+
static styles: lit8.CSSResult[];
|
|
13
13
|
split: SplitMode;
|
|
14
14
|
private validateSplit;
|
|
15
15
|
staggerMs?: number;
|
|
@@ -20,7 +20,7 @@ declare class EFText extends EFText_base {
|
|
|
20
20
|
private _textContent;
|
|
21
21
|
private _templateElement;
|
|
22
22
|
private _segmentsReadyResolvers;
|
|
23
|
-
render():
|
|
23
|
+
render(): lit_html9.TemplateResult<1>;
|
|
24
24
|
set textContent(value: string | null);
|
|
25
25
|
get textContent(): string;
|
|
26
26
|
/**
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { TemporalMixinInterface } from "./EFTemporal.js";
|
|
2
|
-
import * as
|
|
2
|
+
import * as lit9 from "lit";
|
|
3
3
|
import { LitElement } from "lit";
|
|
4
|
-
import * as
|
|
4
|
+
import * as lit_html10 from "lit-html";
|
|
5
5
|
|
|
6
6
|
//#region src/elements/EFTextSegment.d.ts
|
|
7
7
|
declare const EFTextSegment_base: (new (...args: any[]) => TemporalMixinInterface) & typeof LitElement;
|
|
8
8
|
declare class EFTextSegment extends EFTextSegment_base {
|
|
9
|
-
static styles:
|
|
9
|
+
static styles: lit9.CSSResult[];
|
|
10
10
|
/**
|
|
11
11
|
* Registers animation styles that should be shared across all text segments.
|
|
12
12
|
* This is the correct way to inject animation styles for segments - not via innerHTML.
|
|
@@ -32,7 +32,7 @@ declare class EFTextSegment extends EFTextSegment_base {
|
|
|
32
32
|
* @param id The identifier of the stylesheet to remove
|
|
33
33
|
*/
|
|
34
34
|
static unregisterAnimations(id: string): void;
|
|
35
|
-
render():
|
|
35
|
+
render(): lit_html10.TemplateResult<1>;
|
|
36
36
|
private setCSSVariables;
|
|
37
37
|
protected firstUpdated(): void;
|
|
38
38
|
protected updated(): void;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { EFVideo } from "./EFVideo.js";
|
|
2
2
|
import { EFTimegroup } from "./EFTimegroup.js";
|
|
3
|
-
import * as
|
|
3
|
+
import * as lit29 from "lit";
|
|
4
4
|
import { LitElement } from "lit";
|
|
5
|
-
import * as
|
|
5
|
+
import * as lit_html28 from "lit-html";
|
|
6
6
|
import * as lit_html_directives_ref4 from "lit-html/directives/ref";
|
|
7
7
|
|
|
8
8
|
//#region src/elements/EFThumbnailStrip.d.ts
|
|
9
9
|
declare class EFThumbnailStrip extends LitElement {
|
|
10
|
-
static styles:
|
|
10
|
+
static styles: lit29.CSSResult[];
|
|
11
11
|
canvasRef: lit_html_directives_ref4.Ref<HTMLCanvasElement>;
|
|
12
12
|
target: string;
|
|
13
13
|
thumbnailWidth: number;
|
|
@@ -155,7 +155,7 @@ declare class EFThumbnailStrip extends LitElement {
|
|
|
155
155
|
* Invalidate all cached thumbnails for this element.
|
|
156
156
|
*/
|
|
157
157
|
invalidateAll(): void;
|
|
158
|
-
render():
|
|
158
|
+
render(): lit_html28.TemplateResult<1>;
|
|
159
159
|
}
|
|
160
160
|
declare global {
|
|
161
161
|
interface HTMLElementTagNameMap {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { FrameRenderable, FrameState, FrameTask } from "../preview/FrameController.js";
|
|
2
2
|
import { EFFramegen } from "../EF_FRAMEGEN.js";
|
|
3
3
|
import { EFMedia } from "./EFMedia.js";
|
|
4
|
-
import * as
|
|
4
|
+
import * as lit7 from "lit";
|
|
5
5
|
import { PropertyValueMap } from "lit";
|
|
6
|
-
import * as
|
|
7
|
-
import * as
|
|
6
|
+
import * as lit_html7 from "lit-html";
|
|
7
|
+
import * as lit_html_directives_ref2 from "lit-html/directives/ref";
|
|
8
8
|
|
|
9
9
|
//#region src/elements/EFVideo.d.ts
|
|
10
10
|
declare global {
|
|
@@ -23,8 +23,8 @@ interface LoadingState {
|
|
|
23
23
|
declare const EFVideo_base: typeof EFMedia;
|
|
24
24
|
declare class EFVideo extends EFVideo_base implements FrameRenderable {
|
|
25
25
|
#private;
|
|
26
|
-
static styles:
|
|
27
|
-
canvasRef:
|
|
26
|
+
static styles: lit7.CSSResult[];
|
|
27
|
+
canvasRef: lit_html_directives_ref2.Ref<HTMLCanvasElement>;
|
|
28
28
|
/**
|
|
29
29
|
* Duration in milliseconds for video buffering ahead of current time
|
|
30
30
|
* @domAttribute "video-buffer-duration"
|
|
@@ -76,7 +76,7 @@ declare class EFVideo extends EFVideo_base implements FrameRenderable {
|
|
|
76
76
|
};
|
|
77
77
|
constructor();
|
|
78
78
|
protected updated(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
|
|
79
|
-
render():
|
|
79
|
+
render(): lit_html7.TemplateResult<1>;
|
|
80
80
|
get canvasElement(): HTMLCanvasElement | undefined;
|
|
81
81
|
/**
|
|
82
82
|
* @deprecated Use FrameRenderable methods (prepareFrame, renderFrame) via FrameController instead.
|
|
@@ -3,16 +3,16 @@ import { TemporalMixinInterface } from "./EFTemporal.js";
|
|
|
3
3
|
import { EFVideo } from "./EFVideo.js";
|
|
4
4
|
import { EFAudio } from "./EFAudio.js";
|
|
5
5
|
import { TargetController } from "./TargetController.js";
|
|
6
|
-
import * as
|
|
6
|
+
import * as lit10 from "lit";
|
|
7
7
|
import { LitElement, PropertyValueMap } from "lit";
|
|
8
8
|
import { Ref } from "lit/directives/ref.js";
|
|
9
|
-
import * as
|
|
9
|
+
import * as lit_html11 from "lit-html";
|
|
10
10
|
|
|
11
11
|
//#region src/elements/EFWaveform.d.ts
|
|
12
12
|
declare const EFWaveform_base: (new (...args: any[]) => TemporalMixinInterface) & typeof LitElement;
|
|
13
13
|
declare class EFWaveform extends EFWaveform_base implements FrameRenderable {
|
|
14
14
|
#private;
|
|
15
|
-
static styles:
|
|
15
|
+
static styles: lit10.CSSResult;
|
|
16
16
|
canvasRef: Ref<HTMLCanvasElement>;
|
|
17
17
|
private ctx;
|
|
18
18
|
private styleObserver;
|
|
@@ -24,7 +24,7 @@ declare class EFWaveform extends EFWaveform_base implements FrameRenderable {
|
|
|
24
24
|
* @public
|
|
25
25
|
*/
|
|
26
26
|
get renderVersion(): number;
|
|
27
|
-
render():
|
|
27
|
+
render(): lit_html11.TemplateResult<1>;
|
|
28
28
|
mode: "roundBars" | "bars" | "bricks" | "line" | "curve" | "pixel" | "wave" | "spikes";
|
|
29
29
|
color: string;
|
|
30
30
|
target: string;
|