@hyperframes/studio 0.8.49 → 0.8.51

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 (281) hide show
  1. package/dist/assets/{hyperframes-player-CqON2Rzo.js → hyperframes-player-Cn_PpHqP.js} +3 -3
  2. package/dist/assets/index-C05SNrvY.js +360 -0
  3. package/dist/assets/{index-mIgSsKqs.js → index-CPS3Cvj8.js} +1 -1
  4. package/dist/assets/{index-BGovDksE.js → index-Clrb11lj.js} +1 -1
  5. package/dist/assets/index-DUR9qzQM.css +1 -0
  6. package/dist/index.d.ts +29 -16
  7. package/dist/index.html +2 -2
  8. package/dist/index.js +7283 -8606
  9. package/dist/index.js.map +1 -1
  10. package/dist/styles/tailwind-preset.js.map +1 -1
  11. package/package.json +11 -12
  12. package/src/App.tsx +132 -143
  13. package/src/captions/components/CaptionAnimationPanel.tsx +4 -4
  14. package/src/captions/components/CaptionOverlayUtils.ts +2 -3
  15. package/src/captions/components/CaptionPropertyPanel.tsx +3 -3
  16. package/src/captions/components/CaptionTimeline.tsx +1 -1
  17. package/src/captions/components/shared.tsx +2 -2
  18. package/src/components/AskAgentModal.tsx +5 -5
  19. package/src/components/EditorShell.selectionSync.test.tsx +4 -0
  20. package/src/components/EditorShell.tsx +28 -7
  21. package/src/components/ExternalFileConflictBanner.tsx +16 -16
  22. package/src/components/LintModal.tsx +6 -16
  23. package/src/components/MediaPreview.tsx +2 -2
  24. package/src/components/SaveQueuePausedBanner.tsx +2 -2
  25. package/src/components/StudioHeader.tsx +2 -62
  26. package/src/components/StudioLeftSidebar.tsx +7 -4
  27. package/src/components/StudioOverlays.tsx +2 -6
  28. package/src/components/StudioRightPanel.tsx +4 -4
  29. package/src/components/StudioToast.tsx +2 -2
  30. package/src/components/TimelineToolbar.tsx +7 -7
  31. package/src/components/dock/Dock.test.tsx +192 -0
  32. package/src/components/dock/Dock.tsx +264 -0
  33. package/src/components/dock/dock.css +23 -0
  34. package/src/components/dock/dockLayout.ts +58 -0
  35. package/src/components/dock/dockLayoutSchema.ts +66 -0
  36. package/src/components/dock/dockLayoutStore.ts +91 -0
  37. package/src/components/dock/panelRegistry.ts +67 -0
  38. package/src/components/editor/AnimationCard.tsx +4 -4
  39. package/src/components/editor/AnimationCardParts.tsx +6 -6
  40. package/src/components/editor/BlockParamsPanel.tsx +4 -4
  41. package/src/components/editor/BorderRadiusEditor.tsx +1 -1
  42. package/src/components/editor/CanvasContextMenu.tsx +1 -1
  43. package/src/components/editor/ComputedTweenNotice.tsx +1 -1
  44. package/src/components/editor/DomEditOverlay.tsx +1 -1
  45. package/src/components/editor/EaseCurveSection.tsx +1 -1
  46. package/src/components/editor/EaseParamFields.tsx +5 -5
  47. package/src/components/editor/FileTree.tsx +4 -4
  48. package/src/components/editor/FileTreeIcons.tsx +1 -1
  49. package/src/components/editor/FileTreeNodes.tsx +13 -13
  50. package/src/components/editor/InlineTextToolbar.tsx +1 -1
  51. package/src/components/editor/InspectorHeaderActions.tsx +3 -3
  52. package/src/components/editor/KeyframeDiamond.tsx +1 -1
  53. package/src/components/editor/KeyframeEaseList.tsx +1 -1
  54. package/src/components/editor/LayersPanel.tsx +3 -3
  55. package/src/components/editor/MarqueeOverlay.tsx +1 -1
  56. package/src/components/editor/OffCanvasIndicators.tsx +1 -1
  57. package/src/components/editor/PreviewGuides.test.tsx +88 -0
  58. package/src/components/editor/PreviewGuides.tsx +125 -0
  59. package/src/components/editor/PromotableControl.tsx +2 -2
  60. package/src/components/editor/PropertyPanelEmptyState.tsx +7 -7
  61. package/src/components/editor/PropertyPanelFlatHeader.tsx +2 -2
  62. package/src/components/editor/SnapToolbar.test.tsx +21 -0
  63. package/src/components/editor/SnapToolbar.tsx +27 -3
  64. package/src/components/editor/TimelineFxPopover.tsx +1 -1
  65. package/src/components/editor/TopologyLens.tsx +4 -4
  66. package/src/components/editor/Transform3DCube.tsx +2 -2
  67. package/src/components/editor/audioFxRevealTarget.ts +1 -0
  68. package/src/components/editor/domEditing.test.ts +1 -1
  69. package/src/components/editor/domEditingAgentPrompt.test.ts +8 -6
  70. package/src/components/editor/domEditingAgentPrompt.ts +9 -3
  71. package/src/components/editor/previewGuidesStore.ts +26 -0
  72. package/src/components/editor/propertyPanelColor.tsx +11 -11
  73. package/src/components/editor/propertyPanelColorCurveGraph.tsx +1 -1
  74. package/src/components/editor/propertyPanelColorGradingControls.tsx +7 -9
  75. package/src/components/editor/propertyPanelColorGradingSection.tsx +6 -6
  76. package/src/components/editor/propertyPanelColorGradingSlider.tsx +5 -5
  77. package/src/components/editor/propertyPanelColorWheels.tsx +1 -1
  78. package/src/components/editor/propertyPanelCommitField.tsx +1 -1
  79. package/src/components/editor/propertyPanelFill.tsx +4 -4
  80. package/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx +3 -3
  81. package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +7 -7
  82. package/src/components/editor/propertyPanelFlatEffectsSection.tsx +4 -4
  83. package/src/components/editor/propertyPanelFlatMediaSection.tsx +3 -3
  84. package/src/components/editor/propertyPanelFlatOverlaysSection.tsx +1 -1
  85. package/src/components/editor/propertyPanelFlatPrimitives.tsx +10 -10
  86. package/src/components/editor/propertyPanelFlatSelectRow.tsx +3 -3
  87. package/src/components/editor/propertyPanelFlatTextSection.test.tsx +8 -9
  88. package/src/components/editor/propertyPanelFlatTextSection.tsx +5 -5
  89. package/src/components/editor/propertyPanelFlatToggle.tsx +1 -1
  90. package/src/components/editor/propertyPanelFont.tsx +5 -5
  91. package/src/components/editor/propertyPanelFxCarveModule.tsx +1 -1
  92. package/src/components/editor/propertyPanelFxControls.test.tsx +1 -1
  93. package/src/components/editor/propertyPanelFxControls.tsx +6 -6
  94. package/src/components/editor/propertyPanelFxEqModule.tsx +2 -2
  95. package/src/components/editor/propertyPanelFxPresetStyle.ts +2 -2
  96. package/src/components/editor/propertyPanelGradingNumberField.tsx +1 -1
  97. package/src/components/editor/propertyPanelMediaSection.tsx +1 -1
  98. package/src/components/editor/propertyPanelPrimitives.tsx +7 -7
  99. package/src/components/editor/propertyPanelSections.tsx +8 -8
  100. package/src/components/feedback/StudioFeedbackCard.tsx +7 -7
  101. package/src/components/feedback/feedbackTrigger.ts +0 -1
  102. package/src/components/nle/AssetPreviewOverlay.tsx +6 -2
  103. package/src/components/nle/CompositionBreadcrumb.tsx +3 -3
  104. package/src/components/nle/NLEPreview.test.ts +13 -0
  105. package/src/components/nle/NLEPreview.tsx +12 -7
  106. package/src/components/nle/PreviewOverlays.tsx +6 -6
  107. package/src/components/nle/PreviewPane.test.tsx +52 -0
  108. package/src/components/nle/PreviewPane.tsx +4 -2
  109. package/src/components/nle/TimelinePane.tsx +17 -3
  110. package/src/components/nle/TimelineResizeDivider.tsx +2 -2
  111. package/src/components/nle/useCanvasZOrderTimelineMirror.test.tsx +1 -1
  112. package/src/components/nle/useCanvasZOrderTimelineMirror.ts +3 -3
  113. package/src/components/panels/SlideshowPanel.tsx +2 -2
  114. package/src/components/panels/SlideshowSubPanels.tsx +20 -17
  115. package/src/components/panels/VariablesBindElement.tsx +2 -2
  116. package/src/components/panels/VariablesDeclarationForm.tsx +2 -2
  117. package/src/components/panels/VariablesOtherCompositions.tsx +1 -1
  118. package/src/components/panels/VariablesPanel.tsx +3 -3
  119. package/src/components/panels/VariablesValueControls.tsx +2 -2
  120. package/src/components/renders/FfmpegRequiredNotice.tsx +5 -5
  121. package/src/components/renders/RenderQueue.test.tsx +7 -0
  122. package/src/components/renders/RenderQueue.tsx +6 -6
  123. package/src/components/renders/RenderQueueItem.tsx +6 -6
  124. package/src/components/sidebar/AssetCard.tsx +8 -8
  125. package/src/components/sidebar/AssetContextMenu.tsx +8 -8
  126. package/src/components/sidebar/AssetsTab.tsx +2 -2
  127. package/src/components/sidebar/AudioRow.tsx +6 -6
  128. package/src/components/sidebar/BlocksTab.tsx +6 -6
  129. package/src/components/sidebar/CompositionsTab.tsx +5 -5
  130. package/src/components/sidebar/GlobalAssetsView.tsx +1 -1
  131. package/src/components/sidebar/LeftSidebar.lintBadge.test.tsx +58 -0
  132. package/src/components/sidebar/LeftSidebar.tsx +19 -5
  133. package/src/components/sidebar/PromptPreviewModal.tsx +3 -3
  134. package/src/components/ui/Button.test.tsx +60 -0
  135. package/src/components/ui/Button.tsx +4 -4
  136. package/src/components/ui/SearchInput.tsx +2 -2
  137. package/src/components/ui/Tooltip.tsx +1 -1
  138. package/src/hooks/appHotkeysDispatch.ts +11 -0
  139. package/src/hooks/timelineAudioGroupCreate.ts +2 -2
  140. package/src/hooks/timelineTrackVisibility.ts +8 -11
  141. package/src/hooks/useAppHotkeys.textEditing.test.tsx +1 -0
  142. package/src/hooks/useAppHotkeys.ts +4 -1
  143. package/src/hooks/useAskAgentModal.ts +2 -0
  144. package/src/hooks/useClipboard.test.ts +234 -0
  145. package/src/hooks/useClipboard.ts +264 -70
  146. package/src/hooks/useGroupCommits.test.ts +0 -1
  147. package/src/hooks/useLintModal.test.tsx +65 -0
  148. package/src/hooks/useLintModal.ts +3 -0
  149. package/src/hooks/useRazorSplit.history.test.tsx +0 -1
  150. package/src/hooks/useRazorSplit.test.ts +0 -2
  151. package/src/hooks/useRazorSplit.test.tsx +0 -1
  152. package/src/hooks/useSdkSession.lifecycle.test.tsx +49 -4
  153. package/src/hooks/useSdkSession.ts +43 -10
  154. package/src/hooks/useStudioContextValue.ts +5 -19
  155. package/src/hooks/useTimelineAssetDropOps.test.tsx +41 -1
  156. package/src/hooks/useTimelineAssetDropOps.ts +52 -11
  157. package/src/hooks/useTimelineEditing.test.tsx +0 -1
  158. package/src/main.tsx +9 -3
  159. package/src/player/components/AudioWaveform.tsx +1 -1
  160. package/src/player/components/AutomationSelectionMenu.tsx +2 -2
  161. package/src/player/components/ClipContextMenu.tsx +90 -10
  162. package/src/player/components/CompositionThumbnail.test.ts +0 -3
  163. package/src/player/components/CompositionThumbnail.tsx +3 -4
  164. package/src/player/components/EditModal.tsx +1 -1
  165. package/src/player/components/ImageThumbnail.tsx +1 -1
  166. package/src/player/components/KeyframeDiamondContextMenu.tsx +4 -4
  167. package/src/player/components/LayerDisclosureRow.tsx +2 -2
  168. package/src/player/components/PlayerControls.tsx +1 -1
  169. package/src/player/components/ShortcutsPanel.tsx +9 -9
  170. package/src/player/components/SpeedMenu.tsx +3 -5
  171. package/src/player/components/Timeline.tsx +16 -15
  172. package/src/player/components/TimelineCanvas.tsx +45 -8
  173. package/src/player/components/TimelineDiamondConnectors.tsx +1 -1
  174. package/src/player/components/TimelineEmptyState.tsx +5 -5
  175. package/src/player/components/TimelineFxButton.tsx +6 -6
  176. package/src/player/components/TimelineGestureOverlay.test.tsx +35 -1
  177. package/src/player/components/TimelineGestureOverlay.tsx +1 -1
  178. package/src/player/components/TimelineGroupHeader.tsx +5 -5
  179. package/src/player/components/TimelineGroupLaneLabels.tsx +1 -1
  180. package/src/player/components/TimelineGroupRow.tsx +1 -1
  181. package/src/player/components/TimelineLanes.test.tsx +54 -1
  182. package/src/player/components/TimelineLanes.tsx +2 -5
  183. package/src/player/components/TimelineLayerGroupHeader.tsx +2 -2
  184. package/src/player/components/TimelineOverlays.tsx +12 -0
  185. package/src/player/components/TimelineRuler.tsx +1 -1
  186. package/src/player/components/TimelineShortcutHint.tsx +1 -1
  187. package/src/player/components/TimelineTrackHeader.tsx +1 -1
  188. package/src/player/components/TimelineTrackPlainHeader.tsx +3 -3
  189. package/src/player/components/TimelineTypes.ts +6 -1
  190. package/src/player/components/TrackGapContextMenu.tsx +1 -1
  191. package/src/player/components/VideoThumbnail.tsx +4 -2
  192. package/src/player/components/VolumeControl.tsx +2 -2
  193. package/src/player/components/timelineAuthoredMoveTarget.ts +1 -2
  194. package/src/player/components/timelineCallbacks.ts +8 -8
  195. package/src/player/components/timelineClipChildren.test.ts +2 -2
  196. package/src/player/components/timelineClipChildren.tsx +2 -1
  197. package/src/player/components/timelineClipDragCommit.ts +2 -2
  198. package/src/player/components/timelineClipDragPreview.ts +51 -27
  199. package/src/player/components/timelineClipDragTypes.ts +3 -0
  200. package/src/player/components/timelineDragDrop.test.tsx +92 -1
  201. package/src/player/components/timelineDragDrop.ts +82 -33
  202. package/src/player/components/timelineEditCapabilities.ts +1 -2
  203. package/src/player/components/timelineEditing.test.ts +0 -16
  204. package/src/player/components/timelineGroupEditing.test.ts +1 -3
  205. package/src/player/components/timelineGroupEditing.ts +0 -1
  206. package/src/player/components/timelineLaneProps.ts +3 -0
  207. package/src/player/components/timelineSnapping.test.ts +27 -0
  208. package/src/player/components/timelineSnapping.ts +14 -1
  209. package/src/player/components/timelineTrackDisplay.ts +1 -1
  210. package/src/player/components/timelineTrackPersistPipeline.test.ts +0 -53
  211. package/src/player/components/trackHeaderLabelRows.tsx +4 -4
  212. package/src/player/components/useTimelineClipContextMenu.ts +32 -0
  213. package/src/player/components/useTimelineClipDrag.resize.test.tsx +136 -2
  214. package/src/player/components/useTimelineClipDrag.ts +30 -3
  215. package/src/player/components/useTimelineTrackDerivations.ts +1 -1
  216. package/src/player/hooks/timelineDomBinding.test.ts +0 -3
  217. package/src/player/hooks/timelineSyncHydration.test.ts +18 -0
  218. package/src/player/hooks/timelineSyncHydration.ts +37 -18
  219. package/src/player/hooks/useTimelinePlayer.test.ts +3 -20
  220. package/src/player/hooks/useTimelineRowElements.test.ts +28 -0
  221. package/src/player/hooks/useTimelineRowElements.ts +17 -0
  222. package/src/player/hooks/useTimelineSyncCallbacks.emptyManifest.test.tsx +41 -0
  223. package/src/player/hooks/useTimelineSyncCallbacks.ts +9 -10
  224. package/src/player/lib/describeClips.test.ts +95 -0
  225. package/src/player/lib/describeClips.ts +52 -0
  226. package/src/player/lib/timelineDOM.test.ts +0 -80
  227. package/src/player/lib/timelineDOM.ts +1 -61
  228. package/src/player/lib/timelineElementHelpers.ts +2 -53
  229. package/src/player/store/playerStore.ts +5 -0
  230. package/src/player/store/timelineElement.ts +1 -3
  231. package/src/shims/canvasBrowserStub.js +50 -0
  232. package/src/styles/iconTokens.ts +20 -0
  233. package/src/styles/palette-reset.css +6 -0
  234. package/src/styles/studio.css +24 -21
  235. package/src/styles/tailwind-preset.shared.js +3 -0
  236. package/src/styles/theme.css +213 -0
  237. package/src/styles/theme.test.ts +196 -0
  238. package/src/utils/clipboardPayload.test.ts +42 -2
  239. package/src/utils/clipboardPayload.ts +57 -32
  240. package/src/utils/previewSafeMargins.test.ts +34 -0
  241. package/src/utils/previewSafeMargins.ts +10 -0
  242. package/src/utils/razorSplitTransaction.test.ts +0 -1
  243. package/src/utils/sdkCanvasComposition.test.ts +77 -0
  244. package/src/utils/studioUiPreferences.test.ts +43 -0
  245. package/src/utils/studioUiPreferences.ts +34 -6
  246. package/src/utils/timelineDropTrackInsert.test.ts +134 -0
  247. package/src/utils/timelineDropTrackInsert.ts +83 -0
  248. package/src/utils/timelineElementSplit.test.ts +1 -2
  249. package/src/utils/timelineElementSplit.ts +0 -1
  250. package/src/webmcp/tools/lookTools.test.ts +40 -0
  251. package/src/webmcp/tools/lookTools.ts +32 -1
  252. package/dist/assets/index-B9LfWNyM.css +0 -1
  253. package/dist/assets/index-Bgmx2h7W.js +0 -441
  254. package/src/components/StudioGlobalDragOverlay.tsx +0 -24
  255. package/src/components/storyboard/AgentChatMessageButton.test.tsx +0 -47
  256. package/src/components/storyboard/AgentChatMessageButton.tsx +0 -45
  257. package/src/components/storyboard/FramePoster.test.tsx +0 -61
  258. package/src/components/storyboard/FramePoster.tsx +0 -86
  259. package/src/components/storyboard/StoryboardDirection.tsx +0 -45
  260. package/src/components/storyboard/StoryboardFrameFocus.tsx +0 -412
  261. package/src/components/storyboard/StoryboardFrameTile.tsx +0 -117
  262. package/src/components/storyboard/StoryboardGrid.tsx +0 -55
  263. package/src/components/storyboard/StoryboardLoaded.tsx +0 -307
  264. package/src/components/storyboard/StoryboardReviewGuide.tsx +0 -300
  265. package/src/components/storyboard/StoryboardScriptPanel.tsx +0 -26
  266. package/src/components/storyboard/StoryboardSourceEditor.tsx +0 -231
  267. package/src/components/storyboard/StoryboardStatusLegend.tsx +0 -21
  268. package/src/components/storyboard/StoryboardView.tsx +0 -169
  269. package/src/components/storyboard/StoryboardViewModeGuard.test.tsx +0 -187
  270. package/src/components/storyboard/frameComments.test.ts +0 -95
  271. package/src/components/storyboard/frameComments.ts +0 -115
  272. package/src/components/storyboard/frameStatus.ts +0 -36
  273. package/src/components/storyboard/storyboardReviewStage.test.ts +0 -40
  274. package/src/components/storyboard/storyboardReviewStage.ts +0 -45
  275. package/src/components/storyboard/useFrameComments.ts +0 -98
  276. package/src/contexts/ViewModeContext.tsx +0 -142
  277. package/src/hooks/useProjectSignaturePoll.test.tsx +0 -135
  278. package/src/hooks/useProjectSignaturePoll.ts +0 -68
  279. package/src/hooks/useStoryboard.ts +0 -99
  280. package/src/player/hooks/useExpandedTimelineElements.test.ts +0 -774
  281. package/src/player/hooks/useExpandedTimelineElements.ts +0 -460
