@hyperframes/studio 0.8.47 → 0.8.49

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 (123) hide show
  1. package/dist/assets/hyperframes-player-CqON2Rzo.js +464 -0
  2. package/dist/assets/index-B9LfWNyM.css +1 -0
  3. package/dist/assets/{index-ryL7q1QG.js → index-BGovDksE.js} +1 -1
  4. package/dist/assets/{index-j-4hCrZU.js → index-Bgmx2h7W.js} +208 -208
  5. package/dist/assets/{index-B50xQfSG.js → index-mIgSsKqs.js} +1 -1
  6. package/dist/index.d.ts +70 -26
  7. package/dist/index.html +2 -2
  8. package/dist/index.js +1452 -918
  9. package/dist/index.js.map +1 -1
  10. package/package.json +7 -7
  11. package/src/App.tsx +17 -26
  12. package/src/components/AskAgentModal.tsx +9 -7
  13. package/src/components/TimelineToolbar.tsx +26 -1
  14. package/src/components/editor/PropertyPanelFlat.tsx +8 -8
  15. package/src/components/editor/domEditing.ts +7 -1
  16. package/src/components/editor/domEditingAgentPrompt.test.ts +199 -0
  17. package/src/components/editor/domEditingAgentPrompt.ts +136 -34
  18. package/src/components/editor/propertyPanelAutomation.test.ts +82 -1
  19. package/src/components/editor/propertyPanelAutomation.ts +15 -0
  20. package/src/components/editor/propertyPanelFlatMediaSection.test.tsx +35 -0
  21. package/src/components/editor/propertyPanelFlatMediaSection.tsx +23 -9
  22. package/src/components/editor/useVolumeAutomation.test.tsx +81 -10
  23. package/src/components/editor/useVolumeAutomation.ts +23 -2
  24. package/src/components/panels/VariablesPanel.tsx +5 -8
  25. package/src/components/sidebar/CompositionsTab.rootBadge.test.tsx +59 -0
  26. package/src/components/sidebar/CompositionsTab.tsx +15 -0
  27. package/src/components/sidebar/LeftSidebar.masterComposition.test.tsx +47 -0
  28. package/src/components/sidebar/LeftSidebar.tsx +7 -0
  29. package/src/contexts/FileManagerContext.tsx +0 -3
  30. package/src/hooks/appHotkeysDispatch.ts +256 -0
  31. package/src/hooks/domEditDeleteMembers.test.ts +30 -0
  32. package/src/hooks/domEditDeleteMembers.ts +19 -0
  33. package/src/hooks/useActiveComposition.ts +69 -0
  34. package/src/hooks/useAppHotkeys.test.ts +23 -1
  35. package/src/hooks/useAppHotkeys.ts +6 -239
  36. package/src/hooks/useAutoOpenRootComposition.test.tsx +151 -0
  37. package/src/hooks/useAutoOpenRootComposition.ts +33 -0
  38. package/src/hooks/useBlockHandlers.ts +7 -2
  39. package/src/hooks/useDomEditCommits.ts +13 -15
  40. package/src/hooks/useDomEditSession.test.tsx +90 -1
  41. package/src/hooks/useDomEditSession.ts +31 -34
  42. package/src/hooks/useDomSelectionTypes.ts +7 -2
  43. package/src/hooks/useElementLifecycleOps.ts +0 -8
  44. package/src/hooks/useFileManager.projectOwnership.test.tsx +0 -1
  45. package/src/hooks/useFileManager.ts +0 -2
  46. package/src/hooks/useFileTree.test.tsx +214 -0
  47. package/src/hooks/useFileTree.ts +66 -24
  48. package/src/hooks/useGroupCommits.test.ts +98 -0
  49. package/src/hooks/useGroupCommits.ts +66 -9
  50. package/src/hooks/useResetSelectionOnProjectSwitch.test.tsx +234 -0
  51. package/src/hooks/useResetSelectionOnProjectSwitch.ts +34 -0
  52. package/src/hooks/useTimelineAssetDropOps.test.tsx +87 -0
  53. package/src/hooks/useTimelineAssetDropOps.ts +24 -18
  54. package/src/hooks/useTimelineDeleteOps.test.tsx +119 -0
  55. package/src/hooks/useTimelineDeleteOps.ts +103 -9
  56. package/src/hooks/useTimelineEditing.ts +1 -0
  57. package/src/hooks/useTimelineGroupEditing.test.tsx +74 -0
  58. package/src/hooks/useTimelineGroupEditing.ts +19 -5
  59. package/src/hooks/useTimelineSelectionPreviewSync.test.tsx +215 -3
  60. package/src/hooks/useTimelineSelectionPreviewSync.ts +63 -32
  61. package/src/index.ts +10 -0
  62. package/src/player/components/Timeline.test.ts +0 -4
  63. package/src/player/components/Timeline.tsx +0 -1
  64. package/src/player/components/TimelineAutomationLaneSlot.tsx +4 -2
  65. package/src/player/components/TimelineClipDiamonds.test.tsx +13 -1
  66. package/src/player/components/TimelineDiamondConnectors.test.ts +16 -0
  67. package/src/player/components/TimelineDiamondConnectors.tsx +17 -1
  68. package/src/player/components/TimelinePropertyLanes.test.tsx +1 -1
  69. package/src/player/components/automationLaneData.test.ts +11 -3
  70. package/src/player/components/automationLaneData.ts +5 -4
  71. package/src/player/components/automationLaneGeometry.ts +15 -0
  72. package/src/player/components/timelineClipDragCommit.test.ts +114 -0
  73. package/src/player/components/timelineClipDragCommit.ts +18 -49
  74. package/src/player/components/timelineClipDragPreview.test.ts +173 -1
  75. package/src/player/components/timelineClipDragPreview.ts +8 -1
  76. package/src/player/components/timelineCollision.test.ts +23 -0
  77. package/src/player/components/timelineCollision.ts +27 -0
  78. package/src/player/components/timelineDragDrop.test.tsx +21 -4
  79. package/src/player/components/timelineDragDrop.ts +9 -25
  80. package/src/player/components/timelineDragLanding.ts +108 -0
  81. package/src/player/components/timelineDropReveal.test.ts +34 -0
  82. package/src/player/components/timelineDropReveal.ts +12 -0
  83. package/src/player/components/timelineGapCommit.test.ts +96 -0
  84. package/src/player/components/timelineGapCommit.ts +56 -11
  85. package/src/player/components/timelineGaps.ts +2 -2
  86. package/src/player/components/timelineLayout.test.ts +0 -1
  87. package/src/player/components/timelineLayout.ts +1 -6
  88. package/src/player/components/timelineZones.test.ts +20 -1
  89. package/src/player/components/timelineZones.ts +10 -3
  90. package/src/player/components/useAutomationLaneGestures.ts +2 -10
  91. package/src/player/components/useTimelineFocusCoordinator.test.tsx +66 -0
  92. package/src/player/components/useTimelineFocusCoordinator.ts +43 -3
  93. package/src/player/components/useTimelineGeometry.test.tsx +117 -0
  94. package/src/player/components/useTimelineGeometry.ts +7 -2
  95. package/src/player/components/useTimelineGeometryFocusOrder.test.tsx +184 -0
  96. package/src/player/components/useTimelineLogicalFocus.ts +1 -0
  97. package/src/player/components/useTimelineTrackLayout.ts +7 -6
  98. package/src/player/hooks/usePlaybackKeyboard.test.ts +34 -0
  99. package/src/player/hooks/usePlaybackKeyboard.ts +13 -0
  100. package/src/player/lib/timelineElementHelpers.ts +8 -0
  101. package/src/player/lib/timelineIframeHelpers.test.ts +22 -0
  102. package/src/player/lib/timelineIframeHelpers.ts +1 -0
  103. package/src/player/store/playerStore.ts +16 -21
  104. package/src/player/store/readinessSlice.ts +4 -3
  105. package/src/utils/blockInstaller.test.ts +4 -0
  106. package/src/utils/blockInstaller.ts +9 -3
  107. package/src/utils/razorSplitTransaction.test.ts +10 -0
  108. package/src/utils/razorSplitTransaction.ts +5 -0
  109. package/src/utils/retryBudget.ts +20 -0
  110. package/src/utils/sdkCutover.familyGate.test.ts +5 -1
  111. package/src/utils/sdkCutover.test.ts +51 -0
  112. package/src/utils/sdkCutover.ts +7 -4
  113. package/src/utils/sdkCutoverEligibility.test.ts +96 -1
  114. package/src/utils/sdkCutoverEligibility.ts +34 -11
  115. package/src/utils/studioHelpers.test.ts +18 -0
  116. package/src/utils/studioHelpers.ts +41 -5
  117. package/src/utils/studioUiPreferences.test.ts +14 -0
  118. package/src/utils/studioUiPreferences.ts +6 -0
  119. package/src/utils/studioUrlState.ts +15 -7
  120. package/src/utils/timelineCompositionInsert.ts +2 -1
  121. package/src/utils/timelineInspector.ts +10 -0
  122. package/dist/assets/hyperframes-player-B7fsB8C2.js +0 -464
  123. package/dist/assets/index-DnRfAiK2.css +0 -1
