hyperframes 0.6.119 → 0.6.120

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.
@@ -0,0 +1,707 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, RefObject } from 'react';
3
+ import * as zustand from 'zustand';
4
+ import { MusicBeatAnalysis } from '@hyperframes/core/beats';
5
+ import * as react_jsx_runtime from 'react/jsx-runtime';
6
+ import * as _hyperframes_core_gsap_parser from '@hyperframes/core/gsap-parser';
7
+ import { GsapAnimation } from '@hyperframes/core/gsap-parser';
8
+
9
+ interface PlayerProps {
10
+ projectId?: string;
11
+ directUrl?: string;
12
+ onLoad: () => void;
13
+ onCompositionLoadingChange?: (loading: boolean) => void;
14
+ portrait?: boolean;
15
+ style?: React.CSSProperties;
16
+ suppressLoadingOverlay?: boolean;
17
+ }
18
+ /**
19
+ * Renders a composition preview using the <hyperframes-player> web component.
20
+ *
21
+ * The web component handles iframe scaling, dimension detection, and
22
+ * ResizeObserver internally. This wrapper bridges its inner iframe to the
23
+ * forwarded ref so useTimelinePlayer can access it for clip manifest parsing,
24
+ * timeline probing, and DOM inspection.
25
+ */
26
+ declare const Player: react.ForwardRefExoticComponent<PlayerProps & react.RefAttributes<HTMLIFrameElement>>;
27
+
28
+ interface PlayerControlsProps {
29
+ onTogglePlay: () => void;
30
+ onSeek: (time: number) => void;
31
+ disabled?: boolean;
32
+ isFullscreen?: boolean;
33
+ onToggleFullscreen?: () => void;
34
+ }
35
+ declare const PlayerControls: react.NamedExoticComponent<PlayerControlsProps>;
36
+
37
+ interface UserBeat {
38
+ time: number;
39
+ strength: number;
40
+ }
41
+ interface BeatEditState {
42
+ /** Music src these edits apply to; edits reset when the src changes. */
43
+ src: string;
44
+ /** Beats the user added (audio-file coords). */
45
+ added: UserBeat[];
46
+ /** Audio-file times of detected beats the user removed. */
47
+ removed: number[];
48
+ }
49
+
50
+ interface ClipManifestClip {
51
+ id: string | null;
52
+ label: string;
53
+ start: number;
54
+ duration: number;
55
+ track: number;
56
+ kind: "video" | "audio" | "image" | "element" | "composition";
57
+ tagName: string | null;
58
+ compositionId: string | null;
59
+ parentCompositionId: string | null;
60
+ compositionSrc: string | null;
61
+ assetUrl: string | null;
62
+ }
63
+
64
+ /** Minimal keyframe cache types — mirrors GsapKeyframesData without pulling in Node-only gsap-parser. */
65
+ interface KeyframeCacheEntry {
66
+ format: string;
67
+ keyframes: Array<{
68
+ percentage: number;
69
+ /** Original tween-relative percentage (server mutations need this, not the clip-relative `percentage`). */
70
+ tweenPercentage?: number;
71
+ /** Which property group the source tween belongs to (position, scale, rotation, visual, etc.). */
72
+ propertyGroup?: string;
73
+ properties: Record<string, number | string>;
74
+ ease?: string;
75
+ }>;
76
+ ease?: string;
77
+ easeEach?: string;
78
+ }
79
+ interface TimelineElement {
80
+ id: string;
81
+ label?: string;
82
+ key?: string;
83
+ tag: string;
84
+ start: number;
85
+ duration: number;
86
+ track: number;
87
+ domId?: string;
88
+ /** Stable `data-hf-id` attribute value — used as primary patch target when present */
89
+ hfId?: string;
90
+ /** Best-effort selector used when patching source HTML back from timeline edits */
91
+ selector?: string;
92
+ /** Zero-based occurrence index for non-unique selectors */
93
+ selectorIndex?: number;
94
+ /** Source composition file that owns this element, when known */
95
+ sourceFile?: string;
96
+ src?: string;
97
+ playbackStart?: number;
98
+ playbackStartAttr?: "media-start" | "playback-start";
99
+ playbackRate?: number;
100
+ sourceDuration?: number;
101
+ volume?: number;
102
+ /** Path from data-composition-src — identifies sub-composition elements */
103
+ compositionSrc?: string;
104
+ /** Whether this row came from authored clip timing or Studio's full-duration layer fallback. */
105
+ timingSource?: "authored" | "implicit";
106
+ /** Set by data-timeline-locked on the host element — disables move and trim in Studio. */
107
+ timelineLocked?: boolean;
108
+ /** Value of data-timeline-role attribute — used to identify music vs. voiceover. */
109
+ timelineRole?: string;
110
+ /**
111
+ * Set by useExpandedTimelineElements on an inline-expanded sub-composition
112
+ * child: the absolute master-timeline start of the sub-comp host the child
113
+ * lives in. Presence marks the element as expanded; edits subtract it to get
114
+ * the child's local (sourceFile-relative) time. Works at any nesting depth.
115
+ */
116
+ expandedParentStart?: number;
117
+ }
118
+ type ZoomMode = "fit" | "manual";
119
+ type TimelineTool = "select" | "razor";
120
+ interface PlayerState {
121
+ isPlaying: boolean;
122
+ currentTime: number;
123
+ duration: number;
124
+ timelineReady: boolean;
125
+ /** True while a beat dot is being dragged — hides the playhead guideline. */
126
+ beatDragging: boolean;
127
+ elements: TimelineElement[];
128
+ selectedElementId: string | null;
129
+ playbackRate: number;
130
+ audioMuted: boolean;
131
+ loopEnabled: boolean;
132
+ /** Timeline zoom: 'fit' auto-scales to viewport, 'manual' uses manualZoomPercent */
133
+ zoomMode: ZoomMode;
134
+ /** Timeline zoom percent relative to the fit width when in manual mode */
135
+ manualZoomPercent: number;
136
+ /** Work-area in-point (seconds). When set, loop starts here and A jumps here. */
137
+ inPoint: number | null;
138
+ /** Work-area out-point (seconds). When set, loop ends here and E jumps here. */
139
+ outPoint: number | null;
140
+ activeTool: TimelineTool;
141
+ setActiveTool: (tool: TimelineTool) => void;
142
+ /** Set of selected keyframe keys in format `${elementId}:${percentage}`. */
143
+ selectedKeyframes: Set<string>;
144
+ toggleSelectedKeyframe: (key: string) => void;
145
+ clearSelectedKeyframes: () => void;
146
+ /** Tween-relative percentage of the last-clicked keyframe diamond. Operations
147
+ * (drag, resize, rotate) target this instead of recomputing from playhead. */
148
+ activeKeyframePct: number | null;
149
+ setActiveKeyframePct: (pct: number | null) => void;
150
+ /** Multi-select: additional selected elements beyond selectedElementId. */
151
+ selectedElementIds: Set<string>;
152
+ toggleSelectedElementId: (id: string) => void;
153
+ clearSelectedElementIds: () => void;
154
+ /** Keyframe data per element id, populated from parsed GSAP animations. */
155
+ keyframeCache: Map<string, KeyframeCacheEntry>;
156
+ setKeyframeCache: (elementId: string, data: KeyframeCacheEntry | undefined) => void;
157
+ setIsPlaying: (playing: boolean) => void;
158
+ setCurrentTime: (time: number) => void;
159
+ setDuration: (duration: number) => void;
160
+ setPlaybackRate: (rate: number) => void;
161
+ setAudioMuted: (muted: boolean) => void;
162
+ setLoopEnabled: (enabled: boolean) => void;
163
+ setTimelineReady: (ready: boolean) => void;
164
+ setBeatDragging: (dragging: boolean) => void;
165
+ setElements: (elements: TimelineElement[]) => void;
166
+ setSelectedElementId: (id: string | null) => void;
167
+ updateElement: (elementId: string, updates: Partial<Pick<TimelineElement, "start" | "duration" | "track" | "playbackStart">>) => void;
168
+ setZoomMode: (mode: ZoomMode) => void;
169
+ setManualZoomPercent: (percent: number) => void;
170
+ setInPoint: (time: number | null) => void;
171
+ setOutPoint: (time: number | null) => void;
172
+ reset: () => void;
173
+ /**
174
+ * Request a seek from outside the player loop (e.g. Layers panel).
175
+ * useTimelinePlayer subscribes and calls adapter.seek() + liveTime.notify().
176
+ */
177
+ requestedSeekTime: number | null;
178
+ requestSeek: (time: number) => void;
179
+ clearSeekRequest: () => void;
180
+ lintFindingsByElement: Map<string, {
181
+ count: number;
182
+ messages: string[];
183
+ }>;
184
+ setLintFindingsByElement: (map: Map<string, {
185
+ count: number;
186
+ messages: string[];
187
+ }>) => void;
188
+ beatAnalysis: MusicBeatAnalysis | null;
189
+ setBeatAnalysis: (analysis: MusicBeatAnalysis | null) => void;
190
+ /** User edits (add/move/delete) layered over the detected beat grid. */
191
+ beatEdits: BeatEditState | null;
192
+ setBeatEdits: (edits: BeatEditState | null) => void;
193
+ /** Undo/redo stacks for beat edits (in-memory, session-only). */
194
+ beatUndo: BeatHistoryEntry[];
195
+ beatRedo: BeatHistoryEntry[];
196
+ commitBeatEdits: (next: BeatEditState | null, label: string) => void;
197
+ undoBeatEdits: () => string | null;
198
+ redoBeatEdits: () => string | null;
199
+ resetBeatHistory: () => void;
200
+ beatPersist: (() => void) | null;
201
+ setBeatPersist: (fn: (() => void) | null) => void;
202
+ clipManifest: ClipManifestClip[] | null;
203
+ setClipManifest: (clips: ClipManifestClip[] | null) => void;
204
+ clipParentMap: Map<string, string>;
205
+ setClipParentMap: (map: Map<string, string>) => void;
206
+ }
207
+ interface BeatHistoryEntry {
208
+ restore: BeatEditState | null;
209
+ at: number;
210
+ label: string;
211
+ }
212
+ type TimeListener = (time: number) => void;
213
+ declare const liveTime: {
214
+ notify: (t: number) => void;
215
+ subscribe: (cb: TimeListener) => () => boolean;
216
+ };
217
+ declare const usePlayerStore: zustand.UseBoundStore<zustand.StoreApi<PlayerState>>;
218
+
219
+ interface TimelineTheme {
220
+ shellBackground: string;
221
+ shellBorder: string;
222
+ rulerBorder: string;
223
+ rowBackground: string;
224
+ rowBorder: string;
225
+ gutterBackground: string;
226
+ gutterBorder: string;
227
+ textPrimary: string;
228
+ textSecondary: string;
229
+ tickText: string;
230
+ tickMajor: string;
231
+ tickMinor: string;
232
+ clipBackground: string;
233
+ clipBackgroundActive: string;
234
+ clipBorder: string;
235
+ clipBorderHover: string;
236
+ clipBorderActive: string;
237
+ clipShadow: string;
238
+ clipShadowHover: string;
239
+ clipShadowActive: string;
240
+ clipShadowDragging: string;
241
+ handleColor: string;
242
+ panelResizeSeam: string;
243
+ panelResizeActive: string;
244
+ clipRadius: string;
245
+ }
246
+
247
+ type BlockedTimelineEditIntent = "move" | "resize-start" | "resize-end";
248
+
249
+ /**
250
+ * Shared callback signatures for timeline editing operations.
251
+ * Used by NLELayout, Timeline, and any component that passes through
252
+ * the standard set of timeline mutation handlers.
253
+ */
254
+ interface TimelineDropCallbacks {
255
+ onFileDrop?: (files: File[], placement?: {
256
+ start: number;
257
+ track: number;
258
+ }) => Promise<void> | void;
259
+ onAssetDrop?: (assetPath: string, placement: {
260
+ start: number;
261
+ track: number;
262
+ }) => Promise<void> | void;
263
+ onBlockDrop?: (blockName: string, placement: {
264
+ start: number;
265
+ track: number;
266
+ }) => Promise<void> | void;
267
+ }
268
+ interface TimelineEditCallbacks {
269
+ onMoveElement?: (element: TimelineElement, updates: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
270
+ onResizeElement?: (element: TimelineElement, updates: Pick<TimelineElement, "start" | "duration" | "playbackStart">) => Promise<void> | void;
271
+ onBlockedEditAttempt?: (element: TimelineElement, intent: BlockedTimelineEditIntent) => void;
272
+ onSplitElement?: (element: TimelineElement, splitTime: number) => Promise<void> | void;
273
+ onRazorSplit?: (element: TimelineElement, splitTime: number) => Promise<void> | void;
274
+ onRazorSplitAll?: (splitTime: number) => Promise<void> | void;
275
+ onDeleteKeyframe?: (elementId: string, percentage: number) => void;
276
+ onDeleteAllKeyframes?: (elementId: string) => void;
277
+ onChangeKeyframeEase?: (elementId: string, percentage: number, ease: string) => void;
278
+ onMoveKeyframe?: (element: TimelineElement, oldPct: number, newPct: number) => void;
279
+ onToggleKeyframeAtPlayhead?: (element: TimelineElement) => void;
280
+ }
281
+
282
+ type TimelineEditOverrides = Pick<TimelineEditCallbacks, "onMoveElement" | "onResizeElement" | "onBlockedEditAttempt" | "onSplitElement">;
283
+
284
+ interface TimelineProps extends TimelineDropCallbacks, TimelineEditOverrides {
285
+ onSeek?: (time: number) => void;
286
+ onDrillDown?: (element: TimelineElement) => void;
287
+ renderClipContent?: (element: TimelineElement, style: {
288
+ clip: string;
289
+ label: string;
290
+ }) => ReactNode;
291
+ renderClipOverlay?: (element: TimelineElement) => ReactNode;
292
+ onDeleteElement?: (element: TimelineElement) => Promise<void> | void;
293
+ onSelectElement?: (element: TimelineElement | null) => void;
294
+ theme?: Partial<TimelineTheme>;
295
+ }
296
+ declare const Timeline: react.NamedExoticComponent<TimelineProps>;
297
+
298
+ interface VideoThumbnailProps {
299
+ videoSrc: string;
300
+ label: string;
301
+ labelColor: string;
302
+ duration?: number;
303
+ }
304
+ /**
305
+ * Renders a film-strip of video frames extracted client-side via a hidden
306
+ * <video> + <canvas>. Each frame is a fixed-width tile; frames repeat to
307
+ * fill the clip width — matching ClipThumbnail's visual pattern.
308
+ */
309
+ declare const VideoThumbnail: react.NamedExoticComponent<VideoThumbnailProps>;
310
+
311
+ interface CompositionThumbnailProps {
312
+ previewUrl: string;
313
+ label: string;
314
+ labelColor: string;
315
+ selector?: string;
316
+ selectorIndex?: number;
317
+ seekTime?: number;
318
+ duration?: number;
319
+ width?: number;
320
+ height?: number;
321
+ }
322
+ declare const CompositionThumbnail: react.NamedExoticComponent<CompositionThumbnailProps>;
323
+
324
+ /**
325
+ * Runtime iframe integration utilities.
326
+ *
327
+ * Handles the boundary between the studio host page and the preview iframe:
328
+ * - Viewport normalisation on load
329
+ * - Auto-healing missing data-composition-id attributes
330
+ * - Unmuting media via postMessage
331
+ * - Resolving the underlying <iframe> from any wrapper element
332
+ * - Scanning the DOM for composition hosts the manifest missed
333
+ * (element-reference starts that the CDN runtime fails to resolve)
334
+ */
335
+
336
+ /**
337
+ * Resolve the underlying iframe from any host element. Supports:
338
+ * - Direct `<iframe>` element (most common — studio's own `Player.tsx`)
339
+ * - Custom elements (e.g. `<hyperframes-player>`) whose shadow DOM contains an iframe
340
+ * - Wrapper elements whose light DOM contains a descendant iframe
341
+ *
342
+ * Exported so web-component consumers can pre-resolve the iframe before
343
+ * assigning it to `iframeRef` returned by `useTimelinePlayer`. Returns `null`
344
+ * when the element has no associated iframe yet.
345
+ *
346
+ * @example
347
+ * ```tsx
348
+ * const { iframeRef } = useTimelinePlayer();
349
+ * const playerElRef = useRef<HyperframesPlayer>(null);
350
+ *
351
+ * useEffect(() => {
352
+ * iframeRef.current = resolveIframe(playerElRef.current);
353
+ * }, [iframeRef]);
354
+ * ```
355
+ */
356
+ declare function resolveIframe(el: Element | null): HTMLIFrameElement | null;
357
+
358
+ declare function useTimelinePlayer(): {
359
+ iframeRef: react.RefObject<HTMLIFrameElement | null>;
360
+ play: () => void;
361
+ pause: () => void;
362
+ togglePlay: () => void;
363
+ seek: (time: number, options?: {
364
+ keepPlaying?: boolean;
365
+ }) => boolean;
366
+ onIframeLoad: () => void;
367
+ refreshPlayer: () => void;
368
+ saveSeekPosition: () => void;
369
+ resetPlayer: () => void;
370
+ };
371
+
372
+ declare function formatTime(time: number): string;
373
+
374
+ interface NLELayoutProps {
375
+ projectId: string;
376
+ portrait?: boolean;
377
+ /** Slot for overlays rendered on top of the preview (cursors, highlights, etc.) */
378
+ previewOverlay?: ReactNode;
379
+ /** Slot rendered above the timeline tracks (toolbar with split, delete, zoom) */
380
+ timelineToolbar?: ReactNode;
381
+ /** Slot rendered below the timeline tracks */
382
+ timelineFooter?: ReactNode;
383
+ /** Increment to force the preview to reload (e.g., after file writes) */
384
+ refreshKey?: number;
385
+ /** Navigate to a specific composition path (e.g., "compositions/intro.html") */
386
+ activeCompositionPath?: string | null;
387
+ /** Callback to expose the iframe ref (for element picker, etc.) */
388
+ onIframeRef?: (iframe: HTMLIFrameElement | null) => void;
389
+ /** Callback when the viewed composition changes (drill-down/back) */
390
+ onCompositionChange?: (compositionPath: string | null) => void;
391
+ /** Custom clip content renderer for timeline (thumbnails, waveforms, etc.) */
392
+ renderClipContent?: (element: TimelineElement, style: {
393
+ clip: string;
394
+ label: string;
395
+ }) => ReactNode;
396
+ onFileDrop?: (files: File[], placement?: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
397
+ onDeleteElement?: (element: TimelineElement) => Promise<void> | void;
398
+ onAssetDrop?: (assetPath: string, placement: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
399
+ onBlockDrop?: (blockName: string, placement: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
400
+ onPreviewBlockDrop?: (blockName: string, position: {
401
+ left: number;
402
+ top: number;
403
+ }) => Promise<void> | void;
404
+ onBlockedEditAttempt?: (element: TimelineElement, intent: BlockedTimelineEditIntent) => void;
405
+ onSelectTimelineElement?: (element: TimelineElement | null) => void;
406
+ /** Exposes the compIdToSrc map for parent components (e.g., useRenderClipContent) */
407
+ onCompIdToSrcChange?: (map: Map<string, string>) => void;
408
+ /** Whether the timeline panel is visible (default: true) */
409
+ timelineVisible?: boolean;
410
+ /** Callback to toggle timeline visibility */
411
+ onToggleTimeline?: () => void;
412
+ /** Notifies parent when composition loading state changes */
413
+ onCompositionLoadingChange?: (loading: boolean) => void;
414
+ }
415
+ declare const NLELayout: react.NamedExoticComponent<NLELayoutProps>;
416
+
417
+ interface NLEPreviewProps {
418
+ projectId: string;
419
+ iframeRef: RefObject<HTMLIFrameElement | null>;
420
+ onIframeLoad: () => void;
421
+ onCompositionLoadingChange?: (loading: boolean) => void;
422
+ portrait?: boolean;
423
+ directUrl?: string;
424
+ suppressLoadingOverlay?: boolean;
425
+ onStageRef?: (ref: React.RefObject<HTMLDivElement | null>) => void;
426
+ }
427
+ declare const NLEPreview: react.NamedExoticComponent<NLEPreviewProps>;
428
+
429
+ interface CompositionLevel {
430
+ /** Unique id — "master" or composition file path */
431
+ id: string;
432
+ /** Display label — "Master" or filename without extension */
433
+ label: string;
434
+ /** Preview URL for this composition level */
435
+ previewUrl: string;
436
+ }
437
+ interface CompositionBreadcrumbProps {
438
+ stack: CompositionLevel[];
439
+ onNavigate: (index: number) => void;
440
+ }
441
+ declare function CompositionBreadcrumb({ stack, onNavigate }: CompositionBreadcrumbProps): react_jsx_runtime.JSX.Element | null;
442
+
443
+ interface SourceEditorProps {
444
+ content: string;
445
+ filePath?: string;
446
+ language?: string;
447
+ onChange?: (content: string) => void;
448
+ readOnly?: boolean;
449
+ revealOffset?: number | null;
450
+ }
451
+ declare const SourceEditor: react.NamedExoticComponent<SourceEditorProps>;
452
+
453
+ /**
454
+ * Source Patcher — Maps visual property edits back to source HTML files.
455
+ * Handles inline style updates, attribute changes, and text content.
456
+ */
457
+ interface PatchOperation {
458
+ type: "inline-style" | "attribute" | "text-content" | "html-attribute";
459
+ property: string;
460
+ value: string | null;
461
+ }
462
+ interface PatchTarget {
463
+ id?: string | null;
464
+ hfId?: string;
465
+ selector?: string;
466
+ selectorIndex?: number;
467
+ }
468
+ /**
469
+ * Find which source file contains an element by its ID.
470
+ */
471
+ declare function resolveSourceFile(elementId: string | null, selector: string, files: Record<string, string>): string | null;
472
+ /**
473
+ * Apply a patch operation to an HTML source file.
474
+ */
475
+ declare function applyPatch(html: string, elementId: string, op: PatchOperation): string;
476
+
477
+ interface DomEditCapabilities {
478
+ canSelect: boolean;
479
+ canEditStyles: boolean;
480
+ /** Directly editable authored left/top style fields. Canvas drag uses manual edits instead. */
481
+ canMove: boolean;
482
+ /** Directly editable authored width/height style fields. Canvas resize uses manual edits instead. */
483
+ canResize: boolean;
484
+ canApplyManualOffset: boolean;
485
+ canApplyManualSize: boolean;
486
+ canApplyManualRotation: boolean;
487
+ reasonIfDisabled?: string;
488
+ }
489
+ interface DomEditTextField {
490
+ key: string;
491
+ label: string;
492
+ value: string;
493
+ tagName: string;
494
+ attributes: Array<{
495
+ name: string;
496
+ value: string;
497
+ }>;
498
+ inlineStyles: Record<string, string>;
499
+ computedStyles: Record<string, string>;
500
+ source: "self" | "child" | "text-node";
501
+ }
502
+ interface DomEditSelection extends PatchTarget {
503
+ element: HTMLElement;
504
+ label: string;
505
+ tagName: string;
506
+ sourceFile: string;
507
+ compositionPath: string;
508
+ compositionSrc?: string;
509
+ isCompositionHost: boolean;
510
+ isInsideLockedComposition: boolean;
511
+ boundingBox: {
512
+ x: number;
513
+ y: number;
514
+ width: number;
515
+ height: number;
516
+ };
517
+ textContent: string | null;
518
+ dataAttributes: Record<string, string>;
519
+ inlineStyles: Record<string, string>;
520
+ computedStyles: Record<string, string>;
521
+ textFields: DomEditTextField[];
522
+ capabilities: DomEditCapabilities;
523
+ gsapAnimations?: GsapAnimation[];
524
+ }
525
+
526
+ interface ImportedFontAsset {
527
+ family: string;
528
+ path: string;
529
+ url: string;
530
+ }
531
+
532
+ interface PropertyPanelProps {
533
+ projectId: string;
534
+ projectDir: string | null;
535
+ assets: string[];
536
+ element: DomEditSelection | null;
537
+ multiSelectCount?: number;
538
+ copiedAgentPrompt: boolean;
539
+ onClearSelection: () => void;
540
+ onSetStyle: (prop: string, value: string) => void | Promise<void>;
541
+ onSetAttribute: (attr: string, value: string) => void | Promise<void>;
542
+ onSetAttributeLive: (attr: string, value: string | null) => void | Promise<void>;
543
+ onSetHtmlAttribute: (attr: string, value: string | null) => void | Promise<void>;
544
+ onSetManualOffset: (element: DomEditSelection, next: {
545
+ x: number;
546
+ y: number;
547
+ }) => void;
548
+ onSetManualSize: (element: DomEditSelection, next: {
549
+ width: number;
550
+ height: number;
551
+ }) => void;
552
+ onSetManualRotation: (element: DomEditSelection, next: {
553
+ angle: number;
554
+ }) => void;
555
+ onSetText: (value: string, fieldKey?: string) => void;
556
+ onSetTextFieldStyle: (fieldKey: string, property: string, value: string) => void;
557
+ onAddTextField: (afterFieldKey?: string) => string | Promise<string | null> | null;
558
+ onRemoveTextField: (fieldKey: string) => void;
559
+ onAskAgent: () => void;
560
+ onImportAssets?: (files: FileList, dir?: string) => Promise<string[]>;
561
+ fontAssets?: ImportedFontAsset[];
562
+ onImportFonts?: (files: FileList | File[]) => Promise<ImportedFontAsset[]>;
563
+ previewIframeRef?: React.RefObject<HTMLIFrameElement | null>;
564
+ gsapAnimations?: _hyperframes_core_gsap_parser.GsapAnimation[];
565
+ gsapMultipleTimelines?: boolean;
566
+ gsapUnsupportedTimelinePattern?: boolean;
567
+ onUpdateGsapProperty?: (animId: string, prop: string, value: number | string) => void;
568
+ onUpdateGsapMeta?: (animId: string, updates: {
569
+ duration?: number;
570
+ ease?: string;
571
+ position?: number;
572
+ }) => void;
573
+ onDeleteGsapAnimation?: (animId: string) => void;
574
+ onAddGsapProperty?: (animId: string, prop: string) => void;
575
+ onRemoveGsapProperty?: (animId: string, prop: string) => void;
576
+ onUpdateGsapFromProperty?: (animId: string, prop: string, value: number | string) => void;
577
+ onAddGsapFromProperty?: (animId: string, prop: string) => void;
578
+ onRemoveGsapFromProperty?: (animId: string, prop: string) => void;
579
+ onAddGsapAnimation?: (method: "to" | "from" | "set" | "fromTo") => void;
580
+ onSetArcPath?: (animId: string, config: {
581
+ enabled: boolean;
582
+ autoRotate?: boolean | number;
583
+ segments?: _hyperframes_core_gsap_parser.ArcPathSegment[];
584
+ }) => void;
585
+ onUpdateArcSegment?: (animId: string, segmentIndex: number, update: Partial<_hyperframes_core_gsap_parser.ArcPathSegment>) => void;
586
+ /** Unroll computed (helper/loop) tweens into literal tweens for direct editing. */
587
+ onUnroll?: (animationId: string) => void;
588
+ onAddKeyframe?: (animationId: string, percentage: number, property: string, value: number | string) => void;
589
+ onRemoveKeyframe?: (animationId: string, percentage: number) => void;
590
+ onConvertToKeyframes?: (animationId: string) => void;
591
+ onCommitAnimatedProperty?: (selection: DomEditSelection, property: string, value: number | string) => Promise<void>;
592
+ onSeekToTime?: (time: number) => void;
593
+ recordingState?: "idle" | "recording" | "preview";
594
+ recordingDuration?: number;
595
+ onToggleRecording?: () => void;
596
+ }
597
+ interface LocalFontData {
598
+ family: string;
599
+ fullName?: string;
600
+ postscriptName?: string;
601
+ style?: string;
602
+ blob?: () => Promise<Blob>;
603
+ }
604
+ declare global {
605
+ interface Window {
606
+ queryLocalFonts?: () => Promise<LocalFontData[]>;
607
+ }
608
+ }
609
+
610
+ declare const PropertyPanel: react.NamedExoticComponent<PropertyPanelProps>;
611
+
612
+ interface FileTreeProps {
613
+ files: string[];
614
+ activeFile: string | null;
615
+ onSelectFile: (path: string) => void;
616
+ onCreateFile?: (path: string) => void;
617
+ onCreateFolder?: (path: string) => void;
618
+ onDeleteFile?: (path: string) => void;
619
+ onRenameFile?: (oldPath: string, newPath: string) => void;
620
+ onDuplicateFile?: (path: string) => void;
621
+ onMoveFile?: (oldPath: string, newPath: string) => void;
622
+ onImportFiles?: (files: FileList, dir?: string) => void;
623
+ lintFindingsByFile?: Map<string, {
624
+ count: number;
625
+ messages: string[];
626
+ }>;
627
+ }
628
+ declare const FileTree: react.NamedExoticComponent<FileTreeProps>;
629
+
630
+ declare function StudioApp(): react_jsx_runtime.JSX.Element;
631
+
632
+ interface PickedElement {
633
+ id: string | null;
634
+ tagName: string;
635
+ selector: string;
636
+ label: string;
637
+ boundingBox: {
638
+ x: number;
639
+ y: number;
640
+ width: number;
641
+ height: number;
642
+ };
643
+ textContent: string | null;
644
+ src: string | null;
645
+ dataAttributes: Record<string, string>;
646
+ computedStyles: Record<string, string>;
647
+ }
648
+ interface UseElementPickerReturn {
649
+ isPickMode: boolean;
650
+ pickedElement: PickedElement | null;
651
+ enablePick: () => void;
652
+ disablePick: () => void;
653
+ clearPick: () => void;
654
+ /** Update a CSS property on the picked element live + persist to source */
655
+ setStyle: (prop: string, value: string) => void;
656
+ /** Update a data attribute on the picked element + persist to source */
657
+ setDataAttr: (attr: string, value: string) => void;
658
+ /** Update the text content of the picked element + persist to source */
659
+ setTextContent: (text: string) => void;
660
+ /** Override the active iframe (for zoomed canvas view). Pass null to restore primary. */
661
+ setActiveIframe: (el: HTMLIFrameElement | null) => void;
662
+ /** Ref that always points to the active iframe (focused canvas frame or preview panel) */
663
+ activeIframeRef: React.RefObject<HTMLIFrameElement | null>;
664
+ }
665
+ interface PickerOptions {
666
+ /** Workspace files for source patching */
667
+ workspaceFiles?: Record<string, string>;
668
+ /** Callback to sync patched files to the project */
669
+ onSyncFiles?: (files: Record<string, string>) => void;
670
+ }
671
+ /**
672
+ * Hook for element picking via the HyperFrame runtime's picker API.
673
+ * Communicates with the iframe via postMessage.
674
+ */
675
+ declare function useElementPicker(iframeRef: React.RefObject<HTMLIFrameElement | null>, options?: PickerOptions): UseElementPickerReturn;
676
+
677
+ /**
678
+ * HTML Editor — Utility functions for parsing and manipulating HyperFrame HTML source.
679
+ */
680
+ /**
681
+ * Parse a CSS inline style string into a key-value map.
682
+ * e.g. "opacity: 0.5; transform: matrix(1,0,0,1,0,0)" →
683
+ * { opacity: "0.5", transform: "matrix(1,0,0,1,0,0)" }
684
+ */
685
+ declare function parseStyleString(style: string): Record<string, string>;
686
+ /**
687
+ * Merge `newStyles` into an opening tag string's `style` attribute.
688
+ * - New values win over existing ones.
689
+ * - If no `style` attribute is present, one is added before the closing `>`.
690
+ */
691
+ declare function mergeStyleIntoTag(tag: string, newStyles: string): string;
692
+ /**
693
+ * Find the full element block (opening tag through closing tag) in the source.
694
+ * Uses quote-aware scanning to handle attributes containing >.
695
+ * Uses depth counting to handle nested same-name tags.
696
+ */
697
+ declare function findElementBlock(html: string, elementId: string): {
698
+ start: number;
699
+ end: number;
700
+ openTag: string;
701
+ tagName: string;
702
+ indent: string;
703
+ innerContent: string;
704
+ isSelfClosing: boolean;
705
+ } | null;
706
+
707
+ export { CompositionBreadcrumb, type CompositionLevel, CompositionThumbnail, FileTree, NLELayout, NLEPreview, type PatchOperation, type PickedElement, Player, PlayerControls, PropertyPanel, SourceEditor, StudioApp, Timeline, type TimelineElement, VideoThumbnail, applyPatch, findElementBlock, formatTime, liveTime, mergeStyleIntoTag, parseStyleString, resolveIframe, resolveSourceFile, useElementPicker, usePlayerStore, useTimelinePlayer };