hyperframes 0.7.54 → 0.7.56

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.
@@ -1,8 +1,8 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
1
2
  import * as react from 'react';
2
3
  import { ReactNode, RefObject } from 'react';
3
4
  import * as zustand from 'zustand';
4
5
  import { MusicBeatAnalysis } from '@hyperframes/core/beats';
5
- import * as react_jsx_runtime from 'react/jsx-runtime';
6
6
  import { GsapAnimation, ArcPathSegment } from '@hyperframes/parsers/gsap-parser';
7
7
 
8
8
  interface PlayerProps {
@@ -131,6 +131,9 @@ interface TimelineElement {
131
131
  }
132
132
  type ZoomMode = "fit" | "manual";
133
133
  type TimelineTool = "select" | "razor";
134
+ interface SelectElementOptions {
135
+ preserveSet?: boolean;
136
+ }
134
137
  interface PlayerState {
135
138
  isPlaying: boolean;
136
139
  currentTime: number;
@@ -177,6 +180,25 @@ interface PlayerState {
177
180
  setAutoKeyframeEnabled: (enabled: boolean) => void;
178
181
  /** Multi-select: additional selected elements beyond selectedElementId. */
179
182
  selectedElementIds: Set<string>;
183
+ clearSelectedElementIds: () => void;
184
+ /** Replace the whole multi-selection at once (marquee live updates). */
185
+ setSelectedElementIds: (ids: Set<string>) => void;
186
+ /** Timeline magnet toggle — when false, clip drags/trims/drops never snap. */
187
+ timelineSnapEnabled: boolean;
188
+ setTimelineSnapEnabled: (enabled: boolean) => void;
189
+ /**
190
+ * Pin the timeline zoom to its current visual scale before a duration-changing
191
+ * edit, so a subsequent duration change (which recomputes fit-pps) stops
192
+ * rescaling every clip. No-op once already pinned (mode is "manual").
193
+ */
194
+ pinTimelineZoom: (currentPixelsPerSecond: number, fitPixelsPerSecond: number) => void;
195
+ /**
196
+ * The timeline's live pixels-per-second + fit basis, published by <Timeline> on
197
+ * every render. Non-reactive scratch state (never read as a render input).
198
+ */
199
+ timelinePps: number;
200
+ timelineFitPps: number;
201
+ setTimelineScale: (pps: number, fitPps: number) => void;
180
202
  setSelection: (ids: Iterable<string>, anchor?: string | null) => void;
181
203
  addSelectedElementId: (id: string) => void;
182
204
  toggleSelectedElementId: (id: string) => void;
@@ -193,7 +215,7 @@ interface PlayerState {
193
215
  setTimelineReady: (ready: boolean) => void;
194
216
  setBeatDragging: (dragging: boolean) => void;
195
217
  setElements: (elements: TimelineElement[]) => void;
196
- setSelectedElementId: (id: string | null) => void;
218
+ setSelectedElementId: (id: string | null, options?: SelectElementOptions) => void;
197
219
  /** Move the selection anchor within an active multi-selection without collapsing it. */
198
220
  setSelectionAnchor: (id: string | null) => void;
199
221
  updateElement: (elementId: string, updates: Partial<Pick<TimelineElement, "start" | "duration" | "track" | "zIndex" | "hasExplicitZIndex" | "playbackStart" | "hidden">>) => void;
@@ -264,36 +286,6 @@ declare const liveTime: {
264
286
  };
265
287
  declare const usePlayerStore: zustand.UseBoundStore<zustand.StoreApi<PlayerState>>;
266
288
 
267
- type TimelineLayerDropPlacement = {
268
- type: "onto";
269
- layerId: string;
270
- } | {
271
- type: "between";
272
- beforeLayerId: string;
273
- afterLayerId: string;
274
- } | {
275
- type: "above";
276
- layerId: string;
277
- } | {
278
- type: "below";
279
- layerId: string;
280
- };
281
- interface TimelineStackingZIndexChange {
282
- key: string;
283
- zIndex: number;
284
- domId?: string;
285
- selector?: string;
286
- selectorIndex?: number;
287
- sourceFile?: string;
288
- }
289
- interface TimelineStackingReorderIntent {
290
- contextKey: string;
291
- placement: TimelineLayerDropPlacement;
292
- zIndexChanges: TimelineStackingZIndexChange[];
293
- }
294
-
295
- type BlockedTimelineEditIntent = "move" | "resize-start" | "resize-end";
296
-
297
289
  /**
298
290
  * Source Patcher — Maps visual property edits back to source HTML files.
299
291
  * Handles inline style updates, attribute changes, and text content.
@@ -320,21 +312,63 @@ declare function resolveSourceFile(elementId: string | null, selector: string, f
320
312
  */
321
313
  declare function applyPatch(html: string, elementId: string, op: PatchOperation): string;
322
314
 
323
- interface TimelineGroupMoveChange {
324
- element: TimelineElement;
325
- start: number;
315
+ type BlockedTimelineEditIntent = "move" | "resize-start" | "resize-end";
316
+
317
+ interface DomEditCapabilities {
318
+ canSelect: boolean;
319
+ canEditStyles: boolean;
320
+ /** Can take a non-destructive `clip-path: inset()` crop — broader than
321
+ * canEditStyles (a sub-composition host is croppable from the parent view). */
322
+ canCrop: boolean;
323
+ /** Directly editable authored left/top style fields. Canvas drag uses manual edits instead. */
324
+ canMove: boolean;
325
+ /** Directly editable authored width/height style fields. Canvas resize uses manual edits instead. */
326
+ canResize: boolean;
327
+ canApplyManualOffset: boolean;
328
+ canApplyManualSize: boolean;
329
+ canApplyManualRotation: boolean;
330
+ reasonIfDisabled?: string;
326
331
  }
327
- interface TimelineGroupResizeChange {
328
- element: TimelineElement;
329
- start: number;
330
- duration: number;
331
- playbackStart?: number;
332
+ interface DomEditTextField {
333
+ key: string;
334
+ label: string;
335
+ value: string;
336
+ tagName: string;
337
+ attributes: Array<{
338
+ name: string;
339
+ value: string;
340
+ }>;
341
+ inlineStyles: Record<string, string>;
342
+ computedStyles: Record<string, string>;
343
+ source: "self" | "child" | "text-node";
344
+ sourceChildIndex?: number;
332
345
  }
333
- interface TimelineGroupCommitOptions {
334
- beforeTiming?: Promise<void>;
335
- coalesceKey?: string;
346
+ interface DomEditSelection extends PatchTarget {
347
+ element: HTMLElement;
348
+ label: string;
349
+ tagName: string;
350
+ sourceFile: string;
351
+ compositionPath: string;
352
+ compositionSrc?: string;
353
+ isCompositionHost: boolean;
354
+ isInsideLockedComposition: boolean;
355
+ boundingBox: {
356
+ x: number;
357
+ y: number;
358
+ width: number;
359
+ height: number;
360
+ };
361
+ textContent: string | null;
362
+ dataAttributes: Record<string, string>;
363
+ inlineStyles: Record<string, string>;
364
+ computedStyles: Record<string, string>;
365
+ textFields: DomEditTextField[];
366
+ capabilities: DomEditCapabilities;
367
+ gsapAnimations?: GsapAnimation[];
336
368
  }
337
369
 
370
+ type TimelineMoveOperation = "timing" | "lane-reorder" | "track-insert";
371
+
338
372
  /**
339
373
  * Shared callback signatures for timeline editing operations.
340
374
  * Used by NLELayout, Timeline, and any component that passes through
@@ -355,16 +389,24 @@ interface TimelineDropCallbacks {
355
389
  }) => Promise<void> | void;
356
390
  }
357
391
  interface TimelineEditCallbacks {
358
- onMoveElement?: (element: TimelineElement, updates: Pick<TimelineElement, "start" | "track"> & {
359
- stackingReorder?: TimelineStackingReorderIntent | null;
360
- }) => Promise<void> | void;
392
+ onMoveElement?: (element: TimelineElement, updates: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
393
+ /** Atomic multi-clip move (single undo) for main-track ripple + track-insert.
394
+ * `coalesceKey` (drag-commit gesture id) merges the move history entry with a
395
+ * lane change's follow-up z-reorder entry into one undo step. */
396
+ onMoveElements?: (edits: Array<{
397
+ element: TimelineElement;
398
+ updates: Pick<TimelineElement, "start" | "track">;
399
+ }>, coalesceKey?: string, operation?: TimelineMoveOperation) => Promise<void> | void;
361
400
  onResizeElement?: (element: TimelineElement, updates: Pick<TimelineElement, "start" | "duration" | "playbackStart">) => Promise<void> | void;
362
- onMoveElements?: (changes: TimelineGroupMoveChange[], options?: TimelineGroupCommitOptions) => Promise<void> | void;
363
- onResizeElements?: (changes: TimelineGroupResizeChange[], options?: TimelineGroupCommitOptions) => Promise<void> | void;
364
- onPreviewMoveElements?: (changes: TimelineGroupMoveChange[]) => void;
365
- onPreviewResizeElements?: (changes: TimelineGroupResizeChange[]) => void;
401
+ onResizeElements?: (changes: Array<{
402
+ element: TimelineElement;
403
+ start: number;
404
+ duration: number;
405
+ playbackStart?: number;
406
+ }>, options?: {
407
+ coalesceKey?: string;
408
+ }) => Promise<void> | void;
366
409
  onToggleTrackHidden?: (track: number, hidden: boolean) => Promise<void> | void;
367
- onToggleElementHidden?: (elementKey: string, hidden: boolean) => Promise<void> | void;
368
410
  onBlockedEditAttempt?: (element: TimelineElement, intent: BlockedTimelineEditIntent) => void;
369
411
  onSplitElement?: (element: TimelineElement, splitTime: number) => Promise<void> | void;
370
412
  onRazorSplit?: (element: TimelineElement, splitTime: number) => Promise<void> | void;
@@ -405,7 +447,7 @@ interface TimelineTheme {
405
447
  clipRadius: string;
406
448
  }
407
449
 
408
- type TimelineEditOverrides = Pick<TimelineEditCallbacks, "onMoveElement" | "onResizeElement" | "onBlockedEditAttempt" | "onSplitElement">;
450
+ type TimelineEditOverrides = Pick<TimelineEditCallbacks, "onMoveElement" | "onMoveElements" | "onResizeElement" | "onResizeElements" | "onBlockedEditAttempt" | "onSplitElement">;
409
451
 
410
452
  interface TimelineProps extends TimelineDropCallbacks, TimelineEditOverrides {
411
453
  onSeek?: (time: number) => void;
@@ -498,48 +540,61 @@ declare function useTimelinePlayer(): {
498
540
 
499
541
  declare function formatTime(time: number): string;
500
542
 
501
- interface NLELayoutProps {
502
- projectId: string;
503
- portrait?: boolean;
504
- /** Slot for overlays rendered on top of the preview (cursors, highlights, etc.) */
505
- previewOverlay?: ReactNode;
506
- /** Slot rendered above the timeline tracks (toolbar with split, delete, zoom) */
507
- timelineToolbar?: ReactNode;
508
- /** Slot rendered below the timeline tracks */
509
- timelineFooter?: ReactNode;
510
- /** Increment to force the preview to reload (e.g., after file writes) */
511
- refreshKey?: number;
512
- /** Navigate to a specific composition path (e.g., "compositions/intro.html") */
513
- activeCompositionPath?: string | null;
514
- /** Callback to expose the iframe ref (for element picker, etc.) */
515
- onIframeRef?: (iframe: HTMLIFrameElement | null) => void;
516
- /** Callback when the viewed composition changes (drill-down/back) */
517
- onCompositionChange?: (compositionPath: string | null) => void;
518
- /** Custom clip content renderer for timeline (thumbnails, waveforms, etc.) */
519
- renderClipContent?: (element: TimelineElement, style: {
520
- clip: string;
521
- label: string;
522
- }) => ReactNode;
523
- onFileDrop?: (files: File[], placement?: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
524
- onDeleteElement?: (element: TimelineElement) => Promise<void> | void;
525
- onAssetDrop?: (assetPath: string, placement: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
526
- onBlockDrop?: (blockName: string, placement: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
527
- onPreviewBlockDrop?: (blockName: string, position: {
543
+ interface TimelineEditCallbackDeps {
544
+ handleTimelineElementMove: (element: TimelineElement, updates: Pick<TimelineElement, "start" | "track">) => Promise<void> | void;
545
+ handleTimelineElementsMove: (edits: Array<{
546
+ element: TimelineElement;
547
+ updates: Pick<TimelineElement, "start" | "track">;
548
+ }>, coalesceKey?: string, operation?: TimelineMoveOperation) => Promise<void> | void;
549
+ handleTimelineElementResize: (element: TimelineElement, updates: Pick<TimelineElement, "start" | "duration" | "playbackStart">) => Promise<void> | void;
550
+ handleTimelineGroupResize: NonNullable<TimelineEditCallbacks["onResizeElements"]>;
551
+ handleToggleTrackHidden: (track: number, hidden: boolean) => Promise<void> | void;
552
+ handleBlockedTimelineEdit: (element: TimelineElement, intent: BlockedTimelineEditIntent) => void;
553
+ handleTimelineElementSplit: (element: TimelineElement, splitTime: number) => Promise<void> | void;
554
+ handleRazorSplit: (element: TimelineElement, splitTime: number) => Promise<void> | void;
555
+ handleRazorSplitAll: (splitTime: number) => Promise<void> | void;
556
+ }
557
+
558
+ interface BlockPreviewInfo {
559
+ videoUrl?: string;
560
+ posterUrl?: string;
561
+ title: string;
562
+ }
563
+
564
+ type GestureRecordingState = "idle" | "recording" | "preview";
565
+
566
+ type RenderClipContent = (element: TimelineElement, style: {
567
+ clip: string;
568
+ label: string;
569
+ }) => ReactNode;
570
+ type TimelineDropPlacement = Pick<TimelineElement, "start" | "track">;
571
+ interface EditorShellProps extends TimelineEditCallbackDeps {
572
+ /** Left sidebar (media/library), rendered in the top row. */
573
+ left: ReactNode;
574
+ /** Right panel (inspector/design) or null when collapsed, in the top row. */
575
+ right: ReactNode;
576
+ /** Hide the whole shell (e.g. while the storyboard view is active). */
577
+ hidden?: boolean;
578
+ timelineToolbar: ReactNode;
579
+ renderClipContent: RenderClipContent;
580
+ handleTimelineElementDelete: (element: TimelineElement) => Promise<void> | void;
581
+ handleTimelineAssetDrop: (assetPath: string, placement: TimelineDropPlacement) => Promise<void> | void;
582
+ handleTimelineBlockDrop?: (blockName: string, placement: TimelineDropPlacement) => Promise<void> | void;
583
+ handlePreviewBlockDrop?: (blockName: string, position: {
528
584
  left: number;
529
585
  top: number;
530
586
  }) => Promise<void> | void;
531
- onBlockedEditAttempt?: (element: TimelineElement, intent: BlockedTimelineEditIntent) => void;
532
- onSelectTimelineElement?: (element: TimelineElement | null) => void;
533
- /** Exposes the compIdToSrc map for parent components (e.g., useRenderClipContent) */
534
- onCompIdToSrcChange?: (map: Map<string, string>) => void;
535
- /** Whether the timeline panel is visible (default: true) */
536
- timelineVisible?: boolean;
537
- /** Callback to toggle timeline visibility */
538
- onToggleTimeline?: () => void;
539
- /** Notifies parent when composition loading state changes */
540
- onCompositionLoadingChange?: (loading: boolean) => void;
587
+ handleTimelineFileDrop: (files: File[], placement?: TimelineDropPlacement) => Promise<void> | void;
588
+ setCompIdToSrc: (map: Map<string, string>) => void;
589
+ setCompositionLoading: (loading: boolean) => void;
590
+ shouldShowSelectedDomBounds: boolean;
591
+ blockPreview?: BlockPreviewInfo | null;
592
+ isGestureRecording?: boolean;
593
+ recordingState?: GestureRecordingState;
594
+ onToggleRecording?: () => void;
595
+ gestureOverlay?: ReactNode;
541
596
  }
542
- declare const NLELayout: react.NamedExoticComponent<NLELayoutProps>;
597
+ declare function EditorShell({ left, right, hidden, timelineToolbar, renderClipContent, handleTimelineElementDelete, handleTimelineAssetDrop, handleTimelineBlockDrop, handlePreviewBlockDrop, handleTimelineFileDrop, handleTimelineElementMove, handleTimelineElementsMove, handleTimelineElementResize, handleTimelineGroupResize, handleToggleTrackHidden, handleBlockedTimelineEdit, handleTimelineElementSplit, handleRazorSplit, handleRazorSplitAll, setCompIdToSrc, setCompositionLoading, shouldShowSelectedDomBounds, isGestureRecording, recordingState, onToggleRecording, blockPreview, gestureOverlay, }: EditorShellProps): react_jsx_runtime.JSX.Element;
543
598
 
544
599
  interface NLEPreviewProps {
545
600
  projectId: string;
@@ -583,59 +638,6 @@ interface SourceEditorProps {
583
638
  }
584
639
  declare const SourceEditor: react.NamedExoticComponent<SourceEditorProps>;
585
640
 
586
- interface DomEditCapabilities {
587
- canSelect: boolean;
588
- canEditStyles: boolean;
589
- /** Can take a non-destructive `clip-path: inset()` crop — broader than
590
- * canEditStyles (a sub-composition host is croppable from the parent view). */
591
- canCrop: boolean;
592
- /** Directly editable authored left/top style fields. Canvas drag uses manual edits instead. */
593
- canMove: boolean;
594
- /** Directly editable authored width/height style fields. Canvas resize uses manual edits instead. */
595
- canResize: boolean;
596
- canApplyManualOffset: boolean;
597
- canApplyManualSize: boolean;
598
- canApplyManualRotation: boolean;
599
- reasonIfDisabled?: string;
600
- }
601
- interface DomEditTextField {
602
- key: string;
603
- label: string;
604
- value: string;
605
- tagName: string;
606
- attributes: Array<{
607
- name: string;
608
- value: string;
609
- }>;
610
- inlineStyles: Record<string, string>;
611
- computedStyles: Record<string, string>;
612
- source: "self" | "child" | "text-node";
613
- sourceChildIndex?: number;
614
- }
615
- interface DomEditSelection extends PatchTarget {
616
- element: HTMLElement;
617
- label: string;
618
- tagName: string;
619
- sourceFile: string;
620
- compositionPath: string;
621
- compositionSrc?: string;
622
- isCompositionHost: boolean;
623
- isInsideLockedComposition: boolean;
624
- boundingBox: {
625
- x: number;
626
- y: number;
627
- width: number;
628
- height: number;
629
- };
630
- textContent: string | null;
631
- dataAttributes: Record<string, string>;
632
- inlineStyles: Record<string, string>;
633
- computedStyles: Record<string, string>;
634
- textFields: DomEditTextField[];
635
- capabilities: DomEditCapabilities;
636
- gsapAnimations?: GsapAnimation[];
637
- }
638
-
639
641
  interface ImportedFontAsset {
640
642
  family: string;
641
643
  path: string;
@@ -845,4 +847,4 @@ declare function findElementBlock(html: string, elementId: string): {
845
847
  isSelfClosing: boolean;
846
848
  } | null;
847
849
 
848
- 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 };
850
+ export { CompositionBreadcrumb, type CompositionLevel, CompositionThumbnail, EditorShell, FileTree, 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 };
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
6
6
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
7
  <title>HyperFrames Studio</title>
8
- <script type="module" crossorigin src="/assets/index-pRhCpGPz.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-Dq7FEg0K.css">
8
+ <script type="module" crossorigin src="/assets/index-CmVCjZjp.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-D-GyYi2d.css">
10
10
  </head>
11
11
  <body>
12
12
  <div data-hf-id="hf-aph5" id="root"></div>