@@ -1 +1 @@
1
- import{g as P}from"./index-j-4hCrZU.js";function j(c,d){for(var s=0;s<d.length;s++){const a=d[s];if(typeof a!="string"&&!Array.isArray(a)){for(const i in a)if(i!=="default"&&!(i in c)){const l=Object.getOwnPropertyDescriptor(a,i);l&&Object.defineProperty(c,i,l.get?l:{enumerable:!0,get:()=>a[i]})}}}return Object.freeze(Object.defineProperty(c,Symbol.toStringTag,{value:"Module"}))}var v={},w;function k(){if(w)return v;w=1,Object.defineProperty(v,"__esModule",{value:!0}),v.default=d;var c=window.OfflineAudioContext||window.webkitOfflineAudioContext;function d(e){var r=a(e);return r.start(0),[i,y,O(e.sampleRate),s].reduce(function(t,o){return o(t)},r.buffer.getChannelData(0))}function s(e){return e.sort(function(r,t){return t.count-r.count}).splice(0,5)[0].tempo}function a(e){var r=e.length,t=e.numberOfChannels,o=e.sampleRate,n=new c(t,r,o),u=n.createBufferSource();u.buffer=e;var f=n.createBiquadFilter();return f.type="lowpass",u.connect(f),f.connect(n.destination),u}function i(e){for(var r=[],t=.9,o=.3,n=15;r.length<n&&t>=o;)r=l(e,t),t-=.05;if(r.length<n)throw new Error("Could not find enough samples for a reliable detection.");return r}function l(e,r){for(var t=[],o=0,n=e.length;o<n;o+=1)e[o]>r&&(t.push(o),o+=1e4);return t}function y(e){var r=[];return e.forEach(function(t,o){for(var n=function(x){var g=e[o+x]-t,_=r.some(function(h){if(h.interval===g)return h.count+=1});_||r.push({interval:g,count:1})},u=0;u<10;u+=1)n(u)}),r}function O(e){return function(r){var t=[];return r.forEach(function(o){if(o.interval!==0){for(var n=60/(o.interval/e);n<90;)n*=2;for(;n>180;)n/=2;n=Math.round(n);var u=t.some(function(f){if(f.tempo===n)return f.count+=o.count});u||t.push({tempo:n,count:o.count})}}),t}}return v}var p,b;function q(){return b||(b=1,p=k().default),p}var m=q();const A=P(m),D=j({__proto__:null,default:A},[m]);export{D as i};
1
+ import{g as P}from"./index-Bgmx2h7W.js";function j(c,d){for(var s=0;s<d.length;s++){const a=d[s];if(typeof a!="string"&&!Array.isArray(a)){for(const i in a)if(i!=="default"&&!(i in c)){const l=Object.getOwnPropertyDescriptor(a,i);l&&Object.defineProperty(c,i,l.get?l:{enumerable:!0,get:()=>a[i]})}}}return Object.freeze(Object.defineProperty(c,Symbol.toStringTag,{value:"Module"}))}var v={},w;function k(){if(w)return v;w=1,Object.defineProperty(v,"__esModule",{value:!0}),v.default=d;var c=window.OfflineAudioContext||window.webkitOfflineAudioContext;function d(e){var r=a(e);return r.start(0),[i,y,O(e.sampleRate),s].reduce(function(t,o){return o(t)},r.buffer.getChannelData(0))}function s(e){return e.sort(function(r,t){return t.count-r.count}).splice(0,5)[0].tempo}function a(e){var r=e.length,t=e.numberOfChannels,o=e.sampleRate,n=new c(t,r,o),u=n.createBufferSource();u.buffer=e;var f=n.createBiquadFilter();return f.type="lowpass",u.connect(f),f.connect(n.destination),u}function i(e){for(var r=[],t=.9,o=.3,n=15;r.length<n&&t>=o;)r=l(e,t),t-=.05;if(r.length<n)throw new Error("Could not find enough samples for a reliable detection.");return r}function l(e,r){for(var t=[],o=0,n=e.length;o<n;o+=1)e[o]>r&&(t.push(o),o+=1e4);return t}function y(e){var r=[];return e.forEach(function(t,o){for(var n=function(x){var g=e[o+x]-t,_=r.some(function(h){if(h.interval===g)return h.count+=1});_||r.push({interval:g,count:1})},u=0;u<10;u+=1)n(u)}),r}function O(e){return function(r){var t=[];return r.forEach(function(o){if(o.interval!==0){for(var n=60/(o.interval/e);n<90;)n*=2;for(;n>180;)n/=2;n=Math.round(n);var u=t.some(function(f){if(f.tempo===n)return f.count+=o.count});u||t.push({tempo:n,count:o.count})}}),t}}return v}var p,b;function q(){return b||(b=1,p=k().default),p}var m=q();const A=P(m),D=j({__proto__:null,default:A},[m]);export{D as i};
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, RefObject } from 'react';
4
- import { RuntimeTimelineClipIdentity } from '@hyperframes/core';
4
+ import { RuntimeTimelineClipIdentity, HyperframePickerElementInfo } from '@hyperframes/core';
5
5
  import { GsapAnimation, PropertyGroupName } from '@hyperframes/core/gsap-parser';
