@editframe/create 0.43.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.
Files changed (99) hide show
  1. package/README.md +11 -0
  2. package/dist/index.js +16 -28
  3. package/dist/index.js.map +1 -1
  4. package/dist/skills/editframe-brand-video-generator/README.md +155 -0
  5. package/dist/skills/editframe-brand-video-generator/SKILL.md +207 -0
  6. package/dist/skills/editframe-brand-video-generator/references/brand-examples.md +178 -0
  7. package/dist/skills/editframe-brand-video-generator/references/color-psychology.md +227 -0
  8. package/dist/skills/editframe-brand-video-generator/references/composition-patterns.md +383 -0
  9. package/dist/skills/editframe-brand-video-generator/references/editing.md +66 -0
  10. package/dist/skills/editframe-brand-video-generator/references/emotional-arcs.md +496 -0
  11. package/dist/skills/editframe-brand-video-generator/references/genre-selection.md +135 -0
  12. package/dist/skills/editframe-brand-video-generator/references/transition-styles.md +611 -0
  13. package/dist/skills/editframe-brand-video-generator/references/typography-personalities.md +326 -0
  14. package/dist/skills/editframe-brand-video-generator/references/video-archetypes.md +86 -0
  15. package/dist/skills/editframe-brand-video-generator/references/video-fundamentals.md +169 -0
  16. package/dist/skills/editframe-brand-video-generator/references/visual-metaphors.md +50 -0
  17. package/dist/skills/editframe-composition/SKILL.md +169 -0
  18. package/dist/skills/editframe-composition/references/audio.md +483 -0
  19. package/dist/skills/editframe-composition/references/captions.md +844 -0
  20. package/dist/skills/editframe-composition/references/composition-model.md +73 -0
  21. package/dist/skills/editframe-composition/references/configuration.md +403 -0
  22. package/dist/skills/editframe-composition/references/css-parts.md +105 -0
  23. package/dist/skills/editframe-composition/references/css-variables.md +640 -0
  24. package/dist/skills/editframe-composition/references/entry-points.md +810 -0
  25. package/dist/skills/editframe-composition/references/events.md +499 -0
  26. package/dist/skills/editframe-composition/references/getting-started.md +259 -0
  27. package/dist/skills/editframe-composition/references/hooks.md +234 -0
  28. package/dist/skills/editframe-composition/references/image.md +241 -0
  29. package/dist/skills/editframe-composition/references/r3f.md +580 -0
  30. package/dist/skills/editframe-composition/references/render-api.md +484 -0
  31. package/dist/skills/editframe-composition/references/render-strategies.md +119 -0
  32. package/dist/skills/editframe-composition/references/render-to-video.md +1101 -0
  33. package/dist/skills/editframe-composition/references/scripting.md +606 -0
  34. package/dist/skills/editframe-composition/references/sequencing.md +116 -0
  35. package/dist/skills/editframe-composition/references/server-rendering.md +753 -0
  36. package/dist/skills/editframe-composition/references/surface.md +329 -0
  37. package/dist/skills/editframe-composition/references/text.md +627 -0
  38. package/dist/skills/editframe-composition/references/time-model.md +99 -0
  39. package/dist/skills/editframe-composition/references/timegroup-modes.md +102 -0
  40. package/dist/skills/editframe-composition/references/timegroup.md +457 -0
  41. package/dist/skills/editframe-composition/references/timeline-root.md +398 -0
  42. package/dist/skills/editframe-composition/references/transcription.md +47 -0
  43. package/dist/skills/editframe-composition/references/transitions.md +608 -0
  44. package/dist/skills/editframe-composition/references/use-media-info.md +357 -0
  45. package/dist/skills/editframe-composition/references/video.md +506 -0
  46. package/dist/skills/editframe-composition/references/waveform.md +327 -0
  47. package/dist/skills/editframe-editor-gui/SKILL.md +152 -0
  48. package/dist/skills/editframe-editor-gui/references/active-root-temporal.md +657 -0
  49. package/dist/skills/editframe-editor-gui/references/canvas.md +947 -0
  50. package/dist/skills/editframe-editor-gui/references/controls.md +366 -0
  51. package/dist/skills/editframe-editor-gui/references/dial.md +756 -0
  52. package/dist/skills/editframe-editor-gui/references/editor-toolkit.md +587 -0
  53. package/dist/skills/editframe-editor-gui/references/filmstrip.md +460 -0
  54. package/dist/skills/editframe-editor-gui/references/fit-scale.md +772 -0
  55. package/dist/skills/editframe-editor-gui/references/focus-overlay.md +561 -0
  56. package/dist/skills/editframe-editor-gui/references/hierarchy.md +544 -0
  57. package/dist/skills/editframe-editor-gui/references/overlay-item.md +634 -0
  58. package/dist/skills/editframe-editor-gui/references/overlay-layer.md +429 -0
  59. package/dist/skills/editframe-editor-gui/references/pan-zoom.md +568 -0
  60. package/dist/skills/editframe-editor-gui/references/pause.md +397 -0
  61. package/dist/skills/editframe-editor-gui/references/play.md +370 -0
  62. package/dist/skills/editframe-editor-gui/references/preview.md +391 -0
  63. package/dist/skills/editframe-editor-gui/references/resizable-box.md +749 -0
  64. package/dist/skills/editframe-editor-gui/references/scrubber.md +588 -0
  65. package/dist/skills/editframe-editor-gui/references/thumbnail-strip.md +566 -0
  66. package/dist/skills/editframe-editor-gui/references/time-display.md +492 -0
  67. package/dist/skills/editframe-editor-gui/references/timeline-ruler.md +489 -0
  68. package/dist/skills/editframe-editor-gui/references/timeline.md +604 -0
  69. package/dist/skills/editframe-editor-gui/references/toggle-loop.md +618 -0
  70. package/dist/skills/editframe-editor-gui/references/toggle-play.md +526 -0
  71. package/dist/skills/editframe-editor-gui/references/transform-handles.md +924 -0
  72. package/dist/skills/editframe-editor-gui/references/trim-handles.md +725 -0
  73. package/dist/skills/editframe-editor-gui/references/workbench.md +453 -0
  74. package/dist/skills/editframe-motion-design/SKILL.md +101 -0
  75. package/dist/skills/editframe-motion-design/references/0-editframe.md +299 -0
  76. package/dist/skills/editframe-motion-design/references/1-intent.md +201 -0
  77. package/dist/skills/editframe-motion-design/references/2-physics-model.md +405 -0
  78. package/dist/skills/editframe-motion-design/references/3-attention.md +350 -0
  79. package/dist/skills/editframe-motion-design/references/4-process.md +418 -0
  80. package/dist/skills/editframe-vite-plugin/SKILL.md +75 -0
  81. package/dist/skills/editframe-vite-plugin/references/file-api.md +111 -0
  82. package/dist/skills/editframe-vite-plugin/references/getting-started.md +96 -0
  83. package/dist/skills/editframe-vite-plugin/references/jit-transcoding.md +91 -0
  84. package/dist/skills/editframe-vite-plugin/references/local-assets.md +75 -0
  85. package/dist/skills/editframe-vite-plugin/references/visual-testing.md +136 -0
  86. package/dist/skills/editframe-webhooks/SKILL.md +126 -0
  87. package/dist/skills/editframe-webhooks/references/events.md +382 -0
  88. package/dist/skills/editframe-webhooks/references/getting-started.md +232 -0
  89. package/dist/skills/editframe-webhooks/references/security.md +418 -0
  90. package/dist/skills/editframe-webhooks/references/testing.md +409 -0
  91. package/dist/skills/editframe-webhooks/references/troubleshooting.md +457 -0
  92. package/dist/templates/html/AGENTS.md +13 -0
  93. package/dist/templates/react/AGENTS.md +13 -0
  94. package/dist/utils.js +15 -16
  95. package/dist/utils.js.map +1 -1
  96. package/package.json +2 -2
  97. package/tsdown.config.ts +4 -0
  98. package/dist/detectAgent.js +0 -89
  99. 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 -->