@hyperframes/studio 0.8.50 → 0.8.53-desktop.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 (330) hide show
  1. package/dist/assets/{hyperframes-player-CxxDxHTW.js → hyperframes-player-PTEnAHhg.js} +3 -3
  2. package/dist/assets/{index-DUAIWxkI.js → index--myXp8ui.js} +1 -1
  3. package/dist/assets/{index-Dt3qBI-9.js → index-BjslOS9g.js} +1 -1
  4. package/dist/assets/index-BnXZuIJD.css +1 -0
  5. package/dist/assets/index-CNyviK_l.js +390 -0
  6. package/dist/index.css +29 -0
  7. package/dist/index.css.map +1 -0
  8. package/dist/index.d.ts +825 -24
  9. package/dist/index.html +2 -2
  10. package/dist/index.js +8911 -9853
  11. package/dist/index.js.map +1 -1
  12. package/dist/styles/tailwind-preset.js.map +1 -1
  13. package/package.json +13 -12
  14. package/src/App.tsx +145 -163
  15. package/src/captions/components/CaptionAnimationPanel.tsx +4 -4
  16. package/src/captions/components/CaptionOverlay.sender.test.tsx +52 -0
  17. package/src/captions/components/CaptionOverlay.tsx +8 -4
  18. package/src/captions/components/CaptionOverlayUtils.ts +2 -3
  19. package/src/captions/components/CaptionPropertyPanel.tsx +3 -3
  20. package/src/captions/components/CaptionTimeline.tsx +1 -1
  21. package/src/captions/components/shared.tsx +2 -2
  22. package/src/components/AskAgentModal.tsx +5 -5
  23. package/src/components/EditorShell.selectionSync.test.tsx +9 -4
  24. package/src/components/EditorShell.tsx +59 -62
  25. package/src/components/ExternalFileConflictBanner.tsx +16 -16
  26. package/src/components/LintModal.tsx +6 -16
  27. package/src/components/MediaPreview.tsx +2 -2
  28. package/src/components/RightPanelTabs.test.tsx +151 -0
  29. package/src/components/RightPanelTabs.tsx +59 -0
  30. package/src/components/SaveQueuePausedBanner.tsx +2 -2
  31. package/src/components/StudioHeader.dom.test.tsx +181 -0
  32. package/src/components/StudioHeader.test.ts +0 -12
  33. package/src/components/StudioHeader.tsx +118 -221
  34. package/src/components/StudioLeftPanels.tsx +202 -0
  35. package/src/components/StudioOverlays.tsx +1 -1
  36. package/src/components/{StudioRightPanel.tsx → StudioRightPanels.tsx} +57 -194
  37. package/src/components/{StudioRightPanel.types.ts → StudioRightPanels.types.ts} +4 -3
  38. package/src/components/StudioToast.tsx +2 -2
  39. package/src/components/TimelineHistoryButtons.dom.test.tsx +117 -0
  40. package/src/components/TimelineHistoryButtons.tsx +54 -0
  41. package/src/components/TimelineToolbar.test.tsx +8 -0
  42. package/src/components/TimelineToolbar.tsx +8 -12
  43. package/src/components/dock/Dock.test.tsx +274 -0
  44. package/src/components/dock/Dock.tsx +291 -0
  45. package/src/components/dock/dock.css +28 -0
  46. package/src/components/dock/dockAccessibility.test.ts +166 -0
  47. package/src/components/dock/dockAccessibility.ts +98 -0
  48. package/src/components/dock/dockLayout.test.ts +134 -0
  49. package/src/components/dock/dockLayout.ts +105 -0
  50. package/src/components/dock/dockLayoutSchema.ts +66 -0
  51. package/src/components/dock/dockLayoutStore.test.ts +28 -0
  52. package/src/components/dock/dockLayoutStore.ts +108 -0
  53. package/src/components/dock/dockTestHarness.ts +11 -0
  54. package/src/components/dock/panelRegistry.ts +67 -0
  55. package/src/components/editor/AnimationCard.tsx +4 -4
  56. package/src/components/editor/AnimationCardParts.tsx +6 -6
  57. package/src/components/editor/BlockParamsPanel.tsx +4 -4
  58. package/src/components/editor/BorderRadiusEditor.tsx +1 -1
  59. package/src/components/editor/CanvasContextMenu.tsx +1 -1
  60. package/src/components/editor/ComputedTweenNotice.tsx +1 -1
  61. package/src/components/editor/DomEditOverlay.tsx +1 -1
  62. package/src/components/editor/EaseCurveSection.tsx +1 -1
  63. package/src/components/editor/EaseParamFields.tsx +5 -5
  64. package/src/components/editor/FileTree.tsx +4 -4
  65. package/src/components/editor/FileTreeIcons.tsx +1 -1
  66. package/src/components/editor/FileTreeNodes.tsx +13 -13
  67. package/src/components/editor/InlineTextToolbar.tsx +1 -1
  68. package/src/components/editor/InspectorHeaderActions.tsx +3 -3
  69. package/src/components/editor/KeyframeDiamond.tsx +1 -1
  70. package/src/components/editor/KeyframeEaseList.tsx +1 -1
  71. package/src/components/editor/LayersPanel.test.ts +97 -1
  72. package/src/components/editor/LayersPanel.tsx +7 -5
  73. package/src/components/editor/MarqueeOverlay.tsx +1 -1
  74. package/src/components/editor/OffCanvasIndicators.tsx +1 -1
  75. package/src/components/editor/PreviewGuides.tsx +1 -1
  76. package/src/components/editor/PromotableControl.tsx +2 -2
  77. package/src/components/editor/PropertyPanelEmptyState.tsx +7 -7
  78. package/src/components/editor/PropertyPanelFlatHeader.tsx +2 -2
  79. package/src/components/editor/SnapToolbar.test.tsx +0 -3
  80. package/src/components/editor/SnapToolbar.tsx +2 -2
  81. package/src/components/editor/TimelineFxPopover.tsx +1 -1
  82. package/src/components/editor/TopologyLens.test.tsx +28 -0
  83. package/src/components/editor/TopologyLens.tsx +7 -5
  84. package/src/components/editor/Transform3DCube.tsx +2 -2
  85. package/src/components/editor/audioFxRevealTarget.ts +1 -0
  86. package/src/components/editor/domEditing.test.ts +1 -1
  87. package/src/components/editor/domEditingAgentPrompt.test.ts +8 -6
  88. package/src/components/editor/domEditingAgentPrompt.ts +4 -3
  89. package/src/components/editor/propertyPanelColor.tsx +11 -11
  90. package/src/components/editor/propertyPanelColorCurveGraph.tsx +1 -1
  91. package/src/components/editor/propertyPanelColorGradingControls.tsx +7 -9
  92. package/src/components/editor/propertyPanelColorGradingSection.tsx +6 -6
  93. package/src/components/editor/propertyPanelColorGradingSlider.tsx +5 -5
  94. package/src/components/editor/propertyPanelColorWheels.tsx +1 -1
  95. package/src/components/editor/propertyPanelCommitField.tsx +1 -1
  96. package/src/components/editor/propertyPanelFill.tsx +4 -4
  97. package/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx +3 -3
  98. package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +7 -7
  99. package/src/components/editor/propertyPanelFlatEffectsSection.tsx +4 -4
  100. package/src/components/editor/propertyPanelFlatMediaSection.tsx +3 -3
  101. package/src/components/editor/propertyPanelFlatOverlaysSection.tsx +1 -1
  102. package/src/components/editor/propertyPanelFlatPrimitives.tsx +10 -10
  103. package/src/components/editor/propertyPanelFlatSelectRow.tsx +3 -3
  104. package/src/components/editor/propertyPanelFlatTextSection.test.tsx +8 -9
  105. package/src/components/editor/propertyPanelFlatTextSection.tsx +5 -5
  106. package/src/components/editor/propertyPanelFlatToggle.tsx +1 -1
  107. package/src/components/editor/propertyPanelFont.tsx +5 -5
  108. package/src/components/editor/propertyPanelFxCarveModule.tsx +1 -1
  109. package/src/components/editor/propertyPanelFxControls.test.tsx +1 -1
  110. package/src/components/editor/propertyPanelFxControls.tsx +6 -6
  111. package/src/components/editor/propertyPanelFxEqModule.tsx +2 -2
  112. package/src/components/editor/propertyPanelFxPresetStyle.ts +2 -2
  113. package/src/components/editor/propertyPanelGradingNumberField.tsx +1 -1
  114. package/src/components/editor/propertyPanelMediaSection.tsx +1 -1
  115. package/src/components/editor/propertyPanelPrimitives.tsx +7 -7
  116. package/src/components/editor/propertyPanelSections.tsx +8 -8
  117. package/src/components/editor/useColorGradingController.test.ts +38 -0
  118. package/src/components/editor/useColorGradingController.ts +9 -1
  119. package/src/components/editor/useColorGradingPreviews.test.ts +76 -0
  120. package/src/components/editor/useColorGradingPreviews.ts +11 -1
  121. package/src/components/editor/usePreviewCompositionRect.test.tsx +95 -0
  122. package/src/components/editor/usePreviewCompositionRect.ts +21 -0
  123. package/src/components/feedback/StudioFeedbackCard.tsx +7 -7
  124. package/src/components/feedback/feedbackTrigger.ts +0 -1
  125. package/src/components/nle/AssetPreviewOverlay.tsx +6 -2
  126. package/src/components/nle/CompositionBreadcrumb.tsx +3 -3
  127. package/src/components/nle/NLEContext.tsx +0 -43
  128. package/src/components/nle/NLEPreview.tsx +3 -3
  129. package/src/components/nle/PreviewOverlays.tsx +6 -6
  130. package/src/components/nle/PreviewPane.tsx +3 -3
  131. package/src/components/nle/TimelinePane.tsx +53 -67
  132. package/src/components/panels/SlideshowPanel.tsx +3 -3
  133. package/src/components/panels/SlideshowSubPanels.tsx +20 -17
  134. package/src/components/panels/VariablesBindElement.tsx +2 -2
  135. package/src/components/panels/VariablesDeclarationForm.tsx +2 -2
  136. package/src/components/panels/VariablesOtherCompositions.tsx +1 -1
  137. package/src/components/panels/VariablesPanel.tsx +4 -4
  138. package/src/components/panels/VariablesValueControls.tsx +2 -2
  139. package/src/components/pointerTargetSize.test.tsx +8 -0
  140. package/src/components/renders/FfmpegRequiredNotice.tsx +5 -5
  141. package/src/components/renders/RenderQueue.test.tsx +7 -0
  142. package/src/components/renders/RenderQueue.tsx +7 -7
  143. package/src/components/renders/RenderQueueItem.tsx +6 -6
  144. package/src/components/renders/RenderQueuePanel.tsx +1 -1
  145. package/src/components/sidebar/AssetCard.tsx +8 -8
  146. package/src/components/sidebar/AssetContextMenu.tsx +8 -8
  147. package/src/components/sidebar/AssetsTab.tsx +2 -2
  148. package/src/components/sidebar/AudioRow.tsx +6 -6
  149. package/src/components/sidebar/BlocksTab.tsx +6 -6
  150. package/src/components/sidebar/{LeftSidebar.masterComposition.test.tsx → CompositionsPanel.masterComposition.test.tsx} +6 -9
  151. package/src/components/sidebar/CompositionsPanel.tsx +15 -0
  152. package/src/components/sidebar/CompositionsTab.tsx +5 -5
  153. package/src/components/sidebar/GlobalAssetsView.tsx +1 -1
  154. package/src/components/sidebar/PromptPreviewModal.tsx +3 -3
  155. package/src/components/sidebar/SidebarLintButton.test.tsx +53 -0
  156. package/src/components/sidebar/SidebarLintButton.tsx +50 -0
  157. package/src/components/timelineToolbarStyles.ts +5 -0
  158. package/src/components/ui/Button.test.tsx +252 -0
  159. package/src/components/ui/Button.tsx +89 -126
  160. package/src/components/ui/IconButton.tsx +38 -0
  161. package/src/components/ui/Input.tsx +115 -0
  162. package/src/components/ui/Menu.test.tsx +374 -0
  163. package/src/components/ui/Menu.tsx +213 -0
  164. package/src/components/ui/NumberField.tsx +122 -0
  165. package/src/components/ui/Popover.tsx +74 -0
  166. package/src/components/ui/SearchInput.tsx +2 -2
  167. package/src/components/ui/Select.tsx +100 -0
  168. package/src/components/ui/Slider.tsx +153 -0
  169. package/src/components/ui/Tabs.test.tsx +98 -0
  170. package/src/components/ui/Tabs.tsx +71 -0
  171. package/src/components/ui/Toggle.tsx +63 -0
  172. package/src/components/ui/Tooltip.test.tsx +110 -0
  173. package/src/components/ui/Tooltip.tsx +38 -112
  174. package/src/components/ui/cn.test.ts +30 -0
  175. package/src/components/ui/cn.ts +25 -0
  176. package/src/components/ui/index.ts +29 -2
  177. package/src/components/ui/mountHost.testHelpers.ts +24 -0
  178. package/src/components/ui/valueControls.test.tsx +524 -0
  179. package/src/contexts/PanelLayoutContext.tsx +3 -57
  180. package/src/hooks/appHotkeysDispatch.ts +3 -4
  181. package/src/hooks/timelineEditPermission.ts +31 -0
  182. package/src/hooks/useAppHotkeys.previewForwarding.test.tsx +0 -2
  183. package/src/hooks/useAppHotkeys.test.ts +0 -1
  184. package/src/hooks/useAppHotkeys.textEditing.test.tsx +0 -3
  185. package/src/hooks/useAppHotkeys.ts +21 -84
  186. package/src/hooks/useCaptionDetection.sender.test.tsx +46 -0
  187. package/src/hooks/useCaptionDetection.ts +3 -1
  188. package/src/hooks/useDomEditPreviewSync.ts +4 -6
  189. package/src/hooks/useDomEditSession.ts +0 -7
  190. package/src/hooks/useDomEditWiring.ts +4 -9
  191. package/src/hooks/useEditHistoryActions.test.tsx +90 -0
  192. package/src/hooks/useEditHistoryActions.ts +106 -0
  193. package/src/hooks/useFileManager.ts +21 -123
  194. package/src/hooks/useLintModal.test.tsx +65 -0
  195. package/src/hooks/useLintModal.ts +3 -0
  196. package/src/hooks/usePanelLayout.test.ts +108 -296
  197. package/src/hooks/usePanelLayout.ts +50 -255
  198. package/src/hooks/usePersistentEditHistory.ts +1 -1
  199. package/src/hooks/useProjectFileWriter.ts +150 -0
  200. package/src/hooks/useRightPanelIntents.test.ts +181 -0
  201. package/src/hooks/useRightPanelIntents.ts +63 -0
  202. package/src/hooks/useSdkSession.lifecycle.test.tsx +49 -4
  203. package/src/hooks/useSdkSession.ts +43 -10
  204. package/src/hooks/useServerConnection.hashProject.test.tsx +94 -0
  205. package/src/hooks/useServerConnection.ts +43 -10
  206. package/src/hooks/useSlideshowTabState.test.ts +9 -31
  207. package/src/hooks/useSlideshowTabState.ts +6 -19
  208. package/src/hooks/useStudioContextValue.test.ts +6 -18
  209. package/src/hooks/useStudioContextValue.ts +5 -26
  210. package/src/hooks/useTimelineAssetDropOps.test.tsx +41 -1
  211. package/src/hooks/useTimelineAssetDropOps.ts +52 -11
  212. package/src/hooks/useTimelineDeleteOps.test.tsx +111 -0
  213. package/src/hooks/useTimelineEditing.test.tsx +322 -3
  214. package/src/hooks/useTimelineEditing.ts +103 -16
  215. package/src/hooks/useTimelineEditingTypes.ts +3 -0
  216. package/src/hooks/useTrackPendingTimelineEdit.test.tsx +62 -0
  217. package/src/hooks/useTrackPendingTimelineEdit.ts +22 -0
  218. package/src/hostMount.test.tsx +222 -0
  219. package/src/index.ts +87 -0
  220. package/src/main.tsx +9 -3
  221. package/src/player/components/AudioWaveform.tsx +1 -1
  222. package/src/player/components/AutomationSelectionMenu.tsx +3 -3
  223. package/src/player/components/ClipContextMenu.tsx +3 -3
  224. package/src/player/components/CompositionThumbnail.test.ts +0 -3
  225. package/src/player/components/CompositionThumbnail.tsx +3 -4
  226. package/src/player/components/EditModal.tsx +1 -1
  227. package/src/player/components/ImageThumbnail.tsx +1 -1
  228. package/src/player/components/KeyframeDiamondContextMenu.tsx +4 -4
  229. package/src/player/components/LayerDisclosureRow.tsx +2 -2
  230. package/src/player/components/PlayerControls.tsx +1 -1
  231. package/src/player/components/ShortcutsPanel.tsx +9 -9
  232. package/src/player/components/SpeedMenu.tsx +3 -5
  233. package/src/player/components/Timeline.tsx +3 -2
  234. package/src/player/components/TimelineCanvas.tsx +21 -14
  235. package/src/player/components/TimelineClip.tsx +0 -1
  236. package/src/player/components/TimelineDiamondConnectors.tsx +1 -1
  237. package/src/player/components/TimelineEmptyState.tsx +5 -5
  238. package/src/player/components/TimelineFxButton.tsx +6 -6
  239. package/src/player/components/TimelineGroupHeader.tsx +5 -5
  240. package/src/player/components/TimelineGroupLaneLabels.tsx +1 -1
  241. package/src/player/components/TimelineGroupRow.tsx +1 -1
  242. package/src/player/components/TimelineLanes.test.tsx +32 -0
  243. package/src/player/components/TimelineLanes.tsx +2 -5
  244. package/src/player/components/TimelineLayerGroupHeader.tsx +2 -2
  245. package/src/player/components/TimelineRuler.tsx +1 -1
  246. package/src/player/components/TimelineShortcutHint.tsx +1 -1
  247. package/src/player/components/TimelineTrackHeader.test.tsx +26 -5
  248. package/src/player/components/TimelineTrackHeader.tsx +2 -2
  249. package/src/player/components/TimelineTrackPlainHeader.tsx +3 -3
  250. package/src/player/components/TimelineTypes.ts +2 -1
  251. package/src/player/components/TrackGapContextMenu.tsx +1 -1
  252. package/src/player/components/VideoThumbnail.tsx +4 -2
  253. package/src/player/components/VolumeControl.tsx +2 -2
  254. package/src/player/components/timelineCallbacks.ts +8 -8
  255. package/src/player/components/timelineClipDragPreview.ts +51 -27
  256. package/src/player/components/timelineClipDragTypes.ts +3 -0
  257. package/src/player/components/timelineDragDrop.test.tsx +61 -1
  258. package/src/player/components/timelineDragDrop.ts +57 -15
  259. package/src/player/components/timelineLaneProps.ts +3 -0
  260. package/src/player/components/timelineSnapping.test.ts +27 -0
  261. package/src/player/components/timelineSnapping.ts +14 -1
  262. package/src/player/components/timelineTheme.test.ts +4 -4
  263. package/src/player/components/timelineTheme.ts +30 -30
  264. package/src/player/components/trackHeaderLabelRows.tsx +4 -4
  265. package/src/player/components/useTimelineClipDrag.resize.test.tsx +136 -2
  266. package/src/player/components/useTimelineClipDrag.ts +30 -3
  267. package/src/player/lib/describeClips.ts +3 -54
  268. package/src/player/lib/playbackShortcuts.ts +2 -0
  269. package/src/player/lib/timelinePerformanceDiagnostics.test.ts +3 -2
  270. package/src/player/lib/timelinePerformanceDiagnostics.ts +1 -1
  271. package/src/player/store/previewIframeStore.notify.test.tsx +75 -0
  272. package/src/player/store/previewIframeStore.test.tsx +29 -0
  273. package/src/player/store/previewIframeStore.ts +25 -0
  274. package/src/renderQueueExports.test.tsx +58 -0
  275. package/src/styles/classCandidates.test.ts +105 -0
  276. package/src/styles/classCandidates.ts +281 -0
  277. package/src/styles/hex-baseline.json +84 -0
  278. package/src/styles/hexRatchet.test.ts +201 -0
  279. package/src/styles/iconTokens.ts +20 -0
  280. package/src/styles/palette-reset.css +6 -0
  281. package/src/styles/studio.css +24 -21
  282. package/src/styles/styleSources.test.ts +20 -0
  283. package/src/styles/styleSources.ts +47 -0
  284. package/src/styles/tailwind-preset.shared.js +3 -0
  285. package/src/styles/theme.css +270 -0
  286. package/src/styles/theme.test.ts +183 -0
  287. package/src/styles/tokenGate.test.ts +109 -0
  288. package/src/utils/sourcePatcher.ts +1 -1
  289. package/src/utils/studioHelpers.test.ts +18 -0
  290. package/src/utils/studioHelpers.ts +11 -8
  291. package/src/utils/studioUiPreferences.test.ts +10 -16
  292. package/src/utils/studioUiPreferences.ts +7 -16
  293. package/src/utils/timelineDropTrackInsert.test.ts +134 -0
  294. package/src/utils/timelineDropTrackInsert.ts +83 -0
  295. package/src/utils/typingTarget.ts +4 -0
  296. package/dist/assets/index-n2ml4tUn.js +0 -442
  297. package/dist/assets/index-pO6p0H27.css +0 -1
  298. package/src/components/PanelTabButton.tsx +0 -31
  299. package/src/components/StudioLeftSidebar.tsx +0 -204
  300. package/src/components/nle/TimelineResizeDivider.tsx +0 -101
  301. package/src/components/sidebar/LeftSidebar.storage.test.ts +0 -28
  302. package/src/components/sidebar/LeftSidebar.tsx +0 -334
  303. package/src/components/storyboard/AgentChatMessageButton.test.tsx +0 -47
  304. package/src/components/storyboard/AgentChatMessageButton.tsx +0 -45
  305. package/src/components/storyboard/FramePoster.test.tsx +0 -61
  306. package/src/components/storyboard/FramePoster.tsx +0 -86
  307. package/src/components/storyboard/StoryboardDirection.tsx +0 -45
  308. package/src/components/storyboard/StoryboardFrameFocus.tsx +0 -412
  309. package/src/components/storyboard/StoryboardFrameTile.tsx +0 -117
  310. package/src/components/storyboard/StoryboardGrid.tsx +0 -55
  311. package/src/components/storyboard/StoryboardLoaded.tsx +0 -307
  312. package/src/components/storyboard/StoryboardReviewGuide.tsx +0 -300
  313. package/src/components/storyboard/StoryboardScriptPanel.tsx +0 -26
  314. package/src/components/storyboard/StoryboardSourceEditor.tsx +0 -231
  315. package/src/components/storyboard/StoryboardStatusLegend.tsx +0 -21
  316. package/src/components/storyboard/StoryboardView.tsx +0 -169
  317. package/src/components/storyboard/StoryboardViewModeGuard.test.tsx +0 -187
  318. package/src/components/storyboard/frameComments.test.ts +0 -95
  319. package/src/components/storyboard/frameComments.ts +0 -115
  320. package/src/components/storyboard/frameStatus.ts +0 -36
  321. package/src/components/storyboard/storyboardReviewStage.test.ts +0 -40
  322. package/src/components/storyboard/storyboardReviewStage.ts +0 -45
  323. package/src/components/storyboard/useFrameComments.ts +0 -98
  324. package/src/contexts/ViewModeContext.tsx +0 -142
  325. package/src/hooks/useInspectorSplitResize.ts +0 -51
  326. package/src/hooks/useProjectSignaturePoll.test.tsx +0 -135
  327. package/src/hooks/useProjectSignaturePoll.ts +0 -68
  328. package/src/hooks/useStoryboard.ts +0 -99
  329. package/src/utils/fitPanels.test.ts +0 -164
  330. package/src/utils/fitPanels.ts +0 -151
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/styles/tailwind-preset.shared.js","../../src/styles/tailwind-preset.ts"],"sourcesContent":["const studioPreset = {\n theme: {\n extend: {\n colors: {\n studio: {\n bg: \"#0a0a0a\",\n surface: \"#141414\",\n border: \"#262626\",\n text: \"#e5e5e5\",\n muted: \"#737373\",\n accent: \"#3CE6AC\",\n },\n panel: {\n bg: \"#0C0C0E\",\n // Open inspector-section body — slightly lighter than headers (bg)\n // so the recessed scrollable region reads distinct.\n \"bg-inset\": \"#121214\",\n input: \"#161618\",\n surface: \"#18181B\",\n hover: \"#27272A\",\n border: \"#1E1E1E\",\n \"border-input\": \"#27272A\",\n hairline: \"#1A1A1C\",\n \"text-0\": \"#FAFAFA\",\n \"text-1\": \"#E4E4E7\",\n \"text-2\": \"#A1A1AA\",\n \"text-3\": \"#71717A\",\n \"text-4\": \"#52525B\",\n \"text-5\": \"#3F3F46\",\n accent: \"#3CE6AC\",\n danger: \"#EF4444\",\n media: \"#00E3FF\",\n container: \"#F5A623\",\n },\n },\n },\n },\n plugins: [],\n};\n\nexport default studioPreset;\n","import studioPreset from \"./tailwind-preset.shared.js\";\n\nexport default studioPreset;\n"],"mappings":";;;AAAA,IAAM,eAAe;AAAA,EACnB,OAAO;AAAA,IACL,QAAQ;AAAA,MACN,QAAQ;AAAA,QACN,QAAQ;AAAA,UACN,IAAI;AAAA,UACJ,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO;AAAA,UACP,QAAQ;AAAA,QACV;AAAA,QACA,OAAO;AAAA,UACL,IAAI;AAAA;AAAA;AAAA,UAGJ,YAAY;AAAA,UACZ,OAAO;AAAA,UACP,SAAS;AAAA,UACT,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,gBAAgB;AAAA,UAChB,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,WAAW;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,SAAS,CAAC;AACZ;AAEA,IAAO,iCAAQ;;;ACtCf,IAAO,0BAAQ;","names":[]}
1
+ {"version":3,"sources":["../../src/styles/tailwind-preset.shared.js","../../src/styles/tailwind-preset.ts"],"sourcesContent":["/** @deprecated Superseded by `src/styles/theme.css` (published as\n * `@hyperframes/studio/theme.css`); kept one more major for Tailwind v3\n * consumers. Values are byte-identical; theme.test.ts fails if they drift. */\nconst studioPreset = {\n theme: {\n extend: {\n colors: {\n studio: {\n bg: \"#0a0a0a\",\n surface: \"#141414\",\n border: \"#262626\",\n text: \"#e5e5e5\",\n muted: \"#737373\",\n accent: \"#3CE6AC\",\n },\n panel: {\n bg: \"#0C0C0E\",\n // Open inspector-section body — slightly lighter than headers (bg)\n // so the recessed scrollable region reads distinct.\n \"bg-inset\": \"#121214\",\n input: \"#161618\",\n surface: \"#18181B\",\n hover: \"#27272A\",\n border: \"#1E1E1E\",\n \"border-input\": \"#27272A\",\n hairline: \"#1A1A1C\",\n \"text-0\": \"#FAFAFA\",\n \"text-1\": \"#E4E4E7\",\n \"text-2\": \"#A1A1AA\",\n \"text-3\": \"#71717A\",\n \"text-4\": \"#52525B\",\n \"text-5\": \"#3F3F46\",\n accent: \"#3CE6AC\",\n danger: \"#EF4444\",\n media: \"#00E3FF\",\n container: \"#F5A623\",\n },\n },\n },\n },\n plugins: [],\n};\n\nexport default studioPreset;\n","import studioPreset from \"./tailwind-preset.shared.js\";\n\nexport default studioPreset;\n"],"mappings":";;;AAGA,IAAM,eAAe;AAAA,EACnB,OAAO;AAAA,IACL,QAAQ;AAAA,MACN,QAAQ;AAAA,QACN,QAAQ;AAAA,UACN,IAAI;AAAA,UACJ,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO;AAAA,UACP,QAAQ;AAAA,QACV;AAAA,QACA,OAAO;AAAA,UACL,IAAI;AAAA;AAAA;AAAA,UAGJ,YAAY;AAAA,UACZ,OAAO;AAAA,UACP,SAAS;AAAA,UACT,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,gBAAgB;AAAA,UAChB,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,WAAW;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,SAAS,CAAC;AACZ;AAEA,IAAO,iCAAQ;;;ACzCf,IAAO,0BAAQ;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/studio",
3
- "version": "0.8.50",
3
+ "version": "0.8.53-desktop.0",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,12 +23,14 @@
23
23
  "import": "./dist/styles/tailwind-preset.js",
24
24
  "types": "./dist/styles/tailwind-preset.d.ts"
25
25
  },
26
+ "./theme.css": "./src/styles/theme.css",
26
27
  "./package.json": "./package.json"
27
28
  },
28
29
  "publishConfig": {
29
30
  "access": "public"
30
31
  },
31
32
  "dependencies": {
33
+ "@base-ui/react": "1.7.0",
32
34
  "@codemirror/autocomplete": "^6.20.1",
33
35
  "@codemirror/commands": "^6.10.3",
34
36
  "@codemirror/lang-css": "^6.3.1",
@@ -44,32 +46,31 @@
44
46
  "@phosphor-icons/react": "^2.1.10",
45
47
  "@tanstack/react-virtual": "^3.14.6",
46
48
  "bpm-detective": "^2.0.5",
47
- "dompurify": "^3.2.4",
49
+ "cn": "0.2.4",
50
+ "dockview-react": "8.3.1",
48
51
  "gsap": "^3.13.0",
49
- "marked": "^14.1.4",
50
52
  "mediabunny": "^1.45.3",
51
- "@hyperframes/player": "0.8.50",
52
- "@hyperframes/core": "0.8.50",
53
- "@hyperframes/studio-server": "0.8.50",
54
- "@hyperframes/sdk": "0.8.50",
55
- "@hyperframes/parsers": "0.8.50"
53
+ "@hyperframes/parsers": "0.8.53-desktop.0",
54
+ "@hyperframes/core": "0.8.53-desktop.0",
55
+ "@hyperframes/studio-server": "0.8.53-desktop.0",
56
+ "@hyperframes/sdk": "0.8.53-desktop.0",
57
+ "@hyperframes/player": "0.8.53-desktop.0"
56
58
  },
57
59
  "devDependencies": {
60
+ "@tailwindcss/vite": "^4.3.3",
58
61
  "@types/react": "19",
59
62
  "@types/react-dom": "19",
60
63
  "@vitejs/plugin-react": "^4.0.0",
61
- "autoprefixer": "^10.4.0",
62
64
  "chokidar": "^4.0.3",
63
65
  "fake-indexeddb": "^6.2.5",
64
- "postcss": "^8.4.0",
65
66
  "puppeteer-core": "^25.2.1",
66
- "tailwindcss": "^3.4.0",
67
+ "tailwindcss": "^4.3.3",
67
68
  "tsup": "^8.0.0",
68
69
  "typescript": "^5.0.0",
69
70
  "vite": "^6.4.2",
70
71
  "vitest": "^4.1.11",
71
72
  "zustand": "^5.0.0",
72
- "@hyperframes/producer": "0.8.50"
73
+ "@hyperframes/producer": "0.8.53-desktop.0"
73
74
  },
74
75
  "peerDependencies": {
75
76
  "react": "19",
package/src/App.tsx CHANGED
@@ -1,6 +1,7 @@
1
+ import { useOwnPreviewIframe, usePreviewIframeStore } from "./player/store/previewIframeStore";
1
2
  import { buildProjectApiPath } from "./utils/projectRouting";
2
3
  import { useState, useCallback, useRef, useMemo, useLayoutEffect } from "react";
3
- import type { LeftSidebarHandle, SidebarTab } from "./components/sidebar/LeftSidebar";
4
+ import { useDismissingTabSetter, useRightPanelIntent } from "./hooks/useRightPanelIntents";
4
5
  import { useRenderQueue } from "./components/renders/useRenderQueue";
5
6
  import { usePlayerStore } from "./player";
6
7
  import { StudioOverlays } from "./components/StudioOverlays";
@@ -47,14 +48,12 @@ import type { DomEditSelection } from "./components/editor/domEditing";
47
48
  import { StudioHeader } from "./components/StudioHeader";
48
49
  import { useGestureCommit } from "./hooks/useGestureCommit";
49
50
  import { GestureTrailOverlay } from "./components/editor/GestureTrailOverlay";
50
- import { StudioLeftSidebar } from "./components/StudioLeftSidebar";
51
+ import { StudioLeftPanels } from "./components/StudioLeftPanels";
51
52
  import { EditorShell } from "./components/EditorShell";
52
- import { StudioRightPanel } from "./components/StudioRightPanel";
53
+ import { StudioRightPanels } from "./components/StudioRightPanels";
53
54
  import { TimelineToolbar } from "./components/TimelineToolbar";
54
55
  import { StudioPlaybackProvider, StudioShellProvider } from "./contexts/StudioContext";
55
56
  import { PanelLayoutProvider } from "./contexts/PanelLayoutContext";
56
- import { ViewModeProvider, useViewModeState } from "./contexts/ViewModeContext";
57
- import { StoryboardView } from "./components/storyboard/StoryboardView";
58
57
  import { FileManagerProvider } from "./contexts/FileManagerContext";
59
58
  import { DomEditProvider } from "./contexts/DomEditContext";
60
59
  import { StudioSplash } from "./components/StudioSplash";
@@ -68,16 +67,14 @@ const getTimelineSelectionSet = () => usePlayerStore.getState().selectedElementI
68
67
  export function StudioApp() {
69
68
  const { projectId, resolving, waitingForServer } = useServerConnection();
70
69
  const initialUrlStateRef = useRef(readStudioUrlStateFromWindow());
71
- const viewModeValue = useViewModeState();
72
70
  useStudioSessionStart(projectId, resolving, waitingForServer);
73
71
  const [compIdToSrc, setCompIdToSrc] = useState<Map<string, string>>(new Map());
74
- const [previewIframe, setPreviewIframe] = useState<HTMLIFrameElement | null>(null);
72
+ const previewIframe = useOwnPreviewIframe();
75
73
  const [compositionLoading, setCompositionLoading] = useState(true);
76
74
  const [refreshKey, setRefreshKey] = useState(0);
77
75
  const [previewDocumentVersion, refreshPreviewDocumentVersion] = usePreviewDocumentVersion();
78
76
  const [blockPreview, setBlockPreview] = useState<BlockPreviewInfo | null>(null);
79
77
  const previewIframeRef = useRef<HTMLIFrameElement | null>(null);
80
- const leftSidebarRef = useRef<LeftSidebarHandle>(null);
81
78
  const captionEditMode = useCaptionStore((s) => s.isEditMode);
82
79
  const captionHasSelection = useCaptionStore((s) => s.selectedSegmentIds.size > 0);
83
80
  const captionSync = useCaptionSync(projectId);
@@ -204,6 +201,13 @@ export function StudioApp() {
204
201
  setRightCollapsed: panelLayout.setRightCollapsed,
205
202
  setRightPanelTab: panelLayout.setRightPanelTab,
206
203
  });
204
+ const dismissBlockParams = useCallback(() => setActiveBlockParams(null), [setActiveBlockParams]);
205
+ const setRightPanelTab = useDismissingTabSetter(panelLayout.setRightPanelTab, dismissBlockParams);
206
+ const layout = useMemo(
207
+ () => ({ ...panelLayout, setRightPanelTab }),
208
+ [panelLayout, setRightPanelTab],
209
+ );
210
+
207
211
  const clearDomSelectionRef = useRef<() => void>(() => {});
208
212
  const domEditSelectionBridgeRef = useRef<DomEditSelection | null>(null);
209
213
  type DomEditDelete = (s: DomEditSelection, o?: { expandGroup?: boolean }) => Promise<void>;
@@ -236,7 +240,6 @@ export function StudioApp() {
236
240
  showToast,
237
241
  syncHistoryPreviewAfterApply: previewPersistence.syncHistoryPreviewAfterApply,
238
242
  waitForPendingDomEditSaves: previewPersistence.waitForPendingDomEditSaves,
239
- leftSidebarRef,
240
243
  handleCopy,
241
244
  handlePaste,
242
245
  handleCut,
@@ -250,10 +253,6 @@ export function StudioApp() {
250
253
  forceReloadSdkSession: sdkHandle.forceReload,
251
254
  onToggleRecording: () => handleToggleRecordingRef.current(),
252
255
  });
253
- const sidebarTabRef = useRef({
254
- select: (t: SidebarTab) => leftSidebarRef.current?.selectTab(t),
255
- get: () => leftSidebarRef.current?.getTab() ?? "compositions",
256
- });
257
256
  const domEditSession = useDomEditSession({
258
257
  projectId,
259
258
  activeCompPath,
@@ -266,7 +265,7 @@ export function StudioApp() {
266
265
  setSelectedTimelineElementId,
267
266
  setTimelineSelectionSet,
268
267
  setRightCollapsed: panelLayout.setRightCollapsed,
269
- setRightPanelTab: panelLayout.setRightPanelTab,
268
+ setRightPanelTab,
270
269
  showToast,
271
270
  isRecordingRef: isGestureRecordingRef,
272
271
  refreshPreviewDocumentVersion,
@@ -288,8 +287,6 @@ export function StudioApp() {
288
287
  reloadPreview,
289
288
  setRefreshKey,
290
289
  openSourceForSelection: fileManager.openSourceForSelection,
291
- selectSidebarTab: sidebarTabRef.current.select,
292
- getSidebarTab: sidebarTabRef.current.get,
293
290
  sdkSession: editFlowSdkSession,
294
291
  publishSdkSession: sdkHandle.publish,
295
292
  forceReloadSdkSession: sdkHandle.forceReload,
@@ -327,10 +324,8 @@ export function StudioApp() {
327
324
  effectiveTimelineDuration,
328
325
  });
329
326
  const compositionDimensions = useCompositionDimensions(previewIframeRef);
330
- const { lintModal, linting, handleLint, closeLintModal, findingsByFile } = useLintModal(
331
- projectId,
332
- refreshKey,
333
- );
327
+ const { lintModal, linting, handleLint, closeLintModal, findingsByFile, hasLintError } =
328
+ useLintModal(projectId, refreshKey);
334
329
  const frameCapture = useFrameCapture({
335
330
  projectId,
336
331
  activeCompPath,
@@ -364,27 +359,25 @@ export function StudioApp() {
364
359
  const handlePreviewIframeRef = useCallback(
365
360
  (iframe: HTMLIFrameElement | null) => {
366
361
  previewIframeRef.current = iframe;
367
- setPreviewIframe(iframe);
362
+ usePreviewIframeStore.getState().setIframe(iframe);
368
363
  appHotkeys.syncPreviewHotkeys(iframe);
369
364
  resetConsoleErrors();
370
365
  refreshPreviewDocumentVersion();
371
366
  },
372
367
  [appHotkeys, resetConsoleErrors, refreshPreviewDocumentVersion],
373
368
  );
374
- const {
375
- designPanelActive,
376
- inspectorPanelActive,
377
- inspectorButtonActive,
378
- shouldShowMotionPath,
379
- shouldShowSelectedDomBounds,
380
- } = useInspectorState(
381
- panelLayout.rightPanelTab,
382
- panelLayout.rightInspectorPanes,
383
- panelLayout.effectiveRightCollapsed,
384
- isPlaying,
385
- domEditSession.domEditSelection,
386
- gestureState === "recording",
387
- );
369
+ const rightPanel = useRightPanelIntent();
370
+ const { inspectorPanelActive, shouldShowMotionPath, shouldShowSelectedDomBounds } =
371
+ useInspectorState(
372
+ rightPanel,
373
+ isPlaying,
374
+ domEditSession.domEditSelection,
375
+ gestureState === "recording",
376
+ );
377
+ // The dock has no separate "railed by window width" state (it shrinks
378
+ // panels, never auto-hides the group), so rightCollapsed is already the
379
+ // value that decides whether the panel is actually showing.
380
+ const inspectorButtonActive = !panelLayout.rightCollapsed && inspectorPanelActive;
388
381
  useStudioUrlState({
389
382
  projectId,
390
383
  activeCompPath,
@@ -401,7 +394,7 @@ export function StudioApp() {
401
394
  applyMarqueeSelection: domEditSession.applyMarqueeSelection,
402
395
  buildDomSelectionFromTarget: domEditSession.buildDomSelectionFromTarget,
403
396
  applyDomSelection: domEditSession.applyDomSelection,
404
- setRightPanelTab: panelLayout.setRightPanelTab,
397
+ setRightPanelTab,
405
398
  initialState: initialUrlStateRef.current,
406
399
  });
407
400
  const studioCtxValue = buildStudioContextValue({
@@ -441,48 +434,40 @@ export function StudioApp() {
441
434
  return (
442
435
  <StudioShellProvider value={studioCtxValue}>
443
436
  <StudioPlaybackProvider value={studioCtxValue}>
444
- <ViewModeProvider value={viewModeValue}>
445
- <PanelLayoutProvider value={panelLayout}>
446
- <FileManagerProvider value={fileManager}>
447
- <DomEditProvider value={domEditSession}>
448
- <div
449
- className="flex flex-col h-full w-full bg-neutral-950 relative"
450
- onDragOver={fileDrop.onDragOver}
451
- onDrop={fileDrop.onDrop}
452
- >
453
- <StudioHeader
454
- captureFrameHref={frameCapture.captureFrameHref}
455
- captureFrameFilename={frameCapture.captureFrameFilename}
456
- handleCaptureFrameClick={frameCapture.handleCaptureFrameClick}
457
- refreshCaptureFrameTime={frameCapture.refreshCaptureFrameTime}
458
- capturing={frameCapture.capturing}
459
- inspectorButtonActive={inspectorButtonActive}
460
- inspectorPanelActive={inspectorPanelActive}
461
- onExport={() => {
462
- void (async () => {
463
- await previewPersistence.waitForPendingDomEditSaves();
464
- await renderQueue.startRender(undefined);
465
- })();
466
- }}
437
+ <PanelLayoutProvider value={layout}>
438
+ <FileManagerProvider value={fileManager}>
439
+ <DomEditProvider value={domEditSession}>
440
+ <div
441
+ className="flex flex-col h-full w-full bg-neutral-950 relative"
442
+ onDragOver={fileDrop.onDragOver}
443
+ onDrop={fileDrop.onDrop}
444
+ >
445
+ <StudioHeader
446
+ captureFrameHref={frameCapture.captureFrameHref}
447
+ captureFrameFilename={frameCapture.captureFrameFilename}
448
+ handleCaptureFrameClick={frameCapture.handleCaptureFrameClick}
449
+ refreshCaptureFrameTime={frameCapture.refreshCaptureFrameTime}
450
+ capturing={frameCapture.capturing}
451
+ inspectorButtonActive={inspectorButtonActive}
452
+ inspectorPanelActive={inspectorPanelActive}
453
+ onExport={() => {
454
+ void (async () => {
455
+ await previewPersistence.waitForPendingDomEditSaves();
456
+ await renderQueue.startRender(undefined);
457
+ })();
458
+ }}
459
+ />
460
+ {previewPersistence.domEditSaveQueuePaused && !externalFileChanges.blocked && (
461
+ <SaveQueuePausedBanner
462
+ message={previewPersistence.domEditSaveQueuePaused}
463
+ onRetry={previewPersistence.resetDomEditSaveQueueBreaker}
467
464
  />
468
- {previewPersistence.domEditSaveQueuePaused && !externalFileChanges.blocked && (
469
- <SaveQueuePausedBanner
470
- message={previewPersistence.domEditSaveQueuePaused}
471
- onRetry={previewPersistence.resetDomEditSaveQueueBreaker}
472
- />
473
- )}
474
- <ExternalFileConflictBanner coordinator={externalFileChanges} />
475
- {viewModeValue.viewMode === "storyboard" && (
476
- <StoryboardView
477
- projectId={projectId}
478
- onSelectComposition={handleSelectComposition}
479
- />
480
- )}
481
- <EditorShell
482
- hidden={viewModeValue.viewMode === "storyboard"}
483
- left={
484
- <StudioLeftSidebar
485
- leftSidebarRef={leftSidebarRef}
465
+ )}
466
+ <ExternalFileConflictBanner coordinator={externalFileChanges} />
467
+ <EditorShell
468
+ panels={
469
+ <>
470
+ <StudioLeftPanels
486
471
  onSelectComposition={handleSelectComposition}
487
472
  onAddBlock={handleAddBlock}
488
473
  onPreviewBlock={setBlockPreview}
@@ -490,95 +475,92 @@ export function StudioApp() {
490
475
  linting={linting}
491
476
  lintFindingCount={lintModal?.length ?? findingsByFile.size}
492
477
  lintFindingsByFile={findingsByFile}
478
+ lintHasError={hasLintError}
493
479
  onAddAssetToTimeline={handleAddAssetAtPlayhead}
494
480
  onAddCompositionToTimeline={handleAddCompositionAtPlayhead}
495
481
  />
496
- }
497
- right={
498
- panelLayout.effectiveRightCollapsed ? null : (
499
- <StudioRightPanel
500
- designPanelActive={designPanelActive}
501
- activeBlockParams={activeBlockParams}
502
- onCloseBlockParams={() => {
503
- setActiveBlockParams(null);
504
- panelLayout.setRightPanelTab("design");
505
- }}
506
- recordingState={gestureState}
507
- recordingDuration={gestureRecording.recordingDuration}
508
- onToggleRecording={handleToggleRecording}
509
- sdkSession={sdkHandle.session}
510
- publishSdkSession={sdkHandle.publish}
511
- forceReloadSdkSession={sdkHandle.forceReload}
512
- reloadPreview={reloadPreview}
513
- recordEdit={editHistory.recordEdit}
514
- onToggleElementHidden={timelineEditing.handleToggleElementHidden}
515
- onAutoGroupCarveSources={timelineEditing.handleAutoGroupCarveSources}
516
- onAddMediaOverlay={handleAddMediaOverlay}
517
- />
518
- )
519
- }
520
- timelineToolbar={timelineToolbar}
521
- renderClipContent={renderClipContent}
522
- handleTimelineElementDelete={timelineEditing.handleTimelineElementDelete}
523
- handleTimelineAssetDrop={timelineEditing.handleTimelineAssetDrop}
524
- handleTimelineBlockDrop={handleTimelineBlockDrop}
525
- handleTimelineCompositionDrop={timelineEditing.handleTimelineCompositionDrop}
526
- handlePreviewBlockDrop={handlePreviewBlockDrop}
527
- handleTimelineFileDrop={timelineEditing.handleTimelineFileDrop}
528
- handleTimelineElementMove={timelineEditing.handleTimelineElementMove}
529
- handleTimelineElementsMove={handleTimelineElementsMove}
530
- handleTimelineElementResize={timelineEditing.handleTimelineElementResize}
531
- handleTimelineGroupResize={timelineEditing.handleTimelineGroupResize}
532
- handleToggleTrackHidden={timelineEditing.handleToggleTrackHidden}
533
- setAudioGroupAttribute={timelineEditing.setAudioGroupAttribute}
534
- handleGroupClips={timelineEditing.handleAutoGroupCarveSources}
535
- setElementFxAttribute={timelineEditing.setElementFxAttribute}
536
- handleBlockedTimelineEdit={timelineEditing.handleBlockedTimelineEdit}
537
- handleTimelineElementSplit={timelineEditing.handleTimelineElementSplit}
538
- handleRazorSplit={timelineEditing.handleRazorSplit}
539
- handleRazorSplitAll={timelineEditing.handleRazorSplitAll}
540
- onCopyClip={handleCopy}
541
- onPasteClip={handlePaste}
542
- onDuplicateClip={handleDuplicate}
543
- canPasteClip={canPaste}
544
- setCompIdToSrc={setCompIdToSrc}
545
- setCompositionLoading={setCompositionLoading}
546
- shouldShowMotionPath={shouldShowMotionPath}
547
- shouldShowSelectedDomBounds={shouldShowSelectedDomBounds}
548
- isGestureRecording={gestureState === "recording"}
549
- recordingState={gestureState}
550
- onToggleRecording={handleToggleRecording}
551
- blockPreview={blockPreview}
552
- gestureOverlay={
553
- gestureState === "recording" && previewIframe ? (
554
- <GestureTrailOverlay
555
- samples={gestureRecording.samplesRef.current}
556
- sampleCount={gestureRecording.samplesRef.current.length}
557
- trail={gestureRecording.trailRef.current}
558
- canvasRect={canvasRectRef.current!}
559
- compositionSize={compositionDimensions ?? undefined}
560
- mode="recording"
561
- />
562
- ) : undefined
563
- }
564
- />
565
- <StudioOverlays
566
- projectId={projectId}
567
- projectDir={fileManager.projectDir}
568
- lintModal={lintModal}
569
- closeLintModal={closeLintModal}
570
- consoleErrors={consoleErrors}
571
- clearConsoleErrors={() => setConsoleErrors(null)}
572
- domEditSession={domEditSession}
573
- activeCompPath={activeCompPath}
574
- toasts={toasts}
575
- dismissToast={dismissToast}
576
- />
577
- </div>
578
- </DomEditProvider>
579
- </FileManagerProvider>
580
- </PanelLayoutProvider>
581
- </ViewModeProvider>
482
+ <StudioRightPanels
483
+ activeBlockParams={activeBlockParams}
484
+ onDismissBlockParams={dismissBlockParams}
485
+ onCloseBlockParams={() => {
486
+ setActiveBlockParams(null);
487
+ panelLayout.setRightPanelTab("design");
488
+ }}
489
+ recordingState={gestureState}
490
+ recordingDuration={gestureRecording.recordingDuration}
491
+ onToggleRecording={handleToggleRecording}
492
+ sdkSession={sdkHandle.session}
493
+ publishSdkSession={sdkHandle.publish}
494
+ forceReloadSdkSession={sdkHandle.forceReload}
495
+ reloadPreview={reloadPreview}
496
+ recordEdit={editHistory.recordEdit}
497
+ onToggleElementHidden={timelineEditing.handleToggleElementHidden}
498
+ onAutoGroupCarveSources={timelineEditing.handleAutoGroupCarveSources}
499
+ onAddMediaOverlay={handleAddMediaOverlay}
500
+ />
501
+ </>
502
+ }
503
+ timelineToolbar={timelineToolbar}
504
+ renderClipContent={renderClipContent}
505
+ handleTimelineElementDelete={timelineEditing.handleTimelineElementDelete}
506
+ handleTimelineAssetDrop={timelineEditing.handleTimelineAssetDrop}
507
+ handleTimelineBlockDrop={handleTimelineBlockDrop}
508
+ handleTimelineCompositionDrop={timelineEditing.handleTimelineCompositionDrop}
509
+ handlePreviewBlockDrop={handlePreviewBlockDrop}
510
+ handleTimelineFileDrop={timelineEditing.handleTimelineFileDrop}
511
+ handleTimelineElementMove={timelineEditing.handleTimelineElementMove}
512
+ handleTimelineElementsMove={handleTimelineElementsMove}
513
+ handleTimelineElementResize={timelineEditing.handleTimelineElementResize}
514
+ handleTimelineGroupResize={timelineEditing.handleTimelineGroupResize}
515
+ handleToggleTrackHidden={timelineEditing.handleToggleTrackHidden}
516
+ setAudioGroupAttribute={timelineEditing.setAudioGroupAttribute}
517
+ handleGroupClips={timelineEditing.handleAutoGroupCarveSources}
518
+ setElementFxAttribute={timelineEditing.setElementFxAttribute}
519
+ handleBlockedTimelineEdit={timelineEditing.handleBlockedTimelineEdit}
520
+ handleTimelineElementSplit={timelineEditing.handleTimelineElementSplit}
521
+ handleRazorSplit={timelineEditing.handleRazorSplit}
522
+ handleRazorSplitAll={timelineEditing.handleRazorSplitAll}
523
+ onCopyClip={handleCopy}
524
+ onPasteClip={handlePaste}
525
+ onDuplicateClip={handleDuplicate}
526
+ canPasteClip={canPaste}
527
+ setCompIdToSrc={setCompIdToSrc}
528
+ setCompositionLoading={setCompositionLoading}
529
+ shouldShowMotionPath={shouldShowMotionPath}
530
+ shouldShowSelectedDomBounds={shouldShowSelectedDomBounds}
531
+ isGestureRecording={gestureState === "recording"}
532
+ recordingState={gestureState}
533
+ onToggleRecording={handleToggleRecording}
534
+ blockPreview={blockPreview}
535
+ gestureOverlay={
536
+ gestureState === "recording" && previewIframe ? (
537
+ <GestureTrailOverlay
538
+ samples={gestureRecording.samplesRef.current}
539
+ sampleCount={gestureRecording.samplesRef.current.length}
540
+ trail={gestureRecording.trailRef.current}
541
+ canvasRect={canvasRectRef.current!}
542
+ compositionSize={compositionDimensions ?? undefined}
543
+ mode="recording"
544
+ />
545
+ ) : undefined
546
+ }
547
+ />
548
+ <StudioOverlays
549
+ projectId={projectId}
550
+ projectDir={fileManager.projectDir}
551
+ lintModal={lintModal}
552
+ closeLintModal={closeLintModal}
553
+ consoleErrors={consoleErrors}
554
+ clearConsoleErrors={() => setConsoleErrors(null)}
555
+ domEditSession={domEditSession}
556
+ activeCompPath={activeCompPath}
557
+ toasts={toasts}
558
+ dismissToast={dismissToast}
559
+ />
560
+ </div>
561
+ </DomEditProvider>
562
+ </FileManagerProvider>
563
+ </PanelLayoutProvider>
582
564
  </StudioPlaybackProvider>
583
565
  </StudioShellProvider>
584
566
  );
@@ -171,7 +171,7 @@ function AnimationPhase({
171
171
  }}
172
172
  className="flex-1 accent-studio-accent disabled:opacity-40"
173
173
  />
174
- <span className="text-2xs text-neutral-400 font-mono w-8 text-right flex-shrink-0">
174
+ <span className="text-2xs text-neutral-400 font-mono w-8 text-right shrink-0">
175
175
  {intensity.toFixed(2)}
176
176
  </span>
177
177
  </div>
@@ -249,7 +249,7 @@ export const CaptionAnimationPanel = memo(function CaptionAnimationPanel() {
249
249
  return (
250
250
  <div className="flex flex-col h-full min-h-0">
251
251
  {gated && (
252
- <div className="flex-shrink-0 mx-3 mt-2 px-2 py-1.5 rounded border border-amber-500/30 bg-amber-500/10">
252
+ <div className="shrink-0 mx-3 mt-2 px-2 py-1.5 rounded-sm border border-amber-500/30 bg-amber-500/10">
253
253
  <p className="text-2xs text-amber-300/90 leading-snug">
254
254
  Animation editing isn&apos;t applied to playback or saved yet, so these controls are
255
255
  disabled.
@@ -286,13 +286,13 @@ export const CaptionAnimationPanel = memo(function CaptionAnimationPanel() {
286
286
  </div>
287
287
 
288
288
  {/* Footer */}
289
- <div className="flex-shrink-0 px-3 py-2 border-t border-neutral-800">
289
+ <div className="shrink-0 px-3 py-2 border-t border-neutral-800">
290
290
  <button
291
291
  type="button"
292
292
  onClick={handleApplyToAll}
293
293
  disabled={gated}
294
294
  title={gated ? "Disabled until animation editing is applied to playback" : undefined}
295
- className="w-full py-1.5 rounded border border-neutral-700 text-2xs text-neutral-300 hover:border-studio-accent/50 hover:text-studio-accent transition-colors disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:border-neutral-700 disabled:hover:text-neutral-300"
295
+ className="w-full py-1.5 rounded-sm border border-neutral-700 text-2xs text-neutral-300 hover:border-studio-accent/50 hover:text-studio-accent transition-colors disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:border-neutral-700 disabled:hover:text-neutral-300"
296
296
  >
297
297
  Apply to all groups
298
298
  </button>
@@ -4,6 +4,7 @@ import { createRoot } from "react-dom/client";
4
4
  import { expect, it, vi } from "vitest";
5
5
  import { CaptionOverlay } from "./CaptionOverlay";
6
6
  import { useCaptionStore } from "../store";
7
+ import { usePreviewIframeStore } from "../../player/store/previewIframeStore";
7
8
 
8
9
  Reflect.set(globalThis, "IS_REACT_ACT_ENVIRONMENT", true);
9
10
 
@@ -45,3 +46,54 @@ it("only schedules caption layout for messages from its preview", () => {
45
46
  vi.unstubAllGlobals();
46
47
  }
47
48
  });
49
+
50
+ it("observes and accepts messages from the promoted iframe, not the old one", () => {
51
+ const host = document.createElement("div");
52
+ const a = document.createElement("iframe");
53
+ const b = document.createElement("iframe");
54
+ document.body.append(host, a, b);
55
+ const ref = { current: a };
56
+ const root = createRoot(host);
57
+ const schedule = vi.fn(() => 1);
58
+ const observed: Element[] = [];
59
+ vi.stubGlobal("requestAnimationFrame", schedule);
60
+ vi.stubGlobal("cancelAnimationFrame", vi.fn());
61
+ vi.stubGlobal(
62
+ "ResizeObserver",
63
+ class {
64
+ observe(el: Element) {
65
+ observed.push(el);
66
+ }
67
+ disconnect() {}
68
+ },
69
+ );
70
+ useCaptionStore.getState().setEditMode(true);
71
+ const send = (source: MessageEventSource | null) =>
72
+ act(() => {
73
+ window.dispatchEvent(new MessageEvent("message", { source, data: { source: "hf-preview" } }));
74
+ });
75
+ try {
76
+ act(() => usePreviewIframeStore.getState().setIframe(a));
77
+ act(() => root.render(<CaptionOverlay iframeRef={ref} />));
78
+ expect(observed).toContain(a);
79
+ expect(observed).not.toContain(b);
80
+ act(() => {
81
+ ref.current = b;
82
+ usePreviewIframeStore.getState().setIframe(b);
83
+ });
84
+ expect(observed).toContain(b);
85
+ schedule.mockClear();
86
+ send(a.contentWindow);
87
+ expect(schedule).not.toHaveBeenCalled();
88
+ send(b.contentWindow);
89
+ expect(schedule).toHaveBeenCalledOnce();
90
+ } finally {
91
+ act(() => root.unmount());
92
+ usePreviewIframeStore.setState({ iframe: null });
93
+ useCaptionStore.getState().reset();
94
+ host.remove();
95
+ a.remove();
96
+ b.remove();
97
+ vi.unstubAllGlobals();
98
+ }
99
+ });
@@ -1,7 +1,7 @@
1
- import { memo, useState, useCallback, useRef } from "react";
1
+ import { memo, useEffect, useState, useCallback, useRef } from "react";
2
+ import { useLivePreviewIframe } from "../../player/store/previewIframeStore";
2
3
  import { useCaptionStore } from "../store";
3
4
  import { usePlayerStore } from "../../player";
4
- import { useMountEffect } from "../../hooks/useMountEffect";
5
5
  import { shouldHandleCaptionNudgeKey, isEditableEventTarget } from "../keyboard";
6
6
  import {
7
7
  readWordBoxes,
@@ -32,6 +32,7 @@ function syncToStore(segmentId: string, el: HTMLElement, iframeWin: Window) {
32
32
 
33
33
  export const CaptionOverlay = memo(function CaptionOverlay({ iframeRef }: CaptionOverlayProps) {
34
34
  const isEditMode = useCaptionStore((s) => s.isEditMode);
35
+ const livePreviewIframe = useLivePreviewIframe();
35
36
  const model = useCaptionStore((s) => s.model);
36
37
  const selectedSegmentIds = useCaptionStore((s) => s.selectedSegmentIds);
37
38
  const selectSegment = useCaptionStore((s) => s.selectSegment);
@@ -81,7 +82,9 @@ export const CaptionOverlay = memo(function CaptionOverlay({ iframeRef }: Captio
81
82
  | null
82
83
  >(null);
83
84
 
84
- useMountEffect(() => {
85
+ // The observer and message filter bind to the live iframe element, which a promoted reload replaces.
86
+ // eslint-disable-next-line no-restricted-syntax
87
+ useEffect(() => {
85
88
  if (!isEditMode) return;
86
89
 
87
90
  // Let undo/redo (useAppHotkeys) reapply restored models to this iframe.
@@ -240,7 +243,8 @@ export const CaptionOverlay = memo(function CaptionOverlay({ iframeRef }: Captio
240
243
  window.removeEventListener("keydown", handleKeyDown);
241
244
  cancelDragRef.current = null;
242
245
  };
243
- });
246
+ // eslint-disable-next-line react-hooks/exhaustive-deps
247
+ }, [livePreviewIframe]);
244
248
 
245
249
  const getCssScale = useCallback(() => {
246
250
  const iframe = iframeRef.current;