6
6
  import * as zustand from 'zustand';
7
7
  import { StoreApi } from 'zustand';
@@ -252,9 +252,10 @@ interface ThumbnailSlice {
252
252
  }
253
253
 
254
254
  /** Gates `timelineReady` on the composition's declared readiness inputs
255
- * (media, for now) instead of just a known duration. The generation counter
256
- * is module-scope, not store state: it guards an in-flight settlement, not
257
- * something a component reads, so bumping it shouldn't trigger a render. */
255
+ * (media, compute, and the paint-and-idle default) instead of just a known
256
+ * duration. The generation counter is module-scope, not store state: it
257
+ * guards an in-flight settlement, not something a component reads, so
258
+ * bumping it shouldn't trigger a render. */
258
259
 
259
260
  interface PlaybackReadinessSlice {
260
261
  timelineReady: boolean;
@@ -443,14 +444,14 @@ interface PlayerState extends PlayerStoreSlices {
443
444
  /** Timeline magnet toggle — when false, clip drags/trims/drops never snap. */
444
445
  timelineSnapEnabled: boolean;
445
446
  setTimelineSnapEnabled: (enabled: boolean) => void;
447
+ /** Keeps the main track gapless on delete; distinct from the magnet above. */
448
+ rippleEditEnabled: boolean;
449
+ setRippleEditEnabled: (enabled: boolean) => void;
446
450
  /** Transport + ruler readout: timecode ("time") or frame number ("frame"). */
447
451
  timeDisplayMode: TimelineTimeDisplayMode;
448
452
  setTimeDisplayMode: (mode: TimelineTimeDisplayMode) => void;
449
- /**
450
- * Pin the timeline zoom to its current visual scale before a duration-changing
451
- * edit, so a subsequent duration change (which recomputes fit-pps) stops
452
- * rescaling every clip. No-op once already pinned (mode is "manual").
453
- */
453
+ /** Pin the timeline zoom to its current scale before a duration change, so
454
+ * it stops rescaling every clip. No-op once already pinned. */
454
455
  pinTimelineZoom: (currentPixelsPerSecond: number, fitPixelsPerSecond: number) => void;
455
456
  /** The timeline's live pixels-per-second + fit basis, published by <Timeline>. */
456
457
  timelinePps: number;
@@ -482,25 +483,15 @@ interface PlayerState extends PlayerStoreSlices {
482
483
  beginTimelineSession: (projectId: string) => void;
483
484
  /** Clears project data without creating a new hard-project session. */
484
485
  reset: () => void;
485
- /**
486
- * Request a seek from outside the player loop (e.g. Layers panel).
487
- * useTimelinePlayer subscribes and calls adapter.seek() + liveTime.notify().
488
- */
486
+ /** Request a seek from outside the player loop (e.g. Layers panel);
487
+ * useTimelinePlayer subscribes and calls adapter.seek() + liveTime.notify(). */
489
488
  requestedSeekTime: number | null;
490
489
  requestSeek: (time: number) => void;
491
490
  clearSeekRequest: () => void;
492
- /**
493
- * Request the transport start or stop from outside the player loop.
494
- *
495
- * The FX rack auditions a preset by writing it to the running graph, which is
496
- * silent while the transport is paused — so hovering one has to start
497
- * playback, and leaving has to put the playhead back where it was. Hovering is
498
- * not an edit and must not cost the author their place.
499
- *
500
- * A nonce rather than a bare boolean: two hovers in a row both want play, and
501
- * without it the second request is indistinguishable from the first having
502
- * already been served.
503
- */
491
+ /** Request the transport start or stop from outside the player loop: the FX
492
+ * rack starts playback to audition a preset (silent while paused) and
493
+ * restores the playhead on leave, without costing the author their place.
494
+ * A nonce, not a bare boolean, so two hovers in a row both register. */
504
495
  playbackRequest: {
505
496
  playing: boolean;
506
497
  returnTo: number | null;
@@ -675,6 +666,45 @@ interface DomEditSelection extends PatchTarget {
675
666
  gsapAnimations?: GsapAnimation$1[];
676
667
  }
677
668
 
669
+ /**
670
+ * Agent prompt builder for HyperFrames element edit requests.
671
+ */
672
+
673
+ /**
674
+ * The subset of an element selection shared by the Studio DOM editor (`DomEditSelection`) and the
675
+ * runtime picker (`HyperframePickerElementInfo`); a field absent from a caller's type is simply omitted.
676
+ */
677
+ interface AgentPromptElementInfo {
678
+ id: string | null;
679
+ selector?: string | null;
680
+ /** Disambiguates duplicate selectors in `DomEditSelection`. The picker
681
+ * payload has no such concept (one picked node, not a candidate list), so
682
+ * a picker-built prompt always prints index 0 — expected, not a gap. */
683
+ selectorIndex?: number;
684
+ tagName: string;
685
+ label?: string;
686
+ boundingBox: {
687
+ x: number;
688
+ y: number;
689
+ width: number;
690
+ height: number;
691
+ };
692
+ textContent: string | null;
693
+ src?: string | null;
694
+ textFields?: DomEditTextField[];
695
+ inlineStyles?: Record<string, string>;
696
+ computedStyles?: Record<string, string>;
697
+ }
698
+ /**
699
+ * Same convention as `buildElementAgentPrompt`, for a host app embedding only the player: built from
700
+ * the runtime picker's own payload, not `DomEditSelection`. Null selection yields a bare prompt, no guardrails.
701
+ */
702
+ declare function buildPickerAgentPrompt({ selection, userInstruction, }: {
703
+ selection: HyperframePickerElementInfo | null;
704
+ userInstruction?: string;
705
+ }): string;
706
+ declare function buildPickerAgentContextPreview(selection: HyperframePickerElementInfo | null): string;
707
+
678
708
  type BlockedTimelineEditIntent = "move" | "resize-start" | "resize-end";
679
709
 
680
710
  type TimelineMoveOperation = "timing" | "lane-reorder" | "track-insert";
@@ -1216,6 +1246,20 @@ declare const FileTree: react.NamedExoticComponent<FileTreeProps>;
1216
1246
 
1217
1247
  declare function StudioApp(): react_jsx_runtime.JSX.Element;
1218
1248
 
1249
+ interface AgentModalAnchorPoint {
1250
+ x: number;
1251
+ y: number;
1252
+ }
1253
+
1254
+ interface AskAgentModalProps {
1255
+ selectionLabel: string;
1256
+ contextPreview?: string;
1257
+ anchorPoint?: AgentModalAnchorPoint | null;
1258
+ onSubmit: (instruction: string) => void;
1259
+ onClose: () => void;
1260
+ }
1261
+ declare function AskAgentModal({ selectionLabel, contextPreview, anchorPoint, onSubmit, onClose, }: AskAgentModalProps): react_jsx_runtime.JSX.Element;
1262
+
1219
1263
  interface PickedElement {
1220
1264
  id: string | null;
1221
1265
  tagName: string;
@@ -1291,4 +1335,4 @@ declare function findElementBlock(html: string, elementId: string): {
1291
1335
  isSelfClosing: boolean;
1292
1336
  } | null;
1293
1337
 
1294
- 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 };
1338
+ export { type AgentModalAnchorPoint, type AgentPromptElementInfo, AskAgentModal, type AskAgentModalProps, CompositionBreadcrumb, type CompositionLevel, CompositionThumbnail, EditorShell, FileTree, NLEPreview, type PatchOperation, type PickedElement, Player, PlayerControls, PropertyPanel, SourceEditor, StudioApp, Timeline, type TimelineElement, VideoThumbnail, applyPatch, buildPickerAgentContextPreview, buildPickerAgentPrompt, findElementBlock, formatTime, liveTime, mergeStyleIntoTag, parseStyleString, resolveIframe, resolveSourceFile, useElementPicker, usePlayerStore, useTimelinePlayer };
package/dist/index.html CHANGED
@@ -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-j-4hCrZU.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-DnRfAiK2.css">
8
+ <script type="module" crossorigin src="/assets/index-Bgmx2h7W.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-B9LfWNyM.css">
10
10
  </head>
11
11
  <body>
12
12
  <div data-hf-id="hf-aph5" id="root"></div>