@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,749 @@
1
+ ---
2
+ title: Resizable Box Element
3
+ description: Resizable box component with draggable edge and corner handles, supporting configurable minimum and maximum size constraints.
4
+ type: reference
5
+ nav:
6
+ parent: "Transform & Manipulation"
7
+ priority: 11
8
+ api:
9
+ attributes:
10
+ - name: bounds
11
+ type: BoxBounds
12
+ required: true
13
+ description: Bounding box dimensions and position
14
+ - name: min-size
15
+ type: number
16
+ default: 10
17
+ description: Minimum width and height during resize
18
+ events:
19
+ - name: bounds-change
20
+ detail: "{ bounds: BoxBounds }"
21
+ description: Fired during resize or move operations
22
+ types:
23
+ - name: BoxBounds
24
+ type: interface
25
+ definition: |
26
+ interface BoxBounds {
27
+ x: number; // Left position
28
+ y: number; // Top position
29
+ width: number; // Width
30
+ height: number; // Height
31
+ }
32
+ react:
33
+ generate: true
34
+ componentName: ResizableBox
35
+ importPath: "@editframe/react"
36
+ propMapping:
37
+ min-size: minSize
38
+ bounds-change: onBoundsChange
39
+ additionalProps:
40
+ - name: className
41
+ type: string
42
+ description: CSS classes for styling
43
+ nav:
44
+ parent: "Components / Transform & Manipulation"
45
+ priority: 53
46
+ related: ["transform-handles", "dial"]
47
+ ---
48
+
49
+ <!-- html-only -->
50
+ # ef-resizable-box
51
+ <!-- /html-only -->
52
+ <!-- react-only -->
53
+ # ResizableBox
54
+ <!-- /react-only -->
55
+
56
+ Resizable container with drag handles for interactive resizing and positioning.
57
+
58
+ <!-- react-only -->
59
+ ## Import
60
+
61
+ ```tsx
62
+ import { ResizableBox } from "@editframe/react";
63
+ ```
64
+ <!-- /react-only -->
65
+
66
+ ## Basic Usage
67
+
68
+ <!-- html-only -->
69
+ Display a resizable box:
70
+
71
+ ```html live
72
+ <div class="relative w-[600px] h-[400px] border border-gray-300 rounded overflow-hidden bg-gray-50">
73
+ <ef-resizable-box
74
+ .bounds=${{ x: 50, y: 50, width: 200, height: 150 }}
75
+ >
76
+ <div class="flex items-center justify-center text-gray-600">
77
+ Drag handles to resize
78
+ </div>
79
+ </ef-resizable-box>
80
+ </div>
81
+ ```
82
+ <!-- /html-only -->
83
+ <!-- react-only -->
84
+ ```tsx
85
+ import { ResizableBox } from "@editframe/react";
86
+
87
+ export const App = () => {
88
+ const [bounds, setBounds] = useState({
89
+ x: 100,
90
+ y: 100,
91
+ width: 300,
92
+ height: 200
93
+ });
94
+
95
+ return (
96
+ <div className="relative w-full h-screen bg-gray-100">
97
+ <ResizableBox
98
+ bounds={bounds}
99
+ onBoundsChange={(e) => setBounds(e.detail)}
100
+ />
101
+ </div>
102
+ );
103
+ };
104
+ ```
105
+ <!-- /react-only -->
106
+
107
+ ## Bounds
108
+
109
+ <!-- html-only -->
110
+ Resizable box requires bounds relative to its container:
111
+
112
+ ```javascript
113
+ const box = document.querySelector('ef-resizable-box');
114
+
115
+ box.bounds = {
116
+ x: 100, // Left position
117
+ y: 100, // Top position
118
+ width: 200, // Width
119
+ height: 150 // Height
120
+ };
121
+ ```
122
+ <!-- /html-only -->
123
+ <!-- react-only -->
124
+ Bounds define the box position and size relative to its parent container:
125
+
126
+ ```typescript
127
+ interface BoxBounds {
128
+ x: number; // Left position
129
+ y: number; // Top position
130
+ width: number; // Box width
131
+ height: number; // Box height
132
+ }
133
+ ```
134
+ <!-- /react-only -->
135
+
136
+ ## Resize Handles
137
+
138
+ Eight handles for resizing:
139
+
140
+ ### Corner Handles
141
+
142
+ - **Northwest** (top-left)
143
+ - **Northeast** (top-right)
144
+ - **Southwest** (bottom-left)
145
+ - **Southeast** (bottom-right)
146
+
147
+ Corner handles resize both width and height.
148
+
149
+ ### Edge Handles
150
+
151
+ - **North** (top edge) - height only
152
+ - **East** (right edge) - width only
153
+ - **South** (bottom edge) - height only
154
+ - **West** (left edge) - width only
155
+
156
+ ## Drag to Move
157
+
158
+ <!-- html-only -->
159
+ Click and drag the box content (not handles) to move:
160
+
161
+ ```html live
162
+ <div class="relative w-[600px] h-[400px] border border-gray-300 rounded overflow-hidden bg-gray-50">
163
+ <ef-resizable-box
164
+ .bounds=${{ x: 100, y: 80, width: 180, height: 120 }}
165
+ >
166
+ <div class="flex items-center justify-center text-gray-600 cursor-grab active:cursor-grabbing">
167
+ Drag to move
168
+ </div>
169
+ </ef-resizable-box>
170
+ </div>
171
+ ```
172
+ <!-- /html-only -->
173
+ <!-- react-only -->
174
+ Click and drag the box interior to reposition. Visual feedback shows appropriate cursors on hover.
175
+ <!-- /react-only -->
176
+
177
+ ## Events
178
+
179
+ <!-- html-only -->
180
+ Listen for bounds changes:
181
+
182
+ ```javascript
183
+ const box = document.querySelector('ef-resizable-box');
184
+
185
+ box.addEventListener('bounds-change', (e) => {
186
+ const { bounds } = e.detail;
187
+ console.log('New bounds:', bounds);
188
+
189
+ // bounds = { x, y, width, height }
190
+ });
191
+ ```
192
+
193
+ Events fire during both resize and move operations.
194
+ <!-- /html-only -->
195
+ <!-- react-only -->
196
+ The `onBoundsChange` callback fires during both resize and move operations:
197
+
198
+ ```tsx
199
+ <ResizableBox
200
+ bounds={bounds}
201
+ onBoundsChange={(e) => {
202
+ const newBounds = e.detail;
203
+ console.log('New bounds:', newBounds);
204
+ setBounds(newBounds);
205
+ }}
206
+ />
207
+ ```
208
+ <!-- /react-only -->
209
+
210
+ ## Minimum Size
211
+
212
+ <!-- html-only -->
213
+ Set minimum dimensions during resize:
214
+
215
+ ```html
216
+ <ef-resizable-box
217
+ .bounds=${{ x: 50, y: 50, width: 200, height: 150 }}
218
+ min-size="50"
219
+ >
220
+ Cannot resize below 50x50
221
+ </ef-resizable-box>
222
+ ```
223
+ <!-- /html-only -->
224
+ <!-- react-only -->
225
+ ```tsx
226
+ import { ResizableBox } from "@editframe/react";
227
+
228
+ export const ConstrainedBox = () => {
229
+ const [bounds, setBounds] = useState({
230
+ x: 150,
231
+ y: 150,
232
+ width: 200,
233
+ height: 200
234
+ });
235
+
236
+ return (
237
+ <ResizableBox
238
+ bounds={bounds}
239
+ minSize={50} // Cannot resize smaller than 50x50
240
+ onBoundsChange={(e) => setBounds(e.detail)}
241
+ />
242
+ );
243
+ };
244
+ ```
245
+ <!-- /react-only -->
246
+
247
+ ## Container Constraints
248
+
249
+ Resizable box constrains to its parent container:
250
+
251
+ <!-- html-only -->
252
+ ```html
253
+ <div class="relative w-[400px] h-[300px] border">
254
+ <ef-resizable-box
255
+ .bounds=${{ x: 20, y: 20, width: 100, height: 80 }}
256
+ >
257
+ Cannot move or resize outside parent
258
+ </ef-resizable-box>
259
+ </div>
260
+ ```
261
+
262
+ Box cannot be moved or resized beyond container boundaries.
263
+ <!-- /html-only -->
264
+ <!-- react-only -->
265
+ Box automatically constrains to parent boundaries. It cannot be moved or resized beyond its positioned parent container.
266
+ <!-- /react-only -->
267
+
268
+ <!-- html-only -->
269
+ ## Visual Feedback
270
+
271
+ Box shows visual feedback during interaction:
272
+
273
+ ```html live
274
+ <div class="relative w-[600px] h-[400px] border border-gray-300 rounded overflow-hidden bg-gray-50">
275
+ <ef-resizable-box
276
+ .bounds=${{ x: 50, y: 50, width: 180, height: 120 }}
277
+ >
278
+ <div class="flex items-center justify-center">
279
+ Interactive Box
280
+ </div>
281
+ </ef-resizable-box>
282
+ </div>
283
+ ```
284
+
285
+ Border and background change during drag operations.
286
+
287
+ ## Programmatic Control
288
+
289
+ Update bounds programmatically:
290
+
291
+ ```javascript
292
+ const box = document.querySelector('ef-resizable-box');
293
+
294
+ // Animate size
295
+ let size = 100;
296
+ const interval = setInterval(() => {
297
+ size += 10;
298
+ if (size > 300) size = 100;
299
+
300
+ box.bounds = {
301
+ x: 50,
302
+ y: 50,
303
+ width: size,
304
+ height: size * 0.75
305
+ };
306
+ }, 100);
307
+ ```
308
+
309
+ ## Slotted Content
310
+
311
+ Add any content inside the box:
312
+
313
+ ```html
314
+ <ef-resizable-box .bounds=${{ x: 50, y: 50, width: 200, height: 150 }}>
315
+ <img src="image.jpg" class="w-full h-full object-cover">
316
+ </ef-resizable-box>
317
+ ```
318
+
319
+ Content fills the box dimensions automatically.
320
+ <!-- /html-only -->
321
+
322
+ <!-- react-only -->
323
+ ## Crop Region Selector
324
+
325
+ ```tsx
326
+ import { ResizableBox } from "@editframe/react";
327
+
328
+ export const CropSelector = () => {
329
+ const [cropBounds, setCropBounds] = useState({
330
+ x: 50,
331
+ y: 50,
332
+ width: 400,
333
+ height: 300
334
+ });
335
+
336
+ const handleCrop = () => {
337
+ console.log("Crop region:", cropBounds);
338
+ // Apply crop to video/image
339
+ };
340
+
341
+ return (
342
+ <div className="relative">
343
+ <img
344
+ src="/assets/image.jpg"
345
+ alt="Source"
346
+ className="w-full h-auto"
347
+ />
348
+
349
+ <div className="absolute inset-0">
350
+ <ResizableBox
351
+ bounds={cropBounds}
352
+ onBoundsChange={(e) => setCropBounds(e.detail)}
353
+ />
354
+ </div>
355
+
356
+ <button
357
+ onClick={handleCrop}
358
+ className="mt-4 px-4 py-2 bg-blue-500 text-white rounded"
359
+ >
360
+ Apply Crop
361
+ </button>
362
+ </div>
363
+ );
364
+ };
365
+ ```
366
+
367
+ ## Region Markers
368
+
369
+ ```tsx
370
+ import { ResizableBox } from "@editframe/react";
371
+
372
+ interface Region {
373
+ id: string;
374
+ bounds: BoxBounds;
375
+ color: string;
376
+ label: string;
377
+ }
378
+
379
+ export const RegionMarkers = () => {
380
+ const [regions, setRegions] = useState<Region[]>([
381
+ { id: "1", bounds: { x: 100, y: 100, width: 200, height: 150 }, color: "blue", label: "Face" },
382
+ { id: "2", bounds: { x: 400, y: 200, width: 150, height: 150 }, color: "red", label: "Object" }
383
+ ]);
384
+ const [selectedId, setSelectedId] = useState<string>("1");
385
+
386
+ const updateRegion = (id: string, newBounds: BoxBounds) => {
387
+ setRegions(regions.map(r =>
388
+ r.id === id ? { ...r, bounds: newBounds } : r
389
+ ));
390
+ };
391
+
392
+ const selectedRegion = regions.find(r => r.id === selectedId);
393
+
394
+ return (
395
+ <div className="relative w-full h-screen">
396
+ <img
397
+ src="/assets/photo.jpg"
398
+ alt="Annotated"
399
+ className="w-full h-full object-contain"
400
+ />
401
+
402
+ {/* Show all regions */}
403
+ {regions.map((region) => (
404
+ <div
405
+ key={region.id}
406
+ className={`absolute border-2 border-${region.color}-500`}
407
+ style={{
408
+ left: region.bounds.x,
409
+ top: region.bounds.y,
410
+ width: region.bounds.width,
411
+ height: region.bounds.height,
412
+ opacity: selectedId === region.id ? 1 : 0.5
413
+ }}
414
+ onClick={() => setSelectedId(region.id)}
415
+ >
416
+ <span className={`bg-${region.color}-500 text-white px-2 py-1 text-xs`}>
417
+ {region.label}
418
+ </span>
419
+ </div>
420
+ ))}
421
+
422
+ {/* Editable selected region */}
423
+ {selectedRegion && (
424
+ <div
425
+ className="absolute"
426
+ style={{
427
+ left: selectedRegion.bounds.x,
428
+ top: selectedRegion.bounds.y
429
+ }}
430
+ >
431
+ <ResizableBox
432
+ bounds={selectedRegion.bounds}
433
+ onBoundsChange={(e) => updateRegion(selectedId, e.detail)}
434
+ />
435
+ </div>
436
+ )}
437
+ </div>
438
+ );
439
+ };
440
+ ```
441
+
442
+ ## Bounding Box Editor
443
+
444
+ ```tsx
445
+ import { ResizableBox } from "@editframe/react";
446
+
447
+ export const BoundingBoxEditor = () => {
448
+ const [boxes, setBoxes] = useState([
449
+ { x: 50, y: 50, width: 150, height: 150 },
450
+ { x: 250, y: 100, width: 200, height: 100 }
451
+ ]);
452
+
453
+ const updateBox = (index: number, newBounds: BoxBounds) => {
454
+ setBoxes(boxes.map((box, i) => i === index ? newBounds : box));
455
+ };
456
+
457
+ const addBox = () => {
458
+ setBoxes([
459
+ ...boxes,
460
+ { x: 100 + boxes.length * 50, y: 100, width: 150, height: 150 }
461
+ ]);
462
+ };
463
+
464
+ return (
465
+ <div className="relative w-full h-screen bg-gray-100">
466
+ {boxes.map((box, index) => (
467
+ <ResizableBox
468
+ key={index}
469
+ bounds={box}
470
+ onBoundsChange={(e) => updateBox(index, e.detail)}
471
+ />
472
+ ))}
473
+
474
+ <button
475
+ onClick={addBox}
476
+ className="absolute top-4 right-4 px-4 py-2 bg-blue-500 text-white rounded"
477
+ >
478
+ Add Box
479
+ </button>
480
+ </div>
481
+ );
482
+ };
483
+ ```
484
+
485
+ ## Selection Rectangle
486
+
487
+ ```tsx
488
+ import { ResizableBox } from "@editframe/react";
489
+
490
+ export const SelectionTool = () => {
491
+ const [selection, setSelection] = useState<BoxBounds | null>(null);
492
+ const [isSelecting, setIsSelecting] = useState(false);
493
+
494
+ const startSelection = (e: React.MouseEvent) => {
495
+ const rect = e.currentTarget.getBoundingClientRect();
496
+ const x = e.clientX - rect.left;
497
+ const y = e.clientY - rect.top;
498
+
499
+ setSelection({ x, y, width: 0, height: 0 });
500
+ setIsSelecting(true);
501
+ };
502
+
503
+ return (
504
+ <div
505
+ className="relative w-full h-screen bg-gray-100"
506
+ onMouseDown={startSelection}
507
+ >
508
+ {selection && (
509
+ <ResizableBox
510
+ bounds={selection}
511
+ onBoundsChange={(e) => setSelection(e.detail)}
512
+ />
513
+ )}
514
+ </div>
515
+ );
516
+ };
517
+ ```
518
+
519
+ ## With Bounds Display
520
+
521
+ ```tsx
522
+ import { ResizableBox } from "@editframe/react";
523
+
524
+ export const BoundsDisplay = () => {
525
+ const [bounds, setBounds] = useState({
526
+ x: 100,
527
+ y: 100,
528
+ width: 300,
529
+ height: 200
530
+ });
531
+
532
+ return (
533
+ <div className="relative w-full h-screen bg-gray-100">
534
+ <ResizableBox
535
+ bounds={bounds}
536
+ onBoundsChange={(e) => setBounds(e.detail)}
537
+ />
538
+
539
+ <div className="absolute top-4 left-4 bg-white p-4 rounded shadow font-mono text-sm">
540
+ <div>x: {bounds.x.toFixed(0)}</div>
541
+ <div>y: {bounds.y.toFixed(0)}</div>
542
+ <div>width: {bounds.width.toFixed(0)}</div>
543
+ <div>height: {bounds.height.toFixed(0)}</div>
544
+ <div>area: {(bounds.width * bounds.height).toFixed(0)}px2</div>
545
+ </div>
546
+ </div>
547
+ );
548
+ };
549
+ ```
550
+
551
+ ## Aspect Ratio Calculator
552
+
553
+ ```tsx
554
+ import { ResizableBox } from "@editframe/react";
555
+
556
+ export const AspectRatioTool = () => {
557
+ const [bounds, setBounds] = useState({
558
+ x: 100,
559
+ y: 100,
560
+ width: 640,
561
+ height: 360
562
+ });
563
+
564
+ const aspectRatio = bounds.width / bounds.height;
565
+ const commonRatios = [
566
+ { name: "16:9", value: 16/9 },
567
+ { name: "4:3", value: 4/3 },
568
+ { name: "1:1", value: 1 }
569
+ ];
570
+
571
+ const closestRatio = commonRatios.reduce((prev, curr) =>
572
+ Math.abs(curr.value - aspectRatio) < Math.abs(prev.value - aspectRatio)
573
+ ? curr : prev
574
+ );
575
+
576
+ return (
577
+ <div className="relative w-full h-screen bg-gray-100">
578
+ <ResizableBox
579
+ bounds={bounds}
580
+ onBoundsChange={(e) => setBounds(e.detail)}
581
+ />
582
+
583
+ <div className="absolute top-4 left-4 bg-white p-4 rounded shadow">
584
+ <div className="font-semibold mb-2">Aspect Ratio</div>
585
+ <div className="text-2xl font-mono">{aspectRatio.toFixed(3)}</div>
586
+ <div className="text-sm text-gray-600 mt-1">
587
+ Closest: {closestRatio.name}
588
+ </div>
589
+ </div>
590
+ </div>
591
+ );
592
+ };
593
+ ```
594
+ <!-- /react-only -->
595
+
596
+ ## Cursor Styles
597
+
598
+ Handles show appropriate resize cursors:
599
+
600
+ - **nw, se**: `nwse-resize` (diagonal)
601
+ - **ne, sw**: `nesw-resize` (diagonal)
602
+ - **n, s**: `ns-resize` (vertical)
603
+ - **e, w**: `ew-resize` (horizontal)
604
+ - **content**: `grab` / `grabbing` (move)
605
+
606
+ ## Styling
607
+
608
+ Resizable box uses CSS custom properties:
609
+
610
+ ```css
611
+ ef-resizable-box {
612
+ /* Border color */
613
+ --ef-resizable-box-border-color: #2196f3;
614
+
615
+ /* Background color */
616
+ --ef-resizable-box-bg-color: rgba(33, 150, 243, 0.2);
617
+
618
+ /* Border during drag */
619
+ --ef-resizable-box-dragging-border-color: #1976d2;
620
+
621
+ /* Background during drag */
622
+ --ef-resizable-box-dragging-bg-color: rgba(33, 150, 243, 0.3);
623
+
624
+ /* Handle color */
625
+ --ef-resizable-box-handle-color: #2196f3;
626
+ }
627
+ ```
628
+
629
+ <!-- html-only -->
630
+ ## ResizeObserver
631
+
632
+ Resizable box observes its parent container for size changes:
633
+
634
+ ```javascript
635
+ // Box automatically updates constraints when container resizes
636
+ const container = box.offsetParent;
637
+
638
+ // Container dimensions tracked via ResizeObserver
639
+ ```
640
+ <!-- /html-only -->
641
+
642
+ ## Comparison with Transform Handles
643
+
644
+ <!-- html-only -->
645
+ Use **ef-resizable-box** when:
646
+ <!-- /html-only -->
647
+ <!-- react-only -->
648
+ Use **ResizableBox** when:
649
+ <!-- /react-only -->
650
+
651
+ - You need a self-contained resizable container
652
+ - Box is the primary content (not an overlay)
653
+ - Container constraints are important
654
+ - Rotation is not needed
655
+
656
+ <!-- html-only -->
657
+ Use **ef-transform-handles** when:
658
+ <!-- /html-only -->
659
+ <!-- react-only -->
660
+ Use **TransformHandles** when:
661
+ <!-- /react-only -->
662
+
663
+ - You need to transform existing elements
664
+ - Overlay-style interaction is needed
665
+ - Rotation is required
666
+ - Multi-element selection is needed
667
+ <!-- html-only -->
668
+ - Integration with ef-canvas is needed
669
+ <!-- /html-only -->
670
+ <!-- react-only -->
671
+ - Integration with Canvas is needed
672
+ <!-- /react-only -->
673
+
674
+ ## Pointer Events
675
+
676
+ Resizable box and its handles have `pointer-events: auto` and capture pointer events during interaction.
677
+
678
+ ## Touch Support
679
+
680
+ Handles use `touch-action: none` for proper touch device support.
681
+
682
+ ## Aspect Ratio
683
+
684
+ <!-- html-only -->
685
+ Resizable box does not lock aspect ratio by default. To maintain aspect ratio, handle the `bounds-change` event:
686
+
687
+ ```javascript
688
+ const box = document.querySelector('ef-resizable-box');
689
+ const aspectRatio = box.bounds.width / box.bounds.height;
690
+
691
+ box.addEventListener('bounds-change', (e) => {
692
+ const { bounds } = e.detail;
693
+
694
+ // Maintain aspect ratio
695
+ const newHeight = bounds.width / aspectRatio;
696
+ box.bounds = {
697
+ ...bounds,
698
+ height: newHeight
699
+ };
700
+ });
701
+ ```
702
+ <!-- /html-only -->
703
+ <!-- react-only -->
704
+ ResizableBox does not lock aspect ratio by default. To maintain aspect ratio, handle the `onBoundsChange` callback and adjust the bounds accordingly.
705
+ <!-- /react-only -->
706
+
707
+ ## Handle Features
708
+
709
+ - **8 resize handles**: nw, n, ne, e, se, s, sw, w (corners and edges)
710
+ - **Drag to move**: Click and drag the box interior to reposition
711
+ - **Visual feedback**: Handles show appropriate cursors on hover
712
+ - **Constrained sizing**: Respects `minSize` constraint
713
+ - **Boundary clipping**: Automatically constrained to parent container
714
+
715
+ <!-- react-only -->
716
+ ## Event Details
717
+
718
+ The `onBoundsChange` event provides:
719
+
720
+ ```typescript
721
+ interface BoxBounds {
722
+ x: number; // Left position
723
+ y: number; // Top position
724
+ width: number; // Box width
725
+ height: number; // Box height
726
+ }
727
+ ```
728
+
729
+ ## Differences from TransformHandles
730
+
731
+ - **No rotation**: ResizableBox does not support rotation
732
+ - **Simpler API**: Only x, y, width, height (no rotation property)
733
+ - **Self-contained**: Doesn't require OverlayLayer/OverlayItem wrapper
734
+ - **Positioned in parent**: Uses parent's coordinate space directly
735
+ <!-- /react-only -->
736
+
737
+ ## Important Notes
738
+
739
+ - Position is relative to parent container
740
+ - Must have a positioned parent (relative, absolute, or fixed)
741
+ - Automatically constrained to parent boundaries
742
+ - All handles are always visible (no corners-only mode)
743
+ - Use for simple resize/drag operations without rotation
744
+ <!-- html-only -->
745
+ - For more advanced transforms, use ef-transform-handles
746
+ <!-- /html-only -->
747
+ <!-- react-only -->
748
+ - For more advanced transforms, use TransformHandles component
749
+ <!-- /react-only -->