@editframe/create 0.44.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +16 -28
- package/dist/index.js.map +1 -1
- package/dist/skills/editframe-brand-video-generator/README.md +155 -0
- package/dist/skills/editframe-brand-video-generator/SKILL.md +207 -0
- package/dist/skills/editframe-brand-video-generator/references/brand-examples.md +178 -0
- package/dist/skills/editframe-brand-video-generator/references/color-psychology.md +227 -0
- package/dist/skills/editframe-brand-video-generator/references/composition-patterns.md +383 -0
- package/dist/skills/editframe-brand-video-generator/references/editing.md +66 -0
- package/dist/skills/editframe-brand-video-generator/references/emotional-arcs.md +496 -0
- package/dist/skills/editframe-brand-video-generator/references/genre-selection.md +135 -0
- package/dist/skills/editframe-brand-video-generator/references/transition-styles.md +611 -0
- package/dist/skills/editframe-brand-video-generator/references/typography-personalities.md +326 -0
- package/dist/skills/editframe-brand-video-generator/references/video-archetypes.md +86 -0
- package/dist/skills/editframe-brand-video-generator/references/video-fundamentals.md +169 -0
- package/dist/skills/editframe-brand-video-generator/references/visual-metaphors.md +50 -0
- package/dist/skills/editframe-composition/SKILL.md +169 -0
- package/dist/skills/editframe-composition/references/audio.md +483 -0
- package/dist/skills/editframe-composition/references/captions.md +844 -0
- package/dist/skills/editframe-composition/references/composition-model.md +73 -0
- package/dist/skills/editframe-composition/references/configuration.md +403 -0
- package/dist/skills/editframe-composition/references/css-parts.md +105 -0
- package/dist/skills/editframe-composition/references/css-variables.md +640 -0
- package/dist/skills/editframe-composition/references/entry-points.md +810 -0
- package/dist/skills/editframe-composition/references/events.md +499 -0
- package/dist/skills/editframe-composition/references/getting-started.md +259 -0
- package/dist/skills/editframe-composition/references/hooks.md +234 -0
- package/dist/skills/editframe-composition/references/image.md +241 -0
- package/dist/skills/editframe-composition/references/r3f.md +580 -0
- package/dist/skills/editframe-composition/references/render-api.md +484 -0
- package/dist/skills/editframe-composition/references/render-strategies.md +119 -0
- package/dist/skills/editframe-composition/references/render-to-video.md +1101 -0
- package/dist/skills/editframe-composition/references/scripting.md +606 -0
- package/dist/skills/editframe-composition/references/sequencing.md +116 -0
- package/dist/skills/editframe-composition/references/server-rendering.md +753 -0
- package/dist/skills/editframe-composition/references/surface.md +329 -0
- package/dist/skills/editframe-composition/references/text.md +627 -0
- package/dist/skills/editframe-composition/references/time-model.md +99 -0
- package/dist/skills/editframe-composition/references/timegroup-modes.md +102 -0
- package/dist/skills/editframe-composition/references/timegroup.md +457 -0
- package/dist/skills/editframe-composition/references/timeline-root.md +398 -0
- package/dist/skills/editframe-composition/references/transcription.md +47 -0
- package/dist/skills/editframe-composition/references/transitions.md +608 -0
- package/dist/skills/editframe-composition/references/use-media-info.md +357 -0
- package/dist/skills/editframe-composition/references/video.md +506 -0
- package/dist/skills/editframe-composition/references/waveform.md +327 -0
- package/dist/skills/editframe-editor-gui/SKILL.md +152 -0
- package/dist/skills/editframe-editor-gui/references/active-root-temporal.md +657 -0
- package/dist/skills/editframe-editor-gui/references/canvas.md +947 -0
- package/dist/skills/editframe-editor-gui/references/controls.md +366 -0
- package/dist/skills/editframe-editor-gui/references/dial.md +756 -0
- package/dist/skills/editframe-editor-gui/references/editor-toolkit.md +587 -0
- package/dist/skills/editframe-editor-gui/references/filmstrip.md +460 -0
- package/dist/skills/editframe-editor-gui/references/fit-scale.md +772 -0
- package/dist/skills/editframe-editor-gui/references/focus-overlay.md +561 -0
- package/dist/skills/editframe-editor-gui/references/hierarchy.md +544 -0
- package/dist/skills/editframe-editor-gui/references/overlay-item.md +634 -0
- package/dist/skills/editframe-editor-gui/references/overlay-layer.md +429 -0
- package/dist/skills/editframe-editor-gui/references/pan-zoom.md +568 -0
- package/dist/skills/editframe-editor-gui/references/pause.md +397 -0
- package/dist/skills/editframe-editor-gui/references/play.md +370 -0
- package/dist/skills/editframe-editor-gui/references/preview.md +391 -0
- package/dist/skills/editframe-editor-gui/references/resizable-box.md +749 -0
- package/dist/skills/editframe-editor-gui/references/scrubber.md +588 -0
- package/dist/skills/editframe-editor-gui/references/thumbnail-strip.md +566 -0
- package/dist/skills/editframe-editor-gui/references/time-display.md +492 -0
- package/dist/skills/editframe-editor-gui/references/timeline-ruler.md +489 -0
- package/dist/skills/editframe-editor-gui/references/timeline.md +604 -0
- package/dist/skills/editframe-editor-gui/references/toggle-loop.md +618 -0
- package/dist/skills/editframe-editor-gui/references/toggle-play.md +526 -0
- package/dist/skills/editframe-editor-gui/references/transform-handles.md +924 -0
- package/dist/skills/editframe-editor-gui/references/trim-handles.md +725 -0
- package/dist/skills/editframe-editor-gui/references/workbench.md +453 -0
- package/dist/skills/editframe-motion-design/SKILL.md +101 -0
- package/dist/skills/editframe-motion-design/references/0-editframe.md +299 -0
- package/dist/skills/editframe-motion-design/references/1-intent.md +201 -0
- package/dist/skills/editframe-motion-design/references/2-physics-model.md +405 -0
- package/dist/skills/editframe-motion-design/references/3-attention.md +350 -0
- package/dist/skills/editframe-motion-design/references/4-process.md +418 -0
- package/dist/skills/editframe-vite-plugin/SKILL.md +75 -0
- package/dist/skills/editframe-vite-plugin/references/file-api.md +111 -0
- package/dist/skills/editframe-vite-plugin/references/getting-started.md +96 -0
- package/dist/skills/editframe-vite-plugin/references/jit-transcoding.md +91 -0
- package/dist/skills/editframe-vite-plugin/references/local-assets.md +75 -0
- package/dist/skills/editframe-vite-plugin/references/visual-testing.md +136 -0
- package/dist/skills/editframe-webhooks/SKILL.md +126 -0
- package/dist/skills/editframe-webhooks/references/events.md +382 -0
- package/dist/skills/editframe-webhooks/references/getting-started.md +232 -0
- package/dist/skills/editframe-webhooks/references/security.md +418 -0
- package/dist/skills/editframe-webhooks/references/testing.md +409 -0
- package/dist/skills/editframe-webhooks/references/troubleshooting.md +457 -0
- package/dist/templates/html/AGENTS.md +13 -0
- package/dist/templates/react/AGENTS.md +13 -0
- package/dist/utils.js +15 -16
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/tsdown.config.ts +4 -0
- package/dist/detectAgent.js +0 -89
- package/dist/detectAgent.js.map +0 -1
|
@@ -0,0 +1,844 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Captions Element
|
|
3
|
+
description: Synchronized caption display with word-level highlighting, custom styling via CSS parts, and WebVTT/JSON caption file support.
|
|
4
|
+
type: reference
|
|
5
|
+
nav:
|
|
6
|
+
parent: "Media"
|
|
7
|
+
priority: 30
|
|
8
|
+
related: ["text", "transcription"]
|
|
9
|
+
api:
|
|
10
|
+
attributes:
|
|
11
|
+
- name: target
|
|
12
|
+
type: string
|
|
13
|
+
description: Selector for ef-video or ef-audio element
|
|
14
|
+
- name: captions-script
|
|
15
|
+
type: string
|
|
16
|
+
description: ID of script element with JSON captions
|
|
17
|
+
- name: captions-src
|
|
18
|
+
type: string
|
|
19
|
+
description: URL to JSON captions file
|
|
20
|
+
- name: captions-data
|
|
21
|
+
type: object
|
|
22
|
+
description: Direct captions data object
|
|
23
|
+
sub_elements:
|
|
24
|
+
- tag: ef-captions-active-word
|
|
25
|
+
description: Currently spoken word
|
|
26
|
+
attributes:
|
|
27
|
+
- name: wordText
|
|
28
|
+
type: string
|
|
29
|
+
description: Text of active word
|
|
30
|
+
- name: wordIndex
|
|
31
|
+
type: number
|
|
32
|
+
description: Index of active word
|
|
33
|
+
css_variables:
|
|
34
|
+
- name: --ef-word-seed
|
|
35
|
+
type: number
|
|
36
|
+
description: Deterministic random (0-1) per word for animations
|
|
37
|
+
- tag: ef-captions-before-active-word
|
|
38
|
+
description: Words already spoken in current segment
|
|
39
|
+
attributes:
|
|
40
|
+
- name: segmentText
|
|
41
|
+
type: string
|
|
42
|
+
description: Text of caption segment
|
|
43
|
+
- tag: ef-captions-after-active-word
|
|
44
|
+
description: Words not yet spoken in current segment
|
|
45
|
+
attributes:
|
|
46
|
+
- name: segmentText
|
|
47
|
+
type: string
|
|
48
|
+
description: Text of caption segment
|
|
49
|
+
- tag: ef-captions-segment
|
|
50
|
+
description: Full caption segment text
|
|
51
|
+
attributes:
|
|
52
|
+
- name: segmentText
|
|
53
|
+
type: string
|
|
54
|
+
description: Text of caption segment
|
|
55
|
+
react:
|
|
56
|
+
generate: true
|
|
57
|
+
componentName: Captions
|
|
58
|
+
importPath: "@editframe/react"
|
|
59
|
+
propMapping:
|
|
60
|
+
captions-script: captionsScript
|
|
61
|
+
captions-src: src
|
|
62
|
+
captions-data: captionsData
|
|
63
|
+
additionalProps:
|
|
64
|
+
- name: className
|
|
65
|
+
type: string
|
|
66
|
+
description: CSS classes for styling
|
|
67
|
+
- name: children
|
|
68
|
+
type: ReactNode
|
|
69
|
+
description: Caption segment and word components
|
|
70
|
+
nav:
|
|
71
|
+
parent: "Components / Text & Graphics"
|
|
72
|
+
priority: 31
|
|
73
|
+
related: ["text"]
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
<!-- html-only -->
|
|
77
|
+
# ef-captions
|
|
78
|
+
<!-- /html-only -->
|
|
79
|
+
<!-- react-only -->
|
|
80
|
+
# Captions
|
|
81
|
+
<!-- /react-only -->
|
|
82
|
+
|
|
83
|
+
Synchronized captions with word highlighting.
|
|
84
|
+
|
|
85
|
+
<!-- react-only -->
|
|
86
|
+
## Import
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import {
|
|
90
|
+
Captions,
|
|
91
|
+
CaptionsSegment,
|
|
92
|
+
CaptionsActiveWord,
|
|
93
|
+
CaptionsBeforeActiveWord,
|
|
94
|
+
CaptionsAfterActiveWord
|
|
95
|
+
} from "@editframe/react";
|
|
96
|
+
```
|
|
97
|
+
<!-- /react-only -->
|
|
98
|
+
|
|
99
|
+
## Basic Usage
|
|
100
|
+
|
|
101
|
+
<!-- html-only -->
|
|
102
|
+
```html live
|
|
103
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black">
|
|
104
|
+
<ef-video id="my-video" src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain"></ef-video>
|
|
105
|
+
|
|
106
|
+
<ef-captions captions-script="captions-data" class="absolute bottom-8 left-4 right-4 text-center">
|
|
107
|
+
<ef-captions-before-active-word class="text-white/60 text-xl"></ef-captions-before-active-word>
|
|
108
|
+
<ef-captions-active-word class="text-yellow-300 text-xl font-bold"></ef-captions-active-word>
|
|
109
|
+
<ef-captions-after-active-word class="text-white/40 text-xl"></ef-captions-after-active-word>
|
|
110
|
+
</ef-captions>
|
|
111
|
+
|
|
112
|
+
<script type="application/json" id="captions-data">
|
|
113
|
+
{
|
|
114
|
+
"segments": [
|
|
115
|
+
{ "start": 0, "end": 3, "text": "Welcome to the demo." },
|
|
116
|
+
{ "start": 3, "end": 6, "text": "This shows captions." }
|
|
117
|
+
],
|
|
118
|
+
"word_segments": [
|
|
119
|
+
{ "start": 0, "end": 0.5, "text": "Welcome" },
|
|
120
|
+
{ "start": 0.5, "end": 0.7, "text": "to" },
|
|
121
|
+
{ "start": 0.7, "end": 0.9, "text": "the" },
|
|
122
|
+
{ "start": 0.9, "end": 1.4, "text": "demo." },
|
|
123
|
+
{ "start": 3.0, "end": 3.4, "text": "This" },
|
|
124
|
+
{ "start": 3.4, "end": 3.8, "text": "shows" },
|
|
125
|
+
{ "start": 3.8, "end": 4.5, "text": "captions." }
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
</script>
|
|
129
|
+
</ef-timegroup>
|
|
130
|
+
```
|
|
131
|
+
<!-- /html-only -->
|
|
132
|
+
<!-- react-only -->
|
|
133
|
+
```tsx
|
|
134
|
+
<Captions
|
|
135
|
+
src="/assets/captions.json"
|
|
136
|
+
className="absolute bottom-8 text-white text-2xl text-center w-full"
|
|
137
|
+
/>
|
|
138
|
+
```
|
|
139
|
+
<!-- /react-only -->
|
|
140
|
+
|
|
141
|
+
## Caption Data Format
|
|
142
|
+
|
|
143
|
+
<!-- html-only -->
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"segments": [
|
|
147
|
+
{ "start": 0, "end": 3, "text": "Sentence one." }
|
|
148
|
+
],
|
|
149
|
+
"word_segments": [
|
|
150
|
+
{ "start": 0, "end": 0.5, "text": "Sentence" },
|
|
151
|
+
{ "start": 0.5, "end": 1.0, "text": "one." }
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Times are in seconds relative to the parent timegroup.
|
|
157
|
+
<!-- /html-only -->
|
|
158
|
+
<!-- react-only -->
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"segments": [
|
|
162
|
+
{
|
|
163
|
+
"text": "Hello world",
|
|
164
|
+
"start": 0,
|
|
165
|
+
"end": 2000,
|
|
166
|
+
"words": [
|
|
167
|
+
{ "word": "Hello", "start": 0, "end": 500 },
|
|
168
|
+
{ "word": "world", "start": 500, "end": 2000 }
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
<!-- /react-only -->
|
|
175
|
+
|
|
176
|
+
<!-- react-only -->
|
|
177
|
+
## With Video
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
import { Timegroup, Video, Captions } from "@editframe/react";
|
|
181
|
+
|
|
182
|
+
<Timegroup mode="contain" className="absolute w-full h-full">
|
|
183
|
+
<Video src="/assets/video.mp4" className="size-full object-cover" />
|
|
184
|
+
<Captions
|
|
185
|
+
src="/assets/captions.json"
|
|
186
|
+
className="absolute bottom-16 text-white text-3xl text-center w-full px-8"
|
|
187
|
+
/>
|
|
188
|
+
</Timegroup>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Styled Segments
|
|
192
|
+
|
|
193
|
+
Use caption components for custom styling:
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
<Captions src="/assets/captions.json" className="absolute bottom-8 w-full text-center">
|
|
197
|
+
<CaptionsSegment className="text-2xl">
|
|
198
|
+
<CaptionsBeforeActiveWord className="text-gray-400" />
|
|
199
|
+
<CaptionsActiveWord className="text-white font-bold bg-blue-500 px-1" />
|
|
200
|
+
<CaptionsAfterActiveWord className="text-gray-400" />
|
|
201
|
+
</CaptionsSegment>
|
|
202
|
+
</Captions>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Word Highlighting
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
<Captions src="/assets/captions.json" className="absolute bottom-12 w-full text-center">
|
|
209
|
+
<CaptionsSegment className="text-3xl px-4">
|
|
210
|
+
<CaptionsBeforeActiveWord className="opacity-50" />
|
|
211
|
+
<CaptionsActiveWord className="text-yellow-400 font-bold scale-110 inline-block" />
|
|
212
|
+
<CaptionsAfterActiveWord className="opacity-50" />
|
|
213
|
+
</CaptionsSegment>
|
|
214
|
+
</Captions>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Background Box
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
<Captions
|
|
221
|
+
src="/assets/captions.json"
|
|
222
|
+
className="absolute bottom-8 left-1/2 -translate-x-1/2 bg-black/80 px-6 py-3 rounded-lg text-white text-2xl max-w-[800px]"
|
|
223
|
+
/>
|
|
224
|
+
```
|
|
225
|
+
<!-- /react-only -->
|
|
226
|
+
|
|
227
|
+
<!-- html-only -->
|
|
228
|
+
## Active Word Styling
|
|
229
|
+
|
|
230
|
+
### Color Highlighting
|
|
231
|
+
|
|
232
|
+
```html live
|
|
233
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-gradient-to-br from-purple-900 to-indigo-900">
|
|
234
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain opacity-70"></ef-video>
|
|
235
|
+
|
|
236
|
+
<ef-captions captions-script="color-captions" class="absolute bottom-16 left-0 right-0 text-center">
|
|
237
|
+
<ef-captions-before-active-word class="text-white/50 text-2xl"></ef-captions-before-active-word>
|
|
238
|
+
<ef-captions-active-word class="text-cyan-400 text-3xl font-black drop-shadow-lg"></ef-captions-active-word>
|
|
239
|
+
<ef-captions-after-active-word class="text-white/30 text-2xl"></ef-captions-after-active-word>
|
|
240
|
+
</ef-captions>
|
|
241
|
+
|
|
242
|
+
<script type="application/json" id="color-captions">
|
|
243
|
+
{
|
|
244
|
+
"segments": [
|
|
245
|
+
{ "start": 0, "end": 4, "text": "Bold cyan highlights active words." }
|
|
246
|
+
],
|
|
247
|
+
"word_segments": [
|
|
248
|
+
{ "start": 0, "end": 0.6, "text": "Bold" },
|
|
249
|
+
{ "start": 0.6, "end": 1.1, "text": "cyan" },
|
|
250
|
+
{ "start": 1.1, "end": 1.8, "text": "highlights" },
|
|
251
|
+
{ "start": 1.8, "end": 2.3, "text": "active" },
|
|
252
|
+
{ "start": 2.3, "end": 3.0, "text": "words." }
|
|
253
|
+
]
|
|
254
|
+
}
|
|
255
|
+
</script>
|
|
256
|
+
</ef-timegroup>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Background Box Style
|
|
260
|
+
|
|
261
|
+
```html live
|
|
262
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black">
|
|
263
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain"></ef-video>
|
|
264
|
+
|
|
265
|
+
<ef-captions captions-script="box-captions" class="absolute bottom-20 left-0 right-0 text-center leading-relaxed">
|
|
266
|
+
<ef-captions-before-active-word class="text-white text-2xl"></ef-captions-before-active-word>
|
|
267
|
+
<ef-captions-active-word class="text-white text-2xl bg-red-600 px-3 py-1 rounded-lg shadow-xl"></ef-captions-active-word>
|
|
268
|
+
<ef-captions-after-active-word class="text-white/40 text-2xl"></ef-captions-after-active-word>
|
|
269
|
+
</ef-captions>
|
|
270
|
+
|
|
271
|
+
<script type="application/json" id="box-captions">
|
|
272
|
+
{
|
|
273
|
+
"segments": [
|
|
274
|
+
{ "start": 0, "end": 3.5, "text": "Box highlights each word." }
|
|
275
|
+
],
|
|
276
|
+
"word_segments": [
|
|
277
|
+
{ "start": 0, "end": 0.5, "text": "Box" },
|
|
278
|
+
{ "start": 0.5, "end": 1.2, "text": "highlights" },
|
|
279
|
+
{ "start": 1.2, "end": 1.6, "text": "each" },
|
|
280
|
+
{ "start": 1.6, "end": 2.2, "text": "word." }
|
|
281
|
+
]
|
|
282
|
+
}
|
|
283
|
+
</script>
|
|
284
|
+
</ef-timegroup>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Underline and Shadow
|
|
288
|
+
|
|
289
|
+
```html live
|
|
290
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-gradient-to-t from-gray-900 to-gray-700">
|
|
291
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain opacity-60"></ef-video>
|
|
292
|
+
|
|
293
|
+
<ef-captions captions-script="underline-captions" class="absolute bottom-16 left-0 right-0 text-center">
|
|
294
|
+
<ef-captions-before-active-word class="text-gray-400 text-xl"></ef-captions-before-active-word>
|
|
295
|
+
<ef-captions-active-word class="text-white text-2xl font-bold underline decoration-yellow-400 decoration-4 underline-offset-4" style="text-shadow: 2px 2px 8px rgba(0,0,0,0.8);"></ef-captions-active-word>
|
|
296
|
+
<ef-captions-after-active-word class="text-gray-500 text-xl"></ef-captions-after-active-word>
|
|
297
|
+
</ef-captions>
|
|
298
|
+
|
|
299
|
+
<script type="application/json" id="underline-captions">
|
|
300
|
+
{
|
|
301
|
+
"segments": [
|
|
302
|
+
{ "start": 0, "end": 3.5, "text": "Underline with shadow effect." }
|
|
303
|
+
],
|
|
304
|
+
"word_segments": [
|
|
305
|
+
{ "start": 0, "end": 0.6, "text": "Underline" },
|
|
306
|
+
{ "start": 0.6, "end": 1.0, "text": "with" },
|
|
307
|
+
{ "start": 1.0, "end": 1.5, "text": "shadow" },
|
|
308
|
+
{ "start": 1.5, "end": 2.2, "text": "effect." }
|
|
309
|
+
]
|
|
310
|
+
}
|
|
311
|
+
</script>
|
|
312
|
+
</ef-timegroup>
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Animated Pop Effect
|
|
316
|
+
|
|
317
|
+
```html live
|
|
318
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black">
|
|
319
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain"></ef-video>
|
|
320
|
+
|
|
321
|
+
<ef-captions captions-script="pop-captions" class="absolute bottom-16 left-0 right-0 text-center">
|
|
322
|
+
<ef-captions-before-active-word class="text-white/60 text-xl"></ef-captions-before-active-word>
|
|
323
|
+
<ef-captions-active-word class="pop-word text-yellow-300 text-2xl font-bold"></ef-captions-active-word>
|
|
324
|
+
<ef-captions-after-active-word class="text-white/40 text-xl"></ef-captions-after-active-word>
|
|
325
|
+
</ef-captions>
|
|
326
|
+
|
|
327
|
+
<style>
|
|
328
|
+
@keyframes popIn {
|
|
329
|
+
0% { transform: scale(0.5); opacity: 0; }
|
|
330
|
+
50% { transform: scale(1.15); }
|
|
331
|
+
100% { transform: scale(1); opacity: 1; }
|
|
332
|
+
}
|
|
333
|
+
.pop-word {
|
|
334
|
+
display: inline-block;
|
|
335
|
+
animation: popIn 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
336
|
+
}
|
|
337
|
+
</style>
|
|
338
|
+
|
|
339
|
+
<script type="application/json" id="pop-captions">
|
|
340
|
+
{
|
|
341
|
+
"segments": [
|
|
342
|
+
{ "start": 0, "end": 3, "text": "Words pop in dynamically." }
|
|
343
|
+
],
|
|
344
|
+
"word_segments": [
|
|
345
|
+
{ "start": 0, "end": 0.5, "text": "Words" },
|
|
346
|
+
{ "start": 0.5, "end": 1.0, "text": "pop" },
|
|
347
|
+
{ "start": 1.0, "end": 1.3, "text": "in" },
|
|
348
|
+
{ "start": 1.3, "end": 2.2, "text": "dynamically." }
|
|
349
|
+
]
|
|
350
|
+
}
|
|
351
|
+
</script>
|
|
352
|
+
</ef-timegroup>
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Slide-In Animation
|
|
356
|
+
|
|
357
|
+
```html live
|
|
358
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-gradient-to-br from-blue-900 to-purple-900">
|
|
359
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain opacity-50"></ef-video>
|
|
360
|
+
|
|
361
|
+
<ef-captions captions-script="slide-captions" class="absolute bottom-20 left-0 right-0 text-center">
|
|
362
|
+
<ef-captions-before-active-word class="text-white/50 text-xl"></ef-captions-before-active-word>
|
|
363
|
+
<ef-captions-active-word class="slide-word text-white text-2xl font-bold bg-gradient-to-r from-pink-500 to-purple-500 px-2 rounded"></ef-captions-active-word>
|
|
364
|
+
<ef-captions-after-active-word class="text-white/30 text-xl"></ef-captions-after-active-word>
|
|
365
|
+
</ef-captions>
|
|
366
|
+
|
|
367
|
+
<style>
|
|
368
|
+
@keyframes slideIn {
|
|
369
|
+
from { transform: translateX(-30px); opacity: 0; }
|
|
370
|
+
to { transform: translateX(0); opacity: 1; }
|
|
371
|
+
}
|
|
372
|
+
.slide-word {
|
|
373
|
+
display: inline-block;
|
|
374
|
+
animation: slideIn 0.3s ease-out;
|
|
375
|
+
}
|
|
376
|
+
</style>
|
|
377
|
+
|
|
378
|
+
<script type="application/json" id="slide-captions">
|
|
379
|
+
{
|
|
380
|
+
"segments": [
|
|
381
|
+
{ "start": 0, "end": 3, "text": "Words slide in smoothly." }
|
|
382
|
+
],
|
|
383
|
+
"word_segments": [
|
|
384
|
+
{ "start": 0, "end": 0.5, "text": "Words" },
|
|
385
|
+
{ "start": 0.5, "end": 1.0, "text": "slide" },
|
|
386
|
+
{ "start": 1.0, "end": 1.3, "text": "in" },
|
|
387
|
+
{ "start": 1.3, "end": 2.0, "text": "smoothly." }
|
|
388
|
+
]
|
|
389
|
+
}
|
|
390
|
+
</script>
|
|
391
|
+
</ef-timegroup>
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## Segment-Level Styling
|
|
395
|
+
|
|
396
|
+
### Full Segment Display
|
|
397
|
+
|
|
398
|
+
```html live
|
|
399
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black">
|
|
400
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain"></ef-video>
|
|
401
|
+
|
|
402
|
+
<ef-captions captions-script="segment-captions" class="absolute bottom-16 left-0 right-0 text-center">
|
|
403
|
+
<ef-captions-segment class="text-white text-2xl bg-black/70 px-6 py-3 rounded-xl inline-block shadow-2xl border-2 border-white/20"></ef-captions-segment>
|
|
404
|
+
</ef-captions>
|
|
405
|
+
|
|
406
|
+
<script type="application/json" id="segment-captions">
|
|
407
|
+
{
|
|
408
|
+
"segments": [
|
|
409
|
+
{ "start": 0, "end": 3, "text": "This is the first segment." },
|
|
410
|
+
{ "start": 3, "end": 6, "text": "Now showing the second segment." }
|
|
411
|
+
],
|
|
412
|
+
"word_segments": []
|
|
413
|
+
}
|
|
414
|
+
</script>
|
|
415
|
+
</ef-timegroup>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Multi-Line Segments
|
|
419
|
+
|
|
420
|
+
```html live
|
|
421
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-gradient-to-br from-teal-900 to-blue-900">
|
|
422
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain opacity-60"></ef-video>
|
|
423
|
+
|
|
424
|
+
<ef-captions captions-script="multiline-captions" class="absolute bottom-12 left-8 right-8">
|
|
425
|
+
<ef-captions-segment class="block text-white text-xl bg-gradient-to-r from-teal-600/80 to-blue-600/80 px-4 py-3 rounded-lg shadow-xl text-center leading-relaxed"></ef-captions-segment>
|
|
426
|
+
</ef-captions>
|
|
427
|
+
|
|
428
|
+
<script type="application/json" id="multiline-captions">
|
|
429
|
+
{
|
|
430
|
+
"segments": [
|
|
431
|
+
{ "start": 0, "end": 3, "text": "Segment-level styling creates clean caption blocks." },
|
|
432
|
+
{ "start": 3, "end": 6, "text": "Perfect for traditional subtitle appearance." }
|
|
433
|
+
],
|
|
434
|
+
"word_segments": []
|
|
435
|
+
}
|
|
436
|
+
</script>
|
|
437
|
+
</ef-timegroup>
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Karaoke-Style Captions
|
|
441
|
+
|
|
442
|
+
### Classic Karaoke
|
|
443
|
+
|
|
444
|
+
```html live
|
|
445
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-gradient-to-br from-pink-900 via-purple-900 to-indigo-900">
|
|
446
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain opacity-40"></ef-video>
|
|
447
|
+
|
|
448
|
+
<ef-captions captions-script="karaoke1" class="absolute bottom-24 left-0 right-0 text-center leading-relaxed">
|
|
449
|
+
<ef-captions-before-active-word class="text-green-400 text-3xl font-bold"></ef-captions-before-active-word>
|
|
450
|
+
<ef-captions-active-word class="text-white text-3xl font-bold bg-pink-600 px-2 py-1 rounded-md shadow-lg"></ef-captions-active-word>
|
|
451
|
+
<ef-captions-after-active-word class="text-white/50 text-3xl font-bold"></ef-captions-after-active-word>
|
|
452
|
+
</ef-captions>
|
|
453
|
+
|
|
454
|
+
<script type="application/json" id="karaoke1">
|
|
455
|
+
{
|
|
456
|
+
"segments": [
|
|
457
|
+
{ "start": 0, "end": 4, "text": "Sing along with karaoke style captions!" }
|
|
458
|
+
],
|
|
459
|
+
"word_segments": [
|
|
460
|
+
{ "start": 0, "end": 0.5, "text": "Sing" },
|
|
461
|
+
{ "start": 0.5, "end": 1.0, "text": "along" },
|
|
462
|
+
{ "start": 1.0, "end": 1.3, "text": "with" },
|
|
463
|
+
{ "start": 1.3, "end": 2.0, "text": "karaoke" },
|
|
464
|
+
{ "start": 2.0, "end": 2.5, "text": "style" },
|
|
465
|
+
{ "start": 2.5, "end": 3.5, "text": "captions!" }
|
|
466
|
+
]
|
|
467
|
+
}
|
|
468
|
+
</script>
|
|
469
|
+
</ef-timegroup>
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Progress Bar Karaoke
|
|
473
|
+
|
|
474
|
+
```html live
|
|
475
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black">
|
|
476
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain"></ef-video>
|
|
477
|
+
|
|
478
|
+
<ef-captions captions-script="karaoke2" class="absolute bottom-20 left-0 right-0">
|
|
479
|
+
<div class="bg-gradient-to-r from-purple-600/90 to-pink-600/90 py-4 px-8 text-center text-3xl font-bold leading-relaxed">
|
|
480
|
+
<ef-captions-before-active-word class="text-yellow-300"></ef-captions-before-active-word>
|
|
481
|
+
<ef-captions-active-word class="text-white bg-yellow-500 px-2 rounded shadow-xl"></ef-captions-active-word>
|
|
482
|
+
<ef-captions-after-active-word class="text-white/60"></ef-captions-after-active-word>
|
|
483
|
+
</div>
|
|
484
|
+
</ef-captions>
|
|
485
|
+
|
|
486
|
+
<script type="application/json" id="karaoke2">
|
|
487
|
+
{
|
|
488
|
+
"segments": [
|
|
489
|
+
{ "start": 0, "end": 4, "text": "Words turn gold as they're sung." }
|
|
490
|
+
],
|
|
491
|
+
"word_segments": [
|
|
492
|
+
{ "start": 0, "end": 0.6, "text": "Words" },
|
|
493
|
+
{ "start": 0.6, "end": 1.1, "text": "turn" },
|
|
494
|
+
{ "start": 1.1, "end": 1.6, "text": "gold" },
|
|
495
|
+
{ "start": 1.6, "end": 1.9, "text": "as" },
|
|
496
|
+
{ "start": 1.9, "end": 2.4, "text": "they're" },
|
|
497
|
+
{ "start": 2.4, "end": 3.0, "text": "sung." }
|
|
498
|
+
]
|
|
499
|
+
}
|
|
500
|
+
</script>
|
|
501
|
+
</ef-timegroup>
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Gradient Progression
|
|
505
|
+
|
|
506
|
+
```html live
|
|
507
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-gradient-to-br from-gray-900 to-gray-800">
|
|
508
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain opacity-50"></ef-video>
|
|
509
|
+
|
|
510
|
+
<ef-captions captions-script="karaoke3" class="absolute bottom-20 left-0 right-0 text-center text-2xl font-semibold leading-relaxed">
|
|
511
|
+
<ef-captions-before-active-word class="text-blue-400"></ef-captions-before-active-word>
|
|
512
|
+
<ef-captions-active-word class="text-cyan-300 text-3xl" style="text-shadow: 0 0 20px rgba(34, 211, 238, 0.8);"></ef-captions-active-word>
|
|
513
|
+
<ef-captions-after-active-word class="text-gray-500"></ef-captions-after-active-word>
|
|
514
|
+
</ef-captions>
|
|
515
|
+
|
|
516
|
+
<script type="application/json" id="karaoke3">
|
|
517
|
+
{
|
|
518
|
+
"segments": [
|
|
519
|
+
{ "start": 0, "end": 4, "text": "Progressive color transitions flow smoothly." }
|
|
520
|
+
],
|
|
521
|
+
"word_segments": [
|
|
522
|
+
{ "start": 0, "end": 0.6, "text": "Progressive" },
|
|
523
|
+
{ "start": 0.6, "end": 1.1, "text": "color" },
|
|
524
|
+
{ "start": 1.1, "end": 1.8, "text": "transitions" },
|
|
525
|
+
{ "start": 1.8, "end": 2.2, "text": "flow" },
|
|
526
|
+
{ "start": 2.2, "end": 3.0, "text": "smoothly." }
|
|
527
|
+
]
|
|
528
|
+
}
|
|
529
|
+
</script>
|
|
530
|
+
</ef-timegroup>
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
## Custom Timing Examples
|
|
534
|
+
|
|
535
|
+
### Fast-Paced Captions
|
|
536
|
+
|
|
537
|
+
```html live
|
|
538
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black">
|
|
539
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain"></ef-video>
|
|
540
|
+
|
|
541
|
+
<ef-captions captions-script="fast-captions" class="absolute bottom-16 left-0 right-0 text-center">
|
|
542
|
+
<ef-captions-before-active-word class="text-white/50 text-xl"></ef-captions-before-active-word>
|
|
543
|
+
<ef-captions-active-word class="text-orange-400 text-2xl font-bold"></ef-captions-active-word>
|
|
544
|
+
<ef-captions-after-active-word class="text-white/30 text-xl"></ef-captions-after-active-word>
|
|
545
|
+
</ef-captions>
|
|
546
|
+
|
|
547
|
+
<script type="application/json" id="fast-captions">
|
|
548
|
+
{
|
|
549
|
+
"segments": [
|
|
550
|
+
{ "start": 0, "end": 2, "text": "Quick rapid fire speech!" }
|
|
551
|
+
],
|
|
552
|
+
"word_segments": [
|
|
553
|
+
{ "start": 0, "end": 0.3, "text": "Quick" },
|
|
554
|
+
{ "start": 0.3, "end": 0.6, "text": "rapid" },
|
|
555
|
+
{ "start": 0.6, "end": 0.9, "text": "fire" },
|
|
556
|
+
{ "start": 0.9, "end": 1.4, "text": "speech!" }
|
|
557
|
+
]
|
|
558
|
+
}
|
|
559
|
+
</script>
|
|
560
|
+
</ef-timegroup>
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### Slow Dramatic Timing
|
|
564
|
+
|
|
565
|
+
```html live
|
|
566
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-gradient-to-b from-gray-900 to-black">
|
|
567
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain opacity-40"></ef-video>
|
|
568
|
+
|
|
569
|
+
<ef-captions captions-script="slow-captions" class="absolute bottom-20 left-0 right-0 text-center">
|
|
570
|
+
<ef-captions-before-active-word class="text-gray-500 text-2xl"></ef-captions-before-active-word>
|
|
571
|
+
<ef-captions-active-word class="text-white text-4xl font-bold" style="text-shadow: 3px 3px 10px rgba(0,0,0,0.9);"></ef-captions-active-word>
|
|
572
|
+
<ef-captions-after-active-word class="text-gray-600 text-2xl"></ef-captions-after-active-word>
|
|
573
|
+
</ef-captions>
|
|
574
|
+
|
|
575
|
+
<script type="application/json" id="slow-captions">
|
|
576
|
+
{
|
|
577
|
+
"segments": [
|
|
578
|
+
{ "start": 0, "end": 6, "text": "Slow. Deliberate. Speech." }
|
|
579
|
+
],
|
|
580
|
+
"word_segments": [
|
|
581
|
+
{ "start": 0, "end": 1.5, "text": "Slow." },
|
|
582
|
+
{ "start": 2.0, "end": 3.5, "text": "Deliberate." },
|
|
583
|
+
{ "start": 4.0, "end": 5.5, "text": "Speech." }
|
|
584
|
+
]
|
|
585
|
+
}
|
|
586
|
+
</script>
|
|
587
|
+
</ef-timegroup>
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Multiple Speakers
|
|
591
|
+
|
|
592
|
+
```html live
|
|
593
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-gradient-to-br from-indigo-900 to-purple-900">
|
|
594
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain opacity-60"></ef-video>
|
|
595
|
+
|
|
596
|
+
<ef-captions captions-script="speakers-captions" class="absolute bottom-16 left-0 right-0 text-center">
|
|
597
|
+
<ef-captions-segment class="block text-white text-xl bg-blue-600/80 px-4 py-2 rounded-lg mb-2 inline-block"></ef-captions-segment>
|
|
598
|
+
</ef-captions>
|
|
599
|
+
|
|
600
|
+
<script type="application/json" id="speakers-captions">
|
|
601
|
+
{
|
|
602
|
+
"segments": [
|
|
603
|
+
{ "start": 0, "end": 2, "text": "[Speaker 1] Hello there!" },
|
|
604
|
+
{ "start": 2.5, "end": 4.5, "text": "[Speaker 2] Hi, how are you?" },
|
|
605
|
+
{ "start": 5, "end": 7, "text": "[Speaker 1] I'm doing great!" }
|
|
606
|
+
],
|
|
607
|
+
"word_segments": []
|
|
608
|
+
}
|
|
609
|
+
</script>
|
|
610
|
+
</ef-timegroup>
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
## Loading Captions
|
|
614
|
+
|
|
615
|
+
### From External File
|
|
616
|
+
|
|
617
|
+
```html
|
|
618
|
+
<ef-captions captions-src="/captions/video-transcript.json" class="absolute bottom-12 left-0 right-0 text-center">
|
|
619
|
+
<ef-captions-before-active-word class="text-white/60 text-xl"></ef-captions-before-active-word>
|
|
620
|
+
<ef-captions-active-word class="text-yellow-300 text-2xl font-bold"></ef-captions-active-word>
|
|
621
|
+
<ef-captions-after-active-word class="text-white/40 text-xl"></ef-captions-after-active-word>
|
|
622
|
+
</ef-captions>
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
### From Inline Script
|
|
626
|
+
|
|
627
|
+
```html
|
|
628
|
+
<ef-captions captions-script="my-captions">
|
|
629
|
+
<ef-captions-active-word class="text-yellow-300"></ef-captions-active-word>
|
|
630
|
+
</ef-captions>
|
|
631
|
+
|
|
632
|
+
<script type="application/json" id="my-captions">
|
|
633
|
+
{
|
|
634
|
+
"segments": [
|
|
635
|
+
{ "start": 0, "end": 3, "text": "Your caption text here." }
|
|
636
|
+
],
|
|
637
|
+
"word_segments": [
|
|
638
|
+
{ "start": 0, "end": 1, "text": "Your" },
|
|
639
|
+
{ "start": 1, "end": 2, "text": "caption" },
|
|
640
|
+
{ "start": 2, "end": 3, "text": "text" },
|
|
641
|
+
{ "start": 3, "end": 4, "text": "here." }
|
|
642
|
+
]
|
|
643
|
+
}
|
|
644
|
+
</script>
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### Via JavaScript
|
|
648
|
+
|
|
649
|
+
```html
|
|
650
|
+
<ef-captions id="dynamic-captions">
|
|
651
|
+
<ef-captions-active-word class="text-cyan-400"></ef-captions-active-word>
|
|
652
|
+
</ef-captions>
|
|
653
|
+
|
|
654
|
+
<script>
|
|
655
|
+
const captions = document.getElementById('dynamic-captions');
|
|
656
|
+
captions.captionsData = {
|
|
657
|
+
segments: [
|
|
658
|
+
{ start: 0, end: 3, text: "Dynamically loaded captions." }
|
|
659
|
+
],
|
|
660
|
+
word_segments: [
|
|
661
|
+
{ start: 0, end: 1, text: "Dynamically" },
|
|
662
|
+
{ start: 1, end: 2, text: "loaded" },
|
|
663
|
+
{ start: 2, end: 3, text: "captions." }
|
|
664
|
+
]
|
|
665
|
+
};
|
|
666
|
+
</script>
|
|
667
|
+
```
|
|
668
|
+
<!-- /html-only -->
|
|
669
|
+
|
|
670
|
+
## Sub-Elements
|
|
671
|
+
|
|
672
|
+
`ef-captions` uses child elements to separate caption text into styleable parts. These elements act as **slots** — they're containers that `ef-captions` fills with text. You style them with CSS classes, and `ef-captions` handles updating their content.
|
|
673
|
+
|
|
674
|
+
All caption sub-elements use `display: inline` by default for natural text flow. They act as transparent containers — the text flows as if the element boundaries don't exist.
|
|
675
|
+
|
|
676
|
+
### ef-captions-active-word
|
|
677
|
+
|
|
678
|
+
The word currently being spoken. Automatically hidden when empty or contains only punctuation.
|
|
679
|
+
|
|
680
|
+
**CSS Variables:**
|
|
681
|
+
- `--ef-word-seed` - Deterministic random (0-1) per word for animations
|
|
682
|
+
|
|
683
|
+
**Behavior:**
|
|
684
|
+
- Adds trailing space automatically for proper word spacing
|
|
685
|
+
- Hidden via `hidden` attribute when no active word
|
|
686
|
+
|
|
687
|
+
### ef-captions-before-active-word
|
|
688
|
+
|
|
689
|
+
All words in the current segment that have already been spoken.
|
|
690
|
+
|
|
691
|
+
**Behavior:**
|
|
692
|
+
- Adds trailing space when followed by active word
|
|
693
|
+
- Hidden via `hidden` attribute when no prior words
|
|
694
|
+
|
|
695
|
+
### ef-captions-after-active-word
|
|
696
|
+
|
|
697
|
+
All words in the current segment not yet spoken.
|
|
698
|
+
|
|
699
|
+
**Behavior:**
|
|
700
|
+
- No leading space (active word adds trailing space)
|
|
701
|
+
- Hidden via `hidden` attribute when no upcoming words
|
|
702
|
+
|
|
703
|
+
### ef-captions-segment
|
|
704
|
+
|
|
705
|
+
The full text of the current caption segment.
|
|
706
|
+
|
|
707
|
+
**Behavior:**
|
|
708
|
+
- Hidden via `hidden` attribute when no active segment
|
|
709
|
+
- Can be used alone or alongside word-level elements
|
|
710
|
+
|
|
711
|
+
### Layout Patterns
|
|
712
|
+
|
|
713
|
+
```html
|
|
714
|
+
<!-- Inline flow (default) -->
|
|
715
|
+
<ef-captions class="text-white text-xl">
|
|
716
|
+
<ef-captions-before-active-word></ef-captions-before-active-word>
|
|
717
|
+
<ef-captions-active-word class="font-bold"></ef-captions-active-word>
|
|
718
|
+
<ef-captions-after-active-word></ef-captions-after-active-word>
|
|
719
|
+
</ef-captions>
|
|
720
|
+
|
|
721
|
+
<!-- Multi-line layout -->
|
|
722
|
+
<ef-captions>
|
|
723
|
+
<div class="text-center">
|
|
724
|
+
<ef-captions-segment class="block text-white/50 mb-1"></ef-captions-segment>
|
|
725
|
+
</div>
|
|
726
|
+
<div class="text-center">
|
|
727
|
+
<ef-captions-active-word class="text-yellow-400 text-2xl"></ef-captions-active-word>
|
|
728
|
+
</div>
|
|
729
|
+
</ef-captions>
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
### Technical Notes
|
|
733
|
+
|
|
734
|
+
- All sub-elements use light DOM (not shadow DOM) for styling simplicity
|
|
735
|
+
- Parent `ef-captions` element updates child `textContent` directly
|
|
736
|
+
- Empty or punctuation-only content automatically hides elements via `hidden` attribute
|
|
737
|
+
- Elements maintain text flow by using `display: inline` with no margins/padding
|
|
738
|
+
- `--ef-word-seed` provides deterministic randomness based on word index (not random each frame)
|
|
739
|
+
|
|
740
|
+
## Generate Captions
|
|
741
|
+
|
|
742
|
+
Use the Editframe CLI to generate captions from video/audio files:
|
|
743
|
+
|
|
744
|
+
```bash
|
|
745
|
+
npx editframe transcribe video.mp4 -o captions.json
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
<!-- html-only -->
|
|
749
|
+
```bash
|
|
750
|
+
# Transcribe with specific language
|
|
751
|
+
npx editframe transcribe video.mp4 --language en -o captions.json
|
|
752
|
+
|
|
753
|
+
# Transcribe audio file
|
|
754
|
+
npx editframe transcribe audio.mp3 -o captions.json
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
Then load the generated captions:
|
|
758
|
+
|
|
759
|
+
```html live
|
|
760
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black">
|
|
761
|
+
<ef-video src="https://assets.editframe.com/bars-n-tone.mp4" class="size-full object-contain"></ef-video>
|
|
762
|
+
|
|
763
|
+
<!-- In production, use captions-src="/path/to/captions.json" -->
|
|
764
|
+
<ef-captions captions-script="transcribed-captions" class="absolute bottom-16 left-0 right-0 text-center">
|
|
765
|
+
<ef-captions-before-active-word class="text-white/60 text-xl"></ef-captions-before-active-word>
|
|
766
|
+
<ef-captions-active-word class="text-green-400 text-2xl font-bold bg-black/60 px-2 rounded"></ef-captions-active-word>
|
|
767
|
+
<ef-captions-after-active-word class="text-white/40 text-xl"></ef-captions-after-active-word>
|
|
768
|
+
</ef-captions>
|
|
769
|
+
|
|
770
|
+
<script type="application/json" id="transcribed-captions">
|
|
771
|
+
{
|
|
772
|
+
"segments": [
|
|
773
|
+
{ "start": 0, "end": 3, "text": "Transcription generates this format automatically." }
|
|
774
|
+
],
|
|
775
|
+
"word_segments": [
|
|
776
|
+
{ "start": 0, "end": 0.7, "text": "Transcription" },
|
|
777
|
+
{ "start": 0.7, "end": 1.3, "text": "generates" },
|
|
778
|
+
{ "start": 1.3, "end": 1.6, "text": "this" },
|
|
779
|
+
{ "start": 1.6, "end": 2.0, "text": "format" },
|
|
780
|
+
{ "start": 2.0, "end": 3.0, "text": "automatically." }
|
|
781
|
+
]
|
|
782
|
+
}
|
|
783
|
+
</script>
|
|
784
|
+
</ef-timegroup>
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
See [transcription.md](references/transcription.md) for complete transcription workflow documentation.
|
|
788
|
+
<!-- /html-only -->
|
|
789
|
+
<!-- react-only -->
|
|
790
|
+
This uses `whisper_timestamped` to create word-level timestamps. Install it first:
|
|
791
|
+
|
|
792
|
+
```bash
|
|
793
|
+
pip3 install whisper-timestamped
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
See [references/transcription.md](references/transcription.md) for more details.
|
|
797
|
+
|
|
798
|
+
## Multiple Caption Tracks
|
|
799
|
+
|
|
800
|
+
```tsx
|
|
801
|
+
<Timegroup mode="contain" className="absolute w-full h-full">
|
|
802
|
+
<Video src="/assets/video.mp4" className="size-full" />
|
|
803
|
+
|
|
804
|
+
{/* English subtitles */}
|
|
805
|
+
<Captions
|
|
806
|
+
id="en"
|
|
807
|
+
src="/assets/captions-en.json"
|
|
808
|
+
className="absolute bottom-24 text-white text-2xl text-center w-full"
|
|
809
|
+
/>
|
|
810
|
+
|
|
811
|
+
{/* Spanish subtitles */}
|
|
812
|
+
<Captions
|
|
813
|
+
id="es"
|
|
814
|
+
src="/assets/captions-es.json"
|
|
815
|
+
className="absolute bottom-8 text-yellow-300 text-xl text-center w-full"
|
|
816
|
+
/>
|
|
817
|
+
</Timegroup>
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
## Dynamic Captions
|
|
821
|
+
|
|
822
|
+
```tsx
|
|
823
|
+
interface CaptionTrack {
|
|
824
|
+
id: string;
|
|
825
|
+
src: string;
|
|
826
|
+
language: string;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
const tracks: CaptionTrack[] = [
|
|
830
|
+
{ id: "en", src: "/assets/captions-en.json", language: "English" },
|
|
831
|
+
{ id: "es", src: "/assets/captions-es.json", language: "Spanish" },
|
|
832
|
+
];
|
|
833
|
+
|
|
834
|
+
const [selectedTrack, setSelectedTrack] = useState(tracks[0]);
|
|
835
|
+
|
|
836
|
+
<Timegroup mode="contain" className="absolute w-full h-full">
|
|
837
|
+
<Video src="/assets/video.mp4" className="size-full" />
|
|
838
|
+
<Captions
|
|
839
|
+
src={selectedTrack.src}
|
|
840
|
+
className="absolute bottom-8 text-white text-2xl text-center w-full"
|
|
841
|
+
/>
|
|
842
|
+
</Timegroup>
|
|
843
|
+
```
|
|
844
|
+
<!-- /react-only -->
|