@@ -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.49",
3
+ "version": "0.8.51",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,6 +23,7 @@
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": {
@@ -44,32 +45,30 @@
44
45
  "@phosphor-icons/react": "^2.1.10",
45
46
  "@tanstack/react-virtual": "^3.14.6",
46
47
  "bpm-detective": "^2.0.5",
47
- "dompurify": "^3.2.4",
48
+ "dockview-react": "8.3.1",
48
49
  "gsap": "^3.13.0",
49
- "marked": "^14.1.4",
50
50
  "mediabunny": "^1.45.3",
51
- "@hyperframes/parsers": "0.8.49",
52
- "@hyperframes/studio-server": "0.8.49",
53
- "@hyperframes/player": "0.8.49",
54
- "@hyperframes/sdk": "0.8.49",
55
- "@hyperframes/core": "0.8.49"
51
+ "@hyperframes/core": "0.8.51",
52
+ "@hyperframes/player": "0.8.51",
53
+ "@hyperframes/studio-server": "0.8.51",
54
+ "@hyperframes/sdk": "0.8.51",
55
+ "@hyperframes/parsers": "0.8.51"
56
56
  },
57
57
  "devDependencies": {
58
+ "@tailwindcss/vite": "^4.3.3",
58
59
  "@types/react": "19",
59
60
  "@types/react-dom": "19",
60
61
  "@vitejs/plugin-react": "^4.0.0",
61
- "autoprefixer": "^10.4.0",
62
62
  "chokidar": "^4.0.3",
63
63
  "fake-indexeddb": "^6.2.5",
64
- "postcss": "^8.4.0",
65
64
  "puppeteer-core": "^25.2.1",
66
- "tailwindcss": "^3.4.0",
65
+ "tailwindcss": "^4.3.3",
67
66
  "tsup": "^8.0.0",
68
67
  "typescript": "^5.0.0",
69
68
  "vite": "^6.4.2",
70
69
  "vitest": "^4.1.11",
71
70
  "zustand": "^5.0.0",
72
- "@hyperframes/producer": "0.8.49"
71
+ "@hyperframes/producer": "0.8.51"
73
72
  },
