@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,772 @@
1
+ ---
2
+ title: Fit Scale Element
3
+ description: Responsive wrapper that scales its content uniformly to fill available space while preserving the original aspect ratio.
4
+ type: reference
5
+ nav:
6
+ parent: "Preview & Canvas"
7
+ priority: 21
8
+ api:
9
+ attributes:
10
+ - name: paused
11
+ type: boolean
12
+ default: false
13
+ description: Pause scale calculations (useful during animations)
14
+ properties:
15
+ - name: contentChild
16
+ type: "HTMLElement | null"
17
+ description: First content element (excludes style, script, display:none)
18
+ - name: scaleInfo
19
+ type: "{ scale: number, containerWidth: number, containerHeight: number, contentWidth: number, contentHeight: number }"
20
+ description: Current scale calculation result
21
+ functions:
22
+ - name: computeFitScale(input)
23
+ signature: "computeFitScale(input: ScaleInput): ScaleOutput | null"
24
+ description: Pure function to compute scale and centering for given dimensions
25
+ returns: ScaleOutput | null
26
+ types:
27
+ - name: ScaleInput
28
+ type: interface
29
+ definition: |
30
+ interface ScaleInput {
31
+ containerWidth: number; // Container dimensions
32
+ containerHeight: number;
33
+ contentWidth: number; // Content natural dimensions
34
+ contentHeight: number;
35
+ }
36
+ - name: ScaleOutput
37
+ type: interface
38
+ definition: |
39
+ interface ScaleOutput {
40
+ scale: number; // Scale factor to apply
41
+ translateX: number; // X offset for centering
42
+ translateY: number; // Y offset for centering
43
+ }
44
+ react:
45
+ generate: true
46
+ componentName: FitScale
47
+ importPath: "@editframe/react"
48
+ additionalProps:
49
+ - name: children
50
+ type: ReactNode
51
+ required: true
52
+ description: Content to scale and fit
53
+ nav:
54
+ parent: "Components / Layout"
55
+ priority: 56
56
+ related: ["preview", "timegroup"]
57
+ ---
58
+
59
+ <!-- html-only -->
60
+ # ef-fit-scale
61
+ <!-- /html-only -->
62
+ <!-- react-only -->
63
+ # FitScale
64
+ <!-- /react-only -->
65
+
66
+ Responsive container that scales content to fit while preserving aspect ratio.
67
+
68
+ <!-- react-only -->
69
+ ## Import
70
+
71
+ ```tsx
72
+ import { FitScale } from "@editframe/react";
73
+ import { computeFitScale, needsFitScale } from "@editframe/react";
74
+ ```
75
+ <!-- /react-only -->
76
+
77
+ ## Basic Usage
78
+
79
+ <!-- html-only -->
80
+ Scale content to fit container:
81
+
82
+ ```html live
83
+ <div class="w-full h-[400px] border border-gray-300 rounded overflow-hidden bg-gray-100">
84
+ <ef-fit-scale class="w-full h-full">
85
+ <div class="w-[1920px] h-[1080px] bg-blue-500 text-white flex items-center justify-center text-4xl">
86
+ 1920×1080 Content
87
+ </div>
88
+ </ef-fit-scale>
89
+ </div>
90
+ ```
91
+
92
+ Content scales down to fit the container while maintaining its 16:9 aspect ratio.
93
+ <!-- /html-only -->
94
+ <!-- react-only -->
95
+ ```tsx
96
+ import { FitScale, Timegroup, Video } from "@editframe/react";
97
+
98
+ export const App = () => {
99
+ return (
100
+ <div className="w-full h-screen bg-gray-900">
101
+ <FitScale>
102
+ <Timegroup mode="contain" className="w-[1920px] h-[1080px]">
103
+ <Video src="/assets/video.mp4" className="size-full" />
104
+ </Timegroup>
105
+ </FitScale>
106
+ </div>
107
+ );
108
+ };
109
+ ```
110
+ <!-- /react-only -->
111
+
112
+ ## How It Works
113
+
114
+ Fit scale automatically:
115
+
116
+ 1. Observes container size
117
+ 2. Reads content natural dimensions
118
+ 3. Calculates scale factor to fit
119
+ 4. Centers content with translate
120
+ 5. Applies transform to content
121
+
122
+ ## Content Detection
123
+
124
+ Fit scale finds the first content element, ignoring:
125
+
126
+ - `<style>` tags
127
+ - `<script>` tags
128
+ - `<meta>` tags
129
+ - Elements with `display: none`
130
+ - Elements with `display: contents`
131
+
132
+ <!-- html-only -->
133
+ ```html
134
+ <ef-fit-scale>
135
+ <style>/* Ignored */</style>
136
+ <script>/* Ignored */</script>
137
+ <div><!-- This is the content --></div>
138
+ </ef-fit-scale>
139
+ ```
140
+ <!-- /html-only -->
141
+
142
+ <!-- html-only -->
143
+ ## Natural Dimensions
144
+
145
+ For media elements (ef-video, ef-image), fit scale uses natural dimensions:
146
+
147
+ ```html
148
+ <ef-fit-scale class="w-full h-[400px]">
149
+ <ef-video src="https://assets.editframe.com/bars-n-tone.mp4"></ef-video>
150
+ </ef-fit-scale>
151
+ ```
152
+
153
+ Video scales based on its native resolution, not CSS dimensions.
154
+
155
+ ### getNaturalDimensions Method
156
+
157
+ Media elements can provide natural dimensions:
158
+
159
+ ```javascript
160
+ class MyMediaElement extends HTMLElement {
161
+ getNaturalDimensions() {
162
+ return { width: 1920, height: 1080 };
163
+ }
164
+ }
165
+ ```
166
+
167
+ Fit scale calls this method if available.
168
+ <!-- /html-only -->
169
+
170
+ ## Responsive Scaling
171
+
172
+ Fit scale updates automatically when:
173
+
174
+ - Container resizes
175
+ - Content resizes
176
+ - Content child changes
177
+
178
+ <!-- html-only -->
179
+ ```html live
180
+ <div class="flex flex-col gap-4">
181
+ <div class="w-full h-[300px] border border-gray-300 rounded overflow-hidden bg-gray-100">
182
+ <ef-fit-scale class="w-full h-full">
183
+ <div class="w-[800px] h-[600px] bg-green-500 text-white flex items-center justify-center text-2xl">
184
+ 800×600 (4:3)
185
+ </div>
186
+ </ef-fit-scale>
187
+ </div>
188
+
189
+ <div class="w-full h-[300px] border border-gray-300 rounded overflow-hidden bg-gray-100">
190
+ <ef-fit-scale class="w-full h-full">
191
+ <div class="w-[1920px] h-[1080px] bg-blue-500 text-white flex items-center justify-center text-2xl">
192
+ 1920×1080 (16:9)
193
+ </div>
194
+ </ef-fit-scale>
195
+ </div>
196
+ </div>
197
+ ```
198
+
199
+ Different aspect ratios scale correctly.
200
+ <!-- /html-only -->
201
+ <!-- react-only -->
202
+ ```tsx
203
+ import { FitScale, Preview, Timegroup, Video } from "@editframe/react";
204
+
205
+ export const ResponsivePreview = () => {
206
+ return (
207
+ <div className="w-full h-screen p-4">
208
+ <FitScale>
209
+ <div className="w-[1920px] h-[1080px] bg-black">
210
+ <Timegroup mode="contain" className="size-full">
211
+ <Video src="/assets/video.mp4" className="size-full object-cover" />
212
+ </Timegroup>
213
+ </div>
214
+ </FitScale>
215
+ </div>
216
+ );
217
+ };
218
+ ```
219
+ <!-- /react-only -->
220
+
221
+ <!-- react-only -->
222
+ ## With Fixed Aspect Ratio
223
+
224
+ ```tsx
225
+ import { FitScale } from "@editframe/react";
226
+
227
+ export const AspectRatioBox = () => {
228
+ return (
229
+ <div className="w-full h-screen bg-gray-100 p-8">
230
+ <FitScale>
231
+ <div className="w-[1280px] h-[720px] bg-gradient-to-br from-blue-500 to-purple-600">
232
+ <div className="flex items-center justify-center h-full text-white text-4xl">
233
+ 16:9 Content
234
+ </div>
235
+ </div>
236
+ </FitScale>
237
+ </div>
238
+ );
239
+ };
240
+ ```
241
+
242
+ ## Multiple Scaled Containers
243
+
244
+ ```tsx
245
+ import { FitScale, Timegroup, Video } from "@editframe/react";
246
+
247
+ export const MultipleScaled = () => {
248
+ return (
249
+ <div className="grid grid-cols-2 gap-4 p-4 h-screen">
250
+ {/* Left: 16:9 composition */}
251
+ <div className="bg-gray-900">
252
+ <FitScale>
253
+ <Timegroup mode="contain" className="w-[1920px] h-[1080px]">
254
+ <Video src="/assets/video1.mp4" className="size-full" />
255
+ </Timegroup>
256
+ </FitScale>
257
+ </div>
258
+
259
+ {/* Right: 1:1 composition */}
260
+ <div className="bg-gray-900">
261
+ <FitScale>
262
+ <Timegroup mode="contain" className="w-[1080px] h-[1080px]">
263
+ <Video src="/assets/video2.mp4" className="size-full object-cover" />
264
+ </Timegroup>
265
+ </FitScale>
266
+ </div>
267
+ </div>
268
+ );
269
+ };
270
+ ```
271
+ <!-- /react-only -->
272
+
273
+ ## Scale Calculation
274
+
275
+ Fit scale uses contain logic (like `object-fit: contain`):
276
+
277
+ <!-- html-only -->
278
+ ```javascript
279
+ import { computeFitScale } from '@editframe/elements';
280
+
281
+ const result = computeFitScale({
282
+ containerWidth: 800,
283
+ containerHeight: 600,
284
+ contentWidth: 1920,
285
+ contentHeight: 1080
286
+ });
287
+
288
+ // result = {
289
+ // scale: 0.4166, // Scale down to fit
290
+ // translateX: 0, // Centered horizontally
291
+ // translateY: 75 // Centered vertically
292
+ // }
293
+ ```
294
+
295
+ ### Scale Formula
296
+
297
+ ```javascript
298
+ const containerRatio = containerWidth / containerHeight;
299
+ const contentRatio = contentWidth / contentHeight;
300
+
301
+ const scale = containerRatio > contentRatio
302
+ ? containerHeight / contentHeight // Limited by height
303
+ : containerWidth / contentWidth; // Limited by width
304
+ ```
305
+ <!-- /html-only -->
306
+ <!-- react-only -->
307
+ ```tsx
308
+ import { computeFitScale } from "@editframe/react";
309
+
310
+ export const ManualScaleCalculation = () => {
311
+ const containerRef = useRef<HTMLDivElement>(null);
312
+ const [scale, setScale] = useState<number>(1);
313
+
314
+ useEffect(() => {
315
+ if (!containerRef.current) return;
316
+
317
+ const updateScale = () => {
318
+ const result = computeFitScale({
319
+ containerWidth: containerRef.current!.clientWidth,
320
+ containerHeight: containerRef.current!.clientHeight,
321
+ contentWidth: 1920,
322
+ contentHeight: 1080
323
+ });
324
+
325
+ if (result) {
326
+ setScale(result.scale);
327
+ }
328
+ };
329
+
330
+ updateScale();
331
+ window.addEventListener("resize", updateScale);
332
+ return () => window.removeEventListener("resize", updateScale);
333
+ }, []);
334
+
335
+ return (
336
+ <div ref={containerRef} className="w-full h-screen bg-gray-100 p-4">
337
+ <div className="absolute top-4 left-4 bg-white p-2 rounded shadow">
338
+ Scale: {scale.toFixed(3)}x
339
+ </div>
340
+
341
+ <div
342
+ className="w-[1920px] h-[1080px] bg-blue-500"
343
+ style={{ transform: `scale(${scale})` }}
344
+ />
345
+ </div>
346
+ );
347
+ };
348
+ ```
349
+
350
+ The `computeFitScale` function uses this algorithm:
351
+
352
+ 1. Calculate container aspect ratio: `containerWidth / containerHeight`
353
+ 2. Calculate content aspect ratio: `contentWidth / contentHeight`
354
+ 3. Determine scale based on which dimension is limiting:
355
+ - If container is wider: scale by height
356
+ - If container is taller: scale by width
357
+ 4. Calculate centering translations to position content in center
358
+ 5. Return `null` if any dimension is zero or negative
359
+ <!-- /react-only -->
360
+
361
+ ## Centering
362
+
363
+ Content is centered after scaling:
364
+
365
+ ```javascript
366
+ const scaledWidth = contentWidth * scale;
367
+ const scaledHeight = contentHeight * scale;
368
+ const translateX = (containerWidth - scaledWidth) / 2;
369
+ const translateY = (containerHeight - scaledHeight) / 2;
370
+ ```
371
+
372
+ ## Applied Transform
373
+
374
+ Fit scale applies transform to content child:
375
+
376
+ ```css
377
+ .content {
378
+ width: 1920px;
379
+ height: 1080px;
380
+ transform: translate(0px, 75px) scale(0.4166);
381
+ transform-origin: top left;
382
+ }
383
+ ```
384
+
385
+ Transform origin is top-left for correct positioning.
386
+
387
+ ## Pause Calculations
388
+
389
+ Pause scale updates during animations:
390
+
391
+ <!-- html-only -->
392
+ ```html
393
+ <ef-fit-scale paused>
394
+ <!-- Scale calculations paused -->
395
+ </ef-fit-scale>
396
+ ```
397
+
398
+ ```javascript
399
+ const fitScale = document.querySelector('ef-fit-scale');
400
+
401
+ // Pause
402
+ fitScale.paused = true;
403
+
404
+ // Resume
405
+ fitScale.paused = false; // Recalculates immediately
406
+ ```
407
+ <!-- /html-only -->
408
+ <!-- react-only -->
409
+ ```tsx
410
+ import { FitScale } from "@editframe/react";
411
+
412
+ export const PausedScale = () => {
413
+ const [isPaused, setIsPaused] = useState(false);
414
+
415
+ return (
416
+ <div className="space-y-4">
417
+ <div className="flex items-center gap-2">
418
+ <input
419
+ type="checkbox"
420
+ checked={isPaused}
421
+ onChange={(e) => setIsPaused(e.target.checked)}
422
+ id="pause-scale"
423
+ />
424
+ <label htmlFor="pause-scale">Pause scaling</label>
425
+ </div>
426
+
427
+ <div className="w-full h-96 bg-gray-100">
428
+ <FitScale paused={isPaused}>
429
+ <div className="w-[800px] h-[600px] bg-blue-500 text-white flex items-center justify-center text-2xl">
430
+ Resize window to see scaling
431
+ </div>
432
+ </FitScale>
433
+ </div>
434
+ </div>
435
+ );
436
+ };
437
+ ```
438
+ <!-- /react-only -->
439
+
440
+ <!-- html-only -->
441
+ ## Zero Dimension Warning
442
+
443
+ Fit scale warns when container has zero dimensions:
444
+
445
+ ```javascript
446
+ // Console warning if container is 0×0
447
+ // "Container has zero dimensions (0×0). Content will be invisible."
448
+ ```
449
+
450
+ Ensure all ancestors have resolved height:
451
+
452
+ ```css
453
+ html, body {
454
+ height: 100%;
455
+ }
456
+ ```
457
+
458
+ ## Scale Info
459
+
460
+ Access current scale calculation:
461
+
462
+ ```javascript
463
+ const fitScale = document.querySelector('ef-fit-scale');
464
+
465
+ console.log(fitScale.scaleInfo);
466
+ // {
467
+ // scale: 0.4166,
468
+ // containerWidth: 800,
469
+ // containerHeight: 600,
470
+ // contentWidth: 1920,
471
+ // contentHeight: 1080
472
+ // }
473
+ ```
474
+
475
+ ## Content Child
476
+
477
+ Access the content element:
478
+
479
+ ```javascript
480
+ const fitScale = document.querySelector('ef-fit-scale');
481
+ const content = fitScale.contentChild;
482
+
483
+ console.log(content.tagName); // 'EF-VIDEO'
484
+ ```
485
+ <!-- /html-only -->
486
+
487
+ ## Observers
488
+
489
+ Fit scale uses three observers:
490
+
491
+ 1. **Container ResizeObserver**: Tracks container size
492
+ 2. **Content ResizeObserver**: Tracks content size
493
+ 3. **MutationObserver**: Tracks child list changes
494
+
495
+ All observers clean up on disconnect.
496
+
497
+ <!-- html-only -->
498
+ ## Video Integration
499
+
500
+ For ef-video elements, fit scale sets explicit canvas dimensions:
501
+
502
+ ```javascript
503
+ // Breaks circular dependency
504
+ canvas.style.width = `${naturalWidth}px`;
505
+ canvas.style.height = `${naturalHeight}px`;
506
+ ```
507
+
508
+ This prevents video from collapsing to 0×0 when using `width: auto`.
509
+ <!-- /html-only -->
510
+
511
+ ## Layout Properties
512
+
513
+ Fit scale applies container styles:
514
+
515
+ ```css
516
+ ef-fit-scale {
517
+ display: grid;
518
+ width: 100%;
519
+ height: 100%;
520
+ grid-template-columns: 100%;
521
+ grid-template-rows: 100%;
522
+ overflow: hidden;
523
+ box-sizing: border-box;
524
+ contain: layout paint style;
525
+ position: relative;
526
+ }
527
+ ```
528
+
529
+ Grid layout simplifies content positioning.
530
+
531
+ ## Performance
532
+
533
+ Scale calculations use:
534
+
535
+ - **Fixed precision**: Values rounded to 4 decimal places
536
+ - **Change detection**: Only updates when values change
537
+ - **RAF deferral**: Initial calculation deferred to allow layout
538
+
539
+ <!-- html-only -->
540
+ ```javascript
541
+ // Internal precision handling
542
+ transform: `translate(${translateX.toFixed(4)}px, ${translateY.toFixed(4)}px) scale(${scale.toFixed(4)})`;
543
+ ```
544
+ <!-- /html-only -->
545
+
546
+ ## Cleanup
547
+
548
+ Fit scale removes transform when disconnected:
549
+
550
+ ```javascript
551
+ // On disconnect
552
+ content.style.width = '';
553
+ content.style.height = '';
554
+ content.style.transform = '';
555
+ content.style.transformOrigin = '';
556
+ ```
557
+
558
+ Content returns to normal layout.
559
+
560
+ ## Comparison with object-fit
561
+
562
+ **ef-fit-scale**:
563
+ - Works with any element
564
+ - JavaScript-based scaling
565
+ - Provides scale info
566
+ - Can be paused
567
+
568
+ **CSS object-fit**:
569
+ - Only works with replaced elements (img, video)
570
+ - CSS-based scaling
571
+ - No JavaScript API
572
+ - Always active
573
+
574
+ ## Nested Fit Scale
575
+
576
+ Multiple fit scales can nest:
577
+
578
+ <!-- html-only -->
579
+ ```html
580
+ <ef-fit-scale class="w-full h-full">
581
+ <ef-timegroup class="w-[1920px] h-[1080px]">
582
+ <ef-fit-scale class="w-full h-full">
583
+ <ef-video src="video.mp4"></ef-video>
584
+ </ef-fit-scale>
585
+ </ef-timegroup>
586
+ </ef-fit-scale>
587
+ ```
588
+ <!-- /html-only -->
589
+ <!-- react-only -->
590
+ ```tsx
591
+ <FitScale>
592
+ <Timegroup className="w-[1920px] h-[1080px]">
593
+ <FitScale>
594
+ <Video src="/assets/video.mp4" />
595
+ </FitScale>
596
+ </Timegroup>
597
+ </FitScale>
598
+ ```
599
+ <!-- /react-only -->
600
+
601
+ Each fit scale scales its own content independently.
602
+
603
+ <!-- react-only -->
604
+ ## Editor Canvas
605
+
606
+ ```tsx
607
+ import { FitScale, Timegroup, Video, Text } from "@editframe/react";
608
+
609
+ export const EditorCanvas = () => {
610
+ return (
611
+ <div className="h-screen flex flex-col bg-gray-900">
612
+ {/* Toolbar */}
613
+ <div className="bg-gray-800 p-4 border-b border-gray-700">
614
+ <h1 className="text-white text-xl">Video Editor</h1>
615
+ </div>
616
+
617
+ {/* Canvas area with fit scaling */}
618
+ <div className="flex-1 p-8">
619
+ <FitScale>
620
+ <Timegroup mode="fixed" duration="10s" className="w-[1920px] h-[1080px]">
621
+ <Video
622
+ src="/assets/background.mp4"
623
+ duration="10s"
624
+ className="size-full object-cover"
625
+ />
626
+ <Text
627
+ duration="5s"
628
+ className="absolute top-20 left-20 text-white text-4xl"
629
+ >
630
+ Video Title
631
+ </Text>
632
+ </Timegroup>
633
+ </FitScale>
634
+ </div>
635
+ </div>
636
+ );
637
+ };
638
+ ```
639
+
640
+ ## With Viewport Dimensions
641
+
642
+ ```tsx
643
+ import { FitScale } from "@editframe/react";
644
+
645
+ export const ViewportAware = () => {
646
+ const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
647
+
648
+ useEffect(() => {
649
+ const updateDimensions = () => {
650
+ setDimensions({
651
+ width: window.innerWidth,
652
+ height: window.innerHeight
653
+ });
654
+ };
655
+
656
+ updateDimensions();
657
+ window.addEventListener("resize", updateDimensions);
658
+ return () => window.removeEventListener("resize", updateDimensions);
659
+ }, []);
660
+
661
+ return (
662
+ <div className="w-full h-screen bg-gray-900 p-4">
663
+ <div className="absolute top-4 right-4 bg-black/50 text-white p-2 text-sm font-mono z-10">
664
+ Viewport: {dimensions.width} x {dimensions.height}
665
+ </div>
666
+
667
+ <FitScale>
668
+ <div className="w-[1920px] h-[1080px] bg-gradient-to-br from-purple-600 to-blue-600" />
669
+ </FitScale>
670
+ </div>
671
+ );
672
+ };
673
+ ```
674
+
675
+ ## Check if Element Needs Scaling
676
+
677
+ ```tsx
678
+ import { needsFitScale } from "@editframe/react";
679
+
680
+ export const ConditionalScale = () => {
681
+ const contentRef = useRef<HTMLDivElement>(null);
682
+ const [shouldScale, setShouldScale] = useState(false);
683
+
684
+ useEffect(() => {
685
+ if (contentRef.current) {
686
+ setShouldScale(needsFitScale(contentRef.current));
687
+ }
688
+ }, []);
689
+
690
+ return (
691
+ <div className="w-full h-screen bg-gray-100 p-4">
692
+ <div className="mb-4 p-2 bg-white rounded">
693
+ {shouldScale ? "Content needs scaling" : "Content fits naturally"}
694
+ </div>
695
+
696
+ {shouldScale ? (
697
+ <FitScale>
698
+ <div ref={contentRef} className="w-[1920px] h-[1080px] bg-blue-500" />
699
+ </FitScale>
700
+ ) : (
701
+ <div ref={contentRef} className="w-full h-full bg-blue-500" />
702
+ )}
703
+ </div>
704
+ );
705
+ };
706
+ ```
707
+
708
+ ## TypeScript Usage
709
+
710
+ ```tsx
711
+ import { FitScale, computeFitScale } from "@editframe/react";
712
+ import type { ScaleInput, ScaleOutput } from "@editframe/react";
713
+
714
+ export const TypedScaleCalculation = () => {
715
+ const calculateScale = (input: ScaleInput): ScaleOutput | null => {
716
+ return computeFitScale(input);
717
+ };
718
+
719
+ const result = calculateScale({
720
+ containerWidth: 1024,
721
+ containerHeight: 768,
722
+ contentWidth: 1920,
723
+ contentHeight: 1080
724
+ });
725
+
726
+ return (
727
+ <div className="p-4">
728
+ {result && (
729
+ <div className="space-y-2 font-mono text-sm">
730
+ <div>Scale: {result.scale.toFixed(3)}</div>
731
+ <div>Translate X: {result.translateX.toFixed(1)}px</div>
732
+ <div>Translate Y: {result.translateY.toFixed(1)}px</div>
733
+ </div>
734
+ )}
735
+ </div>
736
+ );
737
+ };
738
+ ```
739
+ <!-- /react-only -->
740
+
741
+ ## Behavior
742
+
743
+ - Automatically observes container and content size changes
744
+ - Recalculates scale on resize using ResizeObserver
745
+ - Centers content within container
746
+ - Maintains content aspect ratio
747
+ - Uses CSS transform for scaling (GPU-accelerated)
748
+ - Handles nested content elements
749
+ - Ignores non-visible elements (display: none, etc.)
750
+
751
+ ## Important Notes
752
+
753
+ <!-- html-only -->
754
+ - FitScale is a light DOM component (no shadow DOM)
755
+ - Content should have explicit dimensions (width/height)
756
+ - Works with any content, not just Editframe elements
757
+ - Scale updates automatically on window resize
758
+ - Use `paused` attribute to temporarily stop recalculation
759
+ - Returns `null` from `computeFitScale` for invalid dimensions
760
+ - Uses `contain: layout paint style` for performance
761
+ - Content is centered both horizontally and vertically
762
+ <!-- /html-only -->
763
+ <!-- react-only -->
764
+ - FitScale is a light DOM component (no shadow DOM)
765
+ - Content should have explicit dimensions (width/height)
766
+ - Works with any content, not just Editframe elements
767
+ - Scale updates automatically on window resize
768
+ - Use `paused` prop to temporarily stop recalculation
769
+ - Returns `null` from `computeFitScale` for invalid dimensions
770
+ - Uses `contain: layout paint style` for performance
771
+ - Content is centered both horizontally and vertically
772
+ <!-- /react-only -->