@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.
Files changed (98) hide show
  1. package/dist/index.js +16 -28
  2. package/dist/index.js.map +1 -1
  3. package/dist/skills/editframe-brand-video-generator/README.md +155 -0
  4. package/dist/skills/editframe-brand-video-generator/SKILL.md +207 -0
  5. package/dist/skills/editframe-brand-video-generator/references/brand-examples.md +178 -0
  6. package/dist/skills/editframe-brand-video-generator/references/color-psychology.md +227 -0
  7. package/dist/skills/editframe-brand-video-generator/references/composition-patterns.md +383 -0
  8. package/dist/skills/editframe-brand-video-generator/references/editing.md +66 -0
  9. package/dist/skills/editframe-brand-video-generator/references/emotional-arcs.md +496 -0
  10. package/dist/skills/editframe-brand-video-generator/references/genre-selection.md +135 -0
  11. package/dist/skills/editframe-brand-video-generator/references/transition-styles.md +611 -0
  12. package/dist/skills/editframe-brand-video-generator/references/typography-personalities.md +326 -0
  13. package/dist/skills/editframe-brand-video-generator/references/video-archetypes.md +86 -0
  14. package/dist/skills/editframe-brand-video-generator/references/video-fundamentals.md +169 -0
  15. package/dist/skills/editframe-brand-video-generator/references/visual-metaphors.md +50 -0
  16. package/dist/skills/editframe-composition/SKILL.md +169 -0
  17. package/dist/skills/editframe-composition/references/audio.md +483 -0
  18. package/dist/skills/editframe-composition/references/captions.md +844 -0
  19. package/dist/skills/editframe-composition/references/composition-model.md +73 -0
  20. package/dist/skills/editframe-composition/references/configuration.md +403 -0
  21. package/dist/skills/editframe-composition/references/css-parts.md +105 -0
  22. package/dist/skills/editframe-composition/references/css-variables.md +640 -0
  23. package/dist/skills/editframe-composition/references/entry-points.md +810 -0
  24. package/dist/skills/editframe-composition/references/events.md +499 -0
  25. package/dist/skills/editframe-composition/references/getting-started.md +259 -0
  26. package/dist/skills/editframe-composition/references/hooks.md +234 -0
  27. package/dist/skills/editframe-composition/references/image.md +241 -0
  28. package/dist/skills/editframe-composition/references/r3f.md +580 -0
  29. package/dist/skills/editframe-composition/references/render-api.md +484 -0
  30. package/dist/skills/editframe-composition/references/render-strategies.md +119 -0
  31. package/dist/skills/editframe-composition/references/render-to-video.md +1101 -0
  32. package/dist/skills/editframe-composition/references/scripting.md +606 -0
  33. package/dist/skills/editframe-composition/references/sequencing.md +116 -0
  34. package/dist/skills/editframe-composition/references/server-rendering.md +753 -0
  35. package/dist/skills/editframe-composition/references/surface.md +329 -0
  36. package/dist/skills/editframe-composition/references/text.md +627 -0
  37. package/dist/skills/editframe-composition/references/time-model.md +99 -0
  38. package/dist/skills/editframe-composition/references/timegroup-modes.md +102 -0
  39. package/dist/skills/editframe-composition/references/timegroup.md +457 -0
  40. package/dist/skills/editframe-composition/references/timeline-root.md +398 -0
  41. package/dist/skills/editframe-composition/references/transcription.md +47 -0
  42. package/dist/skills/editframe-composition/references/transitions.md +608 -0
  43. package/dist/skills/editframe-composition/references/use-media-info.md +357 -0
  44. package/dist/skills/editframe-composition/references/video.md +506 -0
  45. package/dist/skills/editframe-composition/references/waveform.md +327 -0
  46. package/dist/skills/editframe-editor-gui/SKILL.md +152 -0
  47. package/dist/skills/editframe-editor-gui/references/active-root-temporal.md +657 -0
  48. package/dist/skills/editframe-editor-gui/references/canvas.md +947 -0
  49. package/dist/skills/editframe-editor-gui/references/controls.md +366 -0
  50. package/dist/skills/editframe-editor-gui/references/dial.md +756 -0
  51. package/dist/skills/editframe-editor-gui/references/editor-toolkit.md +587 -0
  52. package/dist/skills/editframe-editor-gui/references/filmstrip.md +460 -0
  53. package/dist/skills/editframe-editor-gui/references/fit-scale.md +772 -0
  54. package/dist/skills/editframe-editor-gui/references/focus-overlay.md +561 -0
  55. package/dist/skills/editframe-editor-gui/references/hierarchy.md +544 -0
  56. package/dist/skills/editframe-editor-gui/references/overlay-item.md +634 -0
  57. package/dist/skills/editframe-editor-gui/references/overlay-layer.md +429 -0
  58. package/dist/skills/editframe-editor-gui/references/pan-zoom.md +568 -0
  59. package/dist/skills/editframe-editor-gui/references/pause.md +397 -0
  60. package/dist/skills/editframe-editor-gui/references/play.md +370 -0
  61. package/dist/skills/editframe-editor-gui/references/preview.md +391 -0
  62. package/dist/skills/editframe-editor-gui/references/resizable-box.md +749 -0
  63. package/dist/skills/editframe-editor-gui/references/scrubber.md +588 -0
  64. package/dist/skills/editframe-editor-gui/references/thumbnail-strip.md +566 -0
  65. package/dist/skills/editframe-editor-gui/references/time-display.md +492 -0
  66. package/dist/skills/editframe-editor-gui/references/timeline-ruler.md +489 -0
  67. package/dist/skills/editframe-editor-gui/references/timeline.md +604 -0
  68. package/dist/skills/editframe-editor-gui/references/toggle-loop.md +618 -0
  69. package/dist/skills/editframe-editor-gui/references/toggle-play.md +526 -0
  70. package/dist/skills/editframe-editor-gui/references/transform-handles.md +924 -0
  71. package/dist/skills/editframe-editor-gui/references/trim-handles.md +725 -0
  72. package/dist/skills/editframe-editor-gui/references/workbench.md +453 -0
  73. package/dist/skills/editframe-motion-design/SKILL.md +101 -0
  74. package/dist/skills/editframe-motion-design/references/0-editframe.md +299 -0
  75. package/dist/skills/editframe-motion-design/references/1-intent.md +201 -0
  76. package/dist/skills/editframe-motion-design/references/2-physics-model.md +405 -0
  77. package/dist/skills/editframe-motion-design/references/3-attention.md +350 -0
  78. package/dist/skills/editframe-motion-design/references/4-process.md +418 -0
  79. package/dist/skills/editframe-vite-plugin/SKILL.md +75 -0
  80. package/dist/skills/editframe-vite-plugin/references/file-api.md +111 -0
  81. package/dist/skills/editframe-vite-plugin/references/getting-started.md +96 -0
  82. package/dist/skills/editframe-vite-plugin/references/jit-transcoding.md +91 -0
  83. package/dist/skills/editframe-vite-plugin/references/local-assets.md +75 -0
  84. package/dist/skills/editframe-vite-plugin/references/visual-testing.md +136 -0
  85. package/dist/skills/editframe-webhooks/SKILL.md +126 -0
  86. package/dist/skills/editframe-webhooks/references/events.md +382 -0
  87. package/dist/skills/editframe-webhooks/references/getting-started.md +232 -0
  88. package/dist/skills/editframe-webhooks/references/security.md +418 -0
  89. package/dist/skills/editframe-webhooks/references/testing.md +409 -0
  90. package/dist/skills/editframe-webhooks/references/troubleshooting.md +457 -0
  91. package/dist/templates/html/AGENTS.md +13 -0
  92. package/dist/templates/react/AGENTS.md +13 -0
  93. package/dist/utils.js +15 -16
  94. package/dist/utils.js.map +1 -1
  95. package/package.json +1 -1
  96. package/tsdown.config.ts +4 -0
  97. package/dist/detectAgent.js +0 -89
  98. package/dist/detectAgent.js.map +0 -1