74
73
  "peerDependencies": {
75
74
  "react": "19",
package/src/App.tsx CHANGED
@@ -53,8 +53,6 @@ import { StudioRightPanel } from "./components/StudioRightPanel";
53
53
  import { TimelineToolbar } from "./components/TimelineToolbar";
54
54
  import { StudioPlaybackProvider, StudioShellProvider } from "./contexts/StudioContext";
55
55
  import { PanelLayoutProvider } from "./contexts/PanelLayoutContext";
56
- import { ViewModeProvider, useViewModeState } from "./contexts/ViewModeContext";
57
- import { StoryboardView } from "./components/storyboard/StoryboardView";
58
56
  import { FileManagerProvider } from "./contexts/FileManagerContext";
59
57
  import { DomEditProvider } from "./contexts/DomEditContext";
60
58
  import { StudioSplash } from "./components/StudioSplash";
@@ -68,7 +66,6 @@ const getTimelineSelectionSet = () => usePlayerStore.getState().selectedElementI
68
66
  export function StudioApp() {
69
67
  const { projectId, resolving, waitingForServer } = useServerConnection();
70
68
  const initialUrlStateRef = useRef(readStudioUrlStateFromWindow());
71
- const viewModeValue = useViewModeState();
72
69
  useStudioSessionStart(projectId, resolving, waitingForServer);
73
70
  const [compIdToSrc, setCompIdToSrc] = useState<Map<string, string>>(new Map());
74
71
  const [previewIframe, setPreviewIframe] = useState<HTMLIFrameElement | null>(null);
@@ -211,7 +208,7 @@ export function StudioApp() {
211
208
  const domEditDeleteBridge: DomEditDelete = (s, o) => handleDomEditElementDeleteRef.current(s, o);
212
209
  const resetKeyframesRef = useRef<() => boolean>(() => false);
213
210
  const deleteSelectedKeyframesRef = useRef<() => void>(() => {});
214
- const { handleCopy, handlePaste, handleCut } = useClipboard({
211
+ const { handleCopy, handlePaste, handleCut, handleDuplicate, canPaste } = useClipboard({
215
212
  projectId,
216
213
  activeCompPath,
217
214
  domEditSelectionRef: domEditSelectionBridgeRef,
@@ -219,7 +216,7 @@ export function StudioApp() {
219
216
  writeProjectFile: fileManager.writeProjectFile,
220
217
  recordEdit: editHistory.recordEdit,
221
218
  reloadPreview,
222
- handleTimelineElementDelete: timelineEditing.handleTimelineElementDelete,
219
+ handleTimelineElementsDelete: timelineEditing.handleTimelineElementsDelete,
223
220
  handleDomEditElementDelete: domEditDeleteBridge,
224
221
  previewIframeRef,
225
222
  });
@@ -240,6 +237,7 @@ export function StudioApp() {
240
237
  handleCopy,
241
238
  handlePaste,
242
239
  handleCut,
240
+ handleDuplicate,
243
241
  onResetKeyframes: () => resetKeyframesRef.current(),
244
242
  onDeleteSelectedKeyframes: () => deleteSelectedKeyframesRef.current(),
245
243
  onAfterUndoRedo: () => invalidateGsapCacheRef.current(),
@@ -326,10 +324,8 @@ export function StudioApp() {
326
324
  effectiveTimelineDuration,
327
325
  });
328
326
  const compositionDimensions = useCompositionDimensions(previewIframeRef);
329
- const { lintModal, linting, handleLint, closeLintModal, findingsByFile } = useLintModal(
330
- projectId,
331
- refreshKey,
332
- );
327
+ const { lintModal, linting, handleLint, closeLintModal, findingsByFile, hasLintError } =
328
+ useLintModal(projectId, refreshKey);
333
329
  const frameCapture = useFrameCapture({
334
330
  projectId,
335
331
  activeCompPath,
@@ -341,7 +337,7 @@ export function StudioApp() {
341
337
  setConsoleErrors,
342
338
  resetErrors: resetConsoleErrors,
343
339
  } = useConsoleErrorCapture(previewIframe);
344
- const dragOverlay = useGlobalFileDrop(timelineEditing.handleTimelineFileDrop);
340
+ const fileDrop = useGlobalFileDrop(timelineEditing.handleTimelineFileDrop);
345
341
  const handleToggleRecordingRef = useRef<() => void>(() => {});
346
342
  const domEditSessionRef = useRef(domEditSession);
347
343
  domEditSessionRef.current = domEditSession;
@@ -440,143 +436,136 @@ export function StudioApp() {
440
436
  return (
441
437
  <StudioShellProvider value={studioCtxValue}>
442
438
  <StudioPlaybackProvider value={studioCtxValue}>
443
- <ViewModeProvider value={viewModeValue}>
444
- <PanelLayoutProvider value={panelLayout}>
445
- <FileManagerProvider value={fileManager}>
446
- <DomEditProvider value={domEditSession}>
447
- <div
448
- className="flex flex-col h-full w-full bg-neutral-950 relative"
449
- onDragOver={dragOverlay.onDragOver}
450
- onDragEnter={dragOverlay.onDragEnter}
451
- onDragLeave={dragOverlay.onDragLeave}
452
- onDrop={dragOverlay.onDrop}
453
- >
454
- <StudioHeader
455
- captureFrameHref={frameCapture.captureFrameHref}
456
- captureFrameFilename={frameCapture.captureFrameFilename}
457
- handleCaptureFrameClick={frameCapture.handleCaptureFrameClick}
458
- refreshCaptureFrameTime={frameCapture.refreshCaptureFrameTime}
459
- capturing={frameCapture.capturing}
460
- inspectorButtonActive={inspectorButtonActive}
461
- inspectorPanelActive={inspectorPanelActive}
462
- onExport={() => {
463
- void (async () => {
464
- await previewPersistence.waitForPendingDomEditSaves();
465
- await renderQueue.startRender(undefined);
466
- })();
467
- }}
439
+ <PanelLayoutProvider value={panelLayout}>
440
+ <FileManagerProvider value={fileManager}>
441
+ <DomEditProvider value={domEditSession}>
442
+ <div
443
+ className="flex flex-col h-full w-full bg-neutral-950 relative"
444
+ onDragOver={fileDrop.onDragOver}
445
+ onDrop={fileDrop.onDrop}
446
+ >
447
+ <StudioHeader
448
+ captureFrameHref={frameCapture.captureFrameHref}
449
+ captureFrameFilename={frameCapture.captureFrameFilename}
450
+ handleCaptureFrameClick={frameCapture.handleCaptureFrameClick}
451
+ refreshCaptureFrameTime={frameCapture.refreshCaptureFrameTime}
452
+ capturing={frameCapture.capturing}
453
+ inspectorButtonActive={inspectorButtonActive}
454
+ inspectorPanelActive={inspectorPanelActive}
455
+ onExport={() => {
456
+ void (async () => {
457
+ await previewPersistence.waitForPendingDomEditSaves();
458
+ await renderQueue.startRender(undefined);
459
+ })();
460
+ }}
461
+ />
462
+ {previewPersistence.domEditSaveQueuePaused && !externalFileChanges.blocked && (
463
+ <SaveQueuePausedBanner
464
+ message={previewPersistence.domEditSaveQueuePaused}
465
+ onRetry={previewPersistence.resetDomEditSaveQueueBreaker}
468
466
  />
469
- {previewPersistence.domEditSaveQueuePaused && !externalFileChanges.blocked && (
470
- <SaveQueuePausedBanner
471
- message={previewPersistence.domEditSaveQueuePaused}
472
- onRetry={previewPersistence.resetDomEditSaveQueueBreaker}
473
- />
474
- )}
475
- <ExternalFileConflictBanner coordinator={externalFileChanges} />
476
- {viewModeValue.viewMode === "storyboard" && (
477
- <StoryboardView
478
- projectId={projectId}
467
+ )}
468
+ <ExternalFileConflictBanner coordinator={externalFileChanges} />
469
+ <EditorShell
470
+ left={
471
+ <StudioLeftSidebar
472
+ leftSidebarRef={leftSidebarRef}
479
473
  onSelectComposition={handleSelectComposition}
474
+ onAddBlock={handleAddBlock}
475
+ onPreviewBlock={setBlockPreview}
476
+ onLint={handleLint}
477
+ linting={linting}
478
+ lintFindingCount={lintModal?.length ?? findingsByFile.size}
479
+ lintFindingsByFile={findingsByFile}
480
+ lintHasError={hasLintError}
481
+ onAddAssetToTimeline={handleAddAssetAtPlayhead}
482
+ onAddCompositionToTimeline={handleAddCompositionAtPlayhead}
480
483
  />
481
- )}
482
- <EditorShell
483
- hidden={viewModeValue.viewMode === "storyboard"}
484
- left={
485
- <StudioLeftSidebar
486
- leftSidebarRef={leftSidebarRef}
487
- onSelectComposition={handleSelectComposition}
488
- onAddBlock={handleAddBlock}
489
- onPreviewBlock={setBlockPreview}
490
- onLint={handleLint}
491
- linting={linting}
492
- lintFindingCount={lintModal?.length ?? findingsByFile.size}
493
- lintFindingsByFile={findingsByFile}
494
- onAddAssetToTimeline={handleAddAssetAtPlayhead}
495
- onAddCompositionToTimeline={handleAddCompositionAtPlayhead}
484
+ }
485
+ right={
486
+ panelLayout.effectiveRightCollapsed ? null : (
487
+ <StudioRightPanel
488
+ designPanelActive={designPanelActive}
489
+ activeBlockParams={activeBlockParams}
490
+ onCloseBlockParams={() => {
491
+ setActiveBlockParams(null);
492
+ panelLayout.setRightPanelTab("design");
493
+ }}
494
+ recordingState={gestureState}
495
+ recordingDuration={gestureRecording.recordingDuration}
496
+ onToggleRecording={handleToggleRecording}
497
+ sdkSession={sdkHandle.session}
498
+ publishSdkSession={sdkHandle.publish}
499
+ forceReloadSdkSession={sdkHandle.forceReload}
500
+ reloadPreview={reloadPreview}
501
+ recordEdit={editHistory.recordEdit}
502
+ onToggleElementHidden={timelineEditing.handleToggleElementHidden}
503
+ onAutoGroupCarveSources={timelineEditing.handleAutoGroupCarveSources}
504
+ onAddMediaOverlay={handleAddMediaOverlay}
496
505
  />
497
- }
498
- right={
499
- panelLayout.effectiveRightCollapsed ? null : (
500
- <StudioRightPanel
501
- designPanelActive={designPanelActive}
502
- activeBlockParams={activeBlockParams}
503
- onCloseBlockParams={() => {
504
- setActiveBlockParams(null);
505
- panelLayout.setRightPanelTab("design");
506
- }}
507
- recordingState={gestureState}
508
- recordingDuration={gestureRecording.recordingDuration}
509
- onToggleRecording={handleToggleRecording}
510
- sdkSession={sdkHandle.session}
511
- publishSdkSession={sdkHandle.publish}
512
- forceReloadSdkSession={sdkHandle.forceReload}
513
- reloadPreview={reloadPreview}
514
- recordEdit={editHistory.recordEdit}
515
- onToggleElementHidden={timelineEditing.handleToggleElementHidden}
516
- onAutoGroupCarveSources={timelineEditing.handleAutoGroupCarveSources}
517
- onAddMediaOverlay={handleAddMediaOverlay}
518
- />
519
- )
520
- }
521
- timelineToolbar={timelineToolbar}
522
- renderClipContent={renderClipContent}
523
- handleTimelineElementDelete={timelineEditing.handleTimelineElementDelete}
524
- handleTimelineAssetDrop={timelineEditing.handleTimelineAssetDrop}
525
- handleTimelineBlockDrop={handleTimelineBlockDrop}
526
- handleTimelineCompositionDrop={timelineEditing.handleTimelineCompositionDrop}
527
- handlePreviewBlockDrop={handlePreviewBlockDrop}
528
- handleTimelineFileDrop={timelineEditing.handleTimelineFileDrop}
529
- handleTimelineElementMove={timelineEditing.handleTimelineElementMove}
530
- handleTimelineElementsMove={handleTimelineElementsMove}
531
- handleTimelineElementResize={timelineEditing.handleTimelineElementResize}
532
- handleTimelineGroupResize={timelineEditing.handleTimelineGroupResize}
533
- handleToggleTrackHidden={timelineEditing.handleToggleTrackHidden}
534
- setAudioGroupAttribute={timelineEditing.setAudioGroupAttribute}
535
- handleGroupClips={timelineEditing.handleAutoGroupCarveSources}
536
- setElementFxAttribute={timelineEditing.setElementFxAttribute}
537
- handleBlockedTimelineEdit={timelineEditing.handleBlockedTimelineEdit}
538
- handleTimelineElementSplit={timelineEditing.handleTimelineElementSplit}
539
- handleRazorSplit={timelineEditing.handleRazorSplit}
540
- handleRazorSplitAll={timelineEditing.handleRazorSplitAll}
541
- setCompIdToSrc={setCompIdToSrc}
542
- setCompositionLoading={setCompositionLoading}
543
- shouldShowMotionPath={shouldShowMotionPath}
544
- shouldShowSelectedDomBounds={shouldShowSelectedDomBounds}
545
- isGestureRecording={gestureState === "recording"}
546
- recordingState={gestureState}
547
- onToggleRecording={handleToggleRecording}
548
- blockPreview={blockPreview}
549
- gestureOverlay={
550
- gestureState === "recording" && previewIframe ? (
551
- <GestureTrailOverlay
552
- samples={gestureRecording.samplesRef.current}
553
- sampleCount={gestureRecording.samplesRef.current.length}
554
- trail={gestureRecording.trailRef.current}
555
- canvasRect={canvasRectRef.current!}
556
- compositionSize={compositionDimensions ?? undefined}
557
- mode="recording"
558
- />
559
- ) : undefined
560
- }
561
- />
562
- <StudioOverlays
563
- projectId={projectId}
564
- projectDir={fileManager.projectDir}
565
- lintModal={lintModal}
566
- closeLintModal={closeLintModal}
567
- consoleErrors={consoleErrors}
568
- clearConsoleErrors={() => setConsoleErrors(null)}
569
- domEditSession={domEditSession}
570
- activeCompPath={activeCompPath}
571
- dragOverlayActive={dragOverlay.active}
572
- toasts={toasts}
573
- dismissToast={dismissToast}
574
- />
575
- </div>
576
- </DomEditProvider>
577
- </FileManagerProvider>
578
- </PanelLayoutProvider>
579
- </ViewModeProvider>
506
+ )
507
+ }
508
+ timelineToolbar={timelineToolbar}
509
+ renderClipContent={renderClipContent}
510
+ handleTimelineElementDelete={timelineEditing.handleTimelineElementDelete}
511
+ handleTimelineAssetDrop={timelineEditing.handleTimelineAssetDrop}
512
+ handleTimelineBlockDrop={handleTimelineBlockDrop}
513
+ handleTimelineCompositionDrop={timelineEditing.handleTimelineCompositionDrop}
514
+ handlePreviewBlockDrop={handlePreviewBlockDrop}
515
+ handleTimelineFileDrop={timelineEditing.handleTimelineFileDrop}
516
+ handleTimelineElementMove={timelineEditing.handleTimelineElementMove}
517
+ handleTimelineElementsMove={handleTimelineElementsMove}
518
+ handleTimelineElementResize={timelineEditing.handleTimelineElementResize}
519
+ handleTimelineGroupResize={timelineEditing.handleTimelineGroupResize}
520
+ handleToggleTrackHidden={timelineEditing.handleToggleTrackHidden}
521
+ setAudioGroupAttribute={timelineEditing.setAudioGroupAttribute}
522
+ handleGroupClips={timelineEditing.handleAutoGroupCarveSources}
523
+ setElementFxAttribute={timelineEditing.setElementFxAttribute}
524
+ handleBlockedTimelineEdit={timelineEditing.handleBlockedTimelineEdit}
525
+ handleTimelineElementSplit={timelineEditing.handleTimelineElementSplit}
526
+ handleRazorSplit={timelineEditing.handleRazorSplit}
527
+ handleRazorSplitAll={timelineEditing.handleRazorSplitAll}
528
+ onCopyClip={handleCopy}
529
+ onPasteClip={handlePaste}
530
+ onDuplicateClip={handleDuplicate}
531
+ canPasteClip={canPaste}
532
+ setCompIdToSrc={setCompIdToSrc}
533
+ setCompositionLoading={setCompositionLoading}
534
+ shouldShowMotionPath={shouldShowMotionPath}
535
+ shouldShowSelectedDomBounds={shouldShowSelectedDomBounds}
536
+ isGestureRecording={gestureState === "recording"}
537
+ recordingState={gestureState}
538
+ onToggleRecording={handleToggleRecording}
539
+ blockPreview={blockPreview}
540
+ gestureOverlay={
541
+ gestureState === "recording" && previewIframe ? (
542
+ <GestureTrailOverlay
543
+ samples={gestureRecording.samplesRef.current}
544
+ sampleCount={gestureRecording.samplesRef.current.length}
545
+ trail={gestureRecording.trailRef.current}
546
+ canvasRect={canvasRectRef.current!}
547
+ compositionSize={compositionDimensions ?? undefined}
548
+ mode="recording"
549
+ />
550
+ ) : undefined
551
+ }
552
+ />
553
+ <StudioOverlays
554
+ projectId={projectId}
555
+ projectDir={fileManager.projectDir}
556
+ lintModal={lintModal}
557
+ closeLintModal={closeLintModal}
558
+ consoleErrors={consoleErrors}
559
+ clearConsoleErrors={() => setConsoleErrors(null)}
560
+ domEditSession={domEditSession}
561
+ activeCompPath={activeCompPath}
562
+ toasts={toasts}
563
+ dismissToast={dismissToast}
564
+ />
565
+ </div>
566
+ </DomEditProvider>
567
+ </FileManagerProvider>
568
+ </PanelLayoutProvider>
580
569
  </StudioPlaybackProvider>
581
570
  </StudioShellProvider>
582
571
  );
@@ -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>
@@ -221,9 +221,8 @@ export function registerCaptionIframe(ref: React.RefObject<HTMLIFrameElement | n
221
221
 
222
222
  /**
223
223
  * True when the caption preview iframe is mounted AND visible. The caption
224
- * store's isEditMode stays true while the preview is merely hidden (e.g.
225
- * storyboard view), so hotkeys must not route ⌘Z to the caption stack unless
226
- * the user can actually see the captions the undo would change.
224
+ * store's isEditMode stays true while the preview is merely hidden, so hotkeys must not
225
+ * route ⌘Z to the caption stack unless the user can see the captions the undo would change.
227
226
  */
228
227
  export function isCaptionPreviewVisible(): boolean {
229
228
  const iframe = registeredIframe?.current;
@@ -213,7 +213,7 @@ export const CaptionPropertyPanel = memo(function CaptionPropertyPanel({
213
213
  return (
214
214
  <div className="flex flex-col h-full min-h-0">
215
215
  {/* Header */}
216
- <div className="px-3 py-2 border-b border-neutral-800 flex-shrink-0">
216
+ <div className="px-3 py-2 border-b border-neutral-800 shrink-0">
217
217
  <div className="flex items-center justify-between mb-1.5">
218
218
  <span className="text-2xs text-neutral-500">{countLabel}</span>
219
219
  </div>
@@ -225,7 +225,7 @@ export const CaptionPropertyPanel = memo(function CaptionPropertyPanel({
225
225
  aria-selected={activeTab === "style"}
226
226
  onClick={() => setActiveTab("style")}
227
227
  className={[
228
- "flex-1 py-0.5 rounded text-2xs font-medium transition-colors",
228
+ "flex-1 py-0.5 rounded-sm text-2xs font-medium transition-colors",
229
229
  activeTab === "style"
230
230
  ? "bg-studio-accent/20 text-studio-accent border border-studio-accent/50"
231
231
  : "text-neutral-500 border border-neutral-800 hover:text-neutral-300 hover:border-neutral-600",
@@ -239,7 +239,7 @@ export const CaptionPropertyPanel = memo(function CaptionPropertyPanel({
239
239
  aria-selected={activeTab === "animation"}
240
240
  onClick={() => setActiveTab("animation")}
241
241
  className={[
242
- "flex-1 py-0.5 rounded text-2xs font-medium transition-colors",
242
+ "flex-1 py-0.5 rounded-sm text-2xs font-medium transition-colors",
243
243
  activeTab === "animation"
244
244
  ? "bg-studio-accent/20 text-studio-accent border border-studio-accent/50"
245
245
  : "text-neutral-500 border border-neutral-800 hover:text-neutral-300 hover:border-neutral-600",
@@ -109,7 +109,7 @@ export const CaptionTimeline = memo(function CaptionTimeline({
109
109
  tabIndex={0}
110
110
  aria-label={`Caption word "${seg.text}"`}
111
111
  aria-pressed={isSelected}
112
- className={`absolute top-1 bottom-1 rounded flex items-center overflow-hidden cursor-pointer focus-visible:ring-1 focus-visible:ring-white outline-none${
112
+ className={`absolute top-1 bottom-1 rounded flex items-center overflow-hidden cursor-pointer focus-visible:ring-1 focus-visible:ring-white outline-hidden${
113
113
  isSelected ? " ring-1 ring-white/50 z-10" : ""
114
114
  }`}
115
115
  style={{
@@ -2,7 +2,7 @@ import type React from "react";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
 
4
4
  export const inputCls =
5
- "w-full bg-neutral-900 border border-neutral-800 rounded px-1.5 py-0.5 text-2xs text-neutral-200 font-mono outline-none focus:border-studio-accent disabled:opacity-40 disabled:cursor-not-allowed";
5
+ "w-full bg-neutral-900 border border-neutral-800 rounded-sm px-1.5 py-0.5 text-2xs text-neutral-200 font-mono outline-hidden focus:border-studio-accent disabled:opacity-40 disabled:cursor-not-allowed";
6
6
 
7
7
  export function Section({ label, children }: { label: string; children: React.ReactNode }) {
8
8
  return (
@@ -20,7 +20,7 @@ export function Section({ label, children }: { label: string; children: React.Re
20
20
  export function Row({ label, children }: { label: string; children: React.ReactNode }) {
21
21
  return (
22
22
  <div className="flex items-center gap-2">
23
- <span className="text-2xs text-neutral-600 w-14 text-right flex-shrink-0">{label}</span>
23
+ <span className="text-2xs text-neutral-600 w-14 text-right shrink-0">{label}</span>
24
24
  <div className="flex-1 min-w-0">{children}</div>
25
25
  </div>
26
26
  );
@@ -66,8 +66,8 @@ export function AskAgentModal({
66
66
  <div
67
67
  className={
68
68
  anchorPoint
69
- ? "hf-backdrop-in fixed inset-0 z-[100] bg-black/60 backdrop-blur-sm"
70
- : "hf-backdrop-in fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm"
69
+ ? "hf-backdrop-in fixed inset-0 z-100 bg-black/60 backdrop-blur-xs"
70
+ : "hf-backdrop-in fixed inset-0 z-100 flex items-center justify-center bg-black/60 backdrop-blur-xs"
71
71
  }
72
72
  onClick={requestClose}
73
73
  >
@@ -77,7 +77,7 @@ export function AskAgentModal({
77
77
  aria-modal="true"
78
78
  aria-label="Copy prompt to AI agent"
79
79
  tabIndex={-1}
80
- className={`w-[480px] rounded-2xl border border-neutral-800 bg-neutral-950 shadow-2xl outline-none ${
80
+ className={`w-[480px] rounded-2xl border border-neutral-800 bg-neutral-950 shadow-2xl outline-hidden ${
81
81
  anchorPoint ? "fixed" : ""
82
82
  }`}
83
83
  style={modalPositionStyle}
@@ -112,7 +112,7 @@ export function AskAgentModal({
112
112
  <div className="px-5 py-4 space-y-3">
113
113
  <textarea
114
114
  ref={inputRef}
115
- className="w-full h-24 px-3 py-2 rounded-lg border border-neutral-800 bg-neutral-900/60 text-sm text-neutral-200 placeholder-neutral-600 resize-none focus:outline-none focus:border-studio-accent/60 focus:ring-1 focus:ring-studio-accent/30"
115
+ className="w-full h-24 px-3 py-2 rounded-lg border border-neutral-800 bg-neutral-900/60 text-sm text-neutral-200 placeholder-neutral-600 resize-none focus:outline-hidden focus:border-studio-accent/60 focus:ring-1 focus:ring-studio-accent/30"
116
116
  placeholder="Describe what you want to change…"
117
117
  value={value}
118
118
  onChange={(e) => setValue(e.target.value)}
@@ -127,7 +127,7 @@ export function AskAgentModal({
127
127
  <summary className="text-[11px] text-neutral-500 cursor-pointer select-none hover:text-neutral-400">
128
128
  Context included in prompt
129
129
  </summary>
130
- <pre className="mt-2 max-h-40 overflow-auto rounded-lg bg-neutral-900/80 px-3 py-2 text-[11px] leading-relaxed text-neutral-500 whitespace-pre-wrap break-words border border-neutral-800/50">
130
+ <pre className="mt-2 max-h-40 overflow-auto rounded-lg bg-neutral-900/80 px-3 py-2 text-[11px] leading-relaxed text-neutral-500 whitespace-pre-wrap wrap-break-word border border-neutral-800/50">
131
131
  {contextPreview}
132
132
  </pre>
133
133
  </details>
@@ -86,6 +86,10 @@ describe("EditorShell timeline selection sync", () => {
86
86
  handleTimelineElementSplit={vi.fn()}
87
87
  handleRazorSplit={vi.fn()}
88
88
  handleRazorSplitAll={vi.fn()}
89
+ onCopyClip={vi.fn(() => false)}
90
+ onPasteClip={vi.fn(async () => {})}
91
+ onDuplicateClip={vi.fn(async () => false)}
92
+ canPasteClip={vi.fn(() => false)}
89
93
  setCompIdToSrc={vi.fn()}
90
94
  setCompositionLoading={vi.fn()}
91
95
  shouldShowMotionPath={false}