@@ -0,0 +1,725 @@
1
+ ---
2
+ title: Trim Handles Element
3
+ description: Draggable in-point and out-point handles for trimming media element duration directly on the composition timeline.
4
+ type: reference
5
+ nav:
6
+ parent: "Timeline"
7
+ priority: 20
8
+ api:
9
+ attributes:
10
+ - name: mode
11
+ type: string
12
+ default: standalone
13
+ description: Layout mode ("standalone" or "track")
14
+ - name: element-id
15
+ type: string
16
+ description: ID of element being trimmed (included in events)
17
+ - name: pixels-per-ms
18
+ type: number
19
+ description: Zoom level for pixel-to-time conversion
20
+ - name: value
21
+ type: TrimValue
22
+ description: Current trim value ({startMs, endMs})
23
+ - name: intrinsic-duration-ms
24
+ type: number
25
+ description: Total duration of source media
26
+ - name: show-overlays
27
+ type: boolean
28
+ default: true
29
+ description: Show darkened overlay on trimmed regions
30
+ - name: seek-target
31
+ type: string
32
+ description: ID of element to seek when trimming
33
+ types:
34
+ - name: TrimValue
35
+ definition: "{ startMs: number; endMs: number }"
36
+ description: Trim start and end times in milliseconds
37
+ - name: TrimChangeDetail
38
+ definition: "{ elementId: string; type: 'start' | 'end' | 'region'; value: TrimValue }"
39
+ description: Event detail for trim-change events
40
+ events:
41
+ - name: trim-change
42
+ detail: TrimChangeDetail
43
+ description: Emitted during trim handle drag (continuous)
44
+ - name: trim-change-end
45
+ detail: "{ elementId: string; type: 'start' | 'end' | 'region' }"
46
+ description: Emitted when trim drag completes
47
+ react:
48
+ generate: true
49
+ componentName: TrimHandles
50
+ importPath: "@editframe/react"
51
+ propMapping:
52
+ element-id: elementId
53
+ pixels-per-ms: pixelsPerMs
54
+ intrinsic-duration-ms: intrinsicDurationMs
55
+ show-overlays: showOverlays
56
+ seek-target: seekTarget
57
+ additionalProps:
58
+ - name: onTrimChange
59
+ type: "(event: CustomEvent<TrimChangeDetail>) => void"
60
+ description: Callback fired when trim values change
61
+ - name: onTrimChangeEnd
62
+ type: "(event: CustomEvent<TrimChangeDetail>) => void"
63
+ description: Callback fired when trim drag ends
64
+ - name: className
65
+ type: string
66
+ description: CSS classes for styling
67
+ nav:
68
+ parent: "Tools"
69
+ priority: 47
70
+ related: ["timeline", "video", "audio"]
71
+ ---
72
+
73
+ <!-- html-only -->
74
+ # ef-trim-handles
75
+ <!-- /html-only -->
76
+ <!-- react-only -->
77
+ # TrimHandles
78
+ <!-- /react-only -->
79
+
80
+ Interactive trim handles for adjusting the start and end points of media elements. Provides visual feedback and emits events for timeline integration.
81
+
82
+ <!-- react-only -->
83
+ ## Import
84
+
85
+ ```tsx
86
+ import { TrimHandles } from "@editframe/react";
87
+ ```
88
+ <!-- /react-only -->
89
+
90
+ ## Basic Usage
91
+
92
+ <!-- html-only -->
93
+ Trim handles on a video element.
94
+
95
+ ```html live
96
+ <div class="space-y-4">
97
+ <ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black" id="trim-demo">
98
+ <ef-video
99
+ src="https://assets.editframe.com/bars-n-tone.mp4"
100
+ class="size-full object-contain"
101
+ id="trim-video"
102
+ ></ef-video>
103
+ </ef-timegroup>
104
+
105
+ <div class="p-4 bg-gray-900 rounded-lg">
106
+ <div class="text-white text-sm mb-2">Drag handles to trim video:</div>
107
+ <div class="h-[60px] relative bg-gray-800 rounded-lg">
108
+ <ef-trim-handles
109
+ element-id="trim-video"
110
+ intrinsic-duration-ms="10000"
111
+ seek-target="trim-demo"
112
+ ></ef-trim-handles>
113
+ </div>
114
+ <ef-controls target="trim-demo" class="flex gap-2 mt-3">
115
+ <ef-toggle-play class="px-3 py-1 bg-blue-600 text-white rounded"></ef-toggle-play>
116
+ <ef-time-display class="text-white font-mono"></ef-time-display>
117
+ </ef-controls>
118
+ </div>
119
+ </div>
120
+
121
+ <script>
122
+ const handles = document.querySelector('ef-trim-handles');
123
+ const video = document.getElementById('trim-video');
124
+
125
+ handles.addEventListener('trim-change', (e) => {
126
+ const { value } = e.detail;
127
+ video.setAttribute('trimstart', `${value.startMs}ms`);
128
+ video.setAttribute('trimend', `${value.endMs}ms`);
129
+ });
130
+ </script>
131
+ ```
132
+ <!-- /html-only -->
133
+ <!-- react-only -->
134
+ ```tsx
135
+ import { useState } from "react";
136
+ import { TrimHandles, Video } from "@editframe/react";
137
+
138
+ export const TrimExample = () => {
139
+ const [trim, setTrim] = useState({ startMs: 0, endMs: 0 });
140
+
141
+ return (
142
+ <div className="relative h-24 bg-gray-800">
143
+ <TrimHandles
144
+ elementId="video-1"
145
+ value={trim}
146
+ intrinsicDurationMs={10000}
147
+ pixelsPerMs={0.04}
148
+ onTrimChange={(e) => setTrim(e.detail.value)}
149
+ />
150
+
151
+ <Video
152
+ id="video-1"
153
+ src="/video.mp4"
154
+ trimStart={`${trim.startMs}ms`}
155
+ trimEnd={`${trim.endMs}ms`}
156
+ />
157
+ </div>
158
+ );
159
+ };
160
+ ```
161
+ <!-- /react-only -->
162
+
163
+ ## With Timeline
164
+
165
+ <!-- html-only -->
166
+ Trim handles are automatically enabled in `ef-timeline` when `enable-trim` is set.
167
+
168
+ ```html
169
+ <ef-timeline target="composition" enable-trim></ef-timeline>
170
+ ```
171
+
172
+ The timeline handles trim events internally and updates element attributes.
173
+ <!-- /html-only -->
174
+ <!-- react-only -->
175
+ TrimHandles are automatically included when trim mode is enabled:
176
+
177
+ ```tsx
178
+ import { Timeline, Timegroup, Video } from "@editframe/react";
179
+
180
+ <Timeline
181
+ enableTrim
182
+ className="w-full h-96"
183
+ />
184
+
185
+ <Timegroup mode="sequence" className="w-[1920px] h-[1080px]">
186
+ <Video src="/video.mp4" />
187
+ </Timegroup>
188
+ ```
189
+ <!-- /react-only -->
190
+
191
+ ## Track Mode
192
+
193
+ <!-- html-only -->
194
+ In track mode, handles are pinned to container edges for timeline integration.
195
+
196
+ ```html live
197
+ <div class="space-y-4">
198
+ <ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black" id="track-demo">
199
+ <ef-video
200
+ src="https://assets.editframe.com/bars-n-tone.mp4"
201
+ class="size-full object-contain"
202
+ id="track-video"
203
+ trimstart="2s"
204
+ trimend="3s"
205
+ ></ef-video>
206
+ </ef-timegroup>
207
+
208
+ <div class="p-4 bg-gray-900 rounded-lg">
209
+ <div class="text-white text-sm mb-2">Track mode (handles at edges):</div>
210
+ <div class="h-[48px] relative bg-gray-800 rounded-lg overflow-hidden">
211
+ <div style="position: absolute; left: 80px; right: 120px; top: 0; bottom: 0; background: rgba(59,130,246,0.2);">
212
+ <ef-trim-handles
213
+ mode="track"
214
+ element-id="track-video"
215
+ intrinsic-duration-ms="10000"
216
+ pixels-per-ms="0.04"
217
+ ></ef-trim-handles>
218
+ </div>
219
+ </div>
220
+ <ef-controls target="track-demo" class="flex gap-2 mt-3">
221
+ <ef-toggle-play class="px-3 py-1 bg-blue-600 text-white rounded"></ef-toggle-play>
222
+ <ef-time-display class="text-white font-mono"></ef-time-display>
223
+ </ef-controls>
224
+ </div>
225
+ </div>
226
+
227
+ <script>
228
+ const trackHandles = document.querySelectorAll('ef-trim-handles[mode="track"]')[0];
229
+ const trackVideo = document.getElementById('track-video');
230
+
231
+ if (trackHandles && trackVideo) {
232
+ trackHandles.value = { startMs: 2000, endMs: 3000 };
233
+
234
+ trackHandles.addEventListener('trim-change', (e) => {
235
+ const { value } = e.detail;
236
+ trackVideo.setAttribute('trimstart', `${value.startMs}ms`);
237
+ trackVideo.setAttribute('trimend', `${value.endMs}ms`);
238
+ });
239
+ }
240
+ </script>
241
+ ```
242
+ <!-- /html-only -->
243
+ <!-- react-only -->
244
+ Handles pinned to track edges:
245
+
246
+ ```tsx
247
+ import { TrimHandles } from "@editframe/react";
248
+
249
+ <div className="relative h-16 bg-gray-800">
250
+ <TrimHandles
251
+ mode="track"
252
+ elementId="video-1"
253
+ value={{ startMs: 1000, endMs: 2000 }}
254
+ intrinsicDurationMs={10000}
255
+ pixelsPerMs={0.04}
256
+ />
257
+ </div>
258
+ ```
259
+ <!-- /react-only -->
260
+
261
+ <!-- react-only -->
262
+ ## Standalone Mode
263
+
264
+ Handles positioned inline with trim values:
265
+
266
+ ```tsx
267
+ <TrimHandles
268
+ mode="standalone"
269
+ elementId="video-1"
270
+ value={{ startMs: 2000, endMs: 3000 }}
271
+ intrinsicDurationMs={10000}
272
+ pixelsPerMs={0.08}
273
+ />
274
+ ```
275
+
276
+ ## Hide Overlays
277
+
278
+ Show handles without dimmed regions:
279
+
280
+ ```tsx
281
+ <TrimHandles
282
+ elementId="video-1"
283
+ value={{ startMs: 1000, endMs: 1000 }}
284
+ intrinsicDurationMs={10000}
285
+ showOverlays={false}
286
+ />
287
+ ```
288
+
289
+ ## With Seek Target
290
+
291
+ Automatically seek video when dragging:
292
+
293
+ ```tsx
294
+ import { useState } from "react";
295
+ import { TrimHandles, Video } from "@editframe/react";
296
+
297
+ export const SeekingTrimExample = () => {
298
+ const [trim, setTrim] = useState({ startMs: 0, endMs: 0 });
299
+
300
+ return (
301
+ <div>
302
+ <Video
303
+ id="preview-video"
304
+ src="/video.mp4"
305
+ className="w-full aspect-video mb-4"
306
+ />
307
+
308
+ <div className="relative h-16 bg-gray-800">
309
+ <TrimHandles
310
+ elementId="video-1"
311
+ seekTarget="preview-video"
312
+ value={trim}
313
+ intrinsicDurationMs={10000}
314
+ onTrimChange={(e) => setTrim(e.detail.value)}
315
+ />
316
+ </div>
317
+ </div>
318
+ );
319
+ };
320
+ ```
321
+
322
+ ## Trim Both Ends
323
+
324
+ Adjust start and end simultaneously:
325
+
326
+ ```tsx
327
+ import { useState } from "react";
328
+ import { TrimHandles } from "@editframe/react";
329
+
330
+ export const DualTrimExample = () => {
331
+ const [trim, setTrim] = useState({ startMs: 2000, endMs: 3000 });
332
+
333
+ return (
334
+ <div>
335
+ <div className="mb-2 text-sm font-mono">
336
+ Trimmed: {trim.startMs}ms to {10000 - trim.endMs}ms
337
+ </div>
338
+
339
+ <div className="relative h-16 bg-gray-800 rounded">
340
+ <TrimHandles
341
+ elementId="video-1"
342
+ value={trim}
343
+ intrinsicDurationMs={10000}
344
+ pixelsPerMs={0.08}
345
+ onTrimChange={(e) => setTrim(e.detail.value)}
346
+ />
347
+ </div>
348
+ </div>
349
+ );
350
+ };
351
+ ```
352
+ <!-- /react-only -->
353
+
354
+ ## Region Dragging
355
+
356
+ <!-- html-only -->
357
+ In standalone mode, drag the region between handles to slide the trim window.
358
+
359
+ ```html live
360
+ <div class="space-y-4">
361
+ <ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black" id="region-demo">
362
+ <ef-video
363
+ src="https://assets.editframe.com/bars-n-tone.mp4"
364
+ sourcein="2s"
365
+ sourceout="6s"
366
+ class="size-full object-contain"
367
+ id="region-video"
368
+ ></ef-video>
369
+ </ef-timegroup>
370
+
371
+ <div class="p-4 bg-gray-900 rounded-lg">
372
+ <div class="text-white text-sm mb-2">Drag region to slide trim window:</div>
373
+ <div class="h-[60px] relative bg-gray-800 rounded-lg">
374
+ <ef-trim-handles
375
+ element-id="region-video"
376
+ intrinsic-duration-ms="10000"
377
+ seek-target="region-demo"
378
+ ></ef-trim-handles>
379
+ </div>
380
+ <ef-controls target="region-demo" class="flex gap-2 mt-3">
381
+ <ef-toggle-play class="px-3 py-1 bg-blue-600 text-white rounded"></ef-toggle-play>
382
+ <ef-time-display class="text-white font-mono"></ef-time-display>
383
+ </ef-controls>
384
+ </div>
385
+ </div>
386
+
387
+ <script>
388
+ const regionHandles = document.querySelectorAll('ef-trim-handles')[2];
389
+ const regionVideo = document.getElementById('region-video');
390
+
391
+ if (regionHandles && regionVideo) {
392
+ regionHandles.value = { startMs: 2000, endMs: 4000 };
393
+
394
+ regionHandles.addEventListener('trim-change', (e) => {
395
+ const { value, type } = e.detail;
396
+
397
+ if (type === 'region') {
398
+ // Region drag - update sourcein/sourceout instead of trim
399
+ const duration = 10000 - value.startMs - value.endMs;
400
+ regionVideo.setAttribute('sourcein', `${value.startMs}ms`);
401
+ regionVideo.setAttribute('sourceout', `${value.startMs + duration}ms`);
402
+ }
403
+ });
404
+ }
405
+ </script>
406
+ ```
407
+ <!-- /html-only -->
408
+ <!-- react-only -->
409
+ Drag the region between handles to move both:
410
+
411
+ ```tsx
412
+ import { useState } from "react";
413
+ import { TrimHandles } from "@editframe/react";
414
+
415
+ export const RegionDragExample = () => {
416
+ const [trim, setTrim] = useState({ startMs: 2000, endMs: 2000 });
417
+
418
+ const handleTrimChange = (e: CustomEvent<TrimChangeDetail>) => {
419
+ console.log("Trim type:", e.detail.type); // "start", "end", or "region"
420
+ setTrim(e.detail.value);
421
+ };
422
+
423
+ return (
424
+ <div className="relative h-16 bg-gray-800">
425
+ <TrimHandles
426
+ mode="standalone"
427
+ elementId="video-1"
428
+ value={trim}
429
+ intrinsicDurationMs={10000}
430
+ pixelsPerMs={0.08}
431
+ onTrimChange={handleTrimChange}
432
+ />
433
+ </div>
434
+ );
435
+ };
436
+ ```
437
+ <!-- /react-only -->
438
+
439
+ ## Custom Styling
440
+
441
+ <!-- html-only -->
442
+ Style handle appearance with CSS custom properties.
443
+
444
+ ```html live
445
+ <div class="space-y-4">
446
+ <ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black" id="styled-demo">
447
+ <ef-video
448
+ src="https://assets.editframe.com/bars-n-tone.mp4"
449
+ class="size-full object-contain"
450
+ id="styled-video"
451
+ ></ef-video>
452
+ </ef-timegroup>
453
+
454
+ <div class="p-4 bg-gray-900 rounded-lg">
455
+ <div class="text-white text-sm mb-2">Custom styled handles:</div>
456
+ <div class="h-[60px] relative bg-gray-800 rounded-lg">
457
+ <ef-trim-handles
458
+ id="styled-handles"
459
+ element-id="styled-video"
460
+ intrinsic-duration-ms="10000"
461
+ ></ef-trim-handles>
462
+ </div>
463
+ </div>
464
+ </div>
465
+
466
+ <style>
467
+ #styled-handles {
468
+ --trim-handle-width: 12px;
469
+ --trim-handle-color: rgba(139, 92, 246, 0.8);
470
+ --trim-handle-active-color: #8b5cf6;
471
+ --trim-overlay-color: rgba(0, 0, 0, 0.6);
472
+ }
473
+ </style>
474
+
475
+ <script>
476
+ const styledHandles = document.getElementById('styled-handles');
477
+ const styledVideo = document.getElementById('styled-video');
478
+
479
+ styledHandles.addEventListener('trim-change', (e) => {
480
+ const { value } = e.detail;
481
+ styledVideo.setAttribute('trimstart', `${value.startMs}ms`);
482
+ styledVideo.setAttribute('trimend', `${value.endMs}ms`);
483
+ });
484
+ </script>
485
+ ```
486
+ <!-- /html-only -->
487
+ <!-- react-only -->
488
+ Customize handle appearance:
489
+
490
+ ```tsx
491
+ <TrimHandles
492
+ elementId="video-1"
493
+ value={{ startMs: 1000, endMs: 1000 }}
494
+ intrinsicDurationMs={10000}
495
+ className="custom-trim"
496
+ style={{
497
+ '--trim-handle-width': '12px',
498
+ '--trim-handle-color': 'rgba(59, 130, 246, 0.8)',
499
+ '--trim-handle-active-color': '#3b82f6',
500
+ '--trim-overlay-color': 'rgba(0, 0, 0, 0.6)',
501
+ } as React.CSSProperties}
502
+ />
503
+ ```
504
+ <!-- /react-only -->
505
+
506
+ ## Trim Modes
507
+
508
+ **Standalone mode** (`mode="standalone"`, default):
509
+ - Handles positioned absolutely based on trim values
510
+ - Shows draggable region between handles
511
+ - Best for full-width trim UI
512
+
513
+ **Track mode** (`mode="track"`):
514
+ - Handles pinned to container edges
515
+ - No draggable region
516
+ - Best for timeline track integration
517
+
518
+ ## Event Handling
519
+
520
+ <!-- html-only -->
521
+ ### trim-change Event
522
+
523
+ Fired continuously during drag operations.
524
+
525
+ ```typescript
526
+ interface TrimChangeDetail {
527
+ elementId: string; // Element being trimmed
528
+ type: 'start' | 'end' | 'region'; // Which handle/region moved
529
+ value: { // New trim values
530
+ startMs: number;
531
+ endMs: number;
532
+ };
533
+ }
534
+ ```
535
+
536
+ ### trim-change-end Event
537
+
538
+ Fired when drag completes (on pointer up).
539
+
540
+ ```typescript
541
+ interface TrimChangeEndDetail {
542
+ elementId: string;
543
+ type: 'start' | 'end' | 'region';
544
+ }
545
+ ```
546
+ <!-- /html-only -->
547
+ <!-- react-only -->
548
+ Handle trim events:
549
+
550
+ ```tsx
551
+ import { useState } from "react";
552
+ import { TrimHandles } from "@editframe/react";
553
+
554
+ export const TrimEventsExample = () => {
555
+ const [trim, setTrim] = useState({ startMs: 0, endMs: 0 });
556
+ const [isDragging, setIsDragging] = useState(false);
557
+
558
+ return (
559
+ <div>
560
+ <div className="mb-2 text-sm">
561
+ {isDragging ? "Dragging..." : "Idle"}
562
+ </div>
563
+
564
+ <div className="relative h-16 bg-gray-800">
565
+ <TrimHandles
566
+ elementId="video-1"
567
+ value={trim}
568
+ intrinsicDurationMs={10000}
569
+ onTrimChange={(e) => {
570
+ setTrim(e.detail.value);
571
+ setIsDragging(true);
572
+ }}
573
+ onTrimChangeEnd={(e) => {
574
+ console.log("Final trim:", e.detail.value);
575
+ setIsDragging(false);
576
+ }}
577
+ />
578
+ </div>
579
+ </div>
580
+ );
581
+ };
582
+ ```
583
+ <!-- /react-only -->
584
+
585
+ ## Trim Value Semantics
586
+
587
+ Trim values represent time trimmed from source media:
588
+ - `startMs` — Time trimmed from start
589
+ - `endMs` — Time trimmed from end
590
+ - Effective duration = `intrinsicDurationMs - startMs - endMs`
591
+
592
+ Example with 10 second source:
593
+ - `{startMs: 2000, endMs: 3000}` = play seconds 2-7 (5 second duration)
594
+ - `{startMs: 0, endMs: 0}` = play entire source (10 second duration)
595
+
596
+ ## Seeking During Trim
597
+
598
+ <!-- html-only -->
599
+ When `seek-target` is set, the element seeks during trim operations:
600
+ <!-- /html-only -->
601
+ <!-- react-only -->
602
+ When `seekTarget` is set, the element seeks during trim operations:
603
+ <!-- /react-only -->
604
+ - Dragging start handle — seeks to trimmed start (0:00 of visible region)
605
+ - Dragging end handle — seeks to trimmed end (last frame of visible region)
606
+ - Dragging region — maintains relative position
607
+
608
+ <!-- react-only -->
609
+ ## Controlled Trim
610
+
611
+ Full control over trim state:
612
+
613
+ ```tsx
614
+ import { useState } from "react";
615
+ import { TrimHandles, Video } from "@editframe/react";
616
+
617
+ export const ControlledTrimExample = () => {
618
+ const [trim, setTrim] = useState({ startMs: 0, endMs: 0 });
619
+ const intrinsicDuration = 10000;
620
+
621
+ const effectiveDuration = intrinsicDuration - trim.startMs - trim.endMs;
622
+
623
+ return (
624
+ <div>
625
+ <div className="flex gap-4 mb-4">
626
+ <div>
627
+ <label className="text-sm">Trim Start (ms)</label>
628
+ <input
629
+ type="number"
630
+ value={trim.startMs}
631
+ onChange={(e) => setTrim({ ...trim, startMs: Number(e.target.value) })}
632
+ className="w-full p-2 bg-gray-800 rounded"
633
+ />
634
+ </div>
635
+
636
+ <div>
637
+ <label className="text-sm">Trim End (ms)</label>
638
+ <input
639
+ type="number"
640
+ value={trim.endMs}
641
+ onChange={(e) => setTrim({ ...trim, endMs: Number(e.target.value) })}
642
+ className="w-full p-2 bg-gray-800 rounded"
643
+ />
644
+ </div>
645
+
646
+ <div className="text-sm">
647
+ <div>Effective Duration: {effectiveDuration}ms</div>
648
+ </div>
649
+ </div>
650
+
651
+ <div className="relative h-16 bg-gray-800 rounded mb-4">
652
+ <TrimHandles
653
+ elementId="video-1"
654
+ value={trim}
655
+ intrinsicDurationMs={intrinsicDuration}
656
+ pixelsPerMs={0.08}
657
+ onTrimChange={(e) => setTrim(e.detail.value)}
658
+ />
659
+ </div>
660
+
661
+ <Video
662
+ id="video-1"
663
+ src="/video.mp4"
664
+ trimStart={`${trim.startMs}ms`}
665
+ trimEnd={`${trim.endMs}ms`}
666
+ className="w-full aspect-video"
667
+ />
668
+ </div>
669
+ );
670
+ };
671
+ ```
672
+
673
+ ## Responsive Handles
674
+
675
+ Handles adapt to container width:
676
+
677
+ ```tsx
678
+ <div className="w-full max-w-4xl mx-auto">
679
+ <TrimHandles
680
+ elementId="video-1"
681
+ value={{ startMs: 1000, endMs: 1000 }}
682
+ intrinsicDurationMs={10000}
683
+ className="h-16"
684
+ />
685
+ </div>
686
+ ```
687
+ <!-- /react-only -->
688
+
689
+ ## CSS Custom Properties
690
+
691
+ Customize handle appearance:
692
+
693
+ ```css
694
+ ef-trim-handles {
695
+ --trim-handle-width: 8px;
696
+ --trim-handle-color: rgba(255, 255, 255, 0.7);
697
+ --trim-handle-active-color: #3b82f6;
698
+ --trim-overlay-color: rgba(0, 0, 0, 0.4);
699
+ --trim-handle-border-radius-start: 2px 0 0 2px;
700
+ --trim-handle-border-radius-end: 0 2px 2px 0;
701
+ --trim-selected-border-width: 0px;
702
+ --trim-selected-border-color: transparent;
703
+ }
704
+ ```
705
+
706
+ ## Notes
707
+
708
+ - TrimHandles work in two modes: standalone (inline positioning) or track (pinned to edges)
709
+ - Handles constrain dragging to valid ranges
710
+ - Region between handles can be dragged to move both simultaneously
711
+ - Overlays show trimmed (non-visible) regions
712
+ <!-- html-only -->
713
+ - Use `seek-target` to update video preview while dragging
714
+ <!-- /html-only -->
715
+ <!-- react-only -->
716
+ - Use `seekTarget` to update video preview while dragging
717
+ <!-- /react-only -->
718
+ - Event types: "start", "end", or "region" indicate which handle was dragged
719
+ <!-- html-only -->
720
+ - Automatically accounts for zoom level via `pixels-per-ms`
721
+ <!-- /html-only -->
722
+ <!-- react-only -->
723
+ - Automatically accounts for zoom level via `pixelsPerMs`
724
+ <!-- /react-only -->
725
+ - CSS custom properties allow extensive